Merge "[apic_aim] Complete mapping to AIM Subnet"

This commit is contained in:
Jenkins 2016-10-18 18:53:56 +00:00 committed by Gerrit Code Review
commit 0c8d2583cb
2 changed files with 175 additions and 69 deletions

View File

@ -28,6 +28,7 @@ from neutron.db import l3_db
from neutron.db import models_v2
from neutron.extensions import portbindings
from neutron import manager
from neutron.plugins.common import constants as pconst
from neutron.plugins.ml2 import driver_api as api
from opflexagent import constants as ofcst
from oslo_log import log
@ -72,6 +73,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
self.name_mapper = apic_mapper.APICNameMapper(self.db, log)
self.aim = aim_manager.AimManager()
self._core_plugin = None
self._l3_plugin = None
self.aim_cfg_mgr = aim_cfg.ConfigManager(
aim_context.AimContext(db_api.get_session()),
host=aim_cfg.CONF.host)
@ -334,17 +336,18 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
LOG.info(_LI("Mapped network_id %(id)s to %(aname)s"),
{'id': network_id, 'aname': network_aname})
# TODO(rkukura): Combine subnet name and name of
# interfaced router to build display name for each mapped
# Subnet.
dname = aim_utils.sanitize_display_name(context.current['name'])
aim_ctx = aim_context.AimContext(session)
prefix_len = context.current['cidr'].split('/')[1]
subnet_id = context.current['id']
for intf in self._subnet_router_interfaces(session, subnet_id):
gw_ip = intf.ip_address
for gw_ip, router_id in self._subnet_router_ips(session,
subnet_id):
router_db = self.l3_plugin._get_router(context._plugin_context,
router_id)
dname = aim_utils.sanitize_display_name(
router_db.name + " - " +
(context.current['name'] or context.current['cidr']))
gw_ip_mask = gw_ip + '/' + prefix_len
aim_subnet = aim_resource.Subnet(tenant_name=
network_tenant_aname,
@ -382,8 +385,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
{'id': network_id, 'aname': network_aname})
subnet_id = subnet_db.id
for intf in self._subnet_router_interfaces(session, subnet_id):
gw_ip = intf.ip_address
for gw_ip, router_id in self._subnet_router_ips(session, subnet_id):
gw_ip_mask = gw_ip + '/' + prefix_len
aim_subnet = aim_resource.Subnet(tenant_name=network_tenant_aname,
bd_name=network_aname,
@ -578,7 +580,49 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
name=ROUTER_SUBJECT_NAME)
subject = self.aim.update(aim_ctx, subject, display_name=dname)
# REVISIT(rkukura): Update extension attributes?
# REVISIT(rkukura): Refactor to share common code below
# with extend_router_dict. Also consider using joins to
# fetch the subnet_db and network_db as part of the
# initial query.
for intf in (session.query(models_v2.IPAllocation).
join(models_v2.Port).
join(l3_db.RouterPort).
filter(l3_db.RouterPort.router_id == id,
l3_db.RouterPort.port_type ==
n_constants.DEVICE_OWNER_ROUTER_INTF)):
subnet_db = (session.query(models_v2.Subnet).
filter_by(id=intf.subnet_id).
one())
prefix_len = subnet_db.cidr.split('/')[1]
dname = aim_utils.sanitize_display_name(
name + " - " + (subnet_db.name or subnet_db.cidr))
network_id = subnet_db.network_id
network_db = (session.query(models_v2.Network).
filter_by(id=network_id).
one())
network_tenant_id = network_db.tenant_id
network_tenant_aname = self.name_mapper.tenant(
session, network_tenant_id)
LOG.debug("Mapped tenant_id %(id)s to %(aname)s",
{'id': network_tenant_id,
'aname': network_tenant_aname})
network_aname = self.name_mapper.network(session, network_id)
LOG.debug("Mapped network_id %(id)s to %(aname)s",
{'id': network_id, 'aname': network_aname})
gw_ip = intf.ip_address
gw_ip_mask = gw_ip + '/' + prefix_len
aim_subnet = aim_resource.Subnet(tenant_name=
network_tenant_aname,
bd_name=network_aname,
gw_ip_mask=gw_ip_mask)
self.aim.update(aim_ctx, aim_subnet, display_name=dname)
def delete_router(self, context, current):
LOG.debug("APIC AIM MD deleting router: %s", current)
@ -643,12 +687,47 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
dist_names[cisco_apic_l3.CONTRACT_SUBJECT] = aim_subject.dn
sync_state = self._merge_status(aim_ctx, sync_state, aim_subject)
# See if this router has any attached interfaces.
if (session.query(l3_db.RouterPort).
filter(l3_db.RouterPort.router_id == id,
l3_db.RouterPort.port_type ==
n_constants.DEVICE_OWNER_ROUTER_INTF).
count()):
# REVISIT(rkukura): Consider moving the SubnetPool query below
# into this loop, although that might be less efficient when
# many subnets are from the same pool.
active = False
for intf in (session.query(models_v2.IPAllocation).
join(models_v2.Port).
join(l3_db.RouterPort).
filter(l3_db.RouterPort.router_id == id,
l3_db.RouterPort.port_type ==
n_constants.DEVICE_OWNER_ROUTER_INTF)):
active = True
subnet_db = (session.query(models_v2.Subnet).
filter_by(id=intf.subnet_id).
one())
prefix_len = subnet_db.cidr.split('/')[1]
network_id = subnet_db.network_id
network_db = (session.query(models_v2.Network).
filter_by(id=network_id).
one())
network_tenant_id = network_db.tenant_id
network_tenant_aname = self.name_mapper.tenant(session,
network_tenant_id)
LOG.debug("Mapped tenant_id %(id)s to %(aname)s",
{'id': network_tenant_id, 'aname': network_tenant_aname})
network_aname = self.name_mapper.network(session, network_id)
LOG.debug("Mapped network_id %(id)s to %(aname)s",
{'id': network_id, 'aname': network_aname})
gw_ip = intf.ip_address
gw_ip_mask = gw_ip + '/' + prefix_len
aim_subnet = aim_resource.Subnet(tenant_name=network_tenant_aname,
bd_name=network_aname,
gw_ip_mask=gw_ip_mask)
dist_names[gw_ip] = aim_subnet.dn
sync_state = self._merge_status(aim_ctx, sync_state, aim_subnet)
if active:
# Find this router's IPv4 address scope if it has one, or
# else its IPv6 address scope.
scope_id = None
@ -663,7 +742,6 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
l3_db.RouterPort.port_type ==
n_constants.DEVICE_OWNER_ROUTER_INTF).
distinct()):
LOG.debug("got pool_db: %s", pool_db)
if pool_db.ip_version == 4:
scope_id = pool_db.address_scope_id
break
@ -689,11 +767,6 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
dist_names[cisco_apic_l3.VRF] = aim_vrf.dn
sync_state = self._merge_status(aim_ctx, sync_state, aim_vrf)
# TODO(rkukura): Also include interfaced Subnets. This
# probably means splitting the above SubnetPool query to first
# query for subnets, add the corresponding AIM subnet, then
# check the subnet's subnetpool's address scope.
result[cisco_apic.DIST_NAMES] = dist_names
result[cisco_apic.SYNC_STATE] = sync_state
@ -736,10 +809,13 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
prefix_len = subnet['cidr'].split('/')[1]
gw_ip_mask = gw_ip + '/' + prefix_len
dname = aim_utils.sanitize_display_name(
router['name'] + " - " +
(subnet['name'] or subnet['cidr']))
aim_subnet = aim_resource.Subnet(tenant_name=network_tenant_aname,
bd_name=network_aname,
gw_ip_mask=gw_ip_mask,
display_name=subnet['name'])
display_name=dname)
aim_subnet = self.aim.create(aim_ctx, aim_subnet)
# Ensure network's EPG provides/consumes router's Contract.
@ -948,6 +1024,13 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
self._core_plugin = manager.NeutronManager.get_plugin()
return self._core_plugin
@property
def l3_plugin(self):
if not self._l3_plugin:
plugins = manager.NeutronManager.get_service_plugins()
self._l3_plugin = plugins[pconst.L3_ROUTER_NAT]
return self._l3_plugin
def _merge_status(self, aim_ctx, sync_state, resource):
status = self.aim.get_status(aim_ctx, resource)
if not status:
@ -978,10 +1061,10 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
if fixed_ip['subnet_id'] == subnet_id:
return fixed_ip['ip_address']
def _subnet_router_interfaces(self, session, subnet_id):
return (session.query(models_v2.IPAllocation).
def _subnet_router_ips(self, session, subnet_id):
return (session.query(models_v2.IPAllocation.ip_address,
l3_db.RouterPort.router_id).
join(models_v2.Port).
join(l3_db.RouterPort).
filter(
models_v2.IPAllocation.subnet_id == subnet_id,
l3_db.RouterPort.port_type ==

View File

@ -370,7 +370,7 @@ class TestAimMapping(ApicAimTestCase):
app_profile_name=self._app_profile_name,
should_exist=False)
def _check_subnet(self, subnet, net, expected_gw_ips, unexpected_gw_ips):
def _check_subnet(self, subnet, net, expected_gws, unexpected_gw_ips):
prefix_len = subnet['cidr'].split('/')[1]
# REVISIT(rkukura): Check AIM Tenant here?
@ -378,7 +378,7 @@ class TestAimMapping(ApicAimTestCase):
net_aname = self._map_name(net)
for gw_ip in expected_gw_ips:
for gw_ip, router in expected_gws:
gw_ip_mask = gw_ip + '/' + prefix_len
aim_subnet = self._get_subnet(gw_ip_mask,
net_aname,
@ -387,7 +387,10 @@ class TestAimMapping(ApicAimTestCase):
self.assertEqual(net_aname, aim_subnet.bd_name)
self.assertEqual(gw_ip_mask, aim_subnet.gw_ip_mask)
self.assertEqual('private', aim_subnet.scope)
self.assertEqual(subnet['name'], aim_subnet.display_name)
display_name = ("%s - %s" %
(router['name'],
(subnet['name'] or subnet['cidr'])))
self.assertEqual(display_name, aim_subnet.display_name)
self._check_dn(subnet, aim_subnet, gw_ip)
for gw_ip in unexpected_gw_ips:
@ -427,8 +430,8 @@ class TestAimMapping(ApicAimTestCase):
self._tenant_name,
should_exist=False)
def _check_router(self, router, orig_router=None, active=False,
scope=None):
def _check_router(self, router, expected_gw_ips, unexpected_gw_ips,
orig_router=None, scope=None):
orig_router = orig_router or router
# REVISIT(rkukura): Check AIM Tenant here?
@ -455,9 +458,7 @@ class TestAimMapping(ApicAimTestCase):
self._check_any_filter()
# TODO(rkukura): Once AIM Subnets are exposed on router, pass
# in expected_gw_ips and use instead of this active flag.
if active:
if expected_gw_ips:
if scope:
vrf_aname = self._map_name(scope)
vrf_dname = scope['name']
@ -483,6 +484,17 @@ class TestAimMapping(ApicAimTestCase):
else:
self._check_no_dn(router, 'VRF')
# The AIM Subnets are validated in _check_subnet, so just
# check that their DNs are present and valid.
dist_names = router.get('apic:distinguished_names')
for gw_ip in expected_gw_ips:
self.assertIn(gw_ip, dist_names)
aim_subnet = self._find_by_dn(dist_names[gw_ip],
aim_resource.Subnet)
self.assertIsNotNone(aim_subnet)
for gw_ip in unexpected_gw_ips:
self.assertNotIn(gw_ip, dist_names)
def _check_router_deleted(self, router):
aname = self._map_name(router)
@ -585,16 +597,16 @@ class TestAimMapping(ApicAimTestCase):
orig_router = self._make_router(
self.fmt, 'test-tenant', 'router1')['router']
router_id = orig_router['id']
self._check_router(orig_router)
self._check_router(orig_router, [], [])
# Test show.
router = self._show('routers', router_id)['router']
self._check_router(router)
self._check_router(router, [], [])
# Test update.
data = {'router': {'name': 'newnameforrouter'}}
router = self._update('routers', router_id, data)['router']
self._check_router(router, orig_router)
self._check_router(router, [], [], orig_router)
# Test delete.
self._delete('routers', router_id)
@ -602,10 +614,10 @@ class TestAimMapping(ApicAimTestCase):
def test_router_interface(self):
# Create router.
router = self._make_router(
orig_router = self._make_router(
self.fmt, 'test-tenant', 'router1')['router']
router_id = router['id']
self._check_router(router)
router_id = orig_router['id']
self._check_router(orig_router, [], [])
# Create network.
net_resp = self._make_network(self.fmt, 'net1', True)
@ -634,7 +646,7 @@ class TestAimMapping(ApicAimTestCase):
# Check router.
router = self._show('routers', router_id)['router']
self._check_router(router, active=True)
self._check_router(router, [gw1_ip], [])
# Check network.
net = self._show('networks', net_id)['network']
@ -642,7 +654,7 @@ class TestAimMapping(ApicAimTestCase):
# Check subnet1.
subnet = self._show('subnets', subnet1_id)['subnet']
self._check_subnet(subnet, net, [gw1_ip], [])
self._check_subnet(subnet, net, [(gw1_ip, router)], [])
# Check subnet2.
subnet = self._show('subnets', subnet2_id)['subnet']
@ -651,7 +663,13 @@ class TestAimMapping(ApicAimTestCase):
# Test subnet update.
data = {'subnet': {'name': 'newnameforsubnet'}}
subnet = self._update('subnets', subnet1_id, data)['subnet']
self._check_subnet(subnet, net, [gw1_ip], [])
self._check_subnet(subnet, net, [(gw1_ip, router)], [])
# Test router update.
data = {'router': {'name': 'newnameforrouter'}}
router = self._update('routers', router_id, data)['router']
self._check_router(router, [gw1_ip], [], orig_router)
self._check_subnet(subnet, net, [(gw1_ip, router)], [])
# Add subnet2 to router by port.
fixed_ips = [{'subnet_id': subnet2_id, 'ip_address': gw2_ip}]
@ -663,19 +681,19 @@ class TestAimMapping(ApicAimTestCase):
# Check router.
router = self._show('routers', router_id)['router']
self._check_router(router, active=True)
self._check_router(router, [gw1_ip, gw2_ip], [], orig_router)
# Check network.
net = self._show('networks', net_id)['network']
self._check_network(net, routers=[router])
self._check_network(net, routers=[orig_router])
# Check subnet1.
subnet = self._show('subnets', subnet1_id)['subnet']
self._check_subnet(subnet, net, [gw1_ip], [])
self._check_subnet(subnet, net, [(gw1_ip, router)], [])
# Check subnet2.
subnet = self._show('subnets', subnet2_id)['subnet']
self._check_subnet(subnet, net, [gw2_ip], [])
self._check_subnet(subnet, net, [(gw2_ip, router)], [])
# Remove subnet1 from router by subnet.
info = self.l3_plugin.remove_router_interface(
@ -684,11 +702,11 @@ class TestAimMapping(ApicAimTestCase):
# Check router.
router = self._show('routers', router_id)['router']
self._check_router(router, active=True)
self._check_router(router, [gw2_ip], [gw1_ip], orig_router)
# Check network.
net = self._show('networks', net_id)['network']
self._check_network(net, routers=[router])
self._check_network(net, routers=[orig_router])
# Check subnet1.
subnet = self._show('subnets', subnet1_id)['subnet']
@ -696,7 +714,7 @@ class TestAimMapping(ApicAimTestCase):
# Check subnet2.
subnet = self._show('subnets', subnet2_id)['subnet']
self._check_subnet(subnet, net, [gw2_ip], [])
self._check_subnet(subnet, net, [(gw2_ip, router)], [])
# Remove subnet2 from router by port.
info = self.l3_plugin.remove_router_interface(
@ -705,7 +723,7 @@ class TestAimMapping(ApicAimTestCase):
# Check router.
router = self._show('routers', router_id)['router']
self._check_router(router)
self._check_router(router, [], [gw1_ip, gw2_ip], orig_router)
# Check network.
net = self._show('networks', net_id)['network']
@ -738,10 +756,10 @@ class TestAimMapping(ApicAimTestCase):
pool_id = pool['id']
# Create router.
router = self._make_router(
orig_router = self._make_router(
self.fmt, 'test-tenant', 'router1')['router']
router_id = router['id']
self._check_router(router, scope=scope)
router_id = orig_router['id']
self._check_router(orig_router, [], [], scope=scope)
# Create network.
net_resp = self._make_network(self.fmt, 'net1', True)
@ -772,7 +790,7 @@ class TestAimMapping(ApicAimTestCase):
# Check router.
router = self._show('routers', router_id)['router']
self._check_router(router, active=True, scope=scope)
self._check_router(router, [gw1_ip], [], scope=scope)
# Check network.
net = self._show('networks', net_id)['network']
@ -780,7 +798,7 @@ class TestAimMapping(ApicAimTestCase):
# Check subnet1.
subnet = self._show('subnets', subnet1_id)['subnet']
self._check_subnet(subnet, net, [gw1_ip], [])
self._check_subnet(subnet, net, [(gw1_ip, router)], [])
# Check subnet2.
subnet = self._show('subnets', subnet2_id)['subnet']
@ -789,7 +807,13 @@ class TestAimMapping(ApicAimTestCase):
# Test subnet update.
data = {'subnet': {'name': 'newnameforsubnet'}}
subnet = self._update('subnets', subnet1_id, data)['subnet']
self._check_subnet(subnet, net, [gw1_ip], [])
self._check_subnet(subnet, net, [(gw1_ip, router)], [])
# Test router update.
data = {'router': {'name': 'newnameforrouter'}}
router = self._update('routers', router_id, data)['router']
self._check_router(router, [gw1_ip], [], orig_router, scope)
self._check_subnet(subnet, net, [(gw1_ip, router)], [])
# Add subnet2 to router by port.
fixed_ips = [{'subnet_id': subnet2_id, 'ip_address': gw2_ip}]
@ -801,19 +825,19 @@ class TestAimMapping(ApicAimTestCase):
# Check router.
router = self._show('routers', router_id)['router']
self._check_router(router, active=True, scope=scope)
self._check_router(router, [gw1_ip, gw2_ip], [], orig_router, scope)
# Check network.
net = self._show('networks', net_id)['network']
self._check_network(net, routers=[router], scope=scope)
self._check_network(net, routers=[orig_router], scope=scope)
# Check subnet1.
subnet = self._show('subnets', subnet1_id)['subnet']
self._check_subnet(subnet, net, [gw1_ip], [])
self._check_subnet(subnet, net, [(gw1_ip, router)], [])
# Check subnet2.
subnet = self._show('subnets', subnet2_id)['subnet']
self._check_subnet(subnet, net, [gw2_ip], [])
self._check_subnet(subnet, net, [(gw2_ip, router)], [])
# Remove subnet1 from router by subnet.
info = self.l3_plugin.remove_router_interface(
@ -822,11 +846,11 @@ class TestAimMapping(ApicAimTestCase):
# Check router.
router = self._show('routers', router_id)['router']
self._check_router(router, active=True, scope=scope)
self._check_router(router, [gw2_ip], [gw1_ip], orig_router, scope)
# Check network.
net = self._show('networks', net_id)['network']
self._check_network(net, routers=[router], scope=scope)
self._check_network(net, routers=[orig_router], scope=scope)
# Check subnet1.
subnet = self._show('subnets', subnet1_id)['subnet']
@ -834,7 +858,7 @@ class TestAimMapping(ApicAimTestCase):
# Check subnet2.
subnet = self._show('subnets', subnet2_id)['subnet']
self._check_subnet(subnet, net, [gw2_ip], [])
self._check_subnet(subnet, net, [(gw2_ip, router)], [])
# Remove subnet2 from router by port.
info = self.l3_plugin.remove_router_interface(
@ -843,7 +867,7 @@ class TestAimMapping(ApicAimTestCase):
# Check router.
router = self._show('routers', router_id)['router']
self._check_router(router, scope=scope)
self._check_router(router, [], [gw1_ip, gw2_ip], orig_router, scope)
# Check network.
net = self._show('networks', net_id)['network']
@ -1058,10 +1082,9 @@ class TestSyncState(ApicAimTestCase):
context.get_admin_context(), router['id'],
{'subnet_id': subnet['id']})
# TODO(rkukura): Enable when exposing Subnets on router is implemented.
# router = self._show('routers', router['id'])['router']
# self.assertEqual(expected_state,
# router['apic:synchronization_state'])
router = self._show('routers', router['id'])['router']
self.assertEqual(expected_state,
router['apic:synchronization_state'])
subnet = self._show('subnets', subnet['id'])['subnet']
self.assertEqual(expected_state, subnet['apic:synchronization_state'])