From 2edc1ab5c5ca8327760cf63de25263e41eab4d6e Mon Sep 17 00:00:00 2001 From: christides11 Date: Mon, 3 Jul 2023 16:51:09 -0700 Subject: [PATCH] Support for multi external networks extention The multi external networks extention allows multiple external networks to be associated with a single L3Outside. Change-Id: Ib872d8661fae321270130b4986d7d21249919ae6 --- .../versions/8c5b556b4df1_l3out_multi_epg.py | 38 +++++ .../alembic_migrations/versions/HEAD | 2 +- gbpservice/neutron/extensions/cisco_apic.py | 6 + .../ml2plus/drivers/apic_aim/exceptions.py | 5 + .../ml2plus/drivers/apic_aim/extension_db.py | 24 +++ .../drivers/apic_aim/extension_driver.py | 10 +- .../drivers/apic_aim/mechanism_driver.py | 67 +++++--- .../unit/plugins/ml2plus/test_apic_aim.py | 156 ++++++++++++++++-- 8 files changed, 270 insertions(+), 38 deletions(-) create mode 100644 gbpservice/neutron/db/migration/alembic_migrations/versions/8c5b556b4df1_l3out_multi_epg.py diff --git a/gbpservice/neutron/db/migration/alembic_migrations/versions/8c5b556b4df1_l3out_multi_epg.py b/gbpservice/neutron/db/migration/alembic_migrations/versions/8c5b556b4df1_l3out_multi_epg.py new file mode 100644 index 000000000..52ca224a3 --- /dev/null +++ b/gbpservice/neutron/db/migration/alembic_migrations/versions/8c5b556b4df1_l3out_multi_epg.py @@ -0,0 +1,38 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +"""add l3out multiple epgs network extension + +Revision ID: 8c5b556b4df1 +Revises: e29a84f6a15f + +""" + +# revision identifiers, used by Alembic. +revision = '8c5b556b4df1' +down_revision = 'e29a84f6a15f' + +from alembic import op +import sqlalchemy as sa +from sqlalchemy import sql + + +def upgrade(): + op.add_column('apic_aim_network_extensions', + sa.Column('multi_ext_nets', sa.Boolean, + nullable=False, server_default=sql.false())) + pass + + +def downgrade(): + pass diff --git a/gbpservice/neutron/db/migration/alembic_migrations/versions/HEAD b/gbpservice/neutron/db/migration/alembic_migrations/versions/HEAD index d01409c1f..97091db08 100644 --- a/gbpservice/neutron/db/migration/alembic_migrations/versions/HEAD +++ b/gbpservice/neutron/db/migration/alembic_migrations/versions/HEAD @@ -1 +1 @@ -e29a84f6a15f +8c5b556b4df1 diff --git a/gbpservice/neutron/extensions/cisco_apic.py b/gbpservice/neutron/extensions/cisco_apic.py index 0ba47392b..85f58a87d 100644 --- a/gbpservice/neutron/extensions/cisco_apic.py +++ b/gbpservice/neutron/extensions/cisco_apic.py @@ -54,6 +54,7 @@ POLICY_ENFORCEMENT_PREF = 'apic:policy_enforcement_pref' SNAT_SUBNET_ONLY = 'apic:snat_subnet_only' EPG_SUBNET = 'apic:epg_subnet' NO_NAT_CIDRS = 'apic:no_nat_cidrs' +MULTI_EXT_NETS = 'apic:multi_ext_nets' BD = 'BridgeDomain' EPG = 'EndpointGroup' @@ -361,6 +362,11 @@ NET_ATTRIBUTES = { 'convert_to': convert_apic_none_to_empty_list, 'validate': {'type:list_of_unique_strings': None}, }, + MULTI_EXT_NETS: { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, 'default': False, + 'convert_to': conv.convert_to_boolean, + }, } EXT_NET_ATTRIBUTES = { diff --git a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/exceptions.py b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/exceptions.py index 04abca24c..4126f8858 100644 --- a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/exceptions.py +++ b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/exceptions.py @@ -153,3 +153,8 @@ class InvalidNetworkForErspanSession(exceptions.BadRequest): class SnatPoolCannotBeUsedForGatewayIp(exceptions.BadRequest): message = _("Snat only subnet cannot be used to assign network gateway.") + + +class MultiExtNetworkMixing(exceptions.BadRequest): + message = _("All external networks associated with a l3out must " + "use the same apic:multi_ext_nets setting. ") diff --git a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/extension_db.py b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/extension_db.py index a29f63242..43f2ad277 100644 --- a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/extension_db.py +++ b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/extension_db.py @@ -79,6 +79,7 @@ class NetworkExtensionDb(model_base.BASEV2): nested_domain_infra_vlan = sa.Column(sa.Integer, nullable=True) nested_domain_service_vlan = sa.Column(sa.Integer, nullable=True) nested_domain_node_network_vlan = sa.Column(sa.Integer, nullable=True) + multi_ext_nets = sa.Column(sa.Boolean, default=False, nullable=False) class NetworkExtensionCidrDb(model_base.BASEV2): @@ -369,6 +370,7 @@ class ExtensionDbMixin(object): 'policy_enforcement_pref'] net_res[cisco_apic.NO_NAT_CIDRS] = [ c.cidr for c in db_no_nat_cidrs] + net_res[cisco_apic.MULTI_EXT_NETS] = db_obj['multi_ext_nets'] if net_res.get(cisco_apic.EXTERNAL_NETWORK): net_res[cisco_apic.EXTERNAL_CIDRS] = [c.cidr for c in db_cidrs] return net_res @@ -417,6 +419,8 @@ class ExtensionDbMixin(object): if cisco_apic.POLICY_ENFORCEMENT_PREF in res_dict: db_obj['policy_enforcement_pref'] = res_dict[ cisco_apic.POLICY_ENFORCEMENT_PREF] + if cisco_apic.MULTI_EXT_NETS in res_dict: + db_obj['multi_ext_nets'] = res_dict[cisco_apic.MULTI_EXT_NETS] session.add(db_obj) if cisco_apic.EXTERNAL_CIDRS in res_dict: @@ -513,6 +517,15 @@ class ExtensionDbMixin(object): return [c[0] for c in cidrs] + def get_external_cidrs_by_net_id(self, session, nid): + query = BAKERY(lambda s: s.query( + NetworkExtensionCidrDb.cidr)) + query += lambda q: q.filter_by( + network_id=sa.bindparam('nid')) + cidrs = query(session).params(nid=nid) + + return [i[0] for i in cidrs] + def get_subnet_extn_db(self, session, subnet_id): query = BAKERY(lambda s: s.query( SubnetExtensionDb)) @@ -590,6 +603,17 @@ class ExtensionDbMixin(object): c_contracts.append(db_contract['contract_name']) return attr_dict + def get_network_ids_and_multi_by_l3out_dn(self, session, dn): + query = BAKERY(lambda s: s.query( + NetworkExtensionDb.network_id, + NetworkExtensionDb.multi_ext_nets)) + query += lambda q: q.filter( + NetworkExtensionDb.external_network_dn.like( + sa.bindparam('dn') + "/%")) + ids_and_multis = query(session).params(dn=dn) + + return [(i[0], i[1]) for i in ids_and_multis] + def _update_list_attr(self, session, db_model, column, new_values, **filters): if new_values is None: diff --git a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/extension_driver.py b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/extension_driver.py index 6de105ba6..0dce45050 100644 --- a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/extension_driver.py +++ b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/extension_driver.py @@ -137,6 +137,7 @@ class ApicExtensionDriver(api_plus.ExtensionDriver, is_bgp_enabled = data.get(cisco_apic.BGP, False) bgp_type = data.get(cisco_apic.BGP_TYPE, "default_export") asn = data.get(cisco_apic.BGP_ASN, "0") + use_multi_ext_nets = data.get(cisco_apic.MULTI_EXT_NETS, False) self.validate_bgp_params(data) res_dict = {cisco_apic.SVI: is_svi, cisco_apic.BGP: is_bgp_enabled, @@ -162,6 +163,7 @@ class ApicExtensionDriver(api_plus.ExtensionDriver, data.get(cisco_apic.POLICY_ENFORCEMENT_PREF, "unenforced"), cisco_apic.NO_NAT_CIDRS: data.get(cisco_apic.NO_NAT_CIDRS), + cisco_apic.MULTI_EXT_NETS: use_multi_ext_nets, } if cisco_apic.VLANS_LIST in (data.get( cisco_apic.NESTED_DOMAIN_ALLOWED_VLANS) or {}): @@ -225,8 +227,9 @@ class ApicExtensionDriver(api_plus.ExtensionDriver, cisco_apic.EXTRA_CONSUMED_CONTRACTS, cisco_apic.EPG_CONTRACT_MASTERS, cisco_apic.POLICY_ENFORCEMENT_PREF, - cisco_apic.NO_NAT_CIDRS] - if not(set(update_attrs) & set(data.keys())): + cisco_apic.NO_NAT_CIDRS, + cisco_apic.MULTI_EXT_NETS] + if not (set(update_attrs) & set(data.keys())): return res_dict = {} @@ -246,7 +249,8 @@ class ApicExtensionDriver(api_plus.ExtensionDriver, cisco_apic.EXTRA_CONSUMED_CONTRACTS, cisco_apic.EPG_CONTRACT_MASTERS, cisco_apic.POLICY_ENFORCEMENT_PREF, - cisco_apic.NO_NAT_CIDRS] + cisco_apic.NO_NAT_CIDRS, + cisco_apic.MULTI_EXT_NETS] for e_k in ext_keys: if e_k in data: res_dict.update({e_k: data[e_k]}) 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 28d280ae3..7dc309099 100644 --- a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py +++ b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py @@ -809,6 +809,8 @@ class ApicMechanismDriver(api_plus.MechanismDriver, aim_ctx = aim_context.AimContext(session) is_ext = self._is_external(current) + multi_ext_nets_enb = self._is_multi_ext_nets_enabled(current) + wanted_epg_name = current['id'] if multi_ext_nets_enb else None is_svi = self._is_svi(current) if ((current[cisco_apic.EXTRA_PROVIDED_CONTRACTS] or @@ -830,17 +832,28 @@ class ApicMechanismDriver(api_plus.MechanismDriver, l3out, ext_net, ns = self._get_aim_nat_strategy(current) if not ext_net: return # Unmanaged external network + + other_nets = self.get_network_ids_and_multi_by_l3out_dn( + session, l3out.dn) + other_nets = [value for value in other_nets + if value[0] != current['id']] + if len(other_nets) > 0 and other_nets[0][1] != multi_ext_nets_enb: + raise exceptions.MultiExtNetworkMixing() + domains = self._get_vmm_domains(aim_ctx, ns) - ns.create_l3outside(aim_ctx, l3out, vmm_domains=domains) - ns.create_external_network(aim_ctx, ext_net) + ns.create_l3outside(aim_ctx, l3out, vmm_domains=domains, + epg_name=wanted_epg_name) + ns.create_external_network(aim_ctx, ext_net, + epg_name=wanted_epg_name) # Get external CIDRs for all external networks that share # this APIC external network. cidrs = sorted( self.get_external_cidrs_by_ext_net_dn( - session, ext_net.dn, lock_update=True)) + session, ext_net.dn)) ns.update_external_cidrs(aim_ctx, ext_net, cidrs) - for resource in ns.get_l3outside_resources(aim_ctx, l3out): + for resource in ns.get_l3outside_resources(aim_ctx, l3out, + epg_name=wanted_epg_name): if isinstance(resource, aim_resource.BridgeDomain): bd = resource elif isinstance(resource, aim_resource.EndpointGroup): @@ -1207,21 +1220,33 @@ class ApicMechanismDriver(api_plus.MechanismDriver, l3out, ext_net, ns = self._get_aim_nat_strategy(current) if not ext_net: return # Unmanaged external network - # REVISIT: lock_update=True is needed to handle races. Find - # alternative solutions since Neutron discourages using such - # queries. - other_nets = set( - self.get_network_ids_by_ext_net_dn( - session, ext_net.dn, lock_update=True)) - other_nets.discard(current['id']) - if not other_nets: - ns.delete_external_network(aim_ctx, ext_net) - other_nets = set( - self.get_network_ids_by_l3out_dn( - session, l3out.dn, lock_update=True)) - other_nets.discard(current['id']) - if not other_nets: - ns.delete_l3outside(aim_ctx, l3out) + multi_ext_nets_enb = self._is_multi_ext_nets_enabled(current) + + if multi_ext_nets_enb: + cidrs_to_delete = self.get_external_cidrs_by_net_id( + session, current['id']) + ns.delete_external_network(aim_ctx, ext_net, + epg_name=current['id'], + cidrs=cidrs_to_delete) + ns.delete_l3outside(aim_ctx, l3out, epg_name=current['id'], + cidrs=cidrs_to_delete) + else: + # REVISIT: lock_update=True is needed to handle races. Find + # alternative solutions since Neutron discourages using such + # queries. + other_nets = set( + self.get_network_ids_by_ext_net_dn( + session, ext_net.dn, lock_update=True)) + other_nets.discard(current['id']) + if not other_nets: + ns.delete_external_network(aim_ctx, ext_net) + other_nets = set( + self.get_network_ids_by_l3out_dn( + session, l3out.dn, lock_update=True)) + other_nets.discard(current['id']) + if not other_nets: + ns.delete_l3outside(aim_ctx, l3out) + elif self._is_svi(current): l3out, ext_net, _ = self._get_aim_external_objects(current) aim_l3out = self.aim.get(aim_ctx, l3out) @@ -4768,6 +4793,9 @@ class ApicMechanismDriver(api_plus.MechanismDriver, def _is_bgp_enabled(self, network): return network.get(cisco_apic.BGP) + def _is_multi_ext_nets_enabled(self, network): + return network.get(cisco_apic.MULTI_EXT_NETS) + def _nat_type_to_strategy(self, nat_type): ns_cls = nat_strategy.DistributedNatStrategy if nat_type == '': @@ -7130,6 +7158,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver, cisco_apic.NESTED_DOMAIN_INFRA_VLAN: None, cisco_apic.NESTED_DOMAIN_SERVICE_VLAN: None, cisco_apic.NESTED_DOMAIN_NODE_NETWORK_VLAN: None, + cisco_apic.MULTI_EXT_NETS: False, } if net_db.aim_mapping and net_db.aim_mapping.get(cisco_apic.BD): res_dict.update({cisco_apic.BD: net_db.aim_mapping[cisco_apic.BD]}) 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 159971fe1..56f645aee 100644 --- a/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py +++ b/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py @@ -359,7 +359,8 @@ class ApicAimTestCase(test_address_scope.AddressScopeTestCase, ACTIVE_ACTIVE_AAP, EPG_SUBNET, CIDR, PROV, CONS, SVI, BGP, BGP_TYPE, ASN, - 'provider:network_type' + 'provider:network_type', + 'apic:multi_ext_nets' ) self.name_mapper = apic_mapper.APICNameMapper() self.t1_aname = self.name_mapper.project(None, 't1') @@ -449,7 +450,8 @@ class ApicAimTestCase(test_address_scope.AddressScopeTestCase, self.fmt) return self.deserialize(self.fmt, req.get_response(self.api)) - def _make_ext_network(self, name, dn=None, nat_type=None, cidrs=None): + def _make_ext_network(self, name, dn=None, nat_type=None, cidrs=None, + multi_ext_nets=False): kwargs = {'router:external': True} if dn: kwargs[DN] = {'ExternalNetwork': dn} @@ -459,6 +461,8 @@ class ApicAimTestCase(test_address_scope.AddressScopeTestCase, kwargs['apic:nat_type'] = self.nat_type if cidrs: kwargs[CIDR] = cidrs + if multi_ext_nets: + kwargs['apic:multi_ext_nets'] = True return self._make_network(self.fmt, name, True, arg_list=self.extension_attributes, @@ -4114,8 +4118,10 @@ class TestSyncState(ApicAimTestCase): TestSyncState._mocked_get_statuses): self._test_router('error') - def _test_external_network(self, expected_state, dn=None, msg=None): - net = self._make_ext_network('net1', dn=dn) + def _test_external_network(self, expected_state, dn=None, msg=None, + multi_ext_nets=True): + net = self._make_ext_network('net1', dn=dn, + multi_ext_nets=multi_ext_nets) self.assertEqual(expected_state, net['apic:synchronization_state'], msg) net = self._show('networks', net['id'])['network'] @@ -4156,6 +4162,38 @@ class TestSyncState(ApicAimTestCase): dn=self.dn_t1_l1_n1, msg='%s' % a_res) + def test_external_network_multi_ext_networks(self): + ext_net = aim_resource.ExternalNetwork.from_dn(self.dn_t1_l1_n1) + ext_net.monitored = True + aim_ctx = aim_context.AimContext(self.db_session) + self.aim_mgr.create(aim_ctx, ext_net) + + with mock.patch('aim.aim_manager.AimManager.get_status', + TestSyncState._get_synced_status): + with mock.patch('aim.aim_manager.AimManager.get_statuses', + TestSyncState._mocked_get_statuses): + self._test_external_network('synced', + dn=self.dn_t1_l1_n1, + multi_ext_nets=True) + + for expected_status, status_func in [ + ('build', TestSyncState._get_pending_status_for_type), + ('error', TestSyncState._get_failed_status_for_type)]: + for a_res in [aim_resource.ExternalNetwork, + aim_resource.EndpointGroup, + aim_resource.BridgeDomain, + aim_resource.VRF]: + def get_status(self, context, resource, create_if_absent=True): + return status_func(context, resource, a_res) + with mock.patch('aim.aim_manager.AimManager.get_status', + get_status): + with mock.patch('aim.aim_manager.AimManager.get_statuses', + TestSyncState._mocked_get_statuses): + self._test_external_network(expected_status, + dn=self.dn_t1_l1_n1, + msg='%s' % a_res, + multi_ext_nets=True) + def test_unmanaged_external_network(self): self._test_external_network('build') @@ -7262,6 +7300,94 @@ class TestExtensionAttributes(ApicAimTestCase): self.assertFalse(extn.get_network_extn_db(session, net1['id'])) self.assertFalse(extn.get_network_extn_db(session, net2['id'])) + def test_external_network_with_multi_nets_lifecycle(self): + session = db_api.get_reader_session() + extn = extn_db.ExtensionDbMixin() + + # create with APIC DN, nat_typeand default CIDR + net1 = self._make_ext_network('net1', + dn=self.dn_t1_l1_n1, + nat_type='', + multi_ext_nets=True) + + self.assertEqual(self.dn_t1_l1_n1, + net1[DN]['ExternalNetwork']) + self.assertEqual('', net1['apic:nat_type']) + self.assertEqual(['0.0.0.0/0'], net1[CIDR]) + + net1 = self._list( + 'networks', query_params=('id=%s' % net1['id']))['networks'][0] + self.assertEqual(self.dn_t1_l1_n1, + net1[DN]['ExternalNetwork']) + self.assertEqual('', net1['apic:nat_type']) + self.assertEqual(['0.0.0.0/0'], net1[CIDR]) + + # create with nat_type set to default, and CIDR specified + net2 = self._make_ext_network('net2', + dn=self.dn_t1_l2_n2, + cidrs=['5.5.5.0/24', '10.20.0.0/16'], + multi_ext_nets=True) + self.assertEqual('distributed', net2['apic:nat_type']) + self.assertEqual(['10.20.0.0/16', '5.5.5.0/24'], + sorted(net2[CIDR])) + + net2 = self._list( + 'networks', query_params=('id=%s' % net2['id']))['networks'][0] + self.assertEqual('distributed', net2['apic:nat_type']) + self.assertEqual(['10.20.0.0/16', '5.5.5.0/24'], + sorted(net2[CIDR])) + + # update CIDR + net2 = self._update('networks', net2['id'], + {'network': {CIDR: ['20.20.30.0/24']}})['network'] + self.assertEqual('distributed', net2['apic:nat_type']) + self.assertEqual(['20.20.30.0/24'], net2[CIDR]) + + net2 = self._list( + 'networks', query_params=('id=%s' % net2['id']))['networks'][0] + self.assertEqual('distributed', net2['apic:nat_type']) + self.assertEqual(['20.20.30.0/24'], net2[CIDR]) + + net2 = self._update('networks', net2['id'], + {'network': {CIDR: []}})['network'] + self.assertEqual([], net2[CIDR]) + + net2 = self._list( + 'networks', query_params=('id=%s' % net2['id']))['networks'][0] + self.assertEqual([], net2[CIDR]) + + # create without APIC DN -> this is an unmanaged network + net3 = self._make_ext_network('net3') + self.assertTrue(DN not in net3 or 'ExternalNetwork' not in net3[DN]) + self.assertFalse('apic:nat_type' in net3) + self.assertFalse(CIDR in net3) + + net3 = self._list( + 'networks', query_params=('id=%s' % net3['id']))['networks'][0] + self.assertTrue(DN not in net3 or 'ExternalNetwork' not in net3[DN]) + self.assertFalse('apic:nat_type' in net3) + self.assertFalse(CIDR in net3) + + # updating CIDR of unmanaged network is no-op + net3 = self._update('networks', net3['id'], + {'network': {CIDR: ['30.30.20.0/24']}})['network'] + self.assertTrue(DN not in net3 or 'ExternalNetwork' not in net3[DN]) + self.assertFalse('apic:nat_type' in net3) + self.assertFalse(CIDR in net3) + + net3 = self._list( + 'networks', query_params=('id=%s' % net3['id']))['networks'][0] + self.assertTrue(DN not in net3 or 'ExternalNetwork' not in net3[DN]) + self.assertFalse('apic:nat_type' in net3) + self.assertFalse(CIDR in net3) + + # delete the external networks + self._delete('networks', net2['id']) + self._delete('networks', net1['id']) + + self.assertFalse(extn.get_network_extn_db(session, net1['id'])) + self.assertFalse(extn.get_network_extn_db(session, net2['id'])) + def test_external_network_fail(self): # APIC DN not specified resp = self._create_network(self.fmt, 'net1', True, @@ -8033,11 +8159,11 @@ class TestExternalConnectivityBase(object): 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=[], epg_name=None) 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) + mock.ANY, a_ext_net, epg_name=None) 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 = aim_resource.EndpointGroup( @@ -8086,7 +8212,7 @@ class TestExternalConnectivityBase(object): self._make_ext_network('net2', dn=self.dn_t1_l1_n1) self.mock_ns.create_external_network.assert_called_once_with( - mock.ANY, a_ext_net) + mock.ANY, a_ext_net, epg_name=None) self.mock_ns.update_external_cidrs.assert_called_once_with( mock.ANY, a_ext_net, ['0.0.0.0/0']) self._validate() @@ -9161,11 +9287,11 @@ class TestExternalConnectivityBase(object): 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) + vmm_domains=vmm_domains, epg_name=None) 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) + mock.ANY, a_ext_net, epg_name=None) 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, @@ -9214,17 +9340,17 @@ class TestExternalConnectivityBase(object): a_ext_net = aim_resource.ExternalNetwork( tenant_name=self.t1_aname, l3out_name='l1', name='n1') self.mock_ns.create_l3outside.assert_called_once_with( - mock.ANY, a_l3out, vmm_domains=[]) + mock.ANY, a_l3out, vmm_domains=[], epg_name=None) self.mock_ns.create_external_network.assert_called_once_with( - mock.ANY, a_ext_net) + mock.ANY, a_ext_net, epg_name=None) # create second network that shares APIC l3out and external-network self.mock_ns.reset_mock() net2 = self._make_ext_network('net2', dn=self.dn_t1_l1_n1) self.mock_ns.create_l3outside.assert_called_once_with( - mock.ANY, a_l3out, vmm_domains=[]) + mock.ANY, a_l3out, vmm_domains=[], epg_name=None) self.mock_ns.create_external_network.assert_called_once_with( - mock.ANY, a_ext_net) + mock.ANY, a_ext_net, epg_name=None) # create third network that shares APIC l3out only self.mock_ns.reset_mock() @@ -9233,9 +9359,9 @@ class TestExternalConnectivityBase(object): a_ext_net3 = aim_resource.ExternalNetwork( tenant_name=self.t1_aname, l3out_name='l1', name='n2') self.mock_ns.create_l3outside.assert_called_once_with( - mock.ANY, a_l3out, vmm_domains=[]) + mock.ANY, a_l3out, vmm_domains=[], epg_name=None) self.mock_ns.create_external_network.assert_called_once_with( - mock.ANY, a_ext_net3) + mock.ANY, a_ext_net3, epg_name=None) # delete net2 self.mock_ns.reset_mock()