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

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

WIP in fix final error upgrade info

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