diff --git a/vmware_nsx/plugins/nsx_p/plugin.py b/vmware_nsx/plugins/nsx_p/plugin.py index 89a2df2b0b..144b090c7f 100644 --- a/vmware_nsx/plugins/nsx_p/plugin.py +++ b/vmware_nsx/plugins/nsx_p/plugin.py @@ -2333,10 +2333,22 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): return self.nsxpolicy.tier0.get_edge_cluster_path( tier0_uuid) + def _get_router_vlan_interfaces(self, context, router_id): + # return data about VLAN subnet connected to the router + rtr_subnets = self._load_router_subnet_cidrs_from_db( + context, router_id) + vlan_subnets = [] + for sub in rtr_subnets: + net_id = sub['network_id'] + if not self._is_overlay_network(context, net_id): + vlan_subnets.append(sub) + return vlan_subnets + def service_router_has_services(self, context, router_id, router=None): """Check if the neutron router has any services which require a backend service router - currently those are: SNAT, Loadbalancer, Edge firewall + currently those are: SNAT, Loadbalancer, Edge firewall, + VPNaaS & Vlan interfaces """ if not router: router = self._get_router(context, router_id) @@ -2344,10 +2356,15 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): fw_exist = self._router_has_edge_fw_rules(context, router) vpn_exist = self.service_router_has_vpnaas(context, router_id) lb_exist = False + vlan_interfaces = [] if not (fw_exist or snat_exist or vpn_exist): - lb_exist = self.service_router_has_loadbalancers( - context, router_id) - return snat_exist or lb_exist or fw_exist or vpn_exist + vlan_interfaces = self._get_router_vlan_interfaces( + context.elevated(), router_id) + if not vlan_interfaces: + lb_exist = self.service_router_has_loadbalancers( + context, router_id) + return (snat_exist or lb_exist or fw_exist or vpn_exist or + vlan_interfaces) def service_router_has_loadbalancers(self, context, router_id): tags_to_search = [{'scope': lb_const.LR_ROUTER_TYPE, 'tag': router_id}] @@ -2940,6 +2957,12 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): ip_addresses=[rtr_subnet['gateway_ip']], prefix_len=prefix_len)) + # Service router is mandatory for VLAN interfaces + if not self.verify_sr_at_backend(router_id): + self.create_service_router( + context, router_id, router=router_db, + update_firewall=False) + ndra_profile_id = self._get_subnet_ndra_profile(subnet) self.nsxpolicy.tier1.add_segment_interface( router_id, segment_id, @@ -3048,6 +3071,10 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): self.nsxpolicy.tier1.remove_segment_interface( router_id, segment_id) + if not self._core_plugin.service_router_has_services( + context.elevated(), router_id): + self.delete_service_router(router_id) + # try to delete the SNAT/NO_DNAT rules of this subnet router_db = self._get_router(context, router_id) if (subnet and router_db.gw_port and router_db.enable_snat and diff --git a/vmware_nsx/tests/unit/nsx_p/test_plugin.py b/vmware_nsx/tests/unit/nsx_p/test_plugin.py index 11c16b09f3..27f3a9a896 100644 --- a/vmware_nsx/tests/unit/nsx_p/test_plugin.py +++ b/vmware_nsx/tests/unit/nsx_p/test_plugin.py @@ -2615,6 +2615,78 @@ class NsxPTestL3NatTestCase(NsxPTestL3NatTest, nsx_plugin.STATELESS_DHCP_NDRA_PROFILE_ID, sub2_ipversion=4) + def _add_external_gateway_to_router(self, router_id, network_id, + expected_code=exc.HTTPOk.code, + neutron_context=None, ext_ips=None, + **kwargs): + # Copy the neutron api to add support for disabled SNAT + ext_ips = ext_ips or [] + body = {'router': + {'external_gateway_info': {'network_id': network_id}}} + if ext_ips: + body['router']['external_gateway_info'][ + 'external_fixed_ips'] = ext_ips + if 'policy_id' in kwargs: + body['router']['external_gateway_info'][ + 'qos_policy_id'] = kwargs.get('policy_id') + if 'enable_snat' in kwargs: + body['router']['external_gateway_info'][ + 'enable_snat'] = kwargs.get('enable_snat') + return self._update('routers', router_id, body, + expected_code=expected_code, + neutron_context=neutron_context) + + def test_router_vlan_interface_sr(self): + providernet_args = {pnet.NETWORK_TYPE: 'vlan', + pnet.SEGMENTATION_ID: 11} + + with mock.patch('vmware_nsxlib.v3.policy.core_resources.' + 'NsxPolicyTransportZoneApi.get_transport_type', + return_value=nsx_constants.TRANSPORT_TYPE_VLAN), \ + self.network(name='vlan_net', + providernet_args=providernet_args, + arg_list=(pnet.NETWORK_TYPE, + pnet.SEGMENTATION_ID)) as net,\ + self.router() as r,\ + self.subnet(cidr='2001::/64', network=net, + ip_version=6, enable_dhcp=False, + ipv6_address_mode=None, + ipv6_ra_mode=None) as if_subnet,\ + self._create_l3_ext_network() as ext_net,\ + self.subnet(network=ext_net, cidr='10.0.0.0/16', + enable_dhcp=False) as ext_sub,\ + mock.patch("vmware_nsxlib.v3.policy.core_resources." + "NsxPolicyTier1Api.get_edge_cluster_path", + return_value=None),\ + mock.patch("vmware_nsxlib.v3.policy.core_resources." + "NsxPolicyTier1Api.get_realized_id"),\ + mock.patch("vmware_nsxlib.v3.policy.core_resources." + "NsxPolicyTier1Api.set_edge_cluster_path" + ) as add_srv_router,\ + mock.patch("vmware_nsxlib.v3.policy.core_resources." + "NsxPolicyTier1Api.remove_edge_cluster" + ) as del_srv_router: + + # Add router GW + self._add_external_gateway_to_router( + r['router']['id'], + ext_sub['subnet']['network_id'], + enable_snat=False) + + # Adding subnet interface + self._router_interface_action( + 'add', r['router']['id'], + if_subnet['subnet']['id'], None) + # verify service router was created + add_srv_router.assert_called_once_with(r['router']['id'], mock.ANY) + + # Removing subnet interface + self._router_interface_action( + 'remove', r['router']['id'], + if_subnet['subnet']['id'], None) + # verify service router was removed + del_srv_router.assert_called_once_with(r['router']['id']) + def _test_router_vlan_interface_ndprofile(self, profile_with, enable_dhcp=True, mode='slaac'): providernet_args = {pnet.NETWORK_TYPE: 'vlan',