[apic_aim] Map neutron resources to AIM, part 4

The apic_aim mechanism driver and L3 plugin map router interfaces to
AIM Subnets. The DNs and status of these subnets are exposed via
extended Neutron subnet attributes. If any subnets of a network are
attached as interfaces to a router, the network's default EPG provides
and consumes the router's Contract.

A seperate patch will manage the VRFs of routed networks, and will
reject invalid routing topologies, completing the basic east/west
routing functionality.

Another follow-on patch will additionally expose the AIM Subnets via
extended attributes of the router to with the corresponding Neutron
subnet is attached, and will likely include both the Neutron subnet
and router names in the AIM display name of the Subnet.

Change-Id: Id8aa749c2a590bf6d0548162483553edb8a3589d
This commit is contained in:
Robert Kukura 2016-09-12 14:18:08 -04:00
parent 8784548bce
commit a3ddd689bd
3 changed files with 438 additions and 57 deletions

View File

@ -23,6 +23,8 @@ from neutron._i18n import _LW
from neutron.agent.linux import dhcp
from neutron.common import constants as n_constants
from neutron.common import rpc as n_rpc
from neutron.db import l3_db
from neutron.db import models_v2
from neutron.extensions import portbindings
from neutron import manager
from neutron.plugins.ml2 import driver_api as api
@ -62,6 +64,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
self.db = model.DbModel()
self.name_mapper = apic_mapper.APICNameMapper(self.db, log)
self.aim = aim_manager.AimManager()
self._core_plugin = None
# REVISIT(rkukura): Read from config or possibly from AIM?
self.enable_dhcp_opt = True
@ -140,13 +143,16 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
vrf = self._get_unrouted_vrf(aim_ctx)
# REVISIT(rkukura): When AIM changes default
# ep_move_detect_mode value to 'garp', remove it here.
bd = aim_resource.BridgeDomain(tenant_name=tenant_aname,
name=aname,
display_name=dname,
vrf_name=vrf.name,
enable_arp_flood=True,
enable_routing=False,
limit_ip_learn_to_subnets=True)
limit_ip_learn_to_subnets=True,
ep_move_detect_mode='garp')
self.aim.create(aim_ctx, bd)
epg = aim_resource.EndpointGroup(tenant_name=tenant_aname,
@ -246,24 +252,86 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
def create_subnet_precommit(self, context):
LOG.debug("APIC AIM MD creating subnet: %s", context.current)
# TODO(rkukura): Implement.
# Neutron subnets are mapped to AIM Subnets as they are added
# to routers as interfaces.
def update_subnet_precommit(self, context):
LOG.debug("APIC AIM MD updating subnet: %s", context.current)
# TODO(rkukura): Implement.
if context.current['name'] != context.original['name']:
session = context._plugin_context.session
network_id = context.current['network_id']
network = self.plugin.get_network(context._plugin_context,
network_id)
network_tenant_id = network['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.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
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_subnet_precommit(self, context):
LOG.debug("APIC AIM MD deleting subnet: %s", context.current)
# TODO(rkukura): Implement.
# Neutron subnets are unmapped from AIM Subnets as they are
# removed from routers.
def extend_subnet_dict(self, session, base_model, result):
LOG.debug("APIC AIM MD extending dict for subnet: %s", result)
sync_state = cisco_apic.SYNC_SYNCED
dist_names = {}
prefix_len = result['cidr'].split('/')[1]
aim_ctx = aim_context.AimContext(session)
# TODO(rkukura): Implement.
network_id = result['network_id']
network_db = (session.query(models_v2.Network).
filter_by(id=network_id).
one())
network_tenant_id = network_db.tenant_id
result[cisco_apic.DIST_NAMES] = {}
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})
subnet_id = result['id']
for intf in self._subnet_router_interfaces(session, subnet_id):
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)
result[cisco_apic.DIST_NAMES] = dist_names
result[cisco_apic.SYNC_STATE] = sync_state
def create_address_scope_precommit(self, context):
@ -498,6 +566,8 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
contract_name=aname,
name=ROUTER_SUBJECT_NAME)
# TODO(rkukura): Also include VRF and interfaced Subnets.
aim_ctx = aim_context.AimContext(session)
sync_state = cisco_apic.SYNC_SYNCED
sync_state = self._merge_status(aim_ctx, sync_state, contract)
@ -507,13 +577,149 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
subject.dn}
result[cisco_apic.SYNC_STATE] = sync_state
def add_router_interface(self, context, info):
LOG.debug("APIC AIM MD adding router interface: %s", info)
# TODO(rkukura): Implement.
def add_router_interface(self, context, router, port, subnets):
LOG.debug("APIC AIM MD adding subnets %(subnets)s to router "
"%(router)s as interface port %(port)s",
{'subnets': subnets, 'router': router, 'port': port})
def remove_router_interface(self, context, info):
LOG.debug("APIC AIM MD removing router interface: %s", info)
# TODO(rkukura): Implement.
session = context.session
network_id = port['network_id']
network = self.plugin.get_network(context, network_id)
network_tenant_id = network['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.info(_LI("Mapped network_id %(id)s to %(aname)s"),
{'id': network_id, 'aname': network_aname})
router_tenant_id = router['tenant_id']
router_tenant_aname = self.name_mapper.tenant(session,
router_tenant_id)
LOG.debug("Mapped tenant_id %(id)s to %(aname)s",
{'id': router_tenant_id, 'aname': router_tenant_aname})
router_id = router['id']
router_aname = self.name_mapper.router(session, router_id)
LOG.info(_LI("Mapped router_id %(id)s to %(aname)s"),
{'id': router_id, 'aname': router_aname})
aim_ctx = aim_context.AimContext(session)
# Create AIM Subnet(s) for each added Neutron subnet.
for subnet in subnets:
gw_ip = self._ip_for_subnet(subnet, port['fixed_ips'])
prefix_len = subnet['cidr'].split('/')[1]
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,
display_name=subnet['name'])
aim_subnet = self.aim.create(aim_ctx, aim_subnet)
# Ensure network's EPG provides/consumes router's Contract.
aim_epg = aim_resource.EndpointGroup(tenant_name=network_tenant_aname,
app_profile_name=AP_NAME,
name=network_aname)
aim_epg = self.aim.get(aim_ctx, aim_epg)
contracts = aim_epg.consumed_contract_names
if router_aname not in contracts:
contracts.append(router_aname)
aim_epg = self.aim.update(aim_ctx, aim_epg,
consumed_contract_names=contracts)
contracts = aim_epg.provided_contract_names
if router_aname not in contracts:
contracts.append(router_aname)
aim_epg = self.aim.update(aim_ctx, aim_epg,
provided_contract_names=contracts)
# TODO(rkukura): Implement selecting/setting VRF and
# validating topology.
def remove_router_interface(self, context, router_id, port_db, subnets):
LOG.debug("APIC AIM MD removing subnets %(subnets)s from router "
"%(router)s as interface port %(port)s",
{'subnets': subnets, 'router': router_id, 'port': port_db})
session = context.session
network_id = port_db.network_id
network = self.plugin.get_network(context, network_id)
network_tenant_id = network['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.info(_LI("Mapped network_id %(id)s to %(aname)s"),
{'id': network_id, 'aname': network_aname})
router_db = (session.query(l3_db.Router).
filter_by(id=router_id).
one())
router_tenant_id = router_db.tenant_id
router_tenant_aname = self.name_mapper.tenant(session,
router_tenant_id)
LOG.debug("Mapped tenant_id %(id)s to %(aname)s",
{'id': router_tenant_id, 'aname': router_tenant_aname})
router_aname = self.name_mapper.router(session, router_id)
LOG.info(_LI("Mapped router_id %(id)s to %(aname)s"),
{'id': router_id, 'aname': router_aname})
aim_ctx = aim_context.AimContext(session)
# Remove AIM Subnet(s) for each removed Neutron subnet.
for subnet in subnets:
gw_ip = self._ip_for_subnet(subnet, port_db.fixed_ips)
prefix_len = subnet['cidr'].split('/')[1]
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.delete(aim_ctx, aim_subnet)
# Find remaining routers with interfaces to this network.
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()]
# If network is no longer connected to this router, stop
# network's EPG from providing/consuming this router's
# Contract.
if router_id not in router_ids:
aim_epg = aim_resource.EndpointGroup(
tenant_name=network_tenant_aname,
app_profile_name=AP_NAME,
name=network_aname)
aim_epg = self.aim.get(aim_ctx, aim_epg)
contracts = [aname for aname in aim_epg.consumed_contract_names
if aname != router_aname]
aim_epg = self.aim.update(aim_ctx, aim_epg,
consumed_contract_names=contracts)
contracts = [aname for aname in aim_epg.provided_contract_names
if aname != router_aname]
aim_epg = self.aim.update(aim_ctx, aim_epg,
provided_contract_names=contracts)
# TODO(rkukura): If network no longer connected to any router,
# make the network's BD unrouted.
def bind_port(self, context):
LOG.debug("Attempting to bind port %(port)s on network %(net)s",
@ -722,6 +928,12 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
subnet['dhcp_server_ips'] = dhcp_ips
return subnets
@property
def plugin(self):
if not self._core_plugin:
self._core_plugin = manager.NeutronManager.get_plugin()
return self._core_plugin
def _merge_status(self, aim_ctx, sync_state, resource):
status = self.aim.get_status(aim_ctx, resource)
if not status:
@ -746,11 +958,21 @@ class ApicMechanismDriver(api_plus.MechanismDriver):
sync_state = cisco_apic.SYNC_BUILD
return sync_state
def _gateway_ip_mask(self, subnet):
gateway_ip = subnet['gateway_ip']
if gateway_ip:
prefix_len = subnet['cidr'].split('/')[1]
return gateway_ip + '/' + prefix_len
def _ip_for_subnet(self, subnet, fixed_ips):
subnet_id = subnet['id']
for fixed_ip in fixed_ips:
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).
join(models_v2.Port).
join(l3_db.RouterPort).
filter(
models_v2.IPAllocation.subnet_id == subnet_id,
l3_db.RouterPort.port_type ==
n_constants.DEVICE_OWNER_ROUTER_INTF
))
def _get_common_tenant(self, aim_ctx):
attrs = aim_resource.Tenant(name=COMMON_TENANT_NAME,

View File

@ -127,9 +127,28 @@ class ApicL3Plugin(common_db_mixin.CommonDbMixin,
# funtionality to be completely transaction safe.
info = super(ApicL3Plugin, self).add_router_interface(
context, router_id, interface_info)
self._md.add_router_interface(context, info)
return info
def _add_interface_by_subnet(self, context, router, subnet_id, owner):
LOG.debug("APIC AIM L3 Plugin adding interface by subnet %(subnet)s "
"to router %(router)s",
{'subnet': subnet_id, 'router': router['id']})
port, subnets, new_port = (
super(ApicL3Plugin, self)._add_interface_by_subnet(
context, router, subnet_id, owner))
self._md.add_router_interface(context, router, port, subnets)
return port, subnets, new_port
def _add_interface_by_port(self, context, router, port_id, owner):
LOG.debug("APIC AIM L3 Plugin adding interface by port %(port)s "
"to router %(router)s",
{'port': port_id, 'router': router['id']})
port, subnets = (
super(ApicL3Plugin, self)._add_interface_by_port(
context, router, port_id, owner))
self._md.add_router_interface(context, router, port, subnets)
return port, subnets
def remove_router_interface(self, context, router_id, interface_info):
LOG.debug("APIC AIM L3 Plugin removing interface %(interface)s "
"from router %(router)s",
@ -144,5 +163,26 @@ class ApicL3Plugin(common_db_mixin.CommonDbMixin,
# funtionality to be completely transaction safe.
info = super(ApicL3Plugin, self).remove_router_interface(
context, router_id, interface_info)
self._md.remove_router_interface(context, info)
return info
def _remove_interface_by_subnet(self, context, router_id, subnet_id,
owner):
LOG.debug("APIC AIM L3 Plugin removing interface by subnet %(subnet)s "
"from router %(router)s",
{'subnet': subnet_id, 'router': router_id})
port_db, subnets = (
super(ApicL3Plugin, self)._remove_interface_by_subnet(
context, router_id, subnet_id, owner))
self._md.remove_router_interface(context, router_id, port_db, subnets)
return port_db, subnets
def _remove_interface_by_port(self, context, router_id, port_id, subnet_id,
owner):
LOG.debug("APIC AIM L3 Plugin removing interface by port %(port)s "
"from router %(router)s",
{'port': port_id, 'router': router_id})
port_db, subnets = (
super(ApicL3Plugin, self)._remove_interface_by_port(
context, router_id, port_id, subnet_id, owner))
self._md.remove_router_interface(context, router_id, port_db, subnets)
return port_db, subnets

View File

@ -68,6 +68,9 @@ class FakeKeystoneClient(object):
self.projects = FakeProjectManager()
# TODO(rkukura): Also run Neutron L3 tests on apic_aim L3 plugin.
class ApicAimTestCase(test_address_scope.AddressScopeTestCase,
test_l3.L3NatTestCaseMixin):
def setUp(self):
@ -168,6 +171,19 @@ class TestAimMapping(ApicAimTestCase):
self.assertIsNone(bd)
return bd
def _get_subnet(self, gw_ip_mask, bd_name, tenant_name, should_exist=True):
session = db_api.get_session()
aim_ctx = aim_context.AimContext(session)
subnet = aim_resource.Subnet(tenant_name=tenant_name,
bd_name=bd_name,
gw_ip_mask=gw_ip_mask)
subnet = self.aim_mgr.get(aim_ctx, subnet)
if should_exist:
self.assertIsNotNone(subnet)
else:
self.assertIsNone(subnet)
return subnet
def _get_epg(self, epg_name, tenant_name, app_profile_name,
should_exist=True):
session = db_api.get_session()
@ -257,7 +273,7 @@ class TestAimMapping(ApicAimTestCase):
self.assertEqual('Common Unrouted Context', aim_vrf.display_name)
self.assertEqual('enforced', aim_vrf.policy_enforcement_pref)
def _check_unrouted_network(self, net, orig_net=None):
def _check_network(self, net, orig_net=None, routers=None):
orig_net = orig_net or net
# REVISIT(rkukura): Check AIM Tenant here?
@ -265,6 +281,8 @@ class TestAimMapping(ApicAimTestCase):
aname = self._map_name(orig_net)
router_anames = [self._map_name(router) for router in routers or []]
aim_bd = self._get_bd(aname,
self._tenant_name)
self.assertEqual(self._tenant_name, aim_bd.tenant_name)
@ -272,10 +290,10 @@ class TestAimMapping(ApicAimTestCase):
self.assertEqual(net['name'], aim_bd.display_name)
self.assertEqual(self._unrouted_vrf_name, aim_bd.vrf_name)
self.assertTrue(aim_bd.enable_arp_flood)
self.assertFalse(aim_bd.enable_routing)
self.assertFalse(aim_bd.enable_routing) # TODO(rkukura)
self.assertTrue(aim_bd.limit_ip_learn_to_subnets)
self.assertEqual('proxy', aim_bd.l2_unknown_unicast_mode) # REVISIT
self.assertEqual('', aim_bd.ep_move_detect_mode) # REVISIT
self.assertEqual('proxy', aim_bd.l2_unknown_unicast_mode)
self.assertEqual('garp', aim_bd.ep_move_detect_mode)
self._check_dn(net, aim_bd, 'BridgeDomain')
aim_epg = self._get_epg(aname,
@ -286,13 +304,14 @@ class TestAimMapping(ApicAimTestCase):
self.assertEqual(aname, aim_epg.name)
self.assertEqual(net['name'], aim_epg.display_name)
self.assertEqual(aname, aim_epg.bd_name)
self.assertEqual([], aim_epg.provided_contract_names)
self.assertEqual([], aim_epg.consumed_contract_names)
self.assertItemsEqual(router_anames, aim_epg.provided_contract_names)
self.assertItemsEqual(router_anames, aim_epg.consumed_contract_names)
# REVISIT(rkukura): Check openstack_vmm_domain_names and
# physical_domain_names?
self._check_dn(net, aim_epg, 'EndpointGroup')
self._check_unrouted_vrf()
if not routers:
self._check_unrouted_vrf()
def _check_network_deleted(self, net):
aname = self._map_name(net)
@ -306,16 +325,38 @@ class TestAimMapping(ApicAimTestCase):
app_profile_name=self._app_profile_name,
should_exist=False)
def _check_unrouted_subnet(self, subnet):
def _check_subnet(self, subnet, net, expected_gw_ips, unexpected_gw_ips):
prefix_len = subnet['cidr'].split('/')[1]
# REVISIT(rkukura): Check AIM Tenant here?
self.assertEqual('test-tenant', subnet['tenant_id'])
self._check_no_dn(subnet, 'Subnet')
net_aname = self._map_name(net)
# REVISIT(rkukura): Anything else to check?
for gw_ip in expected_gw_ips:
gw_ip_mask = gw_ip + '/' + prefix_len
aim_subnet = self._get_subnet(gw_ip_mask,
net_aname,
self._tenant_name)
self.assertEqual(self._tenant_name, aim_subnet.tenant_name)
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)
self._check_dn(subnet, aim_subnet, gw_ip)
for gw_ip in unexpected_gw_ips:
gw_ip_mask = gw_ip + '/' + prefix_len
self._get_subnet(gw_ip_mask,
net_aname,
self._tenant_name,
should_exist=False)
self._check_no_dn(subnet, gw_ip)
def _check_subnet_deleted(self, subnet):
# REVISIT(rkukura): Anything to check?
# REVISIT(rkukura): Anything to check? We could find all the
# AIM Subnets with the network's bd_name, and make sure none
# are in this subnet.
pass
def _check_address_scope(self, a_s, orig_a_s=None):
@ -408,16 +449,16 @@ class TestAimMapping(ApicAimTestCase):
# Test create.
orig_net = self._make_network(self.fmt, 'net1', True)['network']
net_id = orig_net['id']
self._check_unrouted_network(orig_net)
self._check_network(orig_net)
# Test show.
net = self._show('networks', net_id)['network']
self._check_unrouted_network(net)
self._check_network(net)
# Test update.
data = {'network': {'name': 'newnamefornet'}}
net = self._update('networks', net_id, data)['network']
self._check_unrouted_network(net, orig_net)
self._check_network(net, orig_net)
# Test delete.
self._delete('networks', net_id)
@ -425,22 +466,24 @@ class TestAimMapping(ApicAimTestCase):
def test_subnet_lifecycle(self):
# Create network.
net = self._make_network(self.fmt, 'net1', True)
net_resp = self._make_network(self.fmt, 'net1', True)
net = net_resp['network']
# Test create.
gw_ip = '10.0.0.1'
subnet = self._make_subnet(
self.fmt, net, '10.0.0.1', '10.0.0.0/24')['subnet']
self.fmt, net_resp, gw_ip, '10.0.0.0/24')['subnet']
subnet_id = subnet['id']
self._check_unrouted_subnet(subnet)
self._check_subnet(subnet, net, [], [gw_ip])
# Test show.
subnet = self._show('subnets', subnet_id)['subnet']
self._check_unrouted_subnet(subnet)
self._check_subnet(subnet, net, [], [gw_ip])
# Test update.
data = {'subnet': {'name': 'newnamefornet'}}
subnet = self._update('subnets', subnet_id, data)['subnet']
self._check_unrouted_subnet(subnet)
self._check_subnet(subnet, net, [], [gw_ip])
# Test delete.
self._delete('subnets', subnet_id)
@ -495,32 +538,107 @@ class TestAimMapping(ApicAimTestCase):
# Create network.
net_resp = self._make_network(self.fmt, 'net1', True)
self._check_unrouted_network(net_resp['network'])
net = net_resp['network']
net_id = net['id']
self._check_network(net)
# Create subnet.
subnet = self._make_subnet(self.fmt, net_resp, '10.0.0.1',
'10.0.0.0/24')['subnet']
subnet_id = subnet['id']
self._check_unrouted_subnet(subnet)
# Create subnet1.
gw1_ip = '10.0.1.1'
subnet = self._make_subnet(self.fmt, net_resp, gw1_ip,
'10.0.1.0/24')['subnet']
subnet1_id = subnet['id']
self._check_subnet(subnet, net, [], [gw1_ip])
# Add subnet to router.
# Create subnet2.
gw2_ip = '10.0.2.1'
subnet = self._make_subnet(self.fmt, net_resp, gw2_ip,
'10.0.2.0/24')['subnet']
subnet2_id = subnet['id']
self._check_subnet(subnet, net, [], [gw2_ip])
# Add subnet1 to router by subnet.
info = self.l3_plugin.add_router_interface(
context.get_admin_context(), router_id, {'subnet_id': subnet_id})
self.assertIn(subnet_id, info['subnet_ids'])
self._check_router(router)
context.get_admin_context(), router_id, {'subnet_id': subnet1_id})
self.assertIn(subnet1_id, info['subnet_ids'])
# REVISIT(rkukura): Get and check router?
# TODO(rkukura): Check router and subnet extension attributes,
# BD, EPG, etc..
# Check network.
net = self._show('networks', net_id)['network']
self._check_network(net, routers=[router])
# Remove subnet from router.
# Check subnet1.
subnet = self._show('subnets', subnet1_id)['subnet']
self._check_subnet(subnet, net, [gw1_ip], [])
# Check subnet2.
subnet = self._show('subnets', subnet2_id)['subnet']
self._check_subnet(subnet, net, [], [gw2_ip])
# Test subnet update.
data = {'subnet': {'name': 'newnameforsubnet'}}
subnet = self._update('subnets', subnet1_id, data)['subnet']
self._check_subnet(subnet, net, [gw1_ip], [])
# Add subnet2 to router by port.
fixed_ips = [{'subnet_id': subnet2_id, 'ip_address': gw2_ip}]
port = self._make_port(self.fmt, net_id, fixed_ips=fixed_ips)['port']
port2_id = port['id']
info = self.l3_plugin.add_router_interface(
context.get_admin_context(), router_id, {'port_id': port2_id})
self.assertIn(subnet2_id, info['subnet_ids'])
# REVISIT(rkukura): Get and check router?
# Check network.
net = self._show('networks', net_id)['network']
self._check_network(net, routers=[router])
# Check subnet1.
subnet = self._show('subnets', subnet1_id)['subnet']
self._check_subnet(subnet, net, [gw1_ip], [])
# Check subnet2.
subnet = self._show('subnets', subnet2_id)['subnet']
self._check_subnet(subnet, net, [gw2_ip], [])
# Remove subnet1 from router by subnet.
info = self.l3_plugin.remove_router_interface(
context.get_admin_context(), router_id, {'subnet_id': subnet_id})
self.assertIn(subnet_id, info['subnet_ids'])
self._check_router(router)
context.get_admin_context(), router_id, {'subnet_id': subnet1_id})
self.assertIn(subnet1_id, info['subnet_ids'])
# REVISIT(rkukura): Get and check router?
# Check network.
net = self._show('networks', net_id)['network']
self._check_network(net, routers=[router])
# Check subnet1.
subnet = self._show('subnets', subnet1_id)['subnet']
self._check_subnet(subnet, net, [], [gw1_ip])
# Check subnet2.
subnet = self._show('subnets', subnet2_id)['subnet']
self._check_subnet(subnet, net, [gw2_ip], [])
# Remove subnet2 from router by port.
info = self.l3_plugin.remove_router_interface(
context.get_admin_context(), router_id, {'port_id': port2_id})
self.assertIn(subnet2_id, info['subnet_ids'])
# REVISIT(rkukura): Get and check router?
# Check network.
net = self._show('networks', net_id)['network']
self._check_network(net)
# Check subnet1.
subnet = self._show('subnets', subnet1_id)['subnet']
self._check_subnet(subnet, net, [], [gw1_ip])
# Check subnet2.
subnet = self._show('subnets', subnet2_id)['subnet']
self._check_subnet(subnet, net, [], [gw2_ip])
# def test_create_subnet_with_address_scope(self):
# net = self._make_network(self.fmt, 'net1', True)
# name = self._map_name(net['network'])
# net_resp = self._make_network(self.fmt, 'net1', True)
# name = self._map_name(net_resp['network'])
# self._check(name, vrf_name='UnroutedVRF')
# a_s = self._make_address_scope(self.fmt, 4, name='as1')
@ -533,7 +651,8 @@ class TestAimMapping(ApicAimTestCase):
# default_prefixlen=24)
# sp_id = sp['subnetpool']['id']
# self._make_subnet(self.fmt, net, None, None, subnetpool_id=sp_id)
# self._make_subnet(self.fmt, net_resp, None, None,
# subnetpool_id=sp_id)
# # REVISIT(rkukura): Should the address_scopes VRF be used
# # immediately, or not until connected to a router?
# #