From 991f9ccec1679fee02a8db0ec96adaf050fa6af6 Mon Sep 17 00:00:00 2001 From: Shivanand Tendulker Date: Fri, 2 Aug 2019 02:47:25 -0400 Subject: [PATCH] Add deploy steps for Redfish BIOS interface Change-Id: I49b27921736d30ad40b31dd69147d70fc901b4a4 Co-Authored-By: Shivanand Tendulker Story: 1722275 Task: 29904 --- ironic/drivers/modules/redfish/bios.py | 55 ++++-- .../unit/drivers/modules/redfish/test_bios.py | 186 ++++++++++++------ ...dfish-bios-interface-f5e5415108f87598.yaml | 6 + 3 files changed, 165 insertions(+), 82 deletions(-) create mode 100644 releasenotes/notes/add-deploy-steps-redfish-bios-interface-f5e5415108f87598.yaml diff --git a/ironic/drivers/modules/redfish/bios.py b/ironic/drivers/modules/redfish/bios.py index f80714aedf..a05179555b 100644 --- a/ironic/drivers/modules/redfish/bios.py +++ b/ironic/drivers/modules/redfish/bios.py @@ -35,6 +35,15 @@ sushy = importutils.try_import('sushy') class RedfishBIOS(base.BIOSInterface): + _APPLY_CONFIGURATION_ARGSINFO = { + 'settings': { + 'description': ( + 'A list of BIOS settings to be applied' + ), + 'required': True + } + } + def __init__(self): super(RedfishBIOS, self).__init__() if sushy is None: @@ -89,6 +98,7 @@ class RedfishBIOS(base.BIOSInterface): task.context, node_id, delete_names) @base.clean_step(priority=0) + @base.deploy_step(priority=0) @base.cache_bios_settings def factory_reset(self, task): """Reset the BIOS settings of the node to the factory default. @@ -123,8 +133,9 @@ class RedfishBIOS(base.BIOSInterface): raise exception.RedfishError(error=error_msg) self.post_reset(task) - self._set_cleaning_reboot(task) - return states.CLEANWAIT + self._set_reboot(task) + return (states.CLEANWAIT if + task.node.clean_step else states.DEPLOYWAIT) else: current_attrs = bios.attributes LOG.debug('Post factory reset, BIOS configuration for node ' @@ -132,14 +143,8 @@ class RedfishBIOS(base.BIOSInterface): {'node_uuid': node.uuid, 'attrs': current_attrs}) self._clear_reboot_requested(task) - @base.clean_step(priority=0, argsinfo={ - 'settings': { - 'description': ( - 'A list of BIOS settings to be applied' - ), - 'required': True - } - }) + @base.clean_step(priority=0, argsinfo=_APPLY_CONFIGURATION_ARGSINFO) + @base.deploy_step(priority=0, argsinfo=_APPLY_CONFIGURATION_ARGSINFO) @base.cache_bios_settings def apply_configuration(self, task, settings): """Apply the BIOS settings to the node. @@ -182,7 +187,8 @@ class RedfishBIOS(base.BIOSInterface): self.post_configuration(task, settings) self._set_reboot_requested(task, attributes) - return states.CLEANWAIT + return (states.CLEANWAIT if + task.node.clean_step else states.DEPLOYWAIT) else: # Step 2: Verify requested BIOS settings applied requested_attrs = info.get('requested_bios_attrs') @@ -255,7 +261,7 @@ class RedfishBIOS(base.BIOSInterface): LOG.debug('BIOS settings %(attrs)s for node %(node_uuid)s ' 'not updated.', {'attrs': attrs_not_updated, 'node_uuid': task.node.uuid}) - self._set_clean_failed(task, attrs_not_updated) + self._set_step_failed(task, attrs_not_updated) else: LOG.debug('Verification of BIOS settings for node %(node_uuid)s ' 'successful.', {'node_uuid': task.node.uuid}) @@ -271,15 +277,18 @@ class RedfishBIOS(base.BIOSInterface): """ manager_utils.node_power_action(task, states.REBOOT) - def _set_cleaning_reboot(self, task): - """Set driver_internal_info flags for cleaning reboot. + def _set_reboot(self, task): + """Set driver_internal_info flags for deployment or cleaning reboot. :param task: a TaskManager instance containing the node to act on. """ info = task.node.driver_internal_info info['post_factory_reset_reboot_requested'] = True - info['cleaning_reboot'] = True - info['skip_current_clean_step'] = False + cleaning = ['cleaning_reboot', 'skip_current_clean_step'] + deployment = ['deployment_reboot', 'skip_current_deploy_step'] + field_name = cleaning if task.node.clean_step else deployment + info[field_name[0]] = True + info[field_name[1]] = False task.node.driver_internal_info = info task.node.save() @@ -291,9 +300,12 @@ class RedfishBIOS(base.BIOSInterface): """ info = task.node.driver_internal_info info['post_config_reboot_requested'] = True - info['cleaning_reboot'] = True info['requested_bios_attrs'] = attributes - info['skip_current_clean_step'] = False + cleaning = ['cleaning_reboot', 'skip_current_clean_step'] + deployment = ['deployment_reboot', 'skip_current_deploy_step'] + field_name = cleaning if task.node.clean_step else deployment + info[field_name[0]] = True + info[field_name[1]] = False task.node.driver_internal_info = info task.node.save() @@ -309,8 +321,8 @@ class RedfishBIOS(base.BIOSInterface): task.node.driver_internal_info = info task.node.save() - def _set_clean_failed(self, task, attrs_not_updated): - """Fail the cleaning step and log the error. + def _set_step_failed(self, task, attrs_not_updated): + """Fail the cleaning or deployment step and log the error. :param task: a TaskManager instance containing the node to act on. :param attrs_not_updated: the BIOS attributes that were not updated. @@ -323,5 +335,6 @@ class RedfishBIOS(base.BIOSInterface): {'attrs': attrs_not_updated}) LOG.error(error_msg) task.node.last_error = last_error - if task.node.provision_state in [states.CLEANING, states.CLEANWAIT]: + if task.node.provision_state in [states.CLEANING, states.CLEANWAIT, + states.DEPLOYING, states.DEPLOYWAIT]: task.process_event('fail') diff --git a/ironic/tests/unit/drivers/modules/redfish/test_bios.py b/ironic/tests/unit/drivers/modules/redfish/test_bios.py index 80f6fc0f50..228d148667 100644 --- a/ironic/tests/unit/drivers/modules/redfish/test_bios.py +++ b/ironic/tests/unit/drivers/modules/redfish/test_bios.py @@ -165,36 +165,146 @@ class RedfishBiosTestCase(db_base.DbTestCase): @mock.patch.object(deploy_utils, 'build_agent_options', autospec=True) @mock.patch.object(redfish_utils, 'get_system', autospec=True) @mock.patch.object(manager_utils, 'node_power_action', autospec=True) - def test_factory_reset_step1(self, mock_power_action, mock_get_system, - mock_build_agent_options, mock_prepare): + def _test_step_pre_reboot(self, mock_power_action, mock_get_system, + mock_build_agent_options, mock_prepare): + if self.node.clean_step: + step_data = self.node.clean_step + check_fields = ['cleaning_reboot', 'skip_current_clean_step'] + expected_ret = states.CLEANWAIT + else: + step_data = self.node.deploy_step + check_fields = ['deployment_reboot', 'skip_current_deploy_step'] + expected_ret = states.DEPLOYWAIT + data = step_data['argsinfo'].get('settings', None) + step = step_data['step'] + if step == 'factory_reset': + check_fields.append('post_factory_reset_reboot_requested') + elif step == 'apply_configuration': + check_fields.append('post_config_reboot_requested') + attributes = {s['name']: s['value'] for s in data} mock_build_agent_options.return_value = {'a': 'b'} with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: - task.driver.bios.factory_reset(task) + if step == 'factory_reset': + ret = task.driver.bios.factory_reset(task) + if step == 'apply_configuration': + ret = task.driver.bios.apply_configuration(task, data) mock_get_system.assert_called_with(task.node) mock_power_action.assert_called_once_with(task, states.REBOOT) bios = mock_get_system(task.node).bios - bios.reset_bios.assert_called_once() + if step == 'factory_reset': + bios.reset_bios.assert_called_once() + if step == 'apply_configuration': + bios.set_attributes.assert_called_once_with(attributes) mock_build_agent_options.assert_called_once_with(task.node) mock_prepare.assert_called_once_with(mock.ANY, task, {'a': 'b'}) info = task.node.driver_internal_info - self.assertTrue( - all(x in info for x in ( - 'post_factory_reset_reboot_requested', 'cleaning_reboot', - 'skip_current_clean_step'))) + self.assertTrue(all(x in info for x in check_fields)) + self.assertEqual(expected_ret, ret) + + def test_factory_reset_step_pre_reboot_cleaning(self): + self.node.clean_step = {'priority': 100, 'interface': 'bios', + 'step': 'factory_reset', 'argsinfo': {}} + self.node.save() + self._test_step_pre_reboot() + + def test_factory_reset_step_pre_reboot_deploying(self): + self.node.deploy_step = {'priority': 100, 'interface': 'bios', + 'step': 'factory_reset', 'argsinfo': {}} + self.node.save() + self._test_step_pre_reboot() + + def test_apply_conf_step_pre_reboot_cleaning(self): + data = [{'name': 'ProcTurboMode', 'value': 'Disabled'}, + {'name': 'NicBoot1', 'value': 'NetworkBoot'}] + self.node.clean_step = {'priority': 100, 'interface': 'bios', + 'step': 'apply_configuration', + 'argsinfo': {'settings': data}} + self.node.save() + self._test_step_pre_reboot() + + def test_apply_conf_step_pre_reboot_deploying(self): + data = [{'name': 'ProcTurboMode', 'value': 'Disabled'}, + {'name': 'NicBoot1', 'value': 'NetworkBoot'}] + self.node.deploy_step = {'priority': 100, 'interface': 'bios', + 'step': 'apply_configuration', + 'argsinfo': {'settings': data}} + self.node.save() + self._test_step_pre_reboot() @mock.patch.object(redfish_utils, 'get_system', autospec=True) - def test_factory_reset_step2(self, mock_get_system): + def _test_step_post_reboot(self, mock_get_system): + if self.node.deploy_step: + step_data = self.node.deploy_step + else: + step_data = self.node.clean_step + data = step_data['argsinfo'].get('settings', None) + step = step_data['step'] + if step == 'factory_reset': + check_fields = ['post_factory_reset_reboot_requested'] + if step == 'apply_configuration': + check_fields = ['post_config_reboot_requested', + 'requested_bios_attrs'] with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: - driver_internal_info = task.node.driver_internal_info - driver_internal_info['post_factory_reset_reboot_requested'] = True - task.node.driver_internal_info = driver_internal_info - task.node.save() - task.driver.bios.factory_reset(task) + if step == 'factory_reset': + task.driver.bios.factory_reset(task) + if step == 'apply_configuration': + task.driver.bios.apply_configuration(task, data) mock_get_system.assert_called_with(task.node) info = task.node.driver_internal_info - self.assertNotIn('post_factory_reset_reboot_requested', info) + for field in check_fields: + self.assertNotIn(field, info) + + def test_factory_reset_post_reboot_cleaning(self): + self.node.clean_step = {'priority': 100, 'interface': 'bios', + 'step': 'factory_reset', 'argsinfo': {}} + node = self.node + driver_internal_info = node.driver_internal_info + driver_internal_info['post_factory_reset_reboot_requested'] = True + node.driver_internal_info = driver_internal_info + node.save() + self._test_step_post_reboot() + + def test_factory_reset_post_reboot_deploying(self): + self.node.deploy_step = {'priority': 100, 'interface': 'bios', + 'step': 'factory_reset', 'argsinfo': {}} + node = self.node + driver_internal_info = node.driver_internal_info + driver_internal_info['post_factory_reset_reboot_requested'] = True + node.driver_internal_info = driver_internal_info + node.save() + self._test_step_post_reboot() + + def test_apply_conf_post_reboot_cleaning(self): + data = [{'name': 'ProcTurboMode', 'value': 'Disabled'}, + {'name': 'NicBoot1', 'value': 'NetworkBoot'}] + self.node.clean_step = {'priority': 100, 'interface': 'bios', + 'step': 'apply_configuration', + 'argsinfo': {'settings': data}} + requested_attrs = {'ProcTurboMode': 'Enabled'} + node = self.node + driver_internal_info = node.driver_internal_info + driver_internal_info['post_config_reboot_requested'] = True + driver_internal_info['requested_bios_attrs'] = requested_attrs + self.node.driver_internal_info = driver_internal_info + self.node.save() + self._test_step_post_reboot() + + def test_apply_conf_post_reboot_deploying(self): + data = [{'name': 'ProcTurboMode', 'value': 'Disabled'}, + {'name': 'NicBoot1', 'value': 'NetworkBoot'}] + self.node.deploy_step = {'priority': 100, 'interface': 'bios', + 'step': 'apply_configuration', + 'argsinfo': {'settings': data}} + requested_attrs = {'ProcTurboMode': 'Enabled'} + node = self.node + driver_internal_info = node.driver_internal_info + driver_internal_info['post_config_reboot_requested'] = True + driver_internal_info['requested_bios_attrs'] = requested_attrs + self.node.driver_internal_info = driver_internal_info + self.node.save() + self._test_step_post_reboot() @mock.patch.object(redfish_utils, 'get_system', autospec=True) def test_factory_reset_fail(self, mock_get_system): @@ -215,52 +325,6 @@ class RedfishBiosTestCase(db_base.DbTestCase): exception.RedfishError, 'BIOS factory reset failed', task.driver.bios.factory_reset, task) - @mock.patch.object(pxe_boot.PXEBoot, 'prepare_ramdisk', - spec_set=True, autospec=True) - @mock.patch.object(deploy_utils, 'build_agent_options', autospec=True) - @mock.patch.object(redfish_utils, 'get_system', autospec=True) - @mock.patch.object(manager_utils, 'node_power_action', autospec=True) - def test_apply_configuration_step1(self, mock_power_action, - mock_get_system, - mock_build_agent_options, - mock_prepare): - settings = [{'name': 'ProcTurboMode', 'value': 'Disabled'}, - {'name': 'NicBoot1', 'value': 'NetworkBoot'}] - attributes = {s['name']: s['value'] for s in settings} - mock_build_agent_options.return_value = {'a': 'b'} - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.bios.apply_configuration(task, settings) - mock_get_system.assert_called_with(task.node) - mock_power_action.assert_called_once_with(task, states.REBOOT) - bios = mock_get_system(task.node).bios - bios.set_attributes.assert_called_once_with(attributes) - mock_build_agent_options.assert_called_once_with(task.node) - mock_prepare.assert_called_once_with(mock.ANY, task, {'a': 'b'}) - info = task.node.driver_internal_info - self.assertTrue( - all(x in info for x in ( - 'post_config_reboot_requested', 'cleaning_reboot', - 'skip_current_clean_step'))) - - @mock.patch.object(redfish_utils, 'get_system', autospec=True) - def test_apply_configuration_step2(self, mock_get_system): - settings = [{'name': 'ProcTurboMode', 'value': 'Disabled'}, - {'name': 'NicBoot1', 'value': 'NetworkBoot'}] - requested_attrs = {'ProcTurboMode': 'Enabled'} - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - driver_internal_info = task.node.driver_internal_info - driver_internal_info['post_config_reboot_requested'] = True - driver_internal_info['requested_bios_attrs'] = requested_attrs - task.node.driver_internal_info = driver_internal_info - task.node.save() - task.driver.bios.apply_configuration(task, settings) - mock_get_system.assert_called_with(task.node) - info = task.node.driver_internal_info - self.assertNotIn('post_config_reboot_requested', info) - self.assertNotIn('requested_bios_attrs', info) - @mock.patch.object(redfish_utils, 'get_system', autospec=True) def test_apply_configuration_not_supported(self, mock_get_system): settings = [{'name': 'ProcTurboMode', 'value': 'Disabled'}, diff --git a/releasenotes/notes/add-deploy-steps-redfish-bios-interface-f5e5415108f87598.yaml b/releasenotes/notes/add-deploy-steps-redfish-bios-interface-f5e5415108f87598.yaml new file mode 100644 index 0000000000..00b8b1e8be --- /dev/null +++ b/releasenotes/notes/add-deploy-steps-redfish-bios-interface-f5e5415108f87598.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Adds support for deploy steps to ``bios`` interface of ``redfish`` + hardware type. The methods ``factory_reset`` and ``apply_configuration`` + can be used as deploy steps.