Merge "Speed up trunk MTU enforcement check"

This commit is contained in:
Zuul 2018-01-17 01:30:32 +00:00 committed by Gerrit Code Review
commit 0444c21d41
2 changed files with 64 additions and 32 deletions

View File

@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import collections
from neutron_lib.api import converters
from neutron_lib.api.definitions import portbindings
from neutron_lib.api.definitions import provider_net as provider
@ -167,40 +169,63 @@ class SubPortsValidator(object):
if trunk_validation:
trunk_port_mtu = self._get_port_mtu(context, self.trunk_port_id)
self._prepare_subports(context)
return [self._validate(context, s, trunk_port_mtu)
subport_mtus = self._prepare_subports(context)
return [self._validate(context, s, trunk_port_mtu, subport_mtus)
for s in self.subports]
else:
return self.subports
def _prepare_subports(self, context):
"""Update subports segmentation details if INHERIT is requested."""
port_ids = {
s['port_id']: i
for i, s in enumerate(self.subports)
if s.get('segmentation_type') == constants.INHERIT
}
"""Utility method to parse subports in the request
The objective of this method is two-fold:
* Update subports segmentation details if INHERIT is requested;
* Return the MTU for each of the subport in the request.
This method does two things rather than one to allow us to hit the DB
once, and thus minimize the number of lookups required to learn about
the segmentation type and the MTU of the networks on which subports
are plugged.
"""
InheritIndex = (
collections.namedtuple("InheritIndex", "index has_inherit"))
port_ids = {}
any_has_inherit = False
for i, s in enumerate(self.subports):
has_inherit = s.get('segmentation_type') == constants.INHERIT
any_has_inherit |= has_inherit
port_ids[s['port_id']] = (
InheritIndex(index=i, has_inherit=has_inherit))
core_plugin = directory.get_plugin()
if not port_ids:
return
elif not n_utils.is_extension_supported(core_plugin, provider.ALIAS):
if (any_has_inherit and
not n_utils.is_extension_supported(core_plugin,
provider.ALIAS)):
msg = _("Cannot accept segmentation type %s") % constants.INHERIT
raise n_exc.InvalidInput(error_message=msg)
ports = core_plugin.get_ports(context, filters={'id': port_ids})
# this assumes a user does not try to trunk the same network
# more than once.
network_port_map = {
x['network_id']: {'port_id': x['id']}
for x in ports
}
network_port_map = collections.defaultdict(list)
for p in ports:
network_port_map[p['network_id']].append({'port_id': p['id']})
networks = core_plugin.get_networks(
context.elevated(), filters={'id': network_port_map})
subport_mtus = {}
for net in networks:
port = network_port_map[net['id']]
port.update({'segmentation_id': net[provider.SEGMENTATION_ID],
for port in network_port_map[net['id']]:
if port_ids[port['port_id']].has_inherit:
port.update(
{'segmentation_id': net[provider.SEGMENTATION_ID],
'segmentation_type': net[provider.NETWORK_TYPE]})
self.subports[port_ids[port['port_id']]] = port
self.subports[port_ids[port['port_id']].index] = port
# To speed up the request, record the network MTU for each
# subport to avoid hitting the DB more than necessary. Do
# that only if the extension is available.
if n_utils.is_extension_supported(core_plugin, 'net-mtu'):
subport_mtus[port['port_id']] = net[api.MTU]
return subport_mtus
def _get_port_mtu(self, context, port_id):
"""
@ -228,16 +253,19 @@ class SubPortsValidator(object):
if subport['port_id'] == self.trunk_port_id:
raise trunk_exc.ParentPortInUse(port_id=subport['port_id'])
def _raise_subport_invalid_mtu(self, context, subport, trunk_port_mtu):
def _raise_subport_invalid_mtu(
self, context, subport, trunk_port_mtu, subport_mtus):
# Check MTU sanity - subport MTU must not exceed trunk MTU.
# If for whatever reason trunk_port_mtu is not available,
# the MTU sanity check cannot be enforced.
if trunk_port_mtu:
port_mtu = self._get_port_mtu(context, subport['port_id'])
if port_mtu and port_mtu > trunk_port_mtu:
# missing MTUs for subports is not an error condition: the
# subport UUID may be invalid or non existent.
subport_mtu = subport_mtus.get(subport['port_id'])
if subport_mtu and subport_mtu > trunk_port_mtu:
raise trunk_exc.SubPortMtuGreaterThanTrunkPortMtu(
port_id=subport['port_id'],
port_mtu=port_mtu,
port_mtu=subport_mtu,
trunk_id=self.trunk_port_id,
trunk_mtu=trunk_port_mtu
)
@ -273,10 +301,11 @@ class SubPortsValidator(object):
trunk_validator = TrunkPortValidator(subport['port_id'])
trunk_validator.validate(context, parent_port=False)
def _validate(self, context, subport, trunk_port_mtu):
def _validate(self, context, subport, trunk_port_mtu, subport_mtus):
self._raise_subport_is_parent_port(context, subport)
self._raise_subport_invalid_mtu(context, subport, trunk_port_mtu)
self._raise_subport_invalid_mtu(
context, subport, trunk_port_mtu, subport_mtus)
segmentation_type, segmentation_id = (
self._raise_if_segmentation_details_missing(subport))

View File

@ -170,13 +170,16 @@ class SubPortsValidatorMtuSanityTestCase(test_plugin.Ml2PluginV2TestCase):
def test_validate_subport_mtu_set_trunks_net_exception(self):
self._test_validate_subport_trunk_mtu(1500, 'exc')
def test_validate_subport_mtu_net_exception_trunks_set(self):
self._test_validate_subport_trunk_mtu('exc', 1500)
def _test_validate_subport_trunk_mtu(
self, subport_net_mtu, trunk_net_mtu):
plugin = directory.get_plugin()
orig_get_network = plugin.get_network
orig_get_networks = plugin.get_networks
def get_networks_adjust_mtu(*args, **kwargs):
res = orig_get_networks(*args, **kwargs)
res[0][api.MTU] = subport_net_mtu
return res
def get_network_adjust_mtu(*args, **kwargs):
res = orig_get_network(*args, **kwargs)
@ -185,8 +188,6 @@ class SubPortsValidatorMtuSanityTestCase(test_plugin.Ml2PluginV2TestCase):
raise n_exc.NetworkNotFound(net_id='net-id')
res[api.MTU] = trunk_net_mtu
elif res['name'] == 'net_subport':
if subport_net_mtu == 'exc':
raise n_exc.NetworkNotFound(net_id='net-id')
res[api.MTU] = subport_net_mtu
return res
@ -197,7 +198,9 @@ class SubPortsValidatorMtuSanityTestCase(test_plugin.Ml2PluginV2TestCase):
self.subnet(network=subport_net) as subport_subnet,\
self.port(subnet=subport_subnet) as subport,\
mock.patch.object(plugin, "get_network",
side_effect=get_network_adjust_mtu):
side_effect=get_network_adjust_mtu),\
mock.patch.object(plugin, "get_networks",
side_effect=get_networks_adjust_mtu):
trunk = {'port_id': trunk_port['port']['id'],
'tenant_id': 'test_tenant',
'sub_ports': [{'port_id': subport['port']['id'],