From a5ce4dd8d0b6cc383a5c82ef622cde12c16b2918 Mon Sep 17 00:00:00 2001 From: Shivanand Tendulker Date: Tue, 21 Jul 2020 12:46:10 -0400 Subject: [PATCH] Adds raid validation for in-band AgentRAID deploy step This commit adds support for validation of raid configuration of in-band AgentRAID deploy step 'apply_configuration' and adds a post deploy hook to update root device hint. Change-Id: I52c1ad3e10d9fab3c2366d40af39667a299eb774 --- ironic/drivers/modules/agent.py | 52 +++++++++++++++++-- .../tests/unit/drivers/modules/test_agent.py | 52 ++++++++++++++++--- .../agent-raid-validate-f7348ac034606b83.yaml | 6 +++ 3 files changed, 97 insertions(+), 13 deletions(-) create mode 100644 releasenotes/notes/agent-raid-validate-f7348ac034606b83.yaml diff --git a/ironic/drivers/modules/agent.py b/ironic/drivers/modules/agent.py index 9d5243533e..08eaf0266f 100644 --- a/ironic/drivers/modules/agent.py +++ b/ironic/drivers/modules/agent.py @@ -59,6 +59,21 @@ OPTIONAL_PROPERTIES = { '``image_https_proxy`` are not specified. Optional.'), } +_RAID_APPLY_CONFIGURATION_ARGSINFO = { + "raid_config": { + "description": "The RAID configuration to apply.", + "required": True, + }, + "delete_existing": { + "description": ( + "Setting this to 'True' indicates to delete existing RAID " + "configuration prior to creating the new configuration. " + "Default value is 'True'." + ), + "required": False, + } +} + COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy() COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES) COMMON_PROPERTIES.update(agent_base.VENDOR_PROPERTIES) @@ -634,6 +649,25 @@ class AgentRAID(base.RAIDInterface): """ return agent_base.get_steps(task, 'deploy', interface='raid') + @METRICS.timer('AgentRAID.apply_configuration') + @base.deploy_step(priority=0, + argsinfo=_RAID_APPLY_CONFIGURATION_ARGSINFO) + def apply_configuration(self, task, raid_config, + delete_existing=True): + """Applies RAID configuration on the given node. + + :param task: A TaskManager instance. + :param raid_config: The RAID configuration to apply. + :param delete_existing: Setting this to True indicates to delete RAID + configuration prior to creating the new configuration. + :raises: InvalidParameterValue, if the RAID configuration is invalid. + :returns: states.DEPLOYWAIT if RAID configuration is in progress + asynchronously or None if it is complete. + """ + self.validate_raid_config(task, raid_config) + step = task.node.deploy_step + return agent_base.execute_step(task, step, 'deploy') + @METRICS.timer('AgentRAID.create_configuration') @base.clean_step(priority=0) def create_configuration(self, task, @@ -682,6 +716,8 @@ class AgentRAID(base.RAIDInterface): @staticmethod @agent_base.post_clean_step_hook( interface='raid', step='create_configuration') + @agent_base.post_deploy_step_hook( + interface='raid', step='apply_configuration') def _create_configuration_final(task, command): """Clean step hook after a RAID configuration was created. @@ -699,15 +735,21 @@ class AgentRAID(base.RAIDInterface): the 'command' argument passed. """ try: - clean_result = command['command_result']['clean_result'] + if task.node.provision_state == states.DEPLOYWAIT: + operation = "deploying" + result = command['command_result']['deploy_result'] + else: + operation = "cleaning" + result = command['command_result']['clean_result'] except KeyError: raise exception.IronicException( _("Agent ramdisk didn't return a proper command result while " - "cleaning %(node)s. It returned '%(result)s' after command " - "execution.") % {'node': task.node.uuid, - 'result': command}) + "%(operation)s %(node)s. It returned '%(result)s' after " + "command execution.") % {'operation': operation, + 'node': task.node.uuid, + 'result': command}) - raid.update_raid_info(task.node, clean_result) + raid.update_raid_info(task.node, result) @METRICS.timer('AgentRAID.delete_configuration') @base.clean_step(priority=0) diff --git a/ironic/tests/unit/drivers/modules/test_agent.py b/ironic/tests/unit/drivers/modules/test_agent.py index ecc734a9d4..7c22a584b3 100644 --- a/ironic/tests/unit/drivers/modules/test_agent.py +++ b/ironic/tests/unit/drivers/modules/test_agent.py @@ -1618,8 +1618,8 @@ class AgentRAIDTestCase(db_base.DbTestCase): self.config(enabled_raid_interfaces=['fake', 'agent', 'no-raid']) self.target_raid_config = { "logical_disks": [ - {'size_gb': 200, 'raid_level': 0, 'is_root_volume': True}, - {'size_gb': 200, 'raid_level': 5} + {'size_gb': 200, 'raid_level': "0", 'is_root_volume': True}, + {'size_gb': 200, 'raid_level': "5"} ]} self.clean_step = {'step': 'create_configuration', 'interface': 'raid'} @@ -1661,6 +1661,25 @@ class AgentRAIDTestCase(db_base.DbTestCase): self.assertEqual('apply_configuration', ret[0]['step']) + @mock.patch.object(agent_base, 'execute_step', autospec=True) + def test_apply_configuration(self, execute_mock): + deploy_step = { + 'interface': 'raid', + 'step': 'apply_configuration', + 'args': { + 'raid_config': self.target_raid_config, + 'delete_existing': True + }, + 'priority': 82 + } + with task_manager.acquire(self.context, self.node.uuid) as task: + execute_mock.return_value = states.DEPLOYWAIT + task.node.deploy_step = deploy_step + return_value = task.driver.raid.apply_configuration( + task, self.target_raid_config, delete_existing=True) + self.assertEqual(states.DEPLOYWAIT, return_value) + execute_mock.assert_called_once_with(task, deploy_step, 'deploy') + @mock.patch.object(raid, 'filter_target_raid_config', autospec=True) @mock.patch.object(agent_base, 'execute_step', autospec=True) def test_create_configuration(self, execute_mock, @@ -1761,16 +1780,33 @@ class AgentRAIDTestCase(db_base.DbTestCase): update_raid_info_mock.assert_called_once_with(task.node, 'foo') @mock.patch.object(raid, 'update_raid_info', autospec=True) - def test__create_configuration_final_registered( - self, update_raid_info_mock): - self.node.clean_step = {'interface': 'raid', - 'step': 'create_configuration'} - command = {'command_result': {'clean_result': 'foo'}} - create_hook = agent_base._get_post_step_hook(self.node, 'clean') + def _test__create_configuration_final_registered( + self, update_raid_info_mock, step_type='clean'): + step = {'interface': 'raid'} + if step_type == 'clean': + step['step'] = 'create_configuration' + self.node.clean_step = step + state = states.CLEANWAIT + command = {'command_result': {'clean_result': 'foo'}} + create_hook = agent_base._get_post_step_hook(self.node, 'clean') + else: + step['step'] = 'apply_configuration' + self.node.deploy_step = step + command = {'command_result': {'deploy_result': 'foo'}} + state = states.DEPLOYWAIT + create_hook = agent_base._get_post_step_hook(self.node, 'deploy') + with task_manager.acquire(self.context, self.node.uuid) as task: + task.node.provision_state = state create_hook(task, command) update_raid_info_mock.assert_called_once_with(task.node, 'foo') + def test__create_configuration_final_registered_clean(self): + self._test__create_configuration_final_registered(step_type='clean') + + def test__create_configuration_final_registered_deploy(self): + self._test__create_configuration_final_registered(step_type='deploy') + @mock.patch.object(raid, 'update_raid_info', autospec=True) def test__create_configuration_final_bad_command_result( self, update_raid_info_mock): diff --git a/releasenotes/notes/agent-raid-validate-f7348ac034606b83.yaml b/releasenotes/notes/agent-raid-validate-f7348ac034606b83.yaml new file mode 100644 index 0000000000..54a3f0a108 --- /dev/null +++ b/releasenotes/notes/agent-raid-validate-f7348ac034606b83.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Adds raid configuration validation to deploy step ``apply_configuration`` + of ``agent`` RAID interface. Also, a post deploy hook has been added to + this deploy step to update root device hint.