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:
parent
807b8fb07e
commit
14299b08dd
|
@ -153,8 +153,3 @@ class InvalidNetworkForErspanSession(exceptions.BadRequest):
|
||||||
|
|
||||||
class SnatPoolCannotBeUsedForGatewayIp(exceptions.BadRequest):
|
class SnatPoolCannotBeUsedForGatewayIp(exceptions.BadRequest):
|
||||||
message = _("Snat only subnet cannot be used to assign network gateway.")
|
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. ")
|
|
||||||
|
|
|
@ -448,6 +448,18 @@ class ExtensionDbMixin(object):
|
||||||
|
|
||||||
return [i[0] for i in ids]
|
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):
|
def get_svi_network_ids_by_l3out_dn(self, session, dn, lock_update=False):
|
||||||
query = BAKERY(lambda s: s.query(
|
query = BAKERY(lambda s: s.query(
|
||||||
NetworkExtensionDb.network_id))
|
NetworkExtensionDb.network_id))
|
||||||
|
|
|
@ -823,14 +823,6 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
||||||
l3out, ext_net, ns = self._get_aim_nat_strategy(current)
|
l3out, ext_net, ns = self._get_aim_nat_strategy(current)
|
||||||
if not ext_net:
|
if not ext_net:
|
||||||
return # Unmanaged external network
|
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)
|
domains = self._get_vmm_domains(aim_ctx, ns)
|
||||||
ns.create_l3outside(aim_ctx, l3out, vmm_domains=domains,
|
ns.create_l3outside(aim_ctx, l3out, vmm_domains=domains,
|
||||||
epg_name=wanted_epg_name)
|
epg_name=wanted_epg_name)
|
||||||
|
@ -1225,18 +1217,22 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
||||||
# REVISIT: lock_update=True is needed to handle races. Find
|
# REVISIT: lock_update=True is needed to handle races. Find
|
||||||
# alternative solutions since Neutron discourages using such
|
# alternative solutions since Neutron discourages using such
|
||||||
# queries.
|
# queries.
|
||||||
|
cidrs_to_delete = self.get_external_cidrs_by_net_id(
|
||||||
|
session, current['id'])
|
||||||
other_nets = set(
|
other_nets = set(
|
||||||
self.get_network_ids_by_ext_net_dn(
|
self.get_network_ids_by_ext_net_dn(
|
||||||
session, ext_net.dn, lock_update=True))
|
session, ext_net.dn, lock_update=True))
|
||||||
other_nets.discard(current['id'])
|
other_nets.discard(current['id'])
|
||||||
if not other_nets:
|
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(
|
other_nets = set(
|
||||||
self.get_network_ids_by_l3out_dn(
|
self.get_network_ids_by_l3out_dn_filter_multi(
|
||||||
session, l3out.dn, lock_update=True))
|
session, l3out.dn, False))
|
||||||
other_nets.discard(current['id'])
|
other_nets.discard(current['id'])
|
||||||
if not other_nets:
|
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):
|
elif self._is_svi(current):
|
||||||
l3out, ext_net, _ = self._get_aim_external_objects(current)
|
l3out, ext_net, _ = self._get_aim_external_objects(current)
|
||||||
|
|
|
@ -8079,11 +8079,13 @@ class TestExternalConnectivityBase(object):
|
||||||
self._delete('networks', net1['id'])
|
self._delete('networks', net1['id'])
|
||||||
self.mock_ns.delete_l3outside.assert_called_once_with(
|
self.mock_ns.delete_l3outside.assert_called_once_with(
|
||||||
mock.ANY,
|
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(
|
self.mock_ns.delete_external_network.assert_called_once_with(
|
||||||
mock.ANY,
|
mock.ANY,
|
||||||
aim_resource.ExternalNetwork(
|
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
|
# create with default CIDR
|
||||||
self.mock_ns.reset_mock()
|
self.mock_ns.reset_mock()
|
||||||
|
@ -9249,15 +9251,15 @@ class TestExternalConnectivityBase(object):
|
||||||
self._delete('networks', net1['id'])
|
self._delete('networks', net1['id'])
|
||||||
self.mock_ns.delete_l3outside.assert_not_called()
|
self.mock_ns.delete_l3outside.assert_not_called()
|
||||||
self.mock_ns.delete_external_network.assert_called_once_with(
|
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
|
# delete net3
|
||||||
self.mock_ns.reset_mock()
|
self.mock_ns.reset_mock()
|
||||||
self._delete('networks', net3['id'])
|
self._delete('networks', net3['id'])
|
||||||
self.mock_ns.delete_l3outside.assert_called_once_with(
|
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(
|
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):
|
def test_shared_l3out_external_subnet_overlap(self):
|
||||||
net1 = self._make_ext_network('net1', dn=self.dn_t1_l1_n1)
|
net1 = self._make_ext_network('net1', dn=self.dn_t1_l1_n1)
|
||||||
|
|
Loading…
Reference in New Issue