Add support for node name in port creation
Add support for creating ports using either node UUID or node name. Closes-Bug: #1439901 Change-Id: I215619648bbe7aa1152e0f117971bece74ffe1fe
This commit is contained in:
parent
be33fcf38b
commit
da002a341c
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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'],
|
||||
|
@ -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):
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user