Software RAID: Pass the boot mode to the IPA

Pass the desired target boot mode to the IPA which is needed
to prepare the partitions and bootloader accordingly.

Change-Id: I4ca1bd781ae622535ced7b2d9ff23ff952d56acf
Story: #2006379
Task: #37636
Depends-On: https://review.opendev.org/#/c/696156/
This commit is contained in:
Raphael Glon 2019-11-26 19:40:26 +01:00 committed by Arne Wiebalck
parent 45d9390187
commit 65b5ac6a7a
5 changed files with 91 additions and 24 deletions

View File

@ -1013,6 +1013,9 @@ class AgentDeployMixin(HeartbeatMixin):
on encountering error while setting the boot device on the node. on encountering error while setting the boot device on the node.
""" """
node = task.node node = task.node
# Almost never taken into account on agent side, just used for softraid
# Can be useful with whole_disk_images
target_boot_mode = boot_mode_utils.get_boot_mode(task.node)
LOG.debug('Configuring local boot for node %s', node.uuid) LOG.debug('Configuring local boot for node %s', node.uuid)
# If the target RAID configuration is set to 'software' for the # If the target RAID configuration is set to 'software' for the
@ -1067,7 +1070,9 @@ class AgentDeployMixin(HeartbeatMixin):
result = self._client.install_bootloader( result = self._client.install_bootloader(
node, root_uuid=root_uuid, node, root_uuid=root_uuid,
efi_system_part_uuid=efi_system_part_uuid, efi_system_part_uuid=efi_system_part_uuid,
prep_boot_part_uuid=prep_boot_part_uuid) prep_boot_part_uuid=prep_boot_part_uuid,
target_boot_mode=target_boot_mode
)
if result['command_status'] == 'FAILED': if result['command_status'] == 'FAILED':
if not whole_disk_image: if not whole_disk_image:
msg = (_("Failed to install a bootloader when " msg = (_("Failed to install a bootloader when "

View File

@ -259,12 +259,14 @@ class AgentClient(object):
wait=True) wait=True)
@METRICS.timer('AgentClient.install_bootloader') @METRICS.timer('AgentClient.install_bootloader')
def install_bootloader(self, node, root_uuid, efi_system_part_uuid=None, def install_bootloader(self, node, root_uuid, target_boot_mode,
efi_system_part_uuid=None,
prep_boot_part_uuid=None): prep_boot_part_uuid=None):
"""Install a boot loader on the image. """Install a boot loader on the image.
:param node: A node object. :param node: A node object.
:param root_uuid: The UUID of the root partition. :param root_uuid: The UUID of the root partition.
:param target_boot_mode: The target deployment boot mode.
:param efi_system_part_uuid: The UUID of the efi system partition :param efi_system_part_uuid: The UUID of the efi system partition
where the bootloader will be installed to, only used for uefi where the bootloader will be installed to, only used for uefi
boot mode. boot mode.
@ -279,7 +281,9 @@ class AgentClient(object):
""" """
params = {'root_uuid': root_uuid, params = {'root_uuid': root_uuid,
'efi_system_part_uuid': efi_system_part_uuid, 'efi_system_part_uuid': efi_system_part_uuid,
'prep_boot_part_uuid': prep_boot_part_uuid} 'prep_boot_part_uuid': prep_boot_part_uuid,
'target_boot_mode': target_boot_mode
}
# NOTE(TheJulia): This command explicitly sends a larger timeout # NOTE(TheJulia): This command explicitly sends a larger timeout
# factor to the _command call such that the agent ramdisk has enough # factor to the _command call such that the agent ramdisk has enough
@ -289,11 +293,34 @@ class AgentClient(object):
# compatible. We could at least begin to delineate the commands apart # compatible. We could at least begin to delineate the commands apart
# over the next cycle or two so we don't need a command timeout # over the next cycle or two so we don't need a command timeout
# extension factor. # extension factor.
return self._command(node=node, try:
method='image.install_bootloader', return self._command(node=node,
params=params, method='image.install_bootloader',
wait=True, params=params,
command_timeout_factor=2) wait=True,
command_timeout_factor=2)
except exception.AgentAPIError:
# NOTE(arne_wiebalck): If we require to pass 'uefi' as the boot
# mode, but find that the IPA does not yet support the additional
# 'target_boot_mode' parameter, we need to fail. For 'bios' boot
# mode on the other hand we can retry without the parameter,
# since 'bios' is the default value the IPA will use.
if target_boot_mode == 'uefi':
LOG.error('Unable to pass UEFI boot mode to an out of date '
'agent ramdisk. Please contact the administrator '
'to update the ramdisk to contain an '
'ironic-python-agent version of at least 6.0.0.')
raise
else:
params = {'root_uuid': root_uuid,
'efi_system_part_uuid': efi_system_part_uuid,
'prep_boot_part_uuid': prep_boot_part_uuid
}
return self._command(node=node,
method='image.install_bootloader',
params=params,
wait=True,
command_timeout_factor=2)
@METRICS.timer('AgentClient.get_clean_steps') @METRICS.timer('AgentClient.get_clean_steps')
def get_clean_steps(self, node, ports): def get_clean_steps(self, node, ports):

View File

@ -1082,7 +1082,10 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
@mock.patch.object(agent_client.AgentClient, 'install_bootloader', @mock.patch.object(agent_client.AgentClient, 'install_bootloader',
autospec=True) autospec=True)
@mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True) @mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True)
def test_configure_local_boot(self, try_set_boot_device_mock, @mock.patch.object(boot_mode_utils, 'get_boot_mode', autospec=True,
return_value='whatever')
def test_configure_local_boot(self, boot_mode_mock,
try_set_boot_device_mock,
install_bootloader_mock): install_bootloader_mock):
install_bootloader_mock.return_value = { install_bootloader_mock.return_value = {
'command_status': 'SUCCESS', 'command_error': None} 'command_status': 'SUCCESS', 'command_error': None}
@ -1092,17 +1095,24 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
self.deploy.configure_local_boot(task, root_uuid='some-root-uuid') self.deploy.configure_local_boot(task, root_uuid='some-root-uuid')
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)
boot_mode_mock.assert_called_once_with(task.node)
install_bootloader_mock.assert_called_once_with( install_bootloader_mock.assert_called_once_with(
mock.ANY, task.node, root_uuid='some-root-uuid', mock.ANY, task.node, root_uuid='some-root-uuid',
efi_system_part_uuid=None, prep_boot_part_uuid=None) efi_system_part_uuid=None, prep_boot_part_uuid=None,
target_boot_mode='whatever'
)
@mock.patch.object(agent_client.AgentClient, 'install_bootloader', @mock.patch.object(agent_client.AgentClient, 'install_bootloader',
autospec=True) autospec=True)
@mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True) @mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True)
def test_configure_local_boot_with_prep(self, try_set_boot_device_mock, @mock.patch.object(boot_mode_utils, 'get_boot_mode', autospec=True,
return_value='whatever')
def test_configure_local_boot_with_prep(self, boot_mode_mock,
try_set_boot_device_mock,
install_bootloader_mock): install_bootloader_mock):
install_bootloader_mock.return_value = { install_bootloader_mock.return_value = {
'command_status': 'SUCCESS', 'command_error': None} 'command_status': 'SUCCESS', 'command_error': None}
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'] = False task.node.driver_internal_info['is_whole_disk_image'] = False
@ -1110,14 +1120,20 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
prep_boot_part_uuid='fake-prep') prep_boot_part_uuid='fake-prep')
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)
boot_mode_mock.assert_called_once_with(task.node)
install_bootloader_mock.assert_called_once_with( install_bootloader_mock.assert_called_once_with(
mock.ANY, task.node, root_uuid='some-root-uuid', mock.ANY, task.node, root_uuid='some-root-uuid',
efi_system_part_uuid=None, prep_boot_part_uuid='fake-prep') efi_system_part_uuid=None, prep_boot_part_uuid='fake-prep',
target_boot_mode='whatever'
)
@mock.patch.object(agent_client.AgentClient, 'install_bootloader', @mock.patch.object(agent_client.AgentClient, 'install_bootloader',
autospec=True) autospec=True)
@mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True) @mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True)
def test_configure_local_boot_uefi(self, try_set_boot_device_mock, @mock.patch.object(boot_mode_utils, 'get_boot_mode', autospec=True,
return_value='uefi')
def test_configure_local_boot_uefi(self, boot_mode_mock,
try_set_boot_device_mock,
install_bootloader_mock): install_bootloader_mock):
install_bootloader_mock.return_value = { install_bootloader_mock.return_value = {
'command_status': 'SUCCESS', 'command_error': None} 'command_status': 'SUCCESS', 'command_error': None}
@ -1129,10 +1145,13 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
efi_system_part_uuid='efi-system-part-uuid') efi_system_part_uuid='efi-system-part-uuid')
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)
boot_mode_mock.assert_called_once_with(task.node)
install_bootloader_mock.assert_called_once_with( install_bootloader_mock.assert_called_once_with(
mock.ANY, task.node, root_uuid='some-root-uuid', mock.ANY, task.node, root_uuid='some-root-uuid',
efi_system_part_uuid='efi-system-part-uuid', efi_system_part_uuid='efi-system-part-uuid',
prep_boot_part_uuid=None) prep_boot_part_uuid=None,
target_boot_mode='uefi'
)
@mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True) @mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True)
@mock.patch.object(agent_client.AgentClient, 'install_bootloader', @mock.patch.object(agent_client.AgentClient, 'install_bootloader',
@ -1178,7 +1197,7 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
install_bootloader_mock.assert_called_once_with( install_bootloader_mock.assert_called_once_with(
mock.ANY, task.node, root_uuid=None, mock.ANY, task.node, root_uuid=None,
efi_system_part_uuid='efi-system-part-uuid', efi_system_part_uuid='efi-system-part-uuid',
prep_boot_part_uuid=None) prep_boot_part_uuid=None, target_boot_mode='uefi')
@mock.patch.object(image_service, 'GlanceImageService', autospec=True) @mock.patch.object(image_service, 'GlanceImageService', autospec=True)
@mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True) @mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True)
@ -1242,7 +1261,8 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
# check if the root_uuid comes from the driver_internal_info # check if the root_uuid comes from the driver_internal_info
install_bootloader_mock.assert_called_once_with( install_bootloader_mock.assert_called_once_with(
mock.ANY, task.node, root_uuid=root_uuid, mock.ANY, task.node, root_uuid=root_uuid,
efi_system_part_uuid=None, prep_boot_part_uuid=None) efi_system_part_uuid=None, prep_boot_part_uuid=None,
target_boot_mode='bios')
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)
@ -1324,8 +1344,11 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
autospec=True) autospec=True)
@mock.patch.object(agent_client.AgentClient, 'install_bootloader', @mock.patch.object(agent_client.AgentClient, 'install_bootloader',
autospec=True) autospec=True)
@mock.patch.object(boot_mode_utils, 'get_boot_mode', autospec=True,
return_value='whatever')
def test_configure_local_boot_boot_loader_install_fail( def test_configure_local_boot_boot_loader_install_fail(
self, install_bootloader_mock, collect_logs_mock): self, boot_mode_mock, install_bootloader_mock,
collect_logs_mock):
install_bootloader_mock.return_value = { install_bootloader_mock.return_value = {
'command_status': 'FAILED', 'command_error': 'boom'} 'command_status': 'FAILED', 'command_error': 'boom'}
self.node.provision_state = states.DEPLOYING self.node.provision_state = states.DEPLOYING
@ -1337,9 +1360,12 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
self.assertRaises(exception.InstanceDeployFailure, self.assertRaises(exception.InstanceDeployFailure,
self.deploy.configure_local_boot, self.deploy.configure_local_boot,
task, root_uuid='some-root-uuid') task, root_uuid='some-root-uuid')
boot_mode_mock.assert_called_once_with(task.node)
install_bootloader_mock.assert_called_once_with( install_bootloader_mock.assert_called_once_with(
mock.ANY, task.node, root_uuid='some-root-uuid', mock.ANY, task.node, root_uuid='some-root-uuid',
efi_system_part_uuid=None, prep_boot_part_uuid=None) efi_system_part_uuid=None, prep_boot_part_uuid=None,
target_boot_mode='whatever'
)
collect_logs_mock.assert_called_once_with(mock.ANY, task.node) collect_logs_mock.assert_called_once_with(mock.ANY, task.node)
self.assertEqual(states.DEPLOYFAIL, task.node.provision_state) self.assertEqual(states.DEPLOYFAIL, task.node.provision_state)
self.assertEqual(states.ACTIVE, task.node.target_provision_state) self.assertEqual(states.ACTIVE, task.node.target_provision_state)
@ -1349,9 +1375,11 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
@mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True) @mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True)
@mock.patch.object(agent_client.AgentClient, 'install_bootloader', @mock.patch.object(agent_client.AgentClient, 'install_bootloader',
autospec=True) autospec=True)
@mock.patch.object(boot_mode_utils, 'get_boot_mode', autospec=True,
return_value='whatever')
def test_configure_local_boot_set_boot_device_fail( def test_configure_local_boot_set_boot_device_fail(
self, install_bootloader_mock, try_set_boot_device_mock, self, boot_mode_mock, install_bootloader_mock,
collect_logs_mock): try_set_boot_device_mock, collect_logs_mock):
install_bootloader_mock.return_value = { install_bootloader_mock.return_value = {
'command_status': 'SUCCESS', 'command_error': None} 'command_status': 'SUCCESS', 'command_error': None}
try_set_boot_device_mock.side_effect = RuntimeError('error') try_set_boot_device_mock.side_effect = RuntimeError('error')
@ -1365,9 +1393,11 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
self.deploy.configure_local_boot, self.deploy.configure_local_boot,
task, root_uuid='some-root-uuid', task, root_uuid='some-root-uuid',
prep_boot_part_uuid=None) prep_boot_part_uuid=None)
boot_mode_mock.assert_called_once_with(task.node)
install_bootloader_mock.assert_called_once_with( install_bootloader_mock.assert_called_once_with(
mock.ANY, task.node, root_uuid='some-root-uuid', mock.ANY, task.node, root_uuid='some-root-uuid',
efi_system_part_uuid=None, prep_boot_part_uuid=None) efi_system_part_uuid=None, prep_boot_part_uuid=None,
target_boot_mode='whatever')
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)
collect_logs_mock.assert_called_once_with(mock.ANY, task.node) collect_logs_mock.assert_called_once_with(mock.ANY, task.node)

View File

@ -276,11 +276,12 @@ class TestAgentClient(base.TestCase):
self.client._command = mock.MagicMock(spec_set=[]) self.client._command = mock.MagicMock(spec_set=[])
params = {'root_uuid': root_uuid, params = {'root_uuid': root_uuid,
'efi_system_part_uuid': efi_system_part_uuid, 'efi_system_part_uuid': efi_system_part_uuid,
'prep_boot_part_uuid': prep_boot_part_uuid} 'prep_boot_part_uuid': prep_boot_part_uuid,
'target_boot_mode': 'hello'}
self.client.install_bootloader( self.client.install_bootloader(
self.node, root_uuid, efi_system_part_uuid=efi_system_part_uuid, self.node, root_uuid, efi_system_part_uuid=efi_system_part_uuid,
prep_boot_part_uuid=prep_boot_part_uuid) prep_boot_part_uuid=prep_boot_part_uuid, target_boot_mode='hello')
self.client._command.assert_called_once_with( self.client._command.assert_called_once_with(
command_timeout_factor=2, node=self.node, command_timeout_factor=2, node=self.node,
method='image.install_bootloader', params=params, method='image.install_bootloader', params=params,
@ -290,7 +291,7 @@ class TestAgentClient(base.TestCase):
self._test_install_bootloader(root_uuid='fake-root-uuid', self._test_install_bootloader(root_uuid='fake-root-uuid',
efi_system_part_uuid='fake-efi-uuid') efi_system_part_uuid='fake-efi-uuid')
def test_install_bootloaderi_with_prep(self): def test_install_bootloader_with_prep(self):
self._test_install_bootloader(root_uuid='fake-root-uuid', self._test_install_bootloader(root_uuid='fake-root-uuid',
efi_system_part_uuid='fake-efi-uuid', efi_system_part_uuid='fake-efi-uuid',
prep_boot_part_uuid='fake-prep-uuid') prep_boot_part_uuid='fake-prep-uuid')

View File

@ -0,0 +1,4 @@
---
features:
- |
Adds support for bootable software RAID with UEFI boot mode.