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:
parent
e06dd22e78
commit
62c5674a60
@ -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.
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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',
|
||||||
|
@ -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)
|
||||||
|
@ -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(
|
||||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user