Allow passing rootfs_uuid for the standalone case

Using software RAID with whole disk images requires specifying
a root partition UUID, but it is only possible through Glance.
This change adds an explicit field for that.

Change-Id: I55e3727aab3960ef472ec2db1f23c25db405e801
This commit is contained in:
Dmitry Tantsur 2020-10-20 18:22:25 +02:00
parent 3ba3f4d399
commit 0a68622187
4 changed files with 82 additions and 21 deletions

View File

@ -300,6 +300,13 @@ Populating instance_info
* ``ilo_boot_iso``, ``image_source``, ``root_gb`` under ``instance_info``. * ``ilo_boot_iso``, ``image_source``, ``root_gb`` under ``instance_info``.
#. For software RAID with whole-disk images, the root UUID of the root
partition has to be provided so that the bootloader can be correctly
installed::
baremetal node set $NODE_UUID \
--instance-info image_rootfs_uuid=<uuid>
Deployment Deployment
---------- ----------

View File

@ -1333,26 +1333,29 @@ class AgentDeployMixin(HeartbeatMixin, AgentOobStepsMixin):
# image's metadata (via Glance). Fall back to the driver internal # image's metadata (via Glance). Fall back to the driver internal
# info in case it is not available (e.g. not set or there's no Glance). # info in case it is not available (e.g. not set or there's no Glance).
if software_raid: if software_raid:
image_source = node.instance_info.get('image_source') root_uuid = node.instance_info.get('image_rootfs_uuid')
try: if not root_uuid:
context = task.context image_source = node.instance_info.get('image_source')
context.is_admin = True try:
glance = image_service.GlanceImageService( context = task.context
context=context) context.is_admin = True
image_info = glance.show(image_source) glance = image_service.GlanceImageService(
image_properties = image_info.get('properties') context=context)
root_uuid = image_properties['rootfs_uuid'] image_info = glance.show(image_source)
LOG.debug('Got rootfs_uuid from Glance: %s ' image_properties = image_info.get('properties')
'(node %s)', root_uuid, node.uuid) root_uuid = image_properties['rootfs_uuid']
except Exception as e: LOG.debug('Got rootfs_uuid from Glance: %s '
LOG.warning('Could not get \'rootfs_uuid\' property for ' '(node %s)', root_uuid, node.uuid)
'image %(image)s from Glance for node %(node)s. ' except Exception as e:
'%(cls)s: %(error)s.', LOG.warning(
{'image': image_source, 'node': node.uuid, 'Could not get \'rootfs_uuid\' property for '
'cls': e.__class__.__name__, 'error': e}) 'image %(image)s from Glance for node %(node)s. '
root_uuid = internal_info.get('root_uuid_or_disk_id') '%(cls)s: %(error)s.',
LOG.debug('Got rootfs_uuid from driver internal info: ' {'image': image_source, 'node': node.uuid,
'%s (node %s)', root_uuid, node.uuid) 'cls': e.__class__.__name__, 'error': e})
root_uuid = internal_info.get('root_uuid_or_disk_id')
LOG.debug('Got rootfs_uuid from driver internal info: '
'%s (node %s)', root_uuid, node.uuid)
# For whole disk images it is not necessary that the root_uuid # For whole disk images it is not necessary that the root_uuid
# be provided since the bootloaders on the disk will be used # be provided since the bootloaders on the disk will be used

View File

@ -1317,6 +1317,8 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
def test_configure_local_boot_on_software_raid( def test_configure_local_boot_on_software_raid(
self, install_bootloader_mock, try_set_boot_device_mock, self, install_bootloader_mock, try_set_boot_device_mock,
GlanceImageService_mock): GlanceImageService_mock):
image = GlanceImageService_mock.return_value.show.return_value
image.get.return_value = {'rootfs_uuid': 'rootfs'}
with task_manager.acquire(self.context, self.node['uuid'], with task_manager.acquire(self.context, self.node['uuid'],
shared=False) as task: shared=False) as task:
task.node.driver_internal_info['is_whole_disk_image'] = True task.node.driver_internal_info['is_whole_disk_image'] = True
@ -1336,7 +1338,50 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
} }
self.deploy.configure_local_boot(task) self.deploy.configure_local_boot(task)
self.assertTrue(GlanceImageService_mock.called) self.assertTrue(GlanceImageService_mock.called)
self.assertTrue(install_bootloader_mock.called) install_bootloader_mock.assert_called_once_with(
mock.ANY, task.node,
root_uuid='rootfs',
efi_system_part_uuid=None,
prep_boot_part_uuid=None,
target_boot_mode='bios',
software_raid=True)
try_set_boot_device_mock.assert_called_once_with(
task, boot_devices.DISK, persistent=True)
@mock.patch.object(image_service, 'GlanceImageService', autospec=True)
@mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True)
@mock.patch.object(agent_client.AgentClient, 'install_bootloader',
autospec=True)
def test_configure_local_boot_on_software_raid_explicit_uuid(
self, install_bootloader_mock, try_set_boot_device_mock,
GlanceImageService_mock):
with task_manager.acquire(self.context, self.node['uuid'],
shared=False) as task:
task.node.driver_internal_info['is_whole_disk_image'] = True
task.node.instance_info['image_rootfs_uuid'] = 'rootfs'
task.node.target_raid_config = {
"logical_disks": [
{
"size_gb": 100,
"raid_level": "1",
"controller": "software",
},
{
"size_gb": 'MAX',
"raid_level": "0",
"controller": "software",
}
]
}
self.deploy.configure_local_boot(task)
self.assertFalse(GlanceImageService_mock.called)
install_bootloader_mock.assert_called_once_with(
mock.ANY, task.node,
root_uuid='rootfs',
efi_system_part_uuid=None,
prep_boot_part_uuid=None,
target_boot_mode='bios',
software_raid=True)
try_set_boot_device_mock.assert_called_once_with( try_set_boot_device_mock.assert_called_once_with(
task, boot_devices.DISK, persistent=True) task, boot_devices.DISK, persistent=True)

View File

@ -0,0 +1,6 @@
---
features:
- |
When deploying a node with software RAID with an image not from Glance,
the new ``instance_info`` field ``image_rootfs_uuid`` can be used to
specify the UUID of the root partition to install the bootloader on.