From f11709dba54007df6ec64878c49c39aef5352003 Mon Sep 17 00:00:00 2001 From: Kobi Samoray Date: Wed, 14 Aug 2019 18:50:29 +0300 Subject: [PATCH] NSXv: VDR interface operation performance Change-Id: Ie36c6cbb8fc0a8055a8a3d84e8940b1c62fcba9e --- vmware_nsx/common/profile.py | 33 ++++++++++++ vmware_nsx/plugins/common/plugin.py | 42 +++++++++------ vmware_nsx/plugins/nsx_p/plugin.py | 12 +++-- vmware_nsx/plugins/nsx_v/plugin.py | 83 ++++++++++++++++------------- vmware_nsx/plugins/nsx_v3/plugin.py | 2 +- 5 files changed, 113 insertions(+), 59 deletions(-) create mode 100644 vmware_nsx/common/profile.py diff --git a/vmware_nsx/common/profile.py b/vmware_nsx/common/profile.py new file mode 100644 index 0000000000..a845ca8c37 --- /dev/null +++ b/vmware_nsx/common/profile.py @@ -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 diff --git a/vmware_nsx/plugins/common/plugin.py b/vmware_nsx/plugins/common/plugin.py index 4104058d92..e6070bf755 100644 --- a/vmware_nsx/plugins/common/plugin.py +++ b/vmware_nsx/plugins/common/plugin.py @@ -150,19 +150,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( @@ -197,22 +202,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): @@ -255,8 +265,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) diff --git a/vmware_nsx/plugins/nsx_p/plugin.py b/vmware_nsx/plugins/nsx_p/plugin.py index 67fc951092..49b650f770 100644 --- a/vmware_nsx/plugins/nsx_p/plugin.py +++ b/vmware_nsx/plugins/nsx_p/plugin.py @@ -892,6 +892,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): profile_id = SLAAC_NDRA_PROFILE_ID 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 # advertising (vlan advertising is attached on interface level) slaac_subnets = [s for s in router_subnets @@ -1624,7 +1626,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) @@ -1966,8 +1968,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): self._validate_router_tz(context.elevated(), tier0_uuid, [subnet]) segment_id = self._get_network_nsx_segment_id(context, network_id) - rtr_subnets = self._find_router_subnets(context.elevated(), - router_id) + rtr_subnets = self._load_router_subnet_cidrs_from_db( + context.elevated(), router_id) if overlay_net: # overlay interface pol_subnets = [] @@ -2055,8 +2057,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) - rtr_subnets = self._find_router_subnets(context.elevated(), - router_id) + rtr_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 diff --git a/vmware_nsx/plugins/nsx_v/plugin.py b/vmware_nsx/plugins/nsx_v/plugin.py index b3d06c6627..2687637100 100644 --- a/vmware_nsx/plugins/nsx_v/plugin.py +++ b/vmware_nsx/plugins/nsx_v/plugin.py @@ -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) diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index 71047c6501..0127f4c3f3 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -2152,7 +2152,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)