summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ironic_python_agent/efi_utils.py14
-rw-r--r--ironic_python_agent/extensions/image.py172
-rw-r--r--ironic_python_agent/hardware.py4
-rw-r--r--ironic_python_agent/partition_utils.py3
-rw-r--r--ironic_python_agent/raid_utils.py118
-rw-r--r--ironic_python_agent/tests/unit/extensions/test_image.py269
-rw-r--r--ironic_python_agent/tests/unit/samples/hardware_samples.py7
-rw-r--r--ironic_python_agent/tests/unit/test_efi_utils.py4
-rw-r--r--ironic_python_agent/tests/unit/test_hardware.py110
-rw-r--r--ironic_python_agent/tests/unit/test_partition_utils.py29
-rw-r--r--ironic_python_agent/tests/unit/test_raid_utils.py218
-rw-r--r--ironic_python_agent/tests/unit/test_utils.py12
-rw-r--r--ironic_python_agent/utils.py4
-rw-r--r--releasenotes/notes/add_burnin_dynamic_network_pairing-33e398255050eb98.yaml13
-rw-r--r--releasenotes/notes/detect-endianness-f53a6c4571aba3fe.yaml6
-rw-r--r--releasenotes/notes/handle-partuuid-for-fstab-e0aadea20a056982.yaml10
-rw-r--r--releasenotes/notes/move_swraid_to_efibootmgr-d87c1bfde1661fb5.yaml8
-rw-r--r--releasenotes/notes/rescan-device-after-mkfs-3f9d52a2e3b6fff3.yaml5
-rw-r--r--zuul.d/ironic-python-agent-jobs.yaml2
-rw-r--r--zuul.d/project.yaml2
20 files changed, 592 insertions, 418 deletions
diff --git a/ironic_python_agent/efi_utils.py b/ironic_python_agent/efi_utils.py
index 54d1e62f..c4e9dcc7 100644
--- a/ironic_python_agent/efi_utils.py
+++ b/ironic_python_agent/efi_utils.py
@@ -20,9 +20,9 @@ from oslo_concurrency import processutils
from oslo_log import log
from ironic_python_agent import errors
-from ironic_python_agent.extensions import image
from ironic_python_agent import hardware
from ironic_python_agent import partition_utils
+from ironic_python_agent import raid_utils
from ironic_python_agent import utils
@@ -112,7 +112,7 @@ def manage_uefi(device, efi_system_part_uuid=None):
efi_mounted = False
holders = hardware.get_holder_disks(device)
- efi_md_device = image.prepare_boot_partitions_for_softraid(
+ efi_md_device = raid_utils.prepare_boot_partitions_for_softraid(
device, holders, efi_device_part, target_boot_mode='uefi'
)
efi_devices = hardware.get_component_devices(efi_md_device)
@@ -297,8 +297,14 @@ def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
'File: %s', v_bl)
# These files are always UTF-16 encoded, sometimes have a header.
# Positive bonus is python silently drops the FEFF header.
- with open(mount_point + '/' + v_bl, 'r', encoding='utf-16') as csv:
- contents = str(csv.read())
+ try:
+ with open(mount_point + '/' + v_bl, 'r',
+ encoding='utf-16') as csv:
+ contents = str(csv.read())
+ except UnicodeError:
+ with open(mount_point + '/' + v_bl, 'r',
+ encoding='utf-16-le') as csv:
+ contents = str(csv.read())
csv_contents = contents.split(',', maxsplit=3)
csv_filename = v_bl.split('/')[-1]
v_efi_bl_path = v_bl.replace(csv_filename, str(csv_contents[0]))
diff --git a/ironic_python_agent/extensions/image.py b/ironic_python_agent/extensions/image.py
index 6f20c8a8..de880f84 100644
--- a/ironic_python_agent/extensions/image.py
+++ b/ironic_python_agent/extensions/image.py
@@ -18,8 +18,6 @@ import re
import shutil
import tempfile
-from ironic_lib import disk_utils
-from ironic_lib import utils as ilib_utils
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_log import log
@@ -104,123 +102,6 @@ def _is_bootloader_loaded(dev):
return _find_bootable_device(stdout, dev)
-# 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 = raid_utils.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 = raid_utils.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'
- ilib_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.
-
-
def _umount_all_partitions(path, path_variable, umount_warn_msg):
"""Umount all partitions we may have mounted"""
umount_binds_success = True
@@ -313,7 +194,7 @@ def _install_grub2(device, root_uuid, efi_system_part_uuid=None,
efi_partition = efi_part
if hardware.is_md_device(device):
holders = hardware.get_holder_disks(device)
- efi_partition = prepare_boot_partitions_for_softraid(
+ efi_partition = raid_utils.prepare_boot_partitions_for_softraid(
device, holders, efi_part, target_boot_mode
)
@@ -604,17 +485,58 @@ def _try_preserve_efi_assets(device, path,
def _append_uefi_to_fstab(fs_path, efi_system_part_uuid):
"""Append the efi partition id to the filesystem table.
- :param fs_path:
- :param efi_system_part_uuid:
+ :param fs_path: The path to the filesystem.
+ :param efi_system_part_uuid: uuid to use to try and find the
+ partition. Warning: this may be
+ a partition uuid or a actual uuid.
"""
fstab_file = os.path.join(fs_path, 'etc/fstab')
if not os.path.exists(fstab_file):
return
try:
- fstab_string = ("UUID=%s\t/boot/efi\tvfat\tumask=0077\t"
- "0\t1\n") % efi_system_part_uuid
+ # Collect all of the block devices so we appropriately match UUID
+ # or PARTUUID into an fstab entry.
+ block_devs = hardware.list_all_block_devices(block_type='part')
+
+ # Default to uuid, but if we find a partuuid instead, that is okay,
+ # we just need to know later on.
+ fstab_label = None
+ for bdev in block_devs:
+ # Check UUID first
+ if bdev.uuid and efi_system_part_uuid in bdev.uuid:
+ LOG.debug('Identified block device %(dev)s UUID %(uuid)s '
+ 'for UEFI boot. Proceeding with fstab update using '
+ 'a UUID.',
+ {'dev': bdev.name,
+ 'uuid': efi_system_part_uuid})
+ # What we have works, and is correct, we can break the loop
+ fstab_label = 'UUID'
+ break
+ # Fallback to PARTUUID, since we don't know if the provided
+ # UUID matches a PARTUUID, or UUID field, and the fstab entry
+ # needs to match it.
+ if bdev.partuuid and efi_system_part_uuid in bdev.partuuid:
+ LOG.debug('Identified block device %(dev)s partition UUID '
+ '%(uuid)s for UEFI boot. Proceeding with fstab '
+ 'update using a PARTUUID.',
+ {'dev': bdev.name,
+ 'uuid': efi_system_part_uuid})
+ fstab_label = 'PARTUUID'
+ break
+
+ if not fstab_label:
+ # Fallback to prior behavior, which should generally be correct.
+ LOG.warning('Falling back to fstab entry addition label of UUID. '
+ 'We could not identify which UUID or PARTUUID '
+ 'identifier label should be used, thus UUID will be '
+ 'used.')
+ fstab_label = 'UUID'
+
+ fstab_string = ("%s=%s\t/boot/efi\tvfat\tumask=0077\t"
+ "0\t1\n") % (fstab_label, efi_system_part_uuid)
with open(fstab_file, "r+") as fstab:
- if efi_system_part_uuid not in fstab.read():
+ already_present_string = fstab_label + '=' + efi_system_part_uuid
+ if already_present_string not in fstab.read():
fstab.writelines(fstab_string)
except (OSError, EnvironmentError, IOError) as exc:
LOG.debug('Failed to add entry to /etc/fstab. Error %s', exc)
diff --git a/ironic_python_agent/hardware.py b/ironic_python_agent/hardware.py
index ceafe31e..62e39f1d 100644
--- a/ironic_python_agent/hardware.py
+++ b/ironic_python_agent/hardware.py
@@ -545,6 +545,7 @@ def list_all_block_devices(block_type='disk',
'block', 'vendor'),
by_path=by_path_name,
uuid=device['UUID'],
+ partuuid=device['PARTUUID'],
**extra))
return devices
@@ -612,7 +613,7 @@ class BlockDevice(encoding.SerializableComparable):
def __init__(self, name, model, size, rotational, wwn=None, serial=None,
vendor=None, wwn_with_extension=None,
wwn_vendor_extension=None, hctl=None, by_path=None,
- uuid=None):
+ uuid=None, partuuid=None):
self.name = name
self.model = model
self.size = size
@@ -625,6 +626,7 @@ class BlockDevice(encoding.SerializableComparable):
self.wwn_vendor_extension = wwn_vendor_extension
self.hctl = hctl
self.by_path = by_path
+ self.partuuid = partuuid
class NetworkInterface(encoding.SerializableComparable):
diff --git a/ironic_python_agent/partition_utils.py b/ironic_python_agent/partition_utils.py
index 18b4cfdb..42b90445 100644
--- a/ironic_python_agent/partition_utils.py
+++ b/ironic_python_agent/partition_utils.py
@@ -316,6 +316,9 @@ def work_on_disk(dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format,
"formatted for node %(node)s",
{'ephemeral': ephemeral_part, 'node': node_uuid})
+ # Rescan device to get current status (e.g. reflect modification of mkfs)
+ disk_utils.trigger_device_rescan(dev)
+
uuids_to_return = {
'root uuid': root_part,
'efi system partition uuid': part_dict.get('efi system partition'),
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.
diff --git a/ironic_python_agent/tests/unit/extensions/test_image.py b/ironic_python_agent/tests/unit/extensions/test_image.py
index b2506b9c..00aaf706 100644
--- a/ironic_python_agent/tests/unit/extensions/test_image.py
+++ b/ironic_python_agent/tests/unit/extensions/test_image.py
@@ -234,7 +234,7 @@ class TestImageExtension(base.IronicAgentTest):
('', ''), ('', '')])
expected = [mock.call('efibootmgr', '--version'),
- mock.call('partx', '-a', '/dev/fake', attempts=3,
+ mock.call('partx', '-av', '/dev/fake', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('mount', self.fake_efi_system_part,
@@ -281,7 +281,7 @@ class TestImageExtension(base.IronicAgentTest):
('', ''), ('', '')])
expected = [mock.call('efibootmgr', '--version'),
- mock.call('partx', '-a', '/dev/fake', attempts=3,
+ mock.call('partx', '-av', '/dev/fake', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('mount', self.fake_efi_system_part,
@@ -337,7 +337,7 @@ Boot0002 VENDMAGIC FvFile(9f3c6294-bf9b-4208-9808-be45dfc34b51)
('', ''), ('', '')])
expected = [mock.call('efibootmgr', '--version'),
- mock.call('partx', '-a', '/dev/fake', attempts=3,
+ mock.call('partx', '-av', '/dev/fake', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('mount', self.fake_efi_system_part,
@@ -398,7 +398,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
('', ''), ('', ''),
('', ''), ('', '')])
expected = [mock.call('efibootmgr', '--version'),
- mock.call('partx', '-a', '/dev/fake', attempts=3,
+ mock.call('partx', '-av', '/dev/fake', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('mount', self.fake_efi_system_part,
@@ -450,7 +450,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
('', '')])
expected = [mock.call('efibootmgr', '--version'),
- mock.call('partx', '-a', '/dev/fake', attempts=3,
+ mock.call('partx', '-av', '/dev/fake', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('mount', self.fake_efi_system_part,
@@ -751,6 +751,18 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
mock_is_md_device.return_value = False
mock_md_get_raid_devices.return_value = {}
mock_exists.side_effect = iter([False, True, False, True, True])
+ partuuid_device = ('KNAME="sda" MODEL="DRIVE 0" SIZE="10240000" '
+ 'ROTA="1" TYPE="disk" UUID="987654-3210" '
+ 'PARTUUID=""\n'
+ 'KNAME="sda0" MODEL="DRIVE 0" SIZE="102400" '
+ 'ROTA="1" TYPE="part" '
+ 'UUID="' + self.fake_efi_system_part_uuid + '" '
+ 'PARTUUID="1234-2918"\n')
+ exec_side_effect = [('', '')] * 16
+ exec_side_effect.append((partuuid_device, ''))
+ exec_side_effect.extend([('', '')] * 8)
+ mock_execute.side_effect = exec_side_effect
+
with mock.patch('builtins.open', mock.mock_open()) as mock_open:
image._install_grub2(
self.fake_dev, root_uuid=self.fake_root_uuid,
@@ -805,6 +817,9 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
'GRUB_DISABLE_OS_PROBER': 'true',
'GRUB_SAVEDEFAULT': 'true'},
use_standard_locale=True),
+ mock.call('udevadm', 'settle'),
+ mock.call('lsblk', '-Pbia',
+ '-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID'),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' %
@@ -849,8 +864,21 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
environ_mock.get.return_value = '/sbin'
mock_is_md_device.return_value = False
mock_md_get_raid_devices.return_value = {}
+ partuuid_device = ('KNAME="sda" MODEL="DRIVE 0" SIZE="10240000" '
+ 'ROTA="1" TYPE="disk" UUID="987654-3210" '
+ 'PARTUUID=""\n'
+ 'KNAME="sda0" MODEL="DRIVE 0" SIZE="102400" '
+ 'ROTA="1" TYPE="part" UUID="987654-3210" '
+ 'PARTUUID="' + self.fake_efi_system_part_uuid
+ + '"\n')
+ exec_side_effect = [('', '')] * 16
+ exec_side_effect.append((partuuid_device, ''))
+ exec_side_effect.extend([('', '')] * 8)
+ mock_execute.side_effect = exec_side_effect
+ # Validates the complete opposite path *and* no-write behavior
+ # occurs if the entry already exists.
fstab_data = (
- 'UUID=%s\tpath vfat option' % self.fake_efi_system_part_uuid)
+ 'PARTUUID=%s\tpath vfat option' % self.fake_efi_system_part_uuid)
mock_exists.side_effect = [True, False, True, True, True, False,
True, True]
with mock.patch('builtins.open',
@@ -916,6 +944,9 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
'GRUB_DISABLE_OS_PROBER': 'true',
'GRUB_SAVEDEFAULT': 'true'},
use_standard_locale=True),
+ mock.call('udevadm', 'settle'),
+ mock.call('lsblk', '-Pbia',
+ '-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID'),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
mock.call(('chroot %s /bin/sh -c "umount -a -t vfat"' %
@@ -1026,6 +1057,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
@mock.patch.object(os, 'listdir', lambda *_: ['file1', 'file2'])
@mock.patch.object(image, '_is_bootloader_loaded', lambda *_: False)
+ @mock.patch.object(image, '_append_uefi_to_fstab', autospec=True)
@mock.patch.object(shutil, 'copy2', autospec=True)
@mock.patch.object(os.path, 'isfile', autospec=True)
@mock.patch.object(image, '_efi_boot_setup', autospec=True)
@@ -1042,7 +1074,8 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
mock_is_md_device, mock_exists,
mock_copytree, mock_efi_setup,
mock_isfile, mock_copy2,
- mock_execute, mock_dispatch):
+ mock_fstab_append, mock_execute,
+ mock_dispatch):
mock_exists.return_value = True
mock_efi_setup.return_value = True
mock_get_part_uuid.side_effect = [self.fake_root_part,
@@ -1106,6 +1139,9 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
uuid=self.fake_root_uuid)
mock_get_part_uuid.assert_any_call(self.fake_dev,
uuid=self.fake_efi_system_part_uuid)
+ mock_fstab_append.assert_called_once_with(
+ self.fake_dir,
+ self.fake_efi_system_part_uuid)
self.assertFalse(mock_dispatch.called)
@mock.patch.object(os.path, 'ismount', lambda *_: False)
@@ -1344,6 +1380,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
self.fake_efi_system_part_uuid)
@mock.patch.object(image, '_is_bootloader_loaded', lambda *_: False)
+ @mock.patch.object(image, '_append_uefi_to_fstab', autospec=True)
@mock.patch.object(os, 'listdir', autospec=True)
@mock.patch.object(shutil, 'copy2', autospec=True)
@mock.patch.object(os.path, 'isfile', autospec=True)
@@ -1361,8 +1398,8 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
mock_is_md_device, mock_exists,
mock_copytree, mock_efi_setup,
mock_isfile, mock_copy2,
- mock_oslistdir, mock_execute,
- mock_dispatch):
+ mock_oslistdir, mock_append_to_fstab,
+ mock_execute, mock_dispatch):
mock_exists.return_value = True
mock_efi_setup.return_value = True
mock_get_part_uuid.side_effect = [self.fake_root_part,
@@ -1431,6 +1468,8 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
uuid=self.fake_efi_system_part_uuid)
self.assertFalse(mock_dispatch.called)
self.assertEqual(2, mock_oslistdir.call_count)
+ mock_append_to_fstab.assert_called_with(self.fake_dir,
+ self.fake_efi_system_part_uuid)
@mock.patch.object(os.path, 'ismount', lambda *_: False)
@mock.patch.object(image, '_is_bootloader_loaded', lambda *_: False)
@@ -1654,212 +1693,6 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
uuid=self.fake_root_uuid)
self.assertFalse(mock_dispatch.called)
- @mock.patch.object(disk_utils, 'trigger_device_rescan', autospec=True)
- @mock.patch.object(raid_utils, 'get_next_free_raid_device', autospec=True,
- return_value='/dev/md42')
- @mock.patch.object(disk_utils, 'find_efi_partition', autospec=True)
- def test_prepare_boot_partitions_for_softraid_uefi_gpt(
- self, mock_efi_part, mock_free_raid_device, mock_rescan,
- mock_execute, mock_dispatch):
- mock_efi_part.return_value = {'number': '12'}
- mock_execute.side_effect = [
- ('451', None), # sgdisk -F
- (None, None), # sgdisk create part
- (None, None), # partprobe
- (None, None), # blkid
- ('/dev/sda12: dsfkgsdjfg', None), # blkid
- ('452', None), # sgdisk -F
- (None, None), # sgdisk create part
- (None, None), # partprobe
- (None, None), # blkid
- ('/dev/sdb14: whatever', None), # blkid
- (None, None), # mdadm
- (None, None), # cp
- (None, None), # wipefs
- ]
-
- efi_part = image.prepare_boot_partitions_for_softraid(
- '/dev/md0', ['/dev/sda', '/dev/sdb'], None,
- target_boot_mode='uefi')
-
- mock_efi_part.assert_called_once_with('/dev/md0')
- expected = [
- mock.call('sgdisk', '-F', '/dev/sda'),
- mock.call('sgdisk', '-n', '0:451s:+550MiB', '-t', '0:ef00', '-c',
- '0:uefi-holder-0', '/dev/sda'),
- mock.call('partprobe'),
- mock.call('blkid'),
- mock.call('blkid', '-l', '-t', 'PARTLABEL=uefi-holder-0',
- '/dev/sda'),
- mock.call('sgdisk', '-F', '/dev/sdb'),
- mock.call('sgdisk', '-n', '0:452s:+550MiB', '-t', '0:ef00', '-c',
- '0:uefi-holder-1', '/dev/sdb'),
- mock.call('partprobe'),
- mock.call('blkid'),
- mock.call('blkid', '-l', '-t', 'PARTLABEL=uefi-holder-1',
- '/dev/sdb'),
- mock.call('mdadm', '--create', '/dev/md42', '--force', '--run',
- '--metadata=1.0', '--level', '1', '--name', 'esp',
- '--raid-devices', 2, '/dev/sda12', '/dev/sdb14'),
- mock.call('cp', '/dev/md0p12', '/dev/md42'),
- mock.call('wipefs', '-a', '/dev/md0p12')
- ]
- mock_execute.assert_has_calls(expected, any_order=False)
- self.assertEqual(efi_part, '/dev/md42')
- mock_rescan.assert_called_once_with('/dev/md42')
-
- @mock.patch.object(disk_utils, 'trigger_device_rescan', autospec=True)
- @mock.patch.object(raid_utils, 'get_next_free_raid_device', autospec=True,
- return_value='/dev/md42')
- @mock.patch.object(disk_utils, 'find_efi_partition', autospec=True)
- @mock.patch.object(ilib_utils, 'mkfs', autospec=True)
- def test_prepare_boot_partitions_for_softraid_uefi_gpt_esp_not_found(
- self, mock_mkfs, mock_efi_part, mock_free_raid_device,
- mock_rescan, mock_execute, mock_dispatch):
- mock_efi_part.return_value = None
- mock_execute.side_effect = [
- ('451', None), # sgdisk -F
- (None, None), # sgdisk create part
- (None, None), # partprobe
- (None, None), # blkid
- ('/dev/sda12: dsfkgsdjfg', None), # blkid
- ('452', None), # sgdisk -F
- (None, None), # sgdisk create part
- (None, None), # partprobe
- (None, None), # blkid
- ('/dev/sdb14: whatever', None), # blkid
- (None, None), # mdadm
- ]
-
- efi_part = image.prepare_boot_partitions_for_softraid(
- '/dev/md0', ['/dev/sda', '/dev/sdb'], None,
- target_boot_mode='uefi')
-
- mock_efi_part.assert_called_once_with('/dev/md0')
- expected = [
- mock.call('sgdisk', '-F', '/dev/sda'),
- mock.call('sgdisk', '-n', '0:451s:+550MiB', '-t', '0:ef00', '-c',
- '0:uefi-holder-0', '/dev/sda'),
- mock.call('partprobe'),
- mock.call('blkid'),
- mock.call('blkid', '-l', '-t', 'PARTLABEL=uefi-holder-0',
- '/dev/sda'),
- mock.call('sgdisk', '-F', '/dev/sdb'),
- mock.call('sgdisk', '-n', '0:452s:+550MiB', '-t', '0:ef00', '-c',
- '0:uefi-holder-1', '/dev/sdb'),
- mock.call('partprobe'),
- mock.call('blkid'),
- mock.call('blkid', '-l', '-t', 'PARTLABEL=uefi-holder-1',
- '/dev/sdb'),
- ]
- mock_execute.assert_has_calls(expected, any_order=False)
- mock_mkfs.assert_has_calls([
- mock.call(path='/dev/md42', label='efi-part', fs='vfat'),
- ], any_order=False)
- self.assertEqual(efi_part, '/dev/md42')
- mock_rescan.assert_called_once_with('/dev/md42')
-
- @mock.patch.object(disk_utils, 'trigger_device_rescan', autospec=True)
- @mock.patch.object(raid_utils, 'get_next_free_raid_device', autospec=True,
- return_value='/dev/md42')
- def test_prepare_boot_partitions_for_softraid_uefi_gpt_efi_provided(
- self, mock_free_raid_device, mock_rescan,
- mock_execute, mock_dispatch):
- mock_execute.side_effect = [
- ('451', None), # sgdisk -F
- (None, None), # sgdisk create part
- (None, None), # partprobe
- (None, None), # blkid
- ('/dev/sda12: dsfkgsdjfg', None), # blkid
- ('452', None), # sgdisk -F
- (None, None), # sgdisk create part
- (None, None), # partprobe
- (None, None), # blkid
- ('/dev/sdb14: whatever', None), # blkid
- (None, None), # mdadm create
- (None, None), # cp
- (None, None), # wipefs
- ]
-
- efi_part = image.prepare_boot_partitions_for_softraid(
- '/dev/md0', ['/dev/sda', '/dev/sdb'], '/dev/md0p15',
- target_boot_mode='uefi')
-
- expected = [
- mock.call('sgdisk', '-F', '/dev/sda'),
- mock.call('sgdisk', '-n', '0:451s:+550MiB', '-t', '0:ef00', '-c',
- '0:uefi-holder-0', '/dev/sda'),
- mock.call('partprobe'),
- mock.call('blkid'),
- mock.call('blkid', '-l', '-t', 'PARTLABEL=uefi-holder-0',
- '/dev/sda'),
- mock.call('sgdisk', '-F', '/dev/sdb'),
- mock.call('sgdisk', '-n', '0:452s:+550MiB', '-t', '0:ef00', '-c',
- '0:uefi-holder-1', '/dev/sdb'),
- mock.call('partprobe'),
- mock.call('blkid'),
- mock.call('blkid', '-l', '-t', 'PARTLABEL=uefi-holder-1',
- '/dev/sdb'),
- mock.call('mdadm', '--create', '/dev/md42', '--force', '--run',
- '--metadata=1.0', '--level', '1', '--name', 'esp',
- '--raid-devices', 2, '/dev/sda12', '/dev/sdb14'),
- mock.call('cp', '/dev/md0p15', '/dev/md42'),
- mock.call('wipefs', '-a', '/dev/md0p15')
- ]
- mock_execute.assert_has_calls(expected, any_order=False)
- self.assertEqual(efi_part, '/dev/md42')
-
- @mock.patch.object(disk_utils, 'get_partition_table_type', autospec=True,
- return_value='msdos')
- def test_prepare_boot_partitions_for_softraid_bios_msdos(
- self, mock_label_scan, mock_execute, mock_dispatch):
-
- efi_part = image.prepare_boot_partitions_for_softraid(
- '/dev/md0', ['/dev/sda', '/dev/sdb'], 'notusedanyway',
- target_boot_mode='bios')
-
- expected = [
- mock.call('/dev/sda'),
- mock.call('/dev/sdb'),
- ]
- mock_label_scan.assert_has_calls(expected, any_order=False)
- self.assertIsNone(efi_part)
-
- @mock.patch.object(disk_utils, 'get_partition_table_type', autospec=True,
- return_value='gpt')
- def test_prepare_boot_partitions_for_softraid_bios_gpt(
- self, mock_label_scan, mock_execute, mock_dispatch):
-
- mock_execute.side_effect = [
- ('whatever\n314', None), # sgdisk -F
- (None, None), # bios boot grub
- ('warning message\n914', None), # sgdisk -F
- (None, None), # bios boot grub
- ]
-
- efi_part = image.prepare_boot_partitions_for_softraid(
- '/dev/md0', ['/dev/sda', '/dev/sdb'], 'notusedanyway',
- target_boot_mode='bios')
-
- expected_scan = [
- mock.call('/dev/sda'),
- mock.call('/dev/sdb'),
- ]
-
- mock_label_scan.assert_has_calls(expected_scan, any_order=False)
-
- expected_exec = [
- mock.call('sgdisk', '-F', '/dev/sda'),
- mock.call('sgdisk', '-n', '0:314s:+2MiB', '-t', '0:ef02', '-c',
- '0:bios-boot-part-0', '/dev/sda'),
- mock.call('sgdisk', '-F', '/dev/sdb'),
- mock.call('sgdisk', '-n', '0:914s:+2MiB', '-t', '0:ef02', '-c',
- '0:bios-boot-part-1', '/dev/sdb'),
- ]
-
- mock_execute.assert_has_calls(expected_exec, any_order=False)
- self.assertIsNone(efi_part)
-
@mock.patch.object(image, '_is_bootloader_loaded', lambda *_: True)
@mock.patch.object(hardware, 'is_md_device', autospec=True)
@mock.patch.object(hardware, 'md_restart', autospec=True)
@@ -1869,7 +1702,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
@mock.patch.object(os, 'environ', autospec=True)
@mock.patch.object(os, 'makedirs', autospec=True)
@mock.patch.object(partition_utils, 'get_partition', autospec=True)
- @mock.patch.object(image, 'prepare_boot_partitions_for_softraid',
+ @mock.patch.object(raid_utils, 'prepare_boot_partitions_for_softraid',
autospec=True,
return_value='/dev/md/esp')
@mock.patch.object(image, '_has_dracut',
@@ -1908,7 +1741,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
efi_system_part_uuid=self.fake_efi_system_part_uuid,
target_boot_mode='uefi')
- expected = [mock.call('partx', '-a', '/dev/fake', attempts=3,
+ expected = [mock.call('partx', '-av', '/dev/fake', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('mount', '/dev/fake2', self.fake_dir),
@@ -1987,7 +1820,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
@mock.patch.object(os, 'environ', autospec=True)
@mock.patch.object(os, 'makedirs', autospec=True)
@mock.patch.object(partition_utils, 'get_partition', autospec=True)
- @mock.patch.object(image, 'prepare_boot_partitions_for_softraid',
+ @mock.patch.object(raid_utils, 'prepare_boot_partitions_for_softraid',
autospec=True,
return_value=[])
@mock.patch.object(image, '_has_dracut',
diff --git a/ironic_python_agent/tests/unit/samples/hardware_samples.py b/ironic_python_agent/tests/unit/samples/hardware_samples.py
index 3ab54153..8cc9b1ea 100644
--- a/ironic_python_agent/tests/unit/samples/hardware_samples.py
+++ b/ironic_python_agent/tests/unit/samples/hardware_samples.py
@@ -160,6 +160,13 @@ RAID_BLK_DEVICE_TEMPLATE = (
'PARTUUID=""'
)
+PARTUUID_DEVICE_TEMPLATE = (
+ 'KNAME="sda" MODEL="DRIVE 0" SIZE="1765517033472" '
+ 'ROTA="1" TYPE="disk" UUID="" PARTUUID=""\n'
+ 'KNAME="sda1" MODEL="DRIVE 0" SIZE="107373133824" '
+ 'ROTA="1" TYPE="part" UUID="987654-3210" PARTUUID="1234-5678"\n'
+)
+
SHRED_OUTPUT_0_ITERATIONS_ZERO_FALSE = ()
SHRED_OUTPUT_1_ITERATION_ZERO_TRUE = (
diff --git a/ironic_python_agent/tests/unit/test_efi_utils.py b/ironic_python_agent/tests/unit/test_efi_utils.py
index 775d0fce..137ec8d4 100644
--- a/ironic_python_agent/tests/unit/test_efi_utils.py
+++ b/ironic_python_agent/tests/unit/test_efi_utils.py
@@ -19,9 +19,9 @@ from ironic_lib import disk_utils
from ironic_python_agent import efi_utils
from ironic_python_agent import errors
-from ironic_python_agent.extensions import image
from ironic_python_agent import hardware
from ironic_python_agent import partition_utils
+from ironic_python_agent import raid_utils
from ironic_python_agent.tests.unit import base
from ironic_python_agent import utils
@@ -321,7 +321,7 @@ Boot0002: VENDMAGIC FvFile(9f3c6294-bf9b-4208-9808-be45dfc34b51)
@mock.patch.object(os.path, 'exists', lambda *_: False)
@mock.patch.object(hardware, 'get_component_devices', autospec=True)
- @mock.patch.object(image,
+ @mock.patch.object(raid_utils,
'prepare_boot_partitions_for_softraid',
autospec=True)
@mock.patch.object(hardware, 'get_holder_disks', autospec=True)
diff --git a/ironic_python_agent/tests/unit/test_hardware.py b/ironic_python_agent/tests/unit/test_hardware.py
index 7b19931b..60820727 100644
--- a/ironic_python_agent/tests/unit/test_hardware.py
+++ b/ironic_python_agent/tests/unit/test_hardware.py
@@ -66,6 +66,13 @@ RAID_BLK_DEVICE_TEMPLATE_DEVICES = [
vendor="FooTastic", uuid=""),
]
+BLK_DEVICE_TEMPLATE_PARTUUID_DEVICE = [
+ hardware.BlockDevice(name='/dev/sda1', model='DRIVE 0',
+ size=107373133824, rotational=True,
+ vendor="FooTastic", uuid="987654-3210",
+ partuuid="1234-5678"),
+]
+
class FakeHardwareManager(hardware.GenericHardwareManager):
def __init__(self, hardware_support):
@@ -2563,22 +2570,22 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mock.call('sgdisk', '-F', '/dev/sdb'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '42s', '10GiB'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '42s', '10GiB'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('mdadm', '--create', '/dev/md0', '--force', '--run',
@@ -2658,32 +2665,32 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mock.call('sgdisk', '-F', '/dev/sdc'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '42s', '10GiB'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '42s', '10GiB'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdc', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '42s', '10GiB'),
- mock.call('partx', '-a', '/dev/sdc', attempts=3,
+ mock.call('partx', '-av', '/dev/sdc', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdc', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/sdc', attempts=3,
+ mock.call('partx', '-av', '/dev/sdc', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('mdadm', '--create', '/dev/md0', '--force', '--run',
@@ -2764,42 +2771,42 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mock.call('sgdisk', '-F', '/dev/sdd'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '42s', '10GiB'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '42s', '10GiB'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdc', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '42s', '10GiB'),
- mock.call('partx', '-a', '/dev/sdc', attempts=3,
+ mock.call('partx', '-av', '/dev/sdc', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdd', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '42s', '10GiB'),
- mock.call('partx', '-a', '/dev/sdd', attempts=3,
+ mock.call('partx', '-av', '/dev/sdd', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdc', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/sdc', attempts=3,
+ mock.call('partx', '-av', '/dev/sdc', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdd', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/sdd', attempts=3,
+ mock.call('partx', '-av', '/dev/sdd', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('mdadm', '--create', '/dev/md0', '--force', '--run',
@@ -2865,22 +2872,22 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mock.call('parted', '/dev/sdb', '-s', '--', 'mklabel', 'gpt'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '551MiB', '10GiB'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '551MiB', '10GiB'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('mdadm', '--create', '/dev/md0', '--force', '--run',
@@ -2952,22 +2959,22 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mock.call('parted', '/dev/sdb', '-s', '--', 'mklabel', 'gpt'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '8MiB', '10GiB'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '8MiB', '10GiB'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('mdadm', '--create', '/dev/md0', '--force', '--run',
@@ -3034,22 +3041,22 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mock.call('sgdisk', '-F', '/dev/sdb'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '42s', '10GiB'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '42s', '10GiB'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '30GiB'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '30GiB'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('mdadm', '--create', '/dev/md0', '--force', '--run',
@@ -3118,22 +3125,22 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mock.call('sgdisk', '-F', '/dev/sdb'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '42s', '20GiB'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '42s', '20GiB'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '20GiB', '-1'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '20GiB', '-1'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('mdadm', '--create', '/dev/md0', '--force', '--run',
@@ -3211,22 +3218,22 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mock.call('sgdisk', '-F', '/dev/sdb'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '42s', '10GiB'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '42s', '10GiB'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sda', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/sda', attempts=3,
+ mock.call('partx', '-av', '/dev/sda', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/sdb', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/sdb', attempts=3,
+ mock.call('partx', '-av', '/dev/sdb', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('mdadm', '--create', '/dev/md0', '--force', '--run',
@@ -3558,22 +3565,22 @@ class TestGenericHardwareManager(base.IronicAgentTest):
'gpt'),
mock.call('parted', '/dev/nvme0n1', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '551MiB', '10GiB'),
- mock.call('partx', '-a', '/dev/nvme0n1', attempts=3,
+ mock.call('partx', '-av', '/dev/nvme0n1', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/nvme1n1', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '551MiB', '10GiB'),
- mock.call('partx', '-a', '/dev/nvme1n1', attempts=3,
+ mock.call('partx', '-av', '/dev/nvme1n1', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/nvme0n1', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/nvme0n1', attempts=3,
+ mock.call('partx', '-av', '/dev/nvme0n1', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('parted', '/dev/nvme1n1', '-s', '-a', 'optimal', '--',
'mkpart', 'primary', '10GiB', '-1'),
- mock.call('partx', '-a', '/dev/nvme1n1', attempts=3,
+ mock.call('partx', '-av', '/dev/nvme1n1', attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('mdadm', '--create', '/dev/md0', '--force', '--run',
@@ -4291,6 +4298,25 @@ class TestModuleFunctions(base.IronicAgentTest):
self.assertEqual(RAID_BLK_DEVICE_TEMPLATE_DEVICES, result)
mocked_udev.assert_called_once_with()
+ @mock.patch.object(os, 'readlink', autospec=True)
+ @mock.patch.object(hardware, '_get_device_info',
+ lambda x, y, z: 'FooTastic')
+ @mock.patch.object(hardware, '_udev_settle', autospec=True)
+ @mock.patch.object(hardware.pyudev.Devices, "from_device_file",
+ autospec=False)
+ def test_list_all_block_devices_partuuid_success(
+ self, mocked_fromdevfile,
+ mocked_udev, mocked_readlink,
+ mocked_execute):
+ mocked_readlink.return_value = '../../sda'
+ mocked_fromdevfile.return_value = {}
+ mocked_execute.return_value = (hws.PARTUUID_DEVICE_TEMPLATE, '')
+ result = hardware.list_all_block_devices(block_type='part')
+ mocked_execute.assert_called_once_with(
+ 'lsblk', '-Pbia', '-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID')
+ self.assertEqual(BLK_DEVICE_TEMPLATE_PARTUUID_DEVICE, result)
+ mocked_udev.assert_called_once_with()
+
@mock.patch.object(hardware, '_get_device_info',
lambda x, y: "FooTastic")
@mock.patch.object(hardware, '_udev_settle', autospec=True)
diff --git a/ironic_python_agent/tests/unit/test_partition_utils.py b/ironic_python_agent/tests/unit/test_partition_utils.py
index f6f56695..b4017c2b 100644
--- a/ironic_python_agent/tests/unit/test_partition_utils.py
+++ b/ironic_python_agent/tests/unit/test_partition_utils.py
@@ -408,6 +408,8 @@ class WorkOnDiskTestCase(base.IronicAgentTest):
cpu_arch="")
mock_unlink.assert_called_once_with('fake-path')
+ @mock.patch.object(disk_utils, 'trigger_device_rescan',
+ lambda d: None)
@mock.patch.object(utils, 'mkfs', lambda fs, path, label=None: None)
@mock.patch.object(disk_utils, 'block_uuid', lambda p: 'uuid')
@mock.patch.object(disk_utils, 'populate_image', autospec=True)
@@ -442,6 +444,8 @@ class WorkOnDiskTestCase(base.IronicAgentTest):
self.assertEqual('uuid', res['root uuid'])
self.assertFalse(mock_populate.called)
+ @mock.patch.object(disk_utils, 'trigger_device_rescan',
+ lambda d: None)
@mock.patch.object(utils, 'mkfs', lambda fs, path, label=None: None)
@mock.patch.object(disk_utils, 'block_uuid', lambda p: 'uuid')
@mock.patch.object(disk_utils, 'populate_image', lambda image_path,
@@ -475,11 +479,12 @@ class WorkOnDiskTestCase(base.IronicAgentTest):
disk_label='gpt',
cpu_arch="")
+ @mock.patch.object(disk_utils, 'trigger_device_rescan', autospec=True)
@mock.patch.object(disk_utils, 'block_uuid', autospec=True)
@mock.patch.object(disk_utils, 'populate_image', autospec=True)
@mock.patch.object(utils, 'mkfs', autospec=True)
def test_uefi_localboot(self, mock_mkfs, mock_populate_image,
- mock_block_uuid):
+ mock_block_uuid, mock_trigger_device_rescan):
"""Test that we create a fat filesystem with UEFI localboot."""
root_part = '/dev/fake-part1'
efi_part = '/dev/fake-part2'
@@ -510,12 +515,14 @@ class WorkOnDiskTestCase(base.IronicAgentTest):
root_part, conv_flags=None)
mock_block_uuid.assert_any_call(root_part)
mock_block_uuid.assert_any_call(efi_part)
+ mock_trigger_device_rescan.assert_called_once_with(self.dev)
+ @mock.patch.object(disk_utils, 'trigger_device_rescan', autospec=True)
@mock.patch.object(disk_utils, 'block_uuid', autospec=True)
@mock.patch.object(disk_utils, 'populate_image', autospec=True)
@mock.patch.object(utils, 'mkfs', autospec=True)
def test_preserve_ephemeral(self, mock_mkfs, mock_populate_image,
- mock_block_uuid):
+ mock_block_uuid, mock_trigger_device_rescan):
"""Test that ephemeral partition doesn't get overwritten."""
ephemeral_part = '/dev/fake-part1'
root_part = '/dev/fake-part2'
@@ -543,11 +550,12 @@ class WorkOnDiskTestCase(base.IronicAgentTest):
cpu_arch="")
self.assertFalse(mock_mkfs.called)
+ @mock.patch.object(disk_utils, 'trigger_device_rescan', autospec=True)
@mock.patch.object(disk_utils, 'block_uuid', autospec=True)
@mock.patch.object(disk_utils, 'populate_image', autospec=True)
@mock.patch.object(utils, 'mkfs', autospec=True)
def test_ppc64le_prep_part(self, mock_mkfs, mock_populate_image,
- mock_block_uuid):
+ mock_block_uuid, mock_trigger_device_rescan):
"""Test that PReP partition uuid is returned."""
prep_part = '/dev/fake-part1'
root_part = '/dev/fake-part2'
@@ -573,11 +581,12 @@ class WorkOnDiskTestCase(base.IronicAgentTest):
cpu_arch="ppc64le")
self.assertFalse(mock_mkfs.called)
+ @mock.patch.object(disk_utils, 'trigger_device_rescan', autospec=True)
@mock.patch.object(disk_utils, 'block_uuid', autospec=True)
@mock.patch.object(disk_utils, 'populate_image', autospec=True)
@mock.patch.object(utils, 'mkfs', autospec=True)
def test_convert_to_sparse(self, mock_mkfs, mock_populate_image,
- mock_block_uuid):
+ mock_block_uuid, mock_trigger_device_rescan):
ephemeral_part = '/dev/fake-part1'
swap_part = '/dev/fake-part2'
root_part = '/dev/fake-part3'
@@ -1199,7 +1208,7 @@ class TestGetPartition(base.IronicAgentTest):
root_part = partition_utils.get_partition(
self.fake_dev, self.fake_root_uuid)
self.assertEqual('/dev/test2', root_part)
- expected = [mock.call('partx', '-a', self.fake_dev, attempts=3,
+ expected = [mock.call('partx', '-av', self.fake_dev, attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('lsblk', '-PbioKNAME,UUID,PARTUUID,TYPE,LABEL',
@@ -1219,7 +1228,7 @@ class TestGetPartition(base.IronicAgentTest):
self.assertRaises(errors.DeviceNotFound,
partition_utils.get_partition, self.fake_dev,
self.fake_root_uuid)
- expected = [mock.call('partx', '-a', self.fake_dev, attempts=3,
+ expected = [mock.call('partx', '-av', self.fake_dev, attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('lsblk', '-PbioKNAME,UUID,PARTUUID,TYPE,LABEL',
@@ -1240,7 +1249,7 @@ class TestGetPartition(base.IronicAgentTest):
result = partition_utils.get_partition(
self.fake_dev, self.fake_root_uuid)
self.assertEqual('/dev/loop0', result)
- expected = [mock.call('partx', '-a', self.fake_dev, attempts=3,
+ expected = [mock.call('partx', '-av', self.fake_dev, attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('lsblk', '-PbioKNAME,UUID,PARTUUID,TYPE,LABEL',
@@ -1257,7 +1266,7 @@ class TestGetPartition(base.IronicAgentTest):
partition_utils.get_partition, self.fake_dev,
self.fake_root_uuid)
- expected = [mock.call('partx', '-a', self.fake_dev, attempts=3,
+ expected = [mock.call('partx', '-av', self.fake_dev, attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('lsblk', '-PbioKNAME,UUID,PARTUUID,TYPE,LABEL',
@@ -1275,7 +1284,7 @@ class TestGetPartition(base.IronicAgentTest):
root_part = partition_utils.get_partition(
self.fake_dev, self.fake_root_uuid)
self.assertEqual('/dev/test2', root_part)
- expected = [mock.call('partx', '-a', self.fake_dev, attempts=3,
+ expected = [mock.call('partx', '-av', self.fake_dev, attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('lsblk', '-PbioKNAME,UUID,PARTUUID,TYPE,LABEL',
@@ -1292,7 +1301,7 @@ class TestGetPartition(base.IronicAgentTest):
root_part = partition_utils.get_partition(
self.fake_dev, self.fake_root_uuid)
self.assertEqual('/dev/test2', root_part)
- expected = [mock.call('partx', '-a', self.fake_dev, attempts=3,
+ expected = [mock.call('partx', '-av', self.fake_dev, attempts=3,
delay_on_retry=True),
mock.call('udevadm', 'settle'),
mock.call('lsblk', '-PbioKNAME,UUID,PARTUUID,TYPE,LABEL',
diff --git a/ironic_python_agent/tests/unit/test_raid_utils.py b/ironic_python_agent/tests/unit/test_raid_utils.py
index a20bd13e..5b8577e2 100644
--- a/ironic_python_agent/tests/unit/test_raid_utils.py
+++ b/ironic_python_agent/tests/unit/test_raid_utils.py
@@ -12,6 +12,8 @@
from unittest import mock
+from ironic_lib import disk_utils
+from ironic_lib import utils as ilib_utils
from oslo_concurrency import processutils
from ironic_python_agent import errors
@@ -115,6 +117,222 @@ class TestRaidUtils(base.IronicAgentTest):
raid_utils.create_raid_device, 0,
logical_disk)
+ @mock.patch.object(disk_utils, 'trigger_device_rescan', autospec=True)
+ @mock.patch.object(raid_utils, 'get_next_free_raid_device', autospec=True,
+ return_value='/dev/md42')
+ @mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
+ @mock.patch.object(ilib_utils, 'execute', autospec=True)
+ @mock.patch.object(disk_utils, 'find_efi_partition', autospec=True)
+ def test_prepare_boot_partitions_for_softraid_uefi_gpt(
+ self, mock_efi_part, mock_execute, mock_dispatch,
+ mock_free_raid_device, mock_rescan):
+ mock_efi_part.return_value = {'number': '12'}
+ mock_execute.side_effect = [
+ ('451', None), # sgdisk -F
+ (None, None), # sgdisk create part
+ (None, None), # partprobe
+ (None, None), # blkid
+ ('/dev/sda12: dsfkgsdjfg', None), # blkid
+ ('452', None), # sgdisk -F
+ (None, None), # sgdisk create part
+ (None, None), # partprobe
+ (None, None), # blkid
+ ('/dev/sdb14: whatever', None), # blkid
+ (None, None), # mdadm
+ (None, None), # cp
+ (None, None), # wipefs
+ ]
+
+ efi_part = raid_utils.prepare_boot_partitions_for_softraid(
+ '/dev/md0', ['/dev/sda', '/dev/sdb'], None,
+ target_boot_mode='uefi')
+
+ mock_efi_part.assert_called_once_with('/dev/md0')
+ expected = [
+ mock.call('sgdisk', '-F', '/dev/sda'),
+ mock.call('sgdisk', '-n', '0:451s:+550MiB', '-t', '0:ef00', '-c',
+ '0:uefi-holder-0', '/dev/sda'),
+ mock.call('partprobe'),
+ mock.call('blkid'),
+ mock.call('blkid', '-l', '-t', 'PARTLABEL=uefi-holder-0',
+ '/dev/sda'),
+ mock.call('sgdisk', '-F', '/dev/sdb'),
+ mock.call('sgdisk', '-n', '0:452s:+550MiB', '-t', '0:ef00', '-c',
+ '0:uefi-holder-1', '/dev/sdb'),
+ mock.call('partprobe'),
+ mock.call('blkid'),
+ mock.call('blkid', '-l', '-t', 'PARTLABEL=uefi-holder-1',
+ '/dev/sdb'),
+ mock.call('mdadm', '--create', '/dev/md42', '--force', '--run',
+ '--metadata=1.0', '--level', '1', '--name', 'esp',
+ '--raid-devices', 2, '/dev/sda12', '/dev/sdb14'),
+ mock.call('cp', '/dev/md0p12', '/dev/md42'),
+ mock.call('wipefs', '-a', '/dev/md0p12')
+ ]
+ mock_execute.assert_has_calls(expected, any_order=False)
+ self.assertEqual(efi_part, '/dev/md42')
+ mock_rescan.assert_called_once_with('/dev/md42')
+
+ @mock.patch.object(disk_utils, 'trigger_device_rescan', autospec=True)
+ @mock.patch.object(raid_utils, 'get_next_free_raid_device', autospec=True,
+ return_value='/dev/md42')
+ @mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
+ @mock.patch.object(ilib_utils, 'execute', autospec=True)
+ @mock.patch.object(disk_utils, 'find_efi_partition', autospec=True)
+ @mock.patch.object(ilib_utils, 'mkfs', autospec=True)
+ def test_prepare_boot_partitions_for_softraid_uefi_gpt_esp_not_found(
+ self, mock_mkfs, mock_efi_part, mock_execute, mock_dispatch,
+ mock_free_raid_device, mock_rescan):
+ mock_efi_part.return_value = None
+ mock_execute.side_effect = [
+ ('451', None), # sgdisk -F
+ (None, None), # sgdisk create part
+ (None, None), # partprobe
+ (None, None), # blkid
+ ('/dev/sda12: dsfkgsdjfg', None), # blkid
+ ('452', None), # sgdisk -F
+ (None, None), # sgdisk create part
+ (None, None), # partprobe
+ (None, None), # blkid
+ ('/dev/sdb14: whatever', None), # blkid
+ (None, None), # mdadm
+ ]
+
+ efi_part = raid_utils.prepare_boot_partitions_for_softraid(
+ '/dev/md0', ['/dev/sda', '/dev/sdb'], None,
+ target_boot_mode='uefi')
+
+ mock_efi_part.assert_called_once_with('/dev/md0')
+ expected = [
+ mock.call('sgdisk', '-F', '/dev/sda'),
+ mock.call('sgdisk', '-n', '0:451s:+550MiB', '-t', '0:ef00', '-c',
+ '0:uefi-holder-0', '/dev/sda'),
+ mock.call('partprobe'),
+ mock.call('blkid'),
+ mock.call('blkid', '-l', '-t', 'PARTLABEL=uefi-holder-0',
+ '/dev/sda'),
+ mock.call('sgdisk', '-F', '/dev/sdb'),
+ mock.call('sgdisk', '-n', '0:452s:+550MiB', '-t', '0:ef00', '-c',
+ '0:uefi-holder-1', '/dev/sdb'),
+ mock.call('partprobe'),
+ mock.call('blkid'),
+ mock.call('blkid', '-l', '-t', 'PARTLABEL=uefi-holder-1',
+ '/dev/sdb'),
+ ]
+ mock_execute.assert_has_calls(expected, any_order=False)
+ mock_mkfs.assert_has_calls([
+ mock.call(path='/dev/md42', label='efi-part', fs='vfat'),
+ ], any_order=False)
+ self.assertEqual(efi_part, '/dev/md42')
+ mock_rescan.assert_called_once_with('/dev/md42')
+
+ @mock.patch.object(disk_utils, 'trigger_device_rescan', autospec=True)
+ @mock.patch.object(raid_utils, 'get_next_free_raid_device', autospec=True,
+ return_value='/dev/md42')
+ @mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
+ @mock.patch.object(ilib_utils, 'execute', autospec=True)
+ def test_prepare_boot_partitions_for_softraid_uefi_gpt_efi_provided(
+ self, mock_execute, mock_dispatch, mock_free_raid_device,
+ mock_rescan):
+ mock_execute.side_effect = [
+ ('451', None), # sgdisk -F
+ (None, None), # sgdisk create part
+ (None, None), # partprobe
+ (None, None), # blkid
+ ('/dev/sda12: dsfkgsdjfg', None), # blkid
+ ('452', None), # sgdisk -F
+ (None, None), # sgdisk create part
+ (None, None), # partprobe
+ (None, None), # blkid
+ ('/dev/sdb14: whatever', None), # blkid
+ (None, None), # mdadm create
+ (None, None), # cp
+ (None, None), # wipefs
+ ]
+
+ efi_part = raid_utils.prepare_boot_partitions_for_softraid(
+ '/dev/md0', ['/dev/sda', '/dev/sdb'], '/dev/md0p15',
+ target_boot_mode='uefi')
+
+ expected = [
+ mock.call('sgdisk', '-F', '/dev/sda'),
+ mock.call('sgdisk', '-n', '0:451s:+550MiB', '-t', '0:ef00', '-c',
+ '0:uefi-holder-0', '/dev/sda'),
+ mock.call('partprobe'),
+ mock.call('blkid'),
+ mock.call('blkid', '-l', '-t', 'PARTLABEL=uefi-holder-0',
+ '/dev/sda'),
+ mock.call('sgdisk', '-F', '/dev/sdb'),
+ mock.call('sgdisk', '-n', '0:452s:+550MiB', '-t', '0:ef00', '-c',
+ '0:uefi-holder-1', '/dev/sdb'),
+ mock.call('partprobe'),
+ mock.call('blkid'),
+ mock.call('blkid', '-l', '-t', 'PARTLABEL=uefi-holder-1',
+ '/dev/sdb'),
+ mock.call('mdadm', '--create', '/dev/md42', '--force', '--run',
+ '--metadata=1.0', '--level', '1', '--name', 'esp',
+ '--raid-devices', 2, '/dev/sda12', '/dev/sdb14'),
+ mock.call('cp', '/dev/md0p15', '/dev/md42'),
+ mock.call('wipefs', '-a', '/dev/md0p15')
+ ]
+ mock_execute.assert_has_calls(expected, any_order=False)
+ self.assertEqual(efi_part, '/dev/md42')
+
+ @mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
+ @mock.patch.object(ilib_utils, 'execute', autospec=True)
+ @mock.patch.object(disk_utils, 'get_partition_table_type', autospec=True,
+ return_value='msdos')
+ def test_prepare_boot_partitions_for_softraid_bios_msdos(
+ self, mock_label_scan, mock_execute, mock_dispatch):
+
+ efi_part = raid_utils.prepare_boot_partitions_for_softraid(
+ '/dev/md0', ['/dev/sda', '/dev/sdb'], 'notusedanyway',
+ target_boot_mode='bios')
+
+ expected = [
+ mock.call('/dev/sda'),
+ mock.call('/dev/sdb'),
+ ]
+ mock_label_scan.assert_has_calls(expected, any_order=False)
+ self.assertIsNone(efi_part)
+
+ @mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
+ @mock.patch.object(ilib_utils, 'execute', autospec=True)
+ @mock.patch.object(disk_utils, 'get_partition_table_type', autospec=True,
+ return_value='gpt')
+ def test_prepare_boot_partitions_for_softraid_bios_gpt(
+ self, mock_label_scan, mock_execute, mock_dispatch):
+
+ mock_execute.side_effect = [
+ ('whatever\n314', None), # sgdisk -F
+ (None, None), # bios boot grub
+ ('warning message\n914', None), # sgdisk -F
+ (None, None), # bios boot grub
+ ]
+
+ efi_part = raid_utils.prepare_boot_partitions_for_softraid(
+ '/dev/md0', ['/dev/sda', '/dev/sdb'], 'notusedanyway',
+ target_boot_mode='bios')
+
+ expected_scan = [
+ mock.call('/dev/sda'),
+ mock.call('/dev/sdb'),
+ ]
+
+ mock_label_scan.assert_has_calls(expected_scan, any_order=False)
+
+ expected_exec = [
+ mock.call('sgdisk', '-F', '/dev/sda'),
+ mock.call('sgdisk', '-n', '0:314s:+2MiB', '-t', '0:ef02', '-c',
+ '0:bios-boot-part-0', '/dev/sda'),
+ mock.call('sgdisk', '-F', '/dev/sdb'),
+ mock.call('sgdisk', '-n', '0:914s:+2MiB', '-t', '0:ef02', '-c',
+ '0:bios-boot-part-1', '/dev/sdb'),
+ ]
+
+ mock_execute.assert_has_calls(expected_exec, any_order=False)
+ self.assertIsNone(efi_part)
+
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
class TestGetNextFreeRaidDevice(base.IronicAgentTest):
diff --git a/ironic_python_agent/tests/unit/test_utils.py b/ironic_python_agent/tests/unit/test_utils.py
index 96d31688..1bb7c1fe 100644
--- a/ironic_python_agent/tests/unit/test_utils.py
+++ b/ironic_python_agent/tests/unit/test_utils.py
@@ -431,7 +431,8 @@ class TestUtils(ironic_agent_base.IronicAgentTest):
file_list=[],
io_dict={'journal': mock.ANY, 'ip_addr': mock.ANY, 'ps': mock.ANY,
'df': mock.ANY, 'iptables': mock.ANY, 'lshw': mock.ANY,
- 'lsblk': mock.ANY, 'mdstat': mock.ANY})
+ 'lsblk': mock.ANY, 'mdstat': mock.ANY,
+ 'mount': mock.ANY, 'parted': mock.ANY})
@mock.patch.object(utils, 'gzip_and_b64encode', autospec=True)
@mock.patch.object(utils, 'is_journalctl_present', autospec=True)
@@ -458,7 +459,8 @@ class TestUtils(ironic_agent_base.IronicAgentTest):
file_list=[tmp.name],
io_dict={'journal': mock.ANY, 'ip_addr': mock.ANY, 'ps': mock.ANY,
'df': mock.ANY, 'iptables': mock.ANY, 'lshw': mock.ANY,
- 'lsblk': mock.ANY, 'mdstat': mock.ANY})
+ 'lsblk': mock.ANY, 'mdstat': mock.ANY,
+ 'mount': mock.ANY, 'parted': mock.ANY})
@mock.patch.object(utils, 'gzip_and_b64encode', autospec=True)
@mock.patch.object(utils, 'is_journalctl_present', autospec=True)
@@ -480,7 +482,8 @@ class TestUtils(ironic_agent_base.IronicAgentTest):
file_list=['/var/log'],
io_dict={'iptables': mock.ANY, 'ip_addr': mock.ANY, 'ps': mock.ANY,
'dmesg': mock.ANY, 'df': mock.ANY, 'lshw': mock.ANY,
- 'lsblk': mock.ANY, 'mdstat': mock.ANY})
+ 'lsblk': mock.ANY, 'mdstat': mock.ANY,
+ 'mount': mock.ANY, 'parted': mock.ANY})
@mock.patch.object(utils, 'gzip_and_b64encode', autospec=True)
@mock.patch.object(utils, 'is_journalctl_present', autospec=True)
@@ -506,7 +509,8 @@ class TestUtils(ironic_agent_base.IronicAgentTest):
file_list=['/var/log', tmp.name],
io_dict={'iptables': mock.ANY, 'ip_addr': mock.ANY, 'ps': mock.ANY,
'dmesg': mock.ANY, 'df': mock.ANY, 'lshw': mock.ANY,
- 'lsblk': mock.ANY, 'mdstat': mock.ANY})
+ 'lsblk': mock.ANY, 'mdstat': mock.ANY,
+ 'mount': mock.ANY, 'parted': mock.ANY})
def test_get_ssl_client_options(self):
# defaults
diff --git a/ironic_python_agent/utils.py b/ironic_python_agent/utils.py
index 71e29d16..ff0f8926 100644
--- a/ironic_python_agent/utils.py
+++ b/ironic_python_agent/utils.py
@@ -69,6 +69,8 @@ COLLECT_LOGS_COMMANDS = {
'lshw': ['lshw', '-quiet', '-json'],
'lsblk': ['lsblk', '--all', '-o%s' % ','.join(LSBLK_COLUMNS)],
'mdstat': ['cat', '/proc/mdstat'],
+ 'mount': ['mount'],
+ 'parted': ['parted', '-l'],
}
@@ -888,7 +890,7 @@ def rescan_device(device):
kernel partition records.
"""
try:
- execute('partx', '-a', device, attempts=3, delay_on_retry=True)
+ execute('partx', '-av', device, attempts=3, delay_on_retry=True)
except processutils.ProcessExecutionError:
LOG.warning("Couldn't re-read the partition table "
"on device %s", device)
diff --git a/releasenotes/notes/add_burnin_dynamic_network_pairing-33e398255050eb98.yaml b/releasenotes/notes/add_burnin_dynamic_network_pairing-33e398255050eb98.yaml
index 1802f7ec..f4aaa203 100644
--- a/releasenotes/notes/add_burnin_dynamic_network_pairing-33e398255050eb98.yaml
+++ b/releasenotes/notes/add_burnin_dynamic_network_pairing-33e398255050eb98.yaml
@@ -3,15 +3,16 @@ features:
- |
For network burn-in, nodes can now be paired dynamically via a
distributed coordination backend (as an alternative to a static
- configuration). This allows burn-in to proceed on a 'first come
- first served' basis with the nodes available, rather than a node
+ configuration). This allows burn-in to proceed on a "first come
+ first served" basis with the nodes available, rather than a node
being blocked since the static partner is currently delayed.
+
In order to configure this dynamic pairing, the nodes will need
- at least 'agent_burnin_fio_network_pairing_backend_url' in their
- driver-info (the URL for the coordination backend). In order to
+ at least ``agent_burnin_fio_network_pairing_backend_url`` in their
+ ``driver_info`` (the URL for the coordination backend). In order to
separate different hardware types, which may be using different
networks and shall be burnt-in separately, the nodes can in
- addition define 'agent_burnin_fio_network_pairing_group_name' to
+ addition define ``agent_burnin_fio_network_pairing_group_name`` to
have pairing only happening between nodes in the same group. An
- additional 'agent_burnin_fio_network_pairing_timeout' allows to
+ additional parameter ``agent_burnin_fio_network_pairing_timeout`` allows to
limit the time given to the nodes to wait for a partner.
diff --git a/releasenotes/notes/detect-endianness-f53a6c4571aba3fe.yaml b/releasenotes/notes/detect-endianness-f53a6c4571aba3fe.yaml
new file mode 100644
index 00000000..82d90886
--- /dev/null
+++ b/releasenotes/notes/detect-endianness-f53a6c4571aba3fe.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+ - |
+ In case the CSV file used for the bootloader hint does not have BOM
+ we fail reading its content as utf-16 codec is too generic.
+ Fail over to utf-16-le as Little Endian is mostly used.
diff --git a/releasenotes/notes/handle-partuuid-for-fstab-e0aadea20a056982.yaml b/releasenotes/notes/handle-partuuid-for-fstab-e0aadea20a056982.yaml
new file mode 100644
index 00000000..f9b6bbc5
--- /dev/null
+++ b/releasenotes/notes/handle-partuuid-for-fstab-e0aadea20a056982.yaml
@@ -0,0 +1,10 @@
+---
+fixes:
+ - |
+ Fixes handling of a Partition UUID being returned instead of a
+ Partition's UUID when the OS may not return the Partition's UUID in time.
+ These two fields are typically referred to as PARTUUID and UUID,
+ respectively. Often these sorts of issues arise under heavy IO load.
+ We now scan, and identify which "UUID" we identified, and update
+ a Linux fstab entry appropriately. For more information, please see
+ `story #2009881 <https://storyboard.openstack.org/#!/story/2009881>`_.
diff --git a/releasenotes/notes/move_swraid_to_efibootmgr-d87c1bfde1661fb5.yaml b/releasenotes/notes/move_swraid_to_efibootmgr-d87c1bfde1661fb5.yaml
index 3fa29284..39f202ff 100644
--- a/releasenotes/notes/move_swraid_to_efibootmgr-d87c1bfde1661fb5.yaml
+++ b/releasenotes/notes/move_swraid_to_efibootmgr-d87c1bfde1661fb5.yaml
@@ -1,7 +1,7 @@
---
fixes:
- |
- Use efibootmgr instead of grub2-install for software RAID.
- This fixes an issue with images which include newer versions
- of grub2-install as they refuse bootloader installations in
- UEFI boot mode due to the lack of secure boot support.
+ Uses ``efibootmgr`` instead of ``grub2-install`` for software RAID. This
+ fixes an issue with images which include newer versions of
+ ``grub2-install``, such as CentOS Stream 8, as they refuse bootloader
+ installations in UEFI boot mode due to the lack of secure boot support.
diff --git a/releasenotes/notes/rescan-device-after-mkfs-3f9d52a2e3b6fff3.yaml b/releasenotes/notes/rescan-device-after-mkfs-3f9d52a2e3b6fff3.yaml
new file mode 100644
index 00000000..3a8d9e08
--- /dev/null
+++ b/releasenotes/notes/rescan-device-after-mkfs-3f9d52a2e3b6fff3.yaml
@@ -0,0 +1,5 @@
+---
+fixes:
+ - |
+ Adds device rescan operation after partitioning the root device to ensure
+ that updated UUIDs are reflected correctly
diff --git a/zuul.d/ironic-python-agent-jobs.yaml b/zuul.d/ironic-python-agent-jobs.yaml
index 34c4647a..4d3de185 100644
--- a/zuul.d/ironic-python-agent-jobs.yaml
+++ b/zuul.d/ironic-python-agent-jobs.yaml
@@ -38,6 +38,7 @@
s-object: True
s-proxy: True
devstack_localrc:
+ IRONIC_BOOT_MODE: bios
IRONIC_DEFAULT_BOOT_OPTION: netboot
IRONIC_DEFAULT_DEPLOY_INTERFACE: direct
SWIFT_ENABLE_TEMPURLS: True
@@ -131,7 +132,6 @@
description: Test ironic standalone with IPA from source
vars:
devstack_localrc:
- IRONIC_DEFAULT_BOOT_OPTION: netboot
IRONIC_BUILD_DEPLOY_RAMDISK: True
# NOTE(dtantsur): the ansible deploy doesn't depend on IPA code,
# excluding it from the enabled list to save gate time.
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index fa87feaf..c7448f4a 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -22,6 +22,8 @@
voting: false
- ironic-python-agent-check-image-dib-centos8:
voting: false
+ - ironic-python-agent-check-image-dib-centos9:
+ voting: false
# Non-voting jobs
- ipa-tempest-ironic-inspector-src:
voting: false