Merge "Use a pre-defined partition UUID to detect configdrive on GPT" into stable/xena
This commit is contained in:
commit
3aa5928aa4
|
@ -33,8 +33,11 @@ from oslo_config import cfg
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from oslo_utils import excutils
|
from oslo_utils import excutils
|
||||||
from oslo_utils import units
|
from oslo_utils import units
|
||||||
|
from oslo_utils import uuidutils
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from ironic_python_agent import errors
|
||||||
|
from ironic_python_agent.extensions import image
|
||||||
from ironic_python_agent import utils as ipa_utils
|
from ironic_python_agent import utils as ipa_utils
|
||||||
|
|
||||||
|
|
||||||
|
@ -373,14 +376,18 @@ def create_config_drive_partition(node_uuid, device, configdrive):
|
||||||
"%(part)s",
|
"%(part)s",
|
||||||
{'node': node_uuid, 'part': config_drive_part})
|
{'node': node_uuid, 'part': config_drive_part})
|
||||||
else:
|
else:
|
||||||
cur_parts = set(part['number']
|
part_uuid = None
|
||||||
for part in disk_utils.list_partitions(device))
|
|
||||||
|
|
||||||
if disk_utils.get_partition_table_type(device) == 'gpt':
|
if disk_utils.get_partition_table_type(device) == 'gpt':
|
||||||
|
part_uuid = uuidutils.generate_uuid()
|
||||||
create_option = '0:-%dMB:0' % MAX_CONFIG_DRIVE_SIZE_MB
|
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)
|
run_as_root=True)
|
||||||
else:
|
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
|
# Check if the disk has 4 partitions. The MBR based disk
|
||||||
# cannot have more than 4 partitions.
|
# cannot have more than 4 partitions.
|
||||||
# TODO(stendulker): One can use logical partitions to create
|
# TODO(stendulker): One can use logical partitions to create
|
||||||
|
@ -422,17 +429,29 @@ def create_config_drive_partition(node_uuid, device, configdrive):
|
||||||
# Trigger device rescan
|
# Trigger device rescan
|
||||||
disk_utils.trigger_device_rescan(device)
|
disk_utils.trigger_device_rescan(device)
|
||||||
|
|
||||||
upd_parts = set(part['number']
|
if part_uuid is None:
|
||||||
for part in disk_utils.list_partitions(device))
|
new_parts = {part['number']: part
|
||||||
new_part = set(upd_parts) - set(cur_parts)
|
for part in disk_utils.list_partitions(device)}
|
||||||
if len(new_part) != 1:
|
new_part = set(new_parts) - set(cur_parts)
|
||||||
raise exception.InstanceDeployFailure(
|
if len(new_part) != 1:
|
||||||
'Disk partitioning failed on device %(device)s. '
|
raise exception.InstanceDeployFailure(
|
||||||
'Unable to retrieve config drive partition information.'
|
'Disk partitioning failed on device %(device)s. '
|
||||||
% {'device': device})
|
'Unable to retrieve config drive partition '
|
||||||
|
'information.' % {'device': device})
|
||||||
|
|
||||||
config_drive_part = disk_utils.partition_index_to_path(
|
config_drive_part = disk_utils.partition_index_to_path(
|
||||||
device, new_part.pop())
|
device, new_part.pop())
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
config_drive_part = image._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()
|
disk_utils.udev_settle()
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ from ironic_lib import utils
|
||||||
from oslo_concurrency import processutils
|
from oslo_concurrency import processutils
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from ironic_python_agent.extensions import image
|
||||||
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
|
||||||
|
|
||||||
|
@ -654,6 +655,7 @@ class CreateConfigDriveTestCases(base.IronicAgentTest):
|
||||||
mock_dd.assert_called_with(configdrive_file, configdrive_part)
|
mock_dd.assert_called_with(configdrive_file, configdrive_part)
|
||||||
mock_unlink.assert_called_with(configdrive_file)
|
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, 'execute', autospec=True)
|
||||||
@mock.patch.object(utils, 'unlink_without_raise',
|
@mock.patch.object(utils, 'unlink_without_raise',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
|
@ -663,50 +665,32 @@ class CreateConfigDriveTestCases(base.IronicAgentTest):
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@mock.patch.object(disk_utils, 'get_partition_table_type',
|
@mock.patch.object(disk_utils, 'get_partition_table_type',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@mock.patch.object(disk_utils, 'list_partitions',
|
@mock.patch.object(image, '_get_partition', autospec=True)
|
||||||
autospec=True)
|
|
||||||
@mock.patch.object(partition_utils, 'get_labelled_partition',
|
@mock.patch.object(partition_utils, 'get_labelled_partition',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@mock.patch.object(partition_utils, 'get_configdrive',
|
@mock.patch.object(partition_utils, 'get_configdrive',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
def test_create_partition_gpt(self, mock_get_configdrive,
|
def test_create_partition_gpt(self, mock_get_configdrive,
|
||||||
mock_get_labelled_partition,
|
mock_get_labelled_partition,
|
||||||
mock_list_partitions, mock_table_type,
|
mock_get_partition_by_uuid,
|
||||||
|
mock_table_type,
|
||||||
mock_fix_gpt_partition,
|
mock_fix_gpt_partition,
|
||||||
mock_dd, mock_unlink, mock_execute):
|
mock_dd, mock_unlink, mock_execute):
|
||||||
config_url = 'http://1.2.3.4/cd'
|
config_url = 'http://1.2.3.4/cd'
|
||||||
configdrive_file = '/tmp/xyz'
|
configdrive_file = '/tmp/xyz'
|
||||||
configdrive_mb = 10
|
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_configdrive.return_value = (configdrive_mb, configdrive_file)
|
||||||
mock_get_labelled_partition.return_value = None
|
mock_get_labelled_partition.return_value = None
|
||||||
|
|
||||||
mock_table_type.return_value = 'gpt'
|
mock_table_type.return_value = 'gpt'
|
||||||
mock_list_partitions.side_effect = [initial_partitions,
|
|
||||||
updated_partitions]
|
|
||||||
expected_part = '/dev/fake4'
|
expected_part = '/dev/fake4'
|
||||||
|
mock_get_partition_by_uuid.return_value = expected_part
|
||||||
partition_utils.create_config_drive_partition(self.node_uuid, self.dev,
|
partition_utils.create_config_drive_partition(self.node_uuid, self.dev,
|
||||||
config_url)
|
config_url)
|
||||||
mock_execute.assert_has_calls([
|
mock_execute.assert_has_calls([
|
||||||
mock.call('sgdisk', '-n', '0:-64MB:0', self.dev,
|
mock.call('sgdisk', '-n', '0:-64MB:0', '-u', '0:fake-uuid',
|
||||||
run_as_root=True),
|
self.dev, run_as_root=True),
|
||||||
mock.call('sync'),
|
mock.call('sync'),
|
||||||
mock.call('udevadm', 'settle'),
|
mock.call('udevadm', 'settle'),
|
||||||
mock.call('partprobe', self.dev, attempts=10, run_as_root=True),
|
mock.call('partprobe', self.dev, attempts=10, run_as_root=True),
|
||||||
|
@ -717,7 +701,6 @@ class CreateConfigDriveTestCases(base.IronicAgentTest):
|
||||||
delay_on_retry=True)
|
delay_on_retry=True)
|
||||||
])
|
])
|
||||||
|
|
||||||
self.assertEqual(2, mock_list_partitions.call_count)
|
|
||||||
mock_table_type.assert_called_with(self.dev)
|
mock_table_type.assert_called_with(self.dev)
|
||||||
mock_fix_gpt_partition.assert_called_with(self.dev, self.node_uuid)
|
mock_fix_gpt_partition.assert_called_with(self.dev, self.node_uuid)
|
||||||
mock_dd.assert_called_with(configdrive_file, expected_part)
|
mock_dd.assert_called_with(configdrive_file, expected_part)
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue