SoftwareRAID: Use efibootmgr (and drop grub2-install)

Move the software RAID code path from grub2-install to
efibootmgr:

- remove the UEFI efibootmgr exception for software RAID
- create and populate the ESPs on the holder disks
- update the NVRAM with all ESPs (the component devices
  of the ESP mirror, use unique labels to avoid unintentional
  deduplication of entries in the NVRAM)

Story: #2009794

Change-Id: I7ed34e595215194a589c2f1cd0b39ff0336da8f1
This commit is contained in:
Arne Wiebalck 2022-01-24 15:00:05 +01:00
parent e06dd22e78
commit 62c5674a60
7 changed files with 172 additions and 45 deletions

View File

@ -20,6 +20,8 @@ from oslo_concurrency import processutils
from oslo_log import log from oslo_log import log
from ironic_python_agent import errors 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 partition_utils
from ironic_python_agent import utils from ironic_python_agent import utils
@ -92,16 +94,60 @@ def manage_uefi(device, efi_system_part_uuid=None):
efi_mounted = True efi_mounted = True
valid_efi_bootloaders = _get_efi_bootloaders(efi_partition_mount_point) valid_efi_bootloaders = _get_efi_bootloaders(efi_partition_mount_point)
if valid_efi_bootloaders: if not valid_efi_bootloaders:
_run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
efi_partition_mount_point)
return True
else:
# NOTE(dtantsur): if we have an empty EFI partition, try to use # NOTE(dtantsur): if we have an empty EFI partition, try to use
# grub-install to populate it. # grub-install to populate it.
LOG.warning('Empty EFI partition detected.') LOG.warning('Empty EFI partition detected.')
return False return False
if not hardware.is_md_device(device):
efi_devices = [device]
efi_partition_numbers = [efi_partition]
efi_label_suffix = ''
else:
# umount to allow for signature removal (to avoid confusion about
# which ESP to mount once the instance is deployed)
utils.execute('umount', efi_partition_mount_point, attempts=3,
delay_on_retry=True)
efi_mounted = False
holders = hardware.get_holder_disks(device)
efi_md_device = image.prepare_boot_partitions_for_softraid(
device, holders, efi_device_part, target_boot_mode='uefi'
)
efi_devices = hardware.get_component_devices(efi_md_device)
efi_partition_numbers = []
_PARTITION_NUMBER = re.compile(r'(\d+)$')
for dev in efi_devices:
match = _PARTITION_NUMBER.search(dev)
if match:
partition_number = match.group(1)
efi_partition_numbers.append(partition_number)
else:
raise errors.DeviceNotFound(
"Could not extract the partition number "
"from %s!" % dev)
efi_label_suffix = "(RAID, part%s)"
# remount for _run_efibootmgr
utils.execute('mount', efi_device_part, efi_partition_mount_point)
efi_mounted = True
efi_dev_part = zip(efi_devices, efi_partition_numbers)
for i, (efi_dev, efi_part) in enumerate(efi_dev_part):
LOG.debug("Calling efibootmgr with dev %s part %s",
efi_dev, efi_part)
if efi_label_suffix:
# NOTE (arne_wiebalck): uniqify the labels to prevent
# unintentional boot entry cleanup
_run_efibootmgr(valid_efi_bootloaders, efi_dev, efi_part,
efi_partition_mount_point,
efi_label_suffix % i)
else:
_run_efibootmgr(valid_efi_bootloaders, efi_dev, efi_part,
efi_partition_mount_point)
return True
except processutils.ProcessExecutionError as e: except processutils.ProcessExecutionError as e:
error_msg = ('Could not verify uefi on device %(dev)s, ' error_msg = ('Could not verify uefi on device %(dev)s, '
'failed with %(err)s.' % {'dev': device, 'err': e}) 'failed with %(err)s.' % {'dev': device, 'err': e})
@ -227,7 +273,7 @@ def remove_boot_record(boot_num):
def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition, def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
mount_point): mount_point, label_suffix=None):
"""Executes efibootmgr and removes duplicate entries. """Executes efibootmgr and removes duplicate entries.
:param valid_efi_bootloaders: the list of valid efi bootloaders :param valid_efi_bootloaders: the list of valid efi bootloaders
@ -236,6 +282,9 @@ def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
:param mount_point: The mountpoint for the EFI partition so we can :param mount_point: The mountpoint for the EFI partition so we can
read contents of files if necessary to perform read contents of files if necessary to perform
proper bootloader injection operations. proper bootloader injection operations.
:param label_suffix: a string to be appended to the EFI label,
mainly used in the case of software to uniqify
the entries for the md components.
""" """
# Before updating let's get information about the bootorder # Before updating let's get information about the bootorder
@ -255,9 +304,13 @@ def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
v_efi_bl_path = v_bl.replace(csv_filename, str(csv_contents[0])) v_efi_bl_path = v_bl.replace(csv_filename, str(csv_contents[0]))
v_efi_bl_path = '\\' + v_efi_bl_path.replace('/', '\\') v_efi_bl_path = '\\' + v_efi_bl_path.replace('/', '\\')
label = csv_contents[1] label = csv_contents[1]
if label_suffix:
label = label + " " + str(label_suffix)
else: else:
v_efi_bl_path = '\\' + v_bl.replace('/', '\\') v_efi_bl_path = '\\' + v_bl.replace('/', '\\')
label = 'ironic' + str(label_id) label = 'ironic' + str(label_id)
if label_suffix:
label = label + " " + str(label_suffix)
# Iterate through standard out, and look for duplicates # Iterate through standard out, and look for duplicates
for boot_num, boot_rec in boot_records: for boot_num, boot_rec in boot_records:
@ -268,9 +321,11 @@ def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
LOG.debug("Found bootnum %s matching label", boot_num) LOG.debug("Found bootnum %s matching label", boot_num)
remove_boot_record(boot_num) remove_boot_record(boot_num)
LOG.debug("Adding loader %(path)s on partition %(part)s of device " LOG.info("Adding loader %(path)s on partition %(part)s of device "
" %(dev)s", {'path': v_efi_bl_path, 'part': efi_partition, " %(dev)s with label %(label)s",
'dev': device}) {'path': v_efi_bl_path, 'part': efi_partition,
'dev': device, 'label': label})
# Update the nvram using efibootmgr # Update the nvram using efibootmgr
add_boot_record(device, efi_partition, v_efi_bl_path, label) add_boot_record(device, efi_partition, v_efi_bl_path, label)
# Increment the ID in case the loop runs again. # Increment the ID in case the loop runs again.

View File

@ -105,8 +105,8 @@ def _is_bootloader_loaded(dev):
# TODO(rg): handle PreP boot parts relocation as well # TODO(rg): handle PreP boot parts relocation as well
def _prepare_boot_partitions_for_softraid(device, holders, efi_part, def prepare_boot_partitions_for_softraid(device, holders, efi_part,
target_boot_mode): target_boot_mode):
"""Prepare boot partitions when relevant. """Prepare boot partitions when relevant.
Create either a RAIDed EFI partition or bios boot partitions for software Create either a RAIDed EFI partition or bios boot partitions for software
@ -311,7 +311,7 @@ def _install_grub2(device, root_uuid, efi_system_part_uuid=None,
efi_partition = efi_part efi_partition = efi_part
if hardware.is_md_device(device): if hardware.is_md_device(device):
holders = hardware.get_holder_disks(device) holders = hardware.get_holder_disks(device)
efi_partition = _prepare_boot_partitions_for_softraid( efi_partition = prepare_boot_partitions_for_softraid(
device, holders, efi_part, target_boot_mode device, holders, efi_part, target_boot_mode
) )
@ -648,9 +648,7 @@ def _efi_boot_setup(device, efi_system_part_uuid=None, target_boot_mode=None):
{'target': target_boot_mode, {'target': target_boot_mode,
'current': boot.current_boot_mode}) 'current': boot.current_boot_mode})
# FIXME(arne_wiebalck): make software RAID work with efibootmgr if boot.current_boot_mode == 'uefi':
if (boot.current_boot_mode == 'uefi'
and not hardware.is_md_device(device)):
try: try:
utils.execute('efibootmgr', '--version') utils.execute('efibootmgr', '--version')
except FileNotFoundError: except FileNotFoundError:

View File

@ -181,7 +181,7 @@ def _get_md_uuid(raid_device):
return match.group(1) return match.group(1)
def _get_component_devices(raid_device): def get_component_devices(raid_device):
"""Get the component devices of a Software RAID device. """Get the component devices of a Software RAID device.
Get the UUID of the md device and scan all other devices Get the UUID of the md device and scan all other devices
@ -325,7 +325,7 @@ def md_restart(raid_device):
""" """
try: try:
LOG.debug('Restarting software RAID device %s', raid_device) LOG.debug('Restarting software RAID device %s', raid_device)
component_devices = _get_component_devices(raid_device) component_devices = get_component_devices(raid_device)
il_utils.execute('mdadm', '--stop', raid_device) il_utils.execute('mdadm', '--stop', raid_device)
il_utils.execute('mdadm', '--assemble', raid_device, il_utils.execute('mdadm', '--assemble', raid_device,
*component_devices) *component_devices)
@ -2221,7 +2221,7 @@ class GenericHardwareManager(HardwareManager):
def _delete_config_pass(self, raid_devices): def _delete_config_pass(self, raid_devices):
all_holder_disks = [] all_holder_disks = []
for raid_device in raid_devices: for raid_device in raid_devices:
component_devices = _get_component_devices(raid_device.name) component_devices = get_component_devices(raid_device.name)
if not component_devices: if not component_devices:
# A "Software RAID device" without components is usually # A "Software RAID device" without components is usually
# a partition on an md device (as, for instance, created # a partition on an md device (as, for instance, created

View File

@ -1654,7 +1654,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
self.assertFalse(mock_dispatch.called) self.assertFalse(mock_dispatch.called)
@mock.patch.object(disk_utils, 'find_efi_partition', autospec=True) @mock.patch.object(disk_utils, 'find_efi_partition', autospec=True)
def test__prepare_boot_partitions_for_softraid_uefi_gpt( def test_prepare_boot_partitions_for_softraid_uefi_gpt(
self, mock_efi_part, mock_execute, mock_dispatch): self, mock_efi_part, mock_execute, mock_dispatch):
mock_efi_part.return_value = {'number': '12'} mock_efi_part.return_value = {'number': '12'}
mock_execute.side_effect = [ mock_execute.side_effect = [
@ -1673,7 +1673,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
(None, None), # wipefs (None, None), # wipefs
] ]
efi_part = image._prepare_boot_partitions_for_softraid( efi_part = image.prepare_boot_partitions_for_softraid(
'/dev/md0', ['/dev/sda', '/dev/sdb'], None, '/dev/md0', ['/dev/sda', '/dev/sdb'], None,
target_boot_mode='uefi') target_boot_mode='uefi')
@ -1704,7 +1704,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
@mock.patch.object(disk_utils, 'find_efi_partition', autospec=True) @mock.patch.object(disk_utils, 'find_efi_partition', autospec=True)
@mock.patch.object(ilib_utils, 'mkfs', autospec=True) @mock.patch.object(ilib_utils, 'mkfs', autospec=True)
def test__prepare_boot_partitions_for_softraid_uefi_gpt_esp_not_found( def test_prepare_boot_partitions_for_softraid_uefi_gpt_esp_not_found(
self, mock_mkfs, mock_efi_part, mock_execute, mock_dispatch): self, mock_mkfs, mock_efi_part, mock_execute, mock_dispatch):
mock_efi_part.return_value = None mock_efi_part.return_value = None
mock_execute.side_effect = [ mock_execute.side_effect = [
@ -1721,7 +1721,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
(None, None), # mdadm (None, None), # mdadm
] ]
efi_part = image._prepare_boot_partitions_for_softraid( efi_part = image.prepare_boot_partitions_for_softraid(
'/dev/md0', ['/dev/sda', '/dev/sdb'], None, '/dev/md0', ['/dev/sda', '/dev/sdb'], None,
target_boot_mode='uefi') target_boot_mode='uefi')
@ -1748,7 +1748,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
], any_order=False) ], any_order=False)
self.assertEqual(efi_part, '/dev/md/esp') self.assertEqual(efi_part, '/dev/md/esp')
def test__prepare_boot_partitions_for_softraid_uefi_gpt_efi_provided( def test_prepare_boot_partitions_for_softraid_uefi_gpt_efi_provided(
self, mock_execute, mock_dispatch): self, mock_execute, mock_dispatch):
mock_execute.side_effect = [ mock_execute.side_effect = [
('451', None), # sgdisk -F ('451', None), # sgdisk -F
@ -1766,7 +1766,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
(None, None), # wipefs (None, None), # wipefs
] ]
efi_part = image._prepare_boot_partitions_for_softraid( efi_part = image.prepare_boot_partitions_for_softraid(
'/dev/md0', ['/dev/sda', '/dev/sdb'], '/dev/md0p15', '/dev/md0', ['/dev/sda', '/dev/sdb'], '/dev/md0p15',
target_boot_mode='uefi') target_boot_mode='uefi')
@ -1796,10 +1796,10 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
@mock.patch.object(disk_utils, 'get_partition_table_type', autospec=True, @mock.patch.object(disk_utils, 'get_partition_table_type', autospec=True,
return_value='msdos') return_value='msdos')
def test__prepare_boot_partitions_for_softraid_bios_msdos( def test_prepare_boot_partitions_for_softraid_bios_msdos(
self, mock_label_scan, mock_execute, mock_dispatch): self, mock_label_scan, mock_execute, mock_dispatch):
efi_part = image._prepare_boot_partitions_for_softraid( efi_part = image.prepare_boot_partitions_for_softraid(
'/dev/md0', ['/dev/sda', '/dev/sdb'], 'notusedanyway', '/dev/md0', ['/dev/sda', '/dev/sdb'], 'notusedanyway',
target_boot_mode='bios') target_boot_mode='bios')
@ -1812,7 +1812,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
@mock.patch.object(disk_utils, 'get_partition_table_type', autospec=True, @mock.patch.object(disk_utils, 'get_partition_table_type', autospec=True,
return_value='gpt') return_value='gpt')
def test__prepare_boot_partitions_for_softraid_bios_gpt( def test_prepare_boot_partitions_for_softraid_bios_gpt(
self, mock_label_scan, mock_execute, mock_dispatch): self, mock_label_scan, mock_execute, mock_dispatch):
mock_execute.side_effect = [ mock_execute.side_effect = [
@ -1822,7 +1822,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
(None, None), # bios boot grub (None, None), # bios boot grub
] ]
efi_part = image._prepare_boot_partitions_for_softraid( efi_part = image.prepare_boot_partitions_for_softraid(
'/dev/md0', ['/dev/sda', '/dev/sdb'], 'notusedanyway', '/dev/md0', ['/dev/sda', '/dev/sdb'], 'notusedanyway',
target_boot_mode='bios') target_boot_mode='bios')
@ -1854,7 +1854,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
@mock.patch.object(os, 'environ', autospec=True) @mock.patch.object(os, 'environ', autospec=True)
@mock.patch.object(os, 'makedirs', autospec=True) @mock.patch.object(os, 'makedirs', autospec=True)
@mock.patch.object(partition_utils, 'get_partition', autospec=True) @mock.patch.object(partition_utils, 'get_partition', autospec=True)
@mock.patch.object(image, '_prepare_boot_partitions_for_softraid', @mock.patch.object(image, 'prepare_boot_partitions_for_softraid',
autospec=True, autospec=True,
return_value='/dev/md/esp') return_value='/dev/md/esp')
@mock.patch.object(image, '_has_dracut', @mock.patch.object(image, '_has_dracut',
@ -1972,7 +1972,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
@mock.patch.object(os, 'environ', autospec=True) @mock.patch.object(os, 'environ', autospec=True)
@mock.patch.object(os, 'makedirs', autospec=True) @mock.patch.object(os, 'makedirs', autospec=True)
@mock.patch.object(partition_utils, 'get_partition', autospec=True) @mock.patch.object(partition_utils, 'get_partition', autospec=True)
@mock.patch.object(image, '_prepare_boot_partitions_for_softraid', @mock.patch.object(image, 'prepare_boot_partitions_for_softraid',
autospec=True, autospec=True,
return_value=[]) return_value=[])
@mock.patch.object(image, '_has_dracut', @mock.patch.object(image, '_has_dracut',

View File

@ -19,6 +19,8 @@ from ironic_lib import disk_utils
from ironic_python_agent import efi_utils from ironic_python_agent import efi_utils
from ironic_python_agent import errors 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 partition_utils
from ironic_python_agent.tests.unit import base from ironic_python_agent.tests.unit import base
from ironic_python_agent import utils from ironic_python_agent import utils
@ -158,12 +160,15 @@ class TestManageUefi(base.IronicAgentTest):
mock_rescan.assert_called_once_with(self.fake_dev) mock_rescan.assert_called_once_with(self.fake_dev)
@mock.patch.object(os.path, 'exists', lambda *_: False) @mock.patch.object(os.path, 'exists', lambda *_: False)
@mock.patch.object(hardware, 'is_md_device', autospec=True)
@mock.patch.object(efi_utils, '_get_efi_bootloaders', autospec=True) @mock.patch.object(efi_utils, '_get_efi_bootloaders', autospec=True)
@mock.patch.object(os, 'makedirs', autospec=True) @mock.patch.object(os, 'makedirs', autospec=True)
def test_ok(self, mkdir_mock, mock_efi_bl, mock_utils_efi_part, def test_ok(self, mkdir_mock, mock_efi_bl, mock_is_md_device,
mock_get_part_uuid, mock_execute, mock_rescan): mock_utils_efi_part, mock_get_part_uuid, mock_execute,
mock_rescan):
mock_utils_efi_part.return_value = {'number': '1'} mock_utils_efi_part.return_value = {'number': '1'}
mock_get_part_uuid.return_value = self.fake_dev mock_get_part_uuid.return_value = self.fake_dev
mock_is_md_device.return_value = False
mock_efi_bl.return_value = ['EFI/BOOT/BOOTX64.EFI'] mock_efi_bl.return_value = ['EFI/BOOT/BOOTX64.EFI']
@ -192,13 +197,16 @@ class TestManageUefi(base.IronicAgentTest):
mock_rescan.assert_called_once_with(self.fake_dev) mock_rescan.assert_called_once_with(self.fake_dev)
@mock.patch.object(os.path, 'exists', lambda *_: False) @mock.patch.object(os.path, 'exists', lambda *_: False)
@mock.patch.object(hardware, 'is_md_device', autospec=True)
@mock.patch.object(efi_utils, '_get_efi_bootloaders', autospec=True) @mock.patch.object(efi_utils, '_get_efi_bootloaders', autospec=True)
@mock.patch.object(os, 'makedirs', autospec=True) @mock.patch.object(os, 'makedirs', autospec=True)
def test_found_csv(self, mkdir_mock, mock_efi_bl, mock_utils_efi_part, def test_found_csv(self, mkdir_mock, mock_efi_bl, mock_is_md_device,
mock_get_part_uuid, mock_execute, mock_rescan): mock_utils_efi_part, mock_get_part_uuid, mock_execute,
mock_rescan):
mock_utils_efi_part.return_value = {'number': '1'} mock_utils_efi_part.return_value = {'number': '1'}
mock_get_part_uuid.return_value = self.fake_dev mock_get_part_uuid.return_value = self.fake_dev
mock_efi_bl.return_value = ['EFI/vendor/BOOTX64.CSV'] mock_efi_bl.return_value = ['EFI/vendor/BOOTX64.CSV']
mock_is_md_device.return_value = False
# Format is <file>,<entry_name>,<options>,humanfriendlytextnotused # Format is <file>,<entry_name>,<options>,humanfriendlytextnotused
# https://www.rodsbooks.com/efi-bootloaders/fallback.html # https://www.rodsbooks.com/efi-bootloaders/fallback.html
@ -242,12 +250,15 @@ Boot0002: VENDMAGIC FvFile(9f3c6294-bf9b-4208-9808-be45dfc34b51)
mock_execute.assert_has_calls(expected) mock_execute.assert_has_calls(expected)
@mock.patch.object(os.path, 'exists', lambda *_: False) @mock.patch.object(os.path, 'exists', lambda *_: False)
@mock.patch.object(hardware, 'is_md_device', autospec=True)
@mock.patch.object(efi_utils, '_get_efi_bootloaders', autospec=True) @mock.patch.object(efi_utils, '_get_efi_bootloaders', autospec=True)
@mock.patch.object(os, 'makedirs', autospec=True) @mock.patch.object(os, 'makedirs', autospec=True)
def test_nvme_device(self, mkdir_mock, mock_efi_bl, mock_utils_efi_part, def test_nvme_device(self, mkdir_mock, mock_efi_bl, mock_is_md_device,
mock_get_part_uuid, mock_execute, mock_rescan): mock_utils_efi_part, mock_get_part_uuid,
mock_execute, mock_rescan):
mock_utils_efi_part.return_value = {'number': '1'} mock_utils_efi_part.return_value = {'number': '1'}
mock_get_part_uuid.return_value = '/dev/fakenvme0p1' mock_get_part_uuid.return_value = '/dev/fakenvme0p1'
mock_is_md_device.return_value = False
mock_efi_bl.return_value = ['EFI/BOOT/BOOTX64.EFI'] mock_efi_bl.return_value = ['EFI/BOOT/BOOTX64.EFI']
@ -274,12 +285,15 @@ Boot0002: VENDMAGIC FvFile(9f3c6294-bf9b-4208-9808-be45dfc34b51)
mock_execute.assert_has_calls(expected) mock_execute.assert_has_calls(expected)
@mock.patch.object(os.path, 'exists', lambda *_: False) @mock.patch.object(os.path, 'exists', lambda *_: False)
@mock.patch.object(hardware, 'is_md_device', autospec=True)
@mock.patch.object(efi_utils, '_get_efi_bootloaders', autospec=True) @mock.patch.object(efi_utils, '_get_efi_bootloaders', autospec=True)
@mock.patch.object(os, 'makedirs', autospec=True) @mock.patch.object(os, 'makedirs', autospec=True)
def test_wholedisk(self, mkdir_mock, mock_efi_bl, mock_utils_efi_part, def test_wholedisk(self, mkdir_mock, mock_efi_bl, mock_is_md_device,
mock_get_part_uuid, mock_execute, mock_rescan): mock_utils_efi_part, mock_get_part_uuid, mock_execute,
mock_rescan):
mock_utils_efi_part.return_value = {'number': '1'} mock_utils_efi_part.return_value = {'number': '1'}
mock_get_part_uuid.side_effect = Exception mock_get_part_uuid.side_effect = Exception
mock_is_md_device.return_value = False
mock_efi_bl.return_value = ['EFI/BOOT/BOOTX64.EFI'] mock_efi_bl.return_value = ['EFI/BOOT/BOOTX64.EFI']
@ -304,3 +318,56 @@ Boot0002: VENDMAGIC FvFile(9f3c6294-bf9b-4208-9808-be45dfc34b51)
mkdir_mock.assert_called_once_with(self.fake_dir + '/boot/efi') mkdir_mock.assert_called_once_with(self.fake_dir + '/boot/efi')
mock_efi_bl.assert_called_once_with(self.fake_dir + '/boot/efi') mock_efi_bl.assert_called_once_with(self.fake_dir + '/boot/efi')
mock_execute.assert_has_calls(expected) mock_execute.assert_has_calls(expected)
@mock.patch.object(os.path, 'exists', lambda *_: False)
@mock.patch.object(hardware, 'get_component_devices', autospec=True)
@mock.patch.object(image,
'prepare_boot_partitions_for_softraid',
autospec=True)
@mock.patch.object(hardware, 'get_holder_disks', autospec=True)
@mock.patch.object(hardware, 'is_md_device', autospec=True)
@mock.patch.object(efi_utils, '_get_efi_bootloaders', autospec=True)
@mock.patch.object(os, 'makedirs', autospec=True)
def test_software_raid(self, mkdir_mock, mock_efi_bl, mock_is_md_device,
mock_get_holder_disks, mock_prepare,
mock_get_component_devices,
mock_utils_efi_part, mock_get_part_uuid,
mock_execute, mock_rescan):
mock_utils_efi_part.return_value = {'number': '1'}
mock_get_part_uuid.side_effect = Exception
mock_is_md_device.return_value = True
mock_get_holder_disks.return_value = ['/dev/sda', '/dev/sdb']
mock_prepare.return_value = '/dev/md125'
mock_get_component_devices.return_value = ['/dev/sda3', '/dev/sdb3']
mock_efi_bl.return_value = ['EFI/BOOT/BOOTX64.EFI']
mock_execute.side_effect = iter([('', ''), ('', ''),
('', ''), ('', ''),
('', ''), ('', ''),
('', ''), ('', ''),
('', '')])
expected = [mock.call('mount', self.fake_efi_system_part,
self.fake_dir + '/boot/efi'),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
mock.call('mount', self.fake_efi_system_part,
self.fake_dir + '/boot/efi'),
mock.call('efibootmgr', '-v'),
mock.call('efibootmgr', '-v', '-c', '-d', '/dev/sda3',
'-p', '3', '-w', '-L', 'ironic1 (RAID, part0)',
'-l', '\\EFI\\BOOT\\BOOTX64.EFI'),
mock.call('efibootmgr', '-v'),
mock.call('efibootmgr', '-v', '-c', '-d', '/dev/sdb3',
'-p', '3', '-w', '-L', 'ironic1 (RAID, part1)',
'-l', '\\EFI\\BOOT\\BOOTX64.EFI'),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
mock.call('sync')]
result = efi_utils.manage_uefi(self.fake_dev, None)
self.assertTrue(result)
mkdir_mock.assert_called_once_with(self.fake_dir + '/boot/efi')
mock_efi_bl.assert_called_once_with(self.fake_dir + '/boot/efi')
mock_execute.assert_has_calls(expected)

View File

@ -3660,9 +3660,9 @@ class TestGenericHardwareManager(base.IronicAgentTest):
@mock.patch.object(hardware, '_get_md_uuid', autospec=True) @mock.patch.object(hardware, '_get_md_uuid', autospec=True)
@mock.patch.object(hardware, 'list_all_block_devices', autospec=True) @mock.patch.object(hardware, 'list_all_block_devices', autospec=True)
@mock.patch.object(il_utils, 'execute', autospec=True) @mock.patch.object(il_utils, 'execute', autospec=True)
def test__get_component_devices(self, mocked_execute, def test_get_component_devices(self, mocked_execute,
mocked_list_all_block_devices, mocked_list_all_block_devices,
mocked_md_uuid): mocked_md_uuid):
raid_device1 = hardware.BlockDevice('/dev/md0', 'RAID-1', raid_device1 = hardware.BlockDevice('/dev/md0', 'RAID-1',
107374182400, True) 107374182400, True)
sda = hardware.BlockDevice('/dev/sda', 'model12', 21, True) sda = hardware.BlockDevice('/dev/sda', 'model12', 21, True)
@ -3682,7 +3682,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
[hws.MDADM_EXAMINE_OUTPUT_NON_MEMBER, '_'], [hws.MDADM_EXAMINE_OUTPUT_NON_MEMBER, '_'],
] ]
component_devices = hardware._get_component_devices(raid_device1) component_devices = hardware.get_component_devices(raid_device1)
self.assertEqual(['/dev/sda1'], component_devices) self.assertEqual(['/dev/sda1'], component_devices)
mocked_execute.assert_has_calls([ mocked_execute.assert_has_calls([
mock.call('mdadm', '--examine', '/dev/sda', mock.call('mdadm', '--examine', '/dev/sda',
@ -3739,7 +3739,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual(['/dev/sda'], holder_disks) self.assertEqual(['/dev/sda'], holder_disks)
@mock.patch.object(hardware, 'get_holder_disks', autospec=True) @mock.patch.object(hardware, 'get_holder_disks', autospec=True)
@mock.patch.object(hardware, '_get_component_devices', autospec=True) @mock.patch.object(hardware, 'get_component_devices', autospec=True)
@mock.patch.object(hardware, 'list_all_block_devices', autospec=True) @mock.patch.object(hardware, 'list_all_block_devices', autospec=True)
@mock.patch.object(il_utils, 'execute', autospec=True) @mock.patch.object(il_utils, 'execute', autospec=True)
def test_delete_configuration(self, mocked_execute, mocked_list, def test_delete_configuration(self, mocked_execute, mocked_list,
@ -3827,7 +3827,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mock.call('mdadm', '--assemble', '--scan', check_exit_code=False), mock.call('mdadm', '--assemble', '--scan', check_exit_code=False),
]) ])
@mock.patch.object(hardware, '_get_component_devices', autospec=True) @mock.patch.object(hardware, 'get_component_devices', autospec=True)
@mock.patch.object(hardware, 'list_all_block_devices', autospec=True) @mock.patch.object(hardware, 'list_all_block_devices', autospec=True)
@mock.patch.object(il_utils, 'execute', autospec=True) @mock.patch.object(il_utils, 'execute', autospec=True)
def test_delete_configuration_partition(self, mocked_execute, mocked_list, def test_delete_configuration_partition(self, mocked_execute, mocked_list,
@ -3852,7 +3852,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mock.call('mdadm', '--assemble', '--scan', check_exit_code=False), mock.call('mdadm', '--assemble', '--scan', check_exit_code=False),
]) ])
@mock.patch.object(hardware, '_get_component_devices', autospec=True) @mock.patch.object(hardware, 'get_component_devices', autospec=True)
@mock.patch.object(hardware, 'list_all_block_devices', autospec=True) @mock.patch.object(hardware, 'list_all_block_devices', autospec=True)
@mock.patch.object(il_utils, 'execute', autospec=True) @mock.patch.object(il_utils, 'execute', autospec=True)
def test_delete_configuration_failure_blocks_remaining( def test_delete_configuration_failure_blocks_remaining(

View File

@ -0,0 +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.