NSX|P: Make sure service router exists for vlan interface

When adding a vlan interface to a tier1 router, the edge cluster
must also be set, so that a service router will exist.
When removing a vlan interface, check if the service router is still
needed.

Change-Id: I73b3b02b876eea3d3247487fd12b542b637b6e0b
This commit is contained in:
asarfaty 2020-05-03 11:42:51 +02:00 committed by Adit Sarfaty
parent 136a4005ae
commit 19c74cc3d1
2 changed files with 102 additions and 3 deletions

View File

@ -2125,10 +2125,22 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
return self.nsxpolicy.tier0.get_edge_cluster_path( return self.nsxpolicy.tier0.get_edge_cluster_path(
tier0_uuid) 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): def service_router_has_services(self, context, router_id, router=None):
"""Check if the neutron router has any services """Check if the neutron router has any services
which require a backend service router 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: if not router:
router = self._get_router(context, router_id) router = self._get_router(context, router_id)
@ -2136,9 +2148,14 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
fw_exist = self._router_has_edge_fw_rules(context, router) fw_exist = self._router_has_edge_fw_rules(context, router)
vpn_exist = self.service_router_has_vpnaas(context, router_id) vpn_exist = self.service_router_has_vpnaas(context, router_id)
lb_exist = False lb_exist = False
vlan_interfaces = []
if not (fw_exist or snat_exist or vpn_exist): if not (fw_exist or snat_exist or vpn_exist):
lb_exist = self.service_router_has_loadbalancers(router_id) vlan_interfaces = self._get_router_vlan_interfaces(
return snat_exist or lb_exist or fw_exist or vpn_exist context.elevated(), router_id)
if not vlan_interfaces:
lb_exist = self.service_router_has_loadbalancers(router_id)
return (snat_exist or lb_exist or fw_exist or vpn_exist or
vlan_interfaces)
def service_router_has_loadbalancers(self, router_id): def service_router_has_loadbalancers(self, router_id):
tags_to_search = [{'scope': lb_const.LR_ROUTER_TYPE, 'tag': router_id}] tags_to_search = [{'scope': lb_const.LR_ROUTER_TYPE, 'tag': router_id}]
@ -2646,6 +2663,12 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
ip_addresses=[rtr_subnet['gateway_ip']], ip_addresses=[rtr_subnet['gateway_ip']],
prefix_len=prefix_len)) 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)
slaac_subnet = (subnet.get('ipv6_address_mode') == 'slaac') slaac_subnet = (subnet.get('ipv6_address_mode') == 'slaac')
ndra_profile_id = (SLAAC_NDRA_PROFILE_ID if slaac_subnet ndra_profile_id = (SLAAC_NDRA_PROFILE_ID if slaac_subnet
else NO_SLAAC_NDRA_PROFILE_ID) else NO_SLAAC_NDRA_PROFILE_ID)
@ -2756,6 +2779,10 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
self.nsxpolicy.tier1.remove_segment_interface( self.nsxpolicy.tier1.remove_segment_interface(
router_id, segment_id) 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 # try to delete the SNAT/NO_DNAT rules of this subnet
router_db = self._get_router(context, router_id) router_db = self._get_router(context, router_id)
if (subnet and router_db.gw_port and router_db.enable_snat and if (subnet and router_db.gw_port and router_db.enable_snat and

View File

@ -2179,3 +2179,75 @@ class NsxPTestL3NatTestCase(NsxPTestL3NatTest,
'add', r['router']['id'], 'add', r['router']['id'],
if_subnet['subnet']['id'], None, if_subnet['subnet']['id'], None,
expected_code=exc.HTTPBadRequest.code) expected_code=exc.HTTPBadRequest.code)
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'])