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

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

first version of all functions working

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