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-lib/pym/calculate/lib/utils/partition.py

1132 lines
36 KiB

# -*- coding: utf-8 -*-
# Copyright 2017 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.
import sys
from abc import ABCMeta, abstractmethod
from calculate.lib.utils.files import process, getProgPath
from calculate.lib.utils.tools import Sizes, classificate, traverse
from calculate.lib.utils.device import humanreadableSize
import re
_ = lambda x: x
from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_lib3', sys.modules[__name__])
def round_disk_size(size):
return size - (size % (4 * Sizes.M))
class VolumesError(Exception):
pass
class VolumesBuilder(object):
__metaclass__ = ABCMeta
class DeviceType(object):
Device = 0
RAID = 1
class VolumeType(object):
Generic = 0
Raid = 2
Fat32 = 3
Linux = 4
LinuxSwap = 5
Ntfs = 6
BiosBoot = 7
Efi = 8
Lvm = 9
Extended = 10
class Purpose(object):
Undefined = 100
EfiBoot = 101
BiosBoot = 102
MainRoot = 103
UpdateRoot = 104
Swap = 105
Calculate = 106
Lvm = 107
Windows = 108
Raid = 109
Linux = 110
purpose_type = {
Purpose.Undefined: VolumeType.Generic,
Purpose.Lvm: VolumeType.Lvm,
Purpose.BiosBoot: VolumeType.BiosBoot,
Purpose.EfiBoot: VolumeType.Efi,
Purpose.MainRoot: VolumeType.Linux,
Purpose.UpdateRoot: VolumeType.Linux,
Purpose.Calculate: VolumeType.Linux,
Purpose.Linux: VolumeType.Linux,
Purpose.Swap: VolumeType.LinuxSwap,
Purpose.Windows: VolumeType.Ntfs,
Purpose.Raid: VolumeType.Raid
}
SizeAllFree = "allfree"
@abstractmethod
def create(self):
"""
Пересоздать таблицу разделов
"""
pass
@abstractmethod
def create_volume(self, size, name, purpose=0):
pass
@abstractmethod
def create_last_volume(self, size, name, purpose=0):
pass
@abstractmethod
def set_minimal_volume_size(self, size):
pass
@abstractmethod
def finish(self):
pass
class DosDisk(object):
__metaclass__ = ABCMeta
@abstractmethod
def get_partition_name(self, num):
pass
@abstractmethod
def create_table(self):
pass
@abstractmethod
def create_primary_partition(self, size):
pass
@abstractmethod
def create_logical_partition(self, size):
pass
@abstractmethod
def create_extended_partition(self):
pass
@abstractmethod
def change_partition_type(self, partid, partnum=None):
pass
@abstractmethod
def write(self):
pass
class GptDisk(object):
__metaclass__ = ABCMeta
@abstractmethod
def create_table(self):
pass
@abstractmethod
def create_partition(self, size):
pass
@abstractmethod
def change_partition_type(self, partid, partnum=None):
pass
@abstractmethod
def write(self):
pass
@abstractmethod
def get_partition_name(self, num):
pass
class LvmDisk(object):
__metaclass__ = ABCMeta
@abstractmethod
def create_lvm(self):
pass
@abstractmethod
def create_logical_volume(self, name, size):
pass
@abstractmethod
def write(self):
pass
class BlockDisk(object):
pass
class VirtualDiskError(Exception):
pass
class SizeableDisk(object):
AllFree = None
header_size = 0
def __init__(self, size):
self.disk_size = round_disk_size(size)
self.free_size = self.disk_size - self.header_size
def occupe_size(self, size):
if size is not self.AllFree:
if size < 0:
self.free_size = size
else:
self.free_size -= size
else:
self.free_size = 0
def size(self, size):
if size is not self.AllFree:
if size < 0:
return self.free_size + size
else:
return size
else:
return self.free_size
def check_free_size(self):
if self.free_size < 0:
needbytes = abs(self.free_size)
availbytes = self.disk_size
all_usebytes = availbytes + needbytes
all_use = humanreadableSize(all_usebytes)
raise VirtualDiskError(
_("There is not enough space on this device, "
"to perform the autopartition need {need}").format(
need=all_use))
class VirtualDisk(SizeableDisk, DosDisk, GptDisk):
parttype = ""
class Partition(object):
def __init__(self, dev=None, mount=None, part=None,
partid=None, size=None):
self.dev = dev
self.mount = mount
self.part = part
self.partid = partid
self.size = round_disk_size(size)
self.format = ""
def __init__(self, parent, dev, size):
self.parent = parent
self.dev = dev
self.partitions = []
super(VirtualDisk, self).__init__(size)
volume_types_map = {}
def get_partition_name(self, num):
if self.dev[-1].isdigit():
return ("%sp%%d" % self.dev) % num
else:
return ("%s%%d" % self.dev) % num
def create_table(self):
self.partitions = []
return True
def create_partition(self, size, parttype=None):
real_size = self.size(size)
self.occupe_size(real_size)
self.partitions.append(
self.Partition(
dev=self.get_partition_name(len(self.partitions)+1),
mount="", part=parttype or self.parttype, size=real_size)
)
return True
def create_primary_partition(self, size):
self.create_partition(size, parttype="primary")
return True
def create_logical_partition(self, size):
self.create_partition(size, parttype="logical")
return True
def create_extended_partition(self):
self.create_partition(0, parttype="extended")
self._get_partition().size = self.free_size
self._get_partition().partid = DosPartitionId.get(
VolumesBuilder.VolumeType.Extended)
return True
def _get_partition(self, partnum=None):
if partnum is None:
return self.partitions[-1]
else:
return self.partitions[partnum]
def change_partition_type(self, partid, partnum=None):
part = self._get_partition(partnum)
part.partid = partid
def change_purpose(self, purpose, partnum=None):
part = self._get_partition(partnum)
part.mount = self.parent.get_mount_point(purpose)
if purpose == VolumesBuilder.Purpose.EfiBoot:
self.parent.set_efi(part.dev)
part.format = self.parent.get_format(purpose)
if purpose is VolumesBuilder.Purpose.UpdateRoot:
self.parent.dev_from = part.dev
def write(self):
self.check_free_size()
devtype = self.parent.get_device_type(self.dev)
for partition in self.partitions:
disk_type = "%s-partition" % devtype
if partition.partid in (
GptPartitionId.get(VolumesBuilder.VolumeType.BiosBoot),
DosPartitionId.get(VolumesBuilder.VolumeType.Extended),
DosPartitionId.get(VolumesBuilder.VolumeType.Lvm),
GptPartitionId.get(VolumesBuilder.VolumeType.Lvm),
):
if partition.partid == GptPartitionId.get(
VolumesBuilder.VolumeType.BiosBoot):
self.parent.set_mbr(self.dev)
elif partition.partid in (
DosPartitionId.get(VolumesBuilder.VolumeType.Lvm),
GptPartitionId.get(VolumesBuilder.VolumeType.Lvm)):
self.parent.lvm_size[partition.dev] = partition.size
self.parent.lvm_disk_type[partition.dev] = disk_type
continue
self.parent.disk_dev.append(partition.dev)
self.parent.disk_mount.append(partition.mount)
self.parent.disk_part.append(partition.part)
self.parent.disk_size.append(partition.size)
self.parent.disk_type.append(disk_type)
if partition.format is None:
self.parent.disk_format.append(self.parent.default_format)
else:
self.parent.disk_format.append(partition.format)
return True
class VirtualDosDisk(VirtualDisk, DosDisk):
parttype = "dos"
def write(self):
res = super(VirtualDosDisk, self).write()
self.parent.set_mbr(self.dev)
return res
class VirtualGptDisk(VirtualDisk, GptDisk):
parttype = "gpt"
class VirtualLvm(SizeableDisk, LvmDisk):
class Volume(object):
def __init__(self, dev=None, mount=None, size=None):
self.dev = dev
self.mount = mount
self.size = size
self.format = ""
def __init__(self, parent, devices, vgname, vgsize, pesize=None):
self.parent = parent
self.devices = devices
self.disk_size = vgsize
self.vgname = vgname
self.volumes = []
super(VirtualLvm, self).__init__(vgsize)
volume_types_map = {}
def get_partition_name(self, name):
return "/dev/%s/%s" % (self.vgname, name)
def create_lvm(self):
self.volumes = []
return True
def create_logical_volume(self, lvname, size):
real_size = self.size(size)
self.occupe_size(real_size)
self.volumes.append(
self.Volume(
dev=self.get_partition_name(lvname),
mount="", size=real_size)
)
return True
def _get_partition(self, name):
if name is None:
return self.volumes[-1]
else:
fullname = self.get_partition_name(name)
for volume in self.volumes:
if fullname == volume.dev:
return volume
raise VolumesError(_("Failed to find %s volume") % name)
def change_purpose(self, purpose, partnum=None):
part = self._get_partition(partnum)
part.mount = self.parent.get_mount_point(purpose)
part.format = self.parent.get_format(purpose)
if purpose is VolumesBuilder.Purpose.UpdateRoot:
self.parent.dev_from = part.dev
def write(self):
self.check_free_size()
devtype = self.parent.get_device_type(self.devices[0])
for partition in self.volumes:
self.parent.disk_dev.append(partition.dev)
self.parent.disk_mount.append(partition.mount)
self.parent.disk_part.append("")
self.parent.disk_size.append(partition.size)
self.parent.disk_type.append("%s-lvm" % devtype)
self.parent.disk_format.append(partition.format)
class Fdisk(SizeableDisk):
__metaclass__ = ABCMeta
def __init__(self, dev, disk_size):
super(Fdisk, self).__init__(disk_size)
self.fdisk_process = process(self.fdisk_cmd, dev)
self.fdisk = []
self.dev = dev
self.partnumber = 0
volume_types_map = {}
@abstractmethod
def create_table(self):
pass
@property
def fdisk_cmd(self):
cmd = "/sbin/fdisk"
_fdisk_cmd = getProgPath(cmd)
if not _fdisk_cmd:
raise VolumesError(_("Command not found '%s'") % cmd)
return _fdisk_cmd
def _fdisksize(self, size):
if size is not self.AllFree:
if size < 0:
bytesize = self.free_size + size
else:
bytesize = size
return "+%dK" % (Sizes().to_K(bytesize))
else:
return ""
def change_partition_type(self, partid, partnum=None):
if self.partnumber < 2:
self.fdisk.append("t\n%s\n" % partid)
else:
if partnum is None:
self.fdisk.append("t\n\n%s\n" % partid)
else:
self.fdisk.append("t\n%d\n%s\n" % (partnum, partid))
def search_errors(self, errors):
return re.search("Value out of range[.]|: unknown command",
errors)
def write(self):
self.fdisk_process.write("%sw\nq\n" % "".join(self.fdisk))
self.fdisk_process.success()
self.update_kernel_devfs()
return not self.search_errors(self.fdisk_process.readerr().strip())
def update_kernel_devfs(self):
partprobe = getProgPath("/usr/sbin/partprobe")
if partprobe:
process(partprobe, self.dev).success()
def get_partition_name(self, num):
if self.dev[-1].isdigit():
return ("%sp%%d" % self.dev) % num
else:
return ("%s%%d" % self.dev) % num
class DosFdisk(Fdisk, DosDisk):
"""
Fdisk для создания dos таблицы разделов
"""
header_size = Sizes().M
def create_table(self):
self.fdisk = []
self.partnumber = 0
self.fdisk.append("o\n")
def create_primary_partition(self, size):
end = self._fdisksize(size)
self.occupe_size(size)
self.fdisk.append("n\np\n\n\n%s\n" % end)
self.partnumber += 1
def create_logical_partition(self, size):
end = self._fdisksize(size)
self.occupe_size(size)
self.fdisk.append("n\n\n%s\n" % end)
self.partnumber += 1
def create_extended_partition(self):
self.fdisk.append("n\ne\n\n\n")
self.partnumber += 1
class GptFdisk(Fdisk, GptDisk):
"""
Fdisk для создания dos таблицы разделов
"""
header_size = Sizes().M + Sizes().Sector * 34
def create_table(self):
self.fdisk = []
self.fdisk.append("g\n")
def create_partition(self, size):
end = self._fdisksize(size)
self.occupe_size(size)
self.fdisk.append("n\n\n\n%s\n" % end)
self.partnumber += 1
class LvmVolumeGroup(SizeableDisk, LvmDisk):
def __init__(self, devices, vgname, vgsize, extsize=None):
super(LvmVolumeGroup, self).__init__(vgsize)
self.devices = devices
self.vgname = vgname
self.vgsize = vgsize
self.extsize = extsize
@property
def lvm_cmd(self):
cmd = "/sbin/lvm"
_lvm_cmd = getProgPath(cmd)
if not _lvm_cmd:
raise VolumesError(_("Command not found '%s'") % cmd)
return _lvm_cmd
def wipefs(self):
cmd = "/sbin/wipefs"
wipefs_cmd = getProgPath(cmd)
if not wipefs_cmd:
raise VolumesError(_("Command not found '%s'") % cmd)
for dev in self.devices:
process(wipefs_cmd, "-af", dev).success()
def create_physical_volumes(self):
self.wipefs()
p = process(self.lvm_cmd, "pvcreate", "-ff", *self.devices)
return p.success()
def create_volumes_group(self):
if self.extsize:
p = process(self.lvm_cmd, "vgcreate", self.vgname,
*(["-s%s" % self.extsize] + self.devices))
else:
p = process(self.lvm_cmd, "vgcreate", self.vgname,
*self.devices)
return p.success()
def create_lvm(self):
return self.create_physical_volumes() and self.create_volumes_group()
def create_logical_volume(self, lvname, size):
size_param = self._lvmsize(size)
self.occupe_size(size)
return process(self.lvm_cmd, "lvcreate", size_param,
self.vgname, "-n", lvname).success()
def get_partition_name(self, name):
return "/dev/%s/%s" % (self.vgname, name)
def _lvmsize(self, size):
if size is not self.AllFree:
if size < 0:
bytesize = self.free_size + size
else:
bytesize = size
return "-L%sk" % (Sizes().to_K(bytesize))
else:
return "-l100%FREE"
def write(self):
pass
class DosPartitionId(object):
volume_types_map = {
VolumesBuilder.VolumeType.Ntfs: "07",
VolumesBuilder.VolumeType.Extended: "05",
VolumesBuilder.VolumeType.Linux: "83",
VolumesBuilder.VolumeType.LinuxSwap: "82",
VolumesBuilder.VolumeType.Fat32: "0b",
VolumesBuilder.VolumeType.Lvm: "8e",
VolumesBuilder.VolumeType.Raid: "fd",
VolumesBuilder.VolumeType.Generic: "83",
}
@classmethod
def get(cls, _id, fallback=VolumesBuilder.VolumeType.Generic):
return cls.volume_types_map.get(
_id, cls.volume_types_map[fallback])
class GptPartitionId(object):
volume_types_map = {
VolumesBuilder.VolumeType.Ntfs: "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7",
VolumesBuilder.VolumeType.Fat32: "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7",
VolumesBuilder.VolumeType.Linux: "0fc63daf-8483-4772-8e79-3d69d8477de4",
VolumesBuilder.VolumeType.LinuxSwap:
"0657fd6d-a4ab-43c4-84e5-0933c84b4f4f",
VolumesBuilder.VolumeType.Raid: "a19d880f-05fc-4d3b-a006-743f0f84911e",
VolumesBuilder.VolumeType.Lvm: "e6d6d379-f507-44c2-a23c-238f2a3df928",
VolumesBuilder.VolumeType.Efi: "c12a7328-f81f-11d2-ba4b-00a0c93ec93b",
VolumesBuilder.VolumeType.BiosBoot:
"21686148-6449-6e6f-744e-656564454649",
VolumesBuilder.VolumeType.Generic:
"0fc63daf-8483-4772-8e79-3d69d8477de4",
}
@classmethod
def get(cls, _id, fallback=VolumesBuilder.VolumeType.Generic):
return cls.volume_types_map.get(
_id, cls.volume_types_map[fallback])
class RoundableVolumesBuilder(VolumesBuilder):
rounding = 0.05
free_size = 0
last_partition = False
sizes_map = {}
minimal = 0
def is_roundable_size(self, size):
if (size is not VolumesBuilder.SizeAllFree and size and
abs(size - self.free_size) < min(size * self.rounding,
100 * Sizes.M)):
return True
return False
def refined_size(self, size):
if size is VolumesBuilder.SizeAllFree and self.minimal:
if (self.minimal > self.free_size and
not self.is_roundable_size(self.minimal)):
return self.minimal
if self.last_partition:
if self.is_roundable_size(size):
size = VolumesBuilder.SizeAllFree
if size in self.sizes_map:
return self.sizes_map[size]
return size
def create_last_volume(self, size, name,
purpose=VolumesBuilder.Purpose.Undefined):
self.last_partition = True
return self.create_volume(size, name, purpose)
def set_minimal_volume_size(self, size):
self.minimal = size
class LvmCalculator(object):
def __init__(self, parent):
self.parent = parent
self.size = 0
self.precreate_size = None
def precreate(self, size, purpose):
if purpose == VolumesBuilder.Purpose.Lvm:
self.precreate_size = self.parent.free_size
else:
self.precreate_size = None
def postcreate(self, size, purpose):
if self.precreate_size and purpose == VolumesBuilder.Purpose.Lvm:
self.size += self.precreate_size - self.parent.free_size
self.precreate_size = None
class DosBuilder(RoundableVolumesBuilder):
def __init__(self, dosdisk):
self.dosdisk = dosdisk
self.sizes_map = {VolumesBuilder.SizeAllFree: dosdisk.AllFree}
self.next_part_number = 1
self.lvm_data = LvmCalculator(self)
def create(self):
self.dosdisk.create_table()
def create_volume(self, size, name,
purpose=VolumesBuilder.Purpose.Undefined):
self.lvm_data.precreate(size, purpose)
if self.next_part_number < 4:
self.create_primary_partition(size)
else:
if self.next_part_number == 4:
self.create_extended_partition()
self.next_part_number += 1
self.create_logical_partition(size)
self.change_purpose(purpose)
self.next_part_number += 1
self.lvm_data.postcreate(size, purpose)
return self.dosdisk.get_partition_name(self.next_part_number - 1)
@property
def free_size(self):
return self.dosdisk.free_size
def change_purpose(self, purpose):
parttype = VolumesBuilder.purpose_type.get(
purpose,
VolumesBuilder.purpose_type[VolumesBuilder.Purpose.Undefined])
volume_id = DosPartitionId.get(parttype)
self.dosdisk.change_partition_type(volume_id)
def create_primary_partition(self, size):
self.dosdisk.create_primary_partition(self.refined_size(size))
def create_logical_partition(self, size):
self.dosdisk.create_logical_partition(self.refined_size(size))
def create_extended_partition(self):
self.dosdisk.create_extended_partition()
def finish(self):
if not self.dosdisk.write():
raise VolumesError(_("Failed to create DOS partition table"))
class GptBuilder(RoundableVolumesBuilder):
def __init__(self, gptdisk, table_rules):
self.gptdisk = gptdisk
self.table_rules = table_rules
self.table_rules.parent = self
self.sizes_map = {VolumesBuilder.SizeAllFree: self.gptdisk.AllFree}
self.next_part_number = 1
self.lvm_data = LvmCalculator(self)
def create(self):
self.gptdisk.create_table()
def create_volume(self, size, name,
purpose=VolumesBuilder.Purpose.Undefined):
return self.table_rules.create_volume(size, purpose)
def get_last_partition_name(self):
return self.gptdisk.get_partition_name(self.next_part_number - 1)
@property
def free_size(self):
return self.gptdisk.free_size
def create_partition(self, size, purpose):
self.lvm_data.precreate(size, purpose)
self.gptdisk.create_partition(self.refined_size(size))
self.lvm_data.postcreate(size, purpose)
self.change_purpose(purpose)
self.next_part_number += 1
def change_purpose(self, purpose):
parttype = VolumesBuilder.purpose_type.get(
purpose,
VolumesBuilder.purpose_type[VolumesBuilder.Purpose.Undefined])
volume_id = GptPartitionId.get(parttype)
self.gptdisk.change_partition_type(volume_id)
def finish(self):
self.table_rules.finish()
if not self.gptdisk.write():
raise VolumesError(_("Failed to create GPT partition table"))
class GptRules(object):
parent = None
def finish(self):
pass
class GptOnlyRules(GptRules):
def create_volume(self, size, purpose):
self.parent.create_partition(size, purpose)
return self.parent.get_last_partition_name()
class GptFirstBootRules(GptRules):
boot_purpose = VolumesBuilder.Purpose.BiosBoot
def __init__(self, boot_size):
self.boot_created = False
self.boot_size = boot_size
def create_boot(self, size=None):
minimal = self.parent.minimal
self.parent.minimal = 0
self.parent.create_partition(size or self.boot_size,
self.boot_purpose)
self.boot_created = True
self.parent.minimal = minimal
def create_volume(self, size, purpose):
if not self.boot_created:
if purpose != self.boot_purpose:
self.create_boot()
self.boot_created = True
self.parent.create_partition(size, purpose)
return self.parent.get_last_partition_name()
def EfiGpt(obj, size=None):
obj.boot_purpose = VolumesBuilder.Purpose.EfiBoot
if size:
obj.boot_size = size
return obj
def BiosBoot(obj, size=None):
obj.boot_purpose = VolumesBuilder.Purpose.BiosBoot
if size:
obj.boot_size = size
return obj
class GptPosBootRules(GptFirstBootRules):
def __init__(self, boot_size, part_pos=4):
super(GptPosBootRules, self).__init__(boot_size)
self.part_pos = part_pos
def create_volume(self, size, purpose):
if purpose == self.boot_purpose:
self.boot_created = True
if not self.boot_created:
if self.parent.next_part_number == self.part_pos:
if purpose != self.boot_purpose:
self.create_boot()
elif self.parent.last_partition:
if (size is VolumesBuilder.SizeAllFree or
self.parent.is_roundable_size(size)):
self.parent.create_partition(-self.boot_size, purpose)
volname = self.parent.get_last_partition_name()
self.create_boot(VolumesBuilder.SizeAllFree)
else:
self.parent.create_partition(size, purpose)
volname = self.parent.get_last_partition_name()
self.create_boot(self.boot_size)
return volname
self.parent.create_partition(size, purpose)
return self.parent.get_last_partition_name()
def finish(self):
if not self.boot_created:
self.create_boot(self.boot_size)
class LvmBuilder(RoundableVolumesBuilder):
"""
Сборщик разделов для LVM
"""
def __init__(self, lvmdisk):
self.lvmdisk = lvmdisk
self.sizes_map = {VolumesBuilder.SizeAllFree: lvmdisk.AllFree}
def create(self):
if not self.lvmdisk.create_lvm():
raise VolumesError(_("Failed to create LVM"))
@property
def free_size(self):
return self.lvmdisk.free_size
def create_volume(self, size, name,
purpose=VolumesBuilder.Purpose.Undefined):
if not self.lvmdisk.create_logical_volume(name,
self.refined_size(size)):
raise VolumesError(_("Failed to create volume %s") % name)
self.change_purpose(purpose)
return self.lvmdisk.get_partition_name(name)
def change_purpose(self, purpose):
pass
def finish(self):
self.lvmdisk.write()
class VolumesSet(object):
def __init__(self, builder):
self.builder = builder
self.params = []
def create_volume(self, size, name, purpose, minimal_size=None):
self.params.append((size, name, purpose, minimal_size))
def __enter__(self):
self.builder.create()
return self
def execute(self):
for mark, data in classificate(self.params):
size, name, purpose, minsize = data
if minsize:
self.builder.set_minimal_volume_size(minsize)
if mark.last:
self.builder.create_last_volume(size, name, purpose)
else:
self.builder.create_volume(size, name, purpose)
def __exit__(self, exc_type, exc_val, exc_tb):
self.execute()
self.builder.finish()
class BuilderFactory(object):
__metaclass__ = ABCMeta
@abstractmethod
def createDosBuilder(self, device, size):
pass
@abstractmethod
def createGptBuilder(self, device, size, table_rules):
pass
@abstractmethod
def createLvmBuilder(self, devices, vgname, vgsize, extsize=None):
pass
class DiskFactory(BuilderFactory):
def createDosBuilder(self, device, size):
return DosBuilder(DosFdisk(device, size))
def createGptBuilder(self, device, size, table_rules):
return GptBuilder(GptFdisk(device, size),
table_rules)
def createLvmBuilder(self, devices, vgname, vgsize, extsize=None):
return LvmBuilder(LvmVolumeGroup(devices, vgname, vgsize,
extsize=extsize))
class DeviceInfo(object):
__metaclass__ = ABCMeta
@abstractmethod
def get_device_type(self, path=None, name=None):
pass
class PurposedDosBuilder(DosBuilder):
def change_purpose(self, purpose):
super(PurposedDosBuilder, self).change_purpose(purpose)
self.dosdisk.change_purpose(purpose)
class PurposedGptBuilder(GptBuilder):
def change_purpose(self, purpose):
super(PurposedGptBuilder, self).change_purpose(purpose)
self.gptdisk.change_purpose(purpose)
class PurposedLvmBuilder(LvmBuilder):
def change_purpose(self, purpose):
super(PurposedLvmBuilder, self).change_purpose(purpose)
self.lvmdisk.change_purpose(purpose)
class VariableFactory(BuilderFactory):
def __init__(self, deviceinfo):
self.disk_dev = []
self.disk_mount = []
self.disk_part = []
self.disk_size = []
self.disk_type = []
self.dev_from = ""
self.disk_format = []
self.deviceinfo = deviceinfo
self.lvm_size = {}
self.lvm_disk_type = {}
self.mbr = []
self.efi = []
self.purpose_mount = {
VolumesBuilder.Purpose.Calculate: "/var/calculate",
VolumesBuilder.Purpose.MainRoot: "/",
VolumesBuilder.Purpose.Swap: "swap",
}
self.purpose_format = {
VolumesBuilder.Purpose.Swap: "swap",
VolumesBuilder.Purpose.EfiBoot: "vfat",
VolumesBuilder.Purpose.Windows: "ntfs",
VolumesBuilder.Purpose.UpdateRoot: ""
}
self.default_format = "ext4"
self.pesize = None
self.efinames = iter(
traverse(("/boot/efi", ("/boot/efi%d" % x for x in range(2, 100)))))
def get_mount_point(self, purpose):
if purpose == VolumesBuilder.Purpose.EfiBoot:
return next(self.efinames)
return self.purpose_mount.get(purpose, '')
def get_format(self, purpose):
return self.purpose_format.get(purpose, self.default_format)
def createDosBuilder(self, device, size):
return PurposedDosBuilder(VirtualDosDisk(self, device, size))
def createGptBuilder(self, device, size, table_rules):
return PurposedGptBuilder(VirtualGptDisk(self, device, size),
table_rules)
def createLvmBuilder(self, devices, vgname, vgsize, extsize=None):
return PurposedLvmBuilder(VirtualLvm(self, devices, vgname, vgsize,
pesize=self.pesize))
def get_device_type(self, name):
if name in self.lvm_disk_type:
return self.lvm_disk_type[name]
return self.deviceinfo.get_device_type(name=name)
def set_mbr(self, device):
self.mbr.append(device)
def set_efi(self, device):
self.efi.append(device)
class SchemeError(Exception):
pass
class DeviceSchemeError(SchemeError):
pass
class SchemeBuilder(object):
class PartitionTable(object):
GPT = 0
DOS = 1
def __init__(self):
self.efi = False
self.partition_table = self.PartitionTable.DOS
self.lvm = False
self.devices = []
self.swap_size = Sizes().G
self.root_size = 10 * Sizes().G
self.efi_size = 200 * Sizes().M
self.biosboot_size = 50 * Sizes().M
self.swap = False
self.minimal_lvm_size = 1 * Sizes().G
self.minimal_calculate_size = 500 * Sizes().M
self.update = False
self.calculate = False
self.rootall = False
self.rounding = 0.1
self.vgname = "calculate"
self.extsize = None
def add_device(self, device, _type, size):
self.devices.append((device, _type, size))
def create_lvm_devices(self, volumes_factory):
for dev, _type, size in self.devices:
part_rules = GptFirstBootRules(self.biosboot_size)
if self.efi:
part_rules = EfiGpt(part_rules, size=self.efi_size)
else:
part_rules = BiosBoot(part_rules, size=self.biosboot_size)
if _type == VolumesBuilder.DeviceType.RAID:
yield dev, size
else:
if self.partition_table is SchemeBuilder.PartitionTable.DOS:
builder = volumes_factory.createDosBuilder(dev, size)
else:
builder = volumes_factory.createGptBuilder(
dev, size, part_rules)
builder.create()
builder.set_minimal_volume_size(self.minimal_lvm_size)
devname = builder.create_last_volume(
VolumesBuilder.SizeAllFree, "lvm",
VolumesBuilder.Purpose.Lvm)
builder.finish()
devsize = builder.lvm_data.size
yield devname, devsize
def get_volume_builder(self, volumes_factory):
if not self.devices:
raise DeviceSchemeError(
_("Please select devices for partitions changing"))
if self.lvm:
lvm_devices, lvm_sizes = zip(
*self.create_lvm_devices(volumes_factory))
return volumes_factory.createLvmBuilder(
list(lvm_devices), self.vgname,
sum(lvm_sizes), extsize=self.extsize)
else:
if len(self.devices) > 1:
raise DeviceSchemeError(
_("You should use LVM to install on more that one device"))
dev, devtype, size = self.devices[0]
if self.partition_table is SchemeBuilder.PartitionTable.DOS:
return volumes_factory.createDosBuilder(dev, size)
else:
if devtype is VolumesBuilder.DeviceType.RAID:
return volumes_factory.createGptBuilder(
dev, size, GptOnlyRules())
else:
part_rules = GptPosBootRules("", 4)
if self.efi:
part_rules = EfiGpt(part_rules, size=self.efi_size)
else:
part_rules = BiosBoot(part_rules,
size=self.biosboot_size)
return volumes_factory.createGptBuilder(dev, size,
part_rules)
def process(self, volumes_factory):
volume_builder = self.get_volume_builder(volumes_factory)
volume_builder.rounding = self.rounding
if self.rootall and (self.update or self.calculate):
raise SchemeError(
_("Root to all free size should not use with update partition "
"or calculate partition"))
with VolumesSet(volume_builder) as builder:
if self.swap:
builder.create_volume(self.swap_size, "swap",
VolumesBuilder.Purpose.Swap)
if self.rootall:
builder.create_volume(VolumesBuilder.SizeAllFree, "system1",
VolumesBuilder.Purpose.MainRoot, None)
else:
builder.create_volume(self.root_size, "system1",
VolumesBuilder.Purpose.MainRoot, None)
if self.update:
builder.create_volume(self.root_size, "system2",
VolumesBuilder.Purpose.UpdateRoot)
if self.calculate:
builder.create_volume(VolumesBuilder.SizeAllFree, "calculate",
VolumesBuilder.Purpose.Calculate,
self.minimal_calculate_size)