Skip to content

Commit aeed2df

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) In addition, use canonical device names for the RAID device for the ESP: it seems like tinyIPA silently replaces /dev/md/esp with /dev/md127. Find the next free /dev/md device and use it instead. Also rescan the resulting device before copying files. Co-authored-by: Dmitry Tantsur <[email protected]> Story: #2009794 Change-Id: I7ed34e595215194a589c2f1cd0b39ff0336da8f1 (cherry picked from commit 62c5674)
1 parent c7295e2 commit aeed2df

File tree

7 files changed

+243
-64
lines changed

7 files changed

+243
-64
lines changed

ironic_python_agent/extensions/image.py

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import stat
2121
import tempfile
2222

23+
from ironic_lib import disk_utils
2324
from ironic_lib import utils as ilib_utils
2425
from oslo_concurrency import processutils
2526
from oslo_config import cfg
@@ -261,7 +262,7 @@ def _get_efi_bootloaders(location):
261262

262263

263264
def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
264-
mount_point):
265+
mount_point, label_suffix=None):
265266
"""Executes efibootmgr and removes duplicate entries.
266267
267268
:param valid_efi_bootloaders: the list of valid efi bootloaders
@@ -270,6 +271,9 @@ def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
270271
:param mount_point: The mountpoint for the EFI partition so we can
271272
read contents of files if necessary to perform
272273
proper bootloader injection operations.
274+
:param label_suffix: a string to be appended to the EFI label,
275+
mainly used in the case of software to uniqify
276+
the entries for the md components.
273277
"""
274278

275279
# Before updating let's get information about the bootorder
@@ -292,9 +296,13 @@ def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
292296
v_efi_bl_path = v_bl.replace(csv_filename, str(csv_contents[0]))
293297
v_efi_bl_path = '\\' + v_efi_bl_path.replace('/', '\\')
294298
label = csv_contents[1]
299+
if label_suffix:
300+
label = label + " " + str(label_suffix)
295301
else:
296302
v_efi_bl_path = '\\' + v_bl.replace('/', '\\')
297303
label = 'ironic' + str(label_id)
304+
if label_suffix:
305+
label = label + " " + str(label_suffix)
298306

299307
# Iterate through standard out, and look for duplicates
300308
for line in original_efi_output[0].split('\n'):
@@ -307,9 +315,11 @@ def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
307315
LOG.debug("Found bootnum %s matching label", boot_num)
308316
utils.execute('efibootmgr', '-b', boot_num, '-B')
309317

310-
LOG.debug("Adding loader %(path)s on partition %(part)s of device "
311-
" %(dev)s", {'path': v_efi_bl_path, 'part': efi_partition,
312-
'dev': device})
318+
LOG.info("Adding loader %(path)s on partition %(part)s of device "
319+
" %(dev)s with label %(label)s",
320+
{'path': v_efi_bl_path, 'part': efi_partition,
321+
'dev': device, 'label': label})
322+
313323
# Update the nvram using efibootmgr
314324
# https://linux.die.net/man/8/efibootmgr
315325
utils.execute('efibootmgr', '-v', '-c', '-d', device,
@@ -380,16 +390,60 @@ def _manage_uefi(device, efi_system_part_uuid=None):
380390
efi_mounted = True
381391

382392
valid_efi_bootloaders = _get_efi_bootloaders(efi_partition_mount_point)
383-
if valid_efi_bootloaders:
384-
_run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
385-
efi_partition_mount_point)
386-
return True
387-
else:
393+
if not valid_efi_bootloaders:
388394
# NOTE(dtantsur): if we have an empty EFI partition, try to use
389395
# grub-install to populate it.
390396
LOG.warning('Empty EFI partition detected.')
391397
return False
392398

399+
if not hardware.is_md_device(device):
400+
efi_devices = [device]
401+
efi_partition_numbers = [efi_partition]
402+
efi_label_suffix = ''
403+
else:
404+
# umount to allow for signature removal (to avoid confusion about
405+
# which ESP to mount once the instance is deployed)
406+
utils.execute('umount', efi_partition_mount_point, attempts=3,
407+
delay_on_retry=True)
408+
efi_mounted = False
409+
410+
holders = hardware.get_holder_disks(device)
411+
efi_md_device = prepare_boot_partitions_for_softraid(
412+
device, holders, efi_device_part, target_boot_mode='uefi'
413+
)
414+
efi_devices = hardware.get_component_devices(efi_md_device)
415+
efi_partition_numbers = []
416+
_PARTITION_NUMBER = re.compile(r'(\d+)$')
417+
for dev in efi_devices:
418+
match = _PARTITION_NUMBER.search(dev)
419+
if match:
420+
partition_number = match.group(1)
421+
efi_partition_numbers.append(partition_number)
422+
else:
423+
raise errors.DeviceNotFound(
424+
"Could not extract the partition number "
425+
"from %s!" % dev)
426+
efi_label_suffix = "(RAID, part%s)"
427+
428+
# remount for _run_efibootmgr
429+
utils.execute('mount', efi_device_part, efi_partition_mount_point)
430+
efi_mounted = True
431+
432+
efi_dev_part = zip(efi_devices, efi_partition_numbers)
433+
for i, (efi_dev, efi_part) in enumerate(efi_dev_part):
434+
LOG.debug("Calling efibootmgr with dev %s part %s",
435+
efi_dev, efi_part)
436+
if efi_label_suffix:
437+
# NOTE (arne_wiebalck): uniqify the labels to prevent
438+
# unintentional boot entry cleanup
439+
_run_efibootmgr(valid_efi_bootloaders, efi_dev, efi_part,
440+
efi_partition_mount_point,
441+
efi_label_suffix % i)
442+
else:
443+
_run_efibootmgr(valid_efi_bootloaders, efi_dev, efi_part,
444+
efi_partition_mount_point)
445+
return True
446+
393447
except processutils.ProcessExecutionError as e:
394448
error_msg = ('Could not verify uefi on device %(dev)s'
395449
'failed with %(err)s.' % {'dev': device, 'err': e})
@@ -422,8 +476,8 @@ def _manage_uefi(device, efi_system_part_uuid=None):
422476

423477

424478
# TODO(rg): handle PreP boot parts relocation as well
425-
def _prepare_boot_partitions_for_softraid(device, holders, efi_part,
426-
target_boot_mode):
479+
def prepare_boot_partitions_for_softraid(device, holders, efi_part,
480+
target_boot_mode):
427481
"""Prepare boot partitions when relevant.
428482
429483
Create either a RAIDed EFI partition or bios boot partitions for software
@@ -492,15 +546,17 @@ def _prepare_boot_partitions_for_softraid(device, holders, efi_part,
492546
target_part, holder)
493547

494548
# RAID the ESPs, metadata=1.0 is mandatory to be able to boot
495-
md_device = '/dev/md/esp'
549+
md_device = raid_utils.get_next_free_raid_device()
496550
LOG.debug("Creating md device %(md_device)s for the ESPs "
497551
"on %(efi_partitions)s",
498552
{'md_device': md_device, 'efi_partitions': efi_partitions})
499553
utils.execute('mdadm', '--create', md_device, '--force',
500554
'--run', '--metadata=1.0', '--level', '1',
501-
'--raid-devices', len(efi_partitions),
555+
'--name', 'esp', '--raid-devices', len(efi_partitions),
502556
*efi_partitions)
503557

558+
disk_utils.trigger_device_rescan(md_device)
559+
504560
if efi_part:
505561
# Blockdev copy the source ESP and erase it
506562
LOG.debug("Relocating EFI %s to %s", efi_part, md_device)
@@ -627,7 +683,7 @@ def _install_grub2(device, root_uuid, efi_system_part_uuid=None,
627683
efi_partition = efi_part
628684
if hardware.is_md_device(device):
629685
holders = hardware.get_holder_disks(device)
630-
efi_partition = _prepare_boot_partitions_for_softraid(
686+
efi_partition = prepare_boot_partitions_for_softraid(
631687
device, holders, efi_part, target_boot_mode
632688
)
633689

@@ -1004,9 +1060,7 @@ def _efi_boot_setup(device, efi_system_part_uuid=None, target_boot_mode=None):
10041060
{'target': target_boot_mode,
10051061
'current': boot.current_boot_mode})
10061062

1007-
# FIXME(arne_wiebalck): make software RAID work with efibootmgr
1008-
if (boot.current_boot_mode == 'uefi'
1009-
and not hardware.is_md_device(device)):
1063+
if boot.current_boot_mode == 'uefi':
10101064
try:
10111065
utils.execute('efibootmgr', '--version')
10121066
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
utils.execute('mdadm', '--stop', raid_device)
330330
utils.execute('mdadm', '--assemble', raid_device,
331331
*component_devices)
@@ -2235,7 +2235,7 @@ def _scan_raids():
22352235
def _delete_config_pass(self, raid_devices):
22362236
all_holder_disks = []
22372237
for raid_device in raid_devices:
2238-
component_devices = _get_component_devices(raid_device.name)
2238+
component_devices = get_component_devices(raid_device.name)
22392239
if not component_devices:
22402240
# A "Software RAID device" without components is usually
22412241
# a partition on an md device (as, for instance, created

ironic_python_agent/raid_utils.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,17 @@ def create_raid_device(index, logical_disk):
248248
msg = "Failed re-add {} to {}: {}".format(
249249
dev, md_device, e)
250250
raise errors.SoftwareRAIDError(msg)
251+
252+
253+
def get_next_free_raid_device():
254+
"""Get a device name that is still free."""
255+
from ironic_python_agent import hardware
256+
257+
names = {dev.name for dev in
258+
hardware.dispatch_to_managers('list_block_devices')}
259+
for idx in range(128):
260+
name = f'/dev/md{idx}'
261+
if name not in names:
262+
return name
263+
264+
raise errors.SoftwareRAIDError("No free md (RAID) devices are left")

0 commit comments

Comments
 (0)