diff --git a/vmware_nsx/plugins/common/plugin.py b/vmware_nsx/plugins/common/plugin.py index 15861eace7..4104058d92 100644 --- a/vmware_nsx/plugins/common/plugin.py +++ b/vmware_nsx/plugins/common/plugin.py @@ -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 diff --git a/vmware_nsx/plugins/common_v3/plugin.py b/vmware_nsx/plugins/common_v3/plugin.py index 0cd8c7446c..042bf2405e 100644 --- a/vmware_nsx/plugins/common_v3/plugin.py +++ b/vmware_nsx/plugins/common_v3/plugin.py @@ -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 diff --git a/vmware_nsx/plugins/nsx_p/plugin.py b/vmware_nsx/plugins/nsx_p/plugin.py index 8b5334829d..67fc951092 100644 --- a/vmware_nsx/plugins/nsx_p/plugin.py +++ b/vmware_nsx/plugins/nsx_p/plugin.py @@ -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) diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index dd84f1d17d..71047c6501 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -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: