Dual-stack implicit subnetpools
Commit 3565d7496f
added the
implicit subnetpool extension. That commit limited the semantics
to only cover a single address family. This patch extends that
commit to scope the semantics by address family. This means that
there can be an implicit subnetpool per project per address family,
and there can be a shared implicit subnetpool per address family.
Change-Id: I30b3bd5ac92bd4c51927225af0b21ea5fc570d5b
This commit is contained in:
parent
5fa26eb3a6
commit
9c6f103ca1
|
@ -41,17 +41,20 @@ class ImplicitSubnetpool(model_base.BASEV2):
|
|||
class ImplicitSubnetpoolMixin(object):
|
||||
"""Mixin class for implicit subnetpool."""
|
||||
|
||||
def get_implicit_subnetpool_id(self, context, tenant=None):
|
||||
pool = self.get_implicit_subnetpool(context, tenant=tenant)
|
||||
def get_implicit_subnetpool_id(self, context, tenant=None, ip_version="4"):
|
||||
pool = self.get_implicit_subnetpool(context, tenant=tenant,
|
||||
ip_version=ip_version)
|
||||
return pool['id'] if pool else None
|
||||
|
||||
def get_implicit_subnetpool(self, context, tenant=None):
|
||||
pools = self._get_implicit_subnetpools(context, tenant=tenant)
|
||||
def get_implicit_subnetpool(self, context, tenant=None, ip_version="4"):
|
||||
pools = self._get_implicit_subnetpools(context, tenant=tenant,
|
||||
ip_version=ip_version)
|
||||
return pools[0] if pools else None
|
||||
|
||||
def _get_implicit_subnetpools(self, context, tenant=None):
|
||||
def _get_implicit_subnetpools(self, context, tenant=None, ip_version="4"):
|
||||
admin_context = context.elevated()
|
||||
filters = {"is_implicit": [True]}
|
||||
filters = {"is_implicit": [True],
|
||||
"ip_version": ip_version}
|
||||
if tenant:
|
||||
filters["tenant_id"] = [tenant]
|
||||
else:
|
||||
|
@ -110,17 +113,18 @@ class ImplicitSubnetpoolMixin(object):
|
|||
# Verify feasibility. Only one implicit SP must exist per
|
||||
# tenant (or global)
|
||||
msg = _('There can be at most one implicit '
|
||||
'subnetpool per tenant.')
|
||||
'subnetpool per address family per tenant.')
|
||||
self._validate_implicit_subnetpool(
|
||||
context, subnetpool['id'], tenant=subnetpool['tenant_id'],
|
||||
msg=msg)
|
||||
msg=msg, ip_version=subnetpool['ip_version'])
|
||||
if subnetpool['shared']:
|
||||
# Check globally too
|
||||
msg = _('There can be at most one global implicit '
|
||||
'subnetpool.')
|
||||
'subnetpool per address family.')
|
||||
self._validate_implicit_subnetpool(
|
||||
context, subnetpool['id'],
|
||||
tenant=subnetpool['tenant_id'], msg=msg)
|
||||
tenant=None,
|
||||
msg=msg, ip_version=subnetpool['ip_version'])
|
||||
db_obj = self._get_implicit_subnetpool(
|
||||
context, subnetpool['id'])
|
||||
if db_obj:
|
||||
|
@ -132,9 +136,9 @@ class ImplicitSubnetpoolMixin(object):
|
|||
return is_implicit
|
||||
|
||||
def _validate_implicit_subnetpool(self, context, subnetpool_id,
|
||||
tenant=None, msg=None):
|
||||
tenant=None, msg=None, ip_version="4"):
|
||||
current_implicit_sp = self._get_implicit_subnetpools(
|
||||
context, tenant=tenant)
|
||||
context, tenant=tenant, ip_version=ip_version)
|
||||
if len(current_implicit_sp) > 1:
|
||||
raise n_exc.BadRequest(resource='subnetpools', msg=msg)
|
||||
if (len(current_implicit_sp) == 1 and
|
||||
|
|
|
@ -476,8 +476,11 @@ class Ml2PlusPlugin(ml2_plugin.Ml2Plugin,
|
|||
def _get_subnetpool_id(self, context, subnet):
|
||||
# Check for regular subnetpool ID first, then Tenant's implicit,
|
||||
# then global implicit.
|
||||
ip_version = subnet['ip_version']
|
||||
return (
|
||||
super(Ml2PlusPlugin, self)._get_subnetpool_id(context, subnet) or
|
||||
self.get_implicit_subnetpool_id(context,
|
||||
tenant=subnet['tenant_id']) or
|
||||
self.get_implicit_subnetpool_id(context, tenant=None))
|
||||
tenant=subnet['tenant_id'],
|
||||
ip_version=ip_version) or
|
||||
self.get_implicit_subnetpool_id(context, tenant=None,
|
||||
ip_version=ip_version))
|
||||
|
|
|
@ -1780,16 +1780,18 @@ class TestAimMapping(ApicAimTestCase):
|
|||
def test_network_in_address_scope_pre_existing_common_vrf(self):
|
||||
self.test_network_in_address_scope_pre_existing_vrf(common_vrf=True)
|
||||
|
||||
def test_default_subnetpool(self):
|
||||
def _test_default_subnetpool(self, prefix, sn1, gw1, sn2, gw2, sn3, gw3):
|
||||
# Create a non-default non-shared SP
|
||||
subnetpool = self._make_subnetpool(
|
||||
self.fmt, ['10.0.0.0/8'], name='spool1',
|
||||
self.fmt, [prefix], name='spool1',
|
||||
tenant_id='t1')['subnetpool']
|
||||
net = self._make_network(self.fmt, 'pvt-net1', True,
|
||||
tenant_id='t1')['network']
|
||||
sub = self._make_subnet(
|
||||
self.fmt, {'network': net}, '10.0.1.1', '10.0.1.0/24',
|
||||
tenant_id='t1')['subnet']
|
||||
self.fmt, {'network': net,
|
||||
}, gw1, sn1,
|
||||
tenant_id='t1',
|
||||
ip_version=subnetpool['ip_version'])['subnet']
|
||||
self.assertIsNone(sub['subnetpool_id'])
|
||||
# Make SP default
|
||||
data = {'subnetpool': {'is_implicit': True}}
|
||||
|
@ -1800,18 +1802,20 @@ class TestAimMapping(ApicAimTestCase):
|
|||
tenant_id='t1')['network']
|
||||
# Create another subnet
|
||||
sub = self._make_subnet(
|
||||
self.fmt, {'network': net}, '10.0.2.1',
|
||||
'10.0.2.0/24', tenant_id='t1')['subnet']
|
||||
self.fmt, {'network': net}, gw2,
|
||||
sn2, tenant_id='t1',
|
||||
ip_version=subnetpool['ip_version'])['subnet']
|
||||
# This time, SP ID is set
|
||||
self.assertEqual(subnetpool['id'], sub['subnetpool_id'])
|
||||
# Create a shared SP in a different tenant
|
||||
subnetpool_shared = self._make_subnetpool(
|
||||
self.fmt, ['10.0.0.0/8'], name='spool1', is_implicit=True,
|
||||
self.fmt, [prefix], name='spool1', is_implicit=True,
|
||||
shared=True, tenant_id='t2', admin=True)['subnetpool']
|
||||
# A subnet created in T1 still gets the old pool ID
|
||||
sub = self._make_subnet(
|
||||
self.fmt, {'network': net}, '10.0.3.1',
|
||||
'10.0.3.0/24', tenant_id='t1')['subnet']
|
||||
self.fmt, {'network': net}, gw3,
|
||||
sn3, tenant_id='t1',
|
||||
ip_version=subnetpool_shared['ip_version'])['subnet']
|
||||
# This time, SP ID is set
|
||||
self.assertEqual(subnetpool['id'], sub['subnetpool_id'])
|
||||
# Creating a subnet somewhere else, however, will get the SP ID from
|
||||
|
@ -1819,10 +1823,25 @@ class TestAimMapping(ApicAimTestCase):
|
|||
net = self._make_network(self.fmt, 'pvt-net3', True,
|
||||
tenant_id='t3')['network']
|
||||
sub = self._make_subnet(
|
||||
self.fmt, {'network': net}, '10.0.1.1',
|
||||
'10.0.1.0/24', tenant_id='t3')['subnet']
|
||||
self.fmt, {'network': net}, gw1,
|
||||
sn1, tenant_id='t3',
|
||||
ip_version=subnetpool_shared['ip_version'])['subnet']
|
||||
self.assertEqual(subnetpool_shared['id'], sub['subnetpool_id'])
|
||||
|
||||
def test_default_subnetpool(self):
|
||||
# First do a set with the v4 address family
|
||||
self._test_default_subnetpool('10.0.0.0/8',
|
||||
'10.0.1.0/24', '10.0.1.1',
|
||||
'10.0.2.0/24', '10.0.2.1',
|
||||
'10.0.3.0/24', '10.0.3.1')
|
||||
# Do the same test with v6 (v4 still present), using the same tenants
|
||||
# and shared properties. Since they are different address families,
|
||||
# it should not conflict
|
||||
self._test_default_subnetpool('2001:db8::1/56',
|
||||
'2001:db8:0:1::0/64', '2001:db8:0:1::1',
|
||||
'2001:db8:0:2::0/64', '2001:db8:0:2::1',
|
||||
'2001:db8:0:3::0/64', '2001:db8:0:3::1')
|
||||
|
||||
def test_implicit_subnetpool(self):
|
||||
# Create implicit SP (non-shared)
|
||||
sp = self._make_subnetpool(
|
||||
|
@ -1839,10 +1858,16 @@ class TestAimMapping(ApicAimTestCase):
|
|||
'subnetpools', sp['id'],
|
||||
{'subnetpool': {'is_implicit': True}})['subnetpool']
|
||||
self.assertTrue(sp['is_implicit'])
|
||||
# Create another implicit in the same tenant, it will fail
|
||||
# Create another implicit in the same family, same tenant, it will fail
|
||||
self.assertRaises(webob.exc.HTTPClientError, self._make_subnetpool,
|
||||
self.fmt, ['11.0.0.0/8'], name='spool1',
|
||||
tenant_id='t1', is_implicit=True)
|
||||
# Create another implicit in different family, same tenant, it succeeds
|
||||
sp2 = self._make_subnetpool(
|
||||
self.fmt, ['2001:db8:1::0/56'], name='spool1',
|
||||
is_implicit=True, tenant_id='t1')['subnetpool']
|
||||
self.assertTrue(sp2['is_implicit'])
|
||||
|
||||
# Create a normal SP, will succeed
|
||||
sp2 = self._make_subnetpool(
|
||||
self.fmt, ['11.0.0.0/8'], name='spool2',
|
||||
|
@ -1860,6 +1885,19 @@ class TestAimMapping(ApicAimTestCase):
|
|||
self.assertTrue(sp3['is_implicit'])
|
||||
# Update SP shared state is not supported by Neutron
|
||||
|
||||
# Create another shared implicit in the same family, it will fail
|
||||
self.assertRaises(webob.exc.HTTPClientError, self._make_subnetpool,
|
||||
self.fmt, ['12.0.0.0/8'], name='spool3',
|
||||
tenant_id='t3', shared=True,
|
||||
admin=True, is_implicit=True)
|
||||
|
||||
# Create a shared implicit SP in a different address family
|
||||
sp3 = self._make_subnetpool(
|
||||
self.fmt, ['2001:db8:2::0/56'], name='spoolSharedv6',
|
||||
tenant_id='t2', shared=True, admin=True,
|
||||
is_implicit=True)['subnetpool']
|
||||
self.assertTrue(sp3['is_implicit'])
|
||||
|
||||
|
||||
class TestSyncState(ApicAimTestCase):
|
||||
@staticmethod
|
||||
|
|
Loading…
Reference in New Issue