diff --git a/ironic/drivers/modules/agent.py b/ironic/drivers/modules/agent.py index 997b231476..a015f6305e 100644 --- a/ironic/drivers/modules/agent.py +++ b/ironic/drivers/modules/agent.py @@ -265,6 +265,7 @@ class AgentDeployMixin(agent_base_vendor.AgentDeployMixin): task.process_event('resume') node = task.node iwdi = task.node.driver_internal_info.get('is_whole_disk_image') + cpu_arch = task.node.properties.get('cpu_arch') error = self.check_deploy_success(node) if error is not None: # TODO(jimrollenhagen) power off if using neutron dhcp to @@ -285,6 +286,9 @@ class AgentDeployMixin(agent_base_vendor.AgentDeployMixin): # In case of local boot using partition image, we need both # 'root_uuid_or_disk_id' and 'efi_system_partition_uuid' to configure # bootloader for local boot. + # NOTE(mjturek): In the case of local boot using a partition image on + # ppc64* hardware we need to provide the 'PReP_Boot_partition_uuid' to + # direct where the bootloader should be installed. driver_internal_info = task.node.driver_internal_info root_uuid = self._get_uuid_from_result(task, 'root_uuid') if root_uuid: @@ -312,9 +316,14 @@ class AgentDeployMixin(agent_base_vendor.AgentDeployMixin): efi_sys_uuid = None if not iwdi: if boot_mode_utils.get_boot_mode_for_deploy(node) == 'uefi': - efi_sys_uuid = ( - self._get_uuid_from_result(task, - 'efi_system_partition_uuid')) + efi_sys_uuid = (self._get_uuid_from_result(task, + 'efi_system_partition_uuid')) + + prep_boot_part_uuid = None + if cpu_arch is not None and cpu_arch.startswith('ppc64'): + prep_boot_part_uuid = (self._get_uuid_from_result(task, + 'PReP_Boot_partition_uuid')) + LOG.info('Image successfully written to node %s', node.uuid) if CONF.agent.manage_agent_boot: @@ -324,7 +333,8 @@ class AgentDeployMixin(agent_base_vendor.AgentDeployMixin): # be done on node during deploy stage can be performed. LOG.debug('Executing driver specific tasks before booting up the ' 'instance for node %s', node.uuid) - self.prepare_instance_to_boot(task, root_uuid, efi_sys_uuid) + self.prepare_instance_to_boot(task, root_uuid, + efi_sys_uuid, prep_boot_part_uuid) else: manager_utils.node_set_boot_device(task, 'disk', persistent=True) diff --git a/ironic/drivers/modules/agent_base_vendor.py b/ironic/drivers/modules/agent_base_vendor.py index 71a56c90d9..48163f9ea9 100644 --- a/ironic/drivers/modules/agent_base_vendor.py +++ b/ironic/drivers/modules/agent_base_vendor.py @@ -666,7 +666,8 @@ class AgentDeployMixin(HeartbeatMixin): manager_utils.notify_conductor_resume_deploy(task) @METRICS.timer('AgentDeployMixin.prepare_instance_to_boot') - def prepare_instance_to_boot(self, task, root_uuid, efi_sys_uuid): + def prepare_instance_to_boot(self, task, root_uuid, efi_sys_uuid, + prep_boot_part_uuid=None): """Prepares instance to boot. :param task: a TaskManager object containing the node @@ -680,7 +681,8 @@ class AgentDeployMixin(HeartbeatMixin): # Install the boot loader self.configure_local_boot( task, root_uuid=root_uuid, - efi_system_part_uuid=efi_sys_uuid) + efi_system_part_uuid=efi_sys_uuid, + prep_boot_part_uuid=prep_boot_part_uuid) try: task.driver.boot.prepare_instance(task) except Exception as e: @@ -693,7 +695,8 @@ class AgentDeployMixin(HeartbeatMixin): @METRICS.timer('AgentDeployMixin.configure_local_boot') def configure_local_boot(self, task, root_uuid=None, - efi_system_part_uuid=None): + efi_system_part_uuid=None, + prep_boot_part_uuid=None): """Helper method to configure local boot on the node. This method triggers bootloader installation on the node. @@ -707,6 +710,8 @@ class AgentDeployMixin(HeartbeatMixin): have a bootloader installed. :param efi_system_part_uuid: The UUID of the efi system partition. This is used only in uefi boot mode. + :param prep_boot_part_uuid: The UUID of the PReP Boot partition. + This is used only for booting ppc64* hardware. :raises: InstanceDeployFailure if bootloader installation failed or on encountering error while setting the boot device on the node. """ @@ -720,7 +725,8 @@ class AgentDeployMixin(HeartbeatMixin): 'efi': efi_system_part_uuid}) result = self._client.install_bootloader( 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) if result['command_status'] == 'FAILED': msg = (_("Failed to install a bootloader when " "deploying node %(node)s. Error: %(error)s") % diff --git a/ironic/drivers/modules/agent_client.py b/ironic/drivers/modules/agent_client.py index af9b90323c..c8fab64c02 100644 --- a/ironic/drivers/modules/agent_client.py +++ b/ironic/drivers/modules/agent_client.py @@ -224,7 +224,8 @@ class AgentClient(object): wait=True) @METRICS.timer('AgentClient.install_bootloader') - def install_bootloader(self, node, root_uuid, efi_system_part_uuid=None): + def install_bootloader(self, node, root_uuid, efi_system_part_uuid=None, + prep_boot_part_uuid=None): """Install a boot loader on the image. :param node: A node object. @@ -232,6 +233,9 @@ class AgentClient(object): :param efi_system_part_uuid: The UUID of the efi system partition where the bootloader will be installed to, only used for uefi boot mode. + :param prep_boot_part_uuid: The UUID of the PReP Boot partition where + the bootloader will be installed to when local booting a + partition image on a ppc64* system. :raises: IronicException when failed to issue the request or there was a malformed response from the agent. :raises: AgentAPIError when agent failed to execute specified command. @@ -239,7 +243,8 @@ class AgentClient(object): See :func:`get_commands_status` for a command result sample. """ 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} return self._command(node=node, method='image.install_bootloader', params=params, diff --git a/ironic/drivers/modules/deploy_utils.py b/ironic/drivers/modules/deploy_utils.py index 3b9920c679..fa49826474 100644 --- a/ironic/drivers/modules/deploy_utils.py +++ b/ironic/drivers/modules/deploy_utils.py @@ -345,7 +345,8 @@ def deploy_partition_image( address, port, iqn, lun, image_path, root_mb, swap_mb, ephemeral_mb, ephemeral_format, node_uuid, preserve_ephemeral=False, configdrive=None, - boot_option=None, boot_mode="bios", disk_label=None): + boot_option=None, boot_mode="bios", disk_label=None, + cpu_arch=""): """All-in-one function to deploy a partition image to a node. :param address: The iSCSI IP address. @@ -370,6 +371,7 @@ def deploy_partition_image( :param disk_label: The disk label to be used when creating the partition table. Valid values are: "msdos", "gpt" or None; If None Ironic will figure it out according to the boot_mode parameter. + :param cpu_arch: Architecture of the node being deployed to. :raises: InstanceDeployFailure if image virtual size is bigger than root partition size. :returns: a dictionary containing the following keys: @@ -392,7 +394,7 @@ def deploy_partition_image( dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format, image_path, node_uuid, preserve_ephemeral=preserve_ephemeral, configdrive=configdrive, boot_option=boot_option, - boot_mode=boot_mode, disk_label=disk_label) + boot_mode=boot_mode, disk_label=disk_label, cpu_arch=cpu_arch) return uuid_dict_returned diff --git a/ironic/drivers/modules/iscsi_deploy.py b/ironic/drivers/modules/iscsi_deploy.py index 0cbea4cccd..90a18a9e0e 100644 --- a/ironic/drivers/modules/iscsi_deploy.py +++ b/ironic/drivers/modules/iscsi_deploy.py @@ -179,7 +179,8 @@ def get_deploy_info(node, address, iqn, port=None, lun='1'): 'ephemeral_mb': i_info['ephemeral_mb'], 'preserve_ephemeral': i_info['preserve_ephemeral'], 'boot_option': deploy_utils.get_boot_option(node), - 'boot_mode': _get_boot_mode(node)}) + 'boot_mode': _get_boot_mode(node), + 'cpu_arch': node.properties.get('cpu_arch')}) # Append disk label if specified disk_label = deploy_utils.get_disk_label(node) @@ -408,7 +409,10 @@ class AgentDeployMixin(agent_base_vendor.AgentDeployMixin): uuid_dict_returned = do_agent_iscsi_deploy(task, self._client) root_uuid = uuid_dict_returned.get('root uuid') efi_sys_uuid = uuid_dict_returned.get('efi system partition uuid') - self.prepare_instance_to_boot(task, root_uuid, efi_sys_uuid) + prep_boot_part_uuid = uuid_dict_returned.get( + 'PrEP Boot partition uuid') + self.prepare_instance_to_boot(task, root_uuid, efi_sys_uuid, + prep_boot_part_uuid=prep_boot_part_uuid) self.reboot_and_finish_deploy(task) diff --git a/ironic/tests/unit/drivers/modules/test_agent.py b/ironic/tests/unit/drivers/modules/test_agent.py index ed290ea1cf..dd8a541708 100644 --- a/ironic/tests/unit/drivers/modules/test_agent.py +++ b/ironic/tests/unit/drivers/modules/test_agent.py @@ -983,7 +983,7 @@ class TestAgentDeploy(db_base.DbTestCase): self.assertIn("Ironic Python Agent version 3.1.0 and beyond", log_mock.call_args[0][0]) prepare_instance_mock.assert_called_once_with(mock.ANY, task, - None, None) + None, None, None) power_off_mock.assert_called_once_with(task.node) get_power_state_mock.assert_called_once_with(task) node_power_action_mock.assert_called_once_with( @@ -1078,8 +1078,67 @@ class TestAgentDeploy(db_base.DbTestCase): driver_int_info['root_uuid_or_disk_id']), boot_mode_mock.assert_called_once_with(task.node) self.assertFalse(log_mock.called) - prepare_instance_mock.assert_called_once_with(mock.ANY, task, - 'root_uuid', None) + prepare_instance_mock.assert_called_once_with(mock.ANY, + task, + 'root_uuid', + None, None) + power_off_mock.assert_called_once_with(task.node) + get_power_state_mock.assert_called_once_with(task) + node_power_action_mock.assert_called_once_with( + task, states.POWER_ON) + self.assertEqual(states.ACTIVE, task.node.provision_state) + self.assertEqual(states.NOSTATE, task.node.target_provision_state) + + @mock.patch.object(agent.LOG, 'warning', spec_set=True, autospec=True) + @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy', + autospec=True) + @mock.patch.object(agent.AgentDeployMixin, '_get_uuid_from_result', + autospec=True) + @mock.patch.object(manager_utils, 'node_power_action', autospec=True) + @mock.patch.object(fake.FakePower, 'get_power_state', + spec=types.FunctionType) + @mock.patch.object(agent_client.AgentClient, 'power_off', + spec=types.FunctionType) + @mock.patch.object(agent.AgentDeployMixin, 'prepare_instance_to_boot', + autospec=True) + @mock.patch('ironic.drivers.modules.agent.AgentDeployMixin' + '.check_deploy_success', autospec=True) + def test_reboot_to_instance_partition_localboot_ppc64( + self, check_deploy_mock, prepare_instance_mock, + power_off_mock, get_power_state_mock, + node_power_action_mock, uuid_mock, boot_mode_mock, log_mock): + check_deploy_mock.return_value = None + uuid_mock.side_effect = ['root_uuid', 'prep_boot_part_uuid'] + self.node.provision_state = states.DEPLOYWAIT + self.node.target_provision_state = states.ACTIVE + self.node.save() + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + get_power_state_mock.return_value = states.POWER_OFF + driver_internal_info = task.node.driver_internal_info + driver_internal_info['is_whole_disk_image'] = False + task.node.driver_internal_info = driver_internal_info + boot_option = {'capabilities': '{"boot_option": "local"}'} + task.node.instance_info = boot_option + properties = task.node.properties + properties.update(cpu_arch='ppc64le') + task.node.properties = properties + boot_mode_mock.return_value = 'bios' + task.driver.deploy.reboot_to_instance(task) + + check_deploy_mock.assert_called_once_with(mock.ANY, task.node) + driver_int_info = task.node.driver_internal_info + self.assertEqual('root_uuid', + driver_int_info['root_uuid_or_disk_id']), + uuid_mock_calls = [ + mock.call(mock.ANY, task, 'root_uuid'), + mock.call(mock.ANY, task, 'PReP_Boot_partition_uuid')] + uuid_mock.assert_has_calls(uuid_mock_calls) + boot_mode_mock.assert_called_once_with(task.node) + self.assertFalse(log_mock.called) + prepare_instance_mock.assert_called_once_with( + mock.ANY, task, 'root_uuid', None, 'prep_boot_part_uuid') power_off_mock.assert_called_once_with(task.node) get_power_state_mock.assert_called_once_with(task) node_power_action_mock.assert_called_once_with( @@ -1171,7 +1230,7 @@ class TestAgentDeploy(db_base.DbTestCase): boot_mode_mock.assert_called_once_with(task.node) self.assertFalse(log_mock.called) prepare_instance_mock.assert_called_once_with( - mock.ANY, task, 'root_uuid', 'efi_uuid') + mock.ANY, task, 'root_uuid', 'efi_uuid', None) power_off_mock.assert_called_once_with(task.node) get_power_state_mock.assert_called_once_with(task) node_power_action_mock.assert_called_once_with( 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 98c91d6b21..c99c921875 100644 --- a/ironic/tests/unit/drivers/modules/test_agent_base_vendor.py +++ b/ironic/tests/unit/drivers/modules/test_agent_base_vendor.py @@ -763,7 +763,25 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest): task, boot_devices.DISK) install_bootloader_mock.assert_called_once_with( mock.ANY, task.node, root_uuid='some-root-uuid', - efi_system_part_uuid=None) + efi_system_part_uuid=None, prep_boot_part_uuid=None) + + @mock.patch.object(agent_client.AgentClient, 'install_bootloader', + 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, + install_bootloader_mock): + install_bootloader_mock.return_value = { + 'command_status': 'SUCCESS', 'command_error': None} + with task_manager.acquire(self.context, self.node['uuid'], + shared=False) as task: + task.node.driver_internal_info['is_whole_disk_image'] = False + self.deploy.configure_local_boot(task, root_uuid='some-root-uuid', + prep_boot_part_uuid='fake-prep') + try_set_boot_device_mock.assert_called_once_with( + task, boot_devices.DISK) + install_bootloader_mock.assert_called_once_with( + mock.ANY, task.node, root_uuid='some-root-uuid', + efi_system_part_uuid=None, prep_boot_part_uuid='fake-prep') @mock.patch.object(agent_client.AgentClient, 'install_bootloader', autospec=True) @@ -782,7 +800,8 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest): task, boot_devices.DISK) install_bootloader_mock.assert_called_once_with( 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) @mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True) @mock.patch.object(agent_client.AgentClient, 'install_bootloader', @@ -828,7 +847,7 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest): task, root_uuid='some-root-uuid') install_bootloader_mock.assert_called_once_with( mock.ANY, task.node, root_uuid='some-root-uuid', - efi_system_part_uuid=None) + efi_system_part_uuid=None, prep_boot_part_uuid=None) collect_logs_mock.assert_called_once_with(mock.ANY, task.node) self.assertEqual(states.DEPLOYFAIL, task.node.provision_state) self.assertEqual(states.ACTIVE, task.node.target_provision_state) @@ -852,10 +871,11 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest): task.node.driver_internal_info['is_whole_disk_image'] = False self.assertRaises(exception.InstanceDeployFailure, self.deploy.configure_local_boot, - task, root_uuid='some-root-uuid') + task, root_uuid='some-root-uuid', + prep_boot_part_uuid=None) install_bootloader_mock.assert_called_once_with( mock.ANY, task.node, root_uuid='some-root-uuid', - efi_system_part_uuid=None) + efi_system_part_uuid=None, prep_boot_part_uuid=None) try_set_boot_device_mock.assert_called_once_with( task, boot_devices.DISK) collect_logs_mock.assert_called_once_with(mock.ANY, task.node) @@ -911,7 +931,39 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest): configure_mock.assert_called_once_with( self.deploy, task, root_uuid=root_uuid, - efi_system_part_uuid=efi_system_part_uuid) + efi_system_part_uuid=efi_system_part_uuid, + prep_boot_part_uuid=None) + boot_option_mock.assert_called_once_with(task.node) + prepare_instance_mock.assert_called_once_with(task.driver.boot, + task) + self.assertFalse(failed_state_mock.called) + + @mock.patch.object(deploy_utils, 'set_failed_state', autospec=True) + @mock.patch.object(pxe.PXEBoot, 'prepare_instance', autospec=True) + @mock.patch.object(deploy_utils, 'get_boot_option', autospec=True) + @mock.patch.object(agent_base_vendor.AgentDeployMixin, + 'configure_local_boot', autospec=True) + def test_prepare_instance_to_boot_localboot_prep_partition( + self, configure_mock, boot_option_mock, + prepare_instance_mock, failed_state_mock): + boot_option_mock.return_value = 'local' + prepare_instance_mock.return_value = None + self.node.provision_state = states.DEPLOYING + self.node.target_provision_state = states.ACTIVE + self.node.save() + root_uuid = 'root_uuid' + efi_system_part_uuid = 'efi_sys_uuid' + prep_boot_part_uuid = 'prep_boot_part_uuid' + with task_manager.acquire(self.context, self.node['uuid'], + shared=False) as task: + self.deploy.prepare_instance_to_boot(task, root_uuid, + efi_system_part_uuid, + prep_boot_part_uuid) + configure_mock.assert_called_once_with( + self.deploy, task, + root_uuid=root_uuid, + efi_system_part_uuid=efi_system_part_uuid, + prep_boot_part_uuid=prep_boot_part_uuid) boot_option_mock.assert_called_once_with(task.node) prepare_instance_mock.assert_called_once_with(task.driver.boot, task) @@ -946,7 +998,8 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest): configure_mock.assert_called_once_with( self.deploy, task, root_uuid=root_uuid, - efi_system_part_uuid=efi_system_part_uuid) + efi_system_part_uuid=efi_system_part_uuid, + prep_boot_part_uuid=None) boot_option_mock.assert_called_once_with(task.node) self.assertFalse(prepare_mock.called) self.assertFalse(failed_state_mock.called) diff --git a/ironic/tests/unit/drivers/modules/test_agent_client.py b/ironic/tests/unit/drivers/modules/test_agent_client.py index 6ddc7d2fd0..7efbb5a7e3 100644 --- a/ironic/tests/unit/drivers/modules/test_agent_client.py +++ b/ironic/tests/unit/drivers/modules/test_agent_client.py @@ -247,19 +247,29 @@ class TestAgentClient(base.TestCase): node=self.node, method='iscsi.start_iscsi_target', params=params, wait=True) - def test_install_bootloader(self): + def _test_install_bootloader(self, root_uuid, efi_system_part_uuid=None, + prep_boot_part_uuid=None): self.client._command = mock.MagicMock(spec_set=[]) - root_uuid = 'fake-root-uuid' - efi_system_part_uuid = 'fake-efi-system-part-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} 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) self.client._command.assert_called_once_with( node=self.node, method='image.install_bootloader', params=params, wait=True) + def test_install_bootloader(self): + self._test_install_bootloader(root_uuid='fake-root-uuid', + efi_system_part_uuid='fake-efi-uuid') + + def test_install_bootloaderi_with_prep(self): + self._test_install_bootloader(root_uuid='fake-root-uuid', + efi_system_part_uuid='fake-efi-uuid', + prep_boot_part_uuid='fake-prep-uuid') + def test_get_clean_steps(self): self.client._command = mock.MagicMock(spec_set=[]) ports = [] diff --git a/ironic/tests/unit/drivers/modules/test_deploy_utils.py b/ironic/tests/unit/drivers/modules/test_deploy_utils.py index 6bf0543c49..61264e1f5b 100644 --- a/ironic/tests/unit/drivers/modules/test_deploy_utils.py +++ b/ironic/tests/unit/drivers/modules/test_deploy_utils.py @@ -383,6 +383,7 @@ class PhysicalWorkTestCase(tests_base.TestCase): 'boot_mode': None, 'boot_option': None, 'configdrive': None, + 'cpu_arch': None, 'disk_label': None, 'ephemeral_format': None, 'ephemeral_mb': None, @@ -422,6 +423,7 @@ class PhysicalWorkTestCase(tests_base.TestCase): 'boot_option': deploy_args['boot_option'], 'configdrive': deploy_args['configdrive'], 'disk_label': deploy_args['disk_label'], + 'cpu_arch': deploy_args['cpu_arch'] or '', 'preserve_ephemeral': deploy_args['preserve_ephemeral'] } utils.deploy_partition_image( @@ -444,7 +446,8 @@ class PhysicalWorkTestCase(tests_base.TestCase): # not set 'boot_option': deploy_args['boot_option'] or 'netboot', 'boot_mode': deploy_args['boot_mode'], - 'disk_label': deploy_args['disk_label'] + 'disk_label': deploy_args['disk_label'], + 'cpu_arch': deploy_args['cpu_arch'] or '' } mock_work_on_disk.assert_called_once_with( dev, deploy_args['root_mb'], deploy_args['swap_mb'], @@ -502,6 +505,9 @@ class PhysicalWorkTestCase(tests_base.TestCase): def test_deploy_partition_image_with_configdrive(self): self._test_deploy_partition_image(configdrive='http://1.2.3.4/cd') + def test_deploy_partition_image_with_cpu_arch(self): + self._test_deploy_partition_image(cpu_arch='generic') + @mock.patch.object(disk_utils, 'create_config_drive_partition', autospec=True) @mock.patch.object(disk_utils, 'get_disk_identifier', autospec=True) @@ -775,7 +781,8 @@ class PhysicalWorkTestCase(tests_base.TestCase): preserve_ephemeral=False, boot_option="netboot", boot_mode="bios", - disk_label=None)] + disk_label=None, + cpu_arch="")] self.assertRaises(TestException, utils.deploy_partition_image, address, port, iqn, lun, image_path, diff --git a/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py b/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py index 995ba0aad9..f02c7e0859 100644 --- a/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py +++ b/ironic/tests/unit/drivers/modules/test_iscsi_deploy.py @@ -896,7 +896,7 @@ class ISCSIDeployTestCase(db_base.DbTestCase): task, task.driver.deploy._client) configure_local_boot_mock.assert_called_once_with( task.driver.deploy, task, root_uuid='some-root-uuid', - efi_system_part_uuid=None) + efi_system_part_uuid=None, prep_boot_part_uuid=None) reboot_and_finish_deploy_mock.assert_called_once_with( task.driver.deploy, task) set_boot_device_mock.assert_called_once_with( @@ -928,7 +928,7 @@ class ISCSIDeployTestCase(db_base.DbTestCase): task, task.driver.deploy._client) configure_local_boot_mock.assert_called_once_with( task.driver.deploy, task, root_uuid='some-root-uuid', - efi_system_part_uuid='efi-part-uuid') + efi_system_part_uuid='efi-part-uuid', prep_boot_part_uuid=None) reboot_and_finish_deploy_mock.assert_called_once_with( task.driver.deploy, task) set_boot_device_mock.assert_called_once_with( diff --git a/lower-constraints.txt b/lower-constraints.txt index c8ba804543..69c421e849 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -38,7 +38,7 @@ greenlet==0.4.13 hacking==1.0.0 idna==2.6 imagesize==1.0.0 -ironic-lib==2.5.0 +ironic-lib==2.14.0 iso8601==0.1.11 Jinja2==2.10 jmespath==0.9.3 diff --git a/releasenotes/notes/add-prep-partition-support-d808849795906e64.yaml b/releasenotes/notes/add-prep-partition-support-d808849795906e64.yaml new file mode 100644 index 0000000000..41be144a4b --- /dev/null +++ b/releasenotes/notes/add-prep-partition-support-d808849795906e64.yaml @@ -0,0 +1,7 @@ +--- +features: + - Added support for local booting a partition image for ppc64* + hardware. If a PReP partition is detected when deploying to a + ppc64* machine, the partition will be specified to IPA causing + the bootloader to be installed there directly. This feature + requires a ironic-python-agent ramdisk with ironic-lib >=2.14. diff --git a/requirements.txt b/requirements.txt index e40b32aaae..a566689ff5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ python-cinderclient>=3.3.0 # Apache-2.0 python-neutronclient>=6.7.0 # Apache-2.0 python-glanceclient>=2.8.0 # Apache-2.0 keystoneauth1>=3.4.0 # Apache-2.0 -ironic-lib>=2.5.0 # Apache-2.0 +ironic-lib>=2.14.0 # Apache-2.0 python-swiftclient>=3.2.0 # Apache-2.0 pytz>=2013.6 # MIT stevedore>=1.20.0 # Apache-2.0