Add possibility to remove chassis_uuid from a node
Allow to unset the field "chassis_uuid" from a node using the "ironic node-update <node_uuid> remove chassis_uuid" command. The API version has been bumped to 1.25. Change-Id: I1c8406f83f9d240ede99b0458c5e8b6967f2e37a Closes-Bug: #1563263
This commit is contained in:
parent
dd57ed5a2d
commit
1f61654019
@ -326,6 +326,8 @@ Updates the information stored about a Node.
|
||||
Note that this endpoint can not be used to request state changes, which are
|
||||
managed through sub-resources.
|
||||
|
||||
API microversion 1.25 introduced the ability to unset a node's chassis UUID.
|
||||
|
||||
Normal response codes: 200
|
||||
|
||||
.. TODO: add error codes
|
||||
|
@ -2,6 +2,10 @@
|
||||
REST API Version History
|
||||
========================
|
||||
|
||||
**1.25**
|
||||
|
||||
Add possibility to unset chassis_uuid from a node.
|
||||
|
||||
**1.24**
|
||||
|
||||
Added new endpoints '/v1/nodes/<node>/portgroups' and '/v1/portgroups/<portgroup>/ports'.
|
||||
|
@ -893,8 +893,6 @@ class NodePatchType(types.JsonPatchType):
|
||||
|
||||
_api_base = Node
|
||||
|
||||
_extra_non_removable_attrs = {'/chassis_uuid'}
|
||||
|
||||
@staticmethod
|
||||
def internal_attrs():
|
||||
defaults = types.JsonPatchType.internal_attrs()
|
||||
@ -1192,7 +1190,17 @@ class NodesController(rest.RestController):
|
||||
try:
|
||||
patch_val = getattr(node, field)
|
||||
except AttributeError:
|
||||
# Ignore fields that aren't exposed in the API
|
||||
# Ignore fields that aren't exposed in the API, except
|
||||
# chassis_id. chassis_id would have been set (instead of
|
||||
# chassis_uuid) if the node belongs to a chassis. This
|
||||
# AttributeError is raised for chassis_id only if
|
||||
# 1. the node doesn't belong to a chassis or
|
||||
# 2. the node belonged to a chassis but is now being removed
|
||||
# from the chassis.
|
||||
if (field == "chassis_id" and rpc_node[field] is not None):
|
||||
if not api_utils.allow_remove_chassis_uuid():
|
||||
raise exception.NotAcceptable()
|
||||
rpc_node[field] = None
|
||||
continue
|
||||
if patch_val == wtypes.Unset:
|
||||
patch_val = None
|
||||
|
@ -432,6 +432,16 @@ def allow_portgroups_subcontrollers():
|
||||
versions.MINOR_24_PORTGROUPS_SUBCONTROLLERS)
|
||||
|
||||
|
||||
def allow_remove_chassis_uuid():
|
||||
"""Check if chassis_uuid can be removed from node.
|
||||
|
||||
Version 1.25 of the API added support for chassis_uuid
|
||||
removal
|
||||
"""
|
||||
return (pecan.request.version.minor >=
|
||||
versions.MINOR_25_UNSET_CHASSIS_UUID)
|
||||
|
||||
|
||||
def get_controller_reserved_names(cls):
|
||||
"""Get reserved names for a given controller.
|
||||
|
||||
|
@ -55,6 +55,7 @@ BASE_VERSION = 1
|
||||
# v1.23: Add portgroup support.
|
||||
# v1.24: Add subcontrollers: node.portgroup, portgroup.ports.
|
||||
# Add port.portgroup_uuid field.
|
||||
# v1.25: Add possibility to unset chassis_uuid from node.
|
||||
|
||||
MINOR_0_JUNO = 0
|
||||
MINOR_1_INITIAL_VERSION = 1
|
||||
@ -81,11 +82,12 @@ MINOR_21_RESOURCE_CLASS = 21
|
||||
MINOR_22_LOOKUP_HEARTBEAT = 22
|
||||
MINOR_23_PORTGROUPS = 23
|
||||
MINOR_24_PORTGROUPS_SUBCONTROLLERS = 24
|
||||
MINOR_25_UNSET_CHASSIS_UUID = 25
|
||||
|
||||
# When adding another version, update MINOR_MAX_VERSION and also update
|
||||
# doc/source/dev/webapi-version-history.rst with a detailed explanation of
|
||||
# what the version has changed.
|
||||
MINOR_MAX_VERSION = MINOR_24_PORTGROUPS_SUBCONTROLLERS
|
||||
MINOR_MAX_VERSION = MINOR_25_UNSET_CHASSIS_UUID
|
||||
|
||||
# String representations of the minor and maximum versions
|
||||
MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION)
|
||||
|
@ -32,6 +32,7 @@ from ironic.api.controllers import base as api_base
|
||||
from ironic.api.controllers import v1 as api_v1
|
||||
from ironic.api.controllers.v1 import node as api_node
|
||||
from ironic.api.controllers.v1 import utils as api_utils
|
||||
from ironic.api.controllers.v1 import versions
|
||||
from ironic.common import boot_devices
|
||||
from ironic.common import exception
|
||||
from ironic.common import states
|
||||
@ -1317,6 +1318,40 @@ class TestPatch(test_api_base.BaseApiTest):
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(http_client.OK, response.status_code)
|
||||
|
||||
def test_remove_chassis_uuid(self):
|
||||
self.mock_update_node.return_value = self.node
|
||||
headers = {api_base.Version.string: "1.25"}
|
||||
response = self.patch_json('/nodes/%s' % self.node.uuid,
|
||||
[{'path': '/chassis_uuid',
|
||||
'op': 'remove'}],
|
||||
headers=headers)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(http_client.OK, response.status_code)
|
||||
|
||||
def test_remove_chassis_uuid_invalid_api_version(self):
|
||||
self.mock_update_node.return_value = self.node
|
||||
headers = {api_base.Version.string: "1.24"}
|
||||
response = self.patch_json('/nodes/%s' % self.node.uuid,
|
||||
[{'path': '/chassis_uuid',
|
||||
'op': 'remove'}],
|
||||
headers=headers,
|
||||
expect_errors=True)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_code)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
@mock.patch("pecan.request")
|
||||
def test__update_changed_fields_remove_chassis_uuid(self, mock_pecan_req):
|
||||
mock_pecan_req.version.minor = versions.MINOR_MAX_VERSION
|
||||
controller = api_node.NodesController()
|
||||
|
||||
node_dict = self.node.as_dict()
|
||||
del node_dict['chassis_id']
|
||||
node_no_chassis = api_node.Node(**node_dict)
|
||||
|
||||
controller._update_changed_fields(node_no_chassis, self.node)
|
||||
self.assertIsNone(self.node.chassis_id)
|
||||
|
||||
def test_add_chassis_id(self):
|
||||
response = self.patch_json('/nodes/%s' % self.node.uuid,
|
||||
[{'path': '/chassis_id',
|
||||
|
@ -299,6 +299,13 @@ class TestApiUtils(base.TestCase):
|
||||
mock_request.version.minor = 22
|
||||
self.assertFalse(utils.allow_portgroups())
|
||||
|
||||
@mock.patch.object(pecan, 'request', spec_set=['version'])
|
||||
def test_allow_remove_chassis_uuid(self, mock_request):
|
||||
mock_request.version.minor = 25
|
||||
self.assertTrue(utils.allow_remove_chassis_uuid())
|
||||
mock_request.version.minor = 24
|
||||
self.assertFalse(utils.allow_remove_chassis_uuid())
|
||||
|
||||
|
||||
class TestNodeIdent(base.TestCase):
|
||||
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Adds support for removing the chassis UUID associated with a node (via
|
||||
PATCH /v1/nodes/<ident>). This is available starting with API version 1.25.
|
Loading…
Reference in New Issue
Block a user