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:
Kent Wu
2018-02-19 20:35:07 -08:00
parent b5552abdba
commit 4b3db55433
4 changed files with 128 additions and 14 deletions

View File

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

View File

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

View File

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

View File

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