Merge "Pass prep_boot_part_uuid to install_bootloader for ppc64* partition images"

This commit is contained in:
Zuul 2018-08-01 20:02:09 +00:00 committed by Gerrit Code Review
commit d3236ef7dd
13 changed files with 199 additions and 36 deletions

View File

@ -265,6 +265,7 @@ class AgentDeployMixin(agent_base_vendor.AgentDeployMixin):
task.process_event('resume') task.process_event('resume')
node = task.node node = task.node
iwdi = task.node.driver_internal_info.get('is_whole_disk_image') 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) error = self.check_deploy_success(node)
if error is not None: if error is not None:
# TODO(jimrollenhagen) power off if using neutron dhcp to # 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 # In case of local boot using partition image, we need both
# 'root_uuid_or_disk_id' and 'efi_system_partition_uuid' to configure # 'root_uuid_or_disk_id' and 'efi_system_partition_uuid' to configure
# bootloader for local boot. # 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 driver_internal_info = task.node.driver_internal_info
root_uuid = self._get_uuid_from_result(task, 'root_uuid') root_uuid = self._get_uuid_from_result(task, 'root_uuid')
if root_uuid: if root_uuid:
@ -312,9 +316,14 @@ class AgentDeployMixin(agent_base_vendor.AgentDeployMixin):
efi_sys_uuid = None efi_sys_uuid = None
if not iwdi: if not iwdi:
if boot_mode_utils.get_boot_mode_for_deploy(node) == 'uefi': if boot_mode_utils.get_boot_mode_for_deploy(node) == 'uefi':
efi_sys_uuid = ( efi_sys_uuid = (self._get_uuid_from_result(task,
self._get_uuid_from_result(task, 'efi_system_partition_uuid'))
'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) LOG.info('Image successfully written to node %s', node.uuid)
if CONF.agent.manage_agent_boot: 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. # be done on node during deploy stage can be performed.
LOG.debug('Executing driver specific tasks before booting up the ' LOG.debug('Executing driver specific tasks before booting up the '
'instance for node %s', node.uuid) '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: else:
manager_utils.node_set_boot_device(task, 'disk', persistent=True) manager_utils.node_set_boot_device(task, 'disk', persistent=True)

View File

@ -666,7 +666,8 @@ class AgentDeployMixin(HeartbeatMixin):
manager_utils.notify_conductor_resume_deploy(task) manager_utils.notify_conductor_resume_deploy(task)
@METRICS.timer('AgentDeployMixin.prepare_instance_to_boot') @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. """Prepares instance to boot.
:param task: a TaskManager object containing the node :param task: a TaskManager object containing the node
@ -680,7 +681,8 @@ class AgentDeployMixin(HeartbeatMixin):
# Install the boot loader # Install the boot loader
self.configure_local_boot( self.configure_local_boot(
task, root_uuid=root_uuid, 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: try:
task.driver.boot.prepare_instance(task) task.driver.boot.prepare_instance(task)
except Exception as e: except Exception as e:
@ -693,7 +695,8 @@ class AgentDeployMixin(HeartbeatMixin):
@METRICS.timer('AgentDeployMixin.configure_local_boot') @METRICS.timer('AgentDeployMixin.configure_local_boot')
def configure_local_boot(self, task, root_uuid=None, 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. """Helper method to configure local boot on the node.
This method triggers bootloader installation on the node. This method triggers bootloader installation on the node.
@ -707,6 +710,8 @@ class AgentDeployMixin(HeartbeatMixin):
have a bootloader installed. have a bootloader installed.
:param efi_system_part_uuid: The UUID of the efi system partition. :param efi_system_part_uuid: The UUID of the efi system partition.
This is used only in uefi boot mode. 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 :raises: InstanceDeployFailure if bootloader installation failed or
on encountering error while setting the boot device on the node. on encountering error while setting the boot device on the node.
""" """
@ -720,7 +725,8 @@ class AgentDeployMixin(HeartbeatMixin):
'efi': efi_system_part_uuid}) 'efi': efi_system_part_uuid})
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)
if result['command_status'] == 'FAILED': if result['command_status'] == 'FAILED':
msg = (_("Failed to install a bootloader when " msg = (_("Failed to install a bootloader when "
"deploying node %(node)s. Error: %(error)s") % "deploying node %(node)s. Error: %(error)s") %

View File

@ -224,7 +224,8 @@ 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, efi_system_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.
@ -232,6 +233,9 @@ class AgentClient(object):
: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.
: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 :raises: IronicException when failed to issue the request or there was
a malformed response from the agent. a malformed response from the agent.
:raises: AgentAPIError when agent failed to execute specified command. :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. See :func:`get_commands_status` for a command result sample.
""" """
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}
return self._command(node=node, return self._command(node=node,
method='image.install_bootloader', method='image.install_bootloader',
params=params, params=params,

View File

@ -350,7 +350,8 @@ def deploy_partition_image(
address, port, iqn, lun, image_path, address, port, iqn, lun, image_path,
root_mb, swap_mb, ephemeral_mb, ephemeral_format, node_uuid, root_mb, swap_mb, ephemeral_mb, ephemeral_format, node_uuid,
preserve_ephemeral=False, configdrive=None, 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. """All-in-one function to deploy a partition image to a node.
:param address: The iSCSI IP address. :param address: The iSCSI IP address.
@ -376,6 +377,7 @@ def deploy_partition_image(
:param disk_label: The disk label to be used when creating the :param disk_label: The disk label to be used when creating the
partition table. Valid values are: "msdos", "gpt" or None; If None partition table. Valid values are: "msdos", "gpt" or None; If None
Ironic will figure it out according to the boot_mode parameter. 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 :raises: InstanceDeployFailure if image virtual size is bigger than root
partition size. partition size.
:returns: a dictionary containing the following keys: :returns: a dictionary containing the following keys:
@ -398,7 +400,7 @@ def deploy_partition_image(
dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format, image_path, dev, root_mb, swap_mb, ephemeral_mb, ephemeral_format, image_path,
node_uuid, preserve_ephemeral=preserve_ephemeral, node_uuid, preserve_ephemeral=preserve_ephemeral,
configdrive=configdrive, boot_option=boot_option, 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 return uuid_dict_returned

View File

@ -179,7 +179,8 @@ def get_deploy_info(node, address, iqn, port=None, lun='1'):
'ephemeral_mb': i_info['ephemeral_mb'], 'ephemeral_mb': i_info['ephemeral_mb'],
'preserve_ephemeral': i_info['preserve_ephemeral'], 'preserve_ephemeral': i_info['preserve_ephemeral'],
'boot_option': deploy_utils.get_boot_option(node), '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 # Append disk label if specified
disk_label = deploy_utils.get_disk_label(node) 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) uuid_dict_returned = do_agent_iscsi_deploy(task, self._client)
root_uuid = uuid_dict_returned.get('root uuid') root_uuid = uuid_dict_returned.get('root uuid')
efi_sys_uuid = uuid_dict_returned.get('efi system partition 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) self.reboot_and_finish_deploy(task)

View File

@ -983,7 +983,7 @@ class TestAgentDeploy(db_base.DbTestCase):
self.assertIn("Ironic Python Agent version 3.1.0 and beyond", self.assertIn("Ironic Python Agent version 3.1.0 and beyond",
log_mock.call_args[0][0]) log_mock.call_args[0][0])
prepare_instance_mock.assert_called_once_with(mock.ANY, task, prepare_instance_mock.assert_called_once_with(mock.ANY, task,
None, None) None, None, None)
power_off_mock.assert_called_once_with(task.node) power_off_mock.assert_called_once_with(task.node)
get_power_state_mock.assert_called_once_with(task) get_power_state_mock.assert_called_once_with(task)
node_power_action_mock.assert_called_once_with( 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']), driver_int_info['root_uuid_or_disk_id']),
boot_mode_mock.assert_called_once_with(task.node) boot_mode_mock.assert_called_once_with(task.node)
self.assertFalse(log_mock.called) self.assertFalse(log_mock.called)
prepare_instance_mock.assert_called_once_with(mock.ANY, task, prepare_instance_mock.assert_called_once_with(mock.ANY,
'root_uuid', None) 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) power_off_mock.assert_called_once_with(task.node)
get_power_state_mock.assert_called_once_with(task) get_power_state_mock.assert_called_once_with(task)
node_power_action_mock.assert_called_once_with( 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) boot_mode_mock.assert_called_once_with(task.node)
self.assertFalse(log_mock.called) self.assertFalse(log_mock.called)
prepare_instance_mock.assert_called_once_with( 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) power_off_mock.assert_called_once_with(task.node)
get_power_state_mock.assert_called_once_with(task) get_power_state_mock.assert_called_once_with(task)
node_power_action_mock.assert_called_once_with( node_power_action_mock.assert_called_once_with(

View File

@ -763,7 +763,25 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
task, boot_devices.DISK) task, boot_devices.DISK)
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) 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', @mock.patch.object(agent_client.AgentClient, 'install_bootloader',
autospec=True) autospec=True)
@ -782,7 +800,8 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
task, boot_devices.DISK) task, boot_devices.DISK)
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)
@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',
@ -828,7 +847,7 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
task, root_uuid='some-root-uuid') task, root_uuid='some-root-uuid')
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) efi_system_part_uuid=None, prep_boot_part_uuid=None)
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)
@ -852,10 +871,11 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
task.node.driver_internal_info['is_whole_disk_image'] = False task.node.driver_internal_info['is_whole_disk_image'] = False
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',
prep_boot_part_uuid=None)
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) efi_system_part_uuid=None, prep_boot_part_uuid=None)
try_set_boot_device_mock.assert_called_once_with( try_set_boot_device_mock.assert_called_once_with(
task, boot_devices.DISK) task, boot_devices.DISK)
collect_logs_mock.assert_called_once_with(mock.ANY, task.node) collect_logs_mock.assert_called_once_with(mock.ANY, task.node)
@ -911,7 +931,39 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
configure_mock.assert_called_once_with( configure_mock.assert_called_once_with(
self.deploy, task, self.deploy, task,
root_uuid=root_uuid, 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) boot_option_mock.assert_called_once_with(task.node)
prepare_instance_mock.assert_called_once_with(task.driver.boot, prepare_instance_mock.assert_called_once_with(task.driver.boot,
task) task)
@ -946,7 +998,8 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest):
configure_mock.assert_called_once_with( configure_mock.assert_called_once_with(
self.deploy, task, self.deploy, task,
root_uuid=root_uuid, 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) boot_option_mock.assert_called_once_with(task.node)
self.assertFalse(prepare_mock.called) self.assertFalse(prepare_mock.called)
self.assertFalse(failed_state_mock.called) self.assertFalse(failed_state_mock.called)

View File

@ -247,19 +247,29 @@ class TestAgentClient(base.TestCase):
node=self.node, method='iscsi.start_iscsi_target', node=self.node, method='iscsi.start_iscsi_target',
params=params, wait=True) 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=[]) 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, 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.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( self.client._command.assert_called_once_with(
node=self.node, method='image.install_bootloader', params=params, node=self.node, method='image.install_bootloader', params=params,
wait=True) 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): def test_get_clean_steps(self):
self.client._command = mock.MagicMock(spec_set=[]) self.client._command = mock.MagicMock(spec_set=[])
ports = [] ports = []

View File

@ -383,6 +383,7 @@ class PhysicalWorkTestCase(tests_base.TestCase):
'boot_mode': None, 'boot_mode': None,
'boot_option': None, 'boot_option': None,
'configdrive': None, 'configdrive': None,
'cpu_arch': None,
'disk_label': None, 'disk_label': None,
'ephemeral_format': None, 'ephemeral_format': None,
'ephemeral_mb': None, 'ephemeral_mb': None,
@ -422,6 +423,7 @@ class PhysicalWorkTestCase(tests_base.TestCase):
'boot_option': deploy_args['boot_option'], 'boot_option': deploy_args['boot_option'],
'configdrive': deploy_args['configdrive'], 'configdrive': deploy_args['configdrive'],
'disk_label': deploy_args['disk_label'], 'disk_label': deploy_args['disk_label'],
'cpu_arch': deploy_args['cpu_arch'] or '',
'preserve_ephemeral': deploy_args['preserve_ephemeral'] 'preserve_ephemeral': deploy_args['preserve_ephemeral']
} }
utils.deploy_partition_image( utils.deploy_partition_image(
@ -444,7 +446,8 @@ class PhysicalWorkTestCase(tests_base.TestCase):
# not set # not set
'boot_option': deploy_args['boot_option'] or 'netboot', 'boot_option': deploy_args['boot_option'] or 'netboot',
'boot_mode': deploy_args['boot_mode'], '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( mock_work_on_disk.assert_called_once_with(
dev, deploy_args['root_mb'], deploy_args['swap_mb'], 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): def test_deploy_partition_image_with_configdrive(self):
self._test_deploy_partition_image(configdrive='http://1.2.3.4/cd') 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', @mock.patch.object(disk_utils, 'create_config_drive_partition',
autospec=True) autospec=True)
@mock.patch.object(disk_utils, 'get_disk_identifier', autospec=True) @mock.patch.object(disk_utils, 'get_disk_identifier', autospec=True)
@ -775,7 +781,8 @@ class PhysicalWorkTestCase(tests_base.TestCase):
preserve_ephemeral=False, preserve_ephemeral=False,
boot_option="netboot", boot_option="netboot",
boot_mode="bios", boot_mode="bios",
disk_label=None)] disk_label=None,
cpu_arch="")]
self.assertRaises(TestException, utils.deploy_partition_image, self.assertRaises(TestException, utils.deploy_partition_image,
address, port, iqn, lun, image_path, address, port, iqn, lun, image_path,

View File

@ -896,7 +896,7 @@ class ISCSIDeployTestCase(db_base.DbTestCase):
task, task.driver.deploy._client) task, task.driver.deploy._client)
configure_local_boot_mock.assert_called_once_with( configure_local_boot_mock.assert_called_once_with(
task.driver.deploy, task, root_uuid='some-root-uuid', 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( reboot_and_finish_deploy_mock.assert_called_once_with(
task.driver.deploy, task) task.driver.deploy, task)
set_boot_device_mock.assert_called_once_with( set_boot_device_mock.assert_called_once_with(
@ -928,7 +928,7 @@ class ISCSIDeployTestCase(db_base.DbTestCase):
task, task.driver.deploy._client) task, task.driver.deploy._client)
configure_local_boot_mock.assert_called_once_with( configure_local_boot_mock.assert_called_once_with(
task.driver.deploy, task, root_uuid='some-root-uuid', 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( reboot_and_finish_deploy_mock.assert_called_once_with(
task.driver.deploy, task) task.driver.deploy, task)
set_boot_device_mock.assert_called_once_with( set_boot_device_mock.assert_called_once_with(

View File

@ -38,7 +38,7 @@ greenlet==0.4.13
hacking==1.0.0 hacking==1.0.0
idna==2.6 idna==2.6
imagesize==1.0.0 imagesize==1.0.0
ironic-lib==2.5.0 ironic-lib==2.14.0
iso8601==0.1.11 iso8601==0.1.11
Jinja2==2.10 Jinja2==2.10
jmespath==0.9.3 jmespath==0.9.3

View File

@ -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.

View File

@ -11,7 +11,7 @@ python-cinderclient>=3.3.0 # Apache-2.0
python-neutronclient>=6.7.0 # Apache-2.0 python-neutronclient>=6.7.0 # Apache-2.0
python-glanceclient>=2.8.0 # Apache-2.0 python-glanceclient>=2.8.0 # Apache-2.0
keystoneauth1>=3.4.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 python-swiftclient>=3.2.0 # Apache-2.0
pytz>=2013.6 # MIT pytz>=2013.6 # MIT
stevedore>=1.20.0 # Apache-2.0 stevedore>=1.20.0 # Apache-2.0