Merge "Replace subnetpool config options with admin-only API"

This commit is contained in:
Jenkins 2015-11-13 16:08:38 +00:00 committed by Gerrit Code Review
commit 772b3e3df3
13 changed files with 188 additions and 14 deletions

View File

@ -162,12 +162,16 @@
# CIDR must be passed to create a subnet and that subnet will not be allocated
# from any pool; it will be considered part of the tenant's private address
# space.
# This option is deprecated for removal in the N release. Please refrain from
# using it.
# default_ipv4_subnet_pool =
# Default Subnet Pool to be used for IPv6 subnet-allocation.
# Specifies by UUID the pool to be used in case of subnet-create being
# called without a subnet-pool ID. See the description for
# default_ipv4_subnet_pool for more information.
# This option is deprecated for removal in the N release. Please refrain from
# using it.
# default_ipv6_subnet_pool =
# Set to True to enable IPv6 Prefix Delegation for subnet-allocation in a

View File

@ -22,8 +22,10 @@
"create_subnetpool": "",
"create_subnetpool:shared": "rule:admin_only",
"create_subnetpool:is_default": "rule:admin_only",
"get_subnetpool": "rule:admin_or_owner or rule:shared_subnetpools",
"update_subnetpool": "rule:admin_or_owner",
"update_subnetpool:is_default": "rule:admin_only",
"delete_subnetpool": "rule:admin_or_owner",
"create_address_scope": "",

View File

@ -864,6 +864,13 @@ RESOURCE_ATTRIBUTE_MAP = {
'validate': {'type:non_negative': None},
'convert_to': convert_to_int,
'is_visible': True},
'is_default': {'allow_post': True,
'allow_put': True,
'default': False,
'convert_to': convert_to_boolean,
'is_visible': True,
'required_by_policy': True,
'enforce_policy': True},
SHARED: {'allow_post': True,
'allow_put': False,
'default': False,

View File

@ -72,12 +72,14 @@ core_opts = [
help=_("Maximum number of fixed ips per port. This option "
"is deprecated and will be removed in the N "
"release.")),
cfg.StrOpt('default_ipv4_subnet_pool',
cfg.StrOpt('default_ipv4_subnet_pool', deprecated_for_removal=True,
help=_("Default IPv4 subnet-pool to be used for automatic "
"subnet CIDR allocation")),
cfg.StrOpt('default_ipv6_subnet_pool',
"subnet CIDR allocation. This option is deprecated for "
"removal in the N release.")),
cfg.StrOpt('default_ipv6_subnet_pool', deprecated_for_removal=True,
help=_("Default IPv6 subnet-pool to be used for automatic "
"subnet CIDR allocation")),
"subnet CIDR allocation. This option is deprecated for "
"removal in the N release.")),
cfg.BoolOpt('ipv6_pd_enabled', default=False,
help=_("Enables IPv6 Prefix Delegation for automatic subnet "
"CIDR allocation")),

View File

@ -146,6 +146,7 @@ class DbBasePluginCommon(common_db_mixin.CommonDbMixin):
'default_prefixlen': default_prefixlen,
'min_prefixlen': min_prefixlen,
'max_prefixlen': max_prefixlen,
'is_default': subnetpool['is_default'],
'shared': subnetpool['shared'],
'prefixes': [prefix['cidr']
for prefix in subnetpool['prefixes']],

View File

@ -601,7 +601,7 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
ipam_subnet)
return self._make_subnet_dict(subnet, context=context)
def _get_subnetpool_id(self, subnet):
def _get_subnetpool_id(self, context, subnet):
"""Returns the subnetpool id for this request
If the pool id was explicitly set in the request then that will be
@ -630,10 +630,18 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
'cidr and subnetpool_id')
raise n_exc.BadRequest(resource='subnets', msg=msg)
if ip_version == 6 and cfg.CONF.ipv6_pd_enabled:
return constants.IPV6_PD_POOL_ID
subnetpool = self.get_default_subnetpool(context, ip_version)
if subnetpool:
return subnetpool['id']
# Until the default_subnet_pool config options are removed in the N
# release, check for them after get_default_subnetpool returns None.
# TODO(john-davidge): Remove after Mitaka release.
if ip_version == 4:
return cfg.CONF.default_ipv4_subnet_pool
if cfg.CONF.ipv6_pd_enabled:
return constants.IPV6_PD_POOL_ID
return cfg.CONF.default_ipv6_subnet_pool
def create_subnet(self, context, subnet):
@ -654,7 +662,7 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
subnet['subnet']['cidr'] = '%s/%s' % (net.network, net.prefixlen)
s['tenant_id'] = self._get_tenant_id_for_create(context, s)
subnetpool_id = self._get_subnetpool_id(s)
subnetpool_id = self._get_subnetpool_id(context, s)
if subnetpool_id:
self.ipam.validate_pools_with_subnetpool(s)
if subnetpool_id == constants.IPV6_PD_POOL_ID:
@ -929,6 +937,17 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
'address_scope_id': address_scope_id}
raise n_exc.IllegalSubnetPoolUpdate(reason=msg)
def _check_default_subnetpool_exists(self, context, ip_version):
"""Check if a default already exists for the given IP version.
There can only be one default subnetpool for each IP family. Raise an
InvalidInput error if a default has already been set.
"""
if self.get_default_subnetpool(context, ip_version):
msg = _("A default subnetpool for this IP family has already "
"been set. Only one default may exist per IP family")
raise n_exc.InvalidInput(error_message=msg)
def create_subnetpool(self, context, subnetpool):
"""Create a subnetpool"""
@ -936,6 +955,9 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
sp_reader = subnet_alloc.SubnetPoolReader(sp)
if sp_reader.address_scope_id is attributes.ATTR_NOT_SPECIFIED:
sp_reader.address_scope_id = None
if sp_reader.is_default:
self._check_default_subnetpool_exists(context,
sp_reader.ip_version)
self._validate_address_scope_id(context, sp_reader.address_scope_id,
id, sp_reader.prefixes)
tenant_id = self._get_tenant_id_for_create(context, sp)
@ -948,6 +970,7 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
sp_reader.default_prefixlen,
'min_prefixlen': sp_reader.min_prefixlen,
'max_prefixlen': sp_reader.max_prefixlen,
'is_default': sp_reader.is_default,
'shared': sp_reader.shared,
'default_quota': sp_reader.default_quota,
'address_scope_id': sp_reader.address_scope_id}
@ -986,8 +1009,8 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
updated['prefixes'] = orig_prefixes
for key in ['id', 'name', 'ip_version', 'min_prefixlen',
'max_prefixlen', 'default_prefixlen', 'shared',
'default_quota', 'address_scope_id']:
'max_prefixlen', 'default_prefixlen', 'is_default',
'shared', 'default_quota', 'address_scope_id']:
self._write_key(key, updated, model, new_pool)
return updated
@ -1008,6 +1031,9 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
updated = self._updated_subnetpool_dict(orig_sp, new_sp)
updated['tenant_id'] = orig_sp.tenant_id
reader = subnet_alloc.SubnetPoolReader(updated)
if reader.is_default and not orig_sp.is_default:
self._check_default_subnetpool_exists(context,
reader.ip_version)
if orig_sp.address_scope_id:
self._check_subnetpool_update_allowed(context, id,
orig_sp.address_scope_id)
@ -1044,6 +1070,14 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
page_reverse=page_reverse)
return collection
def get_default_subnetpool(self, context, ip_version):
"""Retrieve the default subnetpool for the given IP version."""
filters = {'is_default': [True],
'ip_version': [ip_version]}
subnetpool = self.get_subnetpools(context, filters=filters)
if subnetpool:
return subnetpool[0]
def delete_subnetpool(self, context, id):
"""Delete a subnetpool."""
with context.session.begin(subtransactions=True):

View File

@ -1 +1 @@
59cb5b6cf4d
13cfb89f881a

View File

@ -0,0 +1,36 @@
# Copyright 2015 Cisco Systems
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
"""add is_default to subnetpool
Revision ID: 13cfb89f881a
Revises: 59cb5b6cf4d
Create Date: 2015-09-30 15:58:31.170153
"""
# revision identifiers, used by Alembic.
revision = '13cfb89f881a'
down_revision = '59cb5b6cf4d'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('subnetpools',
sa.Column('is_default',
sa.Boolean(),
nullable=False))

View File

@ -236,6 +236,7 @@ class SubnetPool(model_base.BASEV2, HasId, HasTenant):
min_prefixlen = sa.Column(sa.Integer, nullable=False)
max_prefixlen = sa.Column(sa.Integer, nullable=False)
shared = sa.Column(sa.Boolean, nullable=False)
is_default = sa.Column(sa.Boolean, nullable=False)
default_quota = sa.Column(sa.Integer, nullable=True)
hash = sa.Column(sa.String(36), nullable=False, server_default='')
address_scope_id = sa.Column(sa.String(36), nullable=True)

View File

@ -226,7 +226,7 @@ class SubnetPoolReader(object):
self._read_id(subnetpool)
self._read_prefix_bounds(subnetpool)
self._read_attrs(subnetpool,
['tenant_id', 'name', 'shared'])
['tenant_id', 'name', 'is_default', 'shared'])
self._read_address_scope(subnetpool)
self.subnetpool = {'id': self.id,
'name': self.name,
@ -240,6 +240,7 @@ class SubnetPoolReader(object):
'default_prefixlen': self.default_prefixlen,
'default_quota': self.default_quota,
'address_scope_id': self.address_scope_id,
'is_default': self.is_default,
'shared': self.shared}
def _read_attrs(self, subnetpool, keys):

View File

@ -22,8 +22,10 @@
"create_subnetpool": "",
"create_subnetpool:shared": "rule:admin_only",
"create_subnetpool:is_default": "rule:admin_only",
"get_subnetpool": "rule:admin_or_owner or rule:shared_subnetpools",
"update_subnetpool": "rule:admin_or_owner",
"update_subnetpool:is_default": "rule:admin_only",
"delete_subnetpool": "rule:admin_or_owner",
"create_address_scope": "",

View File

@ -2823,6 +2823,30 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
self.assertEqual(res.status_int, webob.exc.HTTPClientError.code)
def test_create_subnet_only_ip_version_v4(self):
with self.network() as network:
tenant_id = network['network']['tenant_id']
subnetpool_prefix = '10.0.0.0/8'
with self.subnetpool(prefixes=[subnetpool_prefix],
admin=True,
name="My subnet pool",
tenant_id=tenant_id,
min_prefixlen='25',
is_default=True) as subnetpool:
subnetpool_id = subnetpool['subnetpool']['id']
data = {'subnet': {'network_id': network['network']['id'],
'ip_version': '4',
'prefixlen': '27',
'tenant_id': tenant_id}}
subnet_req = self.new_create_request('subnets', data)
res = subnet_req.get_response(self.api)
subnet = self.deserialize(self.fmt, res)['subnet']
ip_net = netaddr.IPNetwork(subnet['cidr'])
self.assertIn(ip_net, netaddr.IPNetwork(subnetpool_prefix))
self.assertEqual(27, ip_net.prefixlen)
self.assertEqual(subnetpool_id, subnet['subnetpool_id'])
def test_create_subnet_only_ip_version_v4_old(self):
# TODO(john-davidge): Remove after Mitaka release.
with self.network() as network:
tenant_id = network['network']['tenant_id']
subnetpool_prefix = '10.0.0.0/8'
@ -2847,6 +2871,30 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
self.assertEqual(subnetpool_id, subnet['subnetpool_id'])
def test_create_subnet_only_ip_version_v6(self):
with self.network() as network:
tenant_id = network['network']['tenant_id']
subnetpool_prefix = '2000::/56'
with self.subnetpool(prefixes=[subnetpool_prefix],
admin=True,
name="My ipv6 subnet pool",
tenant_id=tenant_id,
min_prefixlen='64',
is_default=True) as subnetpool:
subnetpool_id = subnetpool['subnetpool']['id']
cfg.CONF.set_override('ipv6_pd_enabled', False)
data = {'subnet': {'network_id': network['network']['id'],
'ip_version': '6',
'tenant_id': tenant_id}}
subnet_req = self.new_create_request('subnets', data)
res = subnet_req.get_response(self.api)
subnet = self.deserialize(self.fmt, res)['subnet']
self.assertEqual(subnetpool_id, subnet['subnetpool_id'])
ip_net = netaddr.IPNetwork(subnet['cidr'])
self.assertIn(ip_net, netaddr.IPNetwork(subnetpool_prefix))
self.assertEqual(64, ip_net.prefixlen)
def test_create_subnet_only_ip_version_v6_old(self):
# TODO(john-davidge): Remove after Mitaka release.
with self.network() as network:
tenant_id = network['network']['tenant_id']
subnetpool_prefix = '2000::/56'
@ -2856,9 +2904,9 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
tenant_id=tenant_id,
min_prefixlen='64') as subnetpool:
subnetpool_id = subnetpool['subnetpool']['id']
cfg.CONF.set_override('ipv6_pd_enabled', False)
cfg.CONF.set_override('default_ipv6_subnet_pool',
subnetpool_id)
cfg.CONF.set_override('ipv6_pd_enabled', False)
data = {'subnet': {'network_id': network['network']['id'],
'ip_version': '6',
'tenant_id': tenant_id}}
@ -4858,6 +4906,9 @@ class TestSubnetPoolsV2(NeutronDbPluginV2TestCase):
def _validate_max_prefix(self, prefix, subnetpool):
self.assertEqual(subnetpool['subnetpool']['max_prefixlen'], prefix)
def _validate_is_default(self, subnetpool):
self.assertTrue(subnetpool['subnetpool']['is_default'])
def test_create_subnetpool_empty_prefix_list(self):
self.assertRaises(webob.exc.HTTPClientError,
self._test_create_subnetpool,
@ -4866,6 +4917,38 @@ class TestSubnetPoolsV2(NeutronDbPluginV2TestCase):
tenant_id=self._tenant_id,
min_prefixlen='21')
def test_create_default_subnetpools(self):
for cidr, min_prefixlen in (['fe80::/48', '64'],
['10.10.10.0/24', '24']):
pool = self._test_create_subnetpool([cidr],
admin=True,
tenant_id=self._tenant_id,
name=self._POOL_NAME,
min_prefixlen=min_prefixlen,
is_default=True)
self._validate_is_default(pool)
def test_cannot_create_multiple_default_subnetpools(self):
for cidr1, cidr2, min_prefixlen in (['fe80::/48', '2001::/48', '64'],
['10.10.10.0/24', '10.10.20.0/24',
'24']):
pool = self._test_create_subnetpool([cidr1],
admin=True,
tenant_id=self._tenant_id,
name=self._POOL_NAME,
min_prefixlen=min_prefixlen,
is_default=True)
self._validate_is_default(pool)
self.assertRaises(webob.exc.HTTPClientError,
self._test_create_subnetpool,
[cidr2],
admin=True,
tenant_id=self._tenant_id,
name=self._POOL_NAME,
min_prefixlen=min_prefixlen,
is_default=True)
def test_create_subnetpool_ipv4_24_with_defaults(self):
subnet = netaddr.IPNetwork('10.10.10.0/24')
subnetpool = self._test_create_subnetpool([subnet.cidr],

View File

@ -43,7 +43,7 @@ class TestSubnetAllocation(testlib_api.SqlTestCase):
max_prefixlen=attributes.ATTR_NOT_SPECIFIED,
default_prefixlen=attributes.ATTR_NOT_SPECIFIED,
default_quota=attributes.ATTR_NOT_SPECIFIED,
shared=False):
shared=False, is_default=False):
subnetpool = {'subnetpool': {'name': name,
'tenant_id': self._tenant_id,
'prefixes': prefix_list,
@ -51,6 +51,7 @@ class TestSubnetAllocation(testlib_api.SqlTestCase):
'max_prefixlen': max_prefixlen,
'default_prefixlen': default_prefixlen,
'shared': shared,
'is_default': is_default,
'default_quota': default_quota}}
return plugin.create_subnetpool(ctx, subnetpool)