Merge "Add support for node name in port creation"

This commit is contained in:
Zuul 2024-10-24 18:18:56 +00:00 committed by Gerrit Code Review
commit f76b006b3f
9 changed files with 117 additions and 9 deletions

View File

@ -119,6 +119,9 @@ This method requires a Node UUID and the physical hardware address for the Port
of ``vtep-logical-switch``, ``vtep-physical-switch`` and ``port_id``
to identify ovn vtep switches.
.. versionadded:: 1.94
Added support to create ports passing in either the node name or UUID.
Normal response code: 201
Request
@ -126,7 +129,7 @@ Request
.. rest_parameters:: parameters.yaml
- node_uuid: req_node_uuid
- node_ident: node_ident
- address: req_port_address
- portgroup_uuid: req_portgroup_uuid
- name: req_port_name
@ -137,6 +140,9 @@ Request
- is_smartnic: req_is_smartnic
- uuid: req_uuid
.. note::
Either `node_ident` or `node_uuid` is a valid parameter.
**Example Port creation request:**
.. literalinclude:: samples/port-create-request.json

View File

@ -1,5 +1,5 @@
{
"node_uuid": "6d85703a-565d-469a-96ce-30b6de53079d",
"node_ident": "6d85703a-565d-469a-96ce-30b6de53079d",
"portgroup_uuid": "e43c722c-248e-4c6e-8ce8-0d8ff129387a",
"name": "port1",
"address": "11:11:11:11:11:11",

View File

@ -2,6 +2,11 @@
REST API Version History
========================
1.94 (Epoxy)
-----------------------
Add support to create ports passing in either the node name or UUID.
1.92 (Dalmatian)
-----------------------
@ -10,9 +15,9 @@ nodes associated via traits and used in place of an explicit
list of steps for manual cleaning or servicing, to enable
self-service of maintenance items by project members.
* Adds a new REST API endpoint `/v1/runbooks/` with basic CRUD support.
* Extends the `/v1/nodes/<node>/states/provision` API to accept a runbook
identifier (name or UUID) instead of `clean_steps` or `service_steps` for
* Adds a new REST API endpoint ``/v1/runbooks/`` with basic CRUD support.
* Extends the ``/v1/nodes/<node>/states/provision`` API to accept a runbook
identifier (name or UUID) instead of ``clean_steps`` or ``service_steps`` for
servicing or manual cleaning.
* Implements RBAC-aware lifecycle management for runbooks, allowing projects
to limit who can CRUD and use a runbook.

View File

@ -46,6 +46,7 @@ PORT_SCHEMA = {
'extra': {'type': ['object', 'null']},
'is_smartnic': {'type': ['string', 'boolean', 'null']},
'local_link_connection': {'type': ['null', 'object']},
'node_ident': {'type': 'string'},
'node_uuid': {'type': 'string'},
'physical_network': {'type': ['string', 'null'], 'maxLength': 64},
'portgroup_uuid': {'type': ['string', 'null']},
@ -53,7 +54,11 @@ PORT_SCHEMA = {
'uuid': {'type': ['string', 'null']},
'name': {'type': ['string', 'null']},
},
'required': ['address', 'node_uuid'],
'required': ['address'],
'oneOf': [
{'required': ['node_ident']},
{'required': ['node_uuid']},
],
'additionalProperties': False,
}
@ -65,6 +70,7 @@ PATCH_ALLOWED_FIELDS = [
'extra',
'is_smartnic',
'local_link_connection',
'node_ident',
'node_uuid',
'physical_network',
'portgroup_uuid',
@ -554,8 +560,17 @@ class PortsController(rest.RestController):
node = None
owner = None
lessee = None
node_uuid = port.get('node_uuid')
node_uuid = port.get('node_uuid', None)
node_ident = port.get('node_ident', None)
if node_ident:
if not api_utils.allow_node_ident_as_param_for_port_creation():
raise exception.NotAcceptable()
ident = node_ident or node_uuid
try:
node = api_utils.get_rpc_node(ident)
port['node_uuid'] = node['uuid']
node = api_utils.replace_node_uuid_with_id(port)
owner = node.owner
lessee = node.lessee

View File

@ -2214,3 +2214,8 @@ def allow_attach_detach_vmedia():
def allow_get_vmedia():
"""Check if we should support get virtual media action."""
return api.request.version.minor >= versions.MINOR_93_GET_VMEDIA
def allow_node_ident_as_param_for_port_creation():
"""Check if 'node_ident' parameter is allowed for port creation."""
return api.request.version.minor >= versions.MINOR_94_PORT_NODENAME

View File

@ -131,6 +131,7 @@ BASE_VERSION = 1
# v1.91: Remove special treatment of .json for API objects
# v1.92: Add runbooks API
# v1.93: Add GET API for virtual media
# v1.94: Add node name support for port creation
MINOR_0_JUNO = 0
MINOR_1_INITIAL_VERSION = 1
@ -226,6 +227,7 @@ MINOR_90_OVN_VTEP = 90
MINOR_91_DOT_JSON = 91
MINOR_92_RUNBOOKS = 92
MINOR_93_GET_VMEDIA = 93
MINOR_94_PORT_NODENAME = 94
# When adding another version, update:
# - MINOR_MAX_VERSION
@ -233,7 +235,7 @@ MINOR_93_GET_VMEDIA = 93
# explanation of what changed in the new version
# - common/release_mappings.py, RELEASE_MAPPING['master']['api']
MINOR_MAX_VERSION = MINOR_93_GET_VMEDIA
MINOR_MAX_VERSION = MINOR_94_PORT_NODENAME
# String representations of the minor and maximum versions
_MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION)

View File

@ -776,7 +776,7 @@ RELEASE_MAPPING = {
# make it below. To release, we will preserve a version matching
# the release as a separate block of text, like above.
'master': {
'api': '1.93',
'api': '1.94',
'rpc': '1.61',
'objects': {
'Allocation': ['1.1'],

View File

@ -1958,6 +1958,75 @@ class TestPost(test_api_base.BaseApiTest):
mock_create.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY,
'test-topic')
def test_create_port_missing_address_fails(self, mock_create):
pdict = post_get_test_port(node_uuid=self.node.uuid)
del pdict['address']
response = self.post_json('/ports', pdict, headers=self.headers,
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertIn("'address' is a required property",
response.json['error_message'])
self.assertFalse(mock_create.called)
def test_create_port_with_node_uuid(self, mock_create):
pdict = post_get_test_port(node_uuid=self.node.uuid)
response = self.post_json('/ports', pdict, headers=self.headers)
self.assertEqual(http_client.CREATED, response.status_int)
result = self.get_json('/ports/%s' % response.json['uuid'],
headers=self.headers)
self.assertEqual(self.node.uuid, result['node_uuid'])
mock_create.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY,
'test-topic')
def test_create_port_with_node_ident(self, mock_create):
self.node.name = 'test-node-name'
self.node.save()
pdict = post_get_test_port()
pdict['node_ident'] = self.node.name
del pdict['node_uuid']
response = self.post_json('/ports', pdict, headers=self.headers)
self.assertEqual(http_client.CREATED, response.status_int)
result = self.get_json('/ports/%s' % response.json['uuid'],
headers=self.headers)
self.assertEqual(self.node.uuid, result['node_uuid'])
mock_create.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY,
'test-topic')
def test_create_port_with_both_node_ident_and_node_uuid(self,
mock_create):
self.node.name = 'test-node-name'
self.node.save()
pdict = post_get_test_port(node_uuid=self.node.uuid)
pdict['node_ident'] = self.node.name
response = self.post_json('/ports', pdict, headers=self.headers,
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
def test_create_port_without_node_or_node_uuid(self, mock_create):
pdict = post_get_test_port(node_uuid=self.node.uuid)
del pdict['node_uuid']
response = self.post_json('/ports', pdict, headers=self.headers,
expect_errors=True)
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
def test_create_port_with_node_ident_unsupported_api_version(self,
mock_create):
headers = {api_base.Version.string: '1.93'}
self.node.name = 'test-node-name'
self.node.save()
pdict = post_get_test_port(node_uuid=self.node.uuid)
pdict['node_ident'] = self.node.name
del pdict['node_uuid']
response = self.post_json('/ports', pdict, headers=headers,
expect_errors=True)
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_int)
@mock.patch.object(notification_utils, '_emit_api_notification',
autospec=True)
def test_create_port_error(self, mock_notify, mock_create):

View File

@ -0,0 +1,6 @@
---
fixes:
- |
Add support for passing either a node's name or UUID through the
'node_ident' parameter during port creation. The 'node_uuid' parameter is
now deprecated.