From 1c941eefe55c37ec9f89aa974773fec101e05a17 Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Wed, 24 Oct 2018 16:08:40 +0200 Subject: [PATCH] Support for protected and protected_reason fields Story: #2003869 Task: #27624 Depends-On: https://review.openstack.org/611662 Change-Id: Ib575ace38d1bedce54d0d21517d745ed3204d6f2 --- ironicclient/common/http.py | 2 +- ironicclient/osc/v1/baremetal_node.py | 26 +++++- .../tests/unit/osc/v1/test_baremetal_node.py | 83 +++++++++++++++++++ ironicclient/tests/unit/v1/test_node_shell.py | 2 + ironicclient/v1/resource_fields.py | 4 + .../notes/protected-72d7419245a4f6c3.yaml | 7 ++ 6 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/protected-72d7419245a4f6c3.yaml diff --git a/ironicclient/common/http.py b/ironicclient/common/http.py index 4c8bb29d6..659981692 100644 --- a/ironicclient/common/http.py +++ b/ironicclient/common/http.py @@ -43,7 +43,7 @@ from ironicclient import exc # http://specs.openstack.org/openstack/ironic-specs/specs/kilo/api-microversions.html # noqa # for full details. DEFAULT_VER = '1.9' -LAST_KNOWN_API_VERSION = 47 +LAST_KNOWN_API_VERSION = 48 LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION) LOG = logging.getLogger(__name__) diff --git a/ironicclient/osc/v1/baremetal_node.py b/ironicclient/osc/v1/baremetal_node.py index fb8e3fe3c..e1fafcb19 100755 --- a/ironicclient/osc/v1/baremetal_node.py +++ b/ironicclient/osc/v1/baremetal_node.py @@ -1116,6 +1116,16 @@ class SetBaremetalNode(command.Command): action='store_true', help=_('Enable automated cleaning for the node'), ) + parser.add_argument( + '--protected', + action='store_true', + help=_('Mark the node as protected'), + ) + parser.add_argument( + '--protected-reason', + metavar='', + help=_('Set the reason of marking the node as protected'), + ) parser.add_argument( '--target-raid-config', metavar='', @@ -1174,7 +1184,7 @@ class SetBaremetalNode(command.Command): properties = [] for field in ['automated_clean', 'instance_uuid', 'name', 'chassis_uuid', 'driver', 'resource_class', - 'conductor_group']: + 'conductor_group', 'protected', 'protected_reason']: value = getattr(parsed_args, field) if value: properties.extend(utils.args_array_to_patch( @@ -1436,6 +1446,17 @@ class UnsetBaremetalNode(command.Command): help=_('Unset automated clean option on this baremetal node ' '(the value from configuration will be used)'), ) + parser.add_argument( + "--protected", + action="store_true", + help=_('Unset the protected flag on the node'), + ) + parser.add_argument( + "--protected-reason", + action="store_true", + help=_('Unset the protected reason (gets unset automatically when ' + 'protected is unset)'), + ) return parser @@ -1457,7 +1478,8 @@ class UnsetBaremetalNode(command.Command): 'deploy_interface', 'inspect_interface', 'management_interface', 'network_interface', 'power_interface', 'raid_interface', 'rescue_interface', - 'storage_interface', 'vendor_interface']: + 'storage_interface', 'vendor_interface', + 'protected', 'protected_reason']: if getattr(parsed_args, field): properties.extend(utils.args_array_to_patch('remove', [field])) diff --git a/ironicclient/tests/unit/osc/v1/test_baremetal_node.py b/ironicclient/tests/unit/osc/v1/test_baremetal_node.py index e7f186c00..de920057a 100644 --- a/ironicclient/tests/unit/osc/v1/test_baremetal_node.py +++ b/ironicclient/tests/unit/osc/v1/test_baremetal_node.py @@ -636,6 +636,8 @@ class TestBaremetalList(TestBaremetal): 'Power Interface', 'Power State', 'Properties', + 'Protected', + 'Protected Reason', 'Provision Updated At', 'Provisioning State', 'RAID Interface', @@ -2303,6 +2305,49 @@ class TestBaremetalSet(TestBaremetal): reset_interfaces=None, ) + def test_baremetal_set_protected(self): + arglist = [ + 'node_uuid', + '--protected' + ] + verifylist = [ + ('node', 'node_uuid'), + ('protected', True) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.baremetal_mock.node.update.assert_called_once_with( + 'node_uuid', + [{'path': '/protected', 'value': 'True', 'op': 'add'}], + reset_interfaces=None, + ) + + def test_baremetal_set_protected_with_reason(self): + arglist = [ + 'node_uuid', + '--protected', + '--protected-reason', 'reason!' + ] + verifylist = [ + ('node', 'node_uuid'), + ('protected', True), + ('protected_reason', 'reason!') + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.baremetal_mock.node.update.assert_called_once_with( + 'node_uuid', + [{'path': '/protected', 'value': 'True', 'op': 'add'}, + {'path': '/protected_reason', 'value': 'reason!', 'op': 'add'}], + reset_interfaces=None, + ) + def test_baremetal_set_extra(self): arglist = [ 'node_uuid', @@ -2774,6 +2819,44 @@ class TestBaremetalUnset(TestBaremetal): [{'path': '/automated_clean', 'op': 'remove'}] ) + def test_baremetal_unset_protected(self): + arglist = [ + 'node_uuid', + '--protected', + ] + verifylist = [ + ('node', 'node_uuid'), + ('protected', True) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.baremetal_mock.node.update.assert_called_once_with( + 'node_uuid', + [{'path': '/protected', 'op': 'remove'}] + ) + + def test_baremetal_unset_protected_reason(self): + arglist = [ + 'node_uuid', + '--protected-reason', + ] + verifylist = [ + ('node', 'node_uuid'), + ('protected_reason', True) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.baremetal_mock.node.update.assert_called_once_with( + 'node_uuid', + [{'path': '/protected_reason', 'op': 'remove'}] + ) + def test_baremetal_unset_extra(self): arglist = [ 'node_uuid', diff --git a/ironicclient/tests/unit/v1/test_node_shell.py b/ironicclient/tests/unit/v1/test_node_shell.py index b3f6412a2..04319c2a9 100644 --- a/ironicclient/tests/unit/v1/test_node_shell.py +++ b/ironicclient/tests/unit/v1/test_node_shell.py @@ -65,6 +65,8 @@ class NodeShellTest(utils.BaseTestCase): 'vendor_interface', 'power_state', 'properties', + 'protected', + 'protected_reason', 'provision_state', 'provision_updated_at', 'reservation', diff --git a/ironicclient/v1/resource_fields.py b/ironicclient/v1/resource_fields.py index 6020e9609..570f9d5df 100644 --- a/ironicclient/v1/resource_fields.py +++ b/ironicclient/v1/resource_fields.py @@ -89,6 +89,8 @@ class Resource(object): 'node_uuid': 'Node UUID', 'power_state': 'Power State', 'properties': 'Properties', + 'protected': 'Protected', + 'protected_reason': 'Protected Reason', 'provision_state': 'Provisioning State', 'provision_updated_at': 'Provision Updated At', 'raid_config': 'Current RAID configuration', @@ -234,6 +236,8 @@ NODE_DETAILED_RESOURCE = Resource( 'power_interface', 'power_state', 'properties', + 'protected', + 'protected_reason', 'provision_updated_at', 'provision_state', 'raid_interface', diff --git a/releasenotes/notes/protected-72d7419245a4f6c3.yaml b/releasenotes/notes/protected-72d7419245a4f6c3.yaml new file mode 100644 index 000000000..a364cde8b --- /dev/null +++ b/releasenotes/notes/protected-72d7419245a4f6c3.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Adds the ability to set and unset the ``protected`` and + ``protected_reason`` fields introduced in API 1.48. Setting ``protected`` + allows protecting a deployed node from undeploying, rebuilding and + deleting.