You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
calculate-utils-3-install/install/cl_distr.py

1480 lines
58 KiB

#-*- 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.
from os import path
from random import choice
import string
import os
from time import sleep
import re
import sys
import operator
from shutil import copyfile,copytree
from subprocess import Popen,PIPE,STDOUT
from calculate.lib.utils.files import (runOsCommand,isMount,removeDir,
typeFile,pathJoin,process,getRunCommands,
getProgPath,listDirectory,checkUtils)
from calculate.lib.utils.common import getTupleVersion,cmpVersion
from calculate.lib.utils.device import (detectDeviceForPartition,
getUdevDeviceInfo, countPartitions)
from calculate.lib.cl_vars_share import varsShare
from calculate.lib.cl_template import _terms
from calculate.lib.cl_fill import fillVars
import threading
from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_install',sys.modules[__name__])
class SignalInterrupt:
__interruptProcessObjs = []
__sigint = False
def setSignalInterrupt(self):
"""Handler "SIGINT"""
if SignalInterrupt.__sigint is False:
sys.stdout.write("\b\b")
# killed processes
while (SignalInterrupt.__interruptProcessObjs):
process = SignalInterrupt.__interruptProcessObjs.pop()
if hasattr(process, "pipe") and hasattr(process.pipe,"kill"):
process.pipe.kill()
SignalInterrupt.__sigint = True
def getSignalInterrupt(self):
return SignalInterrupt.__sigint
def addInterruptProcess(self, process):
SignalInterrupt.__interruptProcessObjs.append(process)
class Spinner(threading.Thread):
stopSignal = threading.Event()
sequence= map(lambda x:x,iter("/-\\|"))
write = sys.stdout.write
selfthread = None
def setWriteFunc(self,writeFunc):
Spinner.write = writeFunc
def init(self,checkFunction=None,interval=0.1):
self.curpos = 0
self.checkFunction = checkFunction
self.interval = interval
Spinner.stopSignal.clear()
def stop(self):
Spinner.stopSignal.set()
if Spinner.selfthread:
Spinner.selfthread.join(2)
Spinner.selfthread = None
#Spinner().write('\n')
Spinner().write('\b')
def run(self):
self.write(self.sequence[-1])
Spinner.selfthread = self
while not Spinner.stopSignal.isSet():
if self.checkFunction and self.checkFunction():
self.write("\b%s"%self.sequence[self.curpos])
self.curpos += 1
if self.curpos >= len(self.sequence):
self.curpos = 0
sleep(self.interval)
class DistributiveError(Exception):
"""Error for distributive operations"""
pass
class DistributiveRepository:
varsShare = varsShare()
contentCache = {}
marches = ['i686','x86_64']
extensiton = ['iso','tar.bz2','tar.gz','tar.7z','tar.lzma']
reDistName = re.compile("""
^.*/(?P<name>%(name)s)
-(?P<ver>%(ver)s)
-(?P<march>%(march)s)
.(?P<ext>%(ext)s)$""" %
{'name':"[a-z0-9]+",
'ver':r"(\d+\.)*\d+",
'march':"|".join(marches),
'ext':"|".join(extensiton)
}, re.X)
def __init__(self,directories=[]):
self.dirs = directories
def ini_to_dict(self,filename):
"""Convert ini values to dict"""
if os.access(filename,os.R_OK):
return dict(map(lambda x: x.strip().rpartition('=')[0::2],
open(filename,'r')))
else:
return {}
def _getfromcontent(self,filename,addFunc=None):
"""Get info from content"""
origfilename = filename
if filename in DistributiveRepository.contentCache:
return DistributiveRepository.contentCache[filename].copy()
varsShare = self.varsShare
distr = None
# may be directory is isodir (directory which contains iso image)
extname = "isodir"
try:
distr = IsoDistributive(filename)
if not distr.probe() and filename.startswith('/dev'):
distr.close()
distr = None
distr = PartitionDistributive(filename)
filename = distr.convertToDirectory().directory
except Exception,e:
extname = "dir"
d = self.ini_to_dict(path.join(filename,
'etc/calculate/calculate.ini'))
if not d or not "march" in d:
if path.exists(path.join(filename,'lib64')):
d['march'] = 'x86_64'
else:
d['march']= 'i686'
if d:
d['ext'] = extname
d["name"] = varsShare.getShortnameByMakeprofile(filename) or \
varsShare.getShortnameByIni(filename) or \
varsShare.detectOtherShortname(filename) or \
"Linux"
d['ver'] = \
varsShare.getVersionFromMetapackage(filename,d["name"]) or \
varsShare.getVersionFromCalculateIni(filename) or "0"
reOsLinuxBuild = re.compile("^os_linux_build\s*=\s*(\S+)\s*$")
os_linux_build = \
map(lambda x:x.groups()[0],
filter(lambda x:x,
map(reOsLinuxBuild.search,
reduce(lambda x,y:x+y,
map(lambda x:open(x,"r").readlines(),
filter(path.exists,
[path.join(filename,"etc/calculate/calculate2.env")])),[]))))
if os_linux_build:
d['build'] = os_linux_build[-1]
else:
d['build'] = ""
if addFunc:
d = addFunc(filename,d)
if distr:
distr.close()
DistributiveRepository.contentCache[origfilename] = d
return d.copy()
def _getdistrinfo(self,filename):
"""Get information by distributive"""
varsShare = self.varsShare
# if filename is directory
if not path.isfile(filename):
return self._getfromcontent(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 _getAvailableShortnames(self):
"""Get available distributives shortnames"""
distros = filter(lambda x:x,
map(self.reDistName.search,self._getAvailableDistributives()))
return sorted(list(set(map(lambda x:x.groupdict()['name'],distros))))
def _getAvailableDistributives(self,system=None,shortname=None,march=None,
version=None,op_compare=None):
"""Get all distributives by filter"""
if op_compare is None:
op_compare = operator.eq
if version:
version = getTupleVersion(version)
def distfilter(dist):
d = self._getdistrinfo(dist)
if not d:
return False
# check filter conditions
if system and self.system(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 op_compare(getTupleVersion(d['ver']), version):
return False
return True
def listdistr(pathname):
if path.exists(path.join(pathname,'etc/make.profile')):
return [pathname]
elif path.exists(path.join(pathname,'livecd')):
return [pathname]
elif 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)),
# discard not exists distrib directories
filter(lambda x: os.access(x,os.R_OK),
self.dirs))
# filter distributives
return filter(distfilter,
# join files lists to one list
reduce(lambda x,y: x + y,
allFiles, []))
def getDistributiveByFile(self,filename):
"""Get Distributive object by filename"""
# MAGIC_COMPRESS 0x000004 Check inside compressed files
tf = typeFile(magic=0x6)
ftype = tf.getMType(filename)
if "block special" in ftype:
return IsoDistributive(filename)
if "ISO 9660 CD-ROM" in ftype:
return IsoDistributive(filename)
elif "7-zip" in ftype or \
"POSIX tar archive" in ftype:
return ArchiveDistributive(filename)
elif "Squashfs filesystem" in ftype:
return SquashDistributive(filename)
elif path.isdir(filename):
if path.exists(path.join(filename,"livecd")):
return IsoDistributive(filename)
else:
return DirectoryDistributive(filename)
else:
raise DistributiveError(_("Wrong distribution") + " '%s':\n%s"\
%(filename,ftype))
def extcomparator(self,*exts):
avexts = ("iso","isodir","dir")
vals = []
for i in exts:
if i in avexts:
vals.append(avexts.index(i))
else:
vals.append(-1)
return cmp(vals[0],vals[1])
term = _terms()
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,build2 = self.term._convertVers(x[1].get('build',""),
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 getBestDistributive(self,system=None,shortname=None,march=None,
version=None,discardType=[], op_compare=None):
"""Get the actualest distributive"""
availDistrs = self._getAvailableDistributives(system,shortname,
march,version,
op_compare)
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))
availDistrs = sorted(availDistrs,self.sortdistrfunc,reverse=True)
if availDistrs:
return availDistrs[0][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,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(self.dirs,reStage,lambda x:x.groups()[0])
class Distributive(object, SignalInterrupt):
"""Distributive object. Parent object for all distributive."""
mountError = _("Failed to mount") + " %s:\n%s"
reLive = re.compile(r"^live[^.]*\.squashfs(\.(\d+))?$",re.S)
flagSpinner=True
def __init__(self, parent=None):
self.childs = []
# if specified parent
if parent:
# save parent type for correct resource release
self.parent = parent
# append this object as child to specified parent
parent.childs.append(self)
else:
self.parent = None
def detach(self):
"""Detach child distributive from parent.
At example: ArchiveDistributive create child DirectoryDistributive by
unpacking to temporary directory and at close method remove it. If the
removing directroy do not need, then need call detach in
DirectoryDistributive object
dist1 = ArchiveDistributive(file="/os.tar.bz2",mdirectory="/tmp/test")
dist2 = dist1.convertToDirectory()
dist2.detach()
dist1.close()
...
"""
self.parent = None
def close(self):
"""Release all child distributive and release himself.
Need call this method at end work with object for right releasing
resources.
Example:
dist1 = PartitionDistributive(partition="/dev/sda2")
dist2 = dist1.convertToDirectory()
dist1.close()
"""
# close all child
if self.childs:
for child in reversed(self.childs):
# check detach
if child.parent:
child.close()
self.childs = []
# if has parent
if self.parent:
self.parent.releaseChild(self)
self.parent = None
def releaseChild(self,child):
"""Method of release child state of distributive
At example: PartitionDistributive may be DirectoryDistributive by
mounting it to directory. But at end this directory must be
unmounted."""
pass
def convertToDirectory(self):
"""Default c raise error about impossible convert object"""
raise DistributiveError(_("Failed to convert") + " '%s' "\
%self.__class__.__name__ + _("to") +\
" '%s'" %"DirectoryDistributive")
# def __del__(self):
# """Uncomment this method for automaticaly release all distributive
# instance"""
# self.close()
def runOsCommand(self, *argv, **kwarg):
res, mes = runOsCommand(*argv,**kwarg)
mes = "\n".join(map(lambda x: x.strip(), mes))
return res,mes
def getDirectory(self):
"""Get directory which contains distro"""
return self.convertToDirectory().directory
def getBootDirectory(self):
"""Get directory which contains boot"""
return path.join(self.getDirectory(),"boot")
def _makeDirectory(self,pathname):
"""Make directory and parent.
If directory exists then return False else True"""
try:
parent = path.split(path.normpath(pathname))[0]
if not path.exists(parent):
self._makeDirectory(parent)
else:
if path.exists(pathname):
return False
os.mkdir(pathname)
return True
except Exception, e:
raise DistributiveError(_("Failed to create the directory") +
" '%s':\n%s" %(pathname,str(e)))
except KeyboardInterrupt, e:
self.setSignalInterrupt()
raise DistributiveError(_("Failed to create the directory") +
" '%s':\n%s" %(pathname,str(e)))
def _removeDirectory(self,directory):
"""Remove directory and files contained in it"""
try:
removeDir(directory)
except Exception, e:
raise DistributiveError(_("Failed to remove the directory from") +\
" '%s':\n%s" %(directory,str(e)))
except KeyboardInterrupt, e:
self.setSignalInterrupt()
raise DistributiveError(_("Failed to remove the directory from") +\
" '%s':\n%s" %(directory,str(e)))
def _copyfile(self,infile,outfile):
try:
copyfile(infile,outfile)
except (Exception),e:
raise DistributiveError(_("Failed to copy") + " '%s' to '%s':\n%s"\
%(infile,outfile,str(e)))
def _copytree(self,indir,outdir):
try:
copytree(indir,outdir,symlinks=True)
except Exception ,e:
raise DistributiveError(_("Failed to copy") + " '%s' to '%s':\n%s"\
%(indir,outdir,str(e)))
except KeyboardInterrupt, e:
self.setSignalInterrupt()
raise DistributiveError(_("Failed to copy") + " '%s' to '%s':\n%s"\
%(indir,outdir,str(e)))
def rsync(self,fromdir,todir,hideSpin=False):
"""Copy files from 'fromdir' directory to 'todir' directory"""
if self.flagSpinner and not hideSpin:
def checkRsync():
try:
return len(filter(lambda x:"rsync\x00-a\x00-x" in x,
getRunCommands()))>2
except:
return False
spin = Spinner()
spin.init(checkFunction=checkRsync,interval=0.6)
spin.start()
rsyncCmd = varsShare().getProgPath('/usr/bin/rsync')
if not rsyncCmd:
raise DistributiveError(_("Failed to find '%s' command")%"rsync")
try:
rsyncProcess = process(rsyncCmd, "-a", "-x",
"%s/"%fromdir,"%s/"%todir,stderr=STDOUT)
self.addInterruptProcess(rsyncProcess)
res = rsyncProcess.success()
errmes = rsyncProcess.read()
except Exception,e:
res = False
errmes = str(e)
except KeyboardInterrupt:
self.setSignalInterrupt()
if self.flagSpinner:
spin.stop()
raise KeyboardInterrupt()
if self.flagSpinner and not hideSpin:
spin.stop()
if res is True:
return True
else:
if "No space left on device" in errmes:
errmes = "No space left on device"
raise DistributiveError(_("Failed to copy files from") +\
" '%s' " %fromdir + _("to") +\
" '%s':\n%s" %(todir,errmes))
def _mountToBind(self,srcDirectory,destDirectory):
"""Mount srcDirectory to destDirectory"""
mount = process('/bin/mount',"-o","bind",srcDirectory,destDirectory,
stderr=STDOUT)
if mount.success():
return True
else:
try:
os.rmdir(destDirectory)
except:
pass
raise DistributiveError(self.mountError%(srcDirectory,mount.read()))
def performFormat(self):
pass
def formatPartition(self,format=""):
pass
def rndString(self):
"""Get random string with len 8 char"""
return "".join([choice(string.ascii_letters+string.digits)
for i in xrange(0,8)])
def _getSquashNum(self,reMatch):
if reMatch.groups()[1] and reMatch.groups()[1].isdigit():
return int(reMatch.groups()[1])
else:
return 0
def _getLastLive(self,directory):
"""Get last live squashfs image from directory"""
squashfiles = filter(lambda x:x,
map(self.reLive.search,
listDirectory(directory)))
if squashfiles:
return max(squashfiles, key=self._getSquashNum).group()
return None
def _mountToDirectory(self,file,directory,mountopts="",count=2):
"""Mount squashfs to directory"""
NO_SUCH_DEVICE = 2816
if isMount(directory):
raise DistributiveError(_("Failed to mount to the directory: %s\n")\
%directory+ _("Directory already mounted"))
mountopts = filter(lambda x:x,
mountopts.split(" "))
mountProcess = process('/bin/mount',file,directory,*mountopts,
stderr=STDOUT)
if mountProcess.success():
return True
else:
#2816 code return by mount if device is absent (update /dev by udev)
# try mount 3 times with interval 0.5 second
if mountProcess.returncode() == NO_SUCH_DEVICE and count:
sleep(0.5)
mountProcess.close()
return self._mountToDirectory(file,directory,mountopts,count-1)
try:
self._removeDirectory(directory)
except:
pass
raise DistributiveError(self.mountError%(file,mountProcess.read()))
def _umountDirectory(self,directory):
"""Umount directory"""
if isMount(directory):
for wait in [0,0.5,2,5]:
sleep(wait)
processUmount = process('/bin/umount',directory,stderr=STDOUT)
if processUmount.success():
return True
raise DistributiveError(_("Failed to umount") + " %s:\n%s"%
(directory,processUmount.read()))
else:
return True
def _getMntDirectory(self,directory):
"""Get directory name, which will use for mounting or unpacking
If queried name is not free then to name append random string
"""
newDirectoryName = directory
while path.exists(newDirectoryName):
newDirectoryName = "%s.%s"%(directory,self.rndString())
return newDirectoryName
class DirectoryDistributive(Distributive):
def __init__(self,directory,parent=None,mdirectory=None):
Distributive.__init__(self,parent=parent)
self.directory = directory
self.mdirectory = mdirectory
if not parent:
self._makeDirectory(self.directory)
def bindDirectory(self,mdirectory):
for child in self.childs:
if isinstance(child,DirectoryDistributive) and \
mdirectory in child.directory:
return child
mdirectory = self._getMntDirectory(mdirectory)
self._makeDirectory(mdirectory)
self._mountToBind(self.directory,mdirectory)
return DirectoryDistributive(mdirectory,parent=self)
def releaseChild(self,child):
"""Remove child Directory distributive"""
if isinstance(child,DirectoryDistributive):
self._umountDirectory(child.directory)
self._removeDirectory(child.directory)
child.directory = None
def convertToDirectory(self):
if self.mdirectory:
return self.bindDirectory(self.mdirectory)
else:
return self
def performFormat(self):
"""Format for directory - removing all files"""
execStr = '/bin/rm -rf --one-file-system %s'%self.directory
res,errmes = self.runOsCommand(execStr)
if res == 0:
return True
else:
raise DistributiveError(_("Failed to format the partition") +
" %s:\n%s"%(dev,errmes))
self._makeDirectory(self.directory)
def installFrom(self, source):
"""Install distributive to directory from source distributive"""
if isinstance(source,ArchiveDistributive):
source.unpackTo(self.directory)
else:
# get source distributive as directory distributive
dFrom = source.convertToDirectory()
# copy distributive from source to this
self.rsync(dFrom.directory,self.directory)
class DataPartition:
"""Data partition"""
dev = None
mountPoint = None
fileSystem = "reiserfs"
isFormat = False
systemId = None
partitionTable = None
class MultiPartitions:
"""Data partition list"""
listPartitions = []
def addPartition(self, **argv):
"""Add partition in data partition list"""
dictDataPart = reduce(lambda x,y:\
x.update({y:getattr(DataPartition,y)}) or x,
filter(lambda x: not x.startswith('_'),
DataPartition.__dict__),{})
updateAttrData = filter(lambda x: x[1]!=None, dictDataPart.items())
defaultAttr = []
for attrName, attrValue in updateAttrData:
if not attrName in argv.keys():
defaultAttr.append(attrName)
argv[attrName] = attrValue
if set(argv.keys()) != set(dictDataPart.keys()):
notFoundAttr = set(dictDataPart.keys()) - set(argv.keys())
if notFoundAttr:
raise DistributiveError(_("The following attributes "
"(%s) are not specified")\
%", ".join(map(lambda x:"DataPartition.%s"%x, notFoundAttr)))
unnecessaryAttr = (set(dictDataPart.keys()) ^ set(argv.keys())) -\
set(dictDataPart.keys())
if unnecessaryAttr:
raise DistributiveError(_("Failed to use attributes (%s)")\
%", ".join(map(lambda x:"DataPartition.%s"%x, unnecessaryAttr)))
else:
partObj = DataPartition()
for attr, value in argv.items():
if attr in defaultAttr:
continue
setattr(partObj,attr,value)
self.listPartitions.append(partObj)
def getSystemId(self):
"""Get systemID for change [None,82,...]"""
return map(lambda x: x.systemId, self.listPartitions)
def getPartitionTable(self):
"""Get systemID for change [dos,gpt,...]"""
return map(lambda x: x.partitionTable, self.listPartitions)
def getIsFormat(self):
"""Get list is format [True,...]"""
return map(lambda x: x.isFormat, self.listPartitions)
def getFileSystems(self):
"""Get list file systems ["reiserfs",...]"""
return map(lambda x: x.fileSystem, self.listPartitions)
def getPartitions(self):
"""Get list partition ["/dev/sda",...]"""
return map(lambda x: x.dev, self.listPartitions)
def getMountPoints(self):
"""Get list mount point ["/boot",...]"""
return map(lambda x: x.mountPoint, self.listPartitions)
class PartitionDistributive(Distributive):
formatUtilities = { 'ext2':'/sbin/mkfs.ext2 %s %s',
'ext3':'/sbin/mkfs.ext3 %s %s',
'ext4':'/sbin/mkfs.ext4 %s %s',
'jfs':'/sbin/mkfs.jfs %s -f %s',
'reiserfs':'/sbin/mkfs.reiserfs %s -f %s',
'btrfs':'/sbin/mkfs.btrfs %s %s',
'nilfs2':'/sbin/mkfs.nilfs2 %s %s',
'xfs':'/sbin/mkfs.xfs %s -f %s',
'vfat':'/usr/sbin/mkfs.vfat %s -F 32 %s',
'ntfs-3g':'/usr/sbin/mkfs.ntfs %s -FQ %s',
'ntfs':'/usr/sbin/mkfs.ntfs %s -FQ %s',
'swap':'/sbin/mkswap %s'
}
labelForUtilities = { 'ext2':'-L %s',
'ext3':'-L %s',
'ext4':'-L %s',
'btrfs':'-L %s',
'nilfs2':'-L %s',
'jfs':'-L %s',
'reiserfs':'-l %s',
'xfs':'-L %s',
'vfat':'-n %s',
'ntfs-3g':'-L %s',
'ntfs':'-L %s',
}
formatId = { 'ext2' : '83',
'ext3' : '83',
'ext4' : '83',
'reiserfs' : '83',
'btrfs' : '83',
'nilfs2' : '83',
'jfs' : '83',
'xfs' : '83',
'vfat' : '0b',
'swap' : '82'
}
formatIdGpt = { 'ext2' : '0700',
'ext3' : '0700',
'ext4' : '0700',
'reiserfs' : '0700',
'btrfs' : '0700',
'nilfs2' : '0700',
'jfs' : '0700',
'xfs' : '0700',
'vfat' : '0700',
'swap' : '8200'
}
def __init__(self,partition,parent=None,mdirectory="/mnt/calculate",
check=False,multipartition=None,flagRemoveDir=True,
fileSystem="reiserfs", isFormat=True,systemId=None,
rootLabel="Calculate", partitionTable=None):
"""Initialize partition distributive
mdirectory - directory for mount
check - check partition name and raise DistributiveError if partition
has bad name
"""
Distributive.__init__(self,parent=parent)
self.partition = partition
self.fileSystem = fileSystem
self.mdirectory = mdirectory
self.multipartition = multipartition
self.flagRemoveDir = flagRemoveDir
self.isFormat = isFormat
self.DirectoryObject = None
self.systemId = systemId
self.rootLabel = rootLabel
self.partitionTable = partitionTable
def _mountPartition(self,partition,directory,opts=""):
"""Mount partition to directory"""
self._makeDirectory(directory)
if "ntfs" in opts:
source_dir = isMount(partition)
if source_dir:
self._mountToBind(source_dir,directory)
return
self._mountToDirectory(partition,directory,opts)
def _umountPartition(self,directory):
"""Umount partition and remove directory"""
self._umountDirectory(directory)
if self.flagRemoveDir:
self._removeDirectory(directory)
def releaseChild(self,child):
"""Umount child Directory distributive"""
if isinstance(child,DirectoryDistributive):
self._umountPartition(child.directory)
child.directory = None
def _mountBind(self,srcDirectory,destDirectory):
"""Mount directory to directory"""
self._makeDirectory(destDirectory)
self._makeDirectory(srcDirectory)
self._mountToBind(srcDirectory,destDirectory)
def postinstallMountBind(self):
"""Mount bind mount point and create mount dirs"""
if self.multipartition and self.DirectoryObject:
mulipartDataBind = filter(lambda x: x[2]=="bind",
self.getMultipartData())
dirObj = self.DirectoryObject
mdirectory = dirObj.directory
for srcDir, destDir, fileSystem, isFormat, partTable \
in mulipartDataBind:
realDestDir = pathJoin(mdirectory, destDir)
realSrcDir = pathJoin(mdirectory, srcDir)
self._mountBind(realSrcDir, realDestDir)
isFormat = False
partObj = PartitionDistributive(realSrcDir, flagRemoveDir=False,
fileSystem=fileSystem,
isFormat=isFormat,
parent=dirObj)
DirectoryDistributive(realDestDir,parent=partObj)
def getMultipartData(self):
"""Get multipartition data"""
mulipartData = zip(self.multipartition.getPartitions(),
self.multipartition.getMountPoints(),
self.multipartition.getFileSystems(),
self.multipartition.getIsFormat(),
self.multipartition.getPartitionTable())
return mulipartData
def convertToDirectory(self):
"""Convert partition to directory by mounting"""
mdirectory = self.mdirectory
for child in self.childs:
if isinstance(child,DirectoryDistributive) and \
mdirectory in child.directory:
return child
mdirectory = self._getMntDirectory(mdirectory)
self._mountPartition(self.partition,mdirectory)
dirObj = DirectoryDistributive(mdirectory,parent=self)
if self.multipartition:
mulipartDataNotBind = filter(lambda x: x[2]!="bind",
self.getMultipartData())
for dev, mountPoint, fileSystem, isFormat, partTable\
in mulipartDataNotBind:
if fileSystem!="swap":
realMountPoint = pathJoin(mdirectory, mountPoint)
self._mountPartition(dev,realMountPoint,"-t %s"%fileSystem)
partObj = PartitionDistributive(dev, flagRemoveDir=False,
fileSystem=fileSystem,
isFormat=isFormat,
parent=dirObj)
if fileSystem!="swap":
DirectoryDistributive(realMountPoint,parent=partObj)
self.DirectoryObject = dirObj
return dirObj
def formatAllPartitions(self):
"""Format all partitions"""
FS,DEV,NEEDFORMAT,NEWID,PARTTABLE = 0,1,2,3,4
# get all information to matrix
dataPartitions = zip(self.multipartition.getFileSystems() +\
[self.fileSystem],
self.multipartition.getPartitions() + \
[self.partition],
self.multipartition.getIsFormat() + \
[self.isFormat],
self.multipartition.getSystemId() + \
[self.systemId],
self.multipartition.getPartitionTable() + \
[self.partitionTable])
# get partition which need format
formatPartitions = map(lambda x: (x[FS],x[DEV]),
filter(lambda x: x[NEEDFORMAT] and x[FS]!="bind",
dataPartitions))
# format all get partition
for fileSystem, dev in formatPartitions:
if fileSystem=="swap":
self.formatSwapPartition(dev)
else:
if dev == self.partition:
self.formatPartition(dev, format=fileSystem,
label=self.rootLabel)
else:
self.formatPartition(dev, format=fileSystem)
# change system id for partitions
changeidPartitions = map(lambda x: (x[NEWID],x[DEV],x[PARTTABLE]),
filter(lambda x: x[NEWID],
dataPartitions))
for systemid, dev, partTable in changeidPartitions:
self.changeSystemID(dev,systemid,partTable)
return True
def formatPartition(self, dev,format="reiserfs",label=""):
"""Format partition"""
if not format in self.formatUtilities:
raise DistributiveError(
_("Specified format of '%s' not supported")%format)
if dev in map(lambda y: y.split(" ")[0],
filter(lambda x: x.startswith("/"),
open("/proc/swaps"))):
raise DistributiveError(\
_("Failed to format partition %s, because it is used as swap")%
dev)
if isMount(dev):
raise DistributiveError(
_("Failed to format partition %s, because it is mounted")%dev)
if not os.access(dev,os.W_OK):
raise DistributiveError(_("Failed to format the partition") +
" %s:\n%s"%(dev,_("Permission denied")))
if label:
labelStr = self.labelForUtilities.get(format,"")
if labelStr:
labelStr = labelStr%label
else:
labelStr = ""
execStr = self.formatUtilities[format]%(labelStr,dev)
res,errmes = self.runOsCommand(execStr)
if res == 0:
return True
else:
raise DistributiveError(_("Failed to format the partition") +
" %s:\n%s"%(dev,errmes))
def performFormat(self):
"""Perform format for all partition of this distributive"""
if self.multipartition:
self.formatAllPartitions()
elif self.isFormat:
self.formatPartition(self.partition,format=self.fileSystem,
label=self.rootLabel)
def changeSystemID(self,dev,systemid,parttable):
"""Change partition id, specified by systemid"""
deviceName = detectDeviceForPartition(dev)
if deviceName is None:
raise DistributiveError(
_("Failed to determine the parent device for %s")%dev)
# device hasn't any partition
elif deviceName == "":
return True
fdiskProg, gdiskProg = checkUtils('/sbin/fdisk','/usr/sbin/gdisk')
partitionNumber = \
getUdevDeviceInfo(name=dev).get('ID_PART_ENTRY_NUMBER','')
devicePartitionCount = countPartitions(deviceName)
if deviceName and not partitionNumber:
raise DistributiveError(
_("Failed to determine the partition number for %s")%dev)
if parttable == "dos":
fdisk = process(fdiskProg,deviceName,stderr=STDOUT)
pipe = Popen([fdiskProg,deviceName],
stdin=PIPE, stdout=PIPE,stderr=PIPE)
if devicePartitionCount > 1:
pipe.stdin.write("t\n%s\n%s\nw\n"%(partitionNumber,
systemid))
else:
pipe.stdin.write("t\n%s\nw\n"%systemid)
pipe.stdin.close()
pipe.wait()
elif parttable == "gpt":
pipe = Popen([gdiskProg,deviceName],
stdin=PIPE, stdout=PIPE,stderr=PIPE)
if devicePartitionCount > 1:
pipe.stdin.write("t\n%s\n%s\nw\ny\n"%(partitionNumber,
systemid))
else:
pipe.stdin.write("t\n%s\nw\ny\n"%systemid)
pipe.stdin.close()
pipe.wait()
for waittime in (0.1,0.2,0.5,1,2,4):
if path.exists(dev):
return True
else:
sleep(waittime)
raise DistributiveError(
_("Failed to found partition %s after changing the system id")%dev)
def formatSwapPartition(self, dev):
"""Format swap partition"""
if dev in map(lambda y: y.split(" ")[0],
filter(lambda x: x.startswith("/"),
open("/proc/swaps"))):
raise DistributiveError(\
_("Failed to execute 'mkswap %s', the swap partition is used "
"by the current system") %dev)
if isMount(dev):
raise DistributiveError(
_("Failed to format partition %s, because it is mounted") %dev)
execStr = self.formatUtilities["swap"]%dev
res,errmes = self.runOsCommand(execStr)
if res == 0:
return True
else:
raise DistributiveError(_("Failed to execute '%s'")%execStr +\
"\n%s" %errmes)
def installFrom(self, source):
"""Install distributive to partition from source distributive"""
# get currect partition as directory
distrTo = self.convertToDirectory()
# install into directroy distributive from source
distrTo.installFrom(source)
class ArchiveDistributive(Distributive):
def __init__(self,file,parent=None,mdirectory="/var/calculate/tmp/stage"):
Distributive.__init__(self,parent=parent)
self.file = file
self.mdirectory = mdirectory
def _detectArchive(self,file):
"""Detect archive by "/usr/bin/file" command
Return bzip2,gzip,7z or None
"""
res,mes = self.runOsCommand("/usr/bin/file %s"%file)
if "bzip2 compressed data" in mes:
return "bzip2"
elif "gzip compressed data" in mes:
return "gzip"
elif "7-zip archive data" in mes:
return "7z"
elif file and file.endswith(".tar.lzma"):
if path.exists('/usr/bin/7za'):
return "7z"
else:
return "lzma"
return None
def _unpackArchive(self,archfile,directory):
"""Unpack archive"""
# archive is exists
if not path.exists(archfile):
raise DistributiveError(_("File '%s' not found")%archfile)
# detect type archive
archiveType = self._detectArchive(archfile)
# make directory if archive was detected normally
if archiveType:
self._makeDirectory(directory)
# unpack archive
if archiveType == "7z":
res,mes = self.runOsCommand("7za x -so %s | tar xf - -C %s/"%
(archfile,directory))
elif archiveType == "lzma":
res,mes = self.runOsCommand("lzma -dc %s | tar xf - -C %s/"%
(archfile,directory))
elif archiveType == "bzip2":
res,mes = self.runOsCommand("tar xjf %s -C %s/"%
(archfile,directory))
elif archiveType == "gzip":
res,mes = self.runOsCommand("tar xf %s -C %s/"%
(archfile,directory))
else:
raise DistributiveError(_("Unknown archive type by '%s'")%
archfile)
if res != 0:
raise DistributiveError(_("Error during unpacking\n%s")%mes)
def unpackTo(self,directory):
"""Unpack currect archive to directory"""
self._unpackArchive(self.file,directory)
def convertToDirectory(self):
"""Get archive as directory (unpack to directory)"""
# check may be the archive already unpacked
mdirectory = self.mdirectory
for child in self.childs:
if isinstance(child,DirectoryDistributive) and \
mdirectory in child.directory:
return child
# get temporary directory for unpacking
mdirectory = self._getMntDirectory(mdirectory)
dirdist = DirectoryDistributive(mdirectory,parent=self)
self._unpackArchive(self.file,mdirectory)
return dirdist
def releaseChild(self,child):
"""Remove child Directory distributive"""
if isinstance(child,DirectoryDistributive):
self._removeDirectory(child.directory)
child.directory = None
def packToArchive(self,directory,file):
res,errmes = self.runOsCommand("tar cf %s -C %s ."%(file,directory))
if res != 0:
raise DistributiveError(_("Failed to create the archive") +
" '%s':\n%s"%(file,errmes))
def installFrom(self, source):
"""Install distributive to partition from source distributive"""
# get source distributive as directory distributive
dFrom = source.convertToDirectory()
# install into directroy distributive from source
self.packToArchive(dFrom.directory, self.file)
class SquashDistributive(Distributive):
def __init__(self,file,parent=None,mdirectory="/mnt/livecd",exclude=None,
compress=""):
Distributive.__init__(self,parent=parent)
self.file = file
self.mdirectory = mdirectory
self.exclude = [] if not exclude else exclude
self.compress = compress \
if compress and compress != "gzip" else \
""
def _mountSquash(self,file,directory):
"""Mount squashfs to directory"""
self._makeDirectory(directory)
self._mountToDirectory(file,directory,mountopts="-o loop -t squashfs")
def _umountSquash(self,directory):
self._umountDirectory(directory)
self._removeDirectory(directory)
def convertToDirectory(self):
mdirectory = self.mdirectory
for child in self.childs:
if isinstance(child,DirectoryDistributive) and \
mdirectory in child.directory:
return child
mdirectory = self._getMntDirectory(mdirectory)
self._mountSquash(self.file,mdirectory)
return DirectoryDistributive(mdirectory,parent=self)
def releaseChild(self,child):
"""Umount child Directory distributive"""
if isinstance(child,DirectoryDistributive):
self._umountSquash(child.directory)
child.directory = None
def packToSquash(self,directory,file):
mksquashfsUtil = '/usr/bin/mksquashfs'
if not path.exists(mksquashfsUtil):
raise DistributiveError(_("Failed to create squash") +
" : %s"%_("command '%s' not found")%mksquashfsUtil)
cmd = [mksquashfsUtil, "%s/"%directory,file, "-no-progress"]
if self.exclude:
cmd += ["-e"] + self.exclude
if self.compress:
cmd += ["-comp",self.compress]
processMkSquash = process(*cmd)
if processMkSquash.failed():
raise DistributiveError(_("Failed to create squashfs") +
" '%s':\n%s"%(file,processMkSquash.read()))
def installFrom(self, source):
"""Install distributive to partition from source distributive"""
# get source distributive as directory distributive
dFrom = source.convertToDirectory()
# install into directroy distributive from source
self.packToSquash(dFrom.directory, self.file)
class IsoDistributive(Distributive):
def __init__(self,file,parent=None,mdirectory="/mnt/cdrom",
bdirectory="/var/calculate/tmp/iso",exclude=None,
compress="gzip"):
Distributive.__init__(self,parent=parent)
self.file = file
if path.isdir(self.file):
self.mdirectory = self.file
else:
self.mdirectory = mdirectory
if file == bdirectory:
self.bdirectory = file
else:
self.bdirectory = self._getMntDirectory(bdirectory)
self.exclude = [] if not exclude else exclude
self.compress = compress
def probe(self):
"""Check directory for iso content"""
try:
pathname = self.getIsoContentDirectory()
except:
return False
return path.exists(path.join(pathname,"syslinux")) and \
path.exists(path.join(pathname,"isolinux"))
def _mountIso(self,file,directory):
if self.file != self.mdirectory:
self._makeDirectory(directory)
tf = typeFile(magic=0x6)
ftype = tf.getMType(file)
if "block special" in ftype:
mopts = ""
else:
mopts = "-o loop"
self._mountToDirectory(file,directory,mountopts=mopts)
def _umountIso(self,directory):
if self.file != self.mdirectory:
self._umountDirectory(directory)
self._removeDirectory(directory)
def convertToSquash(self):
mdirectory = self.mdirectory
for child in self.childs:
if isinstance(child,SquashDistributive) and \
mdirectory in child.file:
return child
if self.mdirectory != self.file:
mdirectory = self._getMntDirectory(mdirectory)
self._mountIso(self.file,mdirectory)
fileLive = self._getLastLive(mdirectory)
if not fileLive:
self._umountIso(mdirectory)
raise DistributiveError(_("Iso %s contains no live image") %
self.file)
return SquashDistributive(path.join(mdirectory,fileLive),
parent=self,exclude=self.exclude,
compress=self.compress)
def getIsoContentDirectory(self):
"""Return directory with content of iso image"""
squash = self.convertToSquash()
return path.dirname(squash.file)
def releaseChild(self,child):
"""Umount child Directory distributive"""
if isinstance(child,SquashDistributive):
self._umountIso(path.dirname(child.file))
child.directory = None
def convertToDirectory(self):
return self.convertToSquash().convertToDirectory()
def packToIso(self,directory,file):
# remove previous version of iso
try:
if path.exists(file):
os.unlink(file)
except Exception, e:
raise DistributiveError(_("Failed to remove") +\
" %s:\n%s"%(file,str(e)))
except KeyboardInterrupt, e:
self.setSignalInterrupt()
raise DistributiveError(_("Failed to remove") +\
" %s:\n%s"%(file,str(e)))
mkisoUtil = '/usr/bin/mkisofs'
if not path.exists(mkisoUtil):
raise DistributiveError(_("Failed to create the iso image") +
" : %s"%_("command '%s' not found")%mkisoUtil)
res,errmes = self.runOsCommand(
"%(progname)s %(params)s -o %(target)s %(source)s/"%
{'progname':mkisoUtil,
'params':" ".join(["-b isolinux/isolinux.bin","-no-emul-boot",
"-boot-load-size 4","-boot-info-table","-iso-level 4",
"-hide boot.catalog"]),
'target':file,
'source':directory})
if res == 0:
return True
else:
raise DistributiveError(_("Failed to create the iso image") +
" %s:\n%s"%(file,errmes))
def prepareIso(self):
raise DistributiveError(
_("Iso image cannot be created without overriding prepareIso"))
def installFrom(self, source):
"""Install distributive to partition from source distributive"""
# make temporary directory for creating iso image
isoDirectory = self.bdirectory
self._makeDirectory(isoDirectory)
try:
# getting squash from source
liveimage = self._getLastLive(isoDirectory)
if liveimage:
curnum = self._getSquashNum(self.reLive.search(liveimage))
liveimage = "livecd.squashfs.%d"%(curnum+1)
else:
liveimage = "livecd.squashfs"
if isinstance(source,SquashDistributive):
self._copyfile(source.file,
path.join(isoDirectory,liveimage))
else:
distDirectory = source.convertToDirectory()
squashDistr = SquashDistributive(
path.join(isoDirectory,liveimage),
exclude=self.exclude,
compress=self.compress)
squashDistr.installFrom(distDirectory)
# prepare iso
self.prepareIso(isoDirectory)
# pack iso
if self.bdirectory != self.file:
self.packToIso(isoDirectory, self.file)
except DistributiveError,e:
raise e
except KeyboardInterrupt,e:
self.setSignalInterrupt()
raise DistributiveError(_("Keyboard interruption"))
def close(self):
# close all child
Distributive.close(self)
# remove temporary directory
if path.lexists(self.bdirectory) and self.file != self.bdirectory:
self._removeDirectory(self.bdirectory)
class FlashDistributive(PartitionDistributive):
def installFrom(self, source):
"""Install distributive to partition from source distributive"""
# make temporary directory for creating iso image
distrTo = self.convertToDirectory()
# getting squash from source
if isinstance(source,IsoDistributive):
self.rsync(source.getIsoContentDirectory(),distrTo.directory)
else:
raise DistributiveError(
_("Installation to flash does not support %s"%
source.__class__.__name__))
class ScratchDistributive(Distributive):
def __init__(self,directory,parent=None,mdirectory="/mnt/install",
check=False):
Distributive.__init__(self,parent=parent)
self.directory = directory
self.mdirectory = mdirectory
if check and not (path.exists(path.join(directory,"workspace")) and \
path.exists(path.join(directory,"delta")) and \
path.exists(path.join(directory,"calculate"))):
raise DistributiveError(
_("Wrong scratch distribution in '%s'")%directory)
def _mountLayers(self,source,target):
"""Mount squashfs to directory"""
self._makeDirectory(target)
self._mountToDirectory("none",target,
mountopts="-t aufs "+\
"-o udba=reval,br:%(work)s=rw:%(delta)s=ro+wh:%(static)s=ro" %\
{"work":path.join(source,"workspace"),
"delta":path.join(source,"delta"),
"static":path.join(source,"calculate")})
def _umountLayers(self,directory):
self._umountDirectory(directory)
self._removeDirectory(directory)
def _mountLiveImage(self,file,directory):
"""Mount squashfs to directory"""
self._makeDirectory(directory)
self._mountToDirectory(file,directory,mountopts="-o loop -t squashfs")
def _umountLiveImage(self,directory):
self._umountDirectory(directory)
def convertToDirectory(self):
"""Convert scrach directories to one directory"""
mdirectory = self.mdirectory
for child in self.childs:
if isinstance(child,DirectoryDistributive) and \
mdirectory in child.directory:
return child
mdirectory = self._getMntDirectory(mdirectory)
liveFile = self._getLastLive(self.directory)
self._mountLiveImage(path.join(self.directory,liveFile),
path.join(self.directory,"calculate"))
self._mountLayers(self.directory,mdirectory)
return DirectoryDistributive(mdirectory,parent=self)
def releaseChild(self,child):
"""Umount child Directory distributive"""
if isinstance(child,DirectoryDistributive):
self._umountLayers(child.directory)
self._umountLiveImage(path.join(self.directory,"calculate"))
child.directory = None
def installFrom(self, source):
"""Install distributive to partition from source distributive"""
# get source distributive as directory distributive
dFrom = source.convertToDirectory()
# install into directroy distributive from source
self.packToSquash(dFrom.directory, self.file)
class ScratchPartitionDistributive(PartitionDistributive):
def releaseChild(self,child):
"""Umount child Directory distributive"""
if isinstance(child,ScratchDistributive):
self._umountPartition(child.directory)
child.directory = None
def convertToDirectory(self):
"""Convert scratch partition to directory by scratch directory"""
return self.convertToScratch().convertToDirectory()
def convertToScratch(self):
mdirectory = self.mdirectory
for child in self.childs:
if isinstance(child,ScratchDistributive) and \
mdirectory in child.directory:
return child
mdirectory = self._getMntDirectory(mdirectory)
self._mountPartition(self.partition,mdirectory)
return ScratchDistributive(mdirectory,parent=self)
def prepareScratch(self,directory):
"""Create need scratch directories"""
for scrDirectory in ("calculate","delta","workspace"):
self._makeDirectory(path.join(directory,scrDirectory))
def getBootDirectory(self):
"""Get directory which contains boot"""
return path.join(self.convertToScratch().directory,"boot")
def postinstallMountBind(self):
pass
def installFrom(self, source):
"""Install distributive to partition from source distributive"""
# get currect partition as directory
distrTo = self.convertToScratch()
scratchDirectory = distrTo.directory
try:
# getting squash from source
if isinstance(source,IsoDistributive):
source = source.convertToSquash()
if isinstance(source,SquashDistributive):
self._copyfile(source.file,
path.join(scratchDirectory,"livecd.squashfs"))
else:
distDirectory = source.convertToDirectory()
squashDistr = SquashDistributive(
path.join(scratchDirectory,"livecd.squashfs"))
squashDistr.installFrom(distDirectory)
# prepare scratch
self.prepareScratch(scratchDirectory)
except DistributiveError,e:
raise e
except KeyboardInterrupt,e:
self.setSignalInterrupt()
raise DistributiveError(_("Keyboard interruption"))