[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:
parent
8784548bce
commit
a3ddd689bd
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
# #
|
||||
|
|
Loading…
Reference in New Issue