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:
Aline Bousquet 2016-10-25 14:13:05 +01:00
parent dd57ed5a2d
commit 1f61654019
8 changed files with 76 additions and 4 deletions

View File

@ -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

View File

@ -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'.

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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',

View File

@ -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):

View File

@ -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.