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

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

new functions and refactoring for lliurex-mirror gtk client

File size: 16.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                configpath = os.path.join(self.configpath, distro + ".json")
316                config = json.load(open(configpath,'r'))
317                config['ORIGS'][option] = url
318
319                f=open(configpath,"w")
320                data=unicode(json.dumps(config,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
321                f.write(data)
322                f.close()
323
324                self.build_debmirror_config(distro)
325                return {'status':True,'msg':'set orig'}
326        #def set_mirror_architecture
327
328        def get_option_update(self,distro):
329                configpath = os.path.join(self.configpath,distro + ".json")
330                config = json.load(open(configpath,'r'))
331                if not os.path.exists(configpath):
332                        return {'status':False,'msg':'not exists debmirror.conf to '+ distro }
333
334                if "CURRENT_UPDATE_OPTION" in config.keys():
335                        return {'status':True,'msg':config["CURRENT_UPDATE_OPTION"] }
336                       
337                return {'status':False,'msg':"debmirror.conf hasn't option update variable" }
338        #def set_option_update
339
340        def set_option_update(self,distro,option):
341                configpath = os.path.join(self.configpath, distro + ".json")
342                config = json.load(open(configpath,'r'))
343                config['CURRENT_UPDATE_OPTION'] = str(option)
344
345                f=open(configpath,"w")
346                data=unicode(json.dumps(config,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
347                f.write(data)
348                f.close()
349
350                self.build_debmirror_config(distro)
351                return {'status':True,'msg':'set update option'}
352        #def set_option_update
353
354        def get_percentage(self,distro):
355                if self.variable.has_key(distro):
356                        return {'status':True,'msg':self.variable[distro]['progress']}
357                else:
358                        return {'status':False,'msg':'this repo nos has been configured'}
359        #def get_percentage
360
361        def build_debmirror_config(self,distro):
362                template = self.tpl_env.get_template('debmirror.conf')
363                configpath = os.path.join(self.configpath,distro + ".json")
364                config = json.load(open(configpath,'r'))
365                string_template = template.render(config).encode('utf-8')
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 enable_webserver_into_folder(self,path):
372                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
373                s.bind(('localhost', 0))
374                addr, port = s.getsockname()
375                s.close()
376                self.webserverprocess = Process(target=self._enable_webserver_into_folder,args=(port,path,))
377                self.webserverprocess.start()
378                return {'status':True,'msg':port}
379        #enable_webserver_into_folder
380
381        def _enable_webserver_into_folder(self,port,path):
382                try:
383                        iface = '127.0.0.1'
384                        sock = (iface,port)
385                        proto = "HTTP/1.0"
386                        os.chdir(path)
387                        handler = SimpleHTTPRequestHandler
388                        handler.protocol_version = proto
389                        self.httpd = BaseHTTPServer.HTTPServer(sock,handler)
390                        self.httpd.serve_forever()
391                except Exception, e:
392                        return None
393        #_enable_webserver_into_folder
394
395        def stop_webserver(self):
396                if self.webserverprocess != None:
397                        self.webserverprocess.terminate()
398                return {'status':True,'msg':'Server stopped'}
399        #stop_webserver
400       
401        def set_checksum_validation(self,distro,status):
402                configpath = os.path.join(self.configpath, distro + ".json")
403                config = json.load(open(configpath,'r'))
404                config['CHK_MD5'] = status
405
406                f=open(configpath,"w")
407                data=unicode(json.dumps(config,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
408                f.write(data)
409                f.close()
410
411                self.build_debmirror_config(distro)
412                return {'status':True,'msg':'set checksum validation'}
413        #set_checksum_validation
414       
415        def get_checksum_validation(self,distro):
416
417                configpath = os.path.join(self.configpath,distro + ".json")
418                config = json.load(open(configpath,'r'))
419                if not os.path.exists(configpath):
420                        return {'status':False,'msg':'not exists debmirror.conf to '+ distro }
421                if "IGN_GPG" in config.keys():
422                        return {'status':True,'msg':config["CHK_MD5"] }
423
424                return {'status':False,'msg':"debmirror.conf hasn't orig variable" }
425        #get_checksum_validation
426       
427        def get_available_mirrors(self):
428                versions = os.listdir(self.configpath)
429                versions = [ version.replace('.json','') for version in versions if version.endswith('.json')]
430                return {'status':True,'msg':versions}
431
432        def stopupdate(self):
433                try:
434                        self.debmirrorprocess.terminate()
435                        return {'status':True,'msg':'debmirror stopped'}
436                except Exception as e:
437                        return {'status':False,'msg':str(e)}
438
439
440        def download_time_file(self,distro):
441               
442                configpath = os.path.join(self.configpath,distro + ".json")
443                config = json.load(open(configpath,'r'))
444                path=config["MIRROR_PATH"]
445                f="time-of-last-update"
446                dest=os.path.join(path,f)
447
448                orig_mirror=self.get_mirror_orig(distro)
449                url_mirror="http://"+os.path.join(orig_mirror['msg'],f)
450
451                return self.get_time_file(url_mirror,dest)
452
453        # # def download_time_file                     
454
455               
456        def get_time_file(self,url,dest):
457               
458                try:
459                        r=urlopen(url)
460                        f=open(dest,"wb")
461                        f.write(r.read())
462                        f.close()
463                        r.close()
464                        return {'status':True,'msg':dest + 'successfully downloaded.'}
465               
466                except Exception as e:
467                        return {'status':False,'msg':'Error downloading' + dest + ':' + str(e)}                 
468
469        # def get_time_file             
470
471        def is_mirror_update(self,distro):
472
473                configpath = os.path.join(self.configpath,distro + ".json")
474                config = json.load(open(configpath,'r'))
475                path=config["MIRROR_PATH"]
476                file_time_name="time-of-last-update"
477                file_local_mirror=os.path.join(path,file_time_name)
478
479               
480                if os.path.isfile(file_local_mirror):
481                        url_pool="http://"+os.path.join(config["ORIGS"]['1'],file_time_name)
482                        file_pool=os.path.join("/tmp",file_time_name)
483
484                        exist_file_pool=self.get_time_file(url_pool,file_pool)
485                        if exist_file_pool['status']:
486                                file_local_mirror_content=open(file_local_mirror,"r")
487                                file_local_miror_datetime=(file_local_mirror_content.readline().strip()).split("_")
488                                file_pool_content=open(file_pool,'r')
489                                file_pool_datetime=(file_pool_content.readline().strip()).split("_")
490                                file_local_mirror_content.close()
491                                file_pool_content.close()
492
493                                date_local_mirror=datetime.datetime.strptime(file_local_miror_datetime[0],"%Y/%m/%d")
494                                date_pool=datetime.datetime.strptime(file_pool_datetime[0],"%Y/%m/%d")
495
496                                if date_local_mirror==date_pool:
497                                        time_local_mirror=datetime.datetime.strptime(file_local_miror_datetime[1],"%H:%M")     
498                                        time_pool=datetime.datetime.strptime(file_pool_datetime[1],"%H:%M")
499
500                                        if time_local_mirror<time_pool:
501                                                return {'status':False,'msg':'Mirror not updated','action':'update'}
502                                        else:
503                                                return {'status':True,'msg':'Mirror is updated','action':'nothing'}
504
505                                elif date_local_mirror<date_pool:
506                                        return {'status':False,'msg':'Mirror not updated','action':'update'}
507                                else:
508                                        return {'status':True,'msg':'Mirror is updated','action':'nothing'}     
509                        else:
510                                return {'status':False,'msg':exist_file_pool['msg'],'action':'nothing'} 
511
512                else:
513                        return {'status':False,'msg':file_local_mirror + ' does not exist.','action':'nothing'}
514
515        # def is_mirror_update
516
517        def new_mirror_config(self,config):
518                name = config["NAME"].lower().strip()
519                import string
520                valid_chars = "-_.%s%s" % (string.ascii_letters, string.digits)
521                name = ''.join(c for c in name if c in valid_chars)
522
523                # Checks
524                if name == "":
525                        return {'status':False,'msg':"Name can't void"}
526                newconfigpath = os.path.join(self.configpath,name,'.json')
527                if os.path.exists(newconfigpath):
528                        return {'status':False,'msg':'Path for %s already exists'%config["NAME"]}
529
530                data=unicode(json.dumps(config,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
531                f = open(newconfigpath,'w')
532                f.write(data)
533                f.close()
534                return {'status':True,'msg':'Config has been created'}
535        #def new_mirror_config
536
537        def get_all_configs(self):
538                versions = os.listdir(self.configpath)
539                allconfigs = {}
540                for version in versions:
541                        configfile = os.path.join(self.configpath,version)
542                        f = open(configfile,'r')
543                        allconfigs[version] = json.load(f)
544                        f.close()
545                return {'status':True,'msg':allconfigs}
546        #def get_all_configs
547
548        def update_mirror_config(self,mirror,config):
549                configpath = os.path.join(self.configpath,distro + ".json")
550
551                f=open(configpath,"w")
552
553                data=unicode(json.dumps(config,indent=4,encoding="utf-8",ensure_ascii=False)).encode("utf-8")
554                f.write(data)
555                f.close()
556
557                return {'status':True,'msg':'Updated config'}
558        #def update_mirror_config
Note: See TracBrowser for help on using the repository browser.