diff --git a/pym/install/distr.py b/pym/install/distr.py index 47c7802..78e6828 100644 --- a/pym/install/distr.py +++ b/pym/install/distr.py @@ -301,16 +301,20 @@ class Distributive(object): def getEfiDirectory(self): """Get directory which contains boot/efi""" - return path.join(self.getDirectory(), "boot/efi") + return self.getEfiDirectories()[0] - def _makeDirectory(self, pathname): + def getEfiDirectories(self): + return [path.join(self.getDirectory(), "boot/efi")] + + @classmethod + def _makeDirectory(cls, 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) + cls._makeDirectory(parent) else: if path.exists(pathname): return False @@ -331,7 +335,8 @@ class Distributive(object): raise DistributiveError(_("Squash size unsupported for %s") % str(self.__class__.__name__)) - def _removeDirectory(self, directory): + @classmethod + def _removeDirectory(cls, directory): """Remove directory and files contained in it""" try: if path.exists(directory): @@ -444,8 +449,10 @@ class Distributive(object): return max(squashfiles, key=self._getSquashNum).group() return None - def _mountToDirectory(self, file, directory, mountopts="", count=2): + @classmethod + def _mountToDirectory(cls, file, directory, mountopts="", count=2): """Mount squashfs to directory""" + # 2816 code return by mount if device is absent (update /dev by udev) NO_SUCH_DEVICE = 2816 if isMount(directory): raise DistributiveError( @@ -458,21 +465,21 @@ class Distributive(object): 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, + return cls._mountToDirectory(file, directory, mountopts, count - 1) try: - self._removeDirectory(directory) + cls._removeDirectory(directory) except Exception: pass raise DistributiveError( - self.mountError % (file, mountProcess.read())) + cls.mountError % (file, mountProcess.read())) - def _umountDirectory(self, directory): + @classmethod + def _umountDirectory(cls, directory): """Umount directory""" if isMount(directory): processUmount = None @@ -1050,6 +1057,12 @@ class PartitionDistributive(Distributive): parent=dirObj) DirectoryDistributive(realDestDir, parent=partObj) + def getEfiDirectories(self): + return [path.join(self.getDirectory(), x) + for x in self.multipartition.getMountPoints() + if x.startswith("/boot/efi") + ] + def getMultipartData(self): """Get multipartition data""" mulipartData = zip(self.multipartition.getPartitions(), @@ -1133,15 +1146,17 @@ class PartitionDistributive(Distributive): self.changeSystemID(dev, systemid, partTable) return True - def _checkMount(self, dev): + @classmethod + def _checkMount(cls, 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=""): + @classmethod + def formatPartition(cls, dev, format="ext4", label=""): """Format partition""" - if not format in self.format_map: + if not format in cls.format_map: raise DistributiveError( _("The specified format of '%s' is not supported") % format) if dev in map(lambda y: y.split(" ")[0], @@ -1149,12 +1164,12 @@ class PartitionDistributive(Distributive): open("/proc/swaps"))): raise DistributiveError( _("Failed to format %s: this partition is used as swap") % dev) - self._checkMount(dev) + cls._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) + format_process = cls.format_map[format](dev, label) if format_process.failed(): raise DistributiveError( _("Failed to format the partition") + diff --git a/pym/install/fs_manager.py b/pym/install/fs_manager.py index 06091b9..4504f28 100644 --- a/pym/install/fs_manager.py +++ b/pym/install/fs_manager.py @@ -167,7 +167,7 @@ class FileSystemManager(object): @classmethod def checkFSForTypeMount(cls, fs, roottype, mp): - if mp == '/boot/efi': + if mp.startswith('/boot/efi'): if fs not in ('uefi', 'vfat'): return False else: diff --git a/pym/install/install.py b/pym/install/install.py index 3d2dc94..8840f9a 100644 --- a/pym/install/install.py +++ b/pym/install/install.py @@ -28,7 +28,7 @@ from calculate.core.server.func import MethodsInterface from calculate.core.server.admin import Admins from calculate.lib.utils.mount import isMount from calculate.lib.utils.files import (pathJoin, - process, listDirectory, + process, listDirectory, writeFile, checkUtils, readFile, find, copyWithPath, readLinesFile, getProgPath) from collections import deque @@ -38,7 +38,7 @@ from calculate.lib.utils.device import (detectDeviceForPartition, from calculate.lib.utils.partition import DiskFactory from datavars import DataVarsInstall -from distr import DistributiveError +from distr import DistributiveError, PartitionDistributive from subprocess import Popen, PIPE, STDOUT from itertools import * @@ -236,6 +236,9 @@ class Install(MethodsInterface): self.install_grub_uefi(cmd_grub_install, prefix_boot, target) # не UEFI установка else: + if self.clVars.Get('os_uefi'): + self.update_efi_fstab() + self.mount_efi_fstab() self.install_grub_biosboot(cmd_grub_install, prefix_boot, target) def install_grub_biosboot(self, cmd_grub_install, prefix_boot, target): @@ -272,7 +275,88 @@ class Install(MethodsInterface): raise DistributiveError( _("Failed to install the bootloader")) + def update_efi_fstab(self): + fstab_fn = pathJoin(self.clVars.Get('cl_chroot_path'), "/etc/fstab") + re_efi_record = re.compile("^(# /boot/efi|\S+\s/boot/efi).*\n", + flags=re.M) + + efidata = self.clVars.Get('os_install_fstab_efi_conf') + + if path.exists(fstab_fn): + data = readFile(fstab_fn) + m = re_efi_record.search(data) + newdata = re_efi_record.sub("", data) + if efidata: + if m: + newdata = "%s%s\n%s" % (newdata[:m.start()], + efidata, newdata[m.start():]) + else: + newdata = "%s%s\n" % (newdata, efidata) + if data != newdata: + with writeFile(fstab_fn) as f: + f.write(newdata) + + def format_efi(self): + formatdisk = [(dev, fs) + for dev, mp, fs, make in self.clVars.ZipVars( + 'os_install_disk_dev', + 'os_install_disk_mount', + 'os_install_disk_format', + 'os_install_disk_perform_format') + if mp.startswith('/boot/efi') and make == 'on' + ] + if formatdisk: + self.startTask(_("Formatting the partitions"), progress=True, + num=len(formatdisk)) + i = 1 + for dev, fs in formatdisk: + PartitionDistributive.formatPartition(dev, format="vfat") + self.setProgress(i) + i += 1 + self.endTask(True) + + def mount_efi_fstab(self): + oldmp_efi = self.clVars.select('os_disk_mount', + os_disk_mount__startswith="/boot/efi") + for dev, mp in self.clVars.ZipVars('os_install_disk_dev', + 'os_install_disk_mount'): + if mp.startswith("/boot/efi"): + curdev = isMount(mp) + if curdev != dev: + if curdev: + PartitionDistributive._umountDirectory(mp) + PartitionDistributive._makeDirectory(mp) + PartitionDistributive._mountToDirectory(dev, mp) + if mp in oldmp_efi: + oldmp_efi.remove(mp) + for mp in oldmp_efi: + PartitionDistributive._umountDirectory(mp) + def install_grub_uefi(self, cmd_grub_install, prefix_boot, target): + if self.clVars.Get('cl_action') != "system": + self.format_efi() + self.update_efi_fstab() + self.mount_efi_fstab() + efidirs = [x for x in self.clVars.Get('os_install_disk_mount') + if x.startswith('/boot/efi')] + if len(efidirs) > 1: + labels = ["calculate%d" % i for i in range(1, len(efidirs) + 1)] + else: + labels = ["calculate"] + for efiname, efidir in reversed(zip(labels, reversed(efidirs))): + efidir = pathJoin(target.getDirectory(), efidir) + self._install_grub_uefi(cmd_grub_install, prefix_boot, target, + efidir, efiname) + # удаляем устаревшие + efi_boot_mgr = getProgPath('/usr/sbin/efibootmgr') + p_efibootmgr = process(efi_boot_mgr, "-v") + data = p_efibootmgr.read() + for num, label in re.findall(r"Boot(\d+).*(calculate\d*)", data): + if label not in labels: + process(efi_boot_mgr, "-b", num, "-B").success() + + def _install_grub_uefi( + self, cmd_grub_install, prefix_boot, target, efidir, efiname): """ Установить grub с UEFI загрузчиком """ @@ -280,9 +364,9 @@ class Install(MethodsInterface): "--boot-directory=%s" % pathJoin( prefix_boot, target.getBootDirectory()), + "--bootloader-id=%s" % efiname, "--target=x86_64-efi", - "--efi-directory=%s" % - target.getEfiDirectory(), + "--efi-directory=%s" % efidir, "--force"] # проверяем наличие в nv-ram нужной нам записи для исключения повтора efi_boot_mgr = getProgPath('/usr/sbin/efibootmgr') @@ -295,19 +379,16 @@ class Install(MethodsInterface): if efi_uuid: p_efibootmgr = process(efi_boot_mgr, "-v") data = p_efibootmgr.read() - if re.search(r"Boot.*calculate.*GPT,{uuid}.*{efipath}".format( + if re.search(r"Boot.*{label}\s.*GPT,{uuid}.*{efipath}".format( + label=efiname, uuid=efi_uuid, - efipath=r"\\EFI\\calculate\\grubx64.efi"), + efipath=r"\\EFI\\%s\\grubx64.efi" % efiname), data, flags=re.M | re.I): grub_params.append("--no-nvram") # в случае установки на usb-hdd EFI загрузчик не прописывается # в efivars if self.clVars.Get('os_install_root_type') == 'usb-hdd': grub_params.append("--removable") - if self.clVars.Get('cl_action') != "system" and \ - not isMount('/boot/efi'): - raise DistributiveError(_("Failed to install the bootloader. " - "/boot/efi is not mounted.")) grub_process = process(cmd_grub_install, *grub_params, stderr=STDOUT, envdict=os.environ) @@ -319,7 +400,7 @@ class Install(MethodsInterface): # запись создать не удалось dmesg = getProgPath('/bin/dmesg') if efi_boot_mgr and dmesg: - if not re.search('Boot.*calculate', + if not re.search('Boot.*%s\s' % efiname, process(efi_boot_mgr).read(), re.M) and \ re.search('efivars.*set_variable.*failed', process(dmesg).read(), re.M): diff --git a/pym/install/variables/disk.py b/pym/install/variables/disk.py index 1d83898..c48071d 100644 --- a/pym/install/variables/disk.py +++ b/pym/install/variables/disk.py @@ -208,8 +208,8 @@ class VariableOsDeviceType(ReadonlyVariable): return info["MD_LEVEL"] device_name = path.basename(dev) if device_name in self.usbdevices: - if device.sysfs.read(device.sysfs.Path.Block,device_name, - "removable").strip() == 1: + if device.sysfs.read(device.sysfs.Path.Block, device_name, + "removable").strip() == "1": return "flash" else: return "usb-hdd" @@ -219,13 +219,12 @@ class VariableOsDeviceType(ReadonlyVariable): def get(self): # get usb device by '/dev/disk/by-id'(usb devices contain 'usb' in name) diskIdPath = '/dev/disk/by-id' - if path.exists(diskIdPath): + if device.devfs.exists(diskIdPath): self.usbdevices = \ map(lambda x: \ - os.readlink(path.join(diskIdPath, x)).rpartition('/')[ - 2], + device.devfs.readlink(diskIdPath, x).rpartition('/')[2], filter(lambda x: x.startswith('usb-'), - listDirectory(diskIdPath))) + device.devfs.listdir(diskIdPath, fullpath=False))) else: self.usbdevices = [] return map(self.getType, @@ -946,7 +945,9 @@ class VariableOsLocationSource(LocationHelper, DeviceHelper, Variable): return [ disk_dev for disk_dev, disk_mount in self.ZipVars( "os_disk_dev", "os_disk_mount") - if disk_mount not in ("", "/") or disk_dev == dev_from + if ((disk_mount not in ("", "/") + or disk_dev == dev_from) and + not disk_mount.startswith("/boot/efi")) ] + self.Get('os_bind_path') def get(self): @@ -1077,13 +1078,7 @@ class VariableOsLocationDest(LocationHelper, Variable): return path.normpath(val) return val - def eficonvert(val): - if val.lower() in ("efi", "uefi"): - return "/boot/efi" - else: - return val - - value = map(normpath, map(eficonvert, value)) + value = map(normpath, value) return map(lambda x: x or "/", value) def choice(self): @@ -1091,7 +1086,7 @@ class VariableOsLocationDest(LocationHelper, Variable): return ["/", ""] else: return ['/', '/boot', '/var/calculate', '/home', - '/usr', '/var', '/tmp', 'swap', '/boot/efi', ''] + '/usr', '/var', '/tmp', 'swap', ''] def check(self, value): """Check set location source""" @@ -1126,6 +1121,12 @@ class VariableOsLocationDest(LocationHelper, Variable): binds = filter(lambda x: not x[0].startswith('/dev/') and x[1], zip(source, value)) ########################## + # detect efi specifing + ########################## + if "/boot/efi" in value or "efi" in value: + raise VariableError( + _("Please specify EFI partition by UEFI option")) + ########################## # detect duplicate mps ########################## dupMP = list(set(filter(lambda x: value.count(x) > 1, @@ -1180,17 +1181,6 @@ class VariableOsLocationDest(LocationHelper, Variable): if cdromPartitions: raise VariableError(_("Unable to use CDROM %s for installation") % cdromPartitions) - ############################# - # detect right part for uefi# - ############################# - efiDisks = map(lambda x: device.udev.get_devname(name=x[0]), - filter(lambda x: x[1] == '/boot/efi', - zip(source, value))) - wrongPart = self.Select('os_install_disk_dev', - where='os_install_disk_type', - ne='disk-partition') - if set(efiDisks) & set(wrongPart): - raise VariableError(_("UEFI partition must be a disk partition")) ############################### # check cross bind mount points ############################### @@ -1521,24 +1511,6 @@ class VariableOsInstallDiskData(ReadonlyTableVariable): "os_install_disk_parent"] -class VariableOsInstallDiskEfi(ReadonlyVariable): - type = "list" - - def get(self): - if self.Get('os_install_root_type') == 'usb-hdd': - validParent = self.Select('os_install_disk_parent_base', - where='os_install_disk_mount_base', - eq='/') - efi_partitions = self.select('os_device_efi', - os_device_dev__in=validParent) - else: - efi_partitions = self.select('os_device_efi', - os_device_type="hdd") - - return list({x for x in efi_partitions if x} - - set(self.Get('os_location_source'))) - - class VariableOsInstallDiskParent(SourceReadonlyVariable): """ Partition parent devices using for install @@ -1558,59 +1530,6 @@ class VariableOsInstallDiskParent(SourceReadonlyVariable): humanReadable = Variable.humanReadable -class VariableOsAddonDiskDev(DeviceHelper, ReadonlyVariable): - type = "list" - - def get(self): - if self.Get('os_install_uefi_set') == 'on': - if "/boot/efi" not in self.Get('os_location_dest'): - efiPartition = self.Get('os_install_disk_efi') - if efiPartition: - return efiPartition[:1] - return [] - - -class VariableOsAddonDiskMount(DeviceHelper, ReadonlyVariable): - type = "list" - - def get(self): - if self.Get('os_install_uefi_set') == 'on': - if "/boot/efi" not in self.Get('os_location_dest'): - efiPartition = self.Get('os_install_disk_efi') - if efiPartition: - return ['/boot/efi'] - return [] - - -class VariableOsAddonDiskFormat(DeviceHelper, ReadonlyVariable): - type = "list" - - def get(self): - if self.Get('os_install_uefi_set') == 'on': - if "/boot/efi" not in self.Get('os_location_dest'): - efiPartition = self.Get('os_install_disk_efi') - if efiPartition: - return ['vfat'] - return [] - - -class VariableOsAddonDiskPerformFormat(DeviceHelper, ReadonlyVariable): - type = "list" - - def get(self): - if self.Get('os_install_uefi_set') == 'on': - if "/boot/efi" not in self.Get('os_location_dest'): - efiPartition = self.Get('os_install_disk_efi') - if efiPartition: - fsPart = self.Select('os_disk_format', where='os_disk_dev', - eq=efiPartition[0], limit=1) - if fsPart != "vfat": - return ['on'] - else: - return ['off'] - return [] - - class VariableOsInstallDiskDevBase(DeviceHelper, ReadonlyVariable): """ Variable using for resolv cyclic deps @@ -1624,9 +1543,11 @@ class VariableOsInstallDiskDevBase(DeviceHelper, ReadonlyVariable): return [device.udev.get_devname(name=disk)] return [] - return [source for source, dest in self.ZipVars("os_location_source", - "os_location_dest") - if dest and source.startswith("/dev/")] + return [dev + for dev, mount in self.ZipVars('os_location_source', + 'os_location_dest') + if (dev.startswith("/dev") and mount and + not mount.startswith("/boot/efi"))] class VariableOsInstallDiskParentBase(VariableOsInstallDiskParent): @@ -1645,7 +1566,7 @@ class VariableOsInstallDiskDev(ReadonlyVariable, DeviceHelper): type = "list" def get(self): - return (self.Get('os_addon_disk_dev') + + return (self.Get('os_install_uefi') + self.Get('os_install_disk_dev_base')) def humanReadable(self): @@ -1679,10 +1600,11 @@ class VariableOsInstallDiskMountBase(ReadonlyVariable): if disk: return ["/"] return [] - return map(lambda x: x[1], - filter(lambda x: x[0].startswith('/dev/') and x[1], - zip(self.Get('os_location_source'), - self.Get('os_location_dest')))) + return [mount + for dev, mount in self.ZipVars('os_location_source', + 'os_location_dest') + if (dev.startswith("/dev") and mount and + not mount.startswith("/boot/efi"))] class VariableOsInstallDiskMount(ReadonlyVariable): @@ -1691,10 +1613,16 @@ class VariableOsInstallDiskMount(ReadonlyVariable): """ type = "list" + def generate_uefi_mountpoints(self): + yield "/boot/efi" + for i in range(2, 20): + yield "/boot/efi%d" % i + def get(self): """Get install disk dest""" - return self.Get('os_addon_disk_mount') + \ - self.Get('os_install_disk_mount_base') + mps = self.generate_uefi_mountpoints() + return ([next(mps) for x in self.Get('os_install_uefi')] + + self.Get('os_install_disk_mount_base')) class VariableOsInstallDiskUse(ReadonlyVariable): @@ -1753,12 +1681,12 @@ class VariableOsInstallDiskFormat(ReadonlyVariable): type = "choice-list" def get(self): - _format = map(lambda x: x[2], - filter(lambda x: x[0].startswith('/dev/') and x[1], - zip(self.Get('os_location_source'), - self.Get('os_location_dest'), - self.Get('os_location_format')))) - return self.Get('os_addon_disk_format') + _format + _format = [fs for dev, mp, fs in self.ZipVars('os_location_source', + 'os_location_dest', + 'os_location_format') + if dev.startswith('/dev/') and mp] + efiformat = ['vfat' for x in self.Get('os_install_uefi')] + return efiformat + _format class VariableOsInstallDiskPerformFormat(ReadonlyVariable): @@ -1773,7 +1701,14 @@ class VariableOsInstallDiskPerformFormat(ReadonlyVariable): zip(self.Get('os_location_source'), self.Get('os_location_dest'), self.Get('os_location_perform_format')))) - return self.Get('os_addon_disk_perform_format') + _format + if self.GetBool('cl_autopartition_set'): + efiformat = ['on' for x in self.Get('os_install_uefi')] + res = efiformat + _format + else: + vfatdevs = self.select('os_disk_dev', os_disk_format="vfat") + res = ["off" if dv in vfatdevs else "on" + for dv in self.Get('os_install_uefi')] + _format + return res class VariableOsInstallDiskId(ReadonlyVariable): @@ -1834,11 +1769,20 @@ class VariableOsInstallDiskSize(SourceReadonlyVariable): indexField = 'os_install_disk_dev' def getMap(self): - return { - dev: size for dev, size in chain( + if self.GetBool("cl_autopartition_set"): + return { + dev: size for dev, size in chain( self.ZipVars('os_disk_dev', 'os_disk_size'), + self.ZipVars('cl_autopartition_disk_dev_full', + 'cl_autopartition_disk_size_full'), self.ZipVars('os_location_source', 'os_location_size')) - } + } + else: + return { + dev: size for dev, size in chain( + self.ZipVars('os_disk_dev', 'os_disk_size'), + self.ZipVars('os_location_source', 'os_location_size')) + } def getMapHumanReadable(self): return { @@ -1959,10 +1903,16 @@ class VariableOsInstallBootDevices(ReadonlyVariable): if mbr and dev in devices] return [] +class VariableOsUefi(ReadonlyVariable): + """ + UEFI partitions from fstab + """ + def get(self): + return self.select('os_disk_dev', os_disk_mount__startswith="/boot/efi") class VariableOsInstallUefi(LocationHelper, Variable): """ - Disks for boot mbr + UEFI partitions for install """ type = "choiceedit-list" element = "selecttable" @@ -1997,8 +1947,7 @@ class VariableOsInstallUefi(LocationHelper, Variable): return [] # если происходит обновление загрузчика текущей системы # для определения используем /etc/fstab - fstabefidevs = self.select('os_disk_dev', - os_disk_mount__startswith="/boot/efi") + fstabefidevs = self.Get('os_uefi') if self.Get('cl_action') != 'system': return fstabefidevs rootdev = self.Get('os_install_root_dev') @@ -2037,6 +1986,11 @@ class VariableOsInstallUefi(LocationHelper, Variable): def check(self, value): if value: + efi_boot_mgr = getProgPath('/usr/sbin/efibootmgr') + if not efi_boot_mgr: + raise VariableError( + _("UEFI installation is unavailable, because '%s' command " + "not found") % efi_boot_mgr) if self.install_without_uefiboot: raise VariableError( _("Your system must be loaded in UEFI for using this " @@ -2050,6 +2004,31 @@ class VariableOsInstallUefi(LocationHelper, Variable): raise VariableError( _("Wrong EFI device %s") % badefi[0]) + fstab_disks = [dev + for dev, mp in self.ZipVars('os_disk_dev', 'os_disk_mount') + if mp and not mp.startswith("/boot/efi") + ] + for disk in value: + if disk in fstab_disks: + raise VariableError( + _("Partition {disk} already used by " + "the current system").format(disk=disk)) + not_fat_efi = self.select('os_disk_dev', + os_disk_format__ne="vfat") + for efipart in value: + if efipart in not_fat_efi and isMount(efipart): + raise VariableError( + _("Please unmount {device}, as it will be used for " + "installation").format(device=efipart)) + + for efipart in value: + if efipart in self.select('os_location_source', + os_location_dest__ne=""): + raise VariableError( + _("Partition {disk} already used for " + "installation").format(disk=efipart)) + + def uncompatible(self): """ Uncompatible with autopartition @@ -2273,32 +2252,36 @@ class VariableOsInstallFstabMountConf(DeviceHelper, ReadonlyVariable): else: return s + def formatFstab(self, used, dev, mp, fs, opts, spec): + ret = "{dev}\t{mp}\t{fs}\t{opts}\t{spec}".format( + dev=used, mp=mp, fs=fs, opts=opts, spec=spec + ) + if used.startswith("UUID"): + return "# %s was on %s during installation\n%s" % (mp, dev, ret) + return ret + def get(self): + devicesForFstab = self.Select([ + 'os_install_disk_use', + 'os_install_disk_mount', + 'os_install_disk_format', + 'os_install_disk_options', + 'os_install_disk_dev'], + where='os_install_disk_mount', + func=lambda x: x[0] != "" and x[0] != "swap") + devicesForFstab = sorted( - self.Select(['os_install_disk_use', - 'os_install_disk_mount', - 'os_install_disk_format', - 'os_install_disk_options', - 'os_install_disk_dev'], - where='os_install_disk_mount', - func=lambda x: x[0] != "" and x[0] != "swap"), - lambda x, y: cmp(self.separateDevice(x[1]), - self.separateDevice(y[1]))) - if self.Get('os_install_scratch') == "on": - devicesForFstab = filter(lambda x: x[1] != "/", devicesForFstab) - - # rootLine one string, but it correct work if devicesForFstab is empty - rootLine = "\n".join(map(lambda x: "%s\t%s\t%s\t%s\t0 1" % - ( - self._commentFstab(x[0], x[1], - x[4]), - x[1], x[2], x[3]), - devicesForFstab[:1])) - otherLines = "\n".join(map(lambda x: "%s\t%s\t%s\t%s\t0 0" % - (self._commentFstab(x[0], x[1], - x[4]), x[1], - x[2], x[3]), - devicesForFstab[1:])) + devicesForFstab, key=lambda x: self.separateDevice(x[1])) + + rootLine = "\n".join( + self.formatFstab(used, dev, mp, fs, opts, "0 1") + for used, mp, fs, opts, dev in devicesForFstab[:1] + ) + + otherLines = "\n".join( + self.formatFstab(used, dev, mp, fs, opts, "0 0") + for used, mp, fs, opts, dev in devicesForFstab[1:] + ) bindData = self.ZipVars('os_install_bind_path', 'os_install_bind_mountpoint') @@ -2308,6 +2291,28 @@ class VariableOsInstallFstabMountConf(DeviceHelper, ReadonlyVariable): return "\n".join(filter(lambda x: x, [rootLine, otherLines, bindLines])) +class VariableOsInstallFstabEfiConf(VariableOsInstallFstabMountConf): + def get(self): + devicesForFstab = self.Select([ + 'os_install_disk_use', + 'os_install_disk_mount', + 'os_install_disk_format', + 'os_install_disk_options', + 'os_install_disk_dev'], + where='os_install_disk_mount', + func=lambda x: x[0].startswith("/boot/efi")) + + devicesForFstab = sorted( + devicesForFstab, key=lambda x: self.separateDevice(x[1])) + + efiLines = "\n".join( + self.formatFstab(used, dev, mp, fs, opts, "0 0") + for used, mp, fs, opts, dev in devicesForFstab + ) + + return "\n".join(filter(lambda x: x, [efiLines])) + + class VariableOsInstallFstabSwapConf(VariableOsInstallFstabMountConf): """ FStab.conf contains swap partition