From 1af8d44586f0cfef1cdd49a801ed8e86241799e7 Mon Sep 17 00:00:00 2001 From: Thomas Bachman Date: Sun, 4 Feb 2018 15:20:13 +0000 Subject: [PATCH] Consult Host Domain Mapping for External EPG When creating L3 Out using the AIM library, consult the Host Domain Mapping V2 table to determine which VMM Domains to associate with the external EPG. Only VMM Domains of type 'OpenStack' are used, and only wildcard host entries (i.e. entries where host is '*') are used. If there are no wildcard mappings for OpenStack VMM domains, then all OpenStack VMM domains are associated with the external EPG. Change-Id: I220922704d80d8b855a09c9d03387f836542253b --- .../drivers/apic_aim/mechanism_driver.py | 22 +++++- .../unit/plugins/ml2plus/test_apic_aim.py | 67 ++++++++++++++++++- 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py index ad003f3a8..8596d076d 100644 --- a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py +++ b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py @@ -362,6 +362,16 @@ class ApicMechanismDriver(api_plus.MechanismDriver, if not self._is_dependent_port_change(p)] self._notify_port_update_bulk(plugin_context, affected_port_ids) + def _get_unique_domains(self, mappings): + domains = [] + unique_domains = set() + for mapping in mappings: + if mapping.domain_name not in unique_domains: + unique_domains.add(mapping.domain_name) + domains.append({'type': mapping.domain_type, + 'name': mapping.domain_name}) + return domains + def create_network_precommit(self, context): current = context.current LOG.debug("APIC AIM MD creating network: %s", current) @@ -373,7 +383,17 @@ class ApicMechanismDriver(api_plus.MechanismDriver, l3out, ext_net, ns = self._get_aim_nat_strategy(current) if not ext_net: return # Unmanaged external network - ns.create_l3outside(aim_ctx, l3out) + aim_hd_mappings = self.aim.find(aim_ctx, + aim_infra.HostDomainMappingV2, + domain_type=utils.OPENSTACK_VMM_TYPE) + + domains = [] + if aim_hd_mappings: + domains = self._get_unique_domains(aim_hd_mappings) + if not domains: + domains, _ = self.get_aim_domains(aim_ctx) + + ns.create_l3outside(aim_ctx, l3out, vmm_domains=domains) ns.create_external_network(aim_ctx, ext_net) ns.update_external_cidrs(aim_ctx, ext_net, current[cisco_apic.EXTERNAL_CIDRS]) diff --git a/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py b/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py index 0f50b35d2..7702cd9d6 100644 --- a/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py +++ b/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py @@ -4308,7 +4308,8 @@ class TestExternalConnectivityBase(object): cidrs=['20.10.0.0/16', '4.4.4.0/24']) self.mock_ns.create_l3outside.assert_called_once_with( mock.ANY, - aim_resource.L3Outside(tenant_name=self.t1_aname, name='l1')) + aim_resource.L3Outside(tenant_name=self.t1_aname, name='l1'), + vmm_domains=[]) a_ext_net = aim_resource.ExternalNetwork( tenant_name=self.t1_aname, l3out_name='l1', name='n1') self.mock_ns.create_external_network.assert_called_once_with( @@ -5074,6 +5075,70 @@ class TestExternalConnectivityBase(object): cv.assert_not_called() dv.assert_called_once_with(mock.ANY, a_ext_net1, vrf) + def _create_domains_and_mappings(self, ctx, mappings, create_hds=False): + # The vmm_domains contains the domains that should be + # associated in the create_l3outside call in AIM + vmm_domains = [] + for dom in mappings: + if dom['type'] == 'OpenStack': + self.aim_mgr.create(ctx, + aim_resource.VMMDomain(type=dom['type'], + name=dom['name']), + overwrite=True) + if (not create_hds and dom['host'] == '*') or ( + create_hds and dom['host'] != '*'): + vmm_domains.append({'name': dom['name'], + 'type': dom['type']}) + if create_hds: + hd_mapping = aim_infra.HostDomainMappingV2( + host_name=dom['host'], domain_name=dom['name'], + domain_type=dom['type']) + self.aim_mgr.create(ctx, hd_mapping) + return vmm_domains + + def _test_external_network_lifecycle_with_domains(self, create_hds=False): + mappings = [{'host': 'opflex-1', 'name': 'vm1', 'type': 'OpenStack'}, + {'host': 'opflex-2', 'name': 'vm2', 'type': 'OpenStack'}, + {'host': 'esx-1', 'name': 'vm3', 'type': 'VMware'}, + {'host': '*', 'name': 'vm1', 'type': 'OpenStack'}, + {'host': '*', 'name': 'vm2', 'type': 'OpenStack'}, + {'host': '*', 'name': 'vm3', 'type': 'VMware'}, + {'host': 'h1', 'name': 'ph1', 'type': 'PhysDom'}, + {'host': 'h2', 'name': 'ph2', 'type': 'PhysDom'}, + {'host': 'h3', 'name': 'ph3', 'type': 'PhysDom'}] + aim_ctx = aim_context.AimContext(self.db_session) + # test setup + vmm_domains = self._create_domains_and_mappings(aim_ctx, mappings, + create_hds=create_hds) + + self._register_agent('opflex-1', AGENT_CONF_OPFLEX) + self._register_agent('opflex-2', AGENT_CONF_OPFLEX) + self._make_ext_network('net1', + dn=self.dn_t1_l1_n1, + cidrs=['20.10.0.0/16', '4.4.4.0/24']) + self.mock_ns.create_l3outside.assert_called_once_with( + mock.ANY, + aim_resource.L3Outside(tenant_name=self.t1_aname, name='l1'), + vmm_domains=vmm_domains) + a_ext_net = aim_resource.ExternalNetwork( + tenant_name=self.t1_aname, l3out_name='l1', name='n1') + self.mock_ns.create_external_network.assert_called_once_with( + mock.ANY, a_ext_net) + self.mock_ns.update_external_cidrs.assert_called_once_with( + mock.ANY, a_ext_net, ['20.10.0.0/16', '4.4.4.0/24']) + ext_epg = self.aim_mgr.find(aim_ctx, aim_resource.EndpointGroup, + tenant_name=self.t1_aname, + app_profile_name=self._app_profile_name, + name='EXT-l1') + self.assertEqual(1, len(ext_epg)) + self.assertEqual(ext_epg[0].vmm_domains, vmm_domains) + + def test_external_network_default_domains(self): + self._test_external_network_lifecycle_with_domains() + + def test_external_network_host_domains(self): + self._test_external_network_lifecycle_with_domains(create_hds=True) + class TestExternalDistributedNat(TestExternalConnectivityBase, ApicAimTestCase):