diff options
Diffstat (limited to 'ironic_python_agent/raid_utils.py')
-rw-r--r-- | ironic_python_agent/raid_utils.py | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/ironic_python_agent/raid_utils.py b/ironic_python_agent/raid_utils.py index d4c338a8..0a53eec5 100644 --- a/ironic_python_agent/raid_utils.py +++ b/ironic_python_agent/raid_utils.py @@ -13,6 +13,7 @@ import copy import re +from ironic_lib import disk_utils from ironic_lib import utils as il_utils from oslo_concurrency import processutils from oslo_log import log as logging @@ -262,3 +263,120 @@ def get_next_free_raid_device(): return name raise errors.SoftwareRAIDError("No free md (RAID) devices are left") + + +# TODO(rg): handle PreP boot parts relocation as well +def prepare_boot_partitions_for_softraid(device, holders, efi_part, + target_boot_mode): + """Prepare boot partitions when relevant. + + Create either a RAIDed EFI partition or bios boot partitions for software + RAID, according to both target boot mode and disk holders partition table + types. + + :param device: the softraid device path + :param holders: the softraid drive members + :param efi_part: when relevant the efi partition coming from the image + deployed on softraid device, can be/is often None + :param target_boot_mode: target boot mode can be bios/uefi/None + or anything else for unspecified + + :returns: the path to the ESP md device when target boot mode is uefi, + nothing otherwise. + """ + # Actually any fat partition could be a candidate. Let's assume the + # partition also has the esp flag + if target_boot_mode == 'uefi': + if not efi_part: + + LOG.debug("No explicit EFI partition provided. Scanning for any " + "EFI partition located on software RAID device %s to " + "be relocated", + device) + + # NOTE: for whole disk images, no efi part uuid will be provided. + # Let's try to scan for esp on the root softraid device. If not + # found, it's fine in most cases to just create an empty esp and + # let grub handle the magic. + efi_part = disk_utils.find_efi_partition(device) + if efi_part: + efi_part = '{}p{}'.format(device, efi_part['number']) + + LOG.info("Creating EFI partitions on software RAID holder disks") + # We know that we kept this space when configuring raid,see + # hardware.GenericHardwareManager.create_configuration. + # We could also directly get the EFI partition size. + partsize_mib = ESP_SIZE_MIB + partlabel_prefix = 'uefi-holder-' + efi_partitions = [] + for number, holder in enumerate(holders): + # NOTE: see utils.get_partition_table_type_from_specs + # for uefi we know that we have setup a gpt partition table, + # sgdisk can be used to edit table, more user friendly + # for alignment and relative offsets + partlabel = '{}{}'.format(partlabel_prefix, number) + out, _u = utils.execute('sgdisk', '-F', holder) + start_sector = '{}s'.format(out.splitlines()[-1].strip()) + out, _u = utils.execute( + 'sgdisk', '-n', '0:{}:+{}MiB'.format(start_sector, + partsize_mib), + '-t', '0:ef00', '-c', '0:{}'.format(partlabel), holder) + + # Refresh part table + utils.execute("partprobe") + utils.execute("blkid") + + target_part, _u = utils.execute( + "blkid", "-l", "-t", "PARTLABEL={}".format(partlabel), holder) + + target_part = target_part.splitlines()[-1].split(':', 1)[0] + efi_partitions.append(target_part) + + LOG.debug("EFI partition %s created on holder disk %s", + target_part, holder) + + # RAID the ESPs, metadata=1.0 is mandatory to be able to boot + md_device = get_next_free_raid_device() + LOG.debug("Creating md device %(md_device)s for the ESPs " + "on %(efi_partitions)s", + {'md_device': md_device, 'efi_partitions': efi_partitions}) + utils.execute('mdadm', '--create', md_device, '--force', + '--run', '--metadata=1.0', '--level', '1', + '--name', 'esp', '--raid-devices', len(efi_partitions), + *efi_partitions) + + disk_utils.trigger_device_rescan(md_device) + + if efi_part: + # Blockdev copy the source ESP and erase it + LOG.debug("Relocating EFI %s to %s", efi_part, md_device) + utils.execute('cp', efi_part, md_device) + LOG.debug("Erasing EFI partition %s", efi_part) + utils.execute('wipefs', '-a', efi_part) + else: + fslabel = 'efi-part' + il_utils.mkfs(fs='vfat', path=md_device, label=fslabel) + + return md_device + + elif target_boot_mode == 'bios': + partlabel_prefix = 'bios-boot-part-' + for number, holder in enumerate(holders): + label = disk_utils.get_partition_table_type(holder) + if label == 'gpt': + LOG.debug("Creating bios boot partition on disk holder %s", + holder) + out, _u = utils.execute('sgdisk', '-F', holder) + start_sector = '{}s'.format(out.splitlines()[-1].strip()) + partlabel = '{}{}'.format(partlabel_prefix, number) + out, _u = utils.execute( + 'sgdisk', '-n', '0:{}:+2MiB'.format(start_sector), + '-t', '0:ef02', '-c', '0:{}'.format(partlabel), holder) + + # Q: MBR case, could we dd the boot code from the softraid + # (446 first bytes) if we detect a bootloader with + # _is_bootloader_loaded? + # A: This won't work. Because it includes the address on the + # disk, as in virtual disk, where to load the data from. + # Since there is a structural difference, this means it will + # fail. |