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

Last change on this file since 3719 was 3719, checked in by jrpelegrina, 4 years ago

Fix code

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