diff --git a/ironic/drivers/modules/oneview/power.py b/ironic/drivers/modules/oneview/power.py index 98229d054e..82c05693bd 100644 --- a/ironic/drivers/modules/oneview/power.py +++ b/ironic/drivers/modules/oneview/power.py @@ -52,18 +52,28 @@ class OneViewPower(base.PowerInterface): enclosure_group_uri. Also, checks if the server profile of the node is applied, if NICs are valid for the server profile of the node, and if the server hardware attributes (ram, memory, vcpus count) are - consistent with OneView. + consistent with OneView. It validates if the node is being used by + Oneview. :param task: a task from TaskManager. :raises: MissingParameterValue if a required parameter is missing. :raises: InvalidParameterValue if parameters set are inconsistent with resources in OneView + :raises: InvalidParameterValue if the node in use by OneView. + :raises: OneViewError if not possible to get OneView's information + for the given node, if not possible to retrieve Server + Hardware from OneView. """ common.verify_node_info(task.node) try: common.validate_oneview_resources_compatibility( self.oneview_client, task) + + if deploy_utils.is_node_in_use_by_oneview(self.oneview_client, + task.node): + raise exception.InvalidParameterValue( + _("Node %s is in use by OneView.") % task.node.uuid) except exception.OneViewError as oneview_exc: raise exception.InvalidParameterValue(oneview_exc) @@ -72,7 +82,6 @@ class OneViewPower(base.PowerInterface): """Gets the current power state. :param task: a TaskManager instance. - :param node: The Node. :returns: one of :mod:`ironic.common.states` POWER_OFF, POWER_ON or ERROR. :raises: OneViewError if fails to retrieve power state of OneView @@ -99,7 +108,6 @@ class OneViewPower(base.PowerInterface): """Turn the current power state on or off. :param task: a TaskManager instance. - :param node: The Node. :param power_state: The desired power state POWER_ON, POWER_OFF or REBOOT from :mod:`ironic.common.states`. :raises: InvalidParameterValue if an invalid power state was specified. @@ -135,7 +143,6 @@ class OneViewPower(base.PowerInterface): """Reboot the node :param task: a TaskManager instance. - :param node: The Node. :raises: PowerStateFailure if the final state of the node is not POWER_ON. """ diff --git a/ironic/tests/unit/drivers/modules/oneview/test_deploy_utils.py b/ironic/tests/unit/drivers/modules/oneview/test_deploy_utils.py index c317a6a326..c19a613996 100644 --- a/ironic/tests/unit/drivers/modules/oneview/test_deploy_utils.py +++ b/ironic/tests/unit/drivers/modules/oneview/test_deploy_utils.py @@ -255,7 +255,7 @@ class OneViewDeployUtilsTestCase(db_base.DbTestCase): task.node) ) - # Tests for is_node_in_use_by_oneview + # Tests for is_node_in_use_by_ironic def test_is_node_in_use_by_ironic(self, mock_get_ov_client): """Node has a Server Profile applied by ironic. diff --git a/ironic/tests/unit/drivers/modules/oneview/test_power.py b/ironic/tests/unit/drivers/modules/oneview/test_power.py index e52e92e3c2..5800a43a9b 100644 --- a/ironic/tests/unit/drivers/modules/oneview/test_power.py +++ b/ironic/tests/unit/drivers/modules/oneview/test_power.py @@ -24,12 +24,12 @@ from ironic.common import exception from ironic.common import states from ironic.conductor import task_manager from ironic.drivers.modules.oneview import common +from ironic.drivers.modules.oneview import deploy_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 from ironic.tests.unit.objects import utils as obj_utils - oneview_exceptions = importutils.try_import('oneview_client.exceptions') POWER_ON = 'On' @@ -58,7 +58,11 @@ class OneViewPowerDriverTestCase(db_base.DbTestCase): @mock.patch.object(common, 'validate_oneview_resources_compatibility', spect_set=True, autospec=True) - def test_power_interface_validate(self, mock_validate, mock_get_ov_client): + @mock.patch.object(deploy_utils, 'is_node_in_use_by_oneview', + spect_set=True, autospec=True) + def test_power_interface_validate(self, mock_is_node_in_use_by_oneview, + mock_validate, mock_get_ov_client): + mock_is_node_in_use_by_oneview.return_value = False with task_manager.acquire(self.context, self.node.uuid) as task: task.driver.power.validate(task) self.assertTrue(mock_validate.called) @@ -82,6 +86,35 @@ class OneViewPowerDriverTestCase(db_base.DbTestCase): task.driver.power.validate, task) + @mock.patch.object(common, 'validate_oneview_resources_compatibility', + spect_set=True, autospec=True) + @mock.patch.object(deploy_utils, 'is_node_in_use_by_oneview', + spect_set=True, autospec=True) + def test_power_validate_fail_node_used_by_oneview( + self, mock_is_node_in_use_by_oneview, mock_validate, + mock_get_ov_client): + mock_validate.return_value = True + mock_is_node_in_use_by_oneview.return_value = True + with task_manager.acquire(self.context, self.node.uuid) as task: + self.assertRaises(exception.InvalidParameterValue, + task.driver.power.validate, + task) + + @mock.patch.object(common, 'validate_oneview_resources_compatibility', + spect_set=True, autospec=True) + @mock.patch.object(deploy_utils, 'is_node_in_use_by_oneview', + spect_set=True, autospec=True) + def test_validate_fail_node_in_use_by_oneview( + self, mock_is_node_in_use_by_oneview, mock_validate, + mock_get_ov_client): + mock_validate.return_value = True + mock_is_node_in_use_by_oneview.side_effect = ( + exception.OneViewError('message')) + with task_manager.acquire(self.context, self.node.uuid) as task: + self.assertRaises(exception.InvalidParameterValue, + task.driver.power.validate, + task) + def test_power_interface_get_properties(self, mock_get_ov_client): expected = common.COMMON_PROPERTIES self.assertItemsEqual(expected, self.driver.power.get_properties()) diff --git a/releasenotes/notes/oneview-node-free-for-ironic-61b05fee827664cb.yaml b/releasenotes/notes/oneview-node-free-for-ironic-61b05fee827664cb.yaml new file mode 100644 index 0000000000..4cb06e1bbc --- /dev/null +++ b/releasenotes/notes/oneview-node-free-for-ironic-61b05fee827664cb.yaml @@ -0,0 +1,4 @@ +--- +fixes: + - Fixes an issue with ironic being able to change + the power state of nodes currently in use by OneView.