Add network_type to port local_link_connection
Add network_type to the port objects local_link_connection field. The network_type can be set to either managed or unmanaged. When the type is unmanaged other fields are not required. Use unmanaged when the neutron network_interface is required, but the network is in fact a flat network where no actual switch management is done. Story: 2007315 Task: 39161 Change-Id: I00c5ea22a8163c27c9ce4470e3713c282d0eb131
This commit is contained in:
parent
6798715eaf
commit
c40d221fca
@ -2,6 +2,15 @@
|
||||
REST API Version History
|
||||
========================
|
||||
|
||||
1.64 (Ussuri, master)
|
||||
---------------------
|
||||
|
||||
Added the ``network_type`` to the port objects ``local_link_connection`` field.
|
||||
The ``network_type`` can be set to either ``managed`` or ``unmanaged``. When the
|
||||
type is ``unmanaged`` other fields are not required. Use ``unmanaged`` when the
|
||||
neutron ``network_interface`` is required, but the network is in fact a flat
|
||||
network where no actual switch management is done.
|
||||
|
||||
1.63 (Ussuri, master)
|
||||
---------------------
|
||||
|
||||
|
@ -440,6 +440,14 @@ class PortsController(rest.RestController):
|
||||
if ('is_smartnic' in fields
|
||||
and not api_utils.allow_port_is_smartnic()):
|
||||
raise exception.NotAcceptable()
|
||||
if ('local_link_connection/network_type' in fields
|
||||
and not api_utils.allow_local_link_connection_network_type()):
|
||||
raise exception.NotAcceptable()
|
||||
if isinstance(fields, dict):
|
||||
if (not api_utils.allow_local_link_connection_network_type()
|
||||
and 'network_type' in fields.get('local_link_connection',
|
||||
{}).keys()):
|
||||
raise exception.NotAcceptable()
|
||||
|
||||
@METRICS.timer('PortsController.get_all')
|
||||
@expose.expose(PortCollection, types.uuid_or_name, types.uuid,
|
||||
@ -668,11 +676,10 @@ class PortsController(rest.RestController):
|
||||
'baremetal:port:update', port_uuid)
|
||||
|
||||
context = api.request.context
|
||||
|
||||
fields_to_check = set()
|
||||
for field in (self.advanced_net_fields
|
||||
+ ['portgroup_uuid', 'physical_network',
|
||||
'is_smartnic']):
|
||||
'is_smartnic', 'local_link_connection/network_type']):
|
||||
field_path = '/%s' % field
|
||||
if (api_utils.get_patch_values(patch, field_path)
|
||||
or api_utils.is_path_removed(patch, field_path)):
|
||||
|
@ -274,8 +274,9 @@ class LocalLinkConnectionType(wtypes.UserType):
|
||||
smart_nic_mandatory_fields = {'port_id', 'hostname'}
|
||||
mandatory_fields_list = [local_link_mandatory_fields,
|
||||
smart_nic_mandatory_fields]
|
||||
optional_field = {'switch_info'}
|
||||
valid_fields = set.union(optional_field, *mandatory_fields_list)
|
||||
optional_fields = {'switch_info', 'network_type'}
|
||||
valid_fields = set.union(optional_fields, *mandatory_fields_list)
|
||||
valid_network_types = {'managed', 'unmanaged'}
|
||||
|
||||
@staticmethod
|
||||
def validate(value):
|
||||
@ -318,6 +319,25 @@ class LocalLinkConnectionType(wtypes.UserType):
|
||||
if invalid:
|
||||
raise exception.Invalid(_('%s are invalid keys') % (invalid))
|
||||
|
||||
# If network_type is 'unmanaged', this is a network with no switch
|
||||
# management. i.e local_link_connection details are not required.
|
||||
if 'network_type' in keys:
|
||||
if (value['network_type'] not in
|
||||
LocalLinkConnectionType.valid_network_types):
|
||||
msg = _(
|
||||
'Invalid network_type %(type)s, valid network_types are '
|
||||
'%(valid_network_types)s.') % {
|
||||
'type': value['network_type'],
|
||||
'valid_network_types':
|
||||
LocalLinkConnectionType.valid_network_types}
|
||||
raise exception.Invalid(msg)
|
||||
|
||||
if (value['network_type'] == 'unmanaged'
|
||||
and not (keys - {'network_type'})):
|
||||
# Only valid network_type 'unmanaged' is set, no for further
|
||||
# validation required.
|
||||
return value
|
||||
|
||||
# Check any mandatory fields sets are present
|
||||
for mandatory_set in LocalLinkConnectionType.mandatory_fields_list:
|
||||
if mandatory_set <= keys:
|
||||
|
@ -1369,3 +1369,9 @@ def allow_allocation_owner():
|
||||
def allow_agent_token():
|
||||
"""Check if agent token is available."""
|
||||
return api.request.version.minor >= versions.MINOR_62_AGENT_TOKEN
|
||||
|
||||
|
||||
def allow_local_link_connection_network_type():
|
||||
"""Check if network_type is allowed in ports link_local_connection"""
|
||||
return (api.request.version.minor
|
||||
>= versions.MINOR_64_LOCAL_LINK_CONNECTION_NETWORK_TYPE)
|
||||
|
@ -101,6 +101,7 @@ BASE_VERSION = 1
|
||||
# v1.61: Add retired and retired_reason to the node object.
|
||||
# v1.62: Add agent_token support for agent communication.
|
||||
# v1.63: Add support for indicators
|
||||
# v1.64: Add network_type to port.local_link_connection
|
||||
|
||||
MINOR_0_JUNO = 0
|
||||
MINOR_1_INITIAL_VERSION = 1
|
||||
@ -166,6 +167,7 @@ MINOR_60_ALLOCATION_OWNER = 60
|
||||
MINOR_61_NODE_RETIRED = 61
|
||||
MINOR_62_AGENT_TOKEN = 62
|
||||
MINOR_63_INDICATORS = 63
|
||||
MINOR_64_LOCAL_LINK_CONNECTION_NETWORK_TYPE = 64
|
||||
|
||||
# When adding another version, update:
|
||||
# - MINOR_MAX_VERSION
|
||||
@ -173,7 +175,7 @@ MINOR_63_INDICATORS = 63
|
||||
# explanation of what changed in the new version
|
||||
# - common/release_mappings.py, RELEASE_MAPPING['master']['api']
|
||||
|
||||
MINOR_MAX_VERSION = MINOR_63_INDICATORS
|
||||
MINOR_MAX_VERSION = MINOR_64_LOCAL_LINK_CONNECTION_NETWORK_TYPE
|
||||
|
||||
# String representations of the minor and maximum versions
|
||||
_MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION)
|
||||
|
@ -214,7 +214,7 @@ RELEASE_MAPPING = {
|
||||
}
|
||||
},
|
||||
'master': {
|
||||
'api': '1.63',
|
||||
'api': '1.64',
|
||||
'rpc': '1.50',
|
||||
'objects': {
|
||||
'Allocation': ['1.1'],
|
||||
|
@ -1246,6 +1246,59 @@ class TestPatch(test_api_base.BaseApiTest):
|
||||
self.assertTrue(response.json['error_message'])
|
||||
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_code)
|
||||
|
||||
def test_add_local_link_connection_network_type(self, mock_upd):
|
||||
response = self.patch_json(
|
||||
'/ports/%s' % self.port.uuid,
|
||||
[{'path': '/local_link_connection/network_type',
|
||||
'value': 'unmanaged', 'op': 'add'}],
|
||||
headers={api_base.Version.string: '1.64'})
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(http_client.OK, response.status_code)
|
||||
self.assertEqual(
|
||||
'unmanaged',
|
||||
response.json['local_link_connection']['network_type'])
|
||||
self.assertTrue(mock_upd.called)
|
||||
|
||||
kargs = mock_upd.call_args[0][2]
|
||||
self.assertEqual('unmanaged',
|
||||
kargs.local_link_connection['network_type'])
|
||||
|
||||
def test_add_local_link_connection_network_type_old_api(self, mock_upd):
|
||||
response = self.patch_json(
|
||||
'/ports/%s' % self.port.uuid,
|
||||
[{'path': '/local_link_connection/network_type',
|
||||
'value': 'unmanaged', 'op': 'add'}],
|
||||
headers={api_base.Version.string: '1.63'}, expect_errors=True)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_code)
|
||||
|
||||
def test_remove_local_link_connection_network_type(self, mock_upd):
|
||||
llc = {'network_type': 'unmanaged'}
|
||||
port = obj_utils.create_test_port(self.context,
|
||||
node_id=self.node.id,
|
||||
uuid=uuidutils.generate_uuid(),
|
||||
address='bb:bb:bb:bb:bb:bb',
|
||||
local_link_connection=llc)
|
||||
llc.pop('network_type')
|
||||
response = self.patch_json(
|
||||
'/ports/%s' % port.uuid,
|
||||
[{'path': '/local_link_connection/network_type', 'op': 'remove'}],
|
||||
headers={api_base.Version.string: '1.64'})
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(http_client.OK, response.status_code)
|
||||
self.assertTrue(mock_upd.called)
|
||||
self.assertEqual(llc, response.json['local_link_connection'])
|
||||
|
||||
def test_remove_local_link_connection_network_type_old_api(self, mock_upd):
|
||||
response = self.patch_json(
|
||||
'/ports/%s' % self.port.uuid,
|
||||
[{'path': '/local_link_connection/network_type', 'op': 'remove'}],
|
||||
headers={api_base.Version.string: '1.63'}, expect_errors=True)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_code)
|
||||
|
||||
def test_set_pxe_enabled_false_old_api(self, mock_upd):
|
||||
response = self.patch_json('/ports/%s' % self.port.uuid,
|
||||
[{'path': '/pxe_enabled',
|
||||
@ -2264,6 +2317,26 @@ class TestPost(test_api_base.BaseApiTest):
|
||||
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_int)
|
||||
self.assertFalse(mock_create.called)
|
||||
|
||||
def test_create_port_with_network_type_in_llc(self, mock_create):
|
||||
pdict = post_get_test_port(
|
||||
local_link_connection={'network_type': 'unmanaged'})
|
||||
response = self.post_json('/ports', pdict, headers=self.headers)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(http_client.CREATED, response.status_int)
|
||||
mock_create.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY,
|
||||
'test-topic')
|
||||
|
||||
def test_create_port_with_network_type_in_llc_old_api_version(
|
||||
self, mock_create):
|
||||
headers = {api_base.Version.string: '1.63'}
|
||||
pdict = post_get_test_port(
|
||||
local_link_connection={'network_type': 'unmanaged'})
|
||||
response = self.post_json('/ports', pdict, headers=headers,
|
||||
expect_errors=True)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_int)
|
||||
self.assertFalse(mock_create.called)
|
||||
|
||||
def test_create_port_with_pxe_enabled_old_api_version(self, mock_create):
|
||||
headers = {api_base.Version.string: '1.14'}
|
||||
pdict = post_get_test_port(pxe_enabled=False)
|
||||
|
@ -365,6 +365,23 @@ class TestLocalLinkConnectionType(base.TestCase):
|
||||
self.assertFalse(v.validate_for_smart_nic(value))
|
||||
self.assertRaises(exception.Invalid, v.validate, value)
|
||||
|
||||
def test_local_link_connection_net_type_unmanaged(self):
|
||||
v = types.locallinkconnectiontype
|
||||
value = {'network_type': 'unmanaged'}
|
||||
self.assertItemsEqual(value, v.validate(value))
|
||||
|
||||
def test_local_link_connection_net_type_unmanaged_combine_ok(self):
|
||||
v = types.locallinkconnectiontype
|
||||
value = {'network_type': 'unmanaged',
|
||||
'switch_id': '0a:1b:2c:3d:4e:5f',
|
||||
'port_id': 'rep0-0'}
|
||||
self.assertItemsEqual(value, v.validate(value))
|
||||
|
||||
def test_local_link_connection_net_type_invalid(self):
|
||||
v = types.locallinkconnectiontype
|
||||
value = {'network_type': 'invalid'}
|
||||
self.assertRaises(exception.Invalid, v.validate, value)
|
||||
|
||||
|
||||
@mock.patch("ironic.api.request", mock.Mock(version=mock.Mock(minor=10)))
|
||||
class TestVifType(base.TestCase):
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
To allow use of the ``neutron`` network interface in combination with
|
||||
``flat`` provider networks where no actual switch management is done. The
|
||||
`local_link_connection` field on ports is extended to support the
|
||||
``network_type`` field.
|
Loading…
x
Reference in New Issue
Block a user