[apic_aim] Validate routing topology

Raise an exception if adding an interface to a router would result
in unintended routing due to EPGs being at network rather than subnet
granularity.

Change-Id: Ic47a30e2530a2ebd2268a6625297c9b6d4debf52
(cherry picked from commit f516fe8612)
This commit is contained in:
Robert Kukura
2016-10-25 15:17:31 -04:00
parent cbf8064ed5
commit 1cc18c7689
2 changed files with 135 additions and 13 deletions

View File

@@ -22,6 +22,7 @@ from aim import utils as aim_utils
from neutron._i18n import _LI
from neutron._i18n import _LW
from neutron.common import constants as n_constants
from neutron.common import exceptions
from neutron.db import address_scope_db
from neutron.db import api as db_api
from neutron.db import l3_db
@@ -59,6 +60,11 @@ PROMISCUOUS_TYPES = [n_constants.DEVICE_OWNER_DHCP,
n_constants.DEVICE_OWNER_LOADBALANCER]
class UnsupportedRoutingTopology(exceptions.BadRequest):
message = _("All router interfaces for a network must share either the "
"same router or the same subnet.")
class ApicMechanismDriver(api_plus.MechanismDriver):
# TODO(rkukura): Derivations of tenant_aname throughout need to
# take sharing into account.
@@ -837,20 +843,42 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
aim_epg = self.aim.update(aim_ctx, aim_epg,
provided_contract_names=contracts)
# Find routers with interfaces to this network. The current
# interface is not included, because the RouterPort has not
# yet been added to the DB session.
router_ids = [r[0] for r in
session.query(l3_db.RouterPort.router_id).
join(models_v2.Port).
filter(models_v2.Port.network_id == network_id,
l3_db.RouterPort.port_type ==
n_constants.DEVICE_OWNER_ROUTER_INTF).distinct()]
# Find up to two existing router interfaces for this
# network. The interface currently being added is not
# included, because the RouterPort has not yet been added to
# the DB session.
intfs = (session.query(l3_db.RouterPort.router_id,
models_v2.IPAllocation.subnet_id).
join(models_v2.Port).
join(models_v2.IPAllocation).
filter(models_v2.Port.network_id == network_id,
l3_db.RouterPort.port_type ==
n_constants.DEVICE_OWNER_ROUTER_INTF).
limit(2).
all())
if intfs:
# Since the EPGs that provide/consume routers' contracts
# are at network rather than subnet granularity,
# topologies where different subnets on the same network
# are interfaced to different routers, which are valid in
# Neutron, would result in unintended routing. We
# therefore require that all router interfaces for a
# network share either the same router or the same subnet.
# TODO(rkukura): Validate topology.
if not router_ids:
# Enable routing for BD and set VRF.
different_router = False
different_subnet = False
subnet_ids = [subnet['id'] for subnet in subnets]
for existing_router_id, existing_subnet_id in intfs:
if router_id != existing_router_id:
different_router = True
for subnet_id in subnet_ids:
if subnet_id != existing_subnet_id:
different_subnet = True
if different_router and different_subnet:
raise UnsupportedRoutingTopology()
else:
# No existing interfaces, so enable routing for BD and set
# its VRF.
subnetpool_id = subnets[0]['subnetpool_id']
if subnetpool_id: