From 8f3a066b20b7ffafec95a618d60e40727504f37c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Jens=C3=A5s?= Date: Thu, 3 May 2018 03:15:07 +0200 Subject: [PATCH] Add support to regenerate port mac_address Passing 'null' (None) as the mac address in a port update request causes the port's mac address to be re-generated using the base MAC address Neutron uses for VIFs. This change implementes a temporary lib api definition with a new converter that will generate valid mac if the data provided is None. APIImpact: Port mac_addr regenerated if None passed on update. Closes-Bug: #1768690 Change-Id: I7d04beea4810718c3b745de8ea97897b1323267e --- .../_port_mac_address_regenerate_lib.py | 59 +++++++++++++++++++ .../extensions/port_mac_address_regenerate.py | 24 ++++++++ neutron/plugins/ml2/plugin.py | 3 +- .../tests/contrib/hooks/api_all_extensions | 1 + neutron/tests/unit/plugins/ml2/test_plugin.py | 23 ++++++++ ...c-address-regenerate-312978c834abaa52.yaml | 7 +++ 6 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 neutron/extensions/_port_mac_address_regenerate_lib.py create mode 100644 neutron/extensions/port_mac_address_regenerate.py create mode 100644 releasenotes/notes/port-mac-address-regenerate-312978c834abaa52.yaml diff --git a/neutron/extensions/_port_mac_address_regenerate_lib.py b/neutron/extensions/_port_mac_address_regenerate_lib.py new file mode 100644 index 00000000000..4a75cbcd8c1 --- /dev/null +++ b/neutron/extensions/_port_mac_address_regenerate_lib.py @@ -0,0 +1,59 @@ +# 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. + + +""" +TODO(hjensas): This module should be deleted once neutron-lib containing +Change-Id: Ibfcf179c2051d2bf47d4d1e62e8146cbee29fd64 is released. +""" + +from neutron_lib.api.definitions import port as port_def +from neutron_lib import constants +from neutron_lib.utils import net as net_utils +from oslo_config import cfg + + +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 + + +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: { + 'mac_address': {'allow_post': True, 'allow_put': True, + 'default': constants.ATTR_NOT_SPECIFIED, + 'convert_to': 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/extensions/port_mac_address_regenerate.py b/neutron/extensions/port_mac_address_regenerate.py new file mode 100644 index 00000000000..261299c8c5b --- /dev/null +++ b/neutron/extensions/port_mac_address_regenerate.py @@ -0,0 +1,24 @@ +# 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. + +# TODO(hjensas) When we have neutron-lib release, use it. +# from neutron_lib.api.definitions import port_mac_address_regenerate as apidef + +from neutron.extensions import _port_mac_address_regenerate_lib as apidef +from neutron_lib.api import extensions as api_extensions + + +class Port_mac_address_regenerate(api_extensions.APIExtensionDescriptor): + """Extension to support port MAC address regeneration""" + + api_definition = apidef diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index 9f5dbeb225c..9de2780c35f 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -159,7 +159,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, "subnet-service-types", "ip-substring-filtering", "port-security-groups-filtering", - "empty-string-filtering"] + "empty-string-filtering", + "port-mac-address-regenerate"] @property def supported_extension_aliases(self): diff --git a/neutron/tests/contrib/hooks/api_all_extensions b/neutron/tests/contrib/hooks/api_all_extensions index 0045964345d..e0fd422bc2c 100644 --- a/neutron/tests/contrib/hooks/api_all_extensions +++ b/neutron/tests/contrib/hooks/api_all_extensions @@ -40,6 +40,7 @@ NETWORK_API_EXTENSIONS+=",rbac-policies" NETWORK_API_EXTENSIONS+=",router" NETWORK_API_EXTENSIONS+=",router_availability_zone" NETWORK_API_EXTENSIONS+=",security-group" +NETWORK_API_EXTENSIONS+=",port-mac-address-regenerate" NETWORK_API_EXTENSIONS+=",port-security-groups-filtering" NETWORK_API_EXTENSIONS+=",segment" NETWORK_API_EXTENSIONS+=",service-type" diff --git a/neutron/tests/unit/plugins/ml2/test_plugin.py b/neutron/tests/unit/plugins/ml2/test_plugin.py index 8d135599a66..bc28aedc9bc 100644 --- a/neutron/tests/unit/plugins/ml2/test_plugin.py +++ b/neutron/tests/unit/plugins/ml2/test_plugin.py @@ -962,6 +962,29 @@ class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase): host_arg={portbindings.HOST_ID: HOST}, arg_list=(portbindings.HOST_ID,)) + def test_update_port_regenerate_mac(self): + ctx = context.get_admin_context() + plugin = directory.get_plugin() + data = {'port': {'mac_address': None}} + with self.port() as port: + current_mac = port['port']['mac_address'] + req = self.new_update_request('ports', data, port['port']['id']) + self.assertEqual(200, req.get_response(self.api).status_int) + new_mac = plugin.get_port(ctx, port['port']['id'])['mac_address'] + self.assertNotEqual(current_mac, new_mac) + self.assertTrue(netaddr.valid_mac(new_mac)) + + def test_update_port_mac_does_not_change(self): + ctx = context.get_admin_context() + plugin = directory.get_plugin() + data = {'port': {'description': 'Port Description'}} + with self.port() as port: + current_mac = port['port']['mac_address'] + req = self.new_update_request('ports', data, port['port']['id']) + self.assertEqual(200, req.get_response(self.api).status_int) + new_mac = plugin.get_port(ctx, port['port']['id'])['mac_address'] + self.assertEqual(current_mac, new_mac) + def test_update_non_existent_port(self): ctx = context.get_admin_context() plugin = directory.get_plugin() diff --git a/releasenotes/notes/port-mac-address-regenerate-312978c834abaa52.yaml b/releasenotes/notes/port-mac-address-regenerate-312978c834abaa52.yaml new file mode 100644 index 00000000000..faddd9f1736 --- /dev/null +++ b/releasenotes/notes/port-mac-address-regenerate-312978c834abaa52.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Adds api extenstion ``port-mac-address-regenerate``. When passing + ``'null'`` (``None``) as the ``mac_address`` on port update a converter + will generate a new mac address that will be assigned to the port. + `RFE: #1768690 `_.