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
This commit is contained in:
Harald Jensås 2018-05-03 03:15:07 +02:00
parent 9cb68ce777
commit 8f3a066b20
6 changed files with 116 additions and 1 deletions

View File

@ -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 = {}

View File

@ -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

View File

@ -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):

View File

@ -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"

View File

@ -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()

View File

@ -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 <https://bugs.launchpad.net/neutron/+bug/1768690>`_.