Allocate APIC router ID based on configuration
Also change the API name based on the review comments from SVI patch. Change-Id: Iea0424a419cd3ccb5561f19c93973ff2f211c3e0
This commit is contained in:
@@ -45,6 +45,9 @@ apic_opts = [
|
||||
cfg.StrOpt('l3_domain_dn', default='',
|
||||
help=("The DN of the APIC external routed domain used by the "
|
||||
"auto l3out created for the SVI networks.")),
|
||||
cfg.StrOpt('apic_router_id_pool', default='199.199.199.1/24',
|
||||
help=("The pool of IPs where we allocate the APIC "
|
||||
"router ID from while creating the SVI interface.")),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -67,3 +67,8 @@ class OnlyOneSubnetInSVINetwork(exceptions.BadRequest):
|
||||
class ExternalSubnetOverlapInL3Out(exceptions.BadRequest):
|
||||
message = _("External subnet CIDR %(cidr)s overlaps with existing "
|
||||
"subnets in APIC L3Outside %(l3out)s.")
|
||||
|
||||
|
||||
class ExhaustedApicRouterIdPool(exceptions.IpAddressGenerationFailure):
|
||||
message = _("All the IPs in the APIC router ID pool %(pool)s "
|
||||
"have been taken.")
|
||||
|
||||
@@ -109,8 +109,6 @@ ACI_PORT_DESCR_FORMATS = ('topology/pod-(\d+)/paths-(\d+)/pathep-'
|
||||
'\[eth(\d+)/(\d+(\/\d+)*)\]')
|
||||
ACI_VPCPORT_DESCR_FORMAT = ('topology/pod-(\d+)/protpaths-(\d+)-(\d+)/pathep-'
|
||||
'\[(.*)\]')
|
||||
# TODO(kentwu): A pool of router IDs has to be put in the config file instead
|
||||
APIC_ROUTER_IDS = ['199.199.199.198', '199.199.199.199']
|
||||
|
||||
|
||||
class KeystoneNotificationEndpoint(object):
|
||||
@@ -228,6 +226,34 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
||||
trunk_driver.register()
|
||||
self.port_desc_re = re.compile(ACI_PORT_DESCR_FORMATS)
|
||||
self.vpcport_desc_re = re.compile(ACI_VPCPORT_DESCR_FORMAT)
|
||||
self.apic_router_id_pool = cfg.CONF.ml2_apic_aim.apic_router_id_pool
|
||||
self.apic_router_id_subnet = netaddr.IPSet([self.apic_router_id_pool])
|
||||
|
||||
def _query_used_apic_router_ids(self, aim_ctx):
|
||||
used_ids = netaddr.IPSet()
|
||||
# Find the l3out_nodes created by us
|
||||
aim_l3out_nodes = self.aim.find(
|
||||
aim_ctx, aim_resource.L3OutNode,
|
||||
node_profile_name=L3OUT_NODE_PROFILE_NAME,
|
||||
monitored=False)
|
||||
for aim_l3out_node in aim_l3out_nodes:
|
||||
used_ids.add(aim_l3out_node.router_id)
|
||||
return used_ids
|
||||
|
||||
def _allocate_apic_router_ids(self, aim_ctx, node_path):
|
||||
aim_l3out_nodes = self.aim.find(
|
||||
aim_ctx, aim_resource.L3OutNode,
|
||||
node_profile_name=L3OUT_NODE_PROFILE_NAME,
|
||||
node_path=node_path)
|
||||
for aim_l3out_node in aim_l3out_nodes:
|
||||
if aim_l3out_node.router_id:
|
||||
return aim_l3out_node.router_id
|
||||
used_ids = self._query_used_apic_router_ids(aim_ctx)
|
||||
available_ids = self.apic_router_id_subnet - used_ids
|
||||
for ip_address in available_ids:
|
||||
return str(ip_address)
|
||||
raise exceptions.ExhaustedApicRouterIdPool(
|
||||
pool=self.apic_router_id_pool)
|
||||
|
||||
def _ensure_static_resources(self):
|
||||
session = db_api.get_session()
|
||||
@@ -470,7 +496,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
||||
elif isinstance(resource, aim_resource.VRF):
|
||||
vrf = resource
|
||||
elif self._is_svi(current):
|
||||
_, ext_net, _ = self._get_aim_external_stuff(current)
|
||||
_, ext_net, _ = self._get_aim_external_objects(current)
|
||||
# This means no DN is being provided. Then we should try to create
|
||||
# the l3out automatically
|
||||
if not ext_net:
|
||||
@@ -719,7 +745,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
||||
if not other_nets:
|
||||
ns.delete_l3outside(aim_ctx, l3out)
|
||||
elif self._is_svi(current):
|
||||
l3out, ext_net, _ = self._get_aim_external_stuff(current)
|
||||
l3out, ext_net, _ = self._get_aim_external_objects(current)
|
||||
aim_l3out = self.aim.get(aim_ctx, l3out)
|
||||
if not aim_l3out:
|
||||
return
|
||||
@@ -770,8 +796,8 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
||||
|
||||
# SVI network with pre-existing l3out.
|
||||
if self._is_preexisting_svi_db(network_db):
|
||||
_, ext_net, _ = self._get_aim_external_stuff_db(session,
|
||||
network_db)
|
||||
_, ext_net, _ = self._get_aim_external_objects_db(session,
|
||||
network_db)
|
||||
if ext_net:
|
||||
sync_state = self._merge_status(aim_ctx, sync_state, ext_net)
|
||||
|
||||
@@ -2804,7 +2830,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
||||
ns.common_scope = self.apic_system_id
|
||||
return ns
|
||||
|
||||
def _get_aim_external_stuff(self, network):
|
||||
def _get_aim_external_objects(self, network):
|
||||
ext_net_dn = (network.get(cisco_apic.DIST_NAMES, {})
|
||||
.get(cisco_apic.EXTERNAL_NETWORK))
|
||||
if not ext_net_dn:
|
||||
@@ -2818,9 +2844,9 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
||||
def _get_aim_nat_strategy(self, network):
|
||||
if not self._is_external(network):
|
||||
return None, None, None
|
||||
return self._get_aim_external_stuff(network)
|
||||
return self._get_aim_external_objects(network)
|
||||
|
||||
def _get_aim_external_stuff_db(self, session, network_db):
|
||||
def _get_aim_external_objects_db(self, session, network_db):
|
||||
extn_db = extension_db.ExtensionDbMixin()
|
||||
extn_info = extn_db.get_network_extn_db(session, network_db.id)
|
||||
if extn_info and cisco_apic.EXTERNAL_NETWORK in extn_info:
|
||||
@@ -2836,7 +2862,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
||||
|
||||
def _get_aim_nat_strategy_db(self, session, network_db):
|
||||
if network_db.external is not None:
|
||||
return self._get_aim_external_stuff_db(session, network_db)
|
||||
return self._get_aim_external_objects_db(session, network_db)
|
||||
return None, None, None
|
||||
|
||||
def _subnet_to_gw_ip_mask(self, subnet):
|
||||
@@ -3195,7 +3221,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
||||
return
|
||||
|
||||
aim_ctx = aim_context.AimContext(db_session=session)
|
||||
l3out, ext_net, _ = self._get_aim_external_stuff(network)
|
||||
l3out, ext_net, _ = self._get_aim_external_objects(network)
|
||||
if new_path:
|
||||
aim_l3out_np = aim_resource.L3OutNodeProfile(
|
||||
tenant_name=l3out.tenant_name, l3out_name=l3out.name,
|
||||
@@ -3203,12 +3229,13 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
||||
aim_l3out_np_db = self.aim.get(aim_ctx, aim_l3out_np)
|
||||
if not aim_l3out_np_db:
|
||||
self.aim.create(aim_ctx, aim_l3out_np)
|
||||
|
||||
for idx, node_path in enumerate(node_paths):
|
||||
for node_path in node_paths:
|
||||
apic_router_id = self._allocate_apic_router_ids(aim_ctx,
|
||||
node_path)
|
||||
aim_l3out_node = aim_resource.L3OutNode(
|
||||
tenant_name=l3out.tenant_name, l3out_name=l3out.name,
|
||||
node_profile_name=L3OUT_NODE_PROFILE_NAME,
|
||||
node_path=node_path, router_id=APIC_ROUTER_IDS[idx],
|
||||
node_path=node_path, router_id=apic_router_id,
|
||||
router_id_loopback=False)
|
||||
aim_l3out_node_db = self.aim.get(aim_ctx, aim_l3out_node)
|
||||
if not aim_l3out_node_db:
|
||||
|
||||
@@ -6049,6 +6049,14 @@ class TestPortVlanNetwork(ApicAimTestCase):
|
||||
else:
|
||||
ext_net = aim_resource.ExternalNetwork.from_dn(
|
||||
net1[DN]['ExternalNetwork'])
|
||||
l3out_node = aim_resource.L3OutNode(
|
||||
tenant_name=ext_net.tenant_name,
|
||||
l3out_name=ext_net.l3out_name,
|
||||
node_profile_name=md.L3OUT_NODE_PROFILE_NAME,
|
||||
node_path='topology/pod-1/node-102')
|
||||
l3out_node = self.aim_mgr.get(aim_ctx, l3out_node)
|
||||
apic_router_id = l3out_node.router_id
|
||||
|
||||
l3out_if = aim_resource.L3OutInterface(
|
||||
tenant_name=ext_net.tenant_name,
|
||||
l3out_name=ext_net.l3out_name,
|
||||
@@ -6084,6 +6092,8 @@ class TestPortVlanNetwork(ApicAimTestCase):
|
||||
'encap': 'vlan-%s' % vlan_p2}],
|
||||
epg.static_paths)
|
||||
else:
|
||||
l3out_node = self.aim_mgr.get(aim_ctx, l3out_node)
|
||||
self.assertEqual(l3out_node.router_id, apic_router_id)
|
||||
l3out_if = self.aim_mgr.get(aim_ctx, l3out_if)
|
||||
self.assertEqual(l3out_if.encap, 'vlan-%s' % vlan_p1)
|
||||
self.assertEqual(l3out_if.secondary_addr_a_list,
|
||||
@@ -6162,6 +6172,13 @@ class TestPortVlanNetwork(ApicAimTestCase):
|
||||
else:
|
||||
ext_net = aim_resource.ExternalNetwork.from_dn(
|
||||
net1[DN]['ExternalNetwork'])
|
||||
l3out_node1 = aim_resource.L3OutNode(
|
||||
tenant_name=ext_net.tenant_name,
|
||||
l3out_name=ext_net.l3out_name,
|
||||
node_profile_name=md.L3OUT_NODE_PROFILE_NAME,
|
||||
node_path='topology/pod-1/node-102')
|
||||
l3out_node1 = self.aim_mgr.get(aim_ctx, l3out_node1)
|
||||
|
||||
l3out_if1 = aim_resource.L3OutInterface(
|
||||
tenant_name=ext_net.tenant_name,
|
||||
l3out_name=ext_net.l3out_name,
|
||||
@@ -6205,6 +6222,13 @@ class TestPortVlanNetwork(ApicAimTestCase):
|
||||
else:
|
||||
ext_net = aim_resource.ExternalNetwork.from_dn(
|
||||
net2[DN]['ExternalNetwork'])
|
||||
l3out_node2 = aim_resource.L3OutNode(
|
||||
tenant_name=ext_net.tenant_name,
|
||||
l3out_name=ext_net.l3out_name,
|
||||
node_profile_name=md.L3OUT_NODE_PROFILE_NAME,
|
||||
node_path='topology/pod-1/node-102')
|
||||
l3out_node2 = self.aim_mgr.get(aim_ctx, l3out_node2)
|
||||
self.assertEqual(l3out_node1.router_id, l3out_node2.router_id)
|
||||
l3out_if2 = aim_resource.L3OutInterface(
|
||||
tenant_name=ext_net.tenant_name,
|
||||
l3out_name=ext_net.l3out_name,
|
||||
@@ -6367,6 +6391,12 @@ class TestPortVlanNetwork(ApicAimTestCase):
|
||||
else:
|
||||
ext_net = aim_resource.ExternalNetwork.from_dn(
|
||||
net1[DN]['ExternalNetwork'])
|
||||
l3out_node1a = aim_resource.L3OutNode(
|
||||
tenant_name=ext_net.tenant_name,
|
||||
l3out_name=ext_net.l3out_name,
|
||||
node_profile_name=md.L3OUT_NODE_PROFILE_NAME,
|
||||
node_path='topology/pod-1/node-102')
|
||||
l3out_node1a = self.aim_mgr.get(aim_ctx, l3out_node1a)
|
||||
l3out_if1a = aim_resource.L3OutInterface(
|
||||
tenant_name=ext_net.tenant_name,
|
||||
l3out_name=ext_net.l3out_name,
|
||||
@@ -6377,6 +6407,15 @@ class TestPortVlanNetwork(ApicAimTestCase):
|
||||
self.assertEqual(l3out_if1a.encap, 'vlan-%s' % vlan_p1)
|
||||
self.assertEqual(l3out_if1a.secondary_addr_a_list,
|
||||
[{'addr': '10.0.0.1/24'}])
|
||||
|
||||
l3out_node1b = aim_resource.L3OutNode(
|
||||
tenant_name=ext_net.tenant_name,
|
||||
l3out_name=ext_net.l3out_name,
|
||||
node_profile_name=md.L3OUT_NODE_PROFILE_NAME,
|
||||
node_path='topology/pod-1/node-102')
|
||||
l3out_node1b = self.aim_mgr.get(aim_ctx, l3out_node1b)
|
||||
self.assertEqual(l3out_node1a.router_id,
|
||||
l3out_node1b.router_id)
|
||||
l3out_if1b = aim_resource.L3OutInterface(
|
||||
tenant_name=ext_net.tenant_name,
|
||||
l3out_name=ext_net.l3out_name,
|
||||
@@ -6590,6 +6629,12 @@ class TestPortVlanNetwork(ApicAimTestCase):
|
||||
else:
|
||||
ext_net = aim_resource.ExternalNetwork.from_dn(
|
||||
net1[DN]['ExternalNetwork'])
|
||||
l3out_node1a = aim_resource.L3OutNode(
|
||||
tenant_name=ext_net.tenant_name,
|
||||
l3out_name=ext_net.l3out_name,
|
||||
node_profile_name=md.L3OUT_NODE_PROFILE_NAME,
|
||||
node_path='topology/pod-1/node-102')
|
||||
l3out_node1a = self.aim_mgr.get(aim_ctx, l3out_node1a)
|
||||
l3out_if1a = aim_resource.L3OutInterface(
|
||||
tenant_name=ext_net.tenant_name,
|
||||
l3out_name=ext_net.l3out_name,
|
||||
@@ -6600,6 +6645,15 @@ class TestPortVlanNetwork(ApicAimTestCase):
|
||||
self.assertEqual(l3out_if1a.encap, 'vlan-%s' % vlan_p1)
|
||||
self.assertEqual(l3out_if1a.secondary_addr_a_list,
|
||||
[{'addr': '10.0.0.1/24'}])
|
||||
|
||||
l3out_node1b = aim_resource.L3OutNode(
|
||||
tenant_name=ext_net.tenant_name,
|
||||
l3out_name=ext_net.l3out_name,
|
||||
node_profile_name=md.L3OUT_NODE_PROFILE_NAME,
|
||||
node_path='topology/pod-1/node-201')
|
||||
l3out_node1b = self.aim_mgr.get(aim_ctx, l3out_node1b)
|
||||
self.assertNotEqual(l3out_node1a.router_id,
|
||||
l3out_node1b.router_id)
|
||||
l3out_if1b = aim_resource.L3OutInterface(
|
||||
tenant_name=ext_net.tenant_name,
|
||||
l3out_name=ext_net.l3out_name,
|
||||
@@ -6610,6 +6664,8 @@ class TestPortVlanNetwork(ApicAimTestCase):
|
||||
self.assertEqual(l3out_if1b.encap, 'vlan-%s' % vlan_p1)
|
||||
self.assertEqual(l3out_if1b.secondary_addr_a_list,
|
||||
[{'addr': '10.0.0.1/24'}])
|
||||
self.assertNotEqual(l3out_if1a.primary_addr_a,
|
||||
l3out_if1b.primary_addr_a)
|
||||
if bgp_enabled:
|
||||
primary = netaddr.IPNetwork(l3out_if1a.primary_addr_a)
|
||||
subnet = str(primary.cidr)
|
||||
@@ -6673,6 +6729,29 @@ class TestPortVlanNetwork(ApicAimTestCase):
|
||||
self._test_network_on_multiple_hosts(is_svi=True)
|
||||
self._test_network_on_multiple_hosts(is_svi=True, bgp_enabled=True)
|
||||
|
||||
def test_svi_exhausted_router_id_pool(self):
|
||||
self.driver.apic_router_id_pool = '199.199.199.1'
|
||||
self.driver.apic_router_id_subnet = netaddr.IPSet(
|
||||
[self.driver.apic_router_id_pool])
|
||||
aim_ctx = aim_context.AimContext(self.db_session)
|
||||
net1 = self._make_network(self.fmt, 'net1', True,
|
||||
arg_list=self.extension_attributes,
|
||||
**{'apic:svi': 'True'})['network']
|
||||
hlink2 = aim_infra.HostLink(
|
||||
host_name='h2',
|
||||
interface_name='eth0',
|
||||
path='topology/pod-1/paths-201/pathep-[eth1/19]')
|
||||
self.aim_mgr.create(aim_ctx, hlink2)
|
||||
self._register_agent('h2', AGENT_CONF_OVS)
|
||||
|
||||
with self.subnet(network={'network': net1}) as sub1:
|
||||
with self.port(subnet=sub1) as p1:
|
||||
p1 = self._bind_port_to_host(p1['port']['id'], 'h1')
|
||||
with self.port(subnet=sub1) as p2:
|
||||
result = self._bind_port_to_host(p2['port']['id'], 'h2')
|
||||
self.assertEqual('ExhaustedApicRouterIdPool',
|
||||
result['NeutronError']['type'])
|
||||
|
||||
def test_port_binding_missing_hostlink(self):
|
||||
aim_ctx = aim_context.AimContext(self.db_session)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user