From b1986361a8a8badf837d12227a32ff6245a1ec21 Mon Sep 17 00:00:00 2001 From: Shivanand Tendulker Date: Fri, 2 Aug 2019 01:49:57 -0400 Subject: [PATCH] Add new method 'apply_configuration' to RAIDInterface This commit adds new method 'apply_configuration' to RAIDInterface. This method would be used as deploy step and would accept the target raid config as argsinfo argument. Co-Authored-By: Mark Goddard Change-Id: If50294f5413e67bb333ebba53fb3dab251f5317d Story: 2003817 Task: 26571 Task: 30004 Task: 36061 --- ironic/drivers/base.py | 86 ++++++++++++++++++++++++-- ironic/tests/unit/drivers/test_base.py | 66 +++++++++++++++++++- 2 files changed, 146 insertions(+), 6 deletions(-) diff --git a/ironic/drivers/base.py b/ironic/drivers/base.py index 513ba71131..44175fdbaa 100644 --- a/ironic/drivers/base.py +++ b/ironic/drivers/base.py @@ -38,6 +38,41 @@ LOG = logging.getLogger(__name__) RAID_CONFIG_SCHEMA = os.path.join(os.path.dirname(__file__), 'raid_config_schema.json') +RAID_APPLY_CONFIGURATION_ARGSINFO = { + "raid_config": { + "description": "The RAID configuration to apply.", + "required": True, + }, + "create_root_volume": { + "description": ( + "Setting this to 'False' indicates not to create root " + "volume that is specified in 'raid_config'. Default " + "value is 'True'." + ), + "required": False, + }, + "create_nonroot_volumes": { + "description": ( + "Setting this to 'False' indicates not to create " + "non-root volumes (all except the root volume) in " + "'raid_config'. Default value is 'True'." + ), + "required": False, + }, + "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, + } +} +""" +This may be used as the deploy_step argsinfo argument for RAID interfaces +implementing an apply_configuration deploy step. +""" + class BareDriver(object): """A bare driver object which will have interfaces attached later. @@ -1120,10 +1155,47 @@ class RAIDInterface(BaseInterface): """ raid.validate_configuration(raid_config, self.raid_schema) + # NOTE(mgoddard): This is not marked as a deploy step, because it requires + # the create_configuration method to support use during deployment, which + # might not be true for all implementations. Subclasses wishing to expose + # an apply_configuration deploy step should implement this method with a + # deploy_step decorator. The RAID_APPLY_CONFIGURATION_ARGSINFO variable may + # be used for the deploy_step argsinfo argument. The create_configuration + # method must also accept a delete_existing argument. + def apply_configuration(self, task, raid_config, create_root_volume=True, + create_nonroot_volumes=True, + delete_existing=True): + """Applies RAID configuration on the given node. + + :param task: A TaskManager instance. + :param raid_config: The RAID configuration to apply. + :param create_root_volume: Setting this to False indicates + not to create root volume that is specified in raid_config. + Default value is True. + :param create_nonroot_volumes: Setting this to False indicates + not to create non-root volumes (all except the root volume) in + raid_config. Default value is True. + :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) + node = task.node + node.target_raid_config = raid_config + node.save() + return self.create_configuration( + task, + create_root_volume=create_root_volume, + create_nonroot_volumes=create_nonroot_volumes, + delete_existing=delete_existing) + @abc.abstractmethod def create_configuration(self, task, create_root_volume=True, - create_nonroot_volumes=True): + create_nonroot_volumes=True, + delete_existing=True): """Creates RAID configuration on the given node. This method creates a RAID configuration on the given node. @@ -1143,8 +1215,11 @@ class RAIDInterface(BaseInterface): :param create_nonroot_volumes: Setting this to False indicates not to create non-root volumes (all except the root volume) in the node's target_raid_config. Default value is True. - :returns: states.CLEANWAIT if RAID configuration is in progress - asynchronously or None if it is complete. + :param delete_existing: Setting this to True indicates to delete RAID + configuration prior to creating the new configuration. + :returns: states.CLEANWAIT (cleaning) or states.DEPLOYWAIT (deployment) + if RAID configuration is in progress asynchronously, or None if it + is complete. """ @abc.abstractmethod @@ -1156,8 +1231,9 @@ class RAIDInterface(BaseInterface): cleared by the implementation. :param task: A TaskManager instance. - :returns: states.CLEANWAIT if deletion is in progress - asynchronously or None if it is complete. + :returns: states.CLEANWAIT (cleaning) or states.DEPLOYWAIT (deployment) + if deletion is in progress asynchronously, or None if it is + complete. """ def get_logical_disk_properties(self): diff --git a/ironic/tests/unit/drivers/test_base.py b/ironic/tests/unit/drivers/test_base.py index 7861784032..1cf00fc10b 100644 --- a/ironic/tests/unit/drivers/test_base.py +++ b/ironic/tests/unit/drivers/test_base.py @@ -19,6 +19,7 @@ import mock from ironic.common import exception from ironic.common import raid +from ironic.common import states from ironic.drivers import base as driver_base from ironic.drivers.modules import fake from ironic.tests import base @@ -574,7 +575,8 @@ class MyRAIDInterface(driver_base.RAIDInterface): def create_configuration(self, task, create_root_volume=True, - create_nonroot_volumes=True): + create_nonroot_volumes=True, + delete_existing=True): pass def delete_configuration(self, task): @@ -626,6 +628,68 @@ class RAIDInterfaceTestCase(base.TestCase): raid_interface.get_logical_disk_properties() get_properties_mock.assert_called_once_with(raid_schema) + @mock.patch.object(MyRAIDInterface, 'create_configuration', autospec=True) + @mock.patch.object(MyRAIDInterface, 'validate_raid_config', + autospec=True) + def test_apply_configuration(self, mock_validate, mock_create): + raid_interface = MyRAIDInterface() + node_mock = mock.MagicMock(target_raid_config=None) + task_mock = mock.MagicMock(node=node_mock) + mock_create.return_value = states.DEPLOYWAIT + raid_config = 'some_raid_config' + + result = raid_interface.apply_configuration(task_mock, raid_config) + + self.assertEqual(states.DEPLOYWAIT, result) + mock_validate.assert_called_once_with(raid_interface, task_mock, + raid_config) + mock_create.assert_called_once_with(raid_interface, task_mock, + create_root_volume=True, + create_nonroot_volumes=True, + delete_existing=True) + self.assertEqual(raid_config, node_mock.target_raid_config) + + @mock.patch.object(MyRAIDInterface, 'create_configuration', autospec=True) + @mock.patch.object(MyRAIDInterface, 'validate_raid_config', + autospec=True) + def test_apply_configuration_delete_existing(self, mock_validate, + mock_create): + raid_interface = MyRAIDInterface() + node_mock = mock.MagicMock(target_raid_config=None) + task_mock = mock.MagicMock(node=node_mock) + mock_create.return_value = states.DEPLOYWAIT + raid_config = 'some_raid_config' + + result = raid_interface.apply_configuration(task_mock, raid_config, + delete_existing=True) + + self.assertEqual(states.DEPLOYWAIT, result) + mock_validate.assert_called_once_with(raid_interface, task_mock, + raid_config) + mock_create.assert_called_once_with(raid_interface, task_mock, + create_root_volume=True, + create_nonroot_volumes=True, + delete_existing=True) + self.assertEqual(raid_config, node_mock.target_raid_config) + + @mock.patch.object(MyRAIDInterface, 'create_configuration', autospec=True) + @mock.patch.object(MyRAIDInterface, 'validate_raid_config', + autospec=True) + def test_apply_configuration_invalid(self, mock_validate, mock_create): + raid_interface = MyRAIDInterface() + node_mock = mock.MagicMock(target_raid_config=None) + task_mock = mock.MagicMock(node=node_mock) + mock_validate.side_effect = exception.InvalidParameterValue('bad') + raid_config = 'some_raid_config' + + self.assertRaises(exception.InvalidParameterValue, + raid_interface.apply_configuration, task_mock, + raid_config) + mock_validate.assert_called_once_with(raid_interface, task_mock, + raid_config) + self.assertFalse(mock_create.called) + self.assertIsNone(node_mock.target_raid_config) + class TestDeployInterface(base.TestCase): @mock.patch.object(driver_base.LOG, 'warning', autospec=True)