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

Last change on this file since 3690 was 3690, checked in by jrpelegrina, 2 years ago

WIP in clients checks

File size: 17.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.errorupgrade_token=os.path.join(self.processPath,'errorupgrade_token')
27                self.finalupgrade_token=os.path.join(self.processPath,'finalupgrade_token')
28
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
33                self.createLockToken()
34                self.n4d = xmlrpclib.ServerProxy('https://localhost:9779')
35                self.haveLliurexMirror = False
36                self.metapackageRef=[]
37                self.previousFlavours = []
38
39                self.getTargetMetapackage()
40                self.flavours = []
41                self.getPreviousFlavours()
42                if len(self.n4d.get_methods('MirrorManager')) > 0:
43                        self.haveLliurexMirror = True
44                self.prepareEnvironment()
45
46
47        def createLockToken(self):
48
49                if not os.path.exists(self.lockTokenPath):
50                        f=open(self.lockTokenPath,'w')
51                        f.close
52
53        def getPreviousFlavours(self):
54               
55                if os.path.exists(self.previousflavourspath):
56                        aux = open(self.previousflavourspath,'r')
57                        lines = aux.readlines()
58                        for x in lines:
59                                self.previousFlavours.append(x.strip())
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()
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                         
91                if len(self.previousFlavours)==0:
92                        self.getPreviousFlavours()
93               
94
95                self.writeDefaultSourceslist()
96                self.writeDefaultSourceslistMirror()
97                self.addSourcesListLliurex()
98
99                return self.targetMetapackage
100               
101
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):
111
112                f = open(os.path.join(self.processSourceslist,'default'),'w')
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))
116                f.close()
117
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
125        def prepareEnvironment(self):
126                '''
127                        This funcion delete all environment and rebuild environment
128
129                '''
130                self.cleanEnvironment()
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)
137
138                #self.writeDefaultSourceslist()
139                #self.writeDefaultSourceslistMirror()
140
141        def addSourcesListLliurex(self):
142               
143                #sourcesrefmirror=os.path.join(self.processSourceslist, 'default_mirror')
144
145                newsourcesfile=os.path.join(self.sourcesListPath,'sources.list')
146                extrasources=[]
147
148
149                if self.targetMetapackage=="lliurex-meta-client" or "client" in self.previousFlavours or "client" in self.metapackageRef:
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()
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
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           
192
193        def cleanEnvironment(self):
194                if os.path.exists(self.processPath):
195                        shutil.rmtree(os.path.join(self.processPath))
196
197                self.restoreOrigSourcesList()   
198
199        def cleanLliurexUpLock(self):
200
201                if os.path.exists(self.lockTokenPath):
202                        os.remove(self.lockTokenPath)
203
204        def updateCacheApt(self,options=""):
205                command = "LANG=C LANGUAGE=en apt-get update {options}".format(options=options)
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                '''
220                command = "LANG=C LANGUAGE=en apt-cache policy {package} {options}".format(package=package,options=options)
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')
237
238                if "client" in self.previousFlavours or "lliurex-meta-client"==self.targetMetapackage:
239                        sources=self.readSourcesList()
240                        if sources==0:
241                                sourceslistDefaultPath = os.path.join(self.processSourceslist,'default-mirror')
242
243
244                options = ""
245                if self.canConnectToLliurexNet():
246                        options = "-o Dir::Etc::sourcelist={sourceslistOnlyLliurex} -o Dir::Etc::sourceparts=/dev/null".format(sourceslistOnlyLliurex=sourceslistDefaultPath)
247
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                '''
267                self.updateCacheApt(options)
268                command = "LANG=C LANGUAGE=en apt-get install --force-yes --yes lliurex-up {options}".format(options=options)
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                '''
280                if self.haveLliurexMirror and ('server' in self.flavours or 'lliurex-meta-server'==self.targetMetapackage):
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                '''
289                if self.haveLliurexMirror and ('server' in self.flavours or 'lliurex-meta-server'==self.targetMetapackage):
290                        result = self.n4d.is_alive('','MirrorManager')
291                        return result['status']
292                return False
293
294        def clientCheckingMirrorIsRunning(self):
295
296                if self.targetMetapackage=="lliurex-meta-client" or "client" in self.previousFlavours or "client" in self.metapackageRef:
297
298                        client=xmlrpclib.ServerProxy('https://server:9779')
299                        result=client.is_alive('','MirrorManager')
300                        return result['status'] 
301
302                return False   
303
304        def getPercentageLliurexMirror(self):
305                '''
306                        return int | None
307                '''
308                if self.haveLliurexMirror and ('server' in self.flavours or 'lliurex-meta-server'==self.targetMetapackage):
309                        result = self.n4d.get_percentage('','MirrorManager',self.defaultMirror)
310                        if result['status']:
311                                return result['msg']
312                return None
313       
314        def checkFlavour(self):
315                '''
316                        return None|String
317                        If metapackages has been uninstalled, this function return
318                        package to must install. If return None, you are ok and don't need
319                        install anything.
320                '''
321                self.updateFlavoursList()
322                targetMetapackage = None
323                if 'None' in self.flavours:
324                        # get last flavour
325                        result = self.n4d.lliurex_version('','LliurexVersion','--history')
326                        if result[0]:
327                                history = [ x.strip().split('\t')[0].strip() for x in result[1].split('\n') ]
328                                history = [ x for x in history if not 'lliurex-meta-live' in x ]
329                                for x in reversed(history):
330                                        if x.startswith('-'):
331                                                targetMetapackage = x[2:]
332                                                break
333
334                if targetMetapackage !=None:
335                        self.saveTargetMetapackage(targetMetapackage)                           
336                return targetMetapackage
337
338        def canConnectToLliurexNet(self):
339                '''
340                        return Boolean
341                '''
342                # s =  socket.socket(socket.AF_INET, socket.SOCK_STREAM)
343                # host = socket.gethostbyname('lliurex.net')
344                # result = s.connect_ex((host, 80))
345                # s.close()
346                # if result:
347                #       return False
348                # return True
349
350                try:
351                        req=urllib2.Request("http://lliurex.net/xenial")
352                        res=urllib2.urlopen(req)
353                        return True
354                except:
355                        return False
356                               
357        def getLliurexVersionLliurexNet(self):
358                '''
359                        return dictionary => result
360                        result : {'installed':String,'candidate':String}
361                '''
362                sourceslistDefaultPath = os.path.join(self.processSourceslist,'default')
363                options = ""
364                if self.canConnectToLliurexNet():
365                        options = "-o Dir::Etc::sourcelist={sourceslistOnlyLliurex} -o Dir::Etc::sourceparts=/dev/null".format(sourceslistOnlyLliurex=sourceslistDefaultPath)
366                self.updateCacheApt(options)
367                return self.getPackageVersionAvailable('lliurex-version-timestamp',options)
368
369        def getLliurexVersionLocal(self):
370                self.updateCacheApt('')
371                return self.getPackageVersionAvailable('lliurex-version-timestamp','')         
372
373        def initActionsScript(self,arg):
374                #return 'run-parts --arg="initActions" ' + self.initActionsPath
375                return 'run-parts --arg=' +str(arg) + ' ' + self.initActionsPath
376
377
378        def preActionsScript(self):
379                return 'run-parts --arg="preActions" ' + self.preActionsPath
380
381        def postActionsScript(self):
382                return 'run-parts --arg="postActions" ' + self.postActionsPath
383
384
385        # def requiresInstallFlavour(self):
386               
387        #       flavourToInstall=None
388               
389        #       if not 'None' in self.previuosFlavours:
390        #               if self.previuosFlavours !=self.flavours:
391        #                       flavourToInstall=self.parseFlavourToInstall(self.previousFlavours)
392                                                                               
393        #       else:
394                       
395        #               if self.metapackageRef != self.flavours:
396        #                       flavourToInstall=self.parseFlavourToInstall(self.metapackageRef)
397                                                       
398
399        #       return flavourToInstall                                 
400
401               
402        # def parseFlavourToInstall(self,flavours):
403       
404        #       parse_flavour=""
405
406        #       for item in flavours:
407        #               if item != "edu":
408        #                       parse_flavour=parse_flavour + " " + "lliurex-meta-" + item
409
410        #       return parse_flavour
411                       
412        #def parseFlavourToInstall(
413
414
415        def installInitialFlavour(self,flavourToInstall,options=""):
416                '''
417                        Args :
418                                flavourToInstall String
419                                options String
420                        return dictionary => result
421                        result : {'returncode':Int,'stdout':String,'stderr':String}
422
423                        options are Apt options
424                       
425
426                        This function install lliurex-up
427                '''
428                command = "LANG=C LANGUAGE=en apt-get install --force-yes --yes " + flavourToInstall + "{options} ".format(options=options)
429                p = subprocess.Popen(command,shell=True,stdout=subprocess.PIPE)
430                poutput,perror = p.communicate()
431                return {'returncode':p.returncode,'stdout':poutput,'stderrs':perror}   
432
433        def getPackagesToUpdate(self):
434                '''
435                        packageInfo definition
436                        {
437                                'PACKAGENAME' : {
438                                                'install' : 'INSTALLEDVERSION',
439                                                'candidate' : 'CANDIDATEVERSION',
440                                                'icon' : 'ICONNAME',
441                                                'changelog' : 'CHANGELOGTEXT'
442                                }
443                        }
444                '''
445                self.packageInfo = {}
446                self.updateCacheApt("")
447                psimulate = subprocess.Popen('LANG=C LANGUAGE=en apt-get dist-upgrade -sV',shell=True,stdout=subprocess.PIPE)
448                rawoutputpsimulate = psimulate.stdout.readlines()
449                rawpackagestoinstall = [ aux.strip() for aux in rawoutputpsimulate if aux.startswith('Inst') ]
450                r = [ aux.replace('Inst ','') for aux in rawpackagestoinstall ]
451                for allinfo in r :
452                        self.packageInfo[allinfo.split(' ')[0]] = {}
453                        self.packageInfo[allinfo.split(' ')[0]]['raw'] = ' '.join(allinfo.split(' ')[1:])
454
455                for package in self.packageInfo:
456                        raw = self.packageInfo[package]['raw'].split(' ')
457                        if raw[0].startswith('['):
458                                self.packageInfo[package]['install'] = raw[0][1:-1]
459                                self.packageInfo[package]['candidate'] = raw[1][1:]
460                        elif raw[0].startswith('('):
461                                self.packageInfo[package]['install'] = None
462                                self.packageInfo[package]['candidate'] = raw[0][1:]
463                        self.packageInfo[package].pop('raw')
464                        #packageInfo[package]['changelog'] = os.path.join(self.changelogsPath,package)
465                        #os.system('LANG=C LANGUAGE=en apt-get changelog %s > %s%s'%(package,self.changelogsPath,package))
466                        #packageInfo[package]['icon'] =
467                return self.packageInfo
468
469
470        def checkIncorrectFlavours(self):
471               
472                self.incorrect_flavours=[]
473                count=0
474
475                for item in self.packageInfo:
476                        if item in self.flavourReference:
477                                self.incorrect_flavours.append(item)
478
479
480                if len(self.incorrect_flavours)>0:
481
482                        for item in self.incorrect_flavours:
483                                if self.targetMetapackage != None:
484                                        if item != self.targetMetapackage:
485                                                count=count+1
486                                else:
487                                        meta=item.split("-")[2]
488                                        if 'None' in self.previousFlavours:
489                                                if not meta in self.metapackageRef:
490                                                        count=count+1
491                                        else:           
492                                                if not meta in self.previousFlavours:
493                                                        count=count+1
494
495                if count>0:
496                        return True
497
498                else:
499                        return False   
500
501
502        def distUpgradeProcess(self):
503       
504                return 'apt-get dist-upgrade --yes --force-yes'
505
506
507
508        def checkErrorDistUpgrade(self):
509
510                count=0
511                error=False
512
513                if os.path.exists(self.errorpostaction_token):
514                        aux = open(self.errorpostaction_token,'r')
515                        lines = aux.readlines()
516                        for x in lines:
517                                if 'E: ' in x:
518                                        count=count+1
519                        aux.close()
520
521                if count==0:
522                        cmd='dpkg -l | grep "^i[^i]" >' + self.errorupgrade_token
523                        os.system(cmd)
524               
525                        if os.path.exists(self.errorupgrade_token):
526                                aux = open(self.errorupgrade_token,'r')
527                                lines = aux.readlines()
528                                aux.close()
529                       
530                                if len(lines)>0:
531                                        error=True
532                                        #log_msg="Dist-upgrade process ending with errors"
533                                        #self.log(log_msg)
534                                else:
535                                        j=0
536                                        cmd='apt-get dist-upgrade -sV >' + self.finalupgrade_token
537                                        os.system(cmd)
538                                        if os.path.exists(self.finalupgrade_token):
539                                                aux = open(self.finalupgrade_token,'r')
540                                                lines = aux.readlines()
541                                                aux.close()
542
543                                                for x in lines:
544                                                        if 'Inst' in x:
545                                                                j=j+1
546
547                                                if j>0:
548                                                        error=True             
549                else:
550                        error=True
551
552                return error   
553
554
555        def checkFinalFlavour(self):
556               
557                flavourToInstall=None
558               
559                self.targetMetapackage=self.checkFlavour()
560                if self.targetMetapackage!=None:
561                        #flavourToInstall=self.requiresInstallFlavour()
562                #else:
563                        flavourToInstall=self.targetMetapackage         
564
565                return flavourToInstall         
566
567
568        def installFinalFlavour(self,flavourToInstall):
569
570                return 'apt-get install ' + flavourToInstall + ' --yes --force-yes'
571               
572       
573
574if __name__ == '__main__':
575        x = LliurexUpCore()
Note: See TracBrowser for help on using the repository browser.