|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
# Copyright 2010-2015 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
|
|
|
from itertools import *
|
|
|
from functools import partial
|
|
|
from calculate.lib.datavars import VariableError
|
|
|
|
|
|
from calculate.lib.utils.files import (isMount, removeDir,
|
|
|
processProgress, STDOUT,
|
|
|
typeFile, pathJoin, process,
|
|
|
listDirectory, checkUtils,
|
|
|
MAGIC_COMPRESS, MAGIC_SYMLINK,
|
|
|
MAGIC_CONTINUE, makeDirectory,
|
|
|
isEmpty, check_rw,
|
|
|
PercentProgress, getProgPath)
|
|
|
from calculate.lib.utils.device import (detectDeviceForPartition,
|
|
|
getUdevDeviceInfo, countPartitions)
|
|
|
from calculate.lib.utils.tools import classproperty, Signal, traverse
|
|
|
from calculate.lib.utils.text import _u8
|
|
|
from calculate.lib.variables.linux import LinuxDataVars, Linux
|
|
|
|
|
|
from calculate.lib.cl_lang import setLocalTranslate, _
|
|
|
|
|
|
setLocalTranslate('cl_install3', sys.modules[__name__])
|
|
|
|
|
|
|
|
|
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):
|
|
|
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
|
|
|
|
|
|
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
|
|
|
if self.maxfiles:
|
|
|
percent = 100 * self.value / self.maxfiles
|
|
|
percent = min(percent, 99)
|
|
|
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.
|
|
|
"""
|
|
|
|
|
|
class Type(object):
|
|
|
Directory = "dir"
|
|
|
Partition = "part"
|
|
|
SquashFS = "squash"
|
|
|
Archive = "arch"
|
|
|
Iso = "iso"
|
|
|
Layered = "layered"
|
|
|
|
|
|
mountError = _("Failed to mount") + " %s:\n%s"
|
|
|
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
|
|
|
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
|
|
|
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:
|
|
|
return ArchiveDistributive(filename)
|
|
|
elif "Squashfs filesystem" in ftype:
|
|
|
return SquashDistributive(filename)
|
|
|
elif path.isdir(filename):
|
|
|
if 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()
|
|
|
"""
|
|
|
# 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
|
|
|
|
|
|
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()
|
|
|
|
|
|
@staticmethod
|
|
|
def clear_empty_directories(dn):
|
|
|
dn = path.dirname(dn)
|
|
|
while path.exists(dn) and isEmpty(dn):
|
|
|
os.rmdir(dn)
|
|
|
dn = path.dirname(dn)
|
|
|
|
|
|
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 getEfiDirectory(self):
|
|
|
"""Get directory which contains boot/efi"""
|
|
|
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
|
|
|
except Exception, e:
|
|
|
raise DistributiveError(_("Failed to create the directory") +
|
|
|
" '%s':\n%s" % (pathname, str(e)))
|
|
|
except KeyboardInterrupt, e:
|
|
|
raise DistributiveError(_("Failed to create the directory") +
|
|
|
" '%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)
|
|
|
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:
|
|
|
copyfile(infile, outfile)
|
|
|
except Exception as 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:
|
|
|
raise DistributiveError(_("Failed to copy") + " '%s' to '%s':\n%s" \
|
|
|
% (indir, outdir, str(e)))
|
|
|
|
|
|
def rsync(self, fromdir, todir, callbackProgress=None,
|
|
|
byfile=None, filesnum=0, noperm=False, **kwargs):
|
|
|
"""Copy files from 'fromdir' directory to 'todir' directory"""
|
|
|
cpCmd = getProgPath('/bin/cp')
|
|
|
if not cpCmd:
|
|
|
raise DistributiveError(_("'%s' not found") % "cp")
|
|
|
try:
|
|
|
joinFrom = partial(path.join, fromdir)
|
|
|
params = [cpCmd, "-x"] + \
|
|
|
(["--no-preserve=mode,ownership"] if noperm else ["-a"]) + \
|
|
|
map(joinFrom,
|
|
|
ifilterfalse(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:
|
|
|
percFiles = filter(byfile,
|
|
|
listDirectory(fromdir))
|
|
|
if len(percFiles) > 1:
|
|
|
maxPercOnFile = 100 / len(percFiles)
|
|
|
recountPerc = \
|
|
|
lambda perc, num: perc / len(
|
|
|
percFiles) + maxPercOnFile * num
|
|
|
else:
|
|
|
recountPerc = lambda perc, num: perc
|
|
|
for i, copyfile in enumerate(percFiles):
|
|
|
for perc in progressCopyFile(joinFrom(copyfile),
|
|
|
path.join(todir, copyfile)):
|
|
|
if callbackProgress:
|
|
|
callbackProgress(recountPerc(perc, i))
|
|
|
except Exception, e:
|
|
|
res = False
|
|
|
errmes = str(e)
|
|
|
if not res:
|
|
|
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 OSError:
|
|
|
pass
|
|
|
raise DistributiveError(
|
|
|
self.mountError % (srcDirectory, mount.read()))
|
|
|
|
|
|
def performFormat(self):
|
|
|
pass
|
|
|
|
|
|
def formatPartition(self, dev, format="ext4", label=""):
|
|
|
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_list = filter(lambda x: x,
|
|
|
mountopts.split(" "))
|
|
|
mountProcess = process('/bin/mount', file, directory, *mountopts_list,
|
|
|
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 Exception:
|
|
|
pass
|
|
|
raise DistributiveError(
|
|
|
self.mountError % (file, mountProcess.read()))
|
|
|
|
|
|
def _umountDirectory(self, directory):
|
|
|
"""Umount directory"""
|
|
|
if isMount(directory):
|
|
|
processUmount = None
|
|
|
for wait in [0, 0.5, 2, 5]:
|
|
|
sleep(wait)
|
|
|
processUmount = process('/bin/umount', directory, stderr=STDOUT)
|
|
|
if processUmount.success():
|
|
|
return True
|
|
|
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:
|
|
|
if path.lexists(path.join(directory, 'lib64')):
|
|
|
d['os_arch_machine'] = 'x86_64'
|
|
|
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')
|
|
|
except VariableError:
|
|
|
pass
|
|
|
return d.copy()
|
|
|
|
|
|
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",
|
|
|
PartitionDistributive: "partdir"}
|
|
|
extname = mapExtName.get(distr.__class__, "")
|
|
|
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):
|
|
|
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__)
|
|
|
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'},
|
|
|
]
|
|
|
|
|
|
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 mountSystemDirectories(self, skip=("remote",)):
|
|
|
"""
|
|
|
Подключить к дистрибутиву системые ресурсы (/proc, /sys)
|
|
|
:return:
|
|
|
"""
|
|
|
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':
|
|
|
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'") % 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 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"""
|
|
|
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") +
|
|
|
" %s:\n%s" % (self.directory, p.read()))
|
|
|
|
|
|
def post_clear(self):
|
|
|
if path.exists(self.directory):
|
|
|
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
|
|
|
|
|
|
def installFrom(self, source, **kwargs):
|
|
|
"""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, **kwargs)
|
|
|
|
|
|
def serialize(self):
|
|
|
d = {'type': Distributive.Type.Directory,
|
|
|
'directory': self.directory,
|
|
|
'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")
|
|
|
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
|
|
|
|
|
|
|
|
|
class DataPartition(object):
|
|
|
"""Data partition"""
|
|
|
dev = None
|
|
|
mountPoint = None
|
|
|
fileSystem = "ext4"
|
|
|
isFormat = False
|
|
|
systemId = None
|
|
|
partitionTable = None
|
|
|
|
|
|
|
|
|
class MultiPartitions:
|
|
|
"""Data partition list"""
|
|
|
|
|
|
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,...]"""
|
|
|
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 filesystems ["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 FormatProcess(process):
|
|
|
format_util = ""
|
|
|
dos_id = "0"
|
|
|
gpt_id = "0"
|
|
|
|
|
|
def __init__(self, dev, label=None):
|
|
|
self.dev = dev
|
|
|
self._label = label
|
|
|
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)
|
|
|
return list(traverse(
|
|
|
[cmd] + [x for x in self.param() if x]))
|
|
|
|
|
|
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 ()
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
class FormatExt2(FormatProcessGeneric):
|
|
|
format_util = "/sbin/mkfs.ext2"
|
|
|
|
|
|
|
|
|
class FormatExt3(FormatExt2):
|
|
|
format_util = "/sbin/mkfs.ext3"
|
|
|
|
|
|
|
|
|
class FormatExt4(FormatExt2):
|
|
|
format_util = "/sbin/mkfs.ext4"
|
|
|
|
|
|
|
|
|
class FormatJfs(FormatProcessGeneric):
|
|
|
format_util = "/sbin/mkfs.jfs"
|
|
|
|
|
|
def param(self):
|
|
|
return self.get_label(), "-f", self.dev
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
class FormatNilfs2(FormatProcessGeneric):
|
|
|
format_util = "/sbin/mkfs.nilfs2"
|
|
|
|
|
|
|
|
|
class FormatXfs(FormatProcessGeneric):
|
|
|
format_util = "/sbin/mkfs.xfs"
|
|
|
|
|
|
def param(self):
|
|
|
return self.get_label(), "-f", self.dev
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
class FormatUefi(FormatVfat):
|
|
|
dos_id = "ef"
|
|
|
gpt_id = "EF00"
|
|
|
|
|
|
def label(self):
|
|
|
return ()
|
|
|
|
|
|
|
|
|
class FormatSwap(FormatProcess):
|
|
|
dos_id = "82"
|
|
|
gpt_id = "8200"
|
|
|
format_util = "/sbin/mkswap"
|
|
|
|
|
|
def param(self):
|
|
|
return self.dev,
|
|
|
|
|
|
|
|
|
class FormatBtrfs(FormatProcessGeneric):
|
|
|
format_util = "/sbin/mkfs.btrfs"
|
|
|
|
|
|
def param(self):
|
|
|
return "-f", self.get_label(), self.dev
|
|
|
|
|
|
|
|
|
class PartitionDistributive(Distributive):
|
|
|
format_map = {
|
|
|
'ext2': FormatExt2,
|
|
|
'ext3': FormatExt3,
|
|
|
'ext4': FormatExt4,
|
|
|
'jfs': FormatJfs,
|
|
|
'reiserfs': FormatReiserfs,
|
|
|
'btrfs': FormatBtrfs,
|
|
|
'nilfs2': FormatNilfs2,
|
|
|
'xfs': FormatXfs,
|
|
|
'vfat': FormatVfat,
|
|
|
'ntfs-3g': FormatNtfs,
|
|
|
'ntfs': FormatNtfs,
|
|
|
'uefi': FormatUefi,
|
|
|
'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):
|
|
|
"""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 or DefaultMountPath.DefaultMount
|
|
|
self.multipartition = multipartition
|
|
|
self.flagRemoveDir = flagRemoveDir
|
|
|
self.isFormat = isFormat
|
|
|
self.DirectoryObject = None
|
|
|
self.systemId = systemId
|
|
|
self.rootLabel = rootLabel
|
|
|
self.partitionTable = partitionTable
|
|
|
|
|
|
def getType(self):
|
|
|
return _("partition '%s'") % self.partition
|
|
|
|
|
|
def probe(self):
|
|
|
"""Check directory for flash content"""
|
|
|
try:
|
|
|
pathname = self.getDirectory()
|
|
|
except Exception:
|
|
|
return False
|
|
|
return Linux().detectOtherShortname(pathname)
|
|
|
|
|
|
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 sorted(mulipartDataNotBind, key=lambda x: x[1]):
|
|
|
realMountPoint = None
|
|
|
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 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
|
|
|
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],
|
|
|
self.multipartition.getMountPoints() + \
|
|
|
["/"])
|
|
|
# get partition which need format
|
|
|
formatPartitions = map(lambda x: (x[FS], x[DEV], x[NEWID], x[MP]),
|
|
|
filter(
|
|
|
lambda x: x[NEEDFORMAT] and x[FS] != "bind",
|
|
|
dataPartitions))
|
|
|
# format all get partition
|
|
|
for fileSystem, dev, newID, mp in formatPartitions:
|
|
|
if fileSystem == "swap":
|
|
|
self.formatSwapPartition(dev)
|
|
|
else:
|
|
|
if newID == "EF00":
|
|
|
fileSystem = "uefi"
|
|
|
if dev == self.partition:
|
|
|
self.formatPartition(dev, format=fileSystem,
|
|
|
label=self.rootLabel)
|
|
|
else:
|
|
|
if mp == '/var/calculate':
|
|
|
self.formatPartition(dev, format=fileSystem,
|
|
|
label="Calculate")
|
|
|
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 _checkMount(self, dev):
|
|
|
"""Checking mount point"""
|
|
|
if isMount(dev):
|
|
|
raise DistributiveError(
|
|
|
_("Failed to format %s: this partition is mounted") % dev)
|
|
|
|
|
|
def formatPartition(self, dev, format="ext4", label=""):
|
|
|
"""Format partition"""
|
|
|
if not format in self.format_map:
|
|
|
raise DistributiveError(
|
|
|
_("The specified format of '%s' is 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 %s: this partition is used as swap") % dev)
|
|
|
self._checkMount(dev)
|
|
|
if not os.access(dev, os.W_OK):
|
|
|
raise DistributiveError(_("Failed to format the partition") +
|
|
|
" %s:\n%s" % (dev, _("Permission denied")))
|
|
|
|
|
|
format_process = self.format_map[format](dev, label)
|
|
|
if format_process.failed():
|
|
|
raise DistributiveError(
|
|
|
_("Failed to format the partition") +
|
|
|
" %s:\n%s" % (dev, format_process.readerr()))
|
|
|
|
|
|
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)
|
|
|
if self.systemId:
|
|
|
self.changeSystemID(self.partition, self.systemId,
|
|
|
self.partitionTable)
|
|
|
|
|
|
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', '') or \
|
|
|
getUdevDeviceInfo(name=dev).get('UDISKS_PARTITION_NUMBER', '')
|
|
|
devicePartitionCount = countPartitions(deviceName)
|
|
|
if deviceName and not partitionNumber:
|
|
|
raise DistributiveError(
|
|
|
_("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:
|
|
|
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 %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()))
|
|
|
|
|
|
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
|
|
|
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")
|
|
|
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):
|
|
|
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):
|
|
|
return _("archive %s") % self.file
|
|
|
|
|
|
def _detectArchive(self, file):
|
|
|
"""Detect archive by "/usr/bin/file" command
|
|
|
|
|
|
Return bzip2,gzip,7z or None
|
|
|
"""
|
|
|
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"
|
|
|
elif "gzip compressed data" in p_file.read():
|
|
|
return "gzip"
|
|
|
elif "7-zip archive data" in p_file.read():
|
|
|
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
|
|
|
|
|
|
tar_cmd = getProgPath('/bin/tar')
|
|
|
if archiveType == "7z":
|
|
|
arch_cmd = getProgPath('/usr/bin/7za')
|
|
|
params = ["x", "-so"]
|
|
|
elif archiveType == "lzma":
|
|
|
arch_cmd = getProgPath('/usr/bin/lzma')
|
|
|
params = ["-dc"]
|
|
|
elif archiveType == "bzip2":
|
|
|
arch_cmd = getProgPath('/bin/bunzip2')
|
|
|
params = ["-dc"]
|
|
|
elif archiveType == "gzip":
|
|
|
arch_cmd = getProgPath('/bin/gzip')
|
|
|
params = ["-dc"]
|
|
|
else:
|
|
|
raise DistributiveError(_("Unknown archive type '%s'") %
|
|
|
archfile)
|
|
|
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)
|
|
|
|
|
|
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):
|
|
|
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") +
|
|
|
" '%s':\n%s" % (file, p.read()))
|
|
|
|
|
|
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,
|
|
|
'mdirectory': self.mdirectory,
|
|
|
'childs': [x.serialize() for x in self.childs]}
|
|
|
return d
|
|
|
|
|
|
@classmethod
|
|
|
@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)
|
|
|
for x in data.get('childs', [])]
|
|
|
return ld
|
|
|
|
|
|
|
|
|
class SquashDistributive(Distributive):
|
|
|
def __init__(self, file, parent=None, mdirectory=None, exclude=None,
|
|
|
compress=""):
|
|
|
Distributive.__init__(self, parent=parent)
|
|
|
self.file = file
|
|
|
self.mdirectory = mdirectory or DefaultMountPath.SquashImage
|
|
|
self.exclude = exclude or []
|
|
|
self.compress = compress if compress and compress != "gzip" else ""
|
|
|
|
|
|
def getType(self):
|
|
|
return _("squash image %s") % self.file
|
|
|
|
|
|
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 \
|
|
|
child.directory 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, **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]
|
|
|
if self.compress:
|
|
|
cmd += ["-comp", self.compress]
|
|
|
cmd += ["-progress"]
|
|
|
if self.exclude:
|
|
|
cmd += ["-e"] + self.exclude
|
|
|
# возможность использовать заранее подготовленный 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():
|
|
|
if callable(callbackProgress):
|
|
|
callbackProgress(perc)
|
|
|
if processMkSquash.failed():
|
|
|
raise DistributiveError(_("Failed to create squashfs") +
|
|
|
" '%s':\n%s" % (
|
|
|
file, processMkSquash.read()))
|
|
|
|
|
|
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,
|
|
|
'mdirectory': self.mdirectory,
|
|
|
'childs': [x.serialize() for x in self.childs]}
|
|
|
return d
|
|
|
|
|
|
@classmethod
|
|
|
@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)
|
|
|
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"):
|
|
|
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.compress = compress
|
|
|
self.eventPrepareIso = Signal()
|
|
|
|
|
|
def getType(self):
|
|
|
tf = typeFile(magic=MAGIC_COMPRESS | MAGIC_SYMLINK | MAGIC_CONTINUE)
|
|
|
ftype = tf.getMType(self.file)
|
|
|
if "block special" in ftype:
|
|
|
return _("live image %s") % self.file
|
|
|
if path.isdir(self.file):
|
|
|
return _("image directory %s") % self.file
|
|
|
else:
|
|
|
return _("ISO image %s") % self.file
|
|
|
|
|
|
def probe(self):
|
|
|
"""Check directory for iso content"""
|
|
|
try:
|
|
|
pathname = self.getIsoContentDirectory()
|
|
|
except Exception:
|
|
|
return False
|
|
|
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"))
|
|
|
|
|
|
def _mountIso(self, file, directory):
|
|
|
if self.file != self.mdirectory:
|
|
|
self._makeDirectory(directory)
|
|
|
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"
|
|
|
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 _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():
|
|
|
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
|
|
|
|
|
|
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))
|
|
|
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, **kwargs)
|
|
|
# prepare iso
|
|
|
self.eventPrepareIso.emit(isoDirectory)
|
|
|
# pack iso
|
|
|
if self.bdirectory != self.file:
|
|
|
self.packToIso(isoDirectory, self.file, **kwargs)
|
|
|
except DistributiveError as e:
|
|
|
raise e
|
|
|
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,
|
|
|
'mdirectory': self.mdirectory,
|
|
|
'bdirectory': self.bdirectory,
|
|
|
'childs': [x.serialize() for x in self.childs]}
|
|
|
return d
|
|
|
|
|
|
@classmethod
|
|
|
@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)
|
|
|
for x in data.get('childs', [])]
|
|
|
return ld
|
|
|
|
|
|
|
|
|
class FlashDistributive(PartitionDistributive):
|
|
|
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)$")
|
|
|
for fn in 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):
|
|
|
return _("USB flash %s") % self.partition
|
|
|
|
|
|
def probe(self):
|
|
|
"""Check directory for flash content"""
|
|
|
try:
|
|
|
pathname = self.getDirectory()
|
|
|
except Exception:
|
|
|
return False
|
|
|
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
|
|
|
|
|
|
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
|
|
|
if isinstance(source, IsoDistributive):
|
|
|
self.rsync(source.getIsoContentDirectory(), distrTo.directory,
|
|
|
byfile=lambda x: x.startswith('livecd.'),
|
|
|
noperm=True, **kwargs)
|
|
|
else:
|
|
|
raise DistributiveError(
|
|
|
_("Flash install does not support %s") %
|
|
|
source.__class__.__name__)
|
|
|
|
|
|
|
|
|
class PxeDistributive(Distributive):
|
|
|
needFormat = False
|
|
|
|
|
|
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
|
|
|
|
|
|
def installFrom(self, source, **kwargs):
|
|
|
# make temporary directory for creating iso image
|
|
|
distrTo = self.directory
|
|
|
# getting squash from source
|
|
|
if isinstance(source, IsoDistributive):
|
|
|
if path.exists(self.directory):
|
|
|
removeDir(self.directory)
|
|
|
self._makeDirectory(self.directory)
|
|
|
self.rsync(source.getIsoContentDirectory(), distrTo,
|
|
|
byfile=lambda x: x.startswith('livecd.'),
|
|
|
**kwargs)
|
|
|
else:
|
|
|
raise DistributiveError(
|
|
|
_("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 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:
|
|
|
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):
|
|
|
"""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
|
|
|
raise DistributiveError(
|
|
|
_("Install with layers does not support %s") %
|
|
|
source.__class__.__name__)
|
|
|
|
|
|
def serialize(self):
|
|
|
d = {'type': Distributive.Type.Layered,
|
|
|
'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")
|
|
|
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)
|
|
|
for x in data.get('childs', [])]
|
|
|
return ld
|