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/pym/install/distr.py

2097 lines
79 KiB

9 years ago
# -*- coding: utf-8 -*-
# Copyright 2010-2016 Mir Calculate. 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 datetime
import re
import sys
import operator
import json
from shutil import copyfile, copytree
from subprocess import Popen, PIPE
from itertools import *
from functools import partial
from calculate.lib.datavars import VariableError
from calculate.lib.utils.mount import isMount, Btrfs, BtrfsError
from calculate.lib.utils.files import (removeDir,
processProgress, STDOUT,
typeFile, pathJoin, process,
listDirectory, checkUtils,
MAGIC_COMPRESS, MAGIC_SYMLINK,
MAGIC_CONTINUE, makeDirectory,
isEmpty, check_rw, calculate_exclude,
PercentProgress, getProgPath, xztaropen)
import calculate.lib.utils.device as device
from calculate.lib.utils.device import (detectDeviceForPartition,
countPartitions)
9 years ago
from calculate.lib.utils.tools import classproperty, Signal, traverse
from calculate.lib.utils.text import _u8
from calculate.lib.variables.linux import LinuxDataVars, Linux
9 years ago
from calculate.lib.cl_lang import setLocalTranslate, _
3 years ago
from functools import reduce
9 years ago
setLocalTranslate('cl_install3', sys.modules[__name__])
9 years ago
class DefaultMountPath(object):
"""
Пути по умолчанию для монтирования образов
"""
BaseMountPath = '/run/calculate/mount'
BuildDirectory = '/var/calculate/tmp/iso'
@classproperty
def IsoImage(cls):
return path.join(cls.BaseMountPath, "iso")
@classproperty
def SquashImage(cls):
return path.join(cls.BaseMountPath, "squash")
@classproperty
def ArchiveImage(cls):
return path.join(cls.BaseMountPath, "tarball")
@classproperty
def DefaultMount(cls):
return path.join(cls.BaseMountPath, "distro")
@classproperty
def InstallMount(cls):
return path.join(cls.BaseMountPath, "install")
class cpProcessProgress(processProgress):
9 years ago
def init(self, *args, **kwargs):
self.maxfiles = kwargs.get('maxfiles', 1)
self.stderr = STDOUT
self.command.append("-dRv")
def processInit(self):
self.value = 0
self.percent = 0
return 0
def processEnd(self):
self.value = self.maxfiles
self.percent = 100
return 100
9 years ago
def processString(self, strdata):
if strdata.startswith("cp:") or strdata.startswith("/bin/cp:"):
self._cachedata.append(strdata.partition(": ")[2])
if "->" in strdata:
self.value += 1
12 years ago
if self.maxfiles:
percent = 100 * self.value / self.maxfiles
9 years ago
percent = min(percent, 99)
12 years ago
if percent > self.percent:
self.percent = percent
return percent
else:
return None
def progressCopyFile(source, dest):
"""
Copy file with progress
"""
size = int(os.lstat(source).st_size)
bufsize = (100 - (size % 100) + size) / 100
with open(source, 'rb') as infile:
with open(dest, 'w') as outfile:
for i in xrange(1, 101):
outfile.write(infile.read(bufsize))
yield i
class DistributiveError(Exception):
"""Error for distributive operations"""
pass
class Distributive(object):
"""
Distributive object. Parent object for all distributive.
"""
9 years ago
class Type(object):
Directory = "dir"
Partition = "part"
SquashFS = "squash"
Archive = "arch"
Iso = "iso"
Layered = "layered"
mountError = _("Failed to mount") + " %s:\n%s"
9 years ago
reLive = re.compile(r"^live[^.]*\.squashfs(\.(\d+))?$", re.S)
contentCache = {}
needFormat = True
def __init__(self, parent=None):
self.childs = []
self.ref = 0
self.locked = False
# if specified parent
9 years ago
if isinstance(parent, Distributive):
# 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
@classmethod
9 years ago
def fromFile(cls, filename):
"""Get Distributive object by filename"""
# MAGIC_COMPRESS 0x000004 Check inside compressed files
tf = typeFile(magic=MAGIC_COMPRESS | MAGIC_SYMLINK | MAGIC_CONTINUE)
ftype = tf.getMType(filename)
if ftype:
if "block special" in ftype:
for distrType in (IsoDistributive, FlashDistributive,
PartitionDistributive):
distr = distrType(filename)
res = distr.probe()
distr.close()
if res:
return distr
if "ISO 9660 CD-ROM" in ftype:
return IsoDistributive(filename)
elif "7-zip" in ftype or \
"POSIX tar archive" in ftype:
if "rootfs.tar" in filename:
return ContainerDistributive(path.dirname(filename))
else:
return ArchiveDistributive(filename)
elif "Squashfs filesystem" in ftype:
return SquashDistributive(filename)
elif path.isdir(filename):
if path.isfile(path.join(filename, "rootfs.tar.xz")):
return ContainerDistributive(filename)
elif path.isfile(path.join(filename, "livecd")):
return IsoDistributive(filename)
else:
important_dirs = ("etc", "lib", "bin", "sbin", "var")
if all(path.exists(path.join(filename, x))
for x in important_dirs):
return DirectoryDistributive(filename)
raise DistributiveError(_("Wrong distribution") + " '%s':\n%s" \
% (filename, ftype))
def getType(self):
return _("empty")
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 reserve(self):
self.locked = True
def release(self):
self.locked = False
def __enter__(self):
self.ref += 1
return self
def __exit__(self, type, value, traceback):
self.ref -= 1
if not self.ref:
self.close()
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()
"""
9 years ago
# print "Close", self, self.locked
if self.locked:
return False
if self.ref:
self.ref -= 1
return False
else:
try:
# 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)
finally:
self.parent = None
self.childs = []
return True
9 years ago
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"""
9 years ago
raise DistributiveError(_("Failed to convert") + " '%s' " \
% self.__class__.__name__ + _("to") + \
" '%s'" % "DirectoryDistributive")
9 years ago
# def __del__(self):
# """Uncomment this method for automaticaly release all distributive
# instance"""
# self.close()
@staticmethod
def clear_empty_directories(dn):
dn = path.dirname(dn)
while dn and path.exists(dn) and isEmpty(dn) and not isMount(dn):
os.rmdir(dn)
dn = path.dirname(dn)
def getDirectory(self):
13 years ago
"""Get directory which contains distro"""
return self.convertToDirectory().directory
13 years ago
def getBootDirectory(self):
"""Get directory which contains boot"""
9 years ago
return path.join(self.getDirectory(), "boot")
13 years ago
11 years ago
def getEfiDirectory(self):
"""Get directory which contains boot/efi"""
return self.getEfiDirectories()[0]
11 years ago
def getEfiDirectories(self):
return [path.join(self.getDirectory(), "boot/efi")]
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
3 years ago
except Exception as e:
raise DistributiveError(_("Failed to create the directory") +
9 years ago
" '%s':\n%s" % (pathname, str(e)))
3 years ago
except KeyboardInterrupt as e:
raise DistributiveError(_("Failed to create the directory") +
9 years ago
" '%s':\n%s" % (pathname, str(e)))
def get_squash_size(self):
"""
Получить размер squash образа
:return:
"""
raise DistributiveError(_("Squash size unsupported for %s") %
str(self.__class__.__name__))
def _removeDirectory(self, directory):
"""Remove directory and files contained in it"""
try:
if path.exists(directory):
removeDir(directory)
9 years ago
except Exception as e:
raise DistributiveError(_("Failed to remove the directory from") + \
" '%s':\n%s" % (directory, str(e)))
except KeyboardInterrupt as e:
raise DistributiveError(_("Failed to remove the directory from") + \
" '%s':\n%s" % (directory, str(e)))
def _copyfile(self, infile, outfile):
try:
9 years ago
copyfile(infile, outfile)
except Exception as e:
raise DistributiveError(_("Failed to copy") + " '%s' to '%s':\n%s" \
% (infile, outfile, str(e)))
9 years ago
def _copytree(self, indir, outdir):
14 years ago
try:
9 years ago
copytree(indir, outdir, symlinks=True)
3 years ago
except Exception as e:
9 years ago
raise DistributiveError(_("Failed to copy") + " '%s' to '%s':\n%s" \
% (indir, outdir, str(e)))
3 years ago
except KeyboardInterrupt as e:
9 years ago
raise DistributiveError(_("Failed to copy") + " '%s' to '%s':\n%s" \
% (indir, outdir, str(e)))
14 years ago
9 years ago
def rsync(self, fromdir, todir, callbackProgress=None,
byfile=None, filesnum=0, noperm=False, **kwargs):
"""Copy files from 'fromdir' directory to 'todir' directory"""
9 years ago
cpCmd = getProgPath('/bin/cp')
if not cpCmd:
9 years ago
raise DistributiveError(_("'%s' not found") % "cp")
try:
9 years ago
joinFrom = partial(path.join, fromdir)
params = [cpCmd, "-x"] + \
(["--no-preserve=mode,ownership"] if noperm else ["-a"]) + \
3 years ago
list(map(joinFrom,
filterfalse(byfile or operator.not_,
listDirectory(fromdir)))) + \
[todir]
cpProcess = cpProcessProgress(*params,
maxfiles=filesnum, stderr=STDOUT)
for perc in cpProcess.progress():
if callbackProgress and not byfile: callbackProgress(perc)
res = cpProcess.success()
errmes = cpProcess.read()
# copy by one file
if byfile:
3 years ago
percFiles = list(filter(byfile,
listDirectory(fromdir)))
if len(percFiles) > 1:
9 years ago
maxPercOnFile = 100 / len(percFiles)
recountPerc = \
9 years ago
lambda perc, num: perc / len(
percFiles) + maxPercOnFile * num
else:
9 years ago
recountPerc = lambda perc, num: perc
for i, copyfile in enumerate(percFiles):
for perc in progressCopyFile(joinFrom(copyfile),
9 years ago
path.join(todir, copyfile)):
if callbackProgress:
9 years ago
callbackProgress(recountPerc(perc, i))
3 years ago
except Exception as e:
res = False
errmes = str(e)
if not res:
9 years ago
raise DistributiveError(_("Failed to copy files from") + \
" '%s' " % fromdir + _("to") + \
" '%s':\n%s" % (todir, errmes))
9 years ago
def _mountToBind(self, srcDirectory, destDirectory):
"""Mount srcDirectory to destDirectory"""
9 years ago
mount = process('/bin/mount', "-o", "bind", srcDirectory, destDirectory,
stderr=STDOUT)
if mount.success():
return True
else:
try:
os.rmdir(destDirectory)
9 years ago
except OSError:
pass
9 years ago
raise DistributiveError(
self.mountError % (srcDirectory, mount.read()))
def performFormat(self):
pass
def formatPartition(self, dev, format="ext4", label="", purpose=None):
pass
def rndString(self):
"""Get random string with len 8 char"""
9 years ago
return "".join([choice(string.ascii_letters + string.digits)
for i in xrange(0, 8)])
9 years ago
def _getSquashNum(self, reMatch):
if reMatch.groups()[1] and reMatch.groups()[1].isdigit():
return int(reMatch.groups()[1])
else:
return 0
9 years ago
def _getLastLive(self, directory):
14 years ago
"""Get last live squashfs image from directory"""
3 years ago
squashfiles = list(filter(lambda x: x,
9 years ago
map(self.reLive.search,
3 years ago
listDirectory(directory))))
if squashfiles:
return max(squashfiles, key=self._getSquashNum).group()
return None
14 years ago
def _mountToDirectory(self, file, directory, mountopts="", count=2):
"""Mount squashfs to directory"""
# 2816 code return by mount if device is absent (update /dev by udev)
NO_SUCH_DEVICE = 2816
if isMount(directory):
9 years ago
raise DistributiveError(
_("Failed to mount to the directory: %s\n")
% directory + _("Directory already mounted"))
mountopts_list = filter(lambda x: x,
mountopts.split(" "))
mountProcess = process('/bin/mount', file, directory, *mountopts_list,
stderr=STDOUT)
if mountProcess.success():
return True
else:
# 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,
9 years ago
count - 1)
try:
self._removeDirectory(directory)
9 years ago
except Exception:
pass
9 years ago
raise DistributiveError(
self.mountError % (file, mountProcess.read()))
def _umountDirectory(self, directory):
"""Umount directory"""
if isMount(directory):
9 years ago
processUmount = None
for wait in [0, 0.5, 2, 5]:
sleep(wait)
9 years ago
processUmount = process('/bin/umount', directory, stderr=STDOUT)
if processUmount.success():
return True
9 years ago
else:
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
for i in range(2, 9999):
if not path.exists(newDirectoryName):
return newDirectoryName
else:
newDirectoryName = "%s.%02d" % (directory, i)
return newDirectoryName
@staticmethod
def getInfoFromDirectory(directory):
d = {}
try:
9 years ago
if path.lexists(path.join(directory, 'lib64')):
d['os_arch_machine'] = 'x86_64'
9 years ago
elif path.lexists(path.join(directory, 'lib')):
d['os_arch_machine'] = 'i686'
else:
d['os_arch_machine'] = ''
dv = LinuxDataVars(systemRoot=directory)
d["os_linux_shortname"] = dv.Get('os_linux_shortname')
d['os_linux_ver'] = dv.Get('os_linux_ver')
d['os_linux_build'] = dv.Get('os_linux_build')
d['os_linux_name'] = dv.Get('os_linux_name')
d['os_linux_subname'] = dv.Get('os_linux_subname')
d['os_linux_system'] = dv.Get('os_linux_system')
d['cl_make_profile'] = dv.Get('cl_make_profile')
d['cl_profile_name'] = dv.Get('cl_profile_name')
# make lazy call
d['os_linux_files'] = partial(dv.Get, 'os_linux_files')
d['os_chrootable_set'] = "off"
try:
if process("/usr/bin/chroot", directory, "/bin/true").success():
d['os_chrootable_set'] = "on"
except Exception:
pass
except VariableError:
pass
return d.copy()
9 years ago
def getInfo(self, filename=None):
"""Get info from content"""
image = None
try:
# get from cache
keyCache = None
if not filename:
for keyname in ("file", "partition", "directory"):
if hasattr(self, keyname):
keyCache = getattr(self, keyname)
elif filename in self.contentCache:
keyCache = filename
if keyCache and keyCache in self.contentCache:
return Distributive.contentCache[keyCache].copy()
distr = None
# may be directory is isodir (directory which contains iso image)
extname = "isodir"
try:
distr = self.fromFile(filename) if filename else self
mapExtName = {DirectoryDistributive: "dir",
IsoDistributive: "isodir",
FlashDistributive: "flash",
ContainerDistributive: "lxc",
PartitionDistributive: "partdir"}
extname = mapExtName.get(distr.__class__, "")
if isinstance(distr, ContainerDistributive):
d = distr.get_information()
d['ext'] = extname
return d.copy()
if isinstance(distr, ArchiveDistributive):
raise DistributiveError("Not for archive distributive")
image = distr.convertToDirectory()
except Exception as e:
# TODO: отладка почему образ не подходит
#print str(e)
if distr:
distr.close()
return {}.copy()
d = self.getInfoFromDirectory(image.directory)
d['ext'] = extname
if distr:
distr.close()
if keyCache and not path.isdir(keyCache):
Distributive.contentCache[keyCache] = d
return d.copy()
finally:
if image:
image.close()
@classmethod
def unserialize(cls, data, parent=None):
9 years ago
class_mapper = {cls.Type.Directory: DirectoryDistributive,
cls.Type.Iso: IsoDistributive,
cls.Type.Partition: PartitionDistributive,
cls.Type.SquashFS: SquashDistributive,
cls.Type.Layered: LayeredDistributive}
if not data.get('type', '') in class_mapper:
raise DistributiveError(_("Failed to unserialize type %s") %
data.get('type', ''))
return class_mapper[data['type']].unserialize(data, parent=parent)
@staticmethod
def required(*params):
def decor(f):
def wrapper(cls, data, **kw):
if any(not x in data for x in params):
raise DirectoryDistributive(
_("Failed to unserialize %s") % cls.__class__.__name__)
9 years ago
return f(cls, data, **kw)
return wrapper
return decor
def restore(self):
raise DistributiveError(_("Recovery is not implemented"))
def is_invalid(self):
try:
return not "cl_make_profile" in self.getInfo()
except DistributiveError:
return True
def post_clear(self):
return True
class DirectoryDistributive(Distributive):
"""
Дистрибутив в директории
"""
data = [{'name': 'proc',
'type': 'proc',
'target': 'proc',
'source': 'none'},
{'name': 'sys',
'type': 'sysfs',
'target': 'sys',
'source': 'none'},
{'name': 'dev',
'type': 'bind',
'target': 'dev',
'source': '/dev'},
{'name': 'devpts',
'type': 'bind',
'target': 'dev/pts',
'source': '/dev/pts'},
{'name': 'remote',
'type': 'bind',
'target': 'var/calculate/remote',
'source': '/var/calculate/remote'},
]
9 years ago
def __init__(self, directory, parent=None, mdirectory=None):
Distributive.__init__(self, parent=parent)
self.no_unmount = False
self.directory = directory
self.mdirectory = mdirectory
self.system_mounted = False
if not parent:
self._makeDirectory(self.directory)
def hasSystemDirectories(self):
return self.system_mounted or self.directory == '/'
def mountSystemDirectories(self, skip=("remote",)):
"""
Подключить к дистрибутиву системые ресурсы (/proc, /sys)
:return:
"""
if not self.system_mounted:
for obj in filter(lambda x: x['name'] not in skip, self.data):
target_path = path.join(self.directory, obj['target'])
if obj['type'] == 'bind':
if not path.exists(target_path):
self._makeDirectory(target_path)
if not path.exists(obj['source']):
self._makeDirectory(obj['source'])
self._mountToBind(obj['source'], target_path)
else:
self._mountToDirectory(obj['source'], target_path,
"-t %s" % obj['type'])
self.system_mounted = True
def umountSystemDirectories(self):
for obj in reversed(self.data):
target_path = path.join(self.directory, obj['target'])
if isMount(target_path):
self._umountDirectory(target_path)
self.system_mounted = False
def getType(self):
return _("directory '%s'") % _u8(self.directory)
9 years ago
def bindDirectory(self, mdirectory):
for child in self.childs:
9 years ago
if isinstance(child, DirectoryDistributive) and \
mdirectory in child.directory:
return child
mdirectory = self._getMntDirectory(mdirectory)
self._makeDirectory(mdirectory)
9 years ago
self._mountToBind(self.directory, mdirectory)
return DirectoryDistributive(mdirectory, parent=self)
9 years ago
def releaseChild(self, child):
"""Remove child Directory distributive"""
9 years ago
if isinstance(child, DirectoryDistributive):
self._umountDirectory(child.directory)
self._removeDirectory(child.directory)
child.directory = None
def close(self):
if not self.locked and self.system_mounted:
self.umountSystemDirectories()
Distributive.close(self)
def convertToDirectory(self):
if self.mdirectory:
return self.bindDirectory(self.mdirectory)
else:
return self
def performFormat(self):
"""Format for directory - removing all files"""
9 years ago
rm_command = getProgPath("/bin/rm")
p = process(rm_command, "-rf", "--one-file-system", self.directory,
stderr=process.STDOUT)
if p.success():
self._makeDirectory(self.directory)
return True
else:
raise DistributiveError(_("Failed to clean directory") +
9 years ago
" %s:\n%s" % (self.directory, p.read()))
def post_clear(self):
if path.exists(self.directory):
9 years ago
rm_command = getProgPath("/bin/rm")
p = process(rm_command, "-rf", "--one-file-system", self.directory)
p.success()
self.clear_empty_directories(self.directory)
return True
12 years ago
def installFrom(self, source, **kwargs):
"""Install distributive to directory from source distributive"""
9 years ago
if isinstance(source, ArchiveDistributive):
source.unpackTo(self.directory)
else:
# get source distributive as directory distributive
dFrom = source.convertToDirectory()
# copy distributive from source to this
9 years ago
self.rsync(dFrom.directory, self.directory, **kwargs)
def serialize(self):
d = {'type': Distributive.Type.Directory,
'directory': self.directory,
9 years ago
'system_mounted': self.system_mounted,
'childs': [x.serialize() for x in self.childs]}
if self.mdirectory:
d['mdirectory'] = self.mdirectory
return d
@classmethod
@Distributive.required("directory", "system_mounted")
9 years ago
def unserialize(cls, data, parent=None):
ld = DirectoryDistributive(_u8(data['directory']), parent=parent)
ld.system_mounted = _u8(data['system_mounted'])
if "mdirectory" in data:
ld.mdirectory = _u8(data['mdirectory'])
ld.childs = [Distributive.unserialize(x, parent=ld) for x in
data.get('childs', [])]
return ld
9 years ago
class DataPartition(object):
"""Data partition"""
dev = None
mountPoint = None
fileSystem = "ext4"
isFormat = False
systemId = None
13 years ago
partitionTable = None
9 years ago
class MultiPartitions:
"""Data partition list"""
9 years ago
11 years ago
def __init__(self):
self.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] is not 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 "
"are not specified: (%s)") \
% ", ".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,...]"""
3 years ago
return list(map(lambda x: x.systemId, self.listPartitions))
13 years ago
def getPartitionTable(self):
"""Get systemID for change [dos,gpt,...]"""
3 years ago
return list(map(lambda x: x.partitionTable, self.listPartitions))
13 years ago
def getIsFormat(self):
"""Get list is format [True,...]"""
3 years ago
return list(map(lambda x: x.isFormat, self.listPartitions))
def getFileSystems(self):
"""Get list filesystems ["reiserfs",...]"""
3 years ago
return list(map(lambda x: x.fileSystem, self.listPartitions))
def getPartitions(self):
"""Get list partition ["/dev/sda",...]"""
3 years ago
return list(map(lambda x: x.dev, self.listPartitions))
def getMountPoints(self):
"""Get list mount point ["/boot",...]"""
3 years ago
return list(map(lambda x: x.mountPoint, self.listPartitions))
9 years ago
class FormatProcess(process):
format_util = ""
dos_id = "0"
gpt_id = "0"
def __init__(self, dev, label=None, purpose=None, compression=None):
9 years ago
self.dev = dev
self._label = label
self.purpose = purpose
self.compression = compression
9 years ago
super(FormatProcess, self).__init__(*self.format_command())
def format_command(self):
cmd = getProgPath(self.format_util)
if not cmd:
raise DistributiveError(_("command '%s' not found") % cmd)
params = (self.bootparam()
if self.purpose in ("root", "boot")
else self.param())
9 years ago
return list(traverse(
[cmd] + [x for x in params if x]))
def postaction(self):
return True
def bootparam(self):
return self.param()
9 years ago
def param(self):
return self.get_label(), self.dev
def get_label(self):
if not self._label:
return ()
else:
return self.label()
def label(self):
return ()
9 years ago
class FormatProcessGeneric(FormatProcess):
dos_id = "83"
gpt_id = "8300"
def label(self):
return "-L", self._label
def param(self):
return self.get_label(), self.dev
9 years ago
class FormatExt2(FormatProcessGeneric):
format_util = "/sbin/mkfs.ext2"
9 years ago
class FormatExt3(FormatExt2):
format_util = "/sbin/mkfs.ext3"
9 years ago
class FormatExt4(FormatExt2):
format_util = "/sbin/mkfs.ext4"
9 years ago
class FormatJfs(FormatProcessGeneric):
format_util = "/sbin/mkfs.jfs"
def param(self):
return self.get_label(), "-f", self.dev
class FormatF2FS(FormatProcessGeneric):
format_util = "/usr/sbin/mkfs.f2fs"
def label(self):
return "-l", self._label
def param(self):
return self.get_label(), "-f", self.dev
9 years ago
class FormatReiserfs(FormatProcessGeneric):
format_util = "/sbin/mkfs.reiserfs"
def param(self):
return self.get_label(), "-f", self.dev
def label(self):
return "-l", self._label
9 years ago
class FormatNilfs2(FormatProcessGeneric):
format_util = "/sbin/mkfs.nilfs2"
9 years ago
class FormatXfs(FormatProcessGeneric):
format_util = "/sbin/mkfs.xfs"
def param(self):
return self.get_label(), "-f", self.dev
def nosparse(self):
return "-i", "sparse=0"
def bootparam(self):
return self.get_label(), self.nosparse(), "-f", self.dev
9 years ago
class FormatVfat(FormatProcess):
dos_id = "0b"
gpt_id = "0700"
format_util = "/usr/sbin/mkfs.vfat"
def param(self):
return self.get_label(), "-F", "32", self.dev
def label(self):
return "-n", self._label
9 years ago
class FormatNtfs(FormatProcessGeneric):
dos_id = "07"
gpt_id = "0700"
format_util = "/usr/sbin/mkfs.ntfs"
def param(self):
return self.get_label(), "-FQ", self.dev
9 years ago
class FormatUefi(FormatVfat):
dos_id = "ef"
gpt_id = "EF00"
def label(self):
return ()
9 years ago
class FormatSwap(FormatProcess):
dos_id = "82"
gpt_id = "8200"
format_util = "/sbin/mkswap"
def param(self):
return self.dev,
9 years ago
class FormatBtrfs(FormatProcessGeneric):
format_util = "/sbin/mkfs.btrfs"
def param(self):
return "-f", self.get_label(), self.dev
class PartitionDistributive(Distributive):
9 years ago
format_map = {
'ext2': FormatExt2,
'ext3': FormatExt3,
'ext4': FormatExt4,
'jfs': FormatJfs,
'f2fs': FormatF2FS,
9 years ago
'reiserfs': FormatReiserfs,
'btrfs': FormatBtrfs,
'nilfs2': FormatNilfs2,
'xfs': FormatXfs,
'vfat': FormatVfat,
'ntfs-3g': FormatNtfs,
'ntfs': FormatNtfs,
'uefi': FormatUefi,
'btrfs': FormatBtrfs,
'btrfs-compress': FormatBtrfs,
9 years ago
'swap': FormatSwap
}
def __init__(self, partition, parent=None, mdirectory=None,
check=False, multipartition=None, flagRemoveDir=True,
fileSystem="ext4", isFormat=True, systemId=None,
rootLabel="Calculate", partitionTable=None,
compression=None):
"""Initialize partition distributive
mdirectory - directory for mount
check - check partition name and raise DistributiveError if partition
has bad name
"""
9 years ago
Distributive.__init__(self, parent=parent)
self.partition = partition
self.fileSystem = fileSystem
self.mdirectory = mdirectory or DefaultMountPath.DefaultMount
self.multipartition = multipartition
self.flagRemoveDir = flagRemoveDir
self.isFormat = isFormat
self.DirectoryObject = None
self.systemId = systemId
self.rootLabel = rootLabel
self.compression = compression
13 years ago
self.partitionTable = partitionTable
def getType(self):
9 years ago
return _("partition '%s'") % self.partition
def probe(self):
"""Check directory for flash content"""
try:
pathname = self.getDirectory()
9 years ago
except Exception:
return False
return Linux().detectOtherShortname(pathname)
9 years ago
def _mountPartition(self, partition, directory, opts=""):
"""Mount partition to directory"""
self._makeDirectory(directory)
if "ntfs" in opts:
source_dir = isMount(partition)
if source_dir:
9 years ago
self._mountToBind(source_dir, directory)
return
9 years ago
self._mountToDirectory(partition, directory, opts)
9 years ago
def _umountPartition(self, directory):
"""Umount partition and remove directory"""
self._umountDirectory(directory)
if self.flagRemoveDir:
self._removeDirectory(directory)
9 years ago
def releaseChild(self, child):
"""Umount child Directory distributive"""
9 years ago
if isinstance(child, DirectoryDistributive):
self._umountPartition(child.directory)
child.directory = None
9 years ago
def _mountBind(self, srcDirectory, destDirectory):
"""Mount directory to directory"""
self._makeDirectory(destDirectory)
self._makeDirectory(srcDirectory)
9 years ago
self._mountToBind(srcDirectory, destDirectory)
def postinstallMountBind(self):
"""Mount bind mount point and create mount dirs"""
if self.multipartition and self.DirectoryObject:
3 years ago
mulipartDataBind = list(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)
9 years ago
DirectoryDistributive(realDestDir, parent=partObj)
def getEfiDirectories(self):
return [path.join(self.getDirectory(), x)
for x in self.multipartition.getMountPoints()
if x.startswith("/boot/efi")
]
def getMultipartData(self):
"""Get multipartition data"""
mulipartData = zip(self.multipartition.getPartitions(),
self.multipartition.getMountPoints(),
self.multipartition.getFileSystems(),
13 years ago
self.multipartition.getIsFormat(),
self.multipartition.getPartitionTable())
return mulipartData
def convertToDirectory(self):
"""Convert partition to directory by mounting"""
mapFS = {'btrfs-compress': 'btrfs'}
mapOpts = {'btrfs-compress': ' -o compress=%s' % self.compression}
mdirectory = self.mdirectory
for child in self.childs:
9 years ago
if isinstance(child, DirectoryDistributive) and \
mdirectory in child.directory:
return child
mdirectory = self._getMntDirectory(mdirectory)
self._mountPartition(self.partition, mdirectory,
mapOpts.get(self.fileSystem,""))
9 years ago
dirObj = DirectoryDistributive(mdirectory, parent=self)
if self.multipartition:
9 years ago
mulipartDataNotBind = filter(lambda x: x[2] != "bind",
self.getMultipartData())
9 years ago
for dev, mountPoint, fileSystem, isFormat, partTable \
in sorted(mulipartDataNotBind, key=lambda x: x[1]):
realMountPoint = None
if fileSystem != "swap":
realMountPoint = pathJoin(mdirectory, mountPoint)
self._mountPartition(
dev, realMountPoint,
"-t %s" % mapFS.get(fileSystem,fileSystem) +
mapOpts.get(fileSystem,""))
partObj = PartitionDistributive(dev, flagRemoveDir=False,
fileSystem=fileSystem,
isFormat=isFormat,
parent=dirObj)
9 years ago
if realMountPoint is not None:
DirectoryDistributive(realMountPoint, parent=partObj)
self.DirectoryObject = dirObj
return dirObj
def formatAllPartitions(self):
"""Format all partitions"""
FS, DEV, NEEDFORMAT, NEWID, PARTTABLE, MP = 0, 1, 2, 3, 4, 5
# get all information to matrix
9 years ago
dataPartitions = zip(self.multipartition.getFileSystems() + \
[self.fileSystem],
self.multipartition.getPartitions() + \
[self.partition],
self.multipartition.getIsFormat() + \
[self.isFormat],
self.multipartition.getSystemId() + \
13 years ago
[self.systemId],
self.multipartition.getPartitionTable() + \
[self.partitionTable],
self.multipartition.getMountPoints() + \
["/"])
# get partition which need format
3 years ago
formatPartitions = list(map(lambda x: (x[FS], x[DEV], x[NEWID], x[MP]),
9 years ago
filter(
lambda x: x[NEEDFORMAT] and x[FS] != "bind",
3 years ago
dataPartitions)))
# if has separate /boot partition
bootmp = ["/boot", "/"]
purpose_map = {"/boot": "boot",
"/var/calculate": "calculate",
"/": "root"}
# format all get partition
for fileSystem, dev, newID, mp in formatPartitions:
9 years ago
if fileSystem == "swap":
self.formatSwapPartition(dev)
else:
if newID == "EF00":
fileSystem = "uefi"
if dev == self.partition:
self.formatPartition(dev, format=fileSystem,
label=self.rootLabel,
purpose=purpose_map.get(mp, None))
else:
if mp == '/var/calculate':
self.formatPartition(dev, format=fileSystem,
label="Calculate")
else:
self.formatPartition(dev, format=fileSystem,
purpose=purpose_map.get(mp, None))
# change system id for partitions
3 years ago
changeidPartitions = list(map(lambda x: (x[NEWID], x[DEV], x[PARTTABLE]),
9 years ago
filter(lambda x: x[NEWID],
3 years ago
dataPartitions)))
13 years ago
for systemid, dev, partTable in changeidPartitions:
9 years ago
self.changeSystemID(dev, systemid, partTable)
return True
def _checkMount(self, dev):
"""Checking mount point"""
if isMount(dev):
raise DistributiveError(
9 years ago
_("Failed to format %s: this partition is mounted") % dev)
def formatPartition(self, dev, format="ext4", label="", purpose=None):
"""Format partition"""
if not format in self.format_map:
raise DistributiveError(
9 years ago
_("The specified format of '%s' is not supported") % format)
with open("/proc/swaps") as f:
if dev in map(lambda y: y.split(" ")[0],
filter(lambda x: x.startswith("/"),f)):
raise DistributiveError(
_("Failed to format %s: this partition is used as swap") % dev)
self._checkMount(dev)
9 years ago
if not os.access(dev, os.W_OK):
raise DistributiveError(_("Failed to format the partition") +
9 years ago
" %s:\n%s" % (dev, _("Permission denied")))
format_process = self.format_map[format](dev, label=label,
purpose=purpose, compression=self.compression)
9 years ago
if format_process.failed():
raise DistributiveError(
_("Failed to format the partition") +
" %s:\n%s" % (dev, format_process.readerr()))
format_process.postaction()
def performFormat(self):
"""Perform format for all partition of this distributive"""
if self.multipartition:
self.formatAllPartitions()
elif self.isFormat:
9 years ago
self.formatPartition(self.partition, format=self.fileSystem,
label=self.rootLabel, purpose="root")
if self.systemId:
9 years ago
self.changeSystemID(self.partition, self.systemId,
self.partitionTable)
9 years ago
def changeSystemID(self, dev, systemid, parttable):
"""Change partition id, specified by systemid"""
deviceName = detectDeviceForPartition(dev)
13 years ago
if deviceName is None:
raise DistributiveError(
9 years ago
_("Failed to determine the parent device for %s") % dev)
# device hasn't any partition
elif deviceName == "":
return True
9 years ago
fdiskProg, gdiskProg = checkUtils('/sbin/fdisk', '/usr/sbin/gdisk')
info = device.udev.get_device_info(name=dev)
partitionNumber = (info.get('ID_PART_ENTRY_NUMBER','') or
info.get('UDISKS_PARTITION_NUMBER', ''))
devicePartitionCount = countPartitions(deviceName)
if deviceName and not partitionNumber:
raise DistributiveError(
9 years ago
_("Failed to determine the partition number for %s") % dev)
if parttable in ("dos", "primary", "extended", "logical"):
fdisk = process(fdiskProg, deviceName, stderr=STDOUT)
pipe = Popen([fdiskProg, deviceName],
stdin=PIPE, stdout=PIPE, stderr=PIPE)
if devicePartitionCount > 1:
9 years ago
pipe.stdin.write("t\n%s\n%s\nw\n" % (partitionNumber,
systemid))
else:
9 years ago
pipe.stdin.write("t\n%s\nw\n" % systemid)
13 years ago
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))
13 years ago
else:
pipe.stdin.write("t\n%s\nw\ny\n" % systemid)
13 years ago
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(
9 years ago
_(
"Failed to found partition %s after changing the system ID") % dev)
def formatSwapPartition(self, dev):
"""Format swap partition"""
with open("/proc/swaps") as f:
if dev in map(lambda y: y.split(" ")[0],
filter(lambda x: x.startswith("/"),f)):
raise DistributiveError(
_("Failed to execute 'mkswap %s': "
"the swap partition is used "
"by the current system") % dev)
if isMount(dev):
raise DistributiveError(
9 years ago
_("Failed to format %s: this partition is mounted") % dev)
format_process = self.format_map["swap"](dev)
if format_process.failed():
raise DistributiveError(
_("Failed to format the swap partition") +
" %s:\n%s" % (dev, format_process.readerr()))
12 years ago
def installFrom(self, source, **kwargs):
"""Install distributive to partition from source distributive"""
# get currect partition as directory
distrTo = self.convertToDirectory()
# install into directroy distributive from source
9 years ago
distrTo.installFrom(source, **kwargs)
def serialize(self):
d = {'type': Distributive.Type.Partition,
'partition': self.partition,
'mdirectory': self.mdirectory,
'flag_remove_directory': self.flagRemoveDir,
'childs': [x.serialize() for x in self.childs]}
return d
@classmethod
@Distributive.required("partition", "mdirectory", "flag_remove_directory")
9 years ago
def unserialize(cls, data, parent=None):
ld = PartitionDistributive(_u8(data['partition']), parent=parent)
ld.mdirectory = _u8(data['mdirectory'])
ld.flagRemoveDir = _u8(data['flag_remove_directory'])
ld.childs = [Distributive.unserialize(x, parent=ld) for x in
data.get('childs', [])]
return ld
class ArchiveDistributive(Distributive):
9 years ago
def __init__(self, file, parent=None,
mdirectory="/var/calculate/tmp/stage"):
Distributive.__init__(self, parent=parent)
self.file = file
self.mdirectory = mdirectory
def getType(self):
9 years ago
return _("archive %s") % self.file
9 years ago
def _detectArchive(self, file):
"""Detect archive by "/usr/bin/file" command
Return bzip2,gzip,7z or None
"""
9 years ago
file_cmd = getProgPath('/usr/bin/file')
p_file = process(file_cmd, file, stderr=process.STDOUT)
if "bzip2 compressed data" in p_file.read():
return "bzip2"
9 years ago
elif "gzip compressed data" in p_file.read():
return "gzip"
9 years ago
elif "7-zip archive data" in p_file.read():
return "7z"
elif "XZ compressed data" in p_file.read():
return "xz"
elif file and file.endswith(".tar.lzma"):
if path.exists('/usr/bin/7za'):
return "7z"
else:
return "lzma"
return None
9 years ago
def _unpackArchive(self, archfile, directory):
"""Unpack archive"""
# archive is exists
if not path.exists(archfile):
9 years ago
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
9 years ago
tar_cmd = getProgPath('/bin/tar')
if archiveType == "7z":
9 years ago
arch_cmd = getProgPath('/usr/bin/7za')
params = ["x", "-so"]
elif archiveType == "lzma":
9 years ago
arch_cmd = getProgPath('/usr/bin/lzma')
params = ["-dc"]
elif archiveType == "bzip2":
9 years ago
arch_cmd = getProgPath('/bin/bunzip2')
params = ["-dc"]
elif archiveType == "gzip":
9 years ago
arch_cmd = getProgPath('/bin/gzip')
params = ["-dc"]
elif archiveType == "xz":
arch_cmd = getProgPath('/bin/xz')
params = ["-dc"]
else:
9 years ago
raise DistributiveError(_("Unknown archive type '%s'") %
archfile)
9 years ago
if not arch_cmd or not tar_cmd:
raise DistributiveError(_("Archive type '%s' is not supported") %
archiveType)
arch_process = process(*([arch_cmd] + params + [archfile]))
tar_process = process(tar_cmd, "xf", "-", "-C", "%s/" % directory,
stdin=arch_process)
if tar_process.failed():
arch_error = arch_process.readerr().strip()
tar_error = tar_process.readerr().strip()
if arch_error:
message = "%s\n%s" % (arch_error, tar_error)
else:
message = tar_error
raise DistributiveError(_("Unpacking error\n%s") % message)
9 years ago
def unpackTo(self, directory):
"""Unpack currect archive to directory"""
9 years ago
self._unpackArchive(self.file, directory)
def convertToDirectory(self):
"""Get archive as directory (unpack to directory)"""
# check may be the archive already unpacked
raise DistributiveError(_("Unsupported"))
mdirectory = self.mdirectory
for child in self.childs:
9 years ago
if isinstance(child, DirectoryDistributive) and \
mdirectory in child.directory:
return child
# get temporary directory for unpacking
mdirectory = self._getMntDirectory(mdirectory)
9 years ago
dirdist = DirectoryDistributive(mdirectory, parent=self)
self._unpackArchive(self.file, mdirectory)
return dirdist
9 years ago
def releaseChild(self, child):
"""Remove child Directory distributive"""
9 years ago
if isinstance(child, DirectoryDistributive):
self._removeDirectory(child.directory)
child.directory = None
9 years ago
def packToArchive(self, directory, file):
tar_command = getProgPath("/usr/bin/tar")
p = process(tar_command, "cf", file, "-C", directory, ".",
stderr=process.STDOUT)
if p.failed():
raise DistributiveError(_("Failed to create the archive") +
9 years ago
" '%s':\n%s" % (file, p.read()))
12 years ago
def installFrom(self, source, **kwargs):
"""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)
def serialize(self):
d = {'type': Distributive.Type.Archive,
'file': self.file,
9 years ago
'mdirectory': self.mdirectory,
'childs': [x.serialize() for x in self.childs]}
return d
@classmethod
9 years ago
@Distributive.required("mdirectory", "file")
def unserialize(cls, data, parent=None):
ld = ArchiveDistributive(_u8(data['file']),
mdirectory=_u8(data['mdirectory']),
parent=parent)
ld.childs = [Distributive.unserialize(x, parent=ld)
9 years ago
for x in data.get('childs', [])]
return ld
9 years ago
class ContainerDistributive(ArchiveDistributive):
def __init__(self, basedir, parent=None, exclude=None,
include=None,
mdirectory="/var/calculate/tmp/stage"):
Distributive.__init__(self, parent=parent)
self.basedirectory = basedir
self.file = path.join(basedir, "rootfs.tar.xz")
self.meta = path.join(basedir, "meta.tar.xz")
self.lxd = path.join(basedir, "lxd.tar.xz")
self.exclude = [] if not exclude else exclude
self.include = [] if not include else include
self.mdirectory = mdirectory
def mtime2build(self, fn):
if path.exists(fn):
build_time = datetime.datetime.fromtimestamp(os.stat(fn).st_mtime)
return build_time.strftime("%Y%m%d")
return ""
def packToArchive(self, directory, file):
if not path.exists(self.basedirectory):
self._makeDirectory(self.basedirectory)
tar_command = getProgPath("/usr/bin/tar")
params = ["cf", file, "-J", "-C", directory]
if self.exclude:
exclude_list = list(calculate_exclude(
directory, exclude=self.exclude, include=self.include))
3 years ago
params += list(map(lambda x: "--exclude=./%s" % x, exclude_list))
params += ["."]
#debug_file = "/var/calculate/tmp/rootfs.tar.xz"
#if path.exists(debug_file):
# p = process("/bin/cp", debug_file, file)
#else:
p = process(tar_command, *params, stderr=process.STDOUT)
try:
if p.failed():
raise DistributiveError(_("Failed to create the archive") +
" '%s':\n%s" % (file, p.read()))
except BaseException:
removeDir(self.basedirectory)
raise
def get_information(self):
if path.exists(self.lxd):
with xztaropen(self.lxd) as f:
try:
metadata_yaml = f.getmember("metadata.yaml")
metadata = json.load(f.extractfile(metadata_yaml))
return {
'os_linux_build':
metadata["calculate"]['os_linux_build'],
'os_arch_machine':
metadata["calculate"]['os_arch_machine'],
'os_linux_shortname':
metadata["calculate"]['os_linux_shortname'],
'os_linux_subname':
metadata["calculate"]['os_linux_subname'],
'cl_profile_name':
metadata["calculate"]['cl_profile_name'],
'os_linux_name':
metadata["calculate"]['os_linux_name'],
'os_linux_ver':
metadata["calculate"].get('os_linux_ver', "17"),
}
except (KeyError, ValueError) as e:
pass
return {
'os_linux_build': self.mtime2build(self.file),
'os_arch_machine': "x86_64",
'os_linux_shortname': 'Container',
'os_linux_subname': "",
'cl_profile_name': "",
'os_linux_name': _('Unknown Container')
}
class SquashDistributive(Distributive):
def __init__(self, file, parent=None, mdirectory=None, exclude=None,
compress="", include=None):
Distributive.__init__(self, parent=parent)
self.file = file
self.mdirectory = mdirectory or DefaultMountPath.SquashImage
12 years ago
self.exclude = exclude or []
self.include = include or []
self.compress = compress if compress and compress != "gzip" else ""
def getType(self):
9 years ago
return _("squash image %s") % self.file
9 years ago
def _mountSquash(self, file, directory):
"""Mount squashfs to directory"""
self._makeDirectory(directory)
9 years ago
self._mountToDirectory(file, directory, mountopts="-o loop -t squashfs")
9 years ago
def _umountSquash(self, directory):
self._umountDirectory(directory)
self._removeDirectory(directory)
def convertToDirectory(self):
mdirectory = self.mdirectory
for child in self.childs:
9 years ago
if isinstance(child, DirectoryDistributive) and \
child.directory and \
mdirectory in child.directory:
return child
mdirectory = self._getMntDirectory(mdirectory)
9 years ago
self._mountSquash(self.file, mdirectory)
return DirectoryDistributive(mdirectory, parent=self)
9 years ago
def releaseChild(self, child):
"""Umount child Directory distributive"""
9 years ago
if isinstance(child, DirectoryDistributive):
self._umountSquash(child.directory)
child.directory = None
def packToSquash(self, directory, file, **kwargs):
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]
13 years ago
if self.compress:
cmd += ["-comp", self.compress]
cmd += ["-progress"]
if self.exclude:
exclude_list = list(calculate_exclude(directory, exclude=self.exclude,
include=self.include))
cmd += ["-e"] + exclude_list
9 years ago
# возможность использовать заранее подготовленный livecd.squashfs
if path.exists('/var/calculate/tmp/livecd.squashfs'):
os.system('cp -L /var/calculate/tmp/livecd.squashfs %s' % file)
else:
callbackProgress = kwargs.get('callbackProgress', None)
processMkSquash = PercentProgress(*cmd, stderr=STDOUT, atty=True)
for perc in processMkSquash.progress():
9 years ago
if callable(callbackProgress):
callbackProgress(perc)
if processMkSquash.failed():
raise DistributiveError(_("Failed to create squashfs") +
" '%s':\n%s" % (
9 years ago
file, processMkSquash.read()))
12 years ago
def installFrom(self, source, **kwargs):
"""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, **kwargs)
def serialize(self):
d = {'type': Distributive.Type.SquashFS,
'file': self.file,
9 years ago
'mdirectory': self.mdirectory,
'childs': [x.serialize() for x in self.childs]}
return d
@classmethod
9 years ago
@Distributive.required("mdirectory", "file")
def unserialize(cls, data, parent=None):
ld = SquashDistributive(_u8(data['file']),
mdirectory=_u8(data['mdirectory']),
parent=parent)
ld.childs = [Distributive.unserialize(x, parent=ld)
9 years ago
for x in data.get('childs', [])]
return ld
class IsoDistributive(Distributive):
def __init__(self, file, parent=None, mdirectory=None,
bdirectory=None, exclude=None, compress="gzip",
vol_id="CALCULATE", include=None):
if bdirectory is None:
bdirectory = DefaultMountPath.BuildDirectory
Distributive.__init__(self, parent=parent)
self.file = file
self.vol_id = vol_id
if path.isdir(self.file):
self.mdirectory = self.file
else:
self.mdirectory = mdirectory or DefaultMountPath.IsoImage
if file == bdirectory:
self.bdirectory = file
else:
self.bdirectory = self._getMntDirectory(bdirectory)
self.exclude = [] if not exclude else exclude
self.include = [] if not include else include
13 years ago
self.compress = compress
self.eventPrepareIso = Signal()
def getType(self):
9 years ago
tf = typeFile(magic=MAGIC_COMPRESS | MAGIC_SYMLINK | MAGIC_CONTINUE)
ftype = tf.getMType(self.file)
if "block special" in ftype:
9 years ago
return _("live image %s") % self.file
if path.isdir(self.file):
9 years ago
return _("image directory %s") % self.file
else:
9 years ago
return _("ISO image %s") % self.file
def probe(self):
"""Check directory for iso content"""
try:
pathname = self.getIsoContentDirectory()
9 years ago
except Exception:
return False
9 years ago
return path.exists(path.join(pathname, "syslinux")) and \
path.exists(path.join(pathname, "isolinux"))
def get_squash_size(self):
try:
dn = self.getIsoContentDirectory()
fn = path.join(dn, "livecd.squashfs")
return path.getsize(fn)
except Exception:
raise DistributiveError(_("Failed to get size of the squash image"))
9 years ago
def _mountIso(self, file, directory):
if self.file != self.mdirectory:
self._makeDirectory(directory)
9 years ago
tf = typeFile(magic=MAGIC_COMPRESS | MAGIC_SYMLINK | MAGIC_CONTINUE)
ftype = tf.getMType(file)
if "block special" in ftype:
mopts = "-o ro"
else:
mopts = "-o ro,loop"
9 years ago
self._mountToDirectory(file, directory, mountopts=mopts)
9 years ago
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:
9 years ago
if isinstance(child, SquashDistributive) and \
mdirectory in child.file:
return child
if self.mdirectory != self.file:
mdirectory = self._getMntDirectory(mdirectory)
9 years ago
self._mountIso(self.file, mdirectory)
fileLive = self._getLastLive(mdirectory)
if not fileLive:
self._umountIso(mdirectory)
raise DistributiveError(_("ISO %s contains no live image") %
9 years ago
self.file)
return SquashDistributive(path.join(mdirectory, fileLive),
parent=self, exclude=self.exclude,
compress=self.compress,
include=self.include)
def getIsoContentDirectory(self):
"""Return directory with content of iso image"""
squash = self.convertToSquash()
return path.dirname(squash.file)
9 years ago
def releaseChild(self, child):
"""Umount child Directory distributive"""
9 years ago
if isinstance(child, SquashDistributive):
self._umountIso(path.dirname(child.file))
child.directory = None
def convertToDirectory(self):
return self.convertToSquash().convertToDirectory()
def _get_iso_util(self):
mkisoUtil = '/usr/bin/mkisofs'
if not path.exists(mkisoUtil):
raise DistributiveError(
"{errmess} : {errdescr}".format(
errmess=_("Failed to create the ISO image"),
errdescr=_("command '%s' not found") % mkisoUtil))
return mkisoUtil
def getMkIso(self, output=None, source=None, volume_id=None,
efi_image=None):
"""
Параметры mkisofs при создании образа с поддержкой EFI и обычного обарза
:param output:
:param source:
:param volume_id:
:param efi_image:
:return:
"""
mkisoUtil = self._get_iso_util()
if efi_image is None:
params = ["-b", "isolinux/isolinux.bin", "-no-emul-boot",
"-V", volume_id,
"-boot-load-size", "4",
"-boot-info-table", "-iso-level", "4",
"-hide", "boot.catalog"]
else:
params = ["-J", "-R", "-l", "-no-emul-boot", "-boot-load-size", "4",
"-udf", "-boot-info-table", "-iso-level", "4",
"-b", "isolinux/isolinux.bin",
"-V", volume_id,
"-c", "isolinux/boot.cat", "-eltorito-alt-boot",
"-no-emul-boot", "-eltorito-platform", "efi",
"-eltorito-boot", efi_image]
return [mkisoUtil] + params + ["-o", output, "%s/" % source]
def packToIso(self, directory, file, **kwargs):
# remove previous version of iso
try:
if path.exists(file):
os.unlink(file)
except (Exception, KeyboardInterrupt) as e:
raise DistributiveError(_("Failed to remove") +
" %s:\n%s" % (file, str(e)))
if not path.exists(path.dirname(file)):
makeDirectory(path.dirname(file))
efi_image = 'boot/grub/efi.img'
if path.exists(path.join(directory, efi_image)):
cmd = self.getMkIso(source=directory, output=file,
volume_id=self.vol_id, efi_image=efi_image)
else:
cmd = self.getMkIso(source=directory, output=file,
volume_id=self.vol_id)
callback_progress = kwargs.get('callbackProgress', None)
processMkIsoFs = PercentProgress(*cmd, stderr=STDOUT, atty=True)
for perc in processMkIsoFs.progress():
9 years ago
if callable(callback_progress):
callback_progress(perc)
if processMkIsoFs.failed():
raise DistributiveError(_("Failed to create the ISO image") +
" %s:\n%s" % (file, processMkIsoFs.read()))
else:
return True
12 years ago
def installFrom(self, source, **kwargs):
"""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))
9 years ago
liveimage = "livecd.squashfs.%d" % (curnum + 1)
else:
liveimage = "livecd.squashfs"
9 years ago
if isinstance(source, SquashDistributive):
self._copyfile(source.file,
9 years ago
path.join(isoDirectory, liveimage))
else:
distDirectory = source.convertToDirectory()
squashDistr = SquashDistributive(
9 years ago
path.join(isoDirectory, liveimage),
exclude=self.exclude,
include=self.include,
9 years ago
compress=self.compress)
squashDistr.installFrom(distDirectory, **kwargs)
# prepare iso
self.eventPrepareIso.emit(isoDirectory)
# pack iso
if self.bdirectory != self.file:
self.packToIso(isoDirectory, self.file, **kwargs)
9 years ago
except DistributiveError as e:
raise e
9 years ago
except KeyboardInterrupt as e:
raise DistributiveError(_("Keyboard interruption"))
def close(self):
# close all child
if Distributive.close(self):
# remove temporary directory
if path.lexists(self.bdirectory) and self.file != self.bdirectory:
self._removeDirectory(self.bdirectory)
def serialize(self):
d = {'type': Distributive.Type.Iso,
'file': self.file,
9 years ago
'mdirectory': self.mdirectory,
'bdirectory': self.bdirectory,
'childs': [x.serialize() for x in self.childs]}
return d
@classmethod
9 years ago
@Distributive.required("mdirectory", "bdirectory", "file")
def unserialize(cls, data, parent=None):
ld = IsoDistributive(_u8(data['file']),
mdirectory=_u8(data['mdirectory']),
bdirectory=_u8(data['bdirectory']),
parent=parent)
ld.childs = [Distributive.unserialize(x, parent=ld)
9 years ago
for x in data.get('childs', [])]
return ld
9 years ago
class FlashDistributive(PartitionDistributive):
9 years ago
def _checkMount(self, dev):
"""Checking mount point"""
mp = isMount(dev)
if mp:
if mp.startswith('/media'):
self._umountDirectory(mp)
else:
raise DistributiveError(
_("Failed to format %s: this partition is mounted") % dev)
def performFormat(self):
"""Perform format for all partition of this distributive"""
if not self.isFormat:
dn = self.getDirectory()
clear_match = re.compile(
r"^(boot|efi|isolinux|syslinux|id.*\.uefi|"
"ldlinux.c32|ldlinux.sys|livecd|livecd.squashfs)$")
3 years ago
for fn in list(filter(clear_match.match, listDirectory(dn))):
full_path = path.join(dn, fn)
try:
if path.isdir(full_path):
removeDir(full_path)
else:
os.unlink(full_path)
except (OSError, IOError):
raise DistributiveError(
_("Failed to remove %s") % fn)
else:
super(FlashDistributive, self).performFormat()
def getType(self):
9 years ago
return _("USB flash %s") % self.partition
def probe(self):
"""Check directory for flash content"""
try:
pathname = self.getDirectory()
9 years ago
except Exception:
return False
9 years ago
return path.exists(path.join(pathname, "syslinux")) and \
path.exists(path.join(pathname, "isolinux"))
def writeable(self, mp):
"""
Перемонитровать в rw, при необходимости
"""
if not check_rw(mp):
# перемонитровать в rw
p = process('/bin/mount', '-o', 'remount,rw', mp, stderr=STDOUT)
if p.success() and check_rw(mp):
return True
return False
else:
return True
def convertToDirectory(self):
mp = isMount(self.partition)
if mp and self.writeable(mp):
d = DirectoryDistributive(mp)
d.no_unmount = True
return d
return super(FlashDistributive, self).convertToDirectory()
def releaseChild(self, child):
"""Umount child Directory distributive"""
if isinstance(child, DirectoryDistributive):
if not child.no_unmount:
self._umountPartition(child.directory)
child.directory = None
12 years ago
def installFrom(self, source, **kwargs):
"""Install distributive to partition from source distributive"""
# make temporary directory for creating iso image
distrTo = self.convertToDirectory()
# getting squash from source
9 years ago
if isinstance(source, IsoDistributive):
self.rsync(source.getIsoContentDirectory(), distrTo.directory,
byfile=lambda x: x.startswith('livecd.'),
noperm=True, **kwargs)
else:
raise DistributiveError(
9 years ago
_("Flash install does not support %s") %
source.__class__.__name__)
class PxeDistributive(Distributive):
needFormat = False
9 years ago
def __init__(self, directory, parent=None):
Distributive.__init__(self, parent=parent)
self.directory = path.join(directory, "calculate")
self.origdir = directory
def getDirectory(self):
return self.origdir
9 years ago
def installFrom(self, source, **kwargs):
# make temporary directory for creating iso image
distrTo = self.directory
# getting squash from source
9 years ago
if isinstance(source, IsoDistributive):
if path.exists(self.directory):
removeDir(self.directory)
self._makeDirectory(self.directory)
9 years ago
self.rsync(source.getIsoContentDirectory(), distrTo,
byfile=lambda x: x.startswith('livecd.'),
**kwargs)
else:
raise DistributiveError(
9 years ago
_("PXE install does not support %s" %
source.__class__.__name__))
class LayeredDistributive(Distributive):
"""
Каталог дистрибутива для сборки
"""
needFormat = False
def __init__(self, mdirectory, diff_directory, image_file=None,
parent=None):
"""
:param mdirectory: результирующий каталог
:param diff_directory: каталог содержит изменения от оригинала
:param image_file: образ оригинала
:param parent: родительский дистрибутив
"""
super(LayeredDistributive, self).__init__(parent=parent)
self.mdirectory = mdirectory
self.diff_directory = diff_directory
self.workdir = "%s-work" % self.diff_directory
self.image_mount_dir = None
self.image_distro = None
self.image_file = image_file
def post_clear(self):
if path.exists(self.diff_directory):
self._removeDirectory(self.diff_directory)
self.clear_empty_directories(self.diff_directory)
return True
def getType(self):
return _("layered '{file} {diff}'").format(
file=self.image_file,
diff=self.diff_directory)
def clearDiff(self):
if path.exists(self.diff_directory):
self._removeDirectory(self.diff_directory)
def _mountLayers(self, target):
"""Mount squashfs to directory"""
self._makeDirectory(target)
if not path.exists(self.diff_directory):
self._makeDirectory(self.diff_directory)
if path.exists(self.workdir):
self._removeDirectory(self.workdir)
if not path.exists(self.workdir):
self._makeDirectory(self.workdir)
self._mountToDirectory("none", target, mountopts=(
"-t overlay -o lowerdir=%(static)s,"
"upperdir=%(upper)s,workdir=%(workdir)s" %
{"upper": self.diff_directory,
"workdir": self.workdir,
"static": self.image_mount_dir}))
def _umountLayers(self, directory):
self._umountDirectory(directory)
self._removeDirectory(directory)
self._removeDirectory(self.workdir)
def _mountLiveImage(self):
"""Mount squashfs to directory"""
self.image_distro = IsoDistributive(file=self.image_file, parent=self)
self.image_mount_dir = (
self.image_distro.convertToDirectory().getDirectory())
def _umountLiveImage(self):
if self.image_distro:
self.image_distro.close()
self.image_distro = None
def convertToDirectory(self):
"""Convert scrach directories to one directory"""
mdirectory = self.mdirectory
for child in self.childs:
9 years ago
if isinstance(child, DirectoryDistributive) and \
mdirectory in child.directory:
return child
mdirectory = self._getMntDirectory(mdirectory)
self._mountLiveImage()
self._mountLayers(mdirectory)
return DirectoryDistributive(mdirectory, parent=self)
def releaseChild(self, child):
9 years ago
"""Unmount child Directory distributive"""
if isinstance(child, DirectoryDistributive):
self._umountLayers(child.directory)
self._umountLiveImage()
child.directory = None
def installFrom(self, source, **kwargs):
"""Install distributive to partition from source distributive"""
# make temporary directory for creating iso image
if isinstance(source, (IsoDistributive, SquashDistributive)):
self.image_file = source.file
return True
9 years ago
raise DistributiveError(
_("Install with layers does not support %s") %
source.__class__.__name__)
def serialize(self):
d = {'type': Distributive.Type.Layered,
9 years ago
'mdirectory': self.mdirectory,
'diff_directory': self.diff_directory,
'image_file': self.image_file,
'childs': [x.serialize() for x in self.childs]}
if self.image_mount_dir:
d['image_mount_dir'] = self.image_mount_dir
return d
@classmethod
@Distributive.required("mdirectory", "diff_directory", "image_file")
9 years ago
def unserialize(cls, data, parent=None):
ld = LayeredDistributive(_u8(data['mdirectory']),
_u8(data['diff_directory']),
_u8(data['image_file']),
parent=parent)
ld.childs = [Distributive.unserialize(x, parent=ld)
9 years ago
for x in data.get('childs', [])]
return ld