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

Last change on this file since 2510 was 2510, checked in by kbut, 3 years ago

wip

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