[AIM] Enhance gbp-validate to detect routed subnet overlap

The gbp-validate tool will now fail validation if multiple routed
subnets that map to the same VRF overlap. Note that, unlike the
similar rejection of attempts to add or remove interfaces that result
in overlap, this is not subjet to the allow_routed_vrf_subnet_overlap
config variable, and applies to scoped as well as unscoped subnets.

Change-Id: I01346b94a491e6866bcf9581fe53028889d244ba
This commit is contained in:
Robert Kukura 2019-07-29 14:11:13 -04:00
parent c4bb97aa29
commit edb8138276
2 changed files with 117 additions and 1 deletions

View File

@ -4653,7 +4653,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
self._validate_floatingips(mgr)
self._validate_port_bindings(mgr)
# Note: The queries bellow are executed only once per run of the
# Note: The queries below are executed only once per run of the
# validation CLI tool, but are baked in order to speed up unit
# test execution, where they are called repeatedly.
@ -4832,6 +4832,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
routed_nets = self._get_router_interface_info(mgr)
network_vrfs, router_vrfs = self._determine_vrfs(
mgr, net_dbs, routed_nets)
self._validate_routed_vrfs(mgr, routed_nets, network_vrfs)
for net_db in net_dbs.values():
if not net_db.aim_extension_mapping:
@ -5052,6 +5053,26 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
return network_vrfs, router_vrfs
def _validate_routed_vrfs(self, mgr, routed_nets, network_vrfs):
vrf_subnets = defaultdict(list)
for net_id, intfs in routed_nets.items():
vrf = network_vrfs[net_id]
vrf_subnets[tuple(vrf.identity)] += [
(intf.subnet.id, netaddr.IPNetwork(intf.subnet.cidr))
for intf in intfs]
for vrf_id, subnets in vrf_subnets.items():
subnets.sort(key=lambda s: s[1])
for (id1, cidr1), (id2, cidr2) in zip(subnets[:-1], subnets[1:]):
if id2 != id1 and cidr2 in cidr1:
vrf = aim_resource.VRF(
tenant_name=vrf_id[0], name=vrf_id[1])
mgr.validation_failed(
"overlapping routed subnets %(id1)s (%(cidr1)s) "
"and %(id2)s (%(cidr2)s) mapped to %(vrf)s" %
{'id1': id1, 'cidr1': cidr1,
'id2': id2, 'cidr2': cidr2,
'vrf': vrf})
def _missing_network_extension_mapping(self, mgr, net_db):
# Note that this is intended primarily to handle migration to
# apic_aim, where the previous plugin and/or drivers did not

View File

@ -817,6 +817,101 @@ class TestNeutronMapping(AimValidationTestCase):
self._test_routed_subnet(subnet1_id, '10.0.1.1')
self._test_unscoped_vrf(router_id)
def test_subnet_overlap(self):
# Create two routers.
router1_id = self._make_router(
self.fmt, self._tenant_id, 'router1')['router']['id']
router2_id = self._make_router(
self.fmt, self._tenant_id, 'router2')['router']['id']
# Create a network with four unscoped subnets.
net1_resp = self._make_network(self.fmt, 'net1', True)
subnet1a_id = self._make_subnet(
self.fmt, net1_resp, '10.1.1.1', '10.1.1.0/24')['subnet']['id']
subnet1b_id = self._make_subnet(
self.fmt, net1_resp, '10.2.1.1', '10.2.1.0/24')['subnet']['id']
subnet1c_id = self._make_subnet(
self.fmt, net1_resp, '10.3.1.1', '10.3.1.0/24')['subnet']['id']
subnet1d_id = self._make_subnet(
self.fmt, net1_resp, '10.4.1.1', '10.4.1.0/24')['subnet']['id']
# Add all four net1 subnets to router1.
self.l3_plugin.add_router_interface(
n_context.get_admin_context(), router1_id,
{'subnet_id': subnet1a_id})
self.l3_plugin.add_router_interface(
n_context.get_admin_context(), router1_id,
{'subnet_id': subnet1b_id})
self.l3_plugin.add_router_interface(
n_context.get_admin_context(), router1_id,
{'subnet_id': subnet1c_id})
self.l3_plugin.add_router_interface(
n_context.get_admin_context(), router1_id,
{'subnet_id': subnet1d_id})
# Create another network with four unscoped subnets: one that
# doesn't overlap any net1 subnet, one that overlaps subnet1b
# exactly, one contained within subnet1c, and one containing
# subnet1d.
net2_resp = self._make_network(self.fmt, 'net2', True)
subnet2a_id = self._make_subnet(
self.fmt, net2_resp, '10.1.2.2', '10.1.2.0/24')['subnet']['id']
subnet2b_id = self._make_subnet(
self.fmt, net2_resp, '10.2.1.2', '10.2.1.0/24')['subnet']['id']
subnet2c_id = self._make_subnet(
self.fmt, net2_resp, '10.3.1.2', '10.3.1.0/25')['subnet']['id']
subnet2d_id = self._make_subnet(
self.fmt, net2_resp, '10.4.1.2', '10.4.1.0/23')['subnet']['id']
# Add the non-overlapping net2 subnet to router2 and test that
# validation passes.
self.l3_plugin.add_router_interface(
n_context.get_admin_context(), router2_id,
{'subnet_id': subnet2a_id})
self._validate()
# Disabled overlap rejection so that we can add router
# interfaces below that result in overlapping AIM Subnets
# within a routed VRF. Note that this does not prevent overlap
# detection when validating.
self.driver.aim_mech_driver.allow_routed_vrf_subnet_overlap = True
# Add the net2 subnet that overlaps exactly to router2, test
# that validation fails, remove the subnet, and test the
# validation again passes.
self.l3_plugin.add_router_interface(
n_context.get_admin_context(), router2_id,
{'subnet_id': subnet2b_id})
self._validate_unrepairable()
self.l3_plugin.remove_router_interface(
n_context.get_admin_context(), router2_id,
{'subnet_id': subnet2b_id})
self._validate()
# Add the net2 subnet contained within a net1 subnet to
# router2, test that validation fails, remove the subnet, and
# test the validation again passes.
self.l3_plugin.add_router_interface(
n_context.get_admin_context(), router2_id,
{'subnet_id': subnet2c_id})
self._validate_unrepairable()
self.l3_plugin.remove_router_interface(
n_context.get_admin_context(), router2_id,
{'subnet_id': subnet2c_id})
self._validate()
# Add the net2 subnet containing a net1 subnet to router2,
# test that validation fails, remove the subnet, and test the
# validation again passes.
self.l3_plugin.add_router_interface(
n_context.get_admin_context(), router2_id,
{'subnet_id': subnet2d_id})
self._validate_unrepairable()
self.l3_plugin.remove_router_interface(
n_context.get_admin_context(), router2_id,
{'subnet_id': subnet2d_id})
self._validate()
def test_security_group(self):
# Create security group with a rule.
sg = self._make_security_group(