NSXv: VDR interface operation performance

Change-Id: Ie36c6cbb8fc0a8055a8a3d84e8940b1c62fcba9e
This commit is contained in:
Kobi Samoray 2019-08-14 18:50:29 +03:00
parent e06238a2b4
commit f11709dba5
5 changed files with 113 additions and 59 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

@ -150,19 +150,24 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF]} 'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF]}
return self.get_ports(context, filters=port_filters) 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.""" """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] 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. """Generate a list of cidrs per address pool.
Go over all the router interface subnets. Go over all the router interface subnets.
return a list of lists of subnets cidrs belonging to same return a list of lists of subnets cidrs belonging to same
address pool. 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 = {} cidrs_map = {}
for subnet in subnets: for subnet in subnets:
ads = self._get_subnetpool_address_scope( ads = self._get_subnetpool_address_scope(
@ -197,22 +202,27 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
else: else:
filters['id'] = [entry['port_id'] for entry in bindings] 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.""" """Retrieve subnets attached to the specified router."""
ports = self._get_port_by_device_id(context, router_id, ports = self._get_port_by_device_id(context, router_id,
l3_db.DEVICE_OWNER_ROUTER_INTF) l3_db.DEVICE_OWNER_ROUTER_INTF)
# No need to check for overlapping CIDRs # No need to check for overlapping CIDRs
subnets = [] subnet_ids = []
for port in ports: for port in ports:
for ip in port.get('fixed_ips', []): for ip in port.get('fixed_ips', []):
subnet_qry = context.session.query(models_v2.Subnet) subnet_ids.append(ip.subnet_id)
subnet = subnet_qry.filter_by(id=ip.subnet_id).one()
subnets.append({'id': subnet.id, 'cidr': subnet.cidr, subnet_qry = context.session.query(models_v2.Subnet)
'subnetpool_id': subnet.subnetpool_id, db_subnets = subnet_qry.filter(
'ip_version': subnet.ip_version, models_v2.Subnet.id.in_(subnet_ids)).all()
'network_id': subnet.network_id, subnets = [{'id': subnet.id,
'gateway_ip': subnet.gateway_ip, 'cidr': subnet.cidr,
'ipv6_address_mode': subnet.ipv6_address_mode}) '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 return subnets
def _find_router_gw_subnets(self, context, router): def _find_router_gw_subnets(self, context, router):
@ -255,8 +265,8 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
LOG.info("Inspecting routers for potential configuration changes " LOG.info("Inspecting routers for potential configuration changes "
"due to address scope change on subnetpool %s", subnetpool_id) "due to address scope change on subnetpool %s", subnetpool_id)
for rtr in routers: for rtr in routers:
subnets = self._find_router_subnets(elevated_context, subnets = self._load_router_subnet_cidrs_from_db(elevated_context,
rtr['id']) rtr['id'])
gw_subnets = self._find_router_gw_subnets(elevated_context, gw_subnets = self._find_router_gw_subnets(elevated_context,
rtr) rtr)

View File

@ -892,6 +892,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
profile_id = SLAAC_NDRA_PROFILE_ID profile_id = SLAAC_NDRA_PROFILE_ID
if delete: if delete:
router_subnets = self._load_router_subnet_cidrs_from_db(
context.elevated(), router_id)
# check if there is another slaac overlay subnet that needs # check if there is another slaac overlay subnet that needs
# advertising (vlan advertising is attached on interface level) # advertising (vlan advertising is attached on interface level)
slaac_subnets = [s for s in router_subnets slaac_subnets = [s for s in router_subnets
@ -1624,7 +1626,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
orgaddr, orgmask, _orgnexthop = ( orgaddr, orgmask, _orgnexthop = (
self._get_external_attachment_info( self._get_external_attachment_info(
context, router)) context, router))
router_subnets = self._find_router_subnets( router_subnets = self._load_router_subnet_cidrs_from_db(
context.elevated(), router_id) context.elevated(), router_id)
self._validate_router_gw_and_tz(context, router_id, info, self._validate_router_gw_and_tz(context, router_id, info,
org_enable_snat, router_subnets) org_enable_snat, router_subnets)
@ -1966,8 +1968,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
self._validate_router_tz(context.elevated(), tier0_uuid, [subnet]) self._validate_router_tz(context.elevated(), tier0_uuid, [subnet])
segment_id = self._get_network_nsx_segment_id(context, network_id) segment_id = self._get_network_nsx_segment_id(context, network_id)
rtr_subnets = self._find_router_subnets(context.elevated(), rtr_subnets = self._load_router_subnet_cidrs_from_db(
router_id) context.elevated(), router_id)
if overlay_net: if overlay_net:
# overlay interface # overlay interface
pol_subnets = [] pol_subnets = []
@ -2055,8 +2057,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
overlay_net = self._is_overlay_network(context, network_id) overlay_net = self._is_overlay_network(context, network_id)
segment_id = self._get_network_nsx_segment_id(context, network_id) segment_id = self._get_network_nsx_segment_id(context, network_id)
rtr_subnets = self._find_router_subnets(context.elevated(), rtr_subnets = self._load_router_subnet_cidrs_from_db(
router_id) context.elevated(), router_id)
try: try:
if overlay_net: if overlay_net:
# Remove the tier1 router from this segment on the NSX # 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) address_groups.append(address_group)
return address_groups 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_qry = context.session.query(l3_db_models.FloatingIP)
fip_db = fip_qry.filter_by(router_id=router['id']).all() fip_db = fip_qry.filter_by(router_id=router['id']).all()
snat = []
dnat = [{'dst': fip.floating_ip_address, dnat = [{'dst': fip.floating_ip_address,
'translated': fip.fixed_ip_address} 'translated': fip.fixed_ip_address}
for fip in fip_db if fip.fixed_port_id] 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 gw_port = router.gw_port
if gw_port and gw_port.get('fixed_ips') and router.enable_snat: if gw_port and gw_port.get('fixed_ips') and router.enable_snat:
snat_ip = gw_port['fixed_ips'][0]['ip_address'] snat_ip = gw_port['fixed_ips'][0]['ip_address']
subnets = self._find_router_subnets(context.elevated(), subnets = self._load_router_subnet_cidrs_from_db(
router['id']) context.elevated(), router['id'])
for subnet in subnets: gw_address_scope = self._get_network_address_scope(
# Do not build NAT rules for v6 context.elevated(), gw_port['network_id'])
if subnet.get('ip_version') == 6: if gw_address_scope:
continue for subnet in subnets:
# if the subnets address scope is the same as the gateways: # Do not build NAT rules for v6
# no need for SNAT if subnet.get('ip_version') == 6:
gw_address_scope = self._get_network_address_scope( continue
context.elevated(), gw_port['network_id']) # if the subnets address scope is the same as the gateways:
subnet_address_scope = self._get_subnetpool_address_scope( # no need for SNAT
context.elevated(), subnet['subnetpool_id']) subnet_address_scope = self._get_subnetpool_address_scope(
if (gw_address_scope and context.elevated(), subnet['subnetpool_id'])
gw_address_scope == subnet_address_scope): if gw_address_scope == subnet_address_scope:
LOG.info("No need for SNAT rule for router %(router)s " LOG.info("No need for SNAT rule for router %(router)s "
"and subnet %(subnet)s because they use the " "and subnet %(subnet)s because they use the "
"same address scope %(addr_scope)s.", "same address scope %(addr_scope)s.",
{'router': router['id'], {'router': router['id'],
'subnet': subnet['id'], 'subnet': subnet['id'],
'addr_scope': gw_address_scope}) 'addr_scope': gw_address_scope})
continue continue
snat.append(self._get_default_nat_rule( snat.append(self._get_default_nat_rule(
context, router['id'], subnet, snat_ip)) context, router['id'], subnet, snat_ip))
return (snat, dnat) return (snat, dnat)
def _get_default_nat_rule(self, context, router_id, subnet, snat_ip): 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 rule['vnic_index'] = vcns_const.EXTERNAL_VNIC_INDEX
return rule 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.""" """Open edge firewall holes for nosnat subnets to do static routes."""
no_snat_fw_rules = [] no_snat_fw_rules = []
gw_port = router.gw_port gw_port = router.gw_port
if gw_port and not router.enable_snat: if gw_port and not router.enable_snat:
subnet_cidrs = self._find_router_subnets_cidrs(context.elevated(), subnet_cidrs = self._find_router_subnets_cidrs(context.elevated(),
router['id']) router['id'],
subnets)
if subnet_cidrs: if subnet_cidrs:
no_snat_fw_rules.append({ no_snat_fw_rules.append({
'name': NO_SNAT_RULE_NAME, 'name': NO_SNAT_RULE_NAME,
@ -3937,7 +3942,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
'destination_ip_address': subnet_cidrs}) 'destination_ip_address': subnet_cidrs})
return no_snat_fw_rules 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 """Get the firewall rule for the default gateway address pool
Return the firewall rule that should be added in order to allow 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: if gw_address_scope is None:
return return
subnets = self._find_router_subnets(context.elevated(), if not subnets:
router['id']) subnets = self._load_router_subnet_cidrs_from_db(
context.elevated(), router['id'])
no_nat_cidrs = [] no_nat_cidrs = []
for subnet in subnets: for subnet in subnets:
# if the subnets address scope is the same as the gateways: # 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): def _get_dnat_fw_rule(self, context, router):
# Get FW rule to open dnat firewall flows # 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] dnat_cidrs = [rule['dst'] for rule in dnat_rules]
if dnat_cidrs: if dnat_cidrs:
return { return {
@ -3982,12 +3988,12 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
'enabled': True, 'enabled': True,
'destination_ip_address': dnat_cidrs} '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 # Get FW rule/s to open subnets firewall flows and static routes
# relative flows # relative flows
fw_rules = [] fw_rules = []
subnet_cidrs_per_ads = self._find_router_subnets_cidrs_per_addr_scope( 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 = self._get_extra_routes_by_router_id(context, router['id'])
routes_dest = [route['destination'] for route in routes] routes_dest = [route['destination'] for route in routes]
for subnet_cidrs in subnet_cidrs_per_ads: 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 # Add FW rule/s to open subnets firewall flows and static routes
# relative flows # 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: if subnet_rules:
fw_rules.extend(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 # Add rule for not NAT-ed allocation pools
alloc_pool_rule = self._get_allocation_pools_fw_rule( alloc_pool_rule = self._get_allocation_pools_fw_rule(
context, router_db) context, router_db, subnets=subnets)
if alloc_pool_rule: if alloc_pool_rule:
fw_rules.append(alloc_pool_rule) fw_rules.append(alloc_pool_rule)
# Add no-snat rules # Add no-snat rules
nosnat_fw_rules = self._get_nosnat_subnets_fw_rules( nosnat_fw_rules = self._get_nosnat_subnets_fw_rules(
context, router_db) context, router_db, subnets=subnets)
fw_rules.extend(nosnat_fw_rules) fw_rules.extend(nosnat_fw_rules)
vpn_plugin = directory.get_plugin(plugin_const.VPN) vpn_plugin = directory.get_plugin(plugin_const.VPN)

View File

@ -2152,7 +2152,7 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
self._get_external_attachment_info( self._get_external_attachment_info(
context, router)) context, router))
router_subnets = self._find_router_subnets( router_subnets = self._load_router_subnet_cidrs_from_db(
context.elevated(), router_id) context.elevated(), router_id)
self._validate_router_gw_and_tz(context, router_id, info, self._validate_router_gw_and_tz(context, router_id, info,
org_enable_snat, router_subnets) org_enable_snat, router_subnets)