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
This commit is contained in:
parent
f9c7a63f7a
commit
2edc1ab5c5
|
@ -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
|
|
@ -1 +1 @@
|
||||||
e29a84f6a15f
|
8c5b556b4df1
|
||||||
|
|
|
@ -54,6 +54,7 @@ POLICY_ENFORCEMENT_PREF = 'apic:policy_enforcement_pref'
|
||||||
SNAT_SUBNET_ONLY = 'apic:snat_subnet_only'
|
SNAT_SUBNET_ONLY = 'apic:snat_subnet_only'
|
||||||
EPG_SUBNET = 'apic:epg_subnet'
|
EPG_SUBNET = 'apic:epg_subnet'
|
||||||
NO_NAT_CIDRS = 'apic:no_nat_cidrs'
|
NO_NAT_CIDRS = 'apic:no_nat_cidrs'
|
||||||
|
MULTI_EXT_NETS = 'apic:multi_ext_nets'
|
||||||
|
|
||||||
BD = 'BridgeDomain'
|
BD = 'BridgeDomain'
|
||||||
EPG = 'EndpointGroup'
|
EPG = 'EndpointGroup'
|
||||||
|
@ -361,6 +362,11 @@ NET_ATTRIBUTES = {
|
||||||
'convert_to': convert_apic_none_to_empty_list,
|
'convert_to': convert_apic_none_to_empty_list,
|
||||||
'validate': {'type:list_of_unique_strings': None},
|
'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 = {
|
EXT_NET_ATTRIBUTES = {
|
||||||
|
|
|
@ -153,3 +153,8 @@ 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. ")
|
||||||
|
|
|
@ -79,6 +79,7 @@ class NetworkExtensionDb(model_base.BASEV2):
|
||||||
nested_domain_infra_vlan = sa.Column(sa.Integer, nullable=True)
|
nested_domain_infra_vlan = sa.Column(sa.Integer, nullable=True)
|
||||||
nested_domain_service_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)
|
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):
|
class NetworkExtensionCidrDb(model_base.BASEV2):
|
||||||
|
@ -369,6 +370,7 @@ class ExtensionDbMixin(object):
|
||||||
'policy_enforcement_pref']
|
'policy_enforcement_pref']
|
||||||
net_res[cisco_apic.NO_NAT_CIDRS] = [
|
net_res[cisco_apic.NO_NAT_CIDRS] = [
|
||||||
c.cidr for c in db_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):
|
if net_res.get(cisco_apic.EXTERNAL_NETWORK):
|
||||||
net_res[cisco_apic.EXTERNAL_CIDRS] = [c.cidr for c in db_cidrs]
|
net_res[cisco_apic.EXTERNAL_CIDRS] = [c.cidr for c in db_cidrs]
|
||||||
return net_res
|
return net_res
|
||||||
|
@ -417,6 +419,8 @@ class ExtensionDbMixin(object):
|
||||||
if cisco_apic.POLICY_ENFORCEMENT_PREF in res_dict:
|
if cisco_apic.POLICY_ENFORCEMENT_PREF in res_dict:
|
||||||
db_obj['policy_enforcement_pref'] = res_dict[
|
db_obj['policy_enforcement_pref'] = res_dict[
|
||||||
cisco_apic.POLICY_ENFORCEMENT_PREF]
|
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)
|
session.add(db_obj)
|
||||||
|
|
||||||
if cisco_apic.EXTERNAL_CIDRS in res_dict:
|
if cisco_apic.EXTERNAL_CIDRS in res_dict:
|
||||||
|
@ -513,6 +517,15 @@ class ExtensionDbMixin(object):
|
||||||
|
|
||||||
return [c[0] for c in cidrs]
|
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):
|
def get_subnet_extn_db(self, session, subnet_id):
|
||||||
query = BAKERY(lambda s: s.query(
|
query = BAKERY(lambda s: s.query(
|
||||||
SubnetExtensionDb))
|
SubnetExtensionDb))
|
||||||
|
@ -590,6 +603,17 @@ class ExtensionDbMixin(object):
|
||||||
c_contracts.append(db_contract['contract_name'])
|
c_contracts.append(db_contract['contract_name'])
|
||||||
return attr_dict
|
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,
|
def _update_list_attr(self, session, db_model, column,
|
||||||
new_values, **filters):
|
new_values, **filters):
|
||||||
if new_values is None:
|
if new_values is None:
|
||||||
|
|
|
@ -137,6 +137,7 @@ class ApicExtensionDriver(api_plus.ExtensionDriver,
|
||||||
is_bgp_enabled = data.get(cisco_apic.BGP, False)
|
is_bgp_enabled = data.get(cisco_apic.BGP, False)
|
||||||
bgp_type = data.get(cisco_apic.BGP_TYPE, "default_export")
|
bgp_type = data.get(cisco_apic.BGP_TYPE, "default_export")
|
||||||
asn = data.get(cisco_apic.BGP_ASN, "0")
|
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)
|
self.validate_bgp_params(data)
|
||||||
res_dict = {cisco_apic.SVI: is_svi,
|
res_dict = {cisco_apic.SVI: is_svi,
|
||||||
cisco_apic.BGP: is_bgp_enabled,
|
cisco_apic.BGP: is_bgp_enabled,
|
||||||
|
@ -162,6 +163,7 @@ class ApicExtensionDriver(api_plus.ExtensionDriver,
|
||||||
data.get(cisco_apic.POLICY_ENFORCEMENT_PREF, "unenforced"),
|
data.get(cisco_apic.POLICY_ENFORCEMENT_PREF, "unenforced"),
|
||||||
cisco_apic.NO_NAT_CIDRS:
|
cisco_apic.NO_NAT_CIDRS:
|
||||||
data.get(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(
|
if cisco_apic.VLANS_LIST in (data.get(
|
||||||
cisco_apic.NESTED_DOMAIN_ALLOWED_VLANS) or {}):
|
cisco_apic.NESTED_DOMAIN_ALLOWED_VLANS) or {}):
|
||||||
|
@ -225,8 +227,9 @@ class ApicExtensionDriver(api_plus.ExtensionDriver,
|
||||||
cisco_apic.EXTRA_CONSUMED_CONTRACTS,
|
cisco_apic.EXTRA_CONSUMED_CONTRACTS,
|
||||||
cisco_apic.EPG_CONTRACT_MASTERS,
|
cisco_apic.EPG_CONTRACT_MASTERS,
|
||||||
cisco_apic.POLICY_ENFORCEMENT_PREF,
|
cisco_apic.POLICY_ENFORCEMENT_PREF,
|
||||||
cisco_apic.NO_NAT_CIDRS]
|
cisco_apic.NO_NAT_CIDRS,
|
||||||
if not(set(update_attrs) & set(data.keys())):
|
cisco_apic.MULTI_EXT_NETS]
|
||||||
|
if not (set(update_attrs) & set(data.keys())):
|
||||||
return
|
return
|
||||||
|
|
||||||
res_dict = {}
|
res_dict = {}
|
||||||
|
@ -246,7 +249,8 @@ class ApicExtensionDriver(api_plus.ExtensionDriver,
|
||||||
cisco_apic.EXTRA_CONSUMED_CONTRACTS,
|
cisco_apic.EXTRA_CONSUMED_CONTRACTS,
|
||||||
cisco_apic.EPG_CONTRACT_MASTERS,
|
cisco_apic.EPG_CONTRACT_MASTERS,
|
||||||
cisco_apic.POLICY_ENFORCEMENT_PREF,
|
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:
|
for e_k in ext_keys:
|
||||||
if e_k in data:
|
if e_k in data:
|
||||||
res_dict.update({e_k: data[e_k]})
|
res_dict.update({e_k: data[e_k]})
|
||||||
|
|
|
@ -809,6 +809,8 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
||||||
aim_ctx = aim_context.AimContext(session)
|
aim_ctx = aim_context.AimContext(session)
|
||||||
|
|
||||||
is_ext = self._is_external(current)
|
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)
|
is_svi = self._is_svi(current)
|
||||||
|
|
||||||
if ((current[cisco_apic.EXTRA_PROVIDED_CONTRACTS] or
|
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)
|
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,
|
||||||
ns.create_external_network(aim_ctx, ext_net)
|
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
|
# Get external CIDRs for all external networks that share
|
||||||
# this APIC external network.
|
# this APIC external network.
|
||||||
cidrs = sorted(
|
cidrs = sorted(
|
||||||
self.get_external_cidrs_by_ext_net_dn(
|
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)
|
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):
|
if isinstance(resource, aim_resource.BridgeDomain):
|
||||||
bd = resource
|
bd = resource
|
||||||
elif isinstance(resource, aim_resource.EndpointGroup):
|
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)
|
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
|
||||||
# REVISIT: lock_update=True is needed to handle races. Find
|
multi_ext_nets_enb = self._is_multi_ext_nets_enabled(current)
|
||||||
# alternative solutions since Neutron discourages using such
|
|
||||||
# queries.
|
if multi_ext_nets_enb:
|
||||||
other_nets = set(
|
cidrs_to_delete = self.get_external_cidrs_by_net_id(
|
||||||
self.get_network_ids_by_ext_net_dn(
|
session, current['id'])
|
||||||
session, ext_net.dn, lock_update=True))
|
ns.delete_external_network(aim_ctx, ext_net,
|
||||||
other_nets.discard(current['id'])
|
epg_name=current['id'],
|
||||||
if not other_nets:
|
cidrs=cidrs_to_delete)
|
||||||
ns.delete_external_network(aim_ctx, ext_net)
|
ns.delete_l3outside(aim_ctx, l3out, epg_name=current['id'],
|
||||||
other_nets = set(
|
cidrs=cidrs_to_delete)
|
||||||
self.get_network_ids_by_l3out_dn(
|
else:
|
||||||
session, l3out.dn, lock_update=True))
|
# REVISIT: lock_update=True is needed to handle races. Find
|
||||||
other_nets.discard(current['id'])
|
# alternative solutions since Neutron discourages using such
|
||||||
if not other_nets:
|
# queries.
|
||||||
ns.delete_l3outside(aim_ctx, l3out)
|
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):
|
elif self._is_svi(current):
|
||||||
l3out, ext_net, _ = self._get_aim_external_objects(current)
|
l3out, ext_net, _ = self._get_aim_external_objects(current)
|
||||||
aim_l3out = self.aim.get(aim_ctx, l3out)
|
aim_l3out = self.aim.get(aim_ctx, l3out)
|
||||||
|
@ -4768,6 +4793,9 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
||||||
def _is_bgp_enabled(self, network):
|
def _is_bgp_enabled(self, network):
|
||||||
return network.get(cisco_apic.BGP)
|
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):
|
def _nat_type_to_strategy(self, nat_type):
|
||||||
ns_cls = nat_strategy.DistributedNatStrategy
|
ns_cls = nat_strategy.DistributedNatStrategy
|
||||||
if nat_type == '':
|
if nat_type == '':
|
||||||
|
@ -7130,6 +7158,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
||||||
cisco_apic.NESTED_DOMAIN_INFRA_VLAN: None,
|
cisco_apic.NESTED_DOMAIN_INFRA_VLAN: None,
|
||||||
cisco_apic.NESTED_DOMAIN_SERVICE_VLAN: None,
|
cisco_apic.NESTED_DOMAIN_SERVICE_VLAN: None,
|
||||||
cisco_apic.NESTED_DOMAIN_NODE_NETWORK_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):
|
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]})
|
res_dict.update({cisco_apic.BD: net_db.aim_mapping[cisco_apic.BD]})
|
||||||
|
|
|
@ -359,7 +359,8 @@ class ApicAimTestCase(test_address_scope.AddressScopeTestCase,
|
||||||
ACTIVE_ACTIVE_AAP, EPG_SUBNET,
|
ACTIVE_ACTIVE_AAP, EPG_SUBNET,
|
||||||
CIDR, PROV, CONS, SVI,
|
CIDR, PROV, CONS, SVI,
|
||||||
BGP, BGP_TYPE, ASN,
|
BGP, BGP_TYPE, ASN,
|
||||||
'provider:network_type'
|
'provider:network_type',
|
||||||
|
'apic:multi_ext_nets'
|
||||||
)
|
)
|
||||||
self.name_mapper = apic_mapper.APICNameMapper()
|
self.name_mapper = apic_mapper.APICNameMapper()
|
||||||
self.t1_aname = self.name_mapper.project(None, 't1')
|
self.t1_aname = self.name_mapper.project(None, 't1')
|
||||||
|
@ -449,7 +450,8 @@ class ApicAimTestCase(test_address_scope.AddressScopeTestCase,
|
||||||
self.fmt)
|
self.fmt)
|
||||||
return self.deserialize(self.fmt, req.get_response(self.api))
|
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}
|
kwargs = {'router:external': True}
|
||||||
if dn:
|
if dn:
|
||||||
kwargs[DN] = {'ExternalNetwork': dn}
|
kwargs[DN] = {'ExternalNetwork': dn}
|
||||||
|
@ -459,6 +461,8 @@ class ApicAimTestCase(test_address_scope.AddressScopeTestCase,
|
||||||
kwargs['apic:nat_type'] = self.nat_type
|
kwargs['apic:nat_type'] = self.nat_type
|
||||||
if cidrs:
|
if cidrs:
|
||||||
kwargs[CIDR] = cidrs
|
kwargs[CIDR] = cidrs
|
||||||
|
if multi_ext_nets:
|
||||||
|
kwargs['apic:multi_ext_nets'] = True
|
||||||
|
|
||||||
return self._make_network(self.fmt, name, True,
|
return self._make_network(self.fmt, name, True,
|
||||||
arg_list=self.extension_attributes,
|
arg_list=self.extension_attributes,
|
||||||
|
@ -4114,8 +4118,10 @@ class TestSyncState(ApicAimTestCase):
|
||||||
TestSyncState._mocked_get_statuses):
|
TestSyncState._mocked_get_statuses):
|
||||||
self._test_router('error')
|
self._test_router('error')
|
||||||
|
|
||||||
def _test_external_network(self, expected_state, dn=None, msg=None):
|
def _test_external_network(self, expected_state, dn=None, msg=None,
|
||||||
net = self._make_ext_network('net1', dn=dn)
|
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'],
|
self.assertEqual(expected_state, net['apic:synchronization_state'],
|
||||||
msg)
|
msg)
|
||||||
net = self._show('networks', net['id'])['network']
|
net = self._show('networks', net['id'])['network']
|
||||||
|
@ -4156,6 +4162,38 @@ class TestSyncState(ApicAimTestCase):
|
||||||
dn=self.dn_t1_l1_n1,
|
dn=self.dn_t1_l1_n1,
|
||||||
msg='%s' % a_res)
|
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):
|
def test_unmanaged_external_network(self):
|
||||||
self._test_external_network('build')
|
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, net1['id']))
|
||||||
self.assertFalse(extn.get_network_extn_db(session, net2['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):
|
def test_external_network_fail(self):
|
||||||
# APIC DN not specified
|
# APIC DN not specified
|
||||||
resp = self._create_network(self.fmt, 'net1', True,
|
resp = self._create_network(self.fmt, 'net1', True,
|
||||||
|
@ -8033,11 +8159,11 @@ class TestExternalConnectivityBase(object):
|
||||||
self.mock_ns.create_l3outside.assert_called_once_with(
|
self.mock_ns.create_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'),
|
||||||
vmm_domains=[])
|
vmm_domains=[], epg_name=None)
|
||||||
a_ext_net = aim_resource.ExternalNetwork(
|
a_ext_net = aim_resource.ExternalNetwork(
|
||||||
tenant_name=self.t1_aname, l3out_name='l1', name='n1')
|
tenant_name=self.t1_aname, l3out_name='l1', name='n1')
|
||||||
self.mock_ns.create_external_network.assert_called_once_with(
|
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(
|
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'])
|
mock.ANY, a_ext_net, ['20.10.0.0/16', '4.4.4.0/24'])
|
||||||
ext_epg = aim_resource.EndpointGroup(
|
ext_epg = aim_resource.EndpointGroup(
|
||||||
|
@ -8086,7 +8212,7 @@ class TestExternalConnectivityBase(object):
|
||||||
self._make_ext_network('net2',
|
self._make_ext_network('net2',
|
||||||
dn=self.dn_t1_l1_n1)
|
dn=self.dn_t1_l1_n1)
|
||||||
self.mock_ns.create_external_network.assert_called_once_with(
|
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(
|
self.mock_ns.update_external_cidrs.assert_called_once_with(
|
||||||
mock.ANY, a_ext_net, ['0.0.0.0/0'])
|
mock.ANY, a_ext_net, ['0.0.0.0/0'])
|
||||||
self._validate()
|
self._validate()
|
||||||
|
@ -9161,11 +9287,11 @@ class TestExternalConnectivityBase(object):
|
||||||
self.mock_ns.create_l3outside.assert_called_once_with(
|
self.mock_ns.create_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'),
|
||||||
vmm_domains=vmm_domains)
|
vmm_domains=vmm_domains, epg_name=None)
|
||||||
a_ext_net = aim_resource.ExternalNetwork(
|
a_ext_net = aim_resource.ExternalNetwork(
|
||||||
tenant_name=self.t1_aname, l3out_name='l1', name='n1')
|
tenant_name=self.t1_aname, l3out_name='l1', name='n1')
|
||||||
self.mock_ns.create_external_network.assert_called_once_with(
|
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(
|
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'])
|
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,
|
ext_epg = self.aim_mgr.find(aim_ctx, aim_resource.EndpointGroup,
|
||||||
|
@ -9214,17 +9340,17 @@ class TestExternalConnectivityBase(object):
|
||||||
a_ext_net = aim_resource.ExternalNetwork(
|
a_ext_net = aim_resource.ExternalNetwork(
|
||||||
tenant_name=self.t1_aname, l3out_name='l1', name='n1')
|
tenant_name=self.t1_aname, l3out_name='l1', name='n1')
|
||||||
self.mock_ns.create_l3outside.assert_called_once_with(
|
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(
|
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
|
# create second network that shares APIC l3out and external-network
|
||||||
self.mock_ns.reset_mock()
|
self.mock_ns.reset_mock()
|
||||||
net2 = self._make_ext_network('net2', dn=self.dn_t1_l1_n1)
|
net2 = self._make_ext_network('net2', dn=self.dn_t1_l1_n1)
|
||||||
self.mock_ns.create_l3outside.assert_called_once_with(
|
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(
|
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
|
# create third network that shares APIC l3out only
|
||||||
self.mock_ns.reset_mock()
|
self.mock_ns.reset_mock()
|
||||||
|
@ -9233,9 +9359,9 @@ class TestExternalConnectivityBase(object):
|
||||||
a_ext_net3 = aim_resource.ExternalNetwork(
|
a_ext_net3 = aim_resource.ExternalNetwork(
|
||||||
tenant_name=self.t1_aname, l3out_name='l1', name='n2')
|
tenant_name=self.t1_aname, l3out_name='l1', name='n2')
|
||||||
self.mock_ns.create_l3outside.assert_called_once_with(
|
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(
|
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
|
# delete net2
|
||||||
self.mock_ns.reset_mock()
|
self.mock_ns.reset_mock()
|
||||||
|
|
Loading…
Reference in New Issue