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

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

wip

File size: 19.1 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
364        def build_debmirror_config(self,distro):
365                result = self.render_debmirror_config(distro)
366                string_template = result['msg']
367                f = open(os.path.join(self.debmirrorconfpath,distro),'w')
368                f.write(string_template)
369                f.close()
370        #def build_debmirror_config
371
372        def render_debmirror_config(self,arg):
373                if type(arg) == type(""):
374                        return self._render_debmirror_config_distro(arg)
375                if type(arg) == type({}):
376                        return self._render_debmirror_config_values(arg)
377        #def render_debmirror_config
378
379        def _render_debmirror_config_distro(self,distro):
380                template = self.tpl_env.get_template('debmirror.conf')
381                configpath = os.path.join(self.configpath,distro + ".json")
382                config = json.load(open(configpath,'r'))
383                return {'status':True,'msg':template.render(config).encode('utf-8')}
384        #def render_debmirror_config
385
386        def _render_debmirror_config_values(self,config):
387                template = self.tpl_env.get_template('debmirror.conf')
388                return {'status':True,'msg':template.render(config).encode('utf-8')}
389        #def _render_debmirror_config_values
390
391        def enable_webserver_into_folder(self,path):
392                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
393                s.bind(('localhost', 0))
394                addr, port = s.getsockname()
395                s.close()
396                self.webserverprocess = Process(target=self._enable_webserver_into_folder,args=(port,path,))
397                self.webserverprocess.start()
398                return {'status':True,'msg':port}
399        #enable_webserver_into_folder
400
401        def _enable_webserver_into_folder(self,port,path):
402                try:
403                        iface = '127.0.0.1'
404                        sock = (iface,port)
405                        proto = "HTTP/1.0"
406                        os.chdir(path)
407                        handler = SimpleHTTPRequestHandler
408                        handler.protocol_version = proto
409                        self.httpd = BaseHTTPServer.HTTPServer(sock,handler)
410                        self.httpd.serve_forever()
411                except Exception, e:
412                        return None
413        #_enable_webserver_into_folder
414
415        def stop_webserver(self):
416                if self.webserverprocess != None:
417                        self.webserverprocess.terminate()
418                return {'status':True,'msg':'Server stopped'}
419        #stop_webserver
420       
421        def set_checksum_validation(self,distro,status):
422                configpath = os.path.join(self.configpath, distro + ".json")
423                config = json.load(open(configpath,'r'))
424                config['CHK_MD5'] = status
425
426                f=open(configpath,"w")
427                data=unicode(json.dumps(config,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
428                f.write(data)
429                f.close()
430
431                self.build_debmirror_config(distro)
432                return {'status':True,'msg':'set checksum validation'}
433        #set_checksum_validation
434       
435        def get_checksum_validation(self,distro):
436
437                configpath = os.path.join(self.configpath,distro + ".json")
438                config = json.load(open(configpath,'r'))
439                if not os.path.exists(configpath):
440                        return {'status':False,'msg':'not exists debmirror.conf to '+ distro }
441                if "IGN_GPG" in config.keys():
442                        return {'status':True,'msg':config["CHK_MD5"] }
443
444                return {'status':False,'msg':"debmirror.conf hasn't orig variable" }
445        #get_checksum_validation
446       
447        def get_available_mirrors(self):
448                versions = os.listdir(self.configpath)
449                versions = [ version.replace('.json','') for version in versions if version.endswith('.json')]
450                return {'status':True,'msg':versions}
451
452        def stopupdate(self):
453                try:
454                        self.debmirrorprocess.terminate()
455                        return {'status':True,'msg':'debmirror stopped'}
456                except Exception as e:
457                        return {'status':False,'msg':str(e)}
458
459
460        def download_time_file(self,distro):
461               
462                configpath = os.path.join(self.configpath,distro + ".json")
463                config = json.load(open(configpath,'r'))
464                path=config["MIRROR_PATH"]
465                f="time-of-last-update"
466                dest=os.path.join(path,f)
467
468                orig_mirror=self.get_mirror_orig(distro)
469                url_mirror="http://"+os.path.join(orig_mirror['msg'],f)
470
471                return self.get_time_file(url_mirror,dest)
472
473        # # def download_time_file                     
474
475               
476        def get_time_file(self,url,dest):
477               
478                try:
479                        r=urlopen(url)
480                        f=open(dest,"wb")
481                        f.write(r.read())
482                        f.close()
483                        r.close()
484                        return {'status':True,'msg':dest + 'successfully downloaded.'}
485               
486                except Exception as e:
487                        return {'status':False,'msg':'Error downloading' + dest + ':' + str(e)}                 
488
489        # def get_time_file             
490
491        def is_update_available(self,distro):
492
493                configpath = os.path.join(self.configpath,distro + ".json")
494                config = json.load(open(configpath,'r'))
495                path = config["MIRROR_PATH"]
496                file_time_name = "time-of-last-update"
497                file_local_mirror = os.path.join(path,file_time_name)
498
499               
500                if os.path.isfile(file_local_mirror):
501                        url_pool = "http://"+os.path.join(config["ORIGS"]['1'],file_time_name)
502                        file_pool = os.path.join("/tmp",file_time_name)
503
504                        exist_file_pool = self.get_time_file(url_pool,file_pool)
505                        if exist_file_pool['status']:
506                                file_local_mirror_content=open(file_local_mirror,"r")
507                                file_local_miror_datetime=(file_local_mirror_content.readline().strip()).split("_")
508                                file_pool_content=open(file_pool,'r')
509                                file_pool_datetime=(file_pool_content.readline().strip()).split("_")
510                                file_local_mirror_content.close()
511                                file_pool_content.close()
512
513                                date_local_mirror=datetime.datetime.strptime(file_local_miror_datetime[0],"%Y/%m/%d")
514                                date_pool=datetime.datetime.strptime(file_pool_datetime[0],"%Y/%m/%d")
515
516                                if date_local_mirror==date_pool:
517                                        time_local_mirror=datetime.datetime.strptime(file_local_miror_datetime[1],"%H:%M")     
518                                        time_pool=datetime.datetime.strptime(file_pool_datetime[1],"%H:%M")
519
520                                        if time_local_mirror<time_pool:
521                                                return {'status':False,'msg':'Mirror not updated','action':'update'}
522                                        else:
523                                                return {'status':True,'msg':'Mirror is updated','action':'nothing'}
524
525                                elif date_local_mirror<date_pool:
526                                        return {'status':False,'msg':'Mirror not updated','action':'update'}
527                                else:
528                                        return {'status':True,'msg':'Mirror is updated','action':'nothing'}     
529                        else:
530                                return {'status':False,'msg':exist_file_pool['msg'],'action':'nothing'} 
531
532                else:
533                        return {'status':False,'msg':file_local_mirror + ' does not exist.','action':'nothing'}
534
535        # def is_update_available
536
537        def new_mirror_config(self,config):
538                name = config["NAME"].lower().strip()
539                import string
540                valid_chars = "-_.%s%s" % (string.ascii_letters, string.digits)
541                name = ''.join(c for c in name if c in valid_chars)
542
543                # Checks
544                if name == "":
545                        return {'status':False,'msg':"Name can't void"}
546                while True:
547                        newconfigpath = os.path.join(self.configpath,name + '.json')
548                        if not os.path.exists(newconfigpath):
549                                break
550                        name = name + "1"
551
552                data=unicode(json.dumps(config,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
553                f = open(newconfigpath,'w')
554                f.write(data)
555                f.close()
556                self.variable[name] = self.defaultmirrorinfo
557                return {'status':True,'msg':name}
558        #def new_mirror_config
559
560        def get_all_configs(self):
561                versions = os.listdir(self.configpath)
562                allconfigs = {}
563                for version in versions:
564                        configfile = os.path.join(self.configpath,version)
565                        f = open(configfile,'r')
566                        allconfigs[version.replace('.json','')] = json.load(f)
567                        f.close()
568                return {'status':True,'msg':allconfigs}
569        #def get_all_configs
570
571        def update_mirror_config(self,mirror,config):
572                configpath = os.path.join(self.configpath,mirror + ".json")
573
574                f=open(configpath,"w")
575
576                data=unicode(json.dumps(config,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
577                f.write(data)
578                f.close()
579
580                return {'status':True,'msg':'Updated config'}
581        #def update_mirror_config
582
583        def get_client_ip(self,ip):
584                return {'status':True,'msg':ip}
585        #def get_client_ip
586
587        def watch_update_finish(self,credentials,server):
588                self.watch_thread=threading.Thread(target=self._update,args=(credentials,server,))
589                self.watch_thread.daemon=True
590                self.watch_thread.start()
591               
592        #def watch_update_finish
593
594        def _watch_update_finish(self,credentials,server):
595                import time
596                import xmlrpclib as x
597                while True:
598                        try:
599                                time.sleep(240)
600                                c = x.ServerProxy('https://'+ server +':9779')
601                                result = c.is_alive(credentials,'MirrorManager')
602                                if not result:
603                                        self.stop_webserver()
604                                        break
605                        except:
606                                break
607        #def _watch_update_finish
608
609        def is_alive_get_mirror(self):
610                return {'status':self.get_mirror_thread.is_alive(),'msg':self.exportpercentage}
611        #def is_alive_get_mirror
612
613        def get_mirror(self,config_path):
614                self.get_mirror_thread = threading.Thread(target=self._get_mirror,args=(config_path))
615                self.get_mirror_thread.daemon = True
616                self.get_mirror_thread.start()
617        #def get_mirror
618
619        def _get_mirror(self,config_path):
620                self.get_mirror_process = pexpect.spawn("/usr/bin/debmirror --config-file="+config_path)
621                while True:
622                        try:
623                                self.get_mirror_process.expect('\n')
624                                line =self.get_mirror_process.before
625                                line1=line.strip("\n")
626                                if line1.startswith("[") and line1[5] == "]":
627                                        self.exportpercentage = (int(line1[1:4].strip()),self.get_mirror_process.exitstatus)
628                        except pexpect.EOF:
629                                        line1 = self.get_mirror_process.before
630                                        if line1 != "" and line1.startswith("[") and line1[5] == "]":
631                                                        self.exportpercentage=(int(line1[1:4].strip()),self.get_mirror_process.exitstatus)
632                                        self.get_mirror_process.close()
633                                        status = self.get_mirror_process.exitstatus
634                                        self.exportpercentage=(self.exportpercentage[0],status)
635                                        break
636                        except Exception as e:
637                                break
638        #def _get
Note: See TracBrowser for help on using the repository browser.