DVR: Add IPv6 csnat port address correctly

If router gateway info update, in l3_dvr_db will receive events,
We will get subnets from router attached DVR port, if exists
multiple IPv6 slaac-enabled subnets, previous code will create
csnat port form one of the IPv6 subnets.
This is inconsistency with router interface
udpate in _update_snat_v6_addrs_after_intf_update.

This patch adds all IPv6 subnets if the distributed
router port have multiple IPv6 subnets.

Change-Id: I02ffed238897ebc91b96aebb7f94ed90b7613769
Closes-Bug: #1786169
(cherry picked from commit c6aac9041f)
This commit is contained in:
Dongcan Ye 2018-08-09 07:16:51 +00:00 committed by Swaminathan Vasudevan
parent 3cc89a9c44
commit cf38c373d1
2 changed files with 89 additions and 7 deletions

View File

@ -204,11 +204,11 @@ class DVRResourceOperationHandler(object):
return ports
def _add_csnat_router_interface_port(
self, context, router, network_id, subnet_id, do_pop=True):
self, context, router, network_id, subnets, do_pop=True):
"""Add SNAT interface to the specified router and subnet."""
port_data = {'tenant_id': '',
'network_id': network_id,
'fixed_ips': [{'subnet_id': subnet_id}],
'fixed_ips': subnets,
'device_id': router.id,
'device_owner': const.DEVICE_OWNER_ROUTER_SNAT,
'admin_state_up': True,
@ -253,15 +253,31 @@ class DVRResourceOperationHandler(object):
)
LOG.info('SNAT interface port list does not exist,'
' so create one: %s', port_list)
v6_subnets = []
network = None
for intf in int_ports:
if intf.fixed_ips:
# Passing the subnet for the port to make sure the IP's
# are assigned on the right subnet if multiple subnet
# exists
snat_port = self._add_csnat_router_interface_port(
context, router, intf['network_id'],
intf['fixed_ips'][0]['subnet_id'], do_pop=False)
port_list.append(snat_port)
for fixed_ip in intf['fixed_ips']:
ip_version = n_utils.get_ip_version(
fixed_ip.get('ip_address'))
if ip_version == const.IP_VERSION_4:
snat_port = self._add_csnat_router_interface_port(
context, router, intf['network_id'],
[{'subnet_id': fixed_ip['subnet_id']}],
do_pop=False)
port_list.append(snat_port)
else:
v6_subnets.append(
{"subnet_id": fixed_ip['subnet_id']})
network = intf['network_id']
if v6_subnets:
snat_port = self._add_csnat_router_interface_port(
context, router, network,
v6_subnets, do_pop=False)
port_list.append(snat_port)
if port_list:
self.l3plugin._populate_mtu_and_subnets_for_ports(
context, port_list)
@ -397,7 +413,7 @@ class DVRResourceOperationHandler(object):
admin_context = context.elevated()
self._add_csnat_router_interface_port(
admin_context, router_db, port['network_id'],
port['fixed_ips'][-1]['subnet_id'])
[{'subnet_id': port['fixed_ips'][-1]['subnet_id']}])
@registry.receives(resources.ROUTER_INTERFACE, [events.AFTER_CREATE])
@db_api.retry_if_session_inactive()

View File

@ -596,6 +596,72 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
def test_distributed_to_ha_csnat_ports_removal(self):
self._test_csnat_ports_removal(ha=True)
def test_update_router_gw_info_csnat_ports_add(self):
router_dict = {'name': 'test_router',
'admin_state_up': True,
'distributed': True}
router = self._create_router(router_dict)
with self.network() as net_ext,\
self.network() as net_int,\
self.subnet(
network=net_int,
cidr='2001:db8:1::/64',
gateway_ip='2001:db8:1::1',
ip_version=const.IP_VERSION_6) as v6_subnet1,\
self.subnet(
network=net_int,
cidr='2001:db8:2::/64',
gateway_ip='2001:db8:2::1',
ip_version=const.IP_VERSION_6) as v6_subnet2,\
self.subnet(
network=net_int,
cidr='10.10.10.0/24') as v4_subnet:
self.core_plugin.update_network(
self.ctx, net_ext['network']['id'],
{'network': {'router:external': True}})
# Add router interface, then set router gateway
self.mixin.add_router_interface(self.ctx, router['id'],
{'subnet_id': v6_subnet1['subnet']['id']})
self.mixin.add_router_interface(self.ctx, router['id'],
{'subnet_id': v6_subnet2['subnet']['id']})
self.mixin.add_router_interface(self.ctx, router['id'],
{'subnet_id': v4_subnet['subnet']['id']})
dvr_filters = {'device_owner':
[const.DEVICE_OWNER_DVR_INTERFACE]}
dvr_ports = self.core_plugin.get_ports(
self.ctx, filters=dvr_filters)
# One for IPv4, one for two IPv6 subnets
self.assertEqual(2, len(dvr_ports))
self.mixin.update_router(
self.ctx, router['id'],
{'router': {'external_gateway_info':
{'network_id': net_ext['network']['id']}}})
csnat_filters = {'device_owner':
[const.DEVICE_OWNER_ROUTER_SNAT]}
csnat_ports = self.core_plugin.get_ports(
self.ctx, filters=csnat_filters)
# One for IPv4, one for two IPv6 subnets
self.assertEqual(2, len(csnat_ports))
# Remove v4 subnet interface from router
self.mixin.remove_router_interface(
self.ctx, router['id'],
{'subnet_id': v4_subnet['subnet']['id']})
dvr_ports = self.core_plugin.get_ports(
self.ctx, filters=dvr_filters)
self.assertEqual(1, len(dvr_ports))
csnat_ports = self.core_plugin.get_ports(
self.ctx, filters=csnat_filters)
self.assertEqual(1, len(csnat_ports))
self.assertEqual(2, len(csnat_ports[0]['fixed_ips']))
def test_remove_router_interface_csnat_ports_removal(self):
router_dict = {'name': 'test_router', 'admin_state_up': True,
'distributed': True}