#-*- coding: utf-8 -*- # Copyright 2010 Calculate Ltd. http://www.calculate-linux.org # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. __version__ = "2.2.29" __app__ = "calculate-builder" import os import stat import re import sys import traceback from os import path from cl_utils import process,pathJoin,getRunCommands,getTupleVersion,\ childMounts,_toUNICODE,isMount,typeFile, \ removeDir, pathJoin,listDirectory, getSquashList, \ readFile from subprocess import STDOUT,PIPE from cl_print import color_print from cl_datavars import DataVars from shutil import copy2 as copy_with_perm from cl_distr import IsoDistributive, DirectoryDistributive, \ DistributiveError from cl_template import template,iniParser from cl_vars_share import varsShare from time import sleep from cl_kernel_utils import KernelConfig,InitRamFs from server.utils import dialogYesNo import cl_overriding import hashlib from cl_lang import lang lang().setLanguage(sys.modules[__name__]) class printNoColor: def colorPrint(self,attr,fg,bg,string): sys.stdout.write(string) class BuilderError(Exception): """Installation Error""" builder_errors = "" def installExit(self,*args,**kwars): raise BuilderError(self.__class__.builder_errors) def overprintERROR(self,error): self.__class__.builder_errors += str(error) + "\n" def getBuilderErrors(self): return self.__class__.builder_errors def popBuilderErrors(self): res = self.__class__.builder_errors self.__class__.builder_errors = "" return res def getOverrideMethods(self): return self.installExit, self.overprintERROR cl_overriding.exit, cl_overriding.printERROR = \ BuilderError().getOverrideMethods() class DataVarsBuilder(DataVars): """Variable class for installation""" def importBuilder(self, **args): '''Get variables for builder''' # section name in calculate.env envSection = "builder" # import builder variables self.importData(envSection, ('cl_vars_builder','cl_fill_builder')) class cl_builder(color_print): """Primary class for image manipulation""" def __init__(self): self.clVars = None self.startMessage = "" self.clTempl = None self.force = False self.assembleIso = False self.envFile = '/etc/calculate/assemble.env' self.curProfile = None self.compression = "gzip" def setNoColor(self): self.color = False def initVars(self): """Primary initialization of variables""" self.clVars = DataVarsBuilder() self.clVars.importBuilder() self.clVars.flIniFile() def createDevNodes(self,directory): """Create nodes for dev http://dev.gentoo.org/~a3li/openrc.txt""" devPath = path.join(directory,"dev") # clean dev for pathname,dirs,files in os.walk(devPath,topdown=False): map(lambda x:os.unlink(path.join(pathname,x)),files) map(lambda x:os.unlink(x) if path.islink(x) else os.rmdir(x), map(lambda x:path.join(pathname,x),dirs)) for node,mode,dmode,major,minor in [("console",0600,stat.S_IFCHR,5,1), ("tty1",0600,stat.S_IFCHR,4,1), ("null",0666,stat.S_IFCHR,1,3), ("zero",0666,stat.S_IFCHR,1,5)]: nodePath = path.join(devPath,node) os.mknod(nodePath,mode|dmode,os.makedev(major,minor)) os.chmod(nodePath,mode) def applyTemplatesForSquash(self,directory): """Apply templates for root of system.""" #self.clVars.Set("cl_root_path","/", True) self.clVars.Set("cl_action","squash", True) self.clVars.Set("cl_chroot_path",directory, True) self.clTempl = template(self.clVars) dirsFiles = self.clTempl.applyTemplates() self.createDevNodes(directory) self.clTempl.closeFiles() if self.clTempl.getError(): raise BuilderError(self.clTempl.getError()) else: return dirsFiles def applyTemplatesForIso(self,directory): """Apply templates for root of system.""" #self.clVars.Set("cl_root_path","/", True) self.clVars.Set("cl_action","iso", True) chrootPath=pathJoin(directory, self.clVars.Get('cl_builder_squash_path')) self.clVars.Set("cl_chroot_path",chrootPath, True) self.clVars.Set("cl_root_path","..", True) self.clTempl = template(self.clVars) dirsFiles = self.clTempl.applyTemplates() self.clTempl.closeFiles() if self.clTempl.getError(): raise BuilderError(self.clTempl.getError()) else: return dirsFiles def printMessageForTest(self,message): """Print waiting message and OK or Error by func result""" message = "%s ..." % message self.printSUCCESS(message,printBR=False) self.startMessage = message def printByResult(self,result,failMessage=None): """Print message and result""" if self.startMessage: self.defaultPrint("\r") self.printLine((('greenBr',' * '), ('',self.startMessage), ), (('',''), ), 0, False) self.printRight(self.lenString(self.startMessage)+5,4) if result: self.printLine((('blueBr','['), ('greenBr',' ok '), ('blueBr',']')),[]) else: self.printLine((('blueBr','['), ('redBr',' !! '), ('blueBr',']')),[]) if failMessage: self.printERROR(failMessage) self.startMessage = "" def _getCommand(self,commandlist): return " ".join(map(lambda x:'"%s"'%x if " " in x else x,commandlist)) def runChroot(self,chroot,command): """Run command in chroot specified by cl_assemble_path""" try: envdict = {'CHROOT':"on"} envdict.update(os.environ) commandLine = ["chroot",chroot, "/bin/bash","-c",command] if self.clVars.Get('os_builder_arch_machine') != \ self.clVars.Get('os_arch_machine') and \ self.clVars.Get('os_arch_machine') == 'x86_64': commandLine = ["linux32"]+commandLine chrootCommand = process(*commandLine,stderr=STDOUT, stdout=PIPE,envdict=envdict) except KeyboardInterrupt: chrootCommand.kill() raise BuilderError( _("Interrupted while executing the command")+ ":\n %s"%self._getCommand(chrootCommand.command)) return chrootCommand def cleanNeedlessKernelData(self, distrPath): """Cleaning needless kernel files from /boot,/lib/modules,/usr/src""" self.printMessageForTest(_("Cleaning %s directory")% "/boot") bootDir = path.join(distrPath, "boot") modulesDir = path.join(distrPath, "lib/modules") reTrash =re.compile("^(?:config|initramfs|vmlinuz|System.map)") rightKernelFiles = \ [self.clVars.Get('cl_builder_initrd_install'), self.clVars.Get('cl_builder_kernel'), self.clVars.Get('cl_builder_kernel_config'), self.clVars.Get('cl_builder_kernel_systemmap')] delFiles = filter(lambda x: not x in rightKernelFiles, filter(reTrash.search, os.listdir(bootDir))) map(lambda x:os.unlink(pathJoin(bootDir,x)),delFiles) self.printByResult(True) self.printMessageForTest(_("Cleaning %s directory") % "/lib/modules") reKernelVer = re.compile(" version (\S+)\s",re.S) ftype = typeFile(magic=0x4).getMType resReg = reKernelVer.search(ftype(pathJoin(bootDir, self.clVars.Get('cl_builder_kernel')))) if resReg: kernelVersion = resReg.groups()[0] map(lambda x:removeDir(pathJoin(modulesDir,x)), filter(lambda x:x != kernelVersion, os.listdir(modulesDir))) self.printByResult(True) self.printMessageForTest(_("Cleaning %s directory")% "/usr/src") removeKernelSources = filter(lambda x:x and x != "/usr/src/linux", self.runChroot(distrPath,"qfile -o /usr/src/*")) map(lambda x:removeDir(pathJoin(distrPath,x)), removeKernelSources) self.printByResult(True) def dispatchConf(self, distrPath): """Execute dispatch conf for """ self.printMessageForTest(_("Execute '%s'")%"dispatch-conf") # run dispatch conf interactive os.system("chroot %s /bin/bash -c '/usr/sbin/dispatch-conf'"% distrPath) self.printByResult(True); def updateMan(self, distrPath): """Execute update man""" self.printMessageForTest(_("Execute '%s'")%"mandb") # run mandb self.runChroot(distrPath,'/usr/bin/mandb') self.printByResult(True); def updatePortage(self,builderPath): """Change branch to master and update portage""" if self.assembleIso: # restore resolv conf if needed resolvFile = "etc/resolv.conf" if not path.exists(path.join(builderPath,resolvFile)): if path.exists('/%s'%resolvFile): try: shutil.copyfile('/%s'%resolvFile, path.join(builderPath,resolvFile)) except: pass for gitName,gitDir in (('portage','/usr/portage'), ('overlay','/var/lib/layman/calculate')): gitHead = pathJoin(builderPath,gitDir,".git/HEAD") if "heads/develop" in readFile(gitHead) or \ "heads/update" in readFile(gitHead): self.printMessageForTest( _("Changing {repname} repository branch to '{branch}'"). format(repname=gitName, branch='master')) changeBranch = self.runChroot(builderPath, "cd %s;git pull;git checkout master;git pull"%gitDir) self.printByResult(changeBranch.success()) self.printMessageForTest(_("Updating portage")) updateMeta = self.runChroot(builderPath,"emerge --sync") self.printByResult(updateMeta.success()) def createUrandom(self,devPath): """ Create urandom and other needed devices """ createdDev = [] for node,mode,dmode,major,minor in [("urandom",0666,stat.S_IFCHR,1,9)]: try: nodePath = path.join(devPath,node) if not path.exists(nodePath): os.mknod(nodePath,mode|dmode,os.makedev(major,minor)) os.chmod(nodePath,mode) createdDev.append(node) except: pass def removeUrandom(self,devPath,createdDev): """ Remove urandom and other created devices """ for dev in createdDev: try: os.unlink(path.join(devPath,'dev',dev) except: pass def prepareSourceDistributive(self,distr): """Unmount all bind,proc mount points from source distribute""" mp = self.clVars.Get('cl_builder_path') createdDev = self.createUrandom(mp) self.dispatchConf(mp) self.updatePortage(mp) self.updateMan(mp) self.removeUrandom(mp,createdDev) # unmount all from source distribute mps = filter(lambda x:x!=mp,map(lambda x:x[1],childMounts(mp))) for target in sorted(mps, reverse=True): self.printMessageForTest(_("Unmounting %s")%(target[len(mp):])) umountProcess = process("umount",target) if umountProcess.failed(): raise BuilderError(_("Failed to unmount %s")%target) self.printByResult(True) distrPath = distr.convertToDirectory().getDirectory() self.clVars.Set('cl_builder_squash_path', os.path.basename(os.path.normpath(distrPath))) self.cleanNeedlessKernelData(distrPath) self.applyTemplatesForSquash(distrPath) # change make.profile curProfileFile = pathJoin(mp,"etc/make.profile") self.curProfile = os.readlink(curProfileFile) profile = self.clVars.Get('os_builder_profile') newProfile = None if not self.curProfile.endswith(profile): if profile.startswith("calculate"): newProfile = path.join("../var/lib/layman/calculate/profiles", profile) else: newProfile = path.join("../usr/portage/profiles", profile) os.unlink(curProfileFile) os.symlink(newProfile,curProfileFile) def restoreProfile(self): """Restore profile""" if not self.curProfile: return self.printMessageForTest(_("Restoring the system profile")) builderPath = self.clVars.Get('cl_builder_path') curProfileFile = pathJoin(builderPath,"etc/make.profile") profile = self.clVars.Get('os_builder_profile') newProfile = None if not self.curProfile.endswith(profile): os.unlink(curProfileFile) os.symlink(self.curProfile,curProfileFile) self.curProfile = None self.printByResult(True) def isoPrepacking(self,directory): self.printByResult(True) self.applyTemplatesForIso(directory) self.printMessageForTest(_("Releasing source data")) self.sourceDistr.close() self.printByResult(True) self.sourceDistr = None self.printMessageForTest(_("Creating the iso image")) def flashPrepacking(self,directory): self.printByResult(True) self.applyTemplatesForIso(directory) self.printMessageForTest(_("Releasing source data")) self.sourceDistr.close() self.sourceDistr = None def getFreeFor(self,pathname): """Get free for pathname""" dfProcess = process("/bin/df","-h",pathname) data = dfProcess.readlines() if len(data)>1: data = filter(lambda x:x,"\t".join(data[1:]).split()) if len(data)>3: return data[3] return "Unknown" def getParentMountFor(self,pathname): dfProcess = process("/bin/df","-h",pathname) data = dfProcess.readlines() if len(data)>1: data = filter(lambda x:x,"\t".join(data[1:]).split()) if len(data)>5: return data[5] return "" def getProcessByRoot(self,rootPath): def psax(x): try: rootPath = path.join('/proc',x,'root') cmdLinePath = path.join('/proc',x,'cmdline') if path.exists(rootPath) and path.exists(cmdLinePath): return {'pid':x, 'root':os.readlink(rootPath), 'cmdline':open(cmdLinePath,'r').read().strip()} except: pass return {'pid':x,'root':'','cmdline':''} procid = filter(lambda x:x.isdigit(),os.listdir('/proc')) return filter(lambda x:x['root'].startswith(rootPath), map(psax,procid)) def checkSquashCompress(self): configPath = path.join(self.clVars.Get('cl_builder_path'), 'boot', self.clVars.Get('cl_builder_kernel_config')) optCheck = {'xz':"CONFIG_SQUASHFS_XZ=y", 'lzma':"CONFIG_SQUASHFS_XZ=y", 'lzo':"CONFIG_SQUASHFS_LZO=y"}.get(self.compression,None) if not optCheck: return True res = filter(lambda x:optCheck in x, open(configPath,'r')) if not res: self.printERROR(_("ERROR") +": " + _("the kernel does not support '%s' " "compression format for squashfs")% self.compression) return False return True def printInfo(self): self.printSUCCESS(_("Creating an image of") + " Calculate Linux") self.defaultPrint("%s\n"%_("System information")) subname = self.clVars.Get('os_builder_linux_subname') subname = (" %s"%subname) if subname else "" self.printSUCCESS(_("System to be built")+": %s"% self.clVars.Get('os_builder_linux_name')+subname) self.printSUCCESS(_("System version")+": %s"% self.clVars.Get('os_builder_linux_ver')) self.printSUCCESS(_("Machine hardware name")+": %s"% self.clVars.Get('os_builder_arch_machine')) if self.clVars.Get('os_builder_locale_lang'): self.printSUCCESS(_("Locale by default")+": %s"% self.clVars.Get('os_builder_locale_lang')) if self.clVars.Get('os_builder_clock_timezone'): self.printSUCCESS(_("Timezone by default")+": %s"% self.clVars.Get('os_builder_clock_timezone')) self.printSUCCESS(_("Free disk space for iso building: %s")% self.getFreeFor(path.dirname(path.normpath( self.clVars.Get('cl_builder_iso_path'))))) self.printSUCCESS(_("Free disk space for the iso image: %s")% self.getFreeFor(path.dirname(path.normpath( self.clVars.Get('cl_builder_image'))))) self.defaultPrint("%s\n"%_("Performing pre-install checkups")) imagefile = self.clVars.Get('cl_builder_image') if imagefile: self.printSUCCESS(_("Image to be created at: %s")% imagefile) if not self.checkSquashCompress(): return False if path.exists(imagefile): self.printWARNING(_("WARNING") +": " + _("this image already exists") + ", "+ _("continuing the operation will overwrite it")) if self.getParentMountFor(path.dirname(path.normpath( imagefile))) == "/": if self.clVars.Get('os_root_type')=='livecd': self.printWARNING(_("WARNING") +": " + _("the image will be created on a " "temporary filesystem")) else: self.printWARNING(_("WARNING") +": " + _("the image will be created on the disk mounted to root")) warningProc = \ self.getProcessByRoot(self.clVars.Get('cl_builder_path')) if warningProc: self.printWARNING(_("WARNING") +": " + _("continuing the operation will kill the " "following processes:")) for procObj in warningProc: self.printWARNING( " %s"%" ".join( map(lambda x:x.replace("\n"," ").strip(), procObj['cmdline'].split('\x00')))[:75]) self.defaultPrint("\n") else: self.printWARNING("No path for image creating.") return True def printRescratchInfo(self): self.printSUCCESS(_("Rebuilding the live image of") + " Calculate Linux") self.defaultPrint("%s\n"%_("System information")) subname = self.clVars.Get('os_builder_linux_subname') subname = (" %s"%subname) if subname else "" self.printSUCCESS(_("System to be built")+": %s"% self.clVars.Get('os_builder_linux_name')+subname) self.printSUCCESS(_("System version")+": %s"% self.clVars.Get('os_builder_linux_ver')) self.printSUCCESS(_("Machine hardware name")+": %s"% self.clVars.Get('os_builder_arch_machine')) if self.clVars.Get('os_builder_locale_lang'): self.printSUCCESS(_("Locale by default")+": %s"% self.clVars.Get('os_builder_locale_lang')) if self.clVars.Get('os_builder_clock_timezone'): self.printSUCCESS(_("Timezone by default")+": %s"% self.clVars.Get('os_builder_clock_timezone')) self.printSUCCESS(_("Free disk space on flash: %s")% self.getFreeFor(path.normpath( self.clVars.Get('cl_builder_iso_path')))) self.defaultPrint("%s\n"%_("Performing pre-install checkups")) self.printSUCCESS(_("Image to be created in: %s")% self.clVars.Get('cl_builder_iso_path')) if not self.checkSquashCompress(): return False if self.clVars.Get('cl_builder_remove_squash'): self.printSUCCESS( _("The following squash images will be removed: %s\n")% ", ".join(self.clVars.Get('cl_builder_remove_squash'))) else: self.defaultPrint("\n") return True def checkVariables(self,rescratch=False): """Check values of variables""" buildDirectory = self.clVars.Get('cl_builder_iso_path') if len(filter(lambda x:"cl-builder" in x,getRunCommands()))>2: self.printERROR( _("Before proceeding, complete the cl-builder program")) return False if path.realpath(self.clVars.Get('cl_builder_path')) == "/": self.printERROR(_("The source system should not be '/'")) return False minver = "10.8" if getTupleVersion(self.clVars.Get('os_builder_linux_ver')) < \ getTupleVersion(minver): self.printERROR(_("Command supported only on system %s and higher") % minver ) return False elif not self.clVars.Get('os_builder_linux_shortname') in \ varsShare.dictNameSystem.keys(): self.printERROR(_("This distribution is not Calculate Linux")) return False elif not self.clVars.Get('cl_builder_kernel'): self.printERROR(_("Cannot detect the kernel")) return False elif not self.clVars.Get('cl_builder_initrd_install'): self.printERROR(_("Cannot detect initramfs")) return False elif not rescratch and path.exists(buildDirectory): self.printWARNING( _("Directory '%s' for iso building already exists")% buildDirectory) return False return True def _excludeList(self,sourceDirectory): """Get list for portage exclude by 'cl_builder_tree'""" if self.clVars.Get('cl_builder_tree') == 'off': portagePath = pathJoin(sourceDirectory,"usr/portage") return map(lambda x: path.join("usr/portage",x), filter(lambda x: not x in ( "eclass", "licenses", "profiles"), listDirectory(portagePath))) else: return [] def _isoLogic(self,force): isoFile = self.clVars.Get('cl_builder_image') buildDirectory = self.clVars.Get('cl_builder_iso_path') sourceDirectory = self.clVars.Get('cl_builder_path') bindDirectory = pathJoin(buildDirectory, self.clVars.Get('cl_builder_squash_path')) if not self.printInfo(): return False if not self.checkVariables(): return False excludeList = self._excludeList(sourceDirectory) self.targetDistr = IsoDistributive(isoFile,bdirectory=buildDirectory, exclude=excludeList, compress=self.compression) self.sourceDistr = DirectoryDistributive(sourceDirectory, mdirectory=bindDirectory) if not force: dialogMessage = _("Continue creating the system image") \ + " (yes/no)" try: dialogRes = dialogYesNo(dialogMessage) except KeyboardInterrupt: self.assembleIso = False self.printERROR(_("Image creating interrupted")) return False if dialogRes in (None,False): self.assembleIso = False self.printERROR(_("Image creating interrupted")) return False killMap = [(0,15,_("Terminating %s processes")), (4,9,_("Kill %s processes")), (2,0,None), (10,9,_("Killing again %s processes"))] listing = self.getProcessByRoot(self.clVars.Get('cl_builder_path')) for waitTime,killCode,msg in killMap: sleep(waitTime) listing = filter(lambda x:path.exists(path.join('/proc',x['pid'])), listing) if not listing: break if msg: self.printSUCCESS(msg%",".join(map(lambda x:x['pid'],listing)) +" ...") if not killCode: continue for i in listing: os.kill(int(i['pid']),killCode) self.printMessageForTest(_("Preparing data for the live image")) self.prepareSourceDistributive(self.sourceDistr) self.printByResult(True) self.targetDistr.prepareIso = self.isoPrepacking self.printMessageForTest(_("Creating the squash image")) self.targetDistr.installFrom(self.sourceDistr) self.printByResult(True) if self.clVars.Get('cl_builder_isohybrid_set') == 'on': self.printMessageForTest(_("Performing isohybrid")) processIsoHybrid = process("/usr/bin/isohybrid",isoFile) self.printByResult(processIsoHybrid.success()) self.printMessageForTest(_("Generating the list of installed packages")) self.printByResult(self.createPackageList(sourceDirectory, isoFile[:-4]+".list")) self.printMessageForTest(_("Creating the DIGESTS file")) self.printByResult(self.createDigest(isoFile,isoFile+".DIGESTS")) return True def removeVars(self,*varsname): """Remove vars specified by varsname""" res = True for var in varsname: res = res and self.clVars.Delete(var,True,'default','assemble') return bool(res) def _rescratchLogic(self,force=False): self.clVars.Set('cl_builder_iso_path','/mnt/flash',True) self.clVars.Get('cl_builder_remove_squash') buildDirectory = self.clVars.Get('cl_builder_iso_path') sourceDirectory = self.clVars.Get('cl_builder_path') bindDirectory = pathJoin(buildDirectory, self.clVars.Get('cl_builder_squash_path')) if not (self.clVars.Get('os_root_type') == 'livecd' \ and os.access(self.clVars.Get('cl_builder_iso_path'),os.W_OK)): self.printERROR( _("Your computer must be booted from flash in Builder mode")) return False if not self.printRescratchInfo(): return False if not self.checkVariables(rescratch=True): return False excludeList = self._excludeList(sourceDirectory) self.targetDistr = IsoDistributive(buildDirectory, bdirectory=buildDirectory, exclude=excludeList, compress=self.compression) self.sourceDistr = DirectoryDistributive(sourceDirectory, mdirectory=bindDirectory) if not force: dialogMessage = _("Continue rebuilding the flash live system") \ + " (yes/no)" try: dialogRes = dialogYesNo(dialogMessage) except KeyboardInterrupt: return True if dialogRes in (None,False): return True self.printMessageForTest(_("Removing old images")) oldImages = map(lambda x:pathJoin(buildDirectory,x), self.clVars.Get('cl_builder_remove_squash')) try: map(lambda x:os.unlink(x),oldImages) except (Exception,KeyboardInterrupt),e: raise BuilderError(_("Failed to remove old files")+":\n%s"%str(e)) self.printByResult(True) self.printMessageForTest(_("Preparing data for the live image")) self.prepareSourceDistributive(self.sourceDistr) self.printByResult(True) self.targetDistr.prepareIso = self.flashPrepacking self.printMessageForTest(_("Creating the squash image")) self.targetDistr.installFrom(self.sourceDistr) self.printByResult(True) self.printMessageForTest(_("Synchronizing")) processSync = process("/bin/sync") self.printByResult(processSync.success()) return True def makeIsoImage(self,force=False): return self.makeImage(self._isoLogic,force) def makeRescratch(self,force=False): return self.makeImage(self._rescratchLogic,force) def makeImage(self,logicFunc,*argv): """Make iso image by variables""" self.sourceDistr = None self.targetDistr = None error = None try: if not logicFunc(*argv): return False except (EOFError), e: error = e except (BuilderError,DistributiveError),e: error = e except (Exception),e: error = "" for i in apply(traceback.format_exception, sys.exc_info()): error += i except KeyboardInterrupt,e: self.printByResult(False) self.printWARNING("Interrupting the iso building") error = _("Iso building interrupted manually") if error: self.printByResult(False) try: if self.clTempl: self.clTempl.closeFiles() if self.sourceDistr: self.printMessageForTest(_("Releasing source data")) self.sourceDistr.close() self.printByResult(True) if self.targetDistr: self.printMessageForTest( _("Unmount the partition bound for assemble")) self.targetDistr.close() self.printByResult(True) if self.assembleIso: self.restoreMount() self.restoreProfile() except (BuilderError,DistributiveError),e: error = "%s\n%s" % (str(error),_("Unmounting error")) except KeyboardInterrupt,e: pass if error: self.printByResult(False) if error: for line in filter(lambda x: x,str(error).split('\n')): self.printERROR(line) self.printERROR(_("Failed to build the system")) return False self.printSUCCESS(_("System built successfully")) if self.assembleIso: os.system("cl-make -p %s -fT &>/dev/null"% self.clVars.Get('os_builder_profile')) return True def restoreMount(self): """Mount /proc,/sys, remote, /dev to chroot""" mountResources = (("/dev",None,None), ("/dev/shm",None,None), ("/dev/pts",None,None), (None,"-t proc","/proc"), (None,"-t sysfs","/sys"), ("/var/calculate/remote",None,None)) assemblePath = self.clVars.Get('cl_builder_path') for source,opts,target in mountResources: opts = opts or "-o bind" target = target or source self.printMessageForTest(_("Mounting %s")%(source or target)) target = pathJoin(assemblePath,target) if not path.exists(target): os.makedirs(target,mode=0755) if source == isMount(target): continue args = ["mount"]+opts.split()+[str(source).lower(),target] mountProcess = process(*args) if mountProcess.failed(): raise BuilderError(_("Failed to mount %(from)s to %(to)s")% {'from':source,'to':target}) self.printByResult(True) def setAssembleData(self,newprofile,printVars=None): """Get assemble data from assemble.env""" envData = iniParser(self.envFile) if newprofile == None: distros = self.clVars.Get('cl_builder_distro') if len(distros) == 1: newprofile = distros[0] newprofile = newprofile or "" # if $ at end of newprofile then it be at end of profile name if newprofile.endswith("$"): newprofile = newprofile[:-1] reProfile = re.compile('(^|/)%s$'% "/([^/]+/)*".join(newprofile.strip('/').split('/')),re.S|re.I) else: reProfile = re.compile('(^|/)%s(/|$)'% "/([^/]+/)*".join(newprofile.strip('/').split('/')),re.S|re.I) likeProfile = filter(reProfile.search, self.clVars.Get('cl_builder_distro')) if len(likeProfile) != 1: if printVars: return True if newprofile != "list": if not newprofile: self.printERROR(_("need specify '--profile'")) return False elif not likeProfile: self.printERROR(_("wrong value for '--profile'")) self.printERROR( _("specify '-p list' to print all available " "distributions to be built")) return False else: self.printERROR( _("specified value of '--profile' is ambiguous. " "Please specify profile with more precision.")) self.defaultPrint(_("Select a profile from")+":\n") else: likeProfile = self.clVars.Get('cl_builder_distro') self.defaultPrint( _("Available distributions to be built")+":\n") for profile in likeProfile: self.printSUCCESS(profile) return False newprofile = likeProfile[0] self.clVars.Set('os_builder_profile', newprofile, True) self.assembleIso = True self.clVars.Set('cl_builder_path', _toUNICODE(envData.getVar(newprofile, 'cl_assemble_path')).encode('utf-8'),True) linuxver = _toUNICODE(envData.getVar(newprofile, 'os_assemble_linux_ver')).encode('utf-8') if linuxver: self.clVars.Set('os_builder_linux_ver',linuxver ,True) self.clVars.Set('os_builder_linux_build','',True) return True def createPackageList(self,chrootdir,filename): """Create package list by chrootdir""" pkgdir = path.join(chrootdir,'var/db/pkg') if not path.exists(chrootdir): return False try: packageList = \ sorted(reduce(lambda x,y:x+map(lambda x:path.join(y,x), os.listdir(path.join(pkgdir,y))), os.listdir(pkgdir),[])) open(filename,'w').writelines(map(lambda x:"%s\n"%x,packageList)) except (IOError,OSError),e: return False return True def createDigest(self,filename,digestfile): """Create file with digest""" template = """# %(alg)s HASH\n%(digest)s %(filename)s\n""" try: open(digestfile,'w').writelines(map(lambda x:template%{ 'alg':x.upper(), 'digest': \ process("%ssum"%x,filename).read().partition(' ')[0], 'filename':path.basename(filename)},["md5","sha1"])) except: return False return True def setCompression(self,compress): """Set compression for squashsf in iso livecd""" compress_methods=getSquashList() if compress in compress_methods: self.compression = compress return True self.printERROR(_("'{0}' is not supported by {1}")\ .format(compress,"squashfs")) return False def setLive(self,live): """Set variable for activate patch init.d/calculate: discard --live from cl-install --startup""" if live: self.clVars.Set('cl_builder_live_set',"on",True) else: self.clVars.Set('cl_builder_live_set',"off",True) def setTimezone(self,timezone): """Set timezone""" if not path.exists(path.join("/usr/share/zoneinfo",timezone)) or \ timezone.startswith('/usr/share/zoneinfo'): self.printERROR(_("%s timezone is wrong")%timezone) return False else: self.clVars.Set('os_builder_clock_timezone',timezone,force=True) return True def setLang(self,lang): """Set all locale variable by specified lang""" if not lang in self.clVars.Get('os_lang'): self.printERROR(_("specified language %s is unsupported")%lang) return False self.clVars.Set('os_builder_locale_lang',lang, True) return True