[apic_aim] External connectivity for multi-scope routing
Manage external connectivity for all VRFs associated with a router.
Change-Id: I6016d85b433093bee960010b57a19ceb4b78b67d
(cherry picked from commit b8e7d2afd4
)
This commit is contained in:
parent
eab6e908ef
commit
2ce34e1a26
|
@ -106,6 +106,12 @@ class DbMixin(object):
|
|||
filter_by(network_id=network_id).
|
||||
one_or_none())
|
||||
|
||||
def _get_network_mappings_for_vrf(self, session, vrf):
|
||||
return (session.query(NetworkMapping).
|
||||
filter_by(vrf_tenant_name=vrf.tenant_name,
|
||||
vrf_name=vrf.name).
|
||||
all())
|
||||
|
||||
def _get_network_bd(self, mapping):
|
||||
return aim_resource.BridgeDomain(
|
||||
tenant_name=mapping.bd_tenant_name,
|
||||
|
|
|
@ -706,8 +706,10 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
old_net) if old_net else None
|
||||
new_net = self.plugin.get_network(context,
|
||||
new_net) if new_net else None
|
||||
self._manage_external_connectivity(context,
|
||||
current, old_net, new_net)
|
||||
vrfs = self._get_vrfs_for_router(session, current['id'])
|
||||
for vrf in vrfs:
|
||||
self._manage_external_connectivity(
|
||||
context, current, old_net, new_net, vrf)
|
||||
|
||||
# Send a port update so that SNAT info may be recalculated for
|
||||
# affected ports in the interfaced subnets.
|
||||
|
@ -821,11 +823,10 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
# stack's scope separately, or at least raise an exception.
|
||||
scope_id = self._get_address_scope_id_for_subnets(context, subnets)
|
||||
|
||||
# Find number of existing interface ports on the router,
|
||||
# excluding the one we are adding.
|
||||
#
|
||||
# REVISIT: Just for this scope?
|
||||
router_intf_count = self._get_router_intf_count(session, router)
|
||||
# Find number of existing interface ports on the router for
|
||||
# this scope, excluding the one we are adding.
|
||||
router_intf_count = self._get_router_intf_count(
|
||||
session, router, scope_id)
|
||||
|
||||
# Find up to two existing router interfaces for this
|
||||
# network. The interface currently being added is not
|
||||
|
@ -1136,10 +1137,10 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
if router_db.gw_port_id:
|
||||
net = self.plugin.get_network(context,
|
||||
router_db.gw_port.network_id)
|
||||
# If this was the last interface-port, then we no longer know
|
||||
# the VRF for this router. So update external-conectivity to
|
||||
# exclude this router.
|
||||
if not self._get_router_intf_count(session, router_db):
|
||||
# If this was the last interface for this VRF for this
|
||||
# router, update external-conectivity to exclude this
|
||||
# router.
|
||||
if not self._get_router_intf_count(session, router_db, scope_id):
|
||||
self._manage_external_connectivity(
|
||||
context, router_db, net, None, old_vrf)
|
||||
|
||||
|
@ -1484,23 +1485,61 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
'vrf': vrf})
|
||||
return vrf
|
||||
|
||||
def _get_vrf_for_router(self, session, router):
|
||||
# REVISIT: Return list of VRFs for when multi-scope routing is
|
||||
# supported?
|
||||
def _get_vrfs_for_router(self, session, router_id):
|
||||
# REVISIT: Persist router/VRF relationship?
|
||||
|
||||
# Find the unique VRFs for the scoped interfaces, accounting
|
||||
# for isomorphic scopes.
|
||||
vrfs = {}
|
||||
scope_dbs = (session.query(as_db.AddressScope).
|
||||
join(models_v2.SubnetPool,
|
||||
models_v2.SubnetPool.address_scope_id ==
|
||||
as_db.AddressScope.id).
|
||||
join(models_v2.Subnet,
|
||||
models_v2.Subnet.subnetpool_id ==
|
||||
models_v2.SubnetPool.id).
|
||||
join(models_v2.IPAllocation).
|
||||
join(models_v2.Port).
|
||||
join(l3_db.RouterPort).
|
||||
filter(l3_db.RouterPort.router_id == router_id).
|
||||
filter(l3_db.RouterPort.port_type ==
|
||||
n_constants.DEVICE_OWNER_ROUTER_INTF).
|
||||
distinct())
|
||||
for scope_db in scope_dbs:
|
||||
vrf = self._get_address_scope_vrf(scope_db.aim_mapping)
|
||||
vrfs[tuple(vrf.identity)] = vrf
|
||||
|
||||
# Find VRF for first unscoped interface.
|
||||
network_db = (session.query(models_v2.Network).
|
||||
join(models_v2.Port).
|
||||
join(models_v2.IPAllocation).
|
||||
join(models_v2.Subnet).
|
||||
outerjoin(models_v2.SubnetPool,
|
||||
models_v2.SubnetPool.id ==
|
||||
models_v2.Subnet.subnetpool_id).
|
||||
join(l3_db.RouterPort).
|
||||
filter(l3_db.RouterPort.router_id == router['id'],
|
||||
filter(l3_db.RouterPort.router_id == router_id,
|
||||
l3_db.RouterPort.port_type ==
|
||||
n_constants.DEVICE_OWNER_ROUTER_INTF).
|
||||
filter(sa.or_(models_v2.Subnet.subnetpool_id.is_(None),
|
||||
models_v2.SubnetPool.address_scope_id.is_(
|
||||
None))).
|
||||
limit(1).
|
||||
first())
|
||||
if network_db:
|
||||
return self._get_network_vrf(network_db.aim_mapping)
|
||||
vrf = self._get_network_vrf(network_db.aim_mapping)
|
||||
vrfs[tuple(vrf.identity)] = vrf
|
||||
|
||||
return vrfs.values()
|
||||
|
||||
# Used by policy driver.
|
||||
def _get_address_scope_ids_for_vrf(self, session, vrf):
|
||||
mappings = self._get_address_scope_mappings_for_vrf(session, vrf)
|
||||
if mappings:
|
||||
return [mapping.scope_id for mapping in mappings]
|
||||
return [mapping.scope_id for mapping in mappings]
|
||||
|
||||
def _get_network_ids_for_vrf(self, session, vrf):
|
||||
mappings = self._get_network_mappings_for_vrf(session, vrf)
|
||||
return [mapping.network_id for mapping in mappings]
|
||||
|
||||
def _get_routers_for_vrf(self, session, vrf):
|
||||
# REVISIT: Persist router/VRF relationship?
|
||||
|
@ -1521,24 +1560,7 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
scope_ids))
|
||||
.distinct())
|
||||
else:
|
||||
# For an unscoped VRF, first find all the routed BDs
|
||||
# referencing the VRF.
|
||||
aim_ctx = aim_context.AimContext(session)
|
||||
bds = self.aim.find(
|
||||
aim_ctx, aim_resource.BridgeDomain,
|
||||
tenant_name=vrf.tenant_name,
|
||||
vrf_name=DEFAULT_VRF_NAME,
|
||||
enable_routing=True)
|
||||
|
||||
# Then find network IDs for those BDs mapped from networks.
|
||||
net_ids = []
|
||||
for bd in bds:
|
||||
id = self.name_mapper.reverse_network(
|
||||
session, bd.name, enforce=False)
|
||||
if id:
|
||||
net_ids.append(id)
|
||||
|
||||
# Then find routers interfaced to those networks.
|
||||
net_ids = self._get_network_ids_for_vrf(session, vrf)
|
||||
rtr_dbs = (session.query(l3_db.Router).
|
||||
join(l3_db.RouterPort).
|
||||
join(models_v2.Port).
|
||||
|
@ -2008,12 +2030,48 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
return aim_resource.Subnet.to_gw_ip_mask(
|
||||
subnet['gateway_ip'], int(subnet['cidr'].split('/')[1]))
|
||||
|
||||
def _get_router_intf_count(self, session, router):
|
||||
return (session.query(l3_db.RouterPort)
|
||||
.filter(l3_db.RouterPort.router_id == router['id'])
|
||||
.filter(l3_db.RouterPort.port_type ==
|
||||
n_constants.DEVICE_OWNER_ROUTER_INTF)
|
||||
.count())
|
||||
def _get_router_intf_count(self, session, router, scope_id=None):
|
||||
if not scope_id:
|
||||
result = (session.query(l3_db.RouterPort).
|
||||
filter(l3_db.RouterPort.router_id == router['id']).
|
||||
filter(l3_db.RouterPort.port_type ==
|
||||
n_constants.DEVICE_OWNER_ROUTER_INTF).
|
||||
count())
|
||||
elif scope_id == NO_ADDR_SCOPE:
|
||||
result = (session.query(l3_db.RouterPort).
|
||||
join(models_v2.Port).
|
||||
join(models_v2.IPAllocation).
|
||||
join(models_v2.Subnet).
|
||||
outerjoin(models_v2.SubnetPool,
|
||||
models_v2.Subnet.subnetpool_id ==
|
||||
models_v2.SubnetPool.id).
|
||||
filter(l3_db.RouterPort.router_id == router['id']).
|
||||
filter(l3_db.RouterPort.port_type ==
|
||||
n_constants.DEVICE_OWNER_ROUTER_INTF).
|
||||
filter(sa.or_(models_v2.Subnet.subnetpool_id.is_(None),
|
||||
models_v2.SubnetPool.address_scope_id.is_(
|
||||
None))).
|
||||
count())
|
||||
else:
|
||||
# Include interfaces for isomorphic scope.
|
||||
mapping = self._get_address_scope_mapping(session, scope_id)
|
||||
vrf = self._get_address_scope_vrf(mapping)
|
||||
mappings = self._get_address_scope_mappings_for_vrf(session, vrf)
|
||||
scope_ids = [mapping.scope_id for mapping in mappings]
|
||||
result = (session.query(l3_db.RouterPort).
|
||||
join(models_v2.Port).
|
||||
join(models_v2.IPAllocation).
|
||||
join(models_v2.Subnet).
|
||||
join(models_v2.SubnetPool,
|
||||
models_v2.Subnet.subnetpool_id ==
|
||||
models_v2.SubnetPool.id).
|
||||
filter(l3_db.RouterPort.router_id == router['id']).
|
||||
filter(l3_db.RouterPort.port_type ==
|
||||
n_constants.DEVICE_OWNER_ROUTER_INTF).
|
||||
filter(models_v2.SubnetPool.address_scope_id.in_(
|
||||
scope_ids)).
|
||||
count())
|
||||
return result
|
||||
|
||||
def _get_address_scope_id_for_subnets(self, context, subnets):
|
||||
# Assuming that all the subnets provided are consistent w.r.t.
|
||||
|
@ -2030,34 +2088,11 @@ class ApicMechanismDriver(api_plus.MechanismDriver,
|
|||
scope_id = (subnetpool_db.address_scope_id or NO_ADDR_SCOPE)
|
||||
return scope_id
|
||||
|
||||
def _get_address_scope_id_for_router(self, session, router):
|
||||
scope_id = NO_ADDR_SCOPE
|
||||
for pool_db in (session.query(models_v2.SubnetPool)
|
||||
.join(models_v2.Subnet,
|
||||
models_v2.Subnet.subnetpool_id ==
|
||||
models_v2.SubnetPool.id)
|
||||
.join(models_v2.IPAllocation)
|
||||
.join(models_v2.Port)
|
||||
.join(l3_db.RouterPort)
|
||||
.filter(l3_db.RouterPort.router_id == router['id'],
|
||||
l3_db.RouterPort.port_type ==
|
||||
n_constants.DEVICE_OWNER_ROUTER_INTF)
|
||||
.filter(models_v2.SubnetPool
|
||||
.address_scope_id.isnot(None))
|
||||
.distinct()):
|
||||
if pool_db.ip_version == 4:
|
||||
scope_id = pool_db.address_scope_id
|
||||
break
|
||||
elif pool_db.ip_version == 6:
|
||||
scope_id = pool_db.address_scope_id
|
||||
return scope_id
|
||||
|
||||
def _manage_external_connectivity(self, context, router, old_network,
|
||||
new_network, vrf=None):
|
||||
new_network, vrf):
|
||||
session = context.session
|
||||
aim_ctx = aim_context.AimContext(db_session=session)
|
||||
|
||||
vrf = vrf or self._get_vrf_for_router(session, router)
|
||||
# Keep only the identity attributes of the VRF so that calls to
|
||||
# nat-library have consistent resource values. This
|
||||
# is mainly required to ease unit-test verification.
|
||||
|
|
|
@ -297,9 +297,16 @@ class ApicAimTestCase(test_address_scope.AddressScopeTestCase,
|
|||
|
||||
class TestAimMapping(ApicAimTestCase):
|
||||
def setUp(self):
|
||||
self.call_wrapper = CallRecordWrapper()
|
||||
self.mock_ns = self.call_wrapper.setUp(
|
||||
nat_strategy.DistributedNatStrategy)
|
||||
self._actual_scopes = {}
|
||||
super(TestAimMapping, self).setUp()
|
||||
|
||||
def tearDown(self):
|
||||
self.call_wrapper.tearDown()
|
||||
super(TestAimMapping, self).tearDown()
|
||||
|
||||
def _get_tenant(self, tenant_name):
|
||||
session = db_api.get_session()
|
||||
aim_ctx = aim_context.AimContext(session)
|
||||
|
@ -1124,16 +1131,32 @@ class TestAimMapping(ApicAimTestCase):
|
|||
mock.call(mock.ANY, tenant)]
|
||||
self._check_call_list(exp_calls, self.driver.aim.delete.call_args_list)
|
||||
|
||||
def test_multi_scope_routing(self):
|
||||
# REVISIT: Re-enable testing with non-isomorphic scopes once
|
||||
# they are supported. Also, test with shared scopes?
|
||||
def test_multi_scope_routing_with_unscoped_pools(self):
|
||||
self._test_multi_scope_routing(True)
|
||||
|
||||
def test_multi_scope_routing_without_unscoped_pools(self):
|
||||
self._test_multi_scope_routing(False)
|
||||
|
||||
def _test_multi_scope_routing(self, use_unscoped_pools):
|
||||
# REVISIT: Re-enable testing with non-isomorphic scopes on the
|
||||
# same network once they are supported. Also, test with shared
|
||||
# scopes?
|
||||
|
||||
# Get default unscoped routed VRF DNs for main and sharing
|
||||
# projects.
|
||||
tenant_aname = self.name_mapper.project(None, self._tenant_id)
|
||||
main_vrf = aim_resource.VRF(
|
||||
tenant_name=tenant_aname, name='DefaultVRF').dn
|
||||
tenant_aname = self.name_mapper.project(None, 'tenant_2')
|
||||
shared_vrf = aim_resource.VRF(
|
||||
tenant_name=tenant_aname, name='DefaultVRF').dn
|
||||
|
||||
# Create a v6 scope and pool.
|
||||
scope6 = self._make_address_scope(
|
||||
self.fmt, 6, name='as6')['address_scope']
|
||||
scope6_id = scope6['id']
|
||||
self._check_address_scope(scope6)
|
||||
scope6_vrf = scope6['apic:distinguished_names']['VRF']
|
||||
scope46i_vrf = scope6['apic:distinguished_names']['VRF']
|
||||
pool6 = self._make_subnetpool(
|
||||
self.fmt, ['2001:db8:1::0/56'], name='sp6',
|
||||
tenant_id=self._tenant_id,
|
||||
|
@ -1142,7 +1165,7 @@ class TestAimMapping(ApicAimTestCase):
|
|||
|
||||
# Create isomorphic v4 scope and pool.
|
||||
scope4i = self._make_address_scope_for_vrf(
|
||||
scope6_vrf, 4, name='as4i')['address_scope']
|
||||
scope46i_vrf, 4, name='as4i')['address_scope']
|
||||
scope4i_id = scope4i['id']
|
||||
self._actual_scopes[scope4i_id] = scope6
|
||||
self._check_address_scope(scope4i)
|
||||
|
@ -1156,11 +1179,26 @@ class TestAimMapping(ApicAimTestCase):
|
|||
self.fmt, 4, name='as4n')['address_scope']
|
||||
scope4n_id = scope4n['id']
|
||||
self._check_address_scope(scope4n)
|
||||
scope4n_vrf = scope4n['apic:distinguished_names']['VRF']
|
||||
pool4n = self._make_subnetpool(
|
||||
self.fmt, ['10.2.0.0/16'], name='sp4n', tenant_id=self._tenant_id,
|
||||
address_scope_id=scope4n_id, default_prefixlen=24)['subnetpool']
|
||||
pool4n_id = pool4n['id']
|
||||
|
||||
# Create unscoped pools if required.
|
||||
if use_unscoped_pools:
|
||||
pool4u = self._make_subnetpool(
|
||||
self.fmt, ['10.3.0.0/16', '10.4.0.0/16'], name='sp4u',
|
||||
tenant_id=self._tenant_id, default_prefixlen=24)['subnetpool']
|
||||
pool4u_id = pool4u['id']
|
||||
pool6u = self._make_subnetpool(
|
||||
self.fmt, ['2001:db8:1::0/56'], name='sp6u',
|
||||
tenant_id=self._tenant_id)['subnetpool']
|
||||
pool6u_id = pool6u['id']
|
||||
else:
|
||||
pool4u_id = None
|
||||
pool6u_id = None
|
||||
|
||||
# Create network with subnets using first v4 scope and v6 scope.
|
||||
net_resp = self._make_network(self.fmt, 'net1', True)
|
||||
net1 = net_resp['network']
|
||||
|
@ -1197,12 +1235,13 @@ class TestAimMapping(ApicAimTestCase):
|
|||
self._check_network(net3)
|
||||
gw43_ip = '10.3.1.1'
|
||||
subnet43 = self._make_subnet(
|
||||
self.fmt, net_resp, gw43_ip, '10.3.1.0/24')['subnet']
|
||||
self.fmt, net_resp, gw43_ip, '10.3.1.0/24',
|
||||
subnetpool_id=pool4u_id)['subnet']
|
||||
self._check_subnet(subnet43, net3, [], [gw43_ip])
|
||||
gw63_ip = '2001:db8:1:3::1'
|
||||
subnet63 = self._make_subnet(
|
||||
self.fmt, net_resp, gw63_ip, '2001:db8:1:3::0/64',
|
||||
ip_version=6)['subnet']
|
||||
ip_version=6, subnetpool_id=pool6u_id)['subnet']
|
||||
self._check_subnet(subnet63, net3, [], [gw63_ip])
|
||||
|
||||
# Create shared network with unscoped subnets.
|
||||
|
@ -1212,14 +1251,27 @@ class TestAimMapping(ApicAimTestCase):
|
|||
self._check_network(net4)
|
||||
gw44_ip = '10.4.1.1'
|
||||
subnet44 = self._make_subnet(
|
||||
self.fmt, net_resp, gw44_ip, '10.4.1.0/24')['subnet']
|
||||
self.fmt, net_resp, gw44_ip, '10.4.1.0/24',
|
||||
subnetpool_id=pool4u_id)['subnet']
|
||||
self._check_subnet(subnet44, net4, [], [gw44_ip])
|
||||
gw64_ip = '2001:db8:1:4::1'
|
||||
subnet64 = self._make_subnet(
|
||||
self.fmt, net_resp, gw64_ip, '2001:db8:1:4::0/64',
|
||||
ip_version=6)['subnet']
|
||||
ip_version=6, subnetpool_id=pool6u_id)['subnet']
|
||||
self._check_subnet(subnet64, net4, [], [gw64_ip])
|
||||
|
||||
# Create two external networks with subnets.
|
||||
ext_net1 = self._make_ext_network(
|
||||
'ext-net1', dn=self.dn_t1_l1_n1)
|
||||
self._make_subnet(
|
||||
self.fmt, {'network': ext_net1}, '100.100.100.1',
|
||||
'100.100.100.0/24')
|
||||
ext_net2 = self._make_ext_network(
|
||||
'ext-net2', dn=self.dn_t1_l2_n2)
|
||||
self._make_subnet(
|
||||
self.fmt, {'network': ext_net2}, '200.200.200.1',
|
||||
'200.200.200.0/24')
|
||||
|
||||
def add(subnet):
|
||||
# REVISIT: Adding by port would work, but adding shared
|
||||
# network interface by subnet fails without admin context.
|
||||
|
@ -1265,154 +1317,218 @@ class TestAimMapping(ApicAimTestCase):
|
|||
router, expected_gw_ips, unexpected_gw_ips, scopes,
|
||||
unscoped_project)
|
||||
|
||||
def check_ns(disconnect_vrf_dns, from_net_dn,
|
||||
connect_vrf_dns, to_net_dn):
|
||||
def check_calls(mock, expected_vrf_dns, expected_net_dn):
|
||||
# REVISIT: We should be able to use assert_has_calls()
|
||||
# since assert_called_once_with() works in
|
||||
# TestExternalConnectivityBase, but args don't seem to
|
||||
# match when they should.
|
||||
vrf_dns = []
|
||||
for args, _ in mock.call_args_list:
|
||||
_, net, vrf = args
|
||||
self.assertEqual(expected_net_dn, net.dn)
|
||||
vrf_dns.append(vrf.dn)
|
||||
self.assertEqual(sorted(expected_vrf_dns), sorted(vrf_dns))
|
||||
|
||||
check_calls(
|
||||
self.mock_ns.disconnect_vrf, disconnect_vrf_dns, from_net_dn)
|
||||
check_calls(
|
||||
self.mock_ns.connect_vrf, connect_vrf_dns, to_net_dn)
|
||||
self.mock_ns.reset_mock()
|
||||
|
||||
# Create router.
|
||||
router = self._make_router(
|
||||
self.fmt, self._tenant_id, 'router1')['router']
|
||||
self.fmt, self._tenant_id, 'router1',
|
||||
external_gateway_info={'network_id': ext_net1['id']})['router']
|
||||
router_id = router['id']
|
||||
check([(net1, [], [subnet4i1, subnet61], None, None),
|
||||
(net2, [], [subnet4n2, subnet62], None, None),
|
||||
(net3, [], [subnet43, subnet63], None, None),
|
||||
(net4, [], [subnet44, subnet64], None, None)],
|
||||
[], None)
|
||||
check_ns([], None, [], None)
|
||||
|
||||
# Add first scoped v4 subnet to router.
|
||||
# Add first scoped v4 subnet to router, which should connect
|
||||
# the isomorphic VRF to ext_net1.
|
||||
add(subnet4i1)
|
||||
check([(net1, [subnet4i1], [subnet61], scope4i, None),
|
||||
(net2, [], [subnet4n2, subnet62], None, None),
|
||||
(net3, [], [subnet43, subnet63], None, None),
|
||||
(net4, [], [subnet44, subnet64], None, None)],
|
||||
[scope4i], None)
|
||||
check_ns([], None, [scope46i_vrf], self.dn_t1_l1_n1)
|
||||
|
||||
# Add first scoped v6 subnet to router.
|
||||
# Add first scoped v6 subnet to router, which should not
|
||||
# effect external connectivity.
|
||||
add(subnet61)
|
||||
check([(net1, [subnet4i1, subnet61], [], scope4i, None),
|
||||
(net2, [], [subnet4n2, subnet62], None, None),
|
||||
(net3, [], [subnet43, subnet63], None, None),
|
||||
(net4, [], [subnet44, subnet64], None, None)],
|
||||
[scope4i, scope6], None)
|
||||
check_ns([], None, [], None)
|
||||
|
||||
# Add first unscoped v6 subnet to router.
|
||||
# Add first unscoped v6 subnet to router, which should connect
|
||||
# the default VRF to ext_net1.
|
||||
add(subnet63)
|
||||
check([(net1, [subnet4i1, subnet61], [], scope4i, None),
|
||||
(net2, [], [subnet4n2, subnet62], None, None),
|
||||
(net3, [subnet63], [subnet43], None, None),
|
||||
(net4, [], [subnet44, subnet64], None, None)],
|
||||
[scope4i, scope6], self._tenant_id)
|
||||
|
||||
# Add second scoped v6 subnet to router.
|
||||
add(subnet62)
|
||||
check([(net1, [subnet4i1, subnet61], [], scope4i, None),
|
||||
(net2, [subnet62], [subnet4n2], scope6, None),
|
||||
(net3, [subnet63], [subnet43], None, None),
|
||||
(net4, [], [subnet44, subnet64], None, None)],
|
||||
[scope4i, scope6], self._tenant_id)
|
||||
check_ns([], None, [main_vrf], self.dn_t1_l1_n1)
|
||||
|
||||
# REVISIT: Enable when non-isomorphic network routing is
|
||||
# supported.
|
||||
#
|
||||
# Add second scoped v4 subnet to router.
|
||||
# add(subnet4n2)
|
||||
# Add second scoped v6 subnet to router, which should connect
|
||||
# its VRF to ext_net1.
|
||||
# add(subnet62)
|
||||
# check([(net1, [subnet4i1, subnet61], [], scope4i, None),
|
||||
# (net2, [subnet4n2, subnet62], [], scope6, None),
|
||||
# (net2, [subnet62], [subnet4n2], scope6, None),
|
||||
# (net3, [subnet63], [subnet43], None, None),
|
||||
# (net4, [], [subnet44, subnet64], None, None)],
|
||||
# [scope4i, scope4n, scope6], self._tenant_id)
|
||||
# [scope4i, scope6], self._tenant_id)
|
||||
|
||||
# Add first unscoped v4 subnet to router.
|
||||
# Add second scoped v4 subnet to router, which should connect
|
||||
# its VRF to ext_net1.
|
||||
add(subnet4n2)
|
||||
check([(net1, [subnet4i1, subnet61], [], scope4i, None),
|
||||
(net2, [subnet4n2], [subnet62], scope4n, None),
|
||||
(net3, [subnet63], [subnet43], None, None),
|
||||
(net4, [], [subnet44, subnet64], None, None)],
|
||||
[scope4i, scope4n, scope6], self._tenant_id)
|
||||
check_ns([], None, [scope4n_vrf], self.dn_t1_l1_n1)
|
||||
|
||||
# Add first unscoped v4 subnet to router, which should not
|
||||
# effect external connectivity.
|
||||
add(subnet43)
|
||||
check([(net1, [subnet4i1, subnet61], [], scope4i, None),
|
||||
(net2, [subnet62], [subnet4n2], scope6, None),
|
||||
(net2, [subnet4n2], [subnet62], scope4n, None),
|
||||
(net3, [subnet43, subnet63], [], None, None),
|
||||
(net4, [], [subnet44, subnet64], None, None)],
|
||||
[scope4i, scope6], self._tenant_id)
|
||||
[scope4i, scope4n, scope6], self._tenant_id)
|
||||
check_ns([], None, [], None)
|
||||
|
||||
# Add shared unscoped v4 subnet to router, which should move
|
||||
# unscoped topology but not scoped topologies.
|
||||
# unscoped topology but not scoped topologies, and should
|
||||
# disconnect tenant's own VRF from ext_net1 and connect
|
||||
# sharing tenant's VRF to ext_net1.
|
||||
add(subnet44)
|
||||
# REVISIT: net2 should be scope4n.
|
||||
check([(net1, [subnet4i1, subnet61], [], scope4i, None),
|
||||
(net2, [subnet62], [subnet4n2], scope6, None),
|
||||
(net2, [subnet4n2], [subnet62], scope4n, None),
|
||||
(net3, [subnet43, subnet63], [], None, 'tenant_2'),
|
||||
(net4, [subnet44], [subnet64], None, 'tenant_2')],
|
||||
[scope4i, scope6], 'tenant_2')
|
||||
[scope4i, scope4n, scope6], 'tenant_2')
|
||||
check_ns([main_vrf], self.dn_t1_l1_n1, [shared_vrf], self.dn_t1_l1_n1)
|
||||
|
||||
# Add shared unscoped v6 subnet to router.
|
||||
# Add shared unscoped v6 subnet to router, which should not
|
||||
# effect external connectivity.
|
||||
add(subnet64)
|
||||
# REVISIT: net2 should be scope4n.
|
||||
check([(net1, [subnet4i1, subnet61], [], scope4i, None),
|
||||
(net2, [subnet62], [subnet4n2], scope6, None),
|
||||
(net2, [subnet4n2], [subnet62], scope4n, None),
|
||||
(net3, [subnet43, subnet63], [], None, 'tenant_2'),
|
||||
(net4, [subnet44, subnet64], [], None, 'tenant_2')],
|
||||
[scope4i, scope6], 'tenant_2')
|
||||
[scope4i, scope4n, scope6], 'tenant_2')
|
||||
check_ns([], None, [], None)
|
||||
|
||||
# Remove first scoped v4 subnet from router.
|
||||
# Update router with new gateway, which should disconnect all
|
||||
# VRFs from ext_net1 and connect them to ext_net2.
|
||||
self._update('routers', router_id,
|
||||
{'router': {'external_gateway_info':
|
||||
{'network_id': ext_net2['id']}}})
|
||||
check([(net1, [subnet4i1, subnet61], [], scope4i, None),
|
||||
(net2, [subnet4n2], [subnet62], scope4n, None),
|
||||
(net3, [subnet43, subnet63], [], None, 'tenant_2'),
|
||||
(net4, [subnet44, subnet64], [], None, 'tenant_2')],
|
||||
[scope4i, scope4n, scope6], 'tenant_2')
|
||||
check_ns([scope46i_vrf, scope4n_vrf, shared_vrf], self.dn_t1_l1_n1,
|
||||
[scope46i_vrf, scope4n_vrf, shared_vrf], self.dn_t1_l2_n2)
|
||||
|
||||
# Remove first scoped v4 subnet from router, which should not
|
||||
# effect external connectivity.
|
||||
remove(subnet4i1)
|
||||
# REVISIT: net1 should be scope6, net2 should be scope4n.
|
||||
check([(net1, [subnet61], [subnet4i1], scope4i, None),
|
||||
(net2, [subnet62], [subnet4n2], scope6, None),
|
||||
check([(net1, [subnet61], [subnet4i1], scope6, None),
|
||||
(net2, [subnet4n2], [subnet62], scope4n, None),
|
||||
(net3, [subnet43, subnet63], [], None, 'tenant_2'),
|
||||
(net4, [subnet44, subnet64], [], None, 'tenant_2')],
|
||||
[scope6], 'tenant_2')
|
||||
[scope4n, scope6], 'tenant_2')
|
||||
check_ns([], None, [], None)
|
||||
|
||||
# Remove first scoped v6 subnet from router.
|
||||
# Remove first scoped v6 subnet from router, which should
|
||||
# disconnect isomorphic VRF from ext_net2.
|
||||
remove(subnet61)
|
||||
check([(net1, [], [subnet4i1, subnet61], None, None),
|
||||
(net2, [subnet62], [subnet4n2], scope6, None),
|
||||
(net2, [subnet4n2], [subnet62], scope4n, None),
|
||||
(net3, [subnet43, subnet63], [], None, 'tenant_2'),
|
||||
(net4, [subnet44, subnet64], [], None, 'tenant_2')],
|
||||
[scope6], 'tenant_2')
|
||||
[scope4n], 'tenant_2')
|
||||
check_ns([scope46i_vrf], self.dn_t1_l2_n2, [], None)
|
||||
|
||||
# Remove shared unscoped v4 subnet from router.
|
||||
# Remove shared unscoped v4 subnet from router, which should
|
||||
# not effect external connecivity.
|
||||
remove(subnet44)
|
||||
check([(net1, [], [subnet4i1, subnet61], None, None),
|
||||
(net2, [subnet62], [subnet4n2], scope6, None),
|
||||
(net2, [subnet4n2], [subnet62], scope4n, None),
|
||||
(net3, [subnet43, subnet63], [], None, 'tenant_2'),
|
||||
(net4, [subnet64], [subnet44], None, 'tenant_2')],
|
||||
[scope6], 'tenant_2')
|
||||
[scope4n], 'tenant_2')
|
||||
check_ns([], None, [], None)
|
||||
|
||||
# Remove shared unscoped v6 subnet from router, which should
|
||||
# move remaining unscoped topology back to original tenant.
|
||||
# move remaining unscoped topology back to original tenant,
|
||||
# and should disconnect sharing tenant's VRF from ext_net2 and
|
||||
# connect tenant's own VRF to ext_net1.
|
||||
remove(subnet64)
|
||||
check([(net1, [], [subnet4i1, subnet61], None, None),
|
||||
(net2, [subnet62], [subnet4n2], scope6, None),
|
||||
(net2, [subnet4n2], [subnet62], scope4n, None),
|
||||
(net3, [subnet43, subnet63], [], None, None),
|
||||
(net4, [], [subnet44, subnet64], None, None)],
|
||||
[scope6], self._tenant_id)
|
||||
[scope4n], self._tenant_id)
|
||||
check_ns([shared_vrf], self.dn_t1_l2_n2, [main_vrf], self.dn_t1_l2_n2)
|
||||
|
||||
# Remove first unscoped v6 subnet from router.
|
||||
# Remove first unscoped v6 subnet from router, which should
|
||||
# not effect external connectivity.
|
||||
remove(subnet63)
|
||||
check([(net1, [], [subnet4i1, subnet61], None, None),
|
||||
(net2, [subnet62], [subnet4n2], scope6, None),
|
||||
(net2, [subnet4n2], [subnet62], scope4n, None),
|
||||
(net3, [subnet43], [subnet63], None, None),
|
||||
(net4, [], [subnet44, subnet64], None, None)],
|
||||
[scope6], self._tenant_id)
|
||||
[scope4n], self._tenant_id)
|
||||
check_ns([], None, [], None)
|
||||
|
||||
# REVISIT: Enable when non-isomorphic network routing is
|
||||
# supported.
|
||||
#
|
||||
# Remove second scoped v4 subnet from router.
|
||||
# remove(subnet4n2)
|
||||
# check([(net1, [], [subnet4i1, subnet61], None, None),
|
||||
# (net2, [subnet62], [subnet4n2], scope6, None),
|
||||
# (net3, [subnet43], [subnet63], None, None),
|
||||
# (net4, [], [subnet44, subnet64], None, None)],
|
||||
# [scope6], self._tenant_id)
|
||||
|
||||
# Remove second unscoped v4 subnet from router.
|
||||
remove(subnet43)
|
||||
# Remove second scoped v4 subnet from router, which should
|
||||
# disconnect its VRF from ext_net2.
|
||||
remove(subnet4n2)
|
||||
check([(net1, [], [subnet4i1, subnet61], None, None),
|
||||
(net2, [subnet62], [subnet4n2], scope6, None),
|
||||
(net3, [], [subnet43, subnet63], None, None),
|
||||
(net2, [], [subnet4n2, subnet62], None, None),
|
||||
(net3, [subnet43], [subnet63], None, None),
|
||||
(net4, [], [subnet44, subnet64], None, None)],
|
||||
[scope6], None)
|
||||
[], self._tenant_id)
|
||||
check_ns([scope4n_vrf], self.dn_t1_l2_n2, [], None)
|
||||
|
||||
# Remove second scoped v6 subnet from router.
|
||||
remove(subnet62)
|
||||
# Remove second unscoped v4 subnet from router, which should
|
||||
# disconnect the default VRF from ext_net2.
|
||||
remove(subnet43)
|
||||
check([(net1, [], [subnet4i1, subnet61], None, None),
|
||||
(net2, [], [subnet4n2, subnet62], None, None),
|
||||
(net3, [], [subnet43, subnet63], None, None),
|
||||
(net4, [], [subnet44, subnet64], None, None)],
|
||||
[], None)
|
||||
check_ns([main_vrf], self.dn_t1_l2_n2, [], None)
|
||||
|
||||
# REVISIT: Enable when non-isomorphic network routing is
|
||||
# supported.
|
||||
#
|
||||
# Remove second scoped v6 subnet from router.
|
||||
# remove(subnet62)
|
||||
# check([(net1, [], [subnet4i1, subnet61], None, None),
|
||||
# (net2, [], [subnet4n2, subnet62], None, None),
|
||||
# (net3, [], [subnet43, subnet63], None, None),
|
||||
# (net4, [], [subnet44, subnet64], None, None)],
|
||||
# [], None)
|
||||
# check_ns(...)
|
||||
|
||||
def test_shared_address_scope(self):
|
||||
# Create shared scope as tenant_1.
|
||||
|
|
Loading…
Reference in New Issue