Add support for per-VRF SNAT IP allocation

The edge_nat = True use case requires allocation of an
SNAT IP per VRF. This patch extends the allocation scheme
to support VRF-based allocation of SNAT IPs.

Closes-Bug: #1592518

Change-Id: Ic601a7d16476e75e5bed5d4db2997cacb4c60454
Signed-off-by: Thomas Bachman <tbachman@yahoo.com>
(cherry picked from commit 2897ab1705)
(cherry picked from commit 9be58f4760)
This commit is contained in:
Thomas Bachman
2016-06-16 04:49:20 -04:00
parent 697e8b0461
commit 27a4ffe817
2 changed files with 85 additions and 13 deletions

View File

@@ -456,14 +456,22 @@ class ApicMappingDriver(api.ResourceMappingDriver,
details = {'device': kwargs.get('device')}
return details
def _allocate_snat_ip_for_host_and_ext_net(self, context, host, network,
es_name):
"""Allocate SNAT IP for a host for an external network."""
def get_snat_ip_for_vrf(self, context, vrf_id, network, es_name=None):
"""Allocate SNAT IP for a VRF for an external network."""
# This API supports getting SNAT IPs per VRF and populating
# the dictionary eithe rwith the network name, or the name
# of the external segment
if es_name is None:
es_name = network.get('name')
return self._allocate_snat_ip(context, vrf_id, network, es_name)
def _allocate_snat_ip(self, context, host_or_vrf, network, es_name):
"""Allocate SNAT IP for a host or VRF for an external network."""
snat_subnets = self._get_subnets(context,
filters={'name': [HOST_SNAT_POOL],
'network_id': [network['id']]})
if not snat_subnets:
LOG.info(_("Subnet for host-SNAT-pool could not be found "
LOG.info(_("Subnet for SNAT-pool could not be found "
"for external network %(net_id)s. SNAT will not "
"function on this network"), {'net_id': network['id']})
return {}
@@ -471,14 +479,14 @@ class ApicMappingDriver(api.ResourceMappingDriver,
snat_ports = self._get_ports(context,
filters={'name': [HOST_SNAT_POOL_PORT],
'network_id': [network['id']],
'device_id': [host]})
'device_id': [host_or_vrf]})
snat_ip = None
if not snat_ports:
# Note that the following port is created for only getting
# an IP assignment in the
attrs = {'device_id': host,
attrs = {'device_id': host_or_vrf,
'device_owner': DEVICE_OWNER_SNAT_PORT,
'binding:host_id': host,
'binding:host_id': host_or_vrf,
'binding:vif_type': portbindings.VIF_TYPE_UNBOUND,
'tenant_id': network['tenant_id'],
'name': HOST_SNAT_POOL_PORT,
@@ -493,18 +501,21 @@ class ApicMappingDriver(api.ResourceMappingDriver,
LOG.warning(_("SNAT-port creation failed for subnet "
"%(subnet_id)s on external network "
"%(net_id)s. SNAT will not function on"
"host %(host)s for this network"),
"host or vrf %(host_or_vrf)s for this "
"network"),
{'subnet_id': snat_subnets[0]['id'],
'net_id': network['id'], 'host': host})
'net_id': network['id'],
'host_or_vrf': host_or_vrf})
return {}
elif snat_ports[0]['fixed_ips']:
snat_ip = snat_ports[0]['fixed_ips'][0]['ip_address']
else:
LOG.warning(_("SNAT-port %(port)s for external network "
"%(net)s on host %(host)s doesn't have an "
"IP-address"),
"%(net)s on host or VRF %(host_or_vrf)s doesn't "
"have an IP-address"),
{'port': snat_ports[0]['id'],
'net': network['id'], 'host': host})
'net': network['id'],
'host_or_vrf': host_or_vrf})
return {}
return {'external_segment_name': es_name,
@@ -566,7 +577,7 @@ class ApicMappingDriver(api.ResourceMappingDriver,
ext_net_id)
if host:
host_snat_ip_allocation = (
self._allocate_snat_ip_for_host_and_ext_net(
self._allocate_snat_ip(
context._plugin_context, host, ext_network,
es['name']))
if host_snat_ip_allocation:

View File

@@ -451,6 +451,67 @@ class TestPolicyTarget(ApicMappingTestCase):
mapping['host_snat_ips'][0]['host_snat_ip'])
self.assertEqual(24, mapping['host_snat_ips'][0]['prefixlen'])
def test_get_snat_ip_for_vrf(self):
TEST_VRF1 = 'testvrf1'
TEST_VRF2 = 'testvrf2'
self._mock_external_dict([('supported', '192.168.0.2/24')])
self.driver.apic_manager.ext_net_dict[
'supported']['host_pool_cidr'] = '192.168.200.1/24'
es = self.create_external_segment(name='supported',
cidr='192.168.0.2/24',
expected_res_status=201, shared=False)['external_segment']
self.create_nat_pool(external_segment_id=es['id'],
ip_pool='20.20.20.0/24')
l3p = self.create_l3_policy(name='myl3',
external_segments={es['id']: ['']})['l3_policy']
l2p = self.create_l2_policy(name='myl2',
l3_policy_id=l3p['id'])['l2_policy']
nsp = self.create_network_service_policy(
network_service_params=[
{"type": "ip_pool", "value": "nat_pool", "name": "test"}])[
'network_service_policy']
ptg = self.create_policy_target_group(
name="ptg1", l2_policy_id=l2p['id'],
network_service_policy_id=nsp['id'])['policy_target_group']
pt1 = self.create_policy_target(
policy_target_group_id=ptg['id'])['policy_target']
self._bind_port_to_host(pt1['port_id'], 'h1')
subnet = self._db_plugin.get_subnet(context.get_admin_context(),
es['subnet_id'])
network = self._db_plugin.get_network(context.get_admin_context(),
subnet['network_id'])
details = self.driver.get_snat_ip_for_vrf(context.get_admin_context(),
TEST_VRF1, network, es_name=es['name'])
self.assertEqual(es['name'],
details['external_segment_name'])
self.assertEqual("192.168.200.1",
details['gateway_ip'])
self.assertEqual("192.168.200.2",
details['host_snat_ip'])
self.assertEqual(24, details['prefixlen'])
# Verify that the same VRF returns the same SNAT IP
details2 = self.driver.get_snat_ip_for_vrf(context.get_admin_context(),
TEST_VRF1, network, es_name=es['name'])
self.assertEqual(details, details2)
# Create event on a second VRF to verify that the SNAT
# port gets created for this second VRF
pt2 = self.create_policy_target(
policy_target_group_id=ptg['id'])['policy_target']
self._bind_port_to_host(pt2['port_id'], 'h1')
details = self.driver.get_snat_ip_for_vrf(context.get_admin_context(),
TEST_VRF2, network, es_name = es['name'])
self.assertEqual(es['name'],
details['external_segment_name'])
self.assertEqual("192.168.200.1",
details['gateway_ip'])
self.assertEqual("192.168.200.3",
details['host_snat_ip'])
self.assertEqual(24, details['prefixlen'])
def test_snat_pool_subnet_deletion(self):
self._mock_external_dict([('supported', '192.168.0.2/24')])
self.driver.apic_manager.ext_net_dict[