Use a pre-defined partition UUID to detect configdrive on GPT

Using partition numbers is currently broken for devicemapper devices.
Fortunately, GPT has partition UUIDs, so we can just generate one and
use it for lookup.

Change-Id: I41ffe4f8e4c6e43182090b5aa2a2b4b34f32efd5
This commit is contained in:
Dmitry Tantsur 2022-04-29 15:01:02 +02:00
parent 8111475eb0
commit 65c4de903a
3 changed files with 55 additions and 38 deletions

View File

@ -35,6 +35,7 @@ from oslo_config import cfg
from oslo_log import log
from oslo_utils import excutils
from oslo_utils import units
from oslo_utils import uuidutils
import requests
from ironic_python_agent import errors
@ -377,14 +378,18 @@ def create_config_drive_partition(node_uuid, device, configdrive):
"%(part)s",
{'node': node_uuid, 'part': config_drive_part})
else:
cur_parts = set(part['number']
for part in disk_utils.list_partitions(device))
part_uuid = None
if disk_utils.get_partition_table_type(device) == 'gpt':
part_uuid = uuidutils.generate_uuid()
create_option = '0:-%dMB:0' % MAX_CONFIG_DRIVE_SIZE_MB
utils.execute('sgdisk', '-n', create_option, device,
uuid_option = '0:%s' % part_uuid
utils.execute('sgdisk', '-n', create_option,
'-u', uuid_option, device,
run_as_root=True)
else:
cur_parts = set(part['number']
for part in disk_utils.list_partitions(device))
# Check if the disk has 4 partitions. The MBR based disk
# cannot have more than 4 partitions.
# TODO(stendulker): One can use logical partitions to create
@ -426,17 +431,29 @@ def create_config_drive_partition(node_uuid, device, configdrive):
# Trigger device rescan
disk_utils.trigger_device_rescan(device)
upd_parts = set(part['number']
for part in disk_utils.list_partitions(device))
new_part = set(upd_parts) - set(cur_parts)
if len(new_part) != 1:
raise exception.InstanceDeployFailure(
'Disk partitioning failed on device %(device)s. '
'Unable to retrieve config drive partition information.'
% {'device': device})
if part_uuid is None:
new_parts = {part['number']: part
for part in disk_utils.list_partitions(device)}
new_part = set(new_parts) - set(cur_parts)
if len(new_part) != 1:
raise exception.InstanceDeployFailure(
'Disk partitioning failed on device %(device)s. '
'Unable to retrieve config drive partition '
'information.' % {'device': device})
config_drive_part = disk_utils.partition_index_to_path(
device, new_part.pop())
config_drive_part = disk_utils.partition_index_to_path(
device, new_part.pop())
else:
try:
config_drive_part = get_partition(device, part_uuid)
except errors.DeviceNotFound:
msg = ('Failed to create config drive on disk %(disk)s '
'for node %(node)s. Partition with UUID %(uuid)s '
'has not been found after creation.') % {
'disk': device, 'node': node_uuid,
'uuid': part_uuid}
LOG.error(msg)
raise exception.InstanceDeployFailure(msg)
disk_utils.udev_settle()

View File

@ -656,6 +656,7 @@ class CreateConfigDriveTestCases(base.IronicAgentTest):
mock_dd.assert_called_with(configdrive_file, configdrive_part)
mock_unlink.assert_called_with(configdrive_file)
@mock.patch('oslo_utils.uuidutils.generate_uuid', lambda: 'fake-uuid')
@mock.patch.object(utils, 'execute', autospec=True)
@mock.patch.object(utils, 'unlink_without_raise',
autospec=True)
@ -665,7 +666,7 @@ class CreateConfigDriveTestCases(base.IronicAgentTest):
autospec=True)
@mock.patch.object(disk_utils, 'get_partition_table_type',
autospec=True)
@mock.patch.object(disk_utils, 'list_partitions',
@mock.patch.object(partition_utils, 'get_partition',
autospec=True)
@mock.patch.object(partition_utils, 'get_labelled_partition',
autospec=True)
@ -673,42 +674,25 @@ class CreateConfigDriveTestCases(base.IronicAgentTest):
autospec=True)
def test_create_partition_gpt(self, mock_get_configdrive,
mock_get_labelled_partition,
mock_list_partitions, mock_table_type,
mock_get_partition_by_uuid,
mock_table_type,
mock_fix_gpt_partition,
mock_dd, mock_unlink, mock_execute):
config_url = 'http://1.2.3.4/cd'
configdrive_file = '/tmp/xyz'
configdrive_mb = 10
initial_partitions = [{'end': 49152, 'number': 1, 'start': 1,
'flags': 'boot', 'filesystem': 'ext4',
'size': 49151},
{'end': 51099, 'number': 3, 'start': 49153,
'flags': '', 'filesystem': '', 'size': 2046},
{'end': 51099, 'number': 5, 'start': 49153,
'flags': '', 'filesystem': '', 'size': 2046}]
updated_partitions = [{'end': 49152, 'number': 1, 'start': 1,
'flags': 'boot', 'filesystem': 'ext4',
'size': 49151},
{'end': 51099, 'number': 3, 'start': 49153,
'flags': '', 'filesystem': '', 'size': 2046},
{'end': 51099, 'number': 4, 'start': 49153,
'flags': '', 'filesystem': '', 'size': 2046},
{'end': 51099, 'number': 5, 'start': 49153,
'flags': '', 'filesystem': '', 'size': 2046}]
mock_get_configdrive.return_value = (configdrive_mb, configdrive_file)
mock_get_labelled_partition.return_value = None
mock_table_type.return_value = 'gpt'
mock_list_partitions.side_effect = [initial_partitions,
updated_partitions]
expected_part = '/dev/fake4'
mock_get_partition_by_uuid.return_value = expected_part
partition_utils.create_config_drive_partition(self.node_uuid, self.dev,
config_url)
mock_execute.assert_has_calls([
mock.call('sgdisk', '-n', '0:-64MB:0', self.dev,
run_as_root=True),
mock.call('sgdisk', '-n', '0:-64MB:0', '-u', '0:fake-uuid',
self.dev, run_as_root=True),
mock.call('sync'),
mock.call('udevadm', 'settle'),
mock.call('partprobe', self.dev, attempts=10, run_as_root=True),
@ -719,7 +703,6 @@ class CreateConfigDriveTestCases(base.IronicAgentTest):
delay_on_retry=True)
])
self.assertEqual(2, mock_list_partitions.call_count)
mock_table_type.assert_called_with(self.dev)
mock_fix_gpt_partition.assert_called_with(self.dev, self.node_uuid)
mock_dd.assert_called_with(configdrive_file, expected_part)

View File

@ -0,0 +1,17 @@
---
issues:
- |
Creating a configdrive partition on a devicemapper device (e.g. a multipath
storage device) with MBR partitioning may fail with the following error::
Command execution failed: Failed to create config drive on disk /dev/dm-0
for node 168af30d-0fad-4d67-af99-b28b3238e977. Error: Unexpected error
while running command.
Use GPT partitioning instead.
fixes:
- |
Fixes creating a configdrive partition on a devicemapper device (e.g.
a multipath storage device) with GPT partitioning. The newly created
partition is now detected by a pre-generated UUID rather than by comparing
partition numbers.