source: lliurex-up/trunk/fuentes/python-lliurex-up/lliurex/lliurexup/__init__.py @ 4518

Last change on this file since 4518 was 4518, checked in by jrpelegrina, 3 years ago

Clean code of python lib

File size: 19.1 KB
Line 
1import xmlrpclib 
2import os
3import shutil
4import subprocess
5import socket
6import distutils.dir_util
7import urllib2
8import time
9
10
11class LliurexUpCore(object):
12        """docstring for LliurexUpCore"""
13        def __init__(self):
14                super(LliurexUpCore, self).__init__()
15                self.flavourReference=["lliurex-meta-server","lliurex-meta-client", "lliurex-meta-desktop", "lliurex-meta-music", "lliurex-meta-pyme", "lliurex-meta-infantil"] 
16                self.defaultMirror = 'llx16'
17                self.defaultVersion = 'xenial'
18                self.lockTokenPath="/var/run/lliurexUp.lock"
19                self.processPath = '/var/run/lliurex-up'
20                self.sourcesListPath='/etc/apt/'
21                self.changelogsPath = os.path.join(self.processPath,'changelogs')
22                self.processSourceslist = os.path.join(self.processPath,'sourceslist')
23                self.origsourcesfile=os.path.join(self.sourcesListPath,"sources.list")
24                self.origsourcesfileback=os.path.join(self.sourcesListPath,"lliurexup_sources.list")
25                self.targetMetapackagePath=os.path.join(self.processPath,"targetMetapackage")
26                self.previousflavourspath = os.path.join(self.processPath,'previousflavours')
27                self.errorpostaction_token=os.path.join(self.processPath,'errorpostaction_token')
28                self.errorfinalmetapackage_token=os.path.join(self.processPath,'errorfinalmetapackage_token')
29                self.errorupgrade_token=os.path.join(self.processPath,'errorupgrade_token')
30                self.finalupgrade_token=os.path.join(self.processPath,'finalupgrade_token')
31
32                self.initActionsPath='/usr/share/lliurex-up/initActions'
33                self.preActionsPath = '/usr/share/lliurex-up/preActions'
34                self.postActionsPath = '/usr/share/lliurex-up/postActions'
35
36                self.createLockToken()
37                self.retryN4d=True
38                self.n4dStatus=True
39                self.n4d = xmlrpclib.ServerProxy('https://localhost:9779')
40                self.checkN4dStatus()
41                self.haveLliurexMirror = False
42                self.metapackageRef=[]
43                self.previousFlavours = []
44
45               
46                self.getTargetMetapackage()
47                self.flavours = []
48                self.getPreviousFlavours()
49               
50                if self.n4dStatus:
51                        if len(self.n4d.get_methods('MirrorManager')) > 0:
52                                self.haveLliurexMirror = True
53                       
54                self.retryN4d=True
55                self.prepareEnvironment()
56
57
58        def createLockToken(self):
59
60                if not os.path.exists(self.lockTokenPath):
61                        f=open(self.lockTokenPath,'w')
62                        f.close
63
64        def getPreviousFlavours(self):
65               
66                if os.path.exists(self.previousflavourspath):
67                        aux = open(self.previousflavourspath,'r')
68                        lines = aux.readlines()
69                        for x in lines:
70                                self.previousFlavours.append(x.strip())
71                        aux.close()
72
73        def checkN4dStatus(self):
74       
75                checkStatus=True
76                cmd='systemctl status n4d.service 1>/dev/null'
77                result=os.system(cmd)
78
79                if result !=0:
80                        if self.retryN4d:
81                                self.retryN4d=False
82                                try: 
83                                        cmd='systemctl restart n4d.service 1>/dev/null'
84                                        restart=os.system(cmd)
85                                        time.sleep(5)
86                                        if restart ==0:
87                                                self.checkN4dStatus()
88                                        else:
89                                                self.n4dStatus=False
90                                                                                               
91                                except Exception as e:
92                                        self.n4dStatus=False
93                                       
94                                       
95                        else:
96                                self.n4dStatus=False
97                               
98                                                                               
99                else:           
100                        self.n4dStatus=True
101                       
102                               
103        def getTargetMetapackage(self):
104
105                if os.path.exists(self.targetMetapackagePath):
106                        aux = open(self.targetMetapackagePath,'r')
107                        lines = aux.readlines()
108                        for x in lines:
109                                self.metapackageRef.append(x.strip())
110                        aux.close()             
111
112        def saveTargetMetapackage(self,targetMetapackage):
113
114                aux=open(self.targetMetapackagePath,'w')
115                x=targetMetapackage.split("-")[2]
116                aux.write(x+"\n")
117                x="edu"
118                aux.write(x+"\n")
119                aux.close()
120
121
122        def checkInitialFlavour(self):
123
124                self.targetMetapackage=self.checkFlavour()
125                if len(self.metapackageRef)==0:
126                        self.getTargetMetapackage()
127               
128                self.metapackageRef=sorted(self.metapackageRef) 
129                         
130                if len(self.previousFlavours)==0:
131                        self.getPreviousFlavours()
132               
133
134                self.writeDefaultSourceslist()
135                self.writeDefaultSourceslistMirror()
136                self.addSourcesListLliurex()
137
138                return self.targetMetapackage
139               
140
141        def updateFlavoursList(self):
142               
143                self.flavours = [ x.strip() for x in self.n4d.lliurex_version('','LliurexVersion','-v')[1].split(',') ]
144                if len(self.flavours) > 0:
145                        aux = open(self.previousflavourspath,'w')
146                        for x in self.flavours:
147                                aux.write(x+"\n")
148                        aux.close()
149
150        def writeDefaultSourceslist(self):
151
152                f = open(os.path.join(self.processSourceslist,'default'),'w')
153                f.write('deb http://lliurex.net/{version} {version} main restricted universe multiverse\n'.format(version=self.defaultVersion))
154                f.write('deb http://lliurex.net/{version} {version}-updates main restricted universe multiverse\n'.format(version=self.defaultVersion))
155                f.write('deb http://lliurex.net/{version} {version}-security main restricted universe multiverse\n'.format(version=self.defaultVersion))
156                f.close()
157
158        def writeDefaultSourceslistMirror(self):
159               
160                f = open(os.path.join(self.processSourceslist,'default_mirror'),'w')
161                f.write('deb http://mirror/{version_mirror} {version} main restricted universe multiverse\n'.format(version_mirror=self.defaultMirror,version=self.defaultVersion))
162                f.write('deb http://mirror/{version_mirror} {version}-updates main restricted universe multiverse\n'.format(version_mirror=self.defaultMirror,version=self.defaultVersion))
163                f.write('deb http://mirror/{version_mirror} {version}-security main restricted universe multiverse\n'.format(version_mirror=self.defaultMirror,version=self.defaultVersion))
164                f.close()       
165
166        def prepareEnvironment(self):
167                '''
168                        This funcion delete all environment and rebuild environment
169
170                '''
171                self.cleanEnvironment()
172                if not os.path.exists(self.processPath):
173                        os.mkdir(self.processPath)
174                if not os.path.exists(self.processSourceslist):
175                        os.mkdir(self.processSourceslist)
176                if not os.path.exists(self.changelogsPath):
177                        os.mkdir(self.changelogsPath)
178
179                #self.writeDefaultSourceslist()
180                #self.writeDefaultSourceslistMirror()
181
182        def addSourcesListLliurex(self):
183               
184                #sourcesrefmirror=os.path.join(self.processSourceslist, 'default_mirror')
185
186                newsourcesfile=os.path.join(self.sourcesListPath,'sources.list')
187                extrasources=[]
188
189
190                if self.targetMetapackage=="lliurex-meta-client" or "client" in self.previousFlavours or "client" in self.metapackageRef:
191                        textsearch="/mirror/"+str(self.defaultMirror)
192                        sourcesref=os.path.join(self.processSourceslist, 'default_mirror')
193
194                else:
195                        textsearch="/lliurex.net/"+str(self.defaultVersion)
196                        sourcesref=os.path.join(self.processSourceslist, 'default')     
197
198                if os.path.exists(self.origsourcesfile):
199                        os.rename(self.origsourcesfile,self.origsourcesfileback)
200                        origsources=open(self.origsourcesfileback,'r')
201                        for line in origsources:
202                                if not textsearch in line:
203                                        extrasources.append(line.strip())
204                        origsources.close()
205                               
206                        if os.path.exists(sourcesref):
207                                shutil.copy(sourcesref,self.origsourcesfile)
208                                if len(extrasources)>0: 
209                                        newsourcesedit=open(newsourcesfile,'a')
210                                        for line in extrasources:
211                                                newsourcesedit.write(line+'\n')
212                                        newsourcesedit.close()
213                        else:
214                                os.rename(self.origsourcesfileback,self.origsourcesfile)                                       
215                       
216
217        def restoreOrigSourcesList(self):
218               
219                if os.path.exists(self.origsourcesfileback):
220                        os.rename(self.origsourcesfileback,self.origsourcesfile)
221
222        def readSourcesList(self):
223               
224                count=0
225                if os.path.exists(self.origsourcesfile):
226                        sources=open(self.origsourcesfile,'r')
227                        ref="/lliurex.net/"+str(self.defaultVersion)
228                        for line in sources:
229                                if ref in line:
230                                        if not "#" in line:
231                                                count=count+1
232                return count           
233
234        def cleanEnvironment(self):
235               
236                if os.path.exists(self.processPath):
237                        shutil.rmtree(os.path.join(self.processPath))
238
239                self.restoreOrigSourcesList()   
240
241        def cleanLliurexUpLock(self):
242
243                if os.path.exists(self.lockTokenPath):
244                        os.remove(self.lockTokenPath)
245
246        def updateCacheApt(self,options=""):
247               
248                command = "LANG=C LANGUAGE=en apt-get update {options}".format(options=options)
249                subprocess.Popen(command,shell=True).communicate()
250
251
252        def getPackageVersionAvailable(self,package,options=""):
253                '''
254                        Args :
255                                package String
256                                options String
257
258                        return dictionary => result
259                        result : {'installed':String,'candidate':String}
260
261                        Options are Apt options
262                '''
263                command = "LANG=C LANGUAGE=en apt-cache policy {package} {options}".format(package=package,options=options)
264                p = subprocess.Popen(command,shell=True,stdout=subprocess.PIPE)
265                installed = None
266                candidate = None
267                for line in iter(p.stdout.readline,""):
268                        stripedline = line.strip()
269                        if stripedline.startswith("Installed"):
270                                installed = stripedline.replace("Installed: ","")
271                        if stripedline.startswith("Candidate"):
272                                candidate = stripedline.replace("Candidate: ","")
273                return {"installed":installed,"candidate":candidate}
274
275        def isLliurexUpIsUpdated(self):
276                '''
277                        return Boolean
278                '''
279                sourceslistDefaultPath = os.path.join(self.processSourceslist,'default')
280
281                if "client" in self.previousFlavours or "lliurex-meta-client"==self.targetMetapackage:
282                        sources=self.readSourcesList()
283                        if sources==0:
284                                sourceslistDefaultPath = os.path.join(self.processSourceslist,'default_mirror')
285
286
287                options = ""
288                if self.canConnectToLliurexNet():
289                        options = "-o Dir::Etc::sourcelist={sourceslistOnlyLliurex} -o Dir::Etc::sourceparts=/dev/null".format(sourceslistOnlyLliurex=sourceslistDefaultPath)
290
291                self.updateCacheApt(options)
292                result = self.getPackageVersionAvailable('lliurex-up',options)
293
294                if result['installed'] != result['candidate']:
295                        return False
296                return True
297
298        def installLliurexUp(self,options=""):
299                '''
300                        Args :
301                                options String
302                        return dictionary => result
303                        result : {'returncode':Int,'stdout':String,'stderr':String}
304
305                        options are Apt options
306                       
307
308                        This function install lliurex-up
309                '''
310                self.updateCacheApt(options)
311                command = "LANG=C LANGUAGE=en DEBIAN_FRONTEND=noninteractive apt-get install --allow-downgrades --allow-remove-essential --allow-change-held-packages --yes lliurex-up {options}".format(options=options)
312                p = subprocess.Popen(command,shell=True,stdout=subprocess.PIPE)
313                poutput,perror = p.communicate()
314                return {'returncode':p.returncode,'stdout':poutput,'stderrs':perror}
315
316        def lliurexMirrorIsUpdated(self):
317                '''
318                        return None | dictionary => result
319                        result : {'status':Boolean,'msg':String,'action':String}
320                        result.msg : message of status
321                        result.action : Action to launch
322                '''
323                if self.haveLliurexMirror and ('server' in self.flavours or 'lliurex-meta-server'==self.targetMetapackage):
324                        result = self.n4d.is_update_available('','MirrorManager',self.defaultMirror)
325                        return result
326                return None
327
328        def lliurexMirrorIsRunning(self):
329                '''
330                        return Boolean
331                '''
332                if self.haveLliurexMirror and ('server' in self.flavours or 'lliurex-meta-server'==self.targetMetapackage):
333                        result = self.n4d.is_alive('','MirrorManager')
334                        return result['status']
335                return False
336
337        def clientCheckingMirrorIsRunning(self):
338
339                if self.targetMetapackage=="lliurex-meta-client" or "client" in self.previousFlavours or "client" in self.metapackageRef:
340                       
341                        try:
342                                client=xmlrpclib.ServerProxy('https://server:9779')
343                                result=client.is_alive('','MirrorManager')
344                                return {'ismirrorrunning':result['status'],'exception':False}
345                       
346                        except Exception as e:
347                                return {'ismirrorrunning':None,'exception':str(e)}     
348
349                return {'ismirrorrunning':False,'exception':False}     
350
351        def getPercentageLliurexMirror(self):
352                '''
353                        return int | None
354                '''
355                if self.haveLliurexMirror and ('server' in self.flavours or 'lliurex-meta-server'==self.targetMetapackage):
356                        result = self.n4d.get_percentage('','MirrorManager',self.defaultMirror)
357                        if result['status']:
358                                return result['msg']
359                return None
360       
361        def checkFlavour(self):
362                '''
363                        return None|String
364                        If metapackages has been uninstalled, this function return
365                        package to must install. If return None, you are ok and don't need
366                        install anything.
367                '''
368                self.updateFlavoursList()
369                targetMetapackage = None
370                if 'None' in self.flavours:
371                        # get last flavour
372                        result = self.n4d.lliurex_version('','LliurexVersion','--history')
373                        if result[0]:
374                                history = [ x.strip().split('\t')[0].strip() for x in result[1].split('\n') ]
375                                history = [ x for x in history if not 'lliurex-meta-live' in x ]
376                                for x in reversed(history):
377                                        if x.startswith('-'):
378                                                targetMetapackage = x[2:]
379                                                break
380
381                if targetMetapackage !=None:
382                        self.saveTargetMetapackage(targetMetapackage)                           
383                return targetMetapackage
384
385        def canConnectToLliurexNet(self):
386                '''
387                        return Boolean
388                '''
389                '''
390                s =  socket.socket(socket.AF_INET, socket.SOCK_STREAM)
391                host = socket.gethostbyname('lliurex.net')
392                result = s.connect_ex((host, 80))
393                s.close()
394                if result:
395                        return False
396                return True
397                '''
398                try:
399                        req=urllib2.Request("http://lliurex.net/xenial")
400                        res=urllib2.urlopen(req)
401                        return True
402                except:
403                        return False
404                               
405        def getLliurexVersionLliurexNet(self):
406                '''
407                        return dictionary => result
408                        result : {'installed':String,'candidate':String}
409                '''
410                sourceslistDefaultPath = os.path.join(self.processSourceslist,'default')
411                options = ""
412                if self.canConnectToLliurexNet():
413                        options = "-o Dir::Etc::sourcelist={sourceslistOnlyLliurex} -o Dir::Etc::sourceparts=/dev/null".format(sourceslistOnlyLliurex=sourceslistDefaultPath)
414                self.updateCacheApt(options)
415                return self.getPackageVersionAvailable('lliurex-version-timestamp',options)
416
417        def getLliurexVersionLocal(self):
418               
419                self.updateCacheApt('')
420                return self.getPackageVersionAvailable('lliurex-version-timestamp','')         
421
422        def initActionsScript(self,arg):
423               
424                #return 'run-parts --arg="initActions" ' + self.initActionsPath
425                return 'run-parts --arg=' +str(arg) + ' ' + self.initActionsPath
426
427
428        def preActionsScript(self):
429               
430                return 'run-parts --arg="preActions" ' + self.preActionsPath
431
432        def postActionsScript(self):
433               
434                return 'run-parts --arg="postActions" ' + self.postActionsPath
435
436        '''     
437        def requiresInstallFlavour(self):
438               
439                flavourToInstall=None
440               
441                if not 'None' in self.previuosFlavours:
442                        if self.previuosFlavours !=self.flavours:
443                                flavourToInstall=self.parseFlavourToInstall(self.previousFlavours)
444                                                                               
445                else:
446                       
447                        if self.metapackageRef != self.flavours:
448                                flavourToInstall=self.parseFlavourToInstall(self.metapackageRef)
449                                                       
450
451                return flavourToInstall                                 
452
453               
454        def parseFlavourToInstall(self,flavours):
455       
456                parse_flavour=""
457
458                for item in flavours:
459                        if item != "edu":
460                                parse_flavour=parse_flavour + " " + "lliurex-meta-" + item
461
462                return parse_flavour
463                       
464        #def parseFlavourToInstall
465        '''
466
467        def installInitialFlavour(self,flavourToInstall,options=""):
468                '''
469                        Args :
470                                flavourToInstall String
471                                options String
472                        return dictionary => result
473                        result : {'returncode':Int,'stdout':String,'stderr':String}
474
475                        options are Apt options
476                       
477
478                        This function install lliurex-up
479                '''
480                self.updateCacheApt(options)
481                command = "LANG=C LANGUAGE=en DEBIAN_FRONTEND=noninteractive apt-get install --yes --allow-downgrades --allow-remove-essential --allow-change-held-packages " + flavourToInstall + "{options} ".format(options=options)
482                p = subprocess.Popen(command,shell=True,stdout=subprocess.PIPE)
483                poutput,perror = p.communicate()
484               
485                if p.returncode!=0:
486                        command = "LANG=C LANGUAGE=en DEBIAN_FRONTEND=noninteractive apt-get install -f --yes --allow-downgrades --allow-remove-essential --allow-change-held-packages {options} ".format(options=options)
487                        p = subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
488                        poutput,perror = p.communicate()
489
490
491                return {'returncode':p.returncode,'stdout':poutput,'stderrs':perror}   
492
493        def getPackagesToUpdate(self):
494                '''
495                        packageInfo definition
496                        {
497                                'PACKAGENAME' : {
498                                                'install' : 'INSTALLEDVERSION',
499                                                'candidate' : 'CANDIDATEVERSION',
500                                                'icon' : 'ICONNAME',
501                                                'changelog' : 'CHANGELOGTEXT'
502                                }
503                        }
504                '''
505                self.packageInfo = {}
506                self.updateCacheApt("")
507                psimulate = subprocess.Popen('LANG=C LANGUAGE=en apt-get dist-upgrade -sV',shell=True,stdout=subprocess.PIPE)
508                rawoutputpsimulate = psimulate.stdout.readlines()
509                rawpackagestoinstall = [ aux.strip() for aux in rawoutputpsimulate if aux.startswith('Inst') ]
510                r = [ aux.replace('Inst ','') for aux in rawpackagestoinstall ]
511                for allinfo in r :
512                        self.packageInfo[allinfo.split(' ')[0]] = {}
513                        self.packageInfo[allinfo.split(' ')[0]]['raw'] = ' '.join(allinfo.split(' ')[1:])
514
515                for package in self.packageInfo:
516                        raw = self.packageInfo[package]['raw'].split(' ')
517                        if raw[0].startswith('['):
518                                self.packageInfo[package]['install'] = raw[0][1:-1]
519                                self.packageInfo[package]['candidate'] = raw[1][1:]
520                        elif raw[0].startswith('('):
521                                self.packageInfo[package]['install'] = None
522                                self.packageInfo[package]['candidate'] = raw[0][1:]
523                        self.packageInfo[package].pop('raw')
524                        #packageInfo[package]['changelog'] = os.path.join(self.changelogsPath,package)
525                        #os.system('LANG=C LANGUAGE=en apt-get changelog %s > %s%s'%(package,self.changelogsPath,package))
526                        #packageInfo[package]['icon'] =
527                return self.packageInfo
528
529
530        def checkIncorrectFlavours(self):
531               
532                self.incorrect_flavours=[]
533                count=0
534
535                for item in self.packageInfo:
536                        if item in self.flavourReference:
537                                self.incorrect_flavours.append(item)
538
539
540                if len(self.incorrect_flavours)>0:
541
542                        for item in self.incorrect_flavours:
543                                if self.targetMetapackage != None:
544                                        if item != self.targetMetapackage:
545                                                count=count+1
546                                else:
547                                        meta=item.split("-")[2]
548                                        if 'None' in self.previousFlavours:
549                                                if not meta in self.metapackageRef:
550                                                        count=count+1
551                                        else:           
552                                                if not meta in self.previousFlavours:
553                                                        count=count+1
554
555                if count>0:
556                        return True
557
558                else:
559                        return False   
560
561
562        def distUpgradeProcess(self):
563       
564                return 'apt-get dist-upgrade --yes --allow-downgrades --allow-remove-essential --allow-change-held-packages'
565
566
567
568        def checkErrorDistUpgrade(self):
569
570                countPostaction=0
571                countMetapackage=0
572                error=False
573
574                if os.path.exists(self.errorpostaction_token):
575                        aux = open(self.errorpostaction_token,'r')
576                        lines = aux.readlines()
577                        for x in lines:
578                                if 'E: ' in x:
579                                        countPostaction=countPostaction+1
580                        aux.close()
581
582                if countPostaction==0:
583
584                        if os.path.exists(self.errorfinalmetapackage_token):
585                                aux = open(self.errorfinalmetapackage_token,'r')
586                                lines = aux.readlines()
587                                for x in lines:
588                                        if 'E: ' in x:
589                                                countMetapackage=countMetapackage+1
590                                aux.close()
591                        if countMetapackage==0:
592                               
593                                cmd='dpkg -l | grep "^i[^i]" >' + self.errorupgrade_token
594                                os.system(cmd)
595                       
596                                if os.path.exists(self.errorupgrade_token):
597                                        aux = open(self.errorupgrade_token,'r')
598                                        lines = aux.readlines()
599                                        aux.close()
600                               
601                                        if len(lines)>0:
602                                                error=True
603                                                #log_msg="Dist-upgrade process ending with errors"
604                                                #self.log(log_msg)
605                                        else:
606                                                j=0
607                                                cmd='apt-get dist-upgrade -sV >' + self.finalupgrade_token
608                                                os.system(cmd)
609                                                if os.path.exists(self.finalupgrade_token):
610                                                        aux = open(self.finalupgrade_token,'r')
611                                                        lines = aux.readlines()
612                                                        aux.close()
613
614                                                        for x in lines:
615                                                                if 'Inst' in x:
616                                                                        j=j+1
617
618                                                        if j>0:
619                                                                error=True     
620                        else:
621                                error=True                                     
622                else:
623                        error=True
624
625                return error   
626
627        '''     
628        def checkFinalFlavour(self):
629               
630                flavourToInstall=None
631               
632                self.targetMetapackage=self.checkFlavour()
633                if self.targetMetapackage!=None:
634                        #flavourToInstall=self.requiresInstallFlavour()
635                #else:
636                        flavourToInstall=self.targetMetapackage         
637
638                return flavourToInstall
639        '''             
640
641
642        def installFinalFlavour(self,flavourToInstall):
643
644                return 'apt-get install ' + flavourToInstall + ' --yes  --allow-downgrades --allow-remove-essential --allow-change-held-packages'               
645       
646
647if __name__ == '__main__':
648        x = LliurexUpCore()
Note: See TracBrowser for help on using the repository browser.