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
|
Note that this endpoint can not be used to request state changes, which are
|
||||||
managed through sub-resources.
|
managed through sub-resources.
|
||||||
|
|
||||||
|
API microversion 1.25 introduced the ability to unset a node's chassis UUID.
|
||||||
|
|
||||||
Normal response codes: 200
|
Normal response codes: 200
|
||||||
|
|
||||||
.. TODO: add error codes
|
.. TODO: add error codes
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
REST API Version History
|
REST API Version History
|
||||||
========================
|
========================
|
||||||
|
|
||||||
|
**1.25**
|
||||||
|
|
||||||
|
Add possibility to unset chassis_uuid from a node.
|
||||||
|
|
||||||
**1.24**
|
**1.24**
|
||||||
|
|
||||||
Added new endpoints '/v1/nodes/<node>/portgroups' and '/v1/portgroups/<portgroup>/ports'.
|
Added new endpoints '/v1/nodes/<node>/portgroups' and '/v1/portgroups/<portgroup>/ports'.
|
||||||
|
@ -893,8 +893,6 @@ class NodePatchType(types.JsonPatchType):
|
|||||||
|
|
||||||
_api_base = Node
|
_api_base = Node
|
||||||
|
|
||||||
_extra_non_removable_attrs = {'/chassis_uuid'}
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def internal_attrs():
|
def internal_attrs():
|
||||||
defaults = types.JsonPatchType.internal_attrs()
|
defaults = types.JsonPatchType.internal_attrs()
|
||||||
@ -1192,7 +1190,17 @@ class NodesController(rest.RestController):
|
|||||||
try:
|
try:
|
||||||
patch_val = getattr(node, field)
|
patch_val = getattr(node, field)
|
||||||
except AttributeError:
|
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
|
continue
|
||||||
if patch_val == wtypes.Unset:
|
if patch_val == wtypes.Unset:
|
||||||
patch_val = None
|
patch_val = None
|
||||||
|
@ -432,6 +432,16 @@ def allow_portgroups_subcontrollers():
|
|||||||
versions.MINOR_24_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):
|
def get_controller_reserved_names(cls):
|
||||||
"""Get reserved names for a given controller.
|
"""Get reserved names for a given controller.
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@ BASE_VERSION = 1
|
|||||||
# v1.23: Add portgroup support.
|
# v1.23: Add portgroup support.
|
||||||
# v1.24: Add subcontrollers: node.portgroup, portgroup.ports.
|
# v1.24: Add subcontrollers: node.portgroup, portgroup.ports.
|
||||||
# Add port.portgroup_uuid field.
|
# Add port.portgroup_uuid field.
|
||||||
|
# v1.25: Add possibility to unset chassis_uuid from node.
|
||||||
|
|
||||||
MINOR_0_JUNO = 0
|
MINOR_0_JUNO = 0
|
||||||
MINOR_1_INITIAL_VERSION = 1
|
MINOR_1_INITIAL_VERSION = 1
|
||||||
@ -81,11 +82,12 @@ MINOR_21_RESOURCE_CLASS = 21
|
|||||||
MINOR_22_LOOKUP_HEARTBEAT = 22
|
MINOR_22_LOOKUP_HEARTBEAT = 22
|
||||||
MINOR_23_PORTGROUPS = 23
|
MINOR_23_PORTGROUPS = 23
|
||||||
MINOR_24_PORTGROUPS_SUBCONTROLLERS = 24
|
MINOR_24_PORTGROUPS_SUBCONTROLLERS = 24
|
||||||
|
MINOR_25_UNSET_CHASSIS_UUID = 25
|
||||||
|
|
||||||
# When adding another version, update MINOR_MAX_VERSION and also update
|
# When adding another version, update MINOR_MAX_VERSION and also update
|
||||||
# doc/source/dev/webapi-version-history.rst with a detailed explanation of
|
# doc/source/dev/webapi-version-history.rst with a detailed explanation of
|
||||||
# what the version has changed.
|
# 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
|
# String representations of the minor and maximum versions
|
||||||
MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION)
|
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 import v1 as api_v1
|
||||||
from ironic.api.controllers.v1 import node as api_node
|
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 utils as api_utils
|
||||||
|
from ironic.api.controllers.v1 import versions
|
||||||
from ironic.common import boot_devices
|
from ironic.common import boot_devices
|
||||||
from ironic.common import exception
|
from ironic.common import exception
|
||||||
from ironic.common import states
|
from ironic.common import states
|
||||||
@ -1317,6 +1318,40 @@ class TestPatch(test_api_base.BaseApiTest):
|
|||||||
self.assertEqual('application/json', response.content_type)
|
self.assertEqual('application/json', response.content_type)
|
||||||
self.assertEqual(http_client.OK, response.status_code)
|
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):
|
def test_add_chassis_id(self):
|
||||||
response = self.patch_json('/nodes/%s' % self.node.uuid,
|
response = self.patch_json('/nodes/%s' % self.node.uuid,
|
||||||
[{'path': '/chassis_id',
|
[{'path': '/chassis_id',
|
||||||
|
@ -299,6 +299,13 @@ class TestApiUtils(base.TestCase):
|
|||||||
mock_request.version.minor = 22
|
mock_request.version.minor = 22
|
||||||
self.assertFalse(utils.allow_portgroups())
|
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):
|
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…
x
Reference in New Issue
Block a user