Merge "NSX|V3+P: Improve router interface actions performance"

This commit is contained in:
Zuul 2019-08-19 09:23:26 +00:00 committed by Gerrit Code Review
commit 3cc6f6fc52
4 changed files with 75 additions and 88 deletions

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'])
@ -130,14 +131,15 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
return subnetpool.get('address_scope_id', '')
def _validate_address_scope_for_router_interface(self, context, router_id,
gw_network_id, subnet_id):
gw_network_id, subnet_id,
subnet=None):
"""Validate that the GW address scope is the same as the interface"""
gw_address_scope = self._get_network_address_scope(context,
gw_network_id)
if not gw_address_scope:
return
subnet_address_scope = self._get_subnet_address_scope(context,
subnet_id)
subnet_address_scope = self._get_subnet_address_scope(
context, subnet_id, subnet=subnet)
if (not subnet_address_scope or
subnet_address_scope != gw_address_scope):
raise nsx_exc.NsxRouterInterfaceDoesNotMatchAddressScope(
@ -386,16 +388,6 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
if qos_policy_id:
qos_com_utils.validate_policy_accessable(context, qos_policy_id)
def _get_interface_network(self, context, interface_info):
is_port, is_sub = self._validate_interface_info(interface_info)
if is_port:
net_id = self.get_port(context,
interface_info['port_id'])['network_id']
elif is_sub:
net_id = self.get_subnet(context,
interface_info['subnet_id'])['network_id']
return net_id
def _process_extra_attr_router_create(self, context, router_db, r):
for extra_attr in l3_attrs_db.get_attr_info().keys():
if (extra_attr in r and

View File

@ -52,6 +52,7 @@ from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef
from neutron_lib.api.definitions import availability_zone as az_def
from neutron_lib.api.definitions import external_net as extnet_apidef
from neutron_lib.api.definitions import extra_dhcp_opt as ext_edo
from neutron_lib.api.definitions import l3 as l3_apidef
from neutron_lib.api.definitions import port_security as psec
from neutron_lib.api.definitions import portbindings as pbin
from neutron_lib.api.definitions import provider_net as pnet
@ -287,7 +288,10 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
if subnet_id:
return self.get_subnet(context, subnet_id)
def _get_interface_network(self, context, interface_info):
def _get_interface_network_id(self, context, interface_info, subnet=None):
if subnet:
return subnet['network_id']
is_port, is_sub = self._validate_interface_info(interface_info)
if is_port:
net_id = self.get_port(context,
@ -310,15 +314,14 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
sg_rule[sg_prefix.LOCAL_IP_PREFIX].startswith('::/'))):
sg_rule[sg_prefix.LOCAL_IP_PREFIX] = None
def _validate_interface_address_scope(self, context,
router_db, interface_info):
def _validate_interface_address_scope(self, context, router_db,
interface_subnet):
gw_network_id = (router_db.gw_port.network_id if router_db.gw_port
else None)
subnet = self.get_subnet(context, interface_info['subnet_ids'][0])
if not router_db.enable_snat and gw_network_id:
self._validate_address_scope_for_router_interface(
context.elevated(), router_db.id, gw_network_id, subnet['id'])
context.elevated(), router_db.id, gw_network_id,
interface_subnet['id'], subnet=interface_subnet)
def _validate_address_pairs(self, address_pairs):
for pair in address_pairs:
@ -1260,6 +1263,10 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
"""
pass
def _get_router_gw_info(self, context, router_id):
router = self.get_router(context, router_id)
return router.get(l3_apidef.EXTERNAL_GW_INFO, {})
def _validate_router_gw_and_tz(self, context, router_id, info,
org_enable_snat, router_subnets):
# Ensure that a router cannot have SNAT disabled if there are
@ -1280,6 +1287,10 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
self._validate_router_tz(context, new_tier0_uuid,
router_subnets)
def _get_tier0_uuid_by_router(self, context, router):
network_id = router.gw_port_id and router.gw_port.network_id
return self._get_tier0_uuid_by_net_id(context, network_id)
def _validate_gw_overlap_interfaces(self, context, gateway_net,
interfaces_networks):
# Ensure that interface subnets cannot overlap with the GW subnet

View File

@ -873,7 +873,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
return updated_net
def _update_slaac_on_router(self, context, router_id,
subnet, delete=False):
subnet, router_subnets, delete=False):
# TODO(annak): redesign when policy supports downlink-level
# ndra profile attachment
@ -892,12 +892,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)
# 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
slaac_subnets = [s for s in router_subnets
if s['id'] != subnet['id'] and
s.get('ipv6_address_mode') == 'slaac' and
self._is_overlay_network(context,
@ -1428,10 +1425,6 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
return (ports if not fields else
[db_utils.resource_fields(port, fields) for port in ports])
def _get_tier0_uuid_by_router(self, context, router):
network_id = router.gw_port_id and router.gw_port.network_id
return self._get_tier0_uuid_by_net_id(context, network_id)
def _add_subnet_snat_rule(self, context, router_id, subnet,
gw_address_scope, gw_ip):
if not self._need_router_snat_rules(context, router_id, subnet,
@ -1621,10 +1614,6 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
# remove the edge cluster from the tier1 router
self.nsxpolicy.tier1.remove_edge_cluster(router_id)
def _get_router_gw_info(self, context, router_id):
router = self.get_router(context, router_id)
return router.get(l3_apidef.EXTERNAL_GW_INFO, {})
def _update_router_gw_info(self, context, router_id, info,
called_from=None):
# Get the original data of the router GW
@ -1654,8 +1643,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)
vpn_exist = self.service_router_has_vpnaas(context, router_id)
@ -1802,8 +1789,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
return self.get_router(context, router['id'])
def delete_router(self, context, router_id):
router = self.get_router(context, router_id)
if router.get(l3_apidef.EXTERNAL_GW_INFO):
gw_info = self._get_router_gw_info(context, router_id)
if gw_info:
try:
self._update_router_gw_info(context, router_id, {},
called_from="delete")
@ -1928,16 +1915,17 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
@nsx_plugin_common.api_replay_mode_wrapper
def add_router_interface(self, context, router_id, interface_info):
network_id = self._get_interface_network(context, interface_info)
# NOTE: In dual stack case, neutron would create a separate interface
# for each IP version
# We only allow one subnet per IP version
subnet = self._get_interface_subnet(context, interface_info)
network_id = self._get_interface_network_id(context, interface_info,
subnet=subnet)
extern_net = self._network_is_external(context, network_id)
overlay_net = self._is_overlay_network(context, network_id)
router_db = self._get_router(context, router_id)
gw_network_id = (router_db.gw_port.network_id if router_db.gw_port
else None)
# NOTE: In dual stack case, neutron would create a separate interface
# for each IP version
# We only allow one subnet per IP version
subnet = self._get_interface_subnet(context, interface_info)
with locking.LockManager.get_lock(str(network_id)):
# disallow more than one subnets belong to same network being
@ -1964,26 +1952,26 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
# Update the interface of the neutron router
info = super(NsxPolicyPlugin, self).add_router_interface(
context, router_id, interface_info)
context, router_id, interface_info)
try:
# If it is a no-snat router, interface address scope must be the
# same as the gateways
self._validate_interface_address_scope(context, router_db, info)
self._validate_interface_address_scope(context, router_db, subnet)
# Check GW & subnets TZ
subnets = self._find_router_subnets(context.elevated(), router_id)
tier0_uuid = self._get_tier0_uuid_by_router(
context.elevated(), router_db)
# Validate the TZ of the new subnet match the one of the router
self._validate_router_tz(context.elevated(), tier0_uuid, [subnet])
segment_id = self._get_network_nsx_segment_id(context, network_id)
subnet = self.get_subnet(context, info['subnet_ids'][0])
rtr_subnets = self._find_router_subnets(context.elevated(),
router_id)
if overlay_net:
# overlay interface
pol_subnets = []
for rtr_subnet in subnets:
for rtr_subnet in rtr_subnets:
# For dual stack, we allow one v4 and one v6
# subnet per network
if rtr_subnet['network_id'] == network_id:
@ -1998,11 +1986,11 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
# will update the router only if needed
self._update_slaac_on_router(context, router_id,
subnet)
subnet, rtr_subnets)
else:
# Vlan interface
pol_subnets = []
for rtr_subnet in subnets:
for rtr_subnet in rtr_subnets:
if rtr_subnet['network_id'] == network_id:
prefix_len = int(rtr_subnet['cidr'].split('/')[1])
pol_subnets.append(policy_defs.InterfaceSubnet(
@ -2032,7 +2020,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
# if this is an ipv6 subnet and router has GW,
# we need to add advertisement rule
self._update_router_advertisement_rules(
router_id, subnets, True)
router_id, rtr_subnets, True)
# update firewall rules
self.update_router_firewall(context, router_id, router_db)
@ -2048,7 +2036,6 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
return info
def remove_router_interface(self, context, router_id, interface_info):
LOG.info("Removing router %s interface %s", router_id, interface_info)
# find the subnet - it is need for removing the SNAT rule
subnet = subnet_id = None
if 'port_id' in interface_info:
@ -2068,13 +2055,13 @@ 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)
rtr_subnets = self._find_router_subnets(context.elevated(),
router_id)
try:
if overlay_net:
# Remove the tier1 router from this segment on the NSX
pol_subnets = []
for rtr_subnet in subnets:
for rtr_subnet in rtr_subnets:
# For dual stack, we allow one v4 and one v6
# subnet per network
if rtr_subnet['network_id'] == network_id:
@ -2093,12 +2080,12 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
# will update the router only if needed
self._update_slaac_on_router(context, router_id,
subnet, delete=True)
subnet, rtr_subnets, delete=True)
else:
# VLAN interface
pol_subnets = []
for rtr_subnet in subnets:
for rtr_subnet in rtr_subnets:
if rtr_subnet['network_id'] == network_id:
prefix_len = int(rtr_subnet['cidr'].split('/')[1])
pol_subnets.append(policy_defs.InterfaceSubnet(
@ -2125,7 +2112,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
# if this is an ipv6 subnet and router has GW,
# we need to remove advertisement rule
self._update_router_advertisement_rules(
router_id, subnets, True)
router_id, rtr_subnets, True)
# update firewall rules
self.update_router_firewall(context, router_id, router_db)

View File

@ -2062,10 +2062,6 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
return (ports if not fields else
[db_utils.resource_fields(port, fields) for port in ports])
def _get_tier0_uuid_by_router(self, context, router):
network_id = router.gw_port_id and router.gw_port.network_id
return self._get_tier0_uuid_by_net_id(context, network_id)
def _validate_router_tz(self, context, tier0_uuid, subnets):
# make sure the related GW (Tier0 router) belongs to the same TZ
# as the subnets attached to the Tier1 router
@ -2349,8 +2345,8 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
if not cfg.CONF.nsx_v3.native_dhcp_metadata:
nsx_rpc.handle_router_metadata_access(self, context, router_id,
interface=None)
router = self.get_router(context, router_id)
if router.get(l3_apidef.EXTERNAL_GW_INFO):
gw_info = self._get_router_gw_info(context, router_id)
if gw_info:
self._update_router_gw_info(context, router_id, {})
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
router_id)
@ -2663,15 +2659,16 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
@nsx_plugin_common.api_replay_mode_wrapper
def add_router_interface(self, context, router_id, interface_info):
network_id = self._get_interface_network(context, interface_info)
# In case on dual stack, neutron creates a separate interface per
# IP version
subnet = self._get_interface_subnet(context, interface_info)
network_id = self._get_interface_network_id(context, interface_info,
subnet=subnet)
extern_net = self._network_is_external(context, network_id)
overlay_net = self._is_overlay_network(context, network_id)
router_db = self._get_router(context, router_id)
gw_network_id = (router_db.gw_port.network_id if router_db.gw_port
else None)
# In case on dual stack, neutron creates a separate interface per
# IP version
subnet = self._get_interface_subnet(context, interface_info)
with locking.LockManager.get_lock(str(network_id)):
# disallow more than one subnets belong to same network being
@ -2698,16 +2695,15 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
# Update the interface of the neutron router
info = super(NsxV3Plugin, self).add_router_interface(
context, router_id, interface_info)
context, router_id, interface_info)
try:
subnet = self.get_subnet(context, info['subnet_ids'][0])
port = self.get_port(context, info['port_id'])
nsx_net_id, nsx_port_id = nsx_db.get_nsx_switch_and_port_id(
context.session, port['id'])
context.session, info['port_id'])
# If it is a no-snat router, interface address scope must be the
# same as the gateways
self._validate_interface_address_scope(context, router_db, info)
self._validate_interface_address_scope(context, router_db, subnet)
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
router_id)
@ -2716,7 +2712,8 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
display_name = utils.get_name_and_uuid(
subnet['name'] or 'subnet', subnet['id'])
tags = self.nsxlib.build_v3_tags_payload(
port, resource_type='os-neutron-rport-id',
{'id': info['port_id'], 'project_id': context.project_id},
resource_type='os-neutron-rport-id',
project_name=context.tenant_name)
tags.append({'scope': 'os-subnet-id', 'tag': subnet['id']})
@ -2729,12 +2726,10 @@ 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)
# Validate the TZ of the new subnet match the one of the router
tier0_uuid = self._get_tier0_uuid_by_router(context.elevated(),
router_db)
self._validate_router_tz(context.elevated(), tier0_uuid, subnets)
self._validate_router_tz(context.elevated(), tier0_uuid, [subnet])
# create the interface ports on the NSX
self.nsxlib.router.create_logical_router_intf_port_by_ls_id(
@ -2788,14 +2783,17 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
return info
def remove_router_interface(self, context, router_id, interface_info):
self._validate_interface_info(interface_info, for_removal=True)
# Get the interface port & subnet
subnet = None
subnet_id = None
port_id = None
self._validate_interface_info(interface_info, for_removal=True)
network_id = None
if 'port_id' in interface_info:
port_id = interface_info['port_id']
# find subnet_id - it is need for removing the SNAT rule
port = self._get_port(context, port_id)
network_id = port['network_id']
if port.get('fixed_ips'):
for fip in port['fixed_ips']:
subnet_id = fip['subnet_id']
@ -2810,11 +2808,9 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
self._confirm_router_interface_not_in_use(
context, router_id, subnet_id)
subnet = self._get_subnet(context, subnet_id)
rport_qry = context.session.query(models_v2.Port)
ports = rport_qry.filter_by(
device_id=router_id,
device_owner=l3_db.DEVICE_OWNER_ROUTER_INTF,
network_id=subnet['network_id'])
network_id = subnet['network_id']
ports = self._get_router_interface_ports_by_network(
context, router_id, network_id)
for p in ports:
fip_subnet_ids = [fixed_ip['subnet_id']
for fixed_ip in p['fixed_ips']]
@ -2833,10 +2829,11 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
nsx_net_id, _nsx_port_id = nsx_db.get_nsx_switch_and_port_id(
context.session, port_id)
subnet = self.get_subnet(context, subnet_id)
if not subnet:
subnet = self._get_subnet(context, subnet_id)
ports, address_groups = self._get_ports_and_address_groups(
context, router_id, subnet['network_id'],
exclude_sub_ids=[subnet['id']])
context, router_id, network_id,
exclude_sub_ids=[subnet_id])
nsx_router_id = nsx_db.get_nsx_router_id(
context.session, router_id)
if len(ports) >= 1:
@ -2865,7 +2862,7 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
LOG.error("router port on router %(router_id)s for net "
"%(net_id)s not found at the backend",
{'router_id': router_id,
'net_id': subnet['network_id']})
'net_id': network_id})
# inform the FWaaS that interface port was removed
if self.fwaas_callbacks: