Speed up trunk MTU enforcement check
While things may have become generally slower for reasons still
to be determined at the time this patch was developed, we noticed
that enforcing MTU rules between subports and trunk parent was
done by performing one DB lookup per subport. This patch optmizes
the performance by piggybacking on the logic that is used to
retrieve the subport underlying network's segmentation type when
the segmentation type must be inherited.
Closes-bug: #1741954
Change-Id: Ib18427ef7a3509088eaf8fa0cfed3fb659b6baea
(cherry picked from commit d23a9ad731
)
This commit is contained in:
parent
d8399f1dd6
commit
5ab223d1bd
|
@ -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))
|
||||
|
|
|
@ -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'],
|
||||
|
|
Loading…
Reference in New Issue