NSXv: VDR interface operation performance

Change-Id: Ie36c6cbb8fc0a8055a8a3d84e8940b1c62fcba9e
This commit is contained in:
Kobi Samoray 2019-08-14 18:50:29 +03:00
parent a14b8a6f32
commit 625706119f
6 changed files with 122 additions and 68 deletions

View File

@ -0,0 +1,33 @@
# Copyright 2019 VMware, Inc.
# All Rights Reserved
#
# 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.
import time
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
def profile(func):
def wrap(*args, **kwargs):
f_name = '{}.{}'.format(func.__module__, func.__name__)
started_at = time.time()
result = func(*args, **kwargs)
LOG.debug(">>>>>>>>>>>>> Method %(method)s execution time %(time)f",
{'method': f_name, 'time': time.time() - started_at})
return result
return wrap

View File

@ -116,8 +116,9 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
network = self.get_network(context, net_id)
return network.get(ext_address_scope.IPV4_ADDRESS_SCOPE)
def _get_subnet_address_scope(self, context, subnet_id):
subnet = self.get_subnet(context, subnet_id)
def _get_subnet_address_scope(self, context, subnet_id, subnet=None):
if not subnet:
subnet = self.get_subnet(context, subnet_id)
if not subnet['subnetpool_id']:
return
subnetpool = self.get_subnetpool(context, subnet['subnetpool_id'])
@ -148,19 +149,24 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF]}
return self.get_ports(context, filters=port_filters)
def _find_router_subnets_cidrs(self, context, router_id):
def _find_router_subnets_cidrs(self, context, router_id, subnets=None):
"""Retrieve cidrs of subnets attached to the specified router."""
subnets = self._find_router_subnets(context, router_id)
if not subnets:
subnets = self._load_router_subnet_cidrs_from_db(context,
router_id)
return [subnet['cidr'] for subnet in subnets]
def _find_router_subnets_cidrs_per_addr_scope(self, context, router_id):
def _find_router_subnets_cidrs_per_addr_scope(self, context, router_id,
subnets=None):
"""Generate a list of cidrs per address pool.
Go over all the router interface subnets.
return a list of lists of subnets cidrs belonging to same
address pool.
"""
subnets = self._find_router_subnets(context, router_id)
if not subnets:
subnets = self._load_router_subnet_cidrs_from_db(context,
router_id)
cidrs_map = {}
for subnet in subnets:
ads = self._get_subnetpool_address_scope(
@ -195,22 +201,27 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
else:
filters['id'] = [entry['port_id'] for entry in bindings]
def _find_router_subnets(self, context, router_id):
def _load_router_subnet_cidrs_from_db(self, context, router_id):
"""Retrieve subnets attached to the specified router."""
ports = self._get_port_by_device_id(context, router_id,
l3_db.DEVICE_OWNER_ROUTER_INTF)
# No need to check for overlapping CIDRs
subnets = []
subnet_ids = []
for port in ports:
for ip in port.get('fixed_ips', []):
subnet_qry = context.session.query(models_v2.Subnet)
subnet = subnet_qry.filter_by(id=ip.subnet_id).one()
subnets.append({'id': subnet.id, 'cidr': subnet.cidr,
'subnetpool_id': subnet.subnetpool_id,
'ip_version': subnet.ip_version,
'network_id': subnet.network_id,
'gateway_ip': subnet.gateway_ip,
'ipv6_address_mode': subnet.ipv6_address_mode})
subnet_ids.append(ip.subnet_id)
subnet_qry = context.session.query(models_v2.Subnet)
db_subnets = subnet_qry.filter(
models_v2.Subnet.id.in_(subnet_ids)).all()
subnets = [{'id': subnet.id,
'cidr': subnet.cidr,
'subnetpool_id': subnet.subnetpool_id,
'ip_version': subnet.ip_version,
'network_id': subnet.network_id,
'gateway_ip': subnet.gateway_ip,
'ipv6_address_mode': subnet.ipv6_address_mode}
for subnet in db_subnets]
return subnets
def _find_router_gw_subnets(self, context, router):
@ -253,8 +264,8 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
LOG.info("Inspecting routers for potential configuration changes "
"due to address scope change on subnetpool %s", subnetpool_id)
for rtr in routers:
subnets = self._find_router_subnets(elevated_context,
rtr['id'])
subnets = self._load_router_subnet_cidrs_from_db(elevated_context,
rtr['id'])
gw_subnets = self._find_router_gw_subnets(elevated_context,
rtr)

View File

@ -895,8 +895,9 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
profile_id = SLAAC_NDRA_PROFILE_ID
if delete:
rtr_subnets = self._find_router_subnets(context.elevated(),
router_id)
rtr_subnets = self._load_router_subnet_cidrs_from_db(
context.elevated(), router_id)
# check if there is another slaac overlay subnet that needs
# advertising (vlan advertising is attached on interface level)
slaac_subnets = [s for s in rtr_subnets
@ -1618,7 +1619,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
orgaddr, orgmask, _orgnexthop = (
self._get_external_attachment_info(
context, router))
router_subnets = self._find_router_subnets(
router_subnets = self._load_router_subnet_cidrs_from_db(
context.elevated(), router_id)
self._validate_router_gw_and_tz(context, router_id, info,
org_enable_snat, router_subnets)
@ -1637,8 +1638,6 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
new_enable_snat = router.enable_snat
newaddr, newmask, _newnexthop = self._get_external_attachment_info(
context, router)
router_subnets = self._find_router_subnets(
context.elevated(), router_id)
sr_currently_exists = self.verify_sr_at_backend(router_id)
fw_exist = self._router_has_edge_fw_rules(context, router)
# In this case the following operation must be executed regardless
@ -1916,7 +1915,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
self._validate_interface_address_scope(context, router_db, info)
# Check GW & subnets TZ
subnets = self._find_router_subnets(context.elevated(), router_id)
subnets = self._load_router_subnet_cidrs_from_db(
context.elevated(), router_id)
tier0_uuid = self._get_tier0_uuid_by_router(
context.elevated(), router_db)
#TODO(asarfaty): it is enough to validate only the new subnet,
@ -2013,8 +2013,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
overlay_net = self._is_overlay_network(context, network_id)
segment_id = self._get_network_nsx_segment_id(context, network_id)
subnets = self._find_router_subnets(context.elevated(),
router_id)
subnets = self._load_router_subnet_cidrs_from_db(context.elevated(),
router_id)
try:
if overlay_net:
# Remove the tier1 router from this segment on the NSX

View File

@ -3869,43 +3869,47 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
address_groups.append(address_group)
return address_groups
def _get_nat_rules(self, context, router):
def _get_dnat_rules(self, context, router):
fip_qry = context.session.query(l3_db_models.FloatingIP)
fip_db = fip_qry.filter_by(router_id=router['id']).all()
snat = []
dnat = [{'dst': fip.floating_ip_address,
'translated': fip.fixed_ip_address}
for fip in fip_db if fip.fixed_port_id]
return dnat
def _get_nat_rules(self, context, router):
snat = []
dnat = self._get_dnat_rules(context, router)
gw_port = router.gw_port
if gw_port and gw_port.get('fixed_ips') and router.enable_snat:
snat_ip = gw_port['fixed_ips'][0]['ip_address']
subnets = self._find_router_subnets(context.elevated(),
router['id'])
for subnet in subnets:
# Do not build NAT rules for v6
if subnet.get('ip_version') == 6:
continue
# if the subnets address scope is the same as the gateways:
# no need for SNAT
gw_address_scope = self._get_network_address_scope(
context.elevated(), gw_port['network_id'])
subnet_address_scope = self._get_subnetpool_address_scope(
context.elevated(), subnet['subnetpool_id'])
if (gw_address_scope and
gw_address_scope == subnet_address_scope):
LOG.info("No need for SNAT rule for router %(router)s "
"and subnet %(subnet)s because they use the "
"same address scope %(addr_scope)s.",
{'router': router['id'],
'subnet': subnet['id'],
'addr_scope': gw_address_scope})
continue
subnets = self._load_router_subnet_cidrs_from_db(
context.elevated(), router['id'])
gw_address_scope = self._get_network_address_scope(
context.elevated(), gw_port['network_id'])
if gw_address_scope:
for subnet in subnets:
# Do not build NAT rules for v6
if subnet.get('ip_version') == 6:
continue
# if the subnets address scope is the same as the gateways:
# no need for SNAT
subnet_address_scope = self._get_subnetpool_address_scope(
context.elevated(), subnet['subnetpool_id'])
if gw_address_scope == subnet_address_scope:
LOG.info("No need for SNAT rule for router %(router)s "
"and subnet %(subnet)s because they use the "
"same address scope %(addr_scope)s.",
{'router': router['id'],
'subnet': subnet['id'],
'addr_scope': gw_address_scope})
continue
snat.append(self._get_default_nat_rule(
context, router['id'], subnet, snat_ip))
snat.append(self._get_default_nat_rule(
context, router['id'], subnet, snat_ip))
return (snat, dnat)
def _get_default_nat_rule(self, context, router_id, subnet, snat_ip):
@ -3921,13 +3925,14 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
rule['vnic_index'] = vcns_const.EXTERNAL_VNIC_INDEX
return rule
def _get_nosnat_subnets_fw_rules(self, context, router):
def _get_nosnat_subnets_fw_rules(self, context, router, subnets=None):
"""Open edge firewall holes for nosnat subnets to do static routes."""
no_snat_fw_rules = []
gw_port = router.gw_port
if gw_port and not router.enable_snat:
subnet_cidrs = self._find_router_subnets_cidrs(context.elevated(),
router['id'])
router['id'],
subnets)
if subnet_cidrs:
no_snat_fw_rules.append({
'name': NO_SNAT_RULE_NAME,
@ -3937,7 +3942,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
'destination_ip_address': subnet_cidrs})
return no_snat_fw_rules
def _get_allocation_pools_fw_rule(self, context, router):
def _get_allocation_pools_fw_rule(self, context, router, subnets=None):
"""Get the firewall rule for the default gateway address pool
Return the firewall rule that should be added in order to allow
@ -3953,8 +3958,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
if gw_address_scope is None:
return
subnets = self._find_router_subnets(context.elevated(),
router['id'])
if not subnets:
subnets = self._load_router_subnet_cidrs_from_db(
context.elevated(), router['id'])
no_nat_cidrs = []
for subnet in subnets:
# if the subnets address scope is the same as the gateways:
@ -3973,7 +3979,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
def _get_dnat_fw_rule(self, context, router):
# Get FW rule to open dnat firewall flows
_, dnat_rules = self._get_nat_rules(context, router)
dnat_rules = self._get_dnat_rules(context, router)
dnat_cidrs = [rule['dst'] for rule in dnat_rules]
if dnat_cidrs:
return {
@ -3982,12 +3988,12 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
'enabled': True,
'destination_ip_address': dnat_cidrs}
def _get_subnet_fw_rules(self, context, router):
def _get_subnet_fw_rules(self, context, router, subnets=None):
# Get FW rule/s to open subnets firewall flows and static routes
# relative flows
fw_rules = []
subnet_cidrs_per_ads = self._find_router_subnets_cidrs_per_addr_scope(
context.elevated(), router['id'])
context.elevated(), router['id'], subnets=subnets)
routes = self._get_extra_routes_by_router_id(context, router['id'])
routes_dest = [route['destination'] for route in routes]
for subnet_cidrs in subnet_cidrs_per_ads:
@ -4244,7 +4250,10 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
# Add FW rule/s to open subnets firewall flows and static routes
# relative flows
subnet_rules = self._get_subnet_fw_rules(context, router_db)
subnets = self._load_router_subnet_cidrs_from_db(context.elevated(),
router_id)
subnet_rules = self._get_subnet_fw_rules(context, router_db,
subnets=subnets)
if subnet_rules:
fw_rules.extend(subnet_rules)
@ -4271,13 +4280,13 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
# Add rule for not NAT-ed allocation pools
alloc_pool_rule = self._get_allocation_pools_fw_rule(
context, router_db)
context, router_db, subnets=subnets)
if alloc_pool_rule:
fw_rules.append(alloc_pool_rule)
# Add no-snat rules
nosnat_fw_rules = self._get_nosnat_subnets_fw_rules(
context, router_db)
context, router_db, subnets=subnets)
fw_rules.extend(nosnat_fw_rules)
vpn_plugin = directory.get_plugin(plugin_const.VPN)

View File

@ -2170,7 +2170,7 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
self._get_external_attachment_info(
context, router))
router_subnets = self._find_router_subnets(
router_subnets = self._load_router_subnet_cidrs_from_db(
context.elevated(), router_id)
self._validate_router_gw_and_tz(context, router_id, info,
org_enable_snat, router_subnets)
@ -2760,9 +2760,9 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
resource_type = (None if overlay_net else
nsxlib_consts.LROUTERPORT_CENTRALIZED)
# Check GW & subnets TZ
subnets = self._find_router_subnets(context.elevated(),
router_id)
# If this is an ENS case - check GW & subnets
subnets = self._load_router_subnet_cidrs_from_db(
context.elevated(), router_id)
tier0_uuid = self._get_tier0_uuid_by_router(context.elevated(),
router_db)
self._validate_router_tz(context.elevated(), tier0_uuid, subnets)

View File

@ -156,7 +156,8 @@ class EdgeLoadbalancerDriverV2(base_mgr.LoadbalancerBaseManager):
# Also check if there are any loadbalancers attached to this router
# subnets
router_subnets = self.loadbalancer.core_plugin._find_router_subnets(
core_plugin = self.loadbalancer.core_plugin
router_subnets = core_plugin._load_router_subnet_cidrs_from_db(
context.elevated(), router_id)
subnet_ids = [subnet['id'] for subnet in router_subnets]
if subnet_ids and self._get_lb_ports(context.elevated(), subnet_ids):