[APIC AIM] Populate get_gbp_details correctly for certain ports on external network

Network reverse mapping doesn't work for ports
on external networks because naming convention
followed is opaque to the mechanism driver. Now
we deal with reverse mapping exceptions by
checking if the network mapping table has an entry
for the BD.

Change-Id: I5bb2e9f9718cdf312316d34b988ea4db60bcc6d4
Signed-off-by: Amit Bose <amitbose@gmail.com>
This commit is contained in:
Amit Bose 2018-02-12 15:22:30 -08:00
parent a7cf4ee4b5
commit fa1d544b59
6 changed files with 110 additions and 16 deletions

View File

@ -107,6 +107,5 @@ class APICNameMapper(object):
if self._map(session, "", type_tag, prefix) == name[:pos]:
return name[pos:]
elif enforce:
LOG.error("Attempted to reverse-map invalid APIC name '%s'",
name)
raise exceptions.InternalError()
msg = _("Attempted to reverse-map invalid APIC name '%s'") % name
raise exceptions.InternalError(details=msg)

View File

@ -119,6 +119,12 @@ class DbMixin(object):
vrf_name=vrf.name).
all())
def _get_network_mappings_for_bd(self, session, bd):
return (session.query(NetworkMapping).
filter_by(bd_tenant_name=bd.tenant_name,
bd_name=bd.name).
all())
def _is_vrf_used_by_networks(self, session, vrf):
return (session.query(NetworkMapping.network_id).
filter_by(vrf_tenant_name=vrf.tenant_name,

View File

@ -17,7 +17,11 @@ from neutron_lib import exceptions
class InternalError(exceptions.NeutronException):
message = _("Internal mechanism driver error - see error log for details.")
message = _("Internal mechanism driver error - %(details)s.")
def __init__(self, **kwargs):
kwargs.setdefault('details', _("See error log for details"))
super(InternalError, self).__init__(**kwargs)
class UnsupportedRoutingTopology(exceptions.BadRequest):

View File

@ -2398,6 +2398,11 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
mapping = self._get_network_mapping(session, network['id'])
return mapping and self._get_network_vrf(mapping)
# Used by policy driver.
def get_network_ids_for_bd(self, session, bd):
mapping = self._get_network_mappings_for_bd(session, bd)
return [m.network_id for m in mapping]
def get_aim_domains(self, aim_ctx):
vmms = [{'name': x.name, 'type': x.type}
for x in self.aim.find(aim_ctx, aim_resource.VMMDomain)

View File

@ -38,6 +38,8 @@ from gbpservice.neutron.extensions import cisco_apic
from gbpservice.neutron.extensions import cisco_apic_gbp as aim_ext
from gbpservice.neutron.extensions import cisco_apic_l3
from gbpservice.neutron.extensions import group_policy as gpolicy
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import (
exceptions as md_exc)
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import (
mechanism_driver as md)
from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import apic_mapper
@ -1904,14 +1906,26 @@ class AIMMappingDriver(nrd.CommonNeutronBase, aim_rpc.AIMMappingRPCMixin):
other_vrfs = self.aim.find(aim_ctx, aim_resource.VRF,
name=vrf_name)
bd_tenants = set([x.tenant_name for x in bds])
vrf_tenants = set([x.tenant_name for x in other_vrfs])
vrf_tenants = set([x.tenant_name for x in other_vrfs
if x.tenant_name != vrf_tenant_name])
valid_tenants = bd_tenants - vrf_tenants
# Only keep BDs that don't have a VRF with that name
# already
bds = [x for x in bds if x.tenant_name in valid_tenants]
# Retrieve subnets from BDs
net_ids = [self.name_mapper.reverse_network(session, bd.name)
for bd in bds]
net_ids = []
for bd in bds:
try:
net_ids.append(self.name_mapper.reverse_network(
session, bd.name))
except md_exc.InternalError as aie:
# Check if BD maps to an external network
ext_ids = self.aim_mech_driver.get_network_ids_for_bd(
session, bd)
if not ext_ids:
LOG.error("%s", aie)
raise
net_ids.extend(ext_ids)
if net_ids:
subnets = self._get_subnets(plugin_context,
{'network_id': net_ids})

View File

@ -210,13 +210,13 @@ class AIMBaseTestCase(test_nr_base.CommonNeutronBaseTestCase,
net = self._make_network(self.fmt, name, True,
arg_list=extn_attr,
**kwargs)['network']
self._make_subnet(
subnet = self._make_subnet(
self.fmt, {'network': net}, '100.100.0.1',
'100.100.0.0/16')['subnet']
router = self._make_router(
self.fmt, router_tenant or net['tenant_id'], 'router1',
external_gateway_info={'network_id': net['id']})['router']
return net, router
return net, router, subnet
def _bind_port_to_host(self, port_id, host):
data = {'port': {'binding:host_id': host,
@ -2941,13 +2941,14 @@ class TestPolicyTarget(AIMBaseTestCase,
def _verify_gbp_details_assertions(self, mapping, req_mapping, port_id,
expected_epg_name, expected_epg_tenant,
subnet, default_route=None):
subnet, default_route=None,
map_tenant_name=True):
self.assertEqual(mapping, req_mapping['gbp_details'])
self.assertEqual(port_id, mapping['port_id'])
self.assertEqual(expected_epg_name, mapping['endpoint_group_name'])
self.assertEqual(
self.name_mapper.project(None, expected_epg_tenant),
mapping['ptg_tenant'])
exp_tenant = (self.name_mapper.project(None, expected_epg_tenant)
if map_tenant_name else expected_epg_tenant)
self.assertEqual(exp_tenant, mapping['ptg_tenant'])
self.assertEqual('someid', mapping['vm-name'])
self.assertTrue(mapping['enable_dhcp_optimization'])
self.assertFalse(mapping['enable_metadata_optimization'])
@ -3151,9 +3152,9 @@ class TestPolicyTarget(AIMBaseTestCase,
name='as2', address_scope_id=address_scope['id'],
tenant_id=self._tenant_id)
ext_net1, router1 = self._setup_external_network(
ext_net1, router1, _ = self._setup_external_network(
'l1', dn='uni/tn-t1/out-l1/instP-n1')
ext_net2, router2 = self._setup_external_network(
ext_net2, router2, _ = self._setup_external_network(
'l2', dn='uni/tn-t1/out-l2/instP-n2')
ext_net2_sub2 = self._make_subnet(
self.fmt, {'network': ext_net2}, '200.200.0.1',
@ -3270,6 +3271,71 @@ class TestPolicyTarget(AIMBaseTestCase,
def test_get_gbp_details_no_pt_no_as_unrouted(self):
self._do_test_gbp_details_no_pt(use_as=False, routed=False)
def test_gbp_details_ext_net_no_pt(self):
# Test ports created on Neutron external networks
ext_net1, _, sn1 = self._setup_external_network(
'l1', dn='uni/tn-common/out-l1/instP-n1')
sn1 = {'subnet': sn1}
ext_net2, _, sn2 = self._setup_external_network(
'l2', dn='uni/tn-t1/out-l2/instP-n2')
sn2 = {'subnet': sn2}
with self.port(subnet=sn1) as port:
port_id = port['port']['id']
self._bind_port_to_host(port_id, 'h1')
sn1_1 = self._make_subnet(self.fmt, {'network': ext_net1},
'200.200.0.1', '200.200.0.0/16')
mapping = self.driver.get_gbp_details(
self._neutron_admin_context, device='tap%s' % port_id,
host='h1')
req_mapping = self.driver.request_endpoint_details(
self._neutron_admin_context,
request={'device': 'tap%s' % port_id,
'timestamp': 0, 'request_id': 'request_id'},
host='h1')
self._verify_gbp_details_assertions(
mapping, req_mapping, port_id, "EXT-l1", "common", sn1,
map_tenant_name=False)
vrf_id = '%s %s' % ("common", "openstack_EXT-l1")
vrf_mapping = self.driver.get_vrf_details(
self._neutron_admin_context, vrf_id=vrf_id)
supernet = [sn1['subnet']['cidr'], sn1_1['subnet']['cidr']]
self._verify_vrf_details_assertions(
mapping, "openstack_EXT-l1", vrf_id, supernet, "common")
self._verify_vrf_details_assertions(
vrf_mapping, "openstack_EXT-l1", vrf_id, supernet, "common")
with self.port(subnet=sn2) as port:
port_id = port['port']['id']
self._bind_port_to_host(port_id, 'h1')
sn2_1 = self._make_subnet(self.fmt, {'network': ext_net2},
'250.250.0.1', '250.250.0.0/16')
mapping = self.driver.get_gbp_details(
self._neutron_admin_context, device='tap%s' % port_id,
host='h1')
req_mapping = self.driver.request_endpoint_details(
nctx.get_admin_context(),
request={'device': 'tap%s' % port_id,
'timestamp': 0, 'request_id': 'request_id'},
host='h1')
self._verify_gbp_details_assertions(
mapping, req_mapping, port_id, "EXT-l2", "t1", sn2,
map_tenant_name=False)
vrf_id = '%s %s' % ("t1", "EXT-l2")
vrf_mapping = self.driver.get_vrf_details(
self._neutron_admin_context, vrf_id=vrf_id)
supernet = [sn2['subnet']['cidr'], sn2_1['subnet']['cidr']]
self._verify_vrf_details_assertions(
mapping, "EXT-l2", vrf_id, supernet, "t1")
self._verify_vrf_details_assertions(
vrf_mapping, "EXT-l2", vrf_id, supernet, "t1")
def test_ip_address_owner_update(self):
l3p = self.create_l3_policy(name='myl3')['l3_policy']
l2p = self.create_l2_policy(name='myl2',
@ -5174,7 +5240,7 @@ class TestNeutronPortOperation(AIMBaseTestCase):
# set allowed-address as fixed-IP of ports p3 and p4, which also have
# floating-IPs. Verify that FIP is "stolen" by p1 and p2
net_ext, rtr = self._setup_external_network(
net_ext, rtr, _ = self._setup_external_network(
'l1', dn='uni/tn-t1/out-l1/instP-n1')
p3 = self._make_port(self.fmt, net['network']['id'],
device_owner='compute:',