Skip to content

Commit 62c5674

Browse files
committed
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
1 parent e06dd22 commit 62c5674

File tree

7 files changed

+172
-45
lines changed

7 files changed

+172
-45
lines changed

ironic_python_agent/efi_utils.py

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
from oslo_log import log
2121

2222
from ironic_python_agent import errors
23+
from ironic_python_agent.extensions import image
24+
from ironic_python_agent import hardware
2325
from ironic_python_agent import partition_utils
2426
from ironic_python_agent import utils
2527

@@ -92,16 +94,60 @@ def manage_uefi(device, efi_system_part_uuid=None):
9294
efi_mounted = True
9395

9496
valid_efi_bootloaders = _get_efi_bootloaders(efi_partition_mount_point)
95-
if valid_efi_bootloaders:
96-
_run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
97-
efi_partition_mount_point)
98-
return True
99-
else:
97+
if not valid_efi_bootloaders:
10098
# NOTE(dtantsur): if we have an empty EFI partition, try to use
10199
# grub-install to populate it.
102100
LOG.warning('Empty EFI partition detected.')
103101
return False
104102

103+
if not hardware.is_md_device(device):
104+
efi_devices = [device]
105+
efi_partition_numbers = [efi_partition]
106+
efi_label_suffix = ''
107+
else:
108+
# umount to allow for signature removal (to avoid confusion about
109+
# which ESP to mount once the instance is deployed)
110+
utils.execute('umount', efi_partition_mount_point, attempts=3,
111+
delay_on_retry=True)
112+
efi_mounted = False
113+
114+
holders = hardware.get_holder_disks(device)
115+
efi_md_device = image.prepare_boot_partitions_for_softraid(
116+
device, holders, efi_device_part, target_boot_mode='uefi'
117+
)
118+
efi_devices = hardware.get_component_devices(efi_md_device)
119+
efi_partition_numbers = []
120+
_PARTITION_NUMBER = re.compile(r'(\d+)$')
121+
for dev in efi_devices:
122+
match = _PARTITION_NUMBER.search(dev)
123+
if match:
124+
partition_number = match.group(1)
125+
efi_partition_numbers.append(partition_number)
126+
else:
127+
raise errors.DeviceNotFound(
128+
"Could not extract the partition number "
129+
"from %s!" % dev)
130+
efi_label_suffix = "(RAID, part%s)"
131+
132+
# remount for _run_efibootmgr
133+
utils.execute('mount', efi_device_part, efi_partition_mount_point)
134+
efi_mounted = True
135+
136+
efi_dev_part = zip(efi_devices, efi_partition_numbers)
137+
for i, (efi_dev, efi_part) in enumerate(efi_dev_part):
138+
LOG.debug("Calling efibootmgr with dev %s part %s",
139+
efi_dev, efi_part)
140+
if efi_label_suffix:
141+
# NOTE (arne_wiebalck): uniqify the labels to prevent
142+
# unintentional boot entry cleanup
143+
_run_efibootmgr(valid_efi_bootloaders, efi_dev, efi_part,
144+
efi_partition_mount_point,
145+
efi_label_suffix % i)
146+
else:
147+
_run_efibootmgr(valid_efi_bootloaders, efi_dev, efi_part,
148+
efi_partition_mount_point)
149+
return True
150+
105151
except processutils.ProcessExecutionError as e:
106152
error_msg = ('Could not verify uefi on device %(dev)s, '
107153
'failed with %(err)s.' % {'dev': device, 'err': e})
@@ -227,7 +273,7 @@ def remove_boot_record(boot_num):
227273

228274

229275
def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
230-
mount_point):
276+
mount_point, label_suffix=None):
231277
"""Executes efibootmgr and removes duplicate entries.
232278
233279
:param valid_efi_bootloaders: the list of valid efi bootloaders
@@ -236,6 +282,9 @@ def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
236282
:param mount_point: The mountpoint for the EFI partition so we can
237283
read contents of files if necessary to perform
238284
proper bootloader injection operations.
285+
:param label_suffix: a string to be appended to the EFI label,
286+
mainly used in the case of software to uniqify
287+
the entries for the md components.
239288
"""
240289

241290
# Before updating let's get information about the bootorder
@@ -255,9 +304,13 @@ def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
255304
v_efi_bl_path = v_bl.replace(csv_filename, str(csv_contents[0]))
256305
v_efi_bl_path = '\\' + v_efi_bl_path.replace('/', '\\')
257306
label = csv_contents[1]
307+
if label_suffix:
308+
label = label + " " + str(label_suffix)
258309
else:
259310
v_efi_bl_path = '\\' + v_bl.replace('/', '\\')
260311
label = 'ironic' + str(label_id)
312+
if label_suffix:
313+
label = label + " " + str(label_suffix)
261314

262315
# Iterate through standard out, and look for duplicates
263316
for boot_num, boot_rec in boot_records:
@@ -268,9 +321,11 @@ def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
268321
LOG.debug("Found bootnum %s matching label", boot_num)
269322
remove_boot_record(boot_num)
270323

271-
LOG.debug("Adding loader %(path)s on partition %(part)s of device "
272-
" %(dev)s", {'path': v_efi_bl_path, 'part': efi_partition,
273-
'dev': device})
324+
LOG.info("Adding loader %(path)s on partition %(part)s of device "
325+
" %(dev)s with label %(label)s",
326+
{'path': v_efi_bl_path, 'part': efi_partition,
327+
'dev': device, 'label': label})
328+
274329
# Update the nvram using efibootmgr
275330
add_boot_record(device, efi_partition, v_efi_bl_path, label)
276331
# Increment the ID in case the loop runs again.

ironic_python_agent/extensions/image.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ def _is_bootloader_loaded(dev):
105105

106106

107107
# TODO(rg): handle PreP boot parts relocation as well
108-
def _prepare_boot_partitions_for_softraid(device, holders, efi_part,
109-
target_boot_mode):
108+
def prepare_boot_partitions_for_softraid(device, holders, efi_part,
109+
target_boot_mode):
110110
"""Prepare boot partitions when relevant.
111111
112112
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,
311311
efi_partition = efi_part
312312
if hardware.is_md_device(device):
313313
holders = hardware.get_holder_disks(device)
314-
efi_partition = _prepare_boot_partitions_for_softraid(
314+
efi_partition = prepare_boot_partitions_for_softraid(
315315
device, holders, efi_part, target_boot_mode
316316
)
317317

@@ -648,9 +648,7 @@ def _efi_boot_setup(device, efi_system_part_uuid=None, target_boot_mode=None):
648648
{'target': target_boot_mode,
649649
'current': boot.current_boot_mode})
650650

651-
# FIXME(arne_wiebalck): make software RAID work with efibootmgr
652-
if (boot.current_boot_mode == 'uefi'
653-
and not hardware.is_md_device(device)):
651+
if boot.current_boot_mode == 'uefi':
654652
try:
655653
utils.execute('efibootmgr', '--version')
656654
except FileNotFoundError:

ironic_python_agent/hardware.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def _get_md_uuid(raid_device):
181181
return match.group(1)
182182

183183

184-
def _get_component_devices(raid_device):
184+
def get_component_devices(raid_device):
185185
"""Get the component devices of a Software RAID device.
186186
187187
Get the UUID of the md device and scan all other devices
@@ -325,7 +325,7 @@ def md_restart(raid_device):
325325
"""
326326
try:
327327
LOG.debug('Restarting software RAID device %s', raid_device)
328-
component_devices = _get_component_devices(raid_device)
328+
component_devices = get_component_devices(raid_device)
329329
il_utils.execute('mdadm', '--stop', raid_device)
330330
il_utils.execute('mdadm', '--assemble', raid_device,
331331
*component_devices)
@@ -2221,7 +2221,7 @@ def _scan_raids():
22212221
def _delete_config_pass(self, raid_devices):
22222222
all_holder_disks = []
22232223
for raid_device in raid_devices:
2224-
component_devices = _get_component_devices(raid_device.name)
2224+
component_devices = get_component_devices(raid_device.name)
22252225
if not component_devices:
22262226
# A "Software RAID device" without components is usually
22272227
# a partition on an md device (as, for instance, created

ironic_python_agent/tests/unit/extensions/test_image.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,7 +1654,7 @@ def test__install_grub2_command_fail(self, mock_get_part_uuid,
16541654
self.assertFalse(mock_dispatch.called)
16551655

16561656
@mock.patch.object(disk_utils, 'find_efi_partition', autospec=True)
1657-
def test__prepare_boot_partitions_for_softraid_uefi_gpt(
1657+
def test_prepare_boot_partitions_for_softraid_uefi_gpt(
16581658
self, mock_efi_part, mock_execute, mock_dispatch):
16591659
mock_efi_part.return_value = {'number': '12'}
16601660
mock_execute.side_effect = [
@@ -1673,7 +1673,7 @@ def test__prepare_boot_partitions_for_softraid_uefi_gpt(
16731673
(None, None), # wipefs
16741674
]
16751675

1676-
efi_part = image._prepare_boot_partitions_for_softraid(
1676+
efi_part = image.prepare_boot_partitions_for_softraid(
16771677
'/dev/md0', ['/dev/sda', '/dev/sdb'], None,
16781678
target_boot_mode='uefi')
16791679

@@ -1704,7 +1704,7 @@ def test__prepare_boot_partitions_for_softraid_uefi_gpt(
17041704

17051705
@mock.patch.object(disk_utils, 'find_efi_partition', autospec=True)
17061706
@mock.patch.object(ilib_utils, 'mkfs', autospec=True)
1707-
def test__prepare_boot_partitions_for_softraid_uefi_gpt_esp_not_found(
1707+
def test_prepare_boot_partitions_for_softraid_uefi_gpt_esp_not_found(
17081708
self, mock_mkfs, mock_efi_part, mock_execute, mock_dispatch):
17091709
mock_efi_part.return_value = None
17101710
mock_execute.side_effect = [
@@ -1721,7 +1721,7 @@ def test__prepare_boot_partitions_for_softraid_uefi_gpt_esp_not_found(
17211721
(None, None), # mdadm
17221722
]
17231723

1724-
efi_part = image._prepare_boot_partitions_for_softraid(
1724+
efi_part = image.prepare_boot_partitions_for_softraid(
17251725
'/dev/md0', ['/dev/sda', '/dev/sdb'], None,
17261726
target_boot_mode='uefi')
17271727

@@ -1748,7 +1748,7 @@ def test__prepare_boot_partitions_for_softraid_uefi_gpt_esp_not_found(
17481748
], any_order=False)
17491749
self.assertEqual(efi_part, '/dev/md/esp')
17501750

1751-
def test__prepare_boot_partitions_for_softraid_uefi_gpt_efi_provided(
1751+
def test_prepare_boot_partitions_for_softraid_uefi_gpt_efi_provided(
17521752
self, mock_execute, mock_dispatch):
17531753
mock_execute.side_effect = [
17541754
('451', None), # sgdisk -F
@@ -1766,7 +1766,7 @@ def test__prepare_boot_partitions_for_softraid_uefi_gpt_efi_provided(
17661766
(None, None), # wipefs
17671767
]
17681768

1769-
efi_part = image._prepare_boot_partitions_for_softraid(
1769+
efi_part = image.prepare_boot_partitions_for_softraid(
17701770
'/dev/md0', ['/dev/sda', '/dev/sdb'], '/dev/md0p15',
17711771
target_boot_mode='uefi')
17721772

@@ -1796,10 +1796,10 @@ def test__prepare_boot_partitions_for_softraid_uefi_gpt_efi_provided(
17961796

17971797
@mock.patch.object(disk_utils, 'get_partition_table_type', autospec=True,
17981798
return_value='msdos')
1799-
def test__prepare_boot_partitions_for_softraid_bios_msdos(
1799+
def test_prepare_boot_partitions_for_softraid_bios_msdos(
18001800
self, mock_label_scan, mock_execute, mock_dispatch):
18011801

1802-
efi_part = image._prepare_boot_partitions_for_softraid(
1802+
efi_part = image.prepare_boot_partitions_for_softraid(
18031803
'/dev/md0', ['/dev/sda', '/dev/sdb'], 'notusedanyway',
18041804
target_boot_mode='bios')
18051805

@@ -1812,7 +1812,7 @@ def test__prepare_boot_partitions_for_softraid_bios_msdos(
18121812

18131813
@mock.patch.object(disk_utils, 'get_partition_table_type', autospec=True,
18141814
return_value='gpt')
1815-
def test__prepare_boot_partitions_for_softraid_bios_gpt(
1815+
def test_prepare_boot_partitions_for_softraid_bios_gpt(
18161816
self, mock_label_scan, mock_execute, mock_dispatch):
18171817

18181818
mock_execute.side_effect = [
@@ -1822,7 +1822,7 @@ def test__prepare_boot_partitions_for_softraid_bios_gpt(
18221822
(None, None), # bios boot grub
18231823
]
18241824

1825-
efi_part = image._prepare_boot_partitions_for_softraid(
1825+
efi_part = image.prepare_boot_partitions_for_softraid(
18261826
'/dev/md0', ['/dev/sda', '/dev/sdb'], 'notusedanyway',
18271827
target_boot_mode='bios')
18281828

@@ -1854,7 +1854,7 @@ def test__prepare_boot_partitions_for_softraid_bios_gpt(
18541854
@mock.patch.object(os, 'environ', autospec=True)
18551855
@mock.patch.object(os, 'makedirs', autospec=True)
18561856
@mock.patch.object(partition_utils, 'get_partition', autospec=True)
1857-
@mock.patch.object(image, '_prepare_boot_partitions_for_softraid',
1857+
@mock.patch.object(image, 'prepare_boot_partitions_for_softraid',
18581858
autospec=True,
18591859
return_value='/dev/md/esp')
18601860
@mock.patch.object(image, '_has_dracut',
@@ -1972,7 +1972,7 @@ def test__install_grub2_softraid_uefi_gpt(
19721972
@mock.patch.object(os, 'environ', autospec=True)
19731973
@mock.patch.object(os, 'makedirs', autospec=True)
19741974
@mock.patch.object(partition_utils, 'get_partition', autospec=True)
1975-
@mock.patch.object(image, '_prepare_boot_partitions_for_softraid',
1975+
@mock.patch.object(image, 'prepare_boot_partitions_for_softraid',
19761976
autospec=True,
19771977
return_value=[])
19781978
@mock.patch.object(image, '_has_dracut',

0 commit comments

Comments
 (0)