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):