diff --git a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/exceptions.py b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/exceptions.py index 4126f8858..04abca24c 100644 --- a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/exceptions.py +++ b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/exceptions.py @@ -153,8 +153,3 @@ 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 00c235db0..b55c316d3 100644 --- a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/extension_db.py +++ b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/extension_db.py @@ -448,6 +448,18 @@ class ExtensionDbMixin(object): return [i[0] for i in ids] + def get_network_ids_by_l3out_dn_filter_multi(self, session, dn, + wanted_multi_val=False): + 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 = query(session).params(dn=dn) + + return [i[0] for i in ids if i[1] is wanted_multi_val] + def get_svi_network_ids_by_l3out_dn(self, session, dn, lock_update=False): query = BAKERY(lambda s: s.query( NetworkExtensionDb.network_id)) 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 eef97757d..52e286967 100644 --- a/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py +++ b/gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/mechanism_driver.py @@ -823,14 +823,6 @@ 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, epg_name=wanted_epg_name) @@ -1225,18 +1217,22 @@ class ApicMechanismDriver(api_plus.MechanismDriver, # REVISIT: lock_update=True is needed to handle races. Find # alternative solutions since Neutron discourages using such # queries. + cidrs_to_delete = self.get_external_cidrs_by_net_id( + session, current['id']) 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) + ns.delete_external_network(aim_ctx, ext_net, + cidrs=cidrs_to_delete) other_nets = set( - self.get_network_ids_by_l3out_dn( - session, l3out.dn, lock_update=True)) + self.get_network_ids_by_l3out_dn_filter_multi( + session, l3out.dn, False)) other_nets.discard(current['id']) if not other_nets: - ns.delete_l3outside(aim_ctx, l3out) + ns.delete_l3outside(aim_ctx, l3out, + cidrs=cidrs_to_delete) elif self._is_svi(current): l3out, ext_net, _ = self._get_aim_external_objects(current) 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 a567c44d5..6fbd56a6c 100644 --- a/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py +++ b/gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py @@ -8079,11 +8079,13 @@ class TestExternalConnectivityBase(object): self._delete('networks', net1['id']) self.mock_ns.delete_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'), + cidrs=['33.33.33.0/30']) self.mock_ns.delete_external_network.assert_called_once_with( mock.ANY, aim_resource.ExternalNetwork( - tenant_name=self.t1_aname, l3out_name='l1', name='n1')) + tenant_name=self.t1_aname, l3out_name='l1', name='n1'), + cidrs=['33.33.33.0/30']) # create with default CIDR self.mock_ns.reset_mock() @@ -9249,15 +9251,15 @@ class TestExternalConnectivityBase(object): self._delete('networks', net1['id']) self.mock_ns.delete_l3outside.assert_not_called() self.mock_ns.delete_external_network.assert_called_once_with( - mock.ANY, a_ext_net) + mock.ANY, a_ext_net, cidrs=['0.0.0.0/0']) # delete net3 self.mock_ns.reset_mock() self._delete('networks', net3['id']) self.mock_ns.delete_l3outside.assert_called_once_with( - mock.ANY, a_l3out) + mock.ANY, a_l3out, cidrs=['0.0.0.0/0']) self.mock_ns.delete_external_network.assert_called_once_with( - mock.ANY, a_ext_net3) + mock.ANY, a_ext_net3, cidrs=['0.0.0.0/0']) def test_shared_l3out_external_subnet_overlap(self): net1 = self._make_ext_network('net1', dn=self.dn_t1_l1_n1)