Multi External Network Coexisting

Support for having networks with and without the multi_ext_nets extension
to share the same L3Outside.

Change-Id: Ia2daff31059437ed83813d93d98865131f2919b5
(cherry picked from commit 4f5f8aa66f)
This commit is contained in:
christides11 2023-07-31 14:54:16 -07:00 committed by Christopher Collins
parent 807b8fb07e
commit 14299b08dd
4 changed files with 27 additions and 22 deletions

View File

@ -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. ")

View File

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

View File

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

View File

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