source: lliurex-mirror/trunk/fuentes/n4d-lliurex-mirror.install/usr/share/n4d/python-plugins/MirrorManager.py @ 2482

Last change on this file since 2482 was 2482, checked in by kbut, 4 years ago

a lot of thins

File size: 18.6 KB
Line 
1from jinja2 import Environment
2from jinja2.loaders import FileSystemLoader
3from jinja2 import Template
4
5import tempfile
6
7import os
8import threading
9import datetime
10import pexpect
11import re
12import json
13
14import BaseHTTPServer
15from SimpleHTTPServer import SimpleHTTPRequestHandler
16from multiprocessing import Process
17import socket
18from urllib2 import urlopen
19
20
21class MirrorManager:
22
23
24        def __init__(self):
25                #Default values
26                self.defaultpath = '/etc/lliurex-mirror/'
27                self.debmirrorconfpath = os.path.join(self.defaultpath,'debmirror')
28                self.configpath = os.path.join(self.defaultpath,'conf')
29                self.distro="llx16"
30                self.httpd = None
31                self.debmirrorprocess = None
32
33                self.tpl_env = Environment(loader=FileSystemLoader('/usr/share/n4d/templates/lliurex-mirror'))
34                self.update_thread=threading.Thread()
35                self.percentage=(0,None)
36                self.mirrorworking = None
37                self.webserverprocess = None
38                self.defaultmirrorinfo = {"status_mirror":"New","last_mirror_date":None,"mirror_size":0,"progress":0}
39                self.default_mirror_config = '''
40{
41        "NAME": "",
42        "BANNER": "",
43        "ORIGS" : {"1":"lliruex.net/xenial","2":"","3":""},
44        "ARCHITECTURES": [ "amd64", "i386"],
45        "SECTIONS": ["main", "main/debian-installer", "universe", "restricted", "multiverse", "partner"],
46        "MIRROR_PATH": "/net/mirror/llx16",
47        "DISTROS": ["xenial","xenial-updates","xenial-security"],
48        "IGN_GPG":1,
49        "IGN_RELEASE":0,
50        "CHK_MD5":0,
51        "CURRENT_UPDATE_OPTION":"1"
52}'''
53               
54        #def init
55       
56        def startup(self,options):
57                self.n4d_vars=objects["VariablesManager"]
58                self.variable=objects["VariablesManager"].get_variable("LLIUREXMIRROR")
59               
60               
61                if self.variable==None:
62                        try:
63                                self.n4d_vars.add_variable("LLIUREXMIRROR",{},"","Lliurex Mirror info variable","n4d-lliurex-mirror")
64                        except Exception as e:
65                                pass
66                       
67                if type(self.variable)!=type({}):
68                        self.variable={}
69               
70                try:
71                        for repo in self.get_available_mirrors()['msg']:
72                                if self.variable.has_key(repo) and self.variable[repo].has_key("status_mirror") and self.variable[repo]["status_mirror"] == "Working":
73                                        if not self.update_thread.isAlive():
74                                                self.variable[repo]["status_mirror"] = "Error"
75                                                self.n4d_vars.set_variable("LLIUREXMIRROR",self.variable)
76                                else:
77                                        if not self.variable.has_key(repo):
78                                                self.variable[repo] = self.defaultmirrorinfo
79                except Exception as e:
80                        pass
81        #def startup
82
83        def apt(self):
84                # executed after apt operations
85                pass
86               
87        #def apt
88       
89        # service test and backup functions #
90       
91        def test(self):
92
93                pass
94               
95        #def test
96       
97        def backup(self):
98
99                pass
100               
101        #def backup
102
103        def restore(self):
104                pass
105        #def restore
106
107        def set_cname(self):
108                #Get template
109                template = self.tpl_env.get_template("cname")
110                list_variables = {}
111               
112                list_variables = self.n4d_vars.get_variable_list(['INTERNAL_DOMAIN','HOSTNAME'])
113                for x in list_variables.keys():
114                        if list_variables[x] == None:
115                                return {'status':False,'msg':'Variable ' + x + ' not defined'}
116                       
117                #Encode vars to UTF-8
118                string_template = template.render(list_variables).encode('UTF-8')
119                #Open template file
120                fd, tmpfilepath = tempfile.mkstemp()
121                new_export_file = open(tmpfilepath,'w')
122                new_export_file.write(string_template)
123                new_export_file.close()
124                os.close(fd)
125                #Write template values
126                n4d_mv(tmpfilepath,'/var/lib/dnsmasq/config/cname-mirror',True,'root','root','0644',False )
127               
128                return {'status':True,'msg':'Set mirror cname'}
129        #def set_cname
130       
131        def update(self,distro=None):
132
133                if distro==None:
134                        distro=self.distro
135       
136                if self.update_thread.is_alive():
137                        return {'status':False,'msg':'Lliurex-mirror (n4d instance) is running'}
138               
139                self.percentage=(0,None)
140                self.update_thread=threading.Thread(target=self._update,args=(distro,))
141                self.update_thread.daemon=True
142                self.update_thread.start()
143               
144                return {'status':True,'msg':'running'}
145
146        #def update
147       
148        def _update(self,distro):
149                if not self.variable.has_key(distro):
150                        self.variable[distro]=self.defaultmirrorinfo
151                # link config debmirror to correct path with distro name
152                self.variable[distro]['status_mirror'] = "Working"
153                self.n4d_vars.set_variable("LLIUREXMIRROR",self.variable)
154                self.build_debmirror_config(distro)
155                if os.path.exists('/etc/debmirror.conf'):
156                        os.remove('/etc/debmirror.conf')
157                os.symlink(os.path.join(self.debmirrorconfpath,distro),'/etc/debmirror.conf')
158                self.mirrorworking = distro
159                self.debmirrorprocess=pexpect.spawn("/usr/bin/debmirror")
160                try:
161                        objects["ZCenterVariables"].add_pulsating_color("lliurexmirror")
162                except:
163                        pass
164                while True:
165                        try:
166                                self.debmirrorprocess.expect('\n')
167                                line =self.debmirrorprocess.before
168                                line1=line.strip("\n")
169                                if line1.startswith("[") and line1[5] == "]":
170                                        self.percentage=(int(line1[1:4].strip()),self.debmirrorprocess.exitstatus)
171                                        self.variable[distro]['progress'] = self.percentage[0]
172                                        self.n4d_vars.set_variable("LLIUREXMIRROR",self.variable)
173                        except pexpect.EOF:
174                                        line1 = self.debmirrorprocess.before
175                                        if line1 != "" and line1.startswith("[") and line1[5] == "]":
176                                                        self.percentage=(int(line1[1:4].strip()),self.debmirrorprocess.exitstatus)
177                                        self.debmirrorprocess.close()
178                                        status = self.debmirrorprocess.exitstatus
179                                        self.percentage=(self.percentage[0],status)
180                                        self.variable[distro]['progress'] = self.percentage[0]
181                                        self.variable[distro]['status_mirror'] = "Ok" if status == 0 else "Error"
182                                        self.n4d_vars.set_variable("LLIUREXMIRROR",self.variable)
183                                        break
184                        except Exception as e:
185                                self.variable[distro]['status_mirror'] = "Error"
186                                self.n4d_vars.set_variable("LLIUREXMIRROR",self.variable)
187                                break
188
189                self.download_time_file(distro)
190
191                self.set_mirror_info(distro)
192                self.mirrorworking = None
193                try:
194                        objects["ZCenterVariables"].remove_pulsating_color("lliurexmirror")
195                except:
196                        pass
197
198        #def _update
199       
200        def is_alive(self):
201
202                return {'status':self.update_thread.is_alive(),'msg':self.mirrorworking}
203        #def is_alive
204
205        def set_mirror_info(self,distro=None):
206               
207                if distro!=None:
208                        distro=distro
209                else:
210                        distro=self.distro
211               
212                configpath = os.path.join(self.configpath, distro + ".json")
213                config = json.load(open(configpath,'r'))
214
215                mirrorpath = config["MIRROR_PATH"]
216                #self.n4d_vars.set_variable("ZEROCENTERINTERNAL",self.internal_variable)
217               
218                MIRROR_DATE=datetime.date.today().strftime("%d/%m/%Y")
219                MIRROR_SIZE=self.get_size(mirrorpath)
220               
221                self.variable[distro]["last_mirror_date"]=MIRROR_DATE
222                self.variable[distro]["mirror_size"]=str(MIRROR_SIZE)
223                self.variable[distro]["progress"]=self.percentage[0]
224               
225                print self.n4d_vars.set_variable("LLIUREXMIRROR",self.variable)
226               
227                #set_custom_text(self,app,text):
228                txt="Updated on: " + str(MIRROR_DATE)
229                txt+=" # Size: %.2fGB"%MIRROR_SIZE
230                try:
231                        objects["ZCenterVariables"].set_custom_text("lliurexmirror",txt)
232                        abstract=open('/var/log/lliurex/lliurex-mirror.log','w')
233                        abstract.write(txt+"\n")
234                        abstract.close()
235                except Exception as e:
236                        pass
237
238        #def set_mirror_info(self):
239
240        def get_size(self,start_path = '.'):
241       
242                total_size = 0
243                try:
244                        for dirpath, dirnames, filenames in os.walk(start_path):
245                                for f in filenames:
246                                        fp = os.path.join(dirpath, f)
247                                        total_size += os.path.getsize(fp)
248                                       
249                        total_size/=1024*1024*1024.0
250                        return total_size
251                except:
252                        return 0
253       
254        #def get_size(start_path = '.'):
255       
256        def search_field(self,filepath,fieldname):
257                try:
258                        f = open(filepath,'r')
259                        needle = None
260                        lines = f.readlines()
261                        for x in lines:
262                                        if re.match('\s*'+fieldname,x):
263                                                        needle = x.strip()
264                        return needle
265                except:
266                        return None
267        # def search_field
268       
269        def get_mirror_architecture(self,distro):
270
271                configpath = os.path.join(self.configpath,distro + ".json")
272                config = json.load(open(configpath,'r'))
273                if not os.path.exists(configpath):
274                        return {'status':False,'msg':'not exists debmirror.conf to '+ distro }
275
276                if "ARCHITECTURES" in config.keys():
277                        return {'status':True,'msg':config["ARCHITECTURES"] }
278
279                return {'status':False,'msg':"debmirror.conf hasn't architecture variable" }
280        #def get_mirror_architecture
281       
282        def set_mirror_architecture(self,distro,archs):
283                configpath = os.path.join(self.configpath,distro + ".json")
284               
285                config = json.load(open(configpath,'r'))
286               
287                config['ARCHITECTURES'] = archs
288
289
290                f=open(configpath,"w")
291
292                data=unicode(json.dumps(config,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
293                f.write(data)
294                f.close()
295
296                self.build_debmirror_config(distro)
297                return {'status':True,'msg':'set architecture'}
298               
299        #def set_mirror_architecture
300       
301        def get_mirror_orig(self,distro,option):
302
303                configpath = os.path.join(self.configpath,distro + ".json")
304                config = json.load(open(configpath,'r'))
305                if not os.path.exists(configpath):
306                        return {'status':False,'msg':'not exists debmirror.conf to '+ distro }
307
308                if "ORIGS" in config.keys():
309                        return {'status':True,'msg':config["ORIGS"][option] }
310                       
311                return {'status':False,'msg':"debmirror.conf hasn't orig variable" }   
312        #def get_mirror_from
313
314        def set_mirror_orig(self,distro,url,option):
315                if url == None:
316                        return {'status':False,'msg':'url is None'}
317                configpath = os.path.join(self.configpath, distro + ".json")
318                config = json.load(open(configpath,'r'))
319                config['ORIGS'][option] = url
320
321                f=open(configpath,"w")
322                data=unicode(json.dumps(config,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
323                f.write(data)
324                f.close()
325
326                self.build_debmirror_config(distro)
327                return {'status':True,'msg':'set orig'}
328        #def set_mirror_architecture
329
330        def get_option_update(self,distro):
331                configpath = os.path.join(self.configpath,distro + ".json")
332                config = json.load(open(configpath,'r'))
333                if not os.path.exists(configpath):
334                        return {'status':False,'msg':'not exists debmirror.conf to '+ distro }
335
336                if "CURRENT_UPDATE_OPTION" in config.keys():
337                        return {'status':True,'msg':config["CURRENT_UPDATE_OPTION"] }
338                       
339                return {'status':False,'msg':"debmirror.conf hasn't option update variable" }
340        #def set_option_update
341
342        def set_option_update(self,distro,option):
343                configpath = os.path.join(self.configpath, distro + ".json")
344                config = json.load(open(configpath,'r'))
345                config['CURRENT_UPDATE_OPTION'] = str(option)
346
347                f=open(configpath,"w")
348                data=unicode(json.dumps(config,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
349                f.write(data)
350                f.close()
351
352                self.build_debmirror_config(distro)
353                return {'status':True,'msg':'set update option'}
354        #def set_option_update
355
356        def get_percentage(self,distro):
357                if self.variable.has_key(distro):
358                        return {'status':True,'msg':self.variable[distro]['progress']}
359                else:
360                        return {'status':False,'msg':'this repo nos has been configured'}
361        #def get_percentage
362
363        def build_debmirror_config(self,distro):
364                result = self.render_debmirror_config(distro)
365                string_template = result['msg']
366                f = open(os.path.join(self.debmirrorconfpath,distro),'w')
367                f.write(string_template)
368                f.close()
369        #def build_debmirror_config
370
371        def render_debmirror_config(self,distro):
372                template = self.tpl_env.get_template('debmirror.conf')
373                configpath = os.path.join(self.configpath,distro + ".json")
374                config = json.load(open(configpath,'r'))
375                return {'status':True,'msg':template.render(config).encode('utf-8')}
376
377        #def render_debmirror_config
378       
379        def enable_webserver_into_folder(self,path):
380                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
381                s.bind(('localhost', 0))
382                addr, port = s.getsockname()
383                s.close()
384                self.webserverprocess = Process(target=self._enable_webserver_into_folder,args=(port,path,))
385                self.webserverprocess.start()
386                return {'status':True,'msg':port}
387        #enable_webserver_into_folder
388
389        def _enable_webserver_into_folder(self,port,path):
390                try:
391                        iface = '127.0.0.1'
392                        sock = (iface,port)
393                        proto = "HTTP/1.0"
394                        os.chdir(path)
395                        handler = SimpleHTTPRequestHandler
396                        handler.protocol_version = proto
397                        self.httpd = BaseHTTPServer.HTTPServer(sock,handler)
398                        self.httpd.serve_forever()
399                except Exception, e:
400                        return None
401        #_enable_webserver_into_folder
402
403        def stop_webserver(self):
404                if self.webserverprocess != None:
405                        self.webserverprocess.terminate()
406                return {'status':True,'msg':'Server stopped'}
407        #stop_webserver
408       
409        def set_checksum_validation(self,distro,status):
410                configpath = os.path.join(self.configpath, distro + ".json")
411                config = json.load(open(configpath,'r'))
412                config['CHK_MD5'] = status
413
414                f=open(configpath,"w")
415                data=unicode(json.dumps(config,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
416                f.write(data)
417                f.close()
418
419                self.build_debmirror_config(distro)
420                return {'status':True,'msg':'set checksum validation'}
421        #set_checksum_validation
422       
423        def get_checksum_validation(self,distro):
424
425                configpath = os.path.join(self.configpath,distro + ".json")
426                config = json.load(open(configpath,'r'))
427                if not os.path.exists(configpath):
428                        return {'status':False,'msg':'not exists debmirror.conf to '+ distro }
429                if "IGN_GPG" in config.keys():
430                        return {'status':True,'msg':config["CHK_MD5"] }
431
432                return {'status':False,'msg':"debmirror.conf hasn't orig variable" }
433        #get_checksum_validation
434       
435        def get_available_mirrors(self):
436                versions = os.listdir(self.configpath)
437                versions = [ version.replace('.json','') for version in versions if version.endswith('.json')]
438                return {'status':True,'msg':versions}
439
440        def stopupdate(self):
441                try:
442                        self.debmirrorprocess.terminate()
443                        return {'status':True,'msg':'debmirror stopped'}
444                except Exception as e:
445                        return {'status':False,'msg':str(e)}
446
447
448        def download_time_file(self,distro):
449               
450                configpath = os.path.join(self.configpath,distro + ".json")
451                config = json.load(open(configpath,'r'))
452                path=config["MIRROR_PATH"]
453                f="time-of-last-update"
454                dest=os.path.join(path,f)
455
456                orig_mirror=self.get_mirror_orig(distro)
457                url_mirror="http://"+os.path.join(orig_mirror['msg'],f)
458
459                return self.get_time_file(url_mirror,dest)
460
461        # # def download_time_file                     
462
463               
464        def get_time_file(self,url,dest):
465               
466                try:
467                        r=urlopen(url)
468                        f=open(dest,"wb")
469                        f.write(r.read())
470                        f.close()
471                        r.close()
472                        return {'status':True,'msg':dest + 'successfully downloaded.'}
473               
474                except Exception as e:
475                        return {'status':False,'msg':'Error downloading' + dest + ':' + str(e)}                 
476
477        # def get_time_file             
478
479        def is_update_available(self,distro):
480
481                configpath = os.path.join(self.configpath,distro + ".json")
482                config = json.load(open(configpath,'r'))
483                path=config["MIRROR_PATH"]
484                file_time_name="time-of-last-update"
485                file_local_mirror=os.path.join(path,file_time_name)
486
487               
488                if os.path.isfile(file_local_mirror):
489                        url_pool="http://"+os.path.join(config["ORIGS"]['1'],file_time_name)
490                        file_pool=os.path.join("/tmp",file_time_name)
491
492                        exist_file_pool=self.get_time_file(url_pool,file_pool)
493                        if exist_file_pool['status']:
494                                file_local_mirror_content=open(file_local_mirror,"r")
495                                file_local_miror_datetime=(file_local_mirror_content.readline().strip()).split("_")
496                                file_pool_content=open(file_pool,'r')
497                                file_pool_datetime=(file_pool_content.readline().strip()).split("_")
498                                file_local_mirror_content.close()
499                                file_pool_content.close()
500
501                                date_local_mirror=datetime.datetime.strptime(file_local_miror_datetime[0],"%Y/%m/%d")
502                                date_pool=datetime.datetime.strptime(file_pool_datetime[0],"%Y/%m/%d")
503
504                                if date_local_mirror==date_pool:
505                                        time_local_mirror=datetime.datetime.strptime(file_local_miror_datetime[1],"%H:%M")     
506                                        time_pool=datetime.datetime.strptime(file_pool_datetime[1],"%H:%M")
507
508                                        if time_local_mirror<time_pool:
509                                                return {'status':False,'msg':'Mirror not updated','action':'update'}
510                                        else:
511                                                return {'status':True,'msg':'Mirror is updated','action':'nothing'}
512
513                                elif date_local_mirror<date_pool:
514                                        return {'status':False,'msg':'Mirror not updated','action':'update'}
515                                else:
516                                        return {'status':True,'msg':'Mirror is updated','action':'nothing'}     
517                        else:
518                                return {'status':False,'msg':exist_file_pool['msg'],'action':'nothing'} 
519
520                else:
521                        return {'status':False,'msg':file_local_mirror + ' does not exist.','action':'nothing'}
522
523        # def is_update_available
524
525        def new_mirror_config(self,config):
526                name = config["NAME"].lower().strip()
527                import string
528                valid_chars = "-_.%s%s" % (string.ascii_letters, string.digits)
529                name = ''.join(c for c in name if c in valid_chars)
530
531                # Checks
532                if name == "":
533                        return {'status':False,'msg':"Name can't void"}
534                while True:
535                        newconfigpath = os.path.join(self.configpath,name,'.json')
536                        if not os.path.exists(newconfigpath):
537                                break
538                        name = name + "1"
539
540                data=unicode(json.dumps(config,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
541                f = open(newconfigpath,'w')
542                f.write(data)
543                f.close()
544                return {'status':True,'msg':name}
545        #def new_mirror_config
546
547        def get_all_configs(self):
548                versions = os.listdir(self.configpath)
549                allconfigs = {}
550                for version in versions:
551                        configfile = os.path.join(self.configpath,version)
552                        f = open(configfile,'r')
553                        allconfigs[version] = json.load(f)
554                        f.close()
555                return {'status':True,'msg':allconfigs}
556        #def get_all_configs
557
558        def update_mirror_config(self,mirror,config):
559                configpath = os.path.join(self.configpath,distro + ".json")
560
561                f=open(configpath,"w")
562
563                data=unicode(json.dumps(config,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
564                f.write(data)
565                f.close()
566
567                return {'status':True,'msg':'Updated config'}
568        #def update_mirror_config
569
570        def get_client_ip(self,ip):
571                return {'status':True,'msg':ip}
572        #def get_client_ip
573
574        def watch_update_finish(self,credentials,server):
575                self.watch_thread=threading.Thread(target=self._update,args=(credentials,server,))
576                self.watch_thread.daemon=True
577                self.watch_thread.start()
578               
579        #def watch_update_finish
580
581        def _watch_update_finish(self,credentials,server)
582                import time
583                import xmlrpclib as x
584                while True:
585                        try:
586                                time.sleep(240)
587                                c = x.ServerProxy('https://'+ server +':9779')
588                                result = c.is_alive(credentials,'MirrorManager')
589                                if ! result:
590                                        self.stop_webserver()
591                                        break
592                        except:
593                                break
594        #def _watch_update_finish
595
596        def is_alive_get_mirror(self):
597                return {'status':self.get_mirror_thread.is_alive(),'msg':}
598        #def is_alive_get_mirror
599
600        def get_mirror(self,config_path):
601                self.get_mirror_thread = threading.Thread(target=self._get_mirror,args=(config_path))
602                self.get_mirror_thread.daemon = True
603                self.get_mirror_thread.start()
604        #def get_mirror
605
606        def _get_mirror(self,config_path):
607                self.get_mirror_process = pexpect.spawn("/usr/bin/debmirror --config-file="+config_path)
608                while True:
609                        try:
610                                self.get_mirror_process.expect('\n')
611                                line =self.get_mirror_process.before
612                                line1=line.strip("\n")
613                                if line1.startswith("[") and line1[5] == "]":
614                                        self.exportpercentage = (int(line1[1:4].strip()),self.get_mirror_process.exitstatus)
615                        except pexpect.EOF:
616                                        line1 = self.get_mirror_process.before
617                                        if line1 != "" and line1.startswith("[") and line1[5] == "]":
618                                                        self.exportpercentage=(int(line1[1:4].strip()),self.get_mirror_process.exitstatus)
619                                        self.get_mirror_process.close()
620                                        status = self.get_mirror_process.exitstatus
621                                        self.exportpercentage=(self.exportpercentage[0],status)
622                                        break
623                        except Exception as e:
624                                break
625        #def _get
Note: See TracBrowser for help on using the repository browser.