diff --git a/ironic/drivers/modules/ilo/boot.py b/ironic/drivers/modules/ilo/boot.py index 745b2d4fe0..b8e1f2e6a6 100644 --- a/ironic/drivers/modules/ilo/boot.py +++ b/ironic/drivers/modules/ilo/boot.py @@ -248,6 +248,138 @@ def _parse_deploy_info(node): return info +def validate_driver_info(task): + """Validate the prerequisites for virtual media based boot. + + This method validates whether the 'driver_info' property of the + supplied node contains the required information for this driver. + + :param task: a TaskManager instance containing the node to act on. + :raises: InvalidParameterValue if any parameters are incorrect + :raises: MissingParameterValue if some mandatory information + is missing on the node + """ + node = task.node + ilo_common.parse_driver_info(node) + if 'ilo_deploy_iso' not in node.driver_info: + raise exception.MissingParameterValue(_( + "Missing 'ilo_deploy_iso' parameter in node's 'driver_info'.")) + deploy_iso = node.driver_info['ilo_deploy_iso'] + if not service_utils.is_glance_image(deploy_iso): + try: + image_service.HttpImageService().validate_href(deploy_iso) + except exception.ImageRefValidationFailed: + raise exception.InvalidParameterValue(_( + "Virtual media boot accepts only Glance images or " + "HTTP(S) as driver_info['ilo_deploy_iso']. Either '%s' " + "is not a glance UUID or not a valid HTTP(S) URL or " + "the given URL is not reachable.") % deploy_iso) + + +def _validate_instance_image_info(task): + """Validate instance image information for the task's node. + + :param task: a TaskManager instance containing the node to act on. + :raises: InvalidParameterValue, if some information is invalid. + :raises: MissingParameterValue if 'kernel_id' and 'ramdisk_id' are + missing in the Glance image or 'kernel' and 'ramdisk' not provided + in instance_info for non-Glance image. + """ + + node = task.node + + d_info = _parse_deploy_info(node) + + if node.driver_internal_info.get('is_whole_disk_image'): + props = [] + elif service_utils.is_glance_image(d_info['image_source']): + props = ['kernel_id', 'ramdisk_id'] + else: + props = ['kernel', 'ramdisk'] + deploy_utils.validate_image_properties(task.context, d_info, props) + + +def _disable_secure_boot(task): + """Disables secure boot on node, if secure boot is enabled on node. + + This method checks if secure boot is enabled on node. If enabled, it + disables same and returns True. + + :param task: a TaskManager instance containing the node to act on. + :returns: It returns True, if secure boot was successfully disabled on + the node. + It returns False, if secure boot on node is in disabled state + or if secure boot feature is not supported by the node. + :raises: IloOperationError, if some operation on iLO failed. + """ + cur_sec_state = False + try: + cur_sec_state = ilo_common.get_secure_boot_mode(task) + except exception.IloOperationNotSupported: + LOG.debug('Secure boot mode is not supported for node %s', + task.node.uuid) + return False + + if cur_sec_state: + LOG.debug('Disabling secure boot for node %s', task.node.uuid) + ilo_common.set_secure_boot_mode(task, False) + return True + return False + + +def prepare_node_for_deploy(task): + """Common preparatory steps for all iLO drivers. + + This method performs common preparatory steps required for all drivers. + 1. Power off node + 2. Disables secure boot, if it is in enabled state. + 3. Updates boot_mode capability to 'uefi' if secure boot is requested. + 4. Changes boot mode of the node if secure boot is disabled currently. + + :param task: a TaskManager instance containing the node to act on. + :raises: IloOperationError, if some operation on iLO failed. + """ + manager_utils.node_power_action(task, states.POWER_OFF) + + # Boot mode can be changed only if secure boot is in disabled state. + # secure boot and boot mode cannot be changed together. + change_boot_mode = True + + # Disable secure boot on the node if it is in enabled state. + if _disable_secure_boot(task): + change_boot_mode = False + + if change_boot_mode: + ilo_common.update_boot_mode(task) + else: + # Need to update boot mode that will be used during deploy, if one is + # not provided. + # Since secure boot was disabled, we are in 'uefi' boot mode. + if deploy_utils.get_boot_mode_for_deploy(task.node) is None: + instance_info = task.node.instance_info + instance_info['deploy_boot_mode'] = 'uefi' + task.node.instance_info = instance_info + task.node.save() + + +def disable_secure_boot_if_supported(task): + """Disables secure boot on node, does not throw if its not supported. + + :param task: a TaskManager instance containing the node to act on. + :raises: IloOperationError, if some operation on iLO failed. + """ + try: + ilo_common.update_secure_boot_mode(task, False) + # We need to handle IloOperationNotSupported exception so that if + # the user has incorrectly specified the Node capability + # 'secure_boot' to a node that does not have that capability and + # attempted deploy. Handling this exception here, will help the + # user to tear down such a Node. + except exception.IloOperationNotSupported: + LOG.warning('Secure boot mode is not supported for node %s', + task.node.uuid) + + class IloVirtualMediaBoot(base.BootInterface): def get_properties(self): @@ -264,17 +396,7 @@ class IloVirtualMediaBoot(base.BootInterface): in instance_info for non-Glance image. """ - node = task.node - - d_info = _parse_deploy_info(node) - - if node.driver_internal_info.get('is_whole_disk_image'): - props = [] - elif service_utils.is_glance_image(d_info['image_source']): - props = ['kernel_id', 'ramdisk_id'] - else: - props = ['kernel', 'ramdisk'] - deploy_utils.validate_image_properties(task.context, d_info, props) + _validate_instance_image_info(task) @METRICS.timer('IloVirtualMediaBoot.prepare_ramdisk') def prepare_ramdisk(self, task, ramdisk_params): @@ -316,7 +438,7 @@ class IloVirtualMediaBoot(base.BootInterface): node.save() # Eject all virtual media devices, as we are going to use them - # during deploy. + # during boot. ilo_common.eject_vmedia_devices(task) deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task) diff --git a/ironic/drivers/modules/ilo/deploy.py b/ironic/drivers/modules/ilo/deploy.py index 7da13b0e62..099ae7ad64 100644 --- a/ironic/drivers/modules/ilo/deploy.py +++ b/ironic/drivers/modules/ilo/deploy.py @@ -19,15 +19,10 @@ from ironic_lib import metrics_utils from oslo_log import log as logging from ironic.common import boot_devices -from ironic.common import exception -from ironic.common.glance_service import service_utils -from ironic.common.i18n import _, _LW -from ironic.common import image_service from ironic.common import states from ironic.conductor import task_manager from ironic.conductor import utils as manager_utils from ironic.drivers.modules import agent -from ironic.drivers.modules import deploy_utils from ironic.drivers.modules.ilo import boot as ilo_boot from ironic.drivers.modules.ilo import common as ilo_common from ironic.drivers.modules import iscsi_deploy @@ -37,127 +32,6 @@ LOG = logging.getLogger(__name__) METRICS = metrics_utils.get_metrics_logger(__name__) -def _prepare_agent_vmedia_boot(task): - """Ejects virtual media devices and prepares for vmedia boot.""" - # Eject all virtual media devices, as we are going to use them - # during deploy. - ilo_common.eject_vmedia_devices(task) - - deploy_ramdisk_opts = deploy_utils.build_agent_options(task.node) - deploy_iso = task.node.driver_info['ilo_deploy_iso'] - ilo_common.setup_vmedia(task, deploy_iso, deploy_ramdisk_opts) - manager_utils.node_power_action(task, states.REBOOT) - - -def _disable_secure_boot(task): - """Disables secure boot on node, if secure boot is enabled on node. - - This method checks if secure boot is enabled on node. If enabled, it - disables same and returns True. - - :param task: a TaskManager instance containing the node to act on. - :returns: It returns True, if secure boot was successfully disabled on - the node. - It returns False, if secure boot on node is in disabled state - or if secure boot feature is not supported by the node. - :raises: IloOperationError, if some operation on iLO failed. - """ - cur_sec_state = False - try: - cur_sec_state = ilo_common.get_secure_boot_mode(task) - except exception.IloOperationNotSupported: - LOG.debug('Secure boot mode is not supported for node %s', - task.node.uuid) - return False - - if cur_sec_state: - LOG.debug('Disabling secure boot for node %s', task.node.uuid) - ilo_common.set_secure_boot_mode(task, False) - return True - return False - - -def _prepare_node_for_deploy(task): - """Common preparatory steps for all iLO drivers. - - This method performs common preparatory steps required for all drivers. - 1. Power off node - 2. Disables secure boot, if it is in enabled state. - 3. Updates boot_mode capability to 'uefi' if secure boot is requested. - 4. Changes boot mode of the node if secure boot is disabled currently. - - :param task: a TaskManager instance containing the node to act on. - :raises: IloOperationError, if some operation on iLO failed. - """ - manager_utils.node_power_action(task, states.POWER_OFF) - - # Boot mode can be changed only if secure boot is in disabled state. - # secure boot and boot mode cannot be changed together. - change_boot_mode = True - - # Disable secure boot on the node if it is in enabled state. - if _disable_secure_boot(task): - change_boot_mode = False - - if change_boot_mode: - ilo_common.update_boot_mode(task) - else: - # Need to update boot mode that will be used during deploy, if one is - # not provided. - # Since secure boot was disabled, we are in 'uefi' boot mode. - if deploy_utils.get_boot_mode_for_deploy(task.node) is None: - instance_info = task.node.instance_info - instance_info['deploy_boot_mode'] = 'uefi' - task.node.instance_info = instance_info - task.node.save() - - -def _disable_secure_boot_if_supported(task): - """Disables secure boot on node, does not throw if its not supported. - - :param task: a TaskManager instance containing the node to act on. - :raises: IloOperationError, if some operation on iLO failed. - """ - try: - ilo_common.update_secure_boot_mode(task, False) - # We need to handle IloOperationNotSupported exception so that if - # the user has incorrectly specified the Node capability - # 'secure_boot' to a node that does not have that capability and - # attempted deploy. Handling this exception here, will help the - # user to tear down such a Node. - except exception.IloOperationNotSupported: - LOG.warning(_LW('Secure boot mode is not supported for node %s'), - task.node.uuid) - - -def _validate(task): - """Validate the prerequisites for virtual media based deploy. - - This method validates whether the 'driver_info' property of the - supplied node contains the required information for this driver. - - :param task: a TaskManager instance containing the node to act on. - :raises: InvalidParameterValue if any parameters are incorrect - :raises: MissingParameterValue if some mandatory information - is missing on the node - """ - node = task.node - ilo_common.parse_driver_info(node) - if 'ilo_deploy_iso' not in node.driver_info: - raise exception.MissingParameterValue(_( - "Missing 'ilo_deploy_iso' parameter in node's 'driver_info'.")) - deploy_iso = node.driver_info['ilo_deploy_iso'] - if not service_utils.is_glance_image(deploy_iso): - try: - image_service.HttpImageService().validate_href(deploy_iso) - except exception.ImageRefValidationFailed: - raise exception.InvalidParameterValue(_( - "Virtual media deploy accepts only Glance images or " - "HTTP(S) as driver_info['ilo_deploy_iso']. Either '%s' " - "is not a glance UUID or not a valid HTTP(S) URL or " - "the given URL is not reachable.") % deploy_iso) - - class IloIscsiDeployMixin(object): @METRICS.timer('IloIscsiDeployMixin.tear_down') @@ -174,7 +48,7 @@ class IloIscsiDeployMixin(object): """ manager_utils.node_power_action(task, states.POWER_OFF) - _disable_secure_boot_if_supported(task) + ilo_boot.disable_secure_boot_if_supported(task) return super(IloIscsiDeployMixin, self).tear_down(task) @METRICS.timer('IloIscsiDeployMixin.prepare_cleaning') @@ -221,7 +95,7 @@ class IloVirtualMediaIscsiDeploy(IloIscsiDeployMixin, :raises: MissingParameterValue if some mandatory information is missing on the node """ - _validate(task) + ilo_boot.validate_driver_info(task) super(IloVirtualMediaIscsiDeploy, self).validate(task) @METRICS.timer('IloVirtualMediaIscsiDeploy.prepare') @@ -232,7 +106,7 @@ class IloVirtualMediaIscsiDeploy(IloIscsiDeployMixin, :raises: IloOperationError, if some operation on iLO failed. """ if task.node.provision_state == states.DEPLOYING: - _prepare_node_for_deploy(task) + ilo_boot.prepare_node_for_deploy(task) super(IloVirtualMediaIscsiDeploy, self).prepare(task) @@ -259,7 +133,7 @@ class IloVirtualMediaAgentDeploy(agent.AgentDeploy): :raises: MissingParameterValue if some mandatory information is missing on the node """ - _validate(task) + ilo_boot.validate_driver_info(task) super(IloVirtualMediaAgentDeploy, self).validate(task) @METRICS.timer('IloVirtualMediaAgentDeploy.tear_down') @@ -272,7 +146,7 @@ class IloVirtualMediaAgentDeploy(agent.AgentDeploy): :raises: IloOperationError, if some operation on iLO failed. """ manager_utils.node_power_action(task, states.POWER_OFF) - _disable_secure_boot_if_supported(task) + ilo_boot.disable_secure_boot_if_supported(task) return super(IloVirtualMediaAgentDeploy, self).tear_down(task) @METRICS.timer('IloVirtualMediaAgentDeploy.prepare') @@ -283,7 +157,7 @@ class IloVirtualMediaAgentDeploy(agent.AgentDeploy): :raises: IloOperationError, if some operation on iLO failed. """ if task.node.provision_state == states.DEPLOYING: - _prepare_node_for_deploy(task) + ilo_boot.prepare_node_for_deploy(task) super(IloVirtualMediaAgentDeploy, self).prepare(task) @@ -338,7 +212,7 @@ class IloPXEDeploy(IloIscsiDeployMixin, iscsi_deploy.ISCSIDeploy): :raises: InvalidParameterValue, if some information is invalid. """ if task.node.provision_state == states.DEPLOYING: - _prepare_node_for_deploy(task) + ilo_boot.prepare_node_for_deploy(task) super(IloPXEDeploy, self).prepare(task) diff --git a/ironic/tests/unit/drivers/modules/ilo/test_boot.py b/ironic/tests/unit/drivers/modules/ilo/test_boot.py index 4dc4fe9987..3f4cee1eb8 100644 --- a/ironic/tests/unit/drivers/modules/ilo/test_boot.py +++ b/ironic/tests/unit/drivers/modules/ilo/test_boot.py @@ -427,6 +427,246 @@ class IloBootPrivateMethodsTestCase(db_base.DbTestCase): actual_info = ilo_boot._parse_deploy_info(self.node) self.assertEqual(expected_info, actual_info) + @mock.patch.object(ilo_common, 'parse_driver_info', spec_set=True, + autospec=True) + def test_validate_driver_info_MissingParam(self, mock_parse_driver_info): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + self.assertRaisesRegex(exception.MissingParameterValue, + "Missing 'ilo_deploy_iso'", + ilo_boot.validate_driver_info, task) + mock_parse_driver_info.assert_called_once_with(task.node) + + @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'parse_driver_info', spec_set=True, + autospec=True) + def test_validate_driver_info_valid_uuid(self, mock_parse_driver_info, + mock_is_glance_image): + mock_is_glance_image.return_value = True + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + deploy_iso = '8a81759a-f29b-454b-8ab3-161c6ca1882c' + task.node.driver_info['ilo_deploy_iso'] = deploy_iso + ilo_boot.validate_driver_info(task) + mock_parse_driver_info.assert_called_once_with(task.node) + mock_is_glance_image.assert_called_once_with(deploy_iso) + + @mock.patch.object(image_service.HttpImageService, 'validate_href', + spec_set=True, autospec=True) + @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'parse_driver_info', spec_set=True, + autospec=True) + def test_validate_driver_info_InvalidParam(self, mock_parse_driver_info, + mock_is_glance_image, + mock_validate_href): + deploy_iso = 'http://abc.org/image/qcow2' + mock_validate_href.side_effect = exception.ImageRefValidationFailed( + image_href='http://abc.org/image/qcow2', reason='fail') + mock_is_glance_image.return_value = False + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.node.driver_info['ilo_deploy_iso'] = deploy_iso + self.assertRaisesRegex(exception.InvalidParameterValue, + "Virtual media boot accepts", + ilo_boot.validate_driver_info, task) + mock_parse_driver_info.assert_called_once_with(task.node) + mock_validate_href.assert_called_once_with(mock.ANY, deploy_iso) + + @mock.patch.object(image_service.HttpImageService, 'validate_href', + spec_set=True, autospec=True) + @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'parse_driver_info', spec_set=True, + autospec=True) + def test_validate_driver_info_valid_url(self, mock_parse_driver_info, + mock_is_glance_image, + mock_validate_href): + deploy_iso = 'http://abc.org/image/deploy.iso' + mock_is_glance_image.return_value = False + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.node.driver_info['ilo_deploy_iso'] = deploy_iso + ilo_boot.validate_driver_info(task) + mock_parse_driver_info.assert_called_once_with(task.node) + mock_validate_href.assert_called_once_with(mock.ANY, deploy_iso) + + @mock.patch.object(deploy_utils, 'validate_image_properties', + spec_set=True, autospec=True) + @mock.patch.object(ilo_boot, '_parse_deploy_info', spec_set=True, + autospec=True) + def _test__validate_instance_image_info(self, + deploy_info_mock, + validate_prop_mock, + props_expected): + d_info = {'image_source': 'uuid'} + deploy_info_mock.return_value = d_info + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + ilo_boot._validate_instance_image_info(task) + deploy_info_mock.assert_called_once_with(task.node) + validate_prop_mock.assert_called_once_with( + task.context, d_info, props_expected) + + @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, + autospec=True) + def test__validate_glance_partition_image(self, + is_glance_image_mock): + is_glance_image_mock.return_value = True + self._test__validate_instance_image_info(props_expected=['kernel_id', + 'ramdisk_id']) + + def test__validate_whole_disk_image(self): + self.node.driver_internal_info = {'is_whole_disk_image': True} + self.node.save() + self._test__validate_instance_image_info(props_expected=[]) + + @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, + autospec=True) + def test__validate_non_glance_partition_image(self, is_glance_image_mock): + is_glance_image_mock.return_value = False + self._test__validate_instance_image_info(props_expected=['kernel', + 'ramdisk']) + + @mock.patch.object(ilo_common, 'set_secure_boot_mode', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'get_secure_boot_mode', spec_set=True, + autospec=True) + def test__disable_secure_boot_false(self, + func_get_secure_boot_mode, + func_set_secure_boot_mode): + func_get_secure_boot_mode.return_value = False + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + returned_state = ilo_boot._disable_secure_boot(task) + func_get_secure_boot_mode.assert_called_once_with(task) + self.assertFalse(func_set_secure_boot_mode.called) + self.assertFalse(returned_state) + + @mock.patch.object(ilo_common, 'set_secure_boot_mode', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'get_secure_boot_mode', spec_set=True, + autospec=True) + def test__disable_secure_boot_true(self, + func_get_secure_boot_mode, + func_set_secure_boot_mode): + func_get_secure_boot_mode.return_value = True + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + returned_state = ilo_boot._disable_secure_boot(task) + func_get_secure_boot_mode.assert_called_once_with(task) + func_set_secure_boot_mode.assert_called_once_with(task, False) + self.assertTrue(returned_state) + + @mock.patch.object(ilo_boot.LOG, 'debug', spec_set=True, autospec=True) + @mock.patch.object(ilo_boot, 'exception', spec_set=True, autospec=True) + @mock.patch.object(ilo_common, 'get_secure_boot_mode', spec_set=True, + autospec=True) + def test__disable_secure_boot_exception(self, + func_get_secure_boot_mode, + exception_mock, + mock_log): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + exception_mock.IloOperationNotSupported = Exception + func_get_secure_boot_mode.side_effect = Exception + returned_state = ilo_boot._disable_secure_boot(task) + func_get_secure_boot_mode.assert_called_once_with(task) + self.assertTrue(mock_log.called) + self.assertFalse(returned_state) + + @mock.patch.object(ilo_common, 'update_boot_mode', spec_set=True, + autospec=True) + @mock.patch.object(ilo_boot, '_disable_secure_boot', spec_set=True, + autospec=True) + @mock.patch.object(manager_utils, 'node_power_action', spec_set=True, + autospec=True) + def test_prepare_node_for_deploy(self, + func_node_power_action, + func_disable_secure_boot, + func_update_boot_mode): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + func_disable_secure_boot.return_value = False + ilo_boot.prepare_node_for_deploy(task) + func_node_power_action.assert_called_once_with(task, + states.POWER_OFF) + func_disable_secure_boot.assert_called_once_with(task) + func_update_boot_mode.assert_called_once_with(task) + bootmode = driver_utils.get_node_capability(task.node, "boot_mode") + self.assertIsNone(bootmode) + + @mock.patch.object(ilo_common, 'update_boot_mode', spec_set=True, + autospec=True) + @mock.patch.object(ilo_boot, '_disable_secure_boot', spec_set=True, + autospec=True) + @mock.patch.object(manager_utils, 'node_power_action', spec_set=True, + autospec=True) + def test_prepare_node_for_deploy_sec_boot_on(self, + func_node_power_action, + func_disable_secure_boot, + func_update_boot_mode): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + func_disable_secure_boot.return_value = True + ilo_boot.prepare_node_for_deploy(task) + func_node_power_action.assert_called_once_with(task, + states.POWER_OFF) + func_disable_secure_boot.assert_called_once_with(task) + self.assertFalse(func_update_boot_mode.called) + ret_boot_mode = task.node.instance_info['deploy_boot_mode'] + self.assertEqual('uefi', ret_boot_mode) + bootmode = driver_utils.get_node_capability(task.node, "boot_mode") + self.assertIsNone(bootmode) + + @mock.patch.object(ilo_common, 'update_boot_mode', spec_set=True, + autospec=True) + @mock.patch.object(ilo_boot, '_disable_secure_boot', spec_set=True, + autospec=True) + @mock.patch.object(manager_utils, 'node_power_action', spec_set=True, + autospec=True) + def test_prepare_node_for_deploy_inst_info(self, + func_node_power_action, + func_disable_secure_boot, + func_update_boot_mode): + instance_info = {'capabilities': '{"secure_boot": "true"}'} + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + func_disable_secure_boot.return_value = False + task.node.instance_info = instance_info + ilo_boot.prepare_node_for_deploy(task) + func_node_power_action.assert_called_once_with(task, + states.POWER_OFF) + func_disable_secure_boot.assert_called_once_with(task) + func_update_boot_mode.assert_called_once_with(task) + bootmode = driver_utils.get_node_capability(task.node, "boot_mode") + self.assertIsNone(bootmode) + self.assertNotIn('deploy_boot_mode', task.node.instance_info) + + @mock.patch.object(ilo_common, 'update_boot_mode', spec_set=True, + autospec=True) + @mock.patch.object(ilo_boot, '_disable_secure_boot', spec_set=True, + autospec=True) + @mock.patch.object(manager_utils, 'node_power_action', spec_set=True, + autospec=True) + def test_prepare_node_for_deploy_sec_boot_on_inst_info( + self, func_node_power_action, func_disable_secure_boot, + func_update_boot_mode): + instance_info = {'capabilities': '{"secure_boot": "true"}'} + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + func_disable_secure_boot.return_value = True + task.node.instance_info = instance_info + ilo_boot.prepare_node_for_deploy(task) + func_node_power_action.assert_called_once_with(task, + states.POWER_OFF) + func_disable_secure_boot.assert_called_once_with(task) + self.assertFalse(func_update_boot_mode.called) + bootmode = driver_utils.get_node_capability(task.node, "boot_mode") + self.assertIsNone(bootmode) + self.assertNotIn('deploy_boot_mode', task.node.instance_info) + class IloVirtualMediaBootTestCase(db_base.DbTestCase): @@ -436,39 +676,20 @@ class IloVirtualMediaBootTestCase(db_base.DbTestCase): self.node = obj_utils.create_test_node( self.context, driver='iscsi_ilo', driver_info=INFO_DICT) - @mock.patch.object(deploy_utils, 'validate_image_properties', + @mock.patch.object(ilo_boot, '_validate_instance_image_info', spec_set=True, autospec=True) - @mock.patch.object(ilo_boot, '_parse_deploy_info', spec_set=True, - autospec=True) - def _test_validate(self, - deploy_info_mock, - validate_prop_mock, - props_expected): - d_info = {'image_source': 'uuid'} - deploy_info_mock.return_value = d_info + def test_validate(self, mock_val_instance_image_info): + instance_info = self.node.instance_info + instance_info['ilo_boot_iso'] = 'deploy-iso' + instance_info['image_source'] = '6b2f0c0c-79e8-4db6-842e-43c9764204af' + self.node.instance_info = instance_info + self.node.save() with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: + + task.node.driver_info['ilo_deploy_iso'] = 'deploy-iso' task.driver.boot.validate(task) - deploy_info_mock.assert_called_once_with(task.node) - validate_prop_mock.assert_called_once_with( - task.context, d_info, props_expected) - - @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, - autospec=True) - def test_validate_glance_partition_image(self, is_glance_image_mock): - is_glance_image_mock.return_value = True - self._test_validate(props_expected=['kernel_id', 'ramdisk_id']) - - def test_validate_whole_disk_image(self): - self.node.driver_internal_info = {'is_whole_disk_image': True} - self.node.save() - self._test_validate(props_expected=[]) - - @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, - autospec=True) - def test_validate_non_glance_partition_image(self, is_glance_image_mock): - is_glance_image_mock.return_value = False - self._test_validate(props_expected=['kernel', 'ramdisk']) + mock_val_instance_image_info.assert_called_once_with(task) @mock.patch.object(ilo_common, 'eject_vmedia_devices', spec_set=True, autospec=True) diff --git a/ironic/tests/unit/drivers/modules/ilo/test_deploy.py b/ironic/tests/unit/drivers/modules/ilo/test_deploy.py index 8c8f516173..1a8bd9ccca 100644 --- a/ironic/tests/unit/drivers/modules/ilo/test_deploy.py +++ b/ironic/tests/unit/drivers/modules/ilo/test_deploy.py @@ -19,18 +19,14 @@ import mock import six from ironic.common import boot_devices -from ironic.common import exception -from ironic.common.glance_service import service_utils -from ironic.common import image_service from ironic.common import states from ironic.conductor import task_manager from ironic.conductor import utils as manager_utils from ironic.conf import CONF from ironic.drivers.modules import agent +from ironic.drivers.modules.ilo import boot as ilo_boot from ironic.drivers.modules.ilo import common as ilo_common -from ironic.drivers.modules.ilo import deploy as ilo_deploy from ironic.drivers.modules import iscsi_deploy -from ironic.drivers import utils as driver_utils from ironic.tests.unit.conductor import mgr_utils from ironic.tests.unit.db import base as db_base from ironic.tests.unit.db import utils as db_utils @@ -44,218 +40,6 @@ if six.PY3: INFO_DICT = db_utils.get_test_ilo_info() -class IloDeployPrivateMethodsTestCase(db_base.DbTestCase): - - def setUp(self): - super(IloDeployPrivateMethodsTestCase, self).setUp() - mgr_utils.mock_the_extension_manager(driver="iscsi_ilo") - self.node = obj_utils.create_test_node( - self.context, driver='iscsi_ilo', driver_info=INFO_DICT) - - @mock.patch.object(ilo_common, 'set_secure_boot_mode', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'get_secure_boot_mode', spec_set=True, - autospec=True) - def test__disable_secure_boot_false(self, - func_get_secure_boot_mode, - func_set_secure_boot_mode): - func_get_secure_boot_mode.return_value = False - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - returned_state = ilo_deploy._disable_secure_boot(task) - func_get_secure_boot_mode.assert_called_once_with(task) - self.assertFalse(func_set_secure_boot_mode.called) - self.assertFalse(returned_state) - - @mock.patch.object(ilo_common, 'set_secure_boot_mode', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'get_secure_boot_mode', spec_set=True, - autospec=True) - def test__disable_secure_boot_true(self, - func_get_secure_boot_mode, - func_set_secure_boot_mode): - func_get_secure_boot_mode.return_value = True - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - returned_state = ilo_deploy._disable_secure_boot(task) - func_get_secure_boot_mode.assert_called_once_with(task) - func_set_secure_boot_mode.assert_called_once_with(task, False) - self.assertTrue(returned_state) - - @mock.patch.object(ilo_deploy.LOG, 'debug', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, 'exception', spec_set=True, autospec=True) - @mock.patch.object(ilo_common, 'get_secure_boot_mode', spec_set=True, - autospec=True) - def test__disable_secure_boot_exception(self, - func_get_secure_boot_mode, - exception_mock, - mock_log): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - exception_mock.IloOperationNotSupported = Exception - func_get_secure_boot_mode.side_effect = Exception - returned_state = ilo_deploy._disable_secure_boot(task) - func_get_secure_boot_mode.assert_called_once_with(task) - self.assertTrue(mock_log.called) - self.assertFalse(returned_state) - - @mock.patch.object(ilo_common, 'update_boot_mode', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_disable_secure_boot', spec_set=True, - autospec=True) - @mock.patch.object(manager_utils, 'node_power_action', spec_set=True, - autospec=True) - def test__prepare_node_for_deploy(self, - func_node_power_action, - func_disable_secure_boot, - func_update_boot_mode): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - func_disable_secure_boot.return_value = False - ilo_deploy._prepare_node_for_deploy(task) - func_node_power_action.assert_called_once_with(task, - states.POWER_OFF) - func_disable_secure_boot.assert_called_once_with(task) - func_update_boot_mode.assert_called_once_with(task) - bootmode = driver_utils.get_node_capability(task.node, "boot_mode") - self.assertIsNone(bootmode) - - @mock.patch.object(ilo_common, 'update_boot_mode', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_disable_secure_boot', spec_set=True, - autospec=True) - @mock.patch.object(manager_utils, 'node_power_action', spec_set=True, - autospec=True) - def test__prepare_node_for_deploy_sec_boot_on(self, - func_node_power_action, - func_disable_secure_boot, - func_update_boot_mode): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - func_disable_secure_boot.return_value = True - ilo_deploy._prepare_node_for_deploy(task) - func_node_power_action.assert_called_once_with(task, - states.POWER_OFF) - func_disable_secure_boot.assert_called_once_with(task) - self.assertFalse(func_update_boot_mode.called) - ret_boot_mode = task.node.instance_info['deploy_boot_mode'] - self.assertEqual('uefi', ret_boot_mode) - bootmode = driver_utils.get_node_capability(task.node, "boot_mode") - self.assertIsNone(bootmode) - - @mock.patch.object(ilo_common, 'update_boot_mode', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_disable_secure_boot', spec_set=True, - autospec=True) - @mock.patch.object(manager_utils, 'node_power_action', spec_set=True, - autospec=True) - def test__prepare_node_for_deploy_inst_info(self, - func_node_power_action, - func_disable_secure_boot, - func_update_boot_mode): - instance_info = {'capabilities': '{"secure_boot": "true"}'} - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - func_disable_secure_boot.return_value = False - task.node.instance_info = instance_info - ilo_deploy._prepare_node_for_deploy(task) - func_node_power_action.assert_called_once_with(task, - states.POWER_OFF) - func_disable_secure_boot.assert_called_once_with(task) - func_update_boot_mode.assert_called_once_with(task) - bootmode = driver_utils.get_node_capability(task.node, "boot_mode") - self.assertIsNone(bootmode) - self.assertNotIn('deploy_boot_mode', task.node.instance_info) - - @mock.patch.object(ilo_common, 'update_boot_mode', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_disable_secure_boot', spec_set=True, - autospec=True) - @mock.patch.object(manager_utils, 'node_power_action', spec_set=True, - autospec=True) - def test__prepare_node_for_deploy_sec_boot_on_inst_info( - self, func_node_power_action, func_disable_secure_boot, - func_update_boot_mode): - instance_info = {'capabilities': '{"secure_boot": "true"}'} - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - func_disable_secure_boot.return_value = True - task.node.instance_info = instance_info - ilo_deploy._prepare_node_for_deploy(task) - func_node_power_action.assert_called_once_with(task, - states.POWER_OFF) - func_disable_secure_boot.assert_called_once_with(task) - self.assertFalse(func_update_boot_mode.called) - bootmode = driver_utils.get_node_capability(task.node, "boot_mode") - self.assertIsNone(bootmode) - self.assertNotIn('deploy_boot_mode', task.node.instance_info) - - @mock.patch.object(ilo_common, 'parse_driver_info', spec_set=True, - autospec=True) - def test__validate_MissingParam(self, mock_parse_driver_info): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - self.assertRaisesRegex(exception.MissingParameterValue, - "Missing 'ilo_deploy_iso'", - ilo_deploy._validate, task) - mock_parse_driver_info.assert_called_once_with(task.node) - - @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'parse_driver_info', spec_set=True, - autospec=True) - def test__validate_valid_uuid(self, mock_parse_driver_info, - mock_is_glance_image): - mock_is_glance_image.return_value = True - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - deploy_iso = '8a81759a-f29b-454b-8ab3-161c6ca1882c' - task.node.driver_info['ilo_deploy_iso'] = deploy_iso - ilo_deploy._validate(task) - mock_parse_driver_info.assert_called_once_with(task.node) - mock_is_glance_image.assert_called_once_with(deploy_iso) - - @mock.patch.object(image_service.HttpImageService, 'validate_href', - spec_set=True, autospec=True) - @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'parse_driver_info', spec_set=True, - autospec=True) - def test__validate_InvalidParam(self, mock_parse_driver_info, - mock_is_glance_image, - mock_validate_href): - deploy_iso = 'http://abc.org/image/qcow2' - mock_validate_href.side_effect = exception.ImageRefValidationFailed( - image_href='http://abc.org/image/qcow2', reason='fail') - mock_is_glance_image.return_value = False - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.node.driver_info['ilo_deploy_iso'] = deploy_iso - self.assertRaisesRegex(exception.InvalidParameterValue, - "Virtual media deploy accepts", - ilo_deploy._validate, task) - mock_parse_driver_info.assert_called_once_with(task.node) - mock_validate_href.assert_called_once_with(mock.ANY, deploy_iso) - - @mock.patch.object(image_service.HttpImageService, 'validate_href', - spec_set=True, autospec=True) - @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'parse_driver_info', spec_set=True, - autospec=True) - def test__validate_valid_url(self, mock_parse_driver_info, - mock_is_glance_image, - mock_validate_href): - deploy_iso = 'http://abc.org/image/deploy.iso' - mock_is_glance_image.return_value = False - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.node.driver_info['ilo_deploy_iso'] = deploy_iso - ilo_deploy._validate(task) - mock_parse_driver_info.assert_called_once_with(task.node) - mock_validate_href.assert_called_once_with(mock.ANY, deploy_iso) - - class IloVirtualMediaIscsiDeployTestCase(db_base.DbTestCase): def setUp(self): @@ -266,15 +50,15 @@ class IloVirtualMediaIscsiDeployTestCase(db_base.DbTestCase): @mock.patch.object(iscsi_deploy.ISCSIDeploy, 'validate', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_validate', spec_set=True, + @mock.patch.object(ilo_boot, 'validate_driver_info', spec_set=True, autospec=True) def test_validate(self, - mock_validate, + mock_validate_driver_info, iscsi_validate): with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: task.driver.deploy.validate(task) - mock_validate.assert_called_once_with(task) + mock_validate_driver_info.assert_called_once_with(task) iscsi_validate.assert_called_once_with(mock.ANY, task) @mock.patch.object(ilo_common, 'update_secure_boot_mode', spec_set=True, @@ -297,9 +81,9 @@ class IloVirtualMediaIscsiDeployTestCase(db_base.DbTestCase): iscsi_tear_down_mock.assert_called_once_with(mock.ANY, task) self.assertEqual(states.DELETED, returned_state) - @mock.patch.object(ilo_deploy.LOG, 'warning', + @mock.patch.object(ilo_boot.LOG, 'warning', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, 'exception', spec_set=True, autospec=True) + @mock.patch.object(ilo_boot, 'exception', spec_set=True, autospec=True) @mock.patch.object(iscsi_deploy.ISCSIDeploy, 'tear_down', spec_set=True, autospec=True) @mock.patch.object(ilo_common, 'update_secure_boot_mode', spec_set=True, @@ -335,7 +119,7 @@ class IloVirtualMediaIscsiDeployTestCase(db_base.DbTestCase): @mock.patch.object(iscsi_deploy.ISCSIDeploy, 'prepare', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_prepare_node_for_deploy', spec_set=True, + @mock.patch.object(ilo_boot, 'prepare_node_for_deploy', spec_set=True, autospec=True) def test_prepare(self, func_prepare_node_for_deploy, iscsi_deploy_prepare_mock): @@ -349,7 +133,7 @@ class IloVirtualMediaIscsiDeployTestCase(db_base.DbTestCase): @mock.patch.object(iscsi_deploy.ISCSIDeploy, 'prepare', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_prepare_node_for_deploy', spec_set=True, + @mock.patch.object(ilo_boot, 'prepare_node_for_deploy', spec_set=True, autospec=True) def test_prepare_active_node(self, func_prepare_node_for_deploy, iscsi_deploy_prepare_mock): @@ -414,15 +198,15 @@ class IloVirtualMediaAgentDeployTestCase(db_base.DbTestCase): @mock.patch.object(agent.AgentDeploy, 'validate', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_validate', spec_set=True, + @mock.patch.object(ilo_boot, 'validate_driver_info', spec_set=True, autospec=True) def test_validate(self, - mock_validate, + mock_validate_driver_info, agent_validate): with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: task.driver.deploy.validate(task) - mock_validate.assert_called_once_with(task) + mock_validate_driver_info.assert_called_once_with(task) agent_validate.assert_called_once_with(mock.ANY, task) @mock.patch.object(agent.AgentDeploy, 'tear_down', spec_set=True, @@ -446,8 +230,8 @@ class IloVirtualMediaAgentDeployTestCase(db_base.DbTestCase): @mock.patch.object(agent.AgentDeploy, 'tear_down', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy.LOG, 'warning', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, 'exception', spec_set=True, autospec=True) + @mock.patch.object(ilo_boot.LOG, 'warning', spec_set=True, autospec=True) + @mock.patch.object(ilo_boot, 'exception', spec_set=True, autospec=True) @mock.patch.object(ilo_common, 'update_secure_boot_mode', spec_set=True, autospec=True) @mock.patch.object(manager_utils, 'node_power_action', spec_set=True, @@ -471,7 +255,7 @@ class IloVirtualMediaAgentDeployTestCase(db_base.DbTestCase): self.assertTrue(mock_log.called) self.assertEqual(states.DELETED, returned_state) - @mock.patch.object(ilo_deploy, '_prepare_node_for_deploy', spec_set=True, + @mock.patch.object(ilo_boot, 'prepare_node_for_deploy', spec_set=True, autospec=True) @mock.patch.object(agent.AgentDeploy, 'prepare', spec_set=True, autospec=True) @@ -488,7 +272,7 @@ class IloVirtualMediaAgentDeployTestCase(db_base.DbTestCase): @mock.patch.object(agent.AgentDeploy, 'prepare', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_prepare_node_for_deploy', spec_set=True, + @mock.patch.object(ilo_boot, 'prepare_node_for_deploy', spec_set=True, autospec=True) def test_prepare_active_node(self, func_prepare_node_for_deploy, @@ -589,7 +373,7 @@ class IloPXEDeployTestCase(db_base.DbTestCase): @mock.patch.object(iscsi_deploy.ISCSIDeploy, 'prepare', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_prepare_node_for_deploy', spec_set=True, + @mock.patch.object(ilo_boot, 'prepare_node_for_deploy', spec_set=True, autospec=True) def test_prepare(self, prepare_node_for_deploy_mock, @@ -605,7 +389,7 @@ class IloPXEDeployTestCase(db_base.DbTestCase): @mock.patch.object(iscsi_deploy.ISCSIDeploy, 'prepare', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_prepare_node_for_deploy', spec_set=True, + @mock.patch.object(ilo_boot, 'prepare_node_for_deploy', spec_set=True, autospec=True) def test_prepare_active_node(self, prepare_node_for_deploy_mock, @@ -629,7 +413,7 @@ class IloPXEDeployTestCase(db_base.DbTestCase): @mock.patch.object(iscsi_deploy.ISCSIDeploy, 'prepare', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_prepare_node_for_deploy', spec_set=True, + @mock.patch.object(ilo_boot, 'prepare_node_for_deploy', spec_set=True, autospec=True) def test_prepare_whole_disk_image_uefi(self, prepare_node_for_deploy_mock, pxe_prepare_mock): @@ -673,11 +457,11 @@ class IloPXEDeployTestCase(db_base.DbTestCase): pxe_tear_down_mock.assert_called_once_with(mock.ANY, task) self.assertEqual(states.DELETED, returned_state) - @mock.patch.object(ilo_deploy.LOG, 'warning', + @mock.patch.object(ilo_boot.LOG, 'warning', spec_set=True, autospec=True) @mock.patch.object(iscsi_deploy.ISCSIDeploy, 'tear_down', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, 'exception', spec_set=True, autospec=True) + @mock.patch.object(ilo_boot, 'exception', spec_set=True, autospec=True) @mock.patch.object(ilo_common, 'update_secure_boot_mode', spec_set=True, autospec=True) @mock.patch.object(manager_utils, 'node_power_action', spec_set=True,