#-*- coding: utf-8 -*- # Copyright 2008-2012 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. import os import sys from os import path import re import operator from calculate.lib.datavars import Variable,VariableError,ReadonlyVariable from calculate.lib.utils.common import getSupportArch,getTupleVersion from calculate.lib.utils.files import readLinesFile, listDirectory from calculate.lib.variables.linux import Linux from calculate.install.cl_distr import (Distributive,PartitionDistributive, ScratchPartitionDistributive,DistributiveError,FlashDistributive, MultiPartitions) from calculate.lib.cl_lang import setLocalTranslate setLocalTranslate('cl_install',sys.modules[__name__]) class DistroRepository(Linux): contentCache = {} marches = ['i686','x86_64'] extensiton = ['iso','tar.bz2','tar.gz','tar.7z','tar.lzma'] reDistName = re.compile(""" ^.*/(?P%(name)s) -(?P%(ver)s) -(?P%(march)s) .(?P%(ext)s)$""" % {'name':"[a-z0-9]+", 'ver':r"(\d+\.)*\d+", 'march':"|".join(marches), 'ext':"|".join(extensiton) }, re.X) def _getDistrInfo(self,filename): """Get information by distributive""" # if filename is directory if not path.isfile(filename): return Distributive().getInfo(filename) else: match = self.reDistName.match(filename) if not match: return {} distdic = match.groupdict() distdic["build"] = "" if "ver" in distdic: if re.match("^\d{8}$", distdic["ver"]): distdic["build"] = distdic["ver"] distdic["ver"] = "" return distdic def getImage(self,scratch,rootType,imagePath,march=None, shortName=None,linuxVer=None,linuxBuild=None): """Get image by parameters""" # exclude directory distributive for flash and scratch install if scratch == "on" or rootType == "flash": discardType = ["dir"] else: discardType = [] return self.getBestDistributive(imagePath, march=march, shortname=shortName, discardType=discardType, version=linuxVer, build=linuxBuild) def _getAvailableShortnames(self,dirs): """Get available distributives shortnames""" distros = filter(lambda x:x, map(self.reDistName.search,self._getAvailableDistributives(dirs))) return sorted(list(set(map(lambda x:x.groupdict()['name'],distros)))) def opcompareByString(self,buf): if buf: reOp = re.compile("^(!=|=|==|<=|>=|>|<)?(\d+.*)$") res = reOp.search(buf) if res: return ({'!=':operator.ne, '=':operator.eq, '==':operator.eq, '>=':operator.ge, '<=':operator.le, '<':operator.lt, '>':operator.gt}.get(res.group(1),operator.eq), res.group(2)) else: return operator.eq,buf return None,None def _getAvailableDistributives(self,dirs,system=None,shortname=None,march=None, version=None,build=None): """Get all distributives by filter""" def systemByName(name): return self.dictNameSystem.get(name.upper(),"") verCmp, version = self.opcompareByString(version) if version: version = getTupleVersion(version) buildCmp, build = self.opcompareByString(build) if build and build.isdigit(): build = int(build) def distfilter(dist): d = self._getDistrInfo(dist) if not d: return False # check filter conditions if system and systemByName(d['name']) != system: return False if not "name" in d or not "ver" in d: return False if shortname and d['name'].lower() != shortname.lower(): return False if march and d['march'] != march: return False if version and not verCmp(getTupleVersion(d['ver']), version): return False if build and "build" in d and (not d['build'].isdigit() or not buildCmp(int(d['build']),build)): return False return True def listdistr(pathname): if path.exists(path.join(pathname,'etc/make.profile')) or \ path.exists(path.join(pathname,'livecd')) or \ pathname.startswith('/dev/'): return [pathname] else: # discard inner directories return filter(lambda x:not path.isdir( path.join(pathname,x)), listDirectory(pathname)) # get lists files in directories allFiles = map(lambda x: map(lambda y: path.join(x,y), listdistr(x)), dirs) # filter distributives return filter(distfilter, # join files lists to one list reduce(lambda x,y: x + y, allFiles, [])) def extcomparator(self,*exts): """Compare extensions""" mapExts = {'iso':0, 'flash':-1, 'isodir':-2, 'partdir':-3, 'dir':-4} return cmp(mapExts.get(exts[0],-4),mapExts.get(exts[1],-4)) def sortdistrfunc(self,x,y): """Func of comparing two distributive""" ver1, ver2 = x[1].get('ver',""), y[1].get('ver',"") if ver1 and ver2 and ver1 != "0" and ver2 != "0" and ver1 != ver2: return cmpVersion(ver1,ver2) build1 = getTupleVersion(x[1].get('build',"")) build2 = getTupleVersion(y[1].get('build',"")) if build1 != build2: return cmp(build1,build2) else: ext1 = x[1].get('ext',"") ext2 = y[1].get('ext',"") return self.extcomparator(ext1,ext2) def getAvailableDristibutives(self,dirs,system=None,shortname=None, march=None, version=None, build=None,discardType=[]): """Get list available distributives""" if shortname: shortname = shortname.lower() availDistrs = self._getAvailableDistributives(dirs,system,shortname, march,version, build) availDistrs = filter(lambda x:x[1] and "ext" in x[1] and not x[1]["ext"] in discardType, map(lambda x:(x,self._getDistrInfo(x)), availDistrs)) return map(lambda x:x[0], sorted(availDistrs,self.sortdistrfunc,reverse=True)) def getBestDistributive(self,dirs,system=None,shortname=None,march=None, version=None, build=None,discardType=[]): """Get the actualest distributive""" availDistrs = self.getAvailableDristibutives(dirs,system,shortname, march,version,build,discardType) if availDistrs: return availDistrs[0] else: return None def _findLatestFile(self,dirs,reMatch,keyfunc): """Find latest file in dirs, which match by reMatch, comparable part get by keyfunc""" existsdirs = filter(path.exists,dirs) listimgs = reduce(lambda x,y:x + \ map(lambda x:reMatch.search(path.join(y,x)), listDirectory(y)), existsdirs,[]) listimgs = filter(lambda x:x, listimgs) if listimgs: return max(listimgs,key=keyfunc).group() return "" def getBestStage(self,dirs,march=None,hardened=None): """Get latest stage by march""" if march: march = {'x86_64':'amd64'}.get(march,march) else: march = "[^-]+" if hardened is None: hardened = "(?:-hardened)?" elif hardened == True: hardened = "-hardened" elif hardened == False: hardened = "" reStage = re.compile(r'^.*/stage3-%s%s-(\d+)\.tar\.bz2$'% (march,hardened),re.S) return self._findLatestFile(dirs,reStage,lambda x:x.groups()[0]) class VariableClImage(ReadonlyVariable): """ System image for installation """ type = "object" def get(self): """Get image file from distributive repository""" if self.Get('cl_action') != 'system': return Distributive.fromFile('/') filename = self.Get('cl_image_filename') try: if filename: filename = Distributive.fromFile(filename) except DistributiveError as e: return "" return filename def humanReadable(self): filename = self.Get('cl_image') if filename: return filename.getType() return filename class VariableClImageFilename(Variable,DistroRepository): """ Distributive image filename """ type = 'file' element = 'file' opt = ['--iso'] def init(self): self.label = _("Installation image") self.help = _("ISO image for installation") def get(self): if self.Get('cl_action') != 'system': return "" arch = self.Get('cl_image_arch_machine') or self.Get('os_arch_machine') shortname = self.Get('cl_image_linux_shortname') or \ self.Get('os_linux_shortname') ver = self.Get('cl_image_linux_ver') or None build = self.Get('cl_image_linux_build') or None return self.getImage(self.Get('os_install_scratch'), self.Get('os_install_root_type'), self.Get('cl_image_path'), arch,shortname,ver,build) def check(self,isoimage): """Set image file""" imageData = Distributive().getInfo(isoimage) if not("name" in imageData and imageData.get('build','') and \ "march" in imageData): raise VariableError(_("Wrong image file")) def humanImageName(self,distroinfo,filepath): distroinfo['name'] = distroinfo['name'].upper() return "{name} {march} {build} ({filepath})".format( filepath=filepath,**distroinfo) def choice(self): scratch = self.Get('os_install_scratch') rootType = self.Get('os_install_root_type') imagePath = self.Get('cl_image_path') if scratch == "on" or rootType == "flash" or \ self.Get('cl_install_type') == 'flash': discardType = ["dir"] else: discardType = [] distros = self.getAvailableDristibutives(imagePath, discardType=discardType) return map(lambda x:(x,self.humanImageName(self._getDistrInfo(x),x)), distros) class VariableClImageArchMachine(Variable,DistroRepository): """ Filter by architecture """ type = 'choice' opt = ['--march'] available_arch = ["i686","x86_64"] def init(self): self.label = _("Preferred processor architecture") self.help = _("select the processor architecture") def set(self,march): if march == "auto": march = getSupportArch()[-1] return march def choice(self): return self.available_arch + ["auto"] class VariableClImageLinuxShortname(Variable,Linux,DistroRepository): """ Filter by shortname """ type = 'choice' opt = ['--os','-s'] def init(self): self.label = _("Distributive") self.help = _("select the operation system") def choice(self): return self.dictLinuxName.keys() class VariableClImageLinuxVer(Variable,DistroRepository): """ Filter by version """ value = "" def init(self): self.label = _("Version") self.help = _("select the operation system by version") class VariableClImageLinuxBuild(Variable,DistroRepository): """ Filter by build """ value = "" def init(self): self.label = _("Build") self.help = _("select the operation system by build") class VariableClImagePath(ReadonlyVariable): """ Image search path """ type = "list" def get(self): # if current distributive is live if self.Get('os_root_type') == "livecd": # if builder from flash then this source path '/mnt/flash' # may be this path will be '/mnt/builder' for install # modified system if self.Get('os_scratch') == "on" and path.exists('/mnt/flash'): livedistr = ['/mnt/flash'] # if system boot with kernel param 'docache' elif path.exists('/mnt/squash'): livedistr = ['/mnt/livecd'] # standard livecd else: livedistr = ['/mnt/cdrom'] else: livedistr = [] # search all partition for source installation distributive rootDev = self.Get('os_install_root_dev') livedistr += \ map(lambda x:x[0], filter(lambda x:" live" in x[1] and x[0] != rootDev, zip(self.Get('os_disk_dev'), self.Get('os_disk_content')))) # add to standard path return filter(path.exists, ['/var/calculate/remote/linux', '/var/calculate/linux'] + livedistr) class VariableClTarget(ReadonlyVariable): """ Target distributive """ type = "object" def get(self): listVars = ['os_install_disk_dev', 'os_install_disk_mount', 'os_install_disk_format', 'os_install_disk_perform_format', 'os_install_disk_part', 'os_install_disk_id'] rootLabel = "{short}-{ver}".format( short=self.Get('os_install_linux_shortname'), ver=self.Get('os_install_linux_ver')) osInstallScratch = self.isTrue(self.Get('os_install_scratch')) mapDevId = dict(self.ZipVars('os_disk_dev','os_disk_id')) disk, mount, fileSystem, isFormat, partTable,systemId = \ self.Select(listVars, where='os_install_disk_mount', eq='/',limit=1) if not systemId or mapDevId.get(disk,'') == systemId: systemId = None if osInstallScratch: return ScratchPartitionDistributive(disk,mdirectory='/mnt/install', check=True,fileSystem=fileSystem, isFormat=self.isTrue(isFormat), systemId=systemId, partitionTable=partTable) elif self.Get('os_install_root_type')=="flash": return FlashDistributive(disk,mdirectory="/mnt/install", check=True, fileSystem=fileSystem, isFormat=isFormat, systemId=systemId, partitionTable=partTable) else: target = PartitionDistributive(disk,mdirectory='/mnt/install', check=True,fileSystem=fileSystem, rootLabel=rootLabel, isFormat=self.isTrue(isFormat), systemId=systemId, partitionTable=partTable) multiPartition = None diskData = self.Select(listVars, where='os_install_disk_mount', ne='/') bindData = self.Select(['os_install_bind_path', 'os_install_bind_mountpoint'], where='os_install_bind_mountpoint', ne='') if diskData or bindData: multiPartition = MultiPartitions() target.multipartition = multiPartition for disk,mount,fileSystem,isFormat,partTable,systemId in diskData: if not systemId or mapDevId.get(disk,'') == systemId: systemId = None multiPartition.addPartition(dev=disk, mountPoint=mount, fileSystem=fileSystem, isFormat=self.isTrue(isFormat), systemId=systemId, partitionTable=partTable) for source,dest in bindData: multiPartition.addPartition(dev=source, mountPoint=dest, fileSystem='bind', isFormat=False, systemId=None, partitionTable='') return target