diff --git a/ironic/drivers/modules/agent_base_vendor.py b/ironic/drivers/modules/agent_base_vendor.py index 1f0710023d..d6b4531b9d 100644 --- a/ironic/drivers/modules/agent_base_vendor.py +++ b/ironic/drivers/modules/agent_base_vendor.py @@ -32,6 +32,7 @@ from ironic.conductor import steps as conductor_steps from ironic.conductor import utils as manager_utils from ironic.conf import CONF from ironic.drivers.modules import agent_client +from ironic.drivers.modules import boot_mode_utils from ironic.drivers.modules import deploy_utils from ironic.drivers import utils as driver_utils @@ -830,8 +831,12 @@ class AgentDeployMixin(HeartbeatMixin): root_uuid = internal_info.get('root_uuid_or_disk_id') break + # For whole disk images it is not necessary that the root_uuid + # be provided since the bootloaders on the disk will be used whole_disk_image = internal_info.get('is_whole_disk_image') - if software_raid or (root_uuid and not whole_disk_image): + if (software_raid or (root_uuid and not whole_disk_image) or + (whole_disk_image and + boot_mode_utils.get_boot_mode(node) == 'uefi')): LOG.debug('Installing the bootloader for node %(node)s on ' 'partition %(part)s, EFI system partition %(efi)s', {'node': node.uuid, 'part': root_uuid, @@ -841,11 +846,20 @@ class AgentDeployMixin(HeartbeatMixin): efi_system_part_uuid=efi_system_part_uuid, prep_boot_part_uuid=prep_boot_part_uuid) if result['command_status'] == 'FAILED': - msg = (_("Failed to install a bootloader when " - "deploying node %(node)s. Error: %(error)s") % - {'node': node.uuid, - 'error': result['command_error']}) - log_and_raise_deployment_error(task, msg) + if not whole_disk_image: + msg = (_("Failed to install a bootloader when " + "deploying node %(node)s. Error: %(error)s") % + {'node': node.uuid, + 'error': result['command_error']}) + log_and_raise_deployment_error(task, msg) + else: + # Its possible the install will fail if the IPA image + # has not been updated, log this and continue + LOG.info('Could not install bootloader for whole disk ' + 'image for node %(node)s, Error: %(error)s"', + {'node': node.uuid, + 'error': result['command_error']}) + return try: persistent = True diff --git a/ironic/tests/unit/drivers/modules/test_agent_base_vendor.py b/ironic/tests/unit/drivers/modules/test_agent_base_vendor.py index 6710b97293..a9b77c0b08 100644 --- a/ironic/tests/unit/drivers/modules/test_agent_base_vendor.py +++ b/ironic/tests/unit/drivers/modules/test_agent_base_vendor.py @@ -29,6 +29,7 @@ from ironic.drivers import base as drivers_base from ironic.drivers.modules import agent from ironic.drivers.modules import agent_base_vendor from ironic.drivers.modules import agent_client +from ironic.drivers.modules import boot_mode_utils from ironic.drivers.modules import deploy_utils from ironic.drivers.modules import fake from ironic.drivers.modules import pxe @@ -1168,6 +1169,7 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest): autospec=True) def test_configure_local_boot_whole_disk_image( self, install_bootloader_mock, try_set_boot_device_mock): + with task_manager.acquire(self.context, self.node['uuid'], shared=False) as task: self.deploy.configure_local_boot(task) @@ -1188,6 +1190,26 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest): try_set_boot_device_mock.assert_called_once_with( task, boot_devices.DISK, persistent=True) + @mock.patch.object(boot_mode_utils, 'get_boot_mode', + 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_no_root_uuid_whole_disk( + self, install_bootloader_mock, try_set_boot_device_mock, + boot_mode_mock): + with task_manager.acquire(self.context, self.node['uuid'], + shared=False) as task: + task.node.driver_internal_info['is_whole_disk_image'] = True + boot_mode_mock.return_value = 'uefi' + self.deploy.configure_local_boot( + task, root_uuid=None, + efi_system_part_uuid='efi-system-part-uuid') + install_bootloader_mock.assert_called_once_with( + mock.ANY, task.node, root_uuid=None, + efi_system_part_uuid='efi-system-part-uuid', + prep_boot_part_uuid=None) + @mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True) @mock.patch.object(agent_client.AgentClient, 'install_bootloader', autospec=True) @@ -1252,6 +1274,8 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest): driver_info = task.node.driver_info driver_info['force_persistent_boot_device'] = 'Default' task.node.driver_info = driver_info + driver_info['force_persistent_boot_device'] = 'Always' + task.node.driver_internal_info['is_whole_disk_image'] = False self.deploy.configure_local_boot(task) self.assertFalse(install_bootloader_mock.called) try_set_boot_device_mock.assert_called_once_with( @@ -1267,6 +1291,7 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest): driver_info = task.node.driver_info driver_info['force_persistent_boot_device'] = 'Always' task.node.driver_info = driver_info + task.node.driver_internal_info['is_whole_disk_image'] = False self.deploy.configure_local_boot(task) self.assertFalse(install_bootloader_mock.called) try_set_boot_device_mock.assert_called_once_with( @@ -1282,6 +1307,7 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest): driver_info = task.node.driver_info driver_info['force_persistent_boot_device'] = 'Never' task.node.driver_info = driver_info + task.node.driver_internal_info['is_whole_disk_image'] = False self.deploy.configure_local_boot(task) self.assertFalse(install_bootloader_mock.called) try_set_boot_device_mock.assert_called_once_with( diff --git a/releasenotes/notes/whole-disk-scsi-install-bootloader-f7e791d82da476ca.yaml b/releasenotes/notes/whole-disk-scsi-install-bootloader-f7e791d82da476ca.yaml new file mode 100644 index 0000000000..e5fc1abb73 --- /dev/null +++ b/releasenotes/notes/whole-disk-scsi-install-bootloader-f7e791d82da476ca.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + When installing a whole disk image using iscsi, set up the bootloader even + if a root partition can not be found. The bootloaders will be located on + the disk.