From 56033ba643812a30577f6ab17648806c2ee494ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Jens=C3=A5s?= Date: Thu, 3 May 2018 03:11:19 +0200 Subject: [PATCH] Add support to regenerate port mac_address - Adds converter to generate a valid mac address if the provided data is 'null' (None). - Adds api extension definition for port using the new converter. Related-Bug: #1768690 Change-Id: Ibfcf179c2051d2bf47d4d1e62e8146cbee29fd64 --- api-ref/source/v2/ports.inc | 8 ++++ neutron_lib/api/converters.py | 14 +++++++ neutron_lib/api/definitions/__init__.py | 2 + neutron_lib/api/definitions/port.py | 16 +++---- .../port_mac_address_regenerate.py | 42 +++++++++++++++++++ .../test_port_mac_address_regenerate.py | 20 +++++++++ .../tests/unit/api/test_conversions.py | 16 +++++++ ...c-address-regenerate-cc33d03216b5bc3d.yaml | 9 ++++ 8 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 neutron_lib/api/definitions/port_mac_address_regenerate.py create mode 100644 neutron_lib/tests/unit/api/definitions/test_port_mac_address_regenerate.py create mode 100644 releasenotes/notes/port-mac-address-regenerate-cc33d03216b5bc3d.yaml diff --git a/api-ref/source/v2/ports.inc b/api-ref/source/v2/ports.inc index fd1547403..154bece98 100644 --- a/api-ref/source/v2/ports.inc +++ b/api-ref/source/v2/ports.inc @@ -96,6 +96,14 @@ define QoS policies and associate these to the ports by introducing the ``qos_policy_id`` attribute. The policies should be created before they are associated to the ports. +Regenerate mac address extension +================================ + +The Port MAC address regenerate extension (``port-mac-address-regenerate``) +makes it possible to regenerate the mac address of a port. When passing +``'null'`` (``None``) as the ``mac_address`` on port update, a new mac address +will be generated and set on the port. + Resource timestamps =================== diff --git a/neutron_lib/api/converters.py b/neutron_lib/api/converters.py index 00e94dfb7..598004bb4 100644 --- a/neutron_lib/api/converters.py +++ b/neutron_lib/api/converters.py @@ -11,6 +11,7 @@ # under the License. import netaddr +from oslo_config import cfg from oslo_utils import strutils import six @@ -18,6 +19,7 @@ from neutron_lib._i18n import _ from neutron_lib.api import validators from neutron_lib import constants from neutron_lib import exceptions as n_exc +from neutron_lib.utils import net as net_utils def convert_to_boolean(data): @@ -306,3 +308,15 @@ def convert_uppercase_ip(data): the return value is data with the first two letter uppercased """ return convert_prefix_forced_case(data, "IP") + + +def convert_to_mac_if_none(data): + """Convert to a random mac address if data is None + + :param data: The data value + :return: Random mac address if data is None, else return data. + """ + if data is None: + return net_utils.get_random_mac(cfg.CONF.base_mac.split(':')) + + return data diff --git a/neutron_lib/api/definitions/__init__.py b/neutron_lib/api/definitions/__init__.py index 9c9679b1d..cbcdf322d 100644 --- a/neutron_lib/api/definitions/__init__.py +++ b/neutron_lib/api/definitions/__init__.py @@ -59,6 +59,7 @@ from neutron_lib.api.definitions import network_mtu from neutron_lib.api.definitions import network_mtu_writable from neutron_lib.api.definitions import pagination from neutron_lib.api.definitions import port +from neutron_lib.api.definitions import port_mac_address_regenerate from neutron_lib.api.definitions import port_security from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import portbindings_extended @@ -141,6 +142,7 @@ _ALL_API_DEFINITIONS = { network_mtu_writable, pagination, port, + port_mac_address_regenerate, port_security, portbindings, portbindings_extended, diff --git a/neutron_lib/api/definitions/port.py b/neutron_lib/api/definitions/port.py index 6c7cb997e..6948f2362 100644 --- a/neutron_lib/api/definitions/port.py +++ b/neutron_lib/api/definitions/port.py @@ -24,6 +24,8 @@ UPDATED_TIMESTAMP = "2012-01-01T10:00:00-00:00" RESOURCE_NAME = 'port' COLLECTION_NAME = 'ports' +PORT_MAC_ADDRESS = 'mac_address' + RESOURCE_ATTRIBUTE_MAP = { COLLECTION_NAME: { 'id': {'allow_post': False, 'allow_put': False, @@ -50,13 +52,13 @@ RESOURCE_ATTRIBUTE_MAP = { 'is_filter': True, 'is_sort_key': True, 'is_visible': True}, - 'mac_address': {'allow_post': True, 'allow_put': True, - 'default': constants.ATTR_NOT_SPECIFIED, - 'validate': {'type:mac_address': None}, - 'enforce_policy': True, - 'is_filter': True, - 'is_sort_key': True, - 'is_visible': True}, + PORT_MAC_ADDRESS: {'allow_post': True, 'allow_put': True, + 'default': constants.ATTR_NOT_SPECIFIED, + 'validate': {'type:mac_address': None}, + 'enforce_policy': True, + 'is_filter': True, + 'is_sort_key': True, + 'is_visible': True}, 'fixed_ips': {'allow_post': True, 'allow_put': True, 'default': constants.ATTR_NOT_SPECIFIED, 'convert_list_to': diff --git a/neutron_lib/api/definitions/port_mac_address_regenerate.py b/neutron_lib/api/definitions/port_mac_address_regenerate.py new file mode 100644 index 000000000..19889982d --- /dev/null +++ b/neutron_lib/api/definitions/port_mac_address_regenerate.py @@ -0,0 +1,42 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from neutron_lib.api import converters +from neutron_lib.api.definitions import port as port_def +from neutron_lib import constants + + +NAME = 'Neutron Port MAC address regenerate' +ALIAS = 'port-mac-address-regenerate' +DESCRIPTION = "Network port MAC address regenerate" + +UPDATED_TIMESTAMP = "2018-05-03T10:00:00-00:00" + +RESOURCE_ATTRIBUTE_MAP = { + port_def.COLLECTION_NAME: { + port_def.PORT_MAC_ADDRESS: { + 'allow_post': True, 'allow_put': True, + 'default': constants.ATTR_NOT_SPECIFIED, + 'convert_to': converters.convert_to_mac_if_none, + 'validate': {'type:mac_address': None}, + 'enforce_policy': True, + 'is_visible': True}, + } +} + +IS_SHIM_EXTENSION = False +IS_STANDARD_ATTR_EXTENSION = False +SUB_RESOURCE_ATTRIBUTE_MAP = {} +ACTION_MAP = {} +REQUIRED_EXTENSIONS = [] +OPTIONAL_EXTENSIONS = [] +ACTION_STATUS = {} diff --git a/neutron_lib/tests/unit/api/definitions/test_port_mac_address_regenerate.py b/neutron_lib/tests/unit/api/definitions/test_port_mac_address_regenerate.py new file mode 100644 index 000000000..f61313411 --- /dev/null +++ b/neutron_lib/tests/unit/api/definitions/test_port_mac_address_regenerate.py @@ -0,0 +1,20 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from neutron_lib.api.definitions import port as port_def +from neutron_lib.api.definitions import port_mac_address_regenerate +from neutron_lib.tests.unit.api.definitions import base + + +class PortMacAddressRegenerateTestCase(base.DefinitionBaseTestCase): + extension_module = port_mac_address_regenerate + extension_attributes = (port_def.PORT_MAC_ADDRESS,) diff --git a/neutron_lib/tests/unit/api/test_conversions.py b/neutron_lib/tests/unit/api/test_conversions.py index 55d06fe3e..a5948848c 100644 --- a/neutron_lib/tests/unit/api/test_conversions.py +++ b/neutron_lib/tests/unit/api/test_conversions.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +import mock +import netaddr import six import testtools @@ -331,3 +333,17 @@ class TestConvertUppercasePrefix(base.BaseTestCase): self.assertEqual('fo', converters.convert_prefix_forced_case('fo', 'foo')) + + +class TestConvertPortMacAddress(base.BaseTestCase): + + def test_mac_address_does_not_convert(self): + valid_mac = 'fa:16:3e:b6:78:1f' + self.assertEqual(valid_mac, + converters.convert_to_mac_if_none(valid_mac)) + + @mock.patch('oslo_config.cfg.CONF') + def test_convert_none_to_mac_address(self, CONF): + CONF.base_mac = 'fa:16:3e:00:00:00' + self.assertTrue( + netaddr.valid_mac(converters.convert_to_mac_if_none(None))) diff --git a/releasenotes/notes/port-mac-address-regenerate-cc33d03216b5bc3d.yaml b/releasenotes/notes/port-mac-address-regenerate-cc33d03216b5bc3d.yaml new file mode 100644 index 000000000..bc1f9ad6d --- /dev/null +++ b/releasenotes/notes/port-mac-address-regenerate-cc33d03216b5bc3d.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Adds api extension ``port-mac-address-regenerate``. Also adds converter + ``convert_to_mac_if_none`` used by api extenstion + ``port-mac-address-regenerate``. When passing ``'null'`` (``None``) as the + ``mac_address`` on port update the converter will generate a new mac + address that will be assigned to the port. + `RFE: #1768690 `_.