Move prepare_boot_partitions_for_softraid to raid_utils

prepare_boot_partitions_for_softraid() is used in BIOS and UEFI
modes to prepare the partitions for the bootloader. Move it from
the image extensions to raid_utils to reflect this and avoid the
import of an extension to efi_utils.

Follow-up to 62c5674a600baeeef0af3b12baeab486870eb103.

Change-Id: I9f5974fbbfea5e8cdfbb7e49bea375e5cbfdd145
This commit is contained in:
Arne Wiebalck 2022-01-28 08:38:28 +01:00
parent b8b1991bea
commit a83f38479e
6 changed files with 343 additions and 332 deletions

View File

@ -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)

View File

@ -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
)

View File

@ -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.

View File

@ -1654,212 +1654,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 +1663,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',
@ -1987,7 +1781,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',

View File

@ -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)

View File

@ -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):