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 # 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 # from any pool; it will be considered part of the tenant's private address
# space. # space.
# This option is deprecated for removal in the N release. Please refrain from
# using it.
# default_ipv4_subnet_pool = # default_ipv4_subnet_pool =
# Default Subnet Pool to be used for IPv6 subnet-allocation. # Default Subnet Pool to be used for IPv6 subnet-allocation.
# Specifies by UUID the pool to be used in case of subnet-create being # Specifies by UUID the pool to be used in case of subnet-create being
# called without a subnet-pool ID. See the description for # called without a subnet-pool ID. See the description for
# default_ipv4_subnet_pool for more information. # 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 = # default_ipv6_subnet_pool =
# Set to True to enable IPv6 Prefix Delegation for subnet-allocation in a # Set to True to enable IPv6 Prefix Delegation for subnet-allocation in a

View File

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

View File

@ -864,6 +864,13 @@ RESOURCE_ATTRIBUTE_MAP = {
'validate': {'type:non_negative': None}, 'validate': {'type:non_negative': None},
'convert_to': convert_to_int, 'convert_to': convert_to_int,
'is_visible': True}, '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, SHARED: {'allow_post': True,
'allow_put': False, 'allow_put': False,
'default': False, 'default': False,

View File

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

View File

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

View File

@ -601,7 +601,7 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
ipam_subnet) ipam_subnet)
return self._make_subnet_dict(subnet, context=context) 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 """Returns the subnetpool id for this request
If the pool id was explicitly set in the request then that will be 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') 'cidr and subnetpool_id')
raise n_exc.BadRequest(resource='subnets', msg=msg) 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: if ip_version == 4:
return cfg.CONF.default_ipv4_subnet_pool 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 return cfg.CONF.default_ipv6_subnet_pool
def create_subnet(self, context, subnet): 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) subnet['subnet']['cidr'] = '%s/%s' % (net.network, net.prefixlen)
s['tenant_id'] = self._get_tenant_id_for_create(context, s) 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: if subnetpool_id:
self.ipam.validate_pools_with_subnetpool(s) self.ipam.validate_pools_with_subnetpool(s)
if subnetpool_id == constants.IPV6_PD_POOL_ID: 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} 'address_scope_id': address_scope_id}
raise n_exc.IllegalSubnetPoolUpdate(reason=msg) 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): def create_subnetpool(self, context, subnetpool):
"""Create a subnetpool""" """Create a subnetpool"""
@ -936,6 +955,9 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
sp_reader = subnet_alloc.SubnetPoolReader(sp) sp_reader = subnet_alloc.SubnetPoolReader(sp)
if sp_reader.address_scope_id is attributes.ATTR_NOT_SPECIFIED: if sp_reader.address_scope_id is attributes.ATTR_NOT_SPECIFIED:
sp_reader.address_scope_id = None 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, self._validate_address_scope_id(context, sp_reader.address_scope_id,
id, sp_reader.prefixes) id, sp_reader.prefixes)
tenant_id = self._get_tenant_id_for_create(context, sp) 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, sp_reader.default_prefixlen,
'min_prefixlen': sp_reader.min_prefixlen, 'min_prefixlen': sp_reader.min_prefixlen,
'max_prefixlen': sp_reader.max_prefixlen, 'max_prefixlen': sp_reader.max_prefixlen,
'is_default': sp_reader.is_default,
'shared': sp_reader.shared, 'shared': sp_reader.shared,
'default_quota': sp_reader.default_quota, 'default_quota': sp_reader.default_quota,
'address_scope_id': sp_reader.address_scope_id} 'address_scope_id': sp_reader.address_scope_id}
@ -986,8 +1009,8 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
updated['prefixes'] = orig_prefixes updated['prefixes'] = orig_prefixes
for key in ['id', 'name', 'ip_version', 'min_prefixlen', for key in ['id', 'name', 'ip_version', 'min_prefixlen',
'max_prefixlen', 'default_prefixlen', 'shared', 'max_prefixlen', 'default_prefixlen', 'is_default',
'default_quota', 'address_scope_id']: 'shared', 'default_quota', 'address_scope_id']:
self._write_key(key, updated, model, new_pool) self._write_key(key, updated, model, new_pool)
return updated return updated
@ -1008,6 +1031,9 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
updated = self._updated_subnetpool_dict(orig_sp, new_sp) updated = self._updated_subnetpool_dict(orig_sp, new_sp)
updated['tenant_id'] = orig_sp.tenant_id updated['tenant_id'] = orig_sp.tenant_id
reader = subnet_alloc.SubnetPoolReader(updated) 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: if orig_sp.address_scope_id:
self._check_subnetpool_update_allowed(context, id, self._check_subnetpool_update_allowed(context, id,
orig_sp.address_scope_id) orig_sp.address_scope_id)
@ -1044,6 +1070,14 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
page_reverse=page_reverse) page_reverse=page_reverse)
return collection 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): def delete_subnetpool(self, context, id):
"""Delete a subnetpool.""" """Delete a subnetpool."""
with context.session.begin(subtransactions=True): 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) min_prefixlen = sa.Column(sa.Integer, nullable=False)
max_prefixlen = sa.Column(sa.Integer, nullable=False) max_prefixlen = sa.Column(sa.Integer, nullable=False)
shared = sa.Column(sa.Boolean, 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) default_quota = sa.Column(sa.Integer, nullable=True)
hash = sa.Column(sa.String(36), nullable=False, server_default='') hash = sa.Column(sa.String(36), nullable=False, server_default='')
address_scope_id = sa.Column(sa.String(36), nullable=True) 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_id(subnetpool)
self._read_prefix_bounds(subnetpool) self._read_prefix_bounds(subnetpool)
self._read_attrs(subnetpool, self._read_attrs(subnetpool,
['tenant_id', 'name', 'shared']) ['tenant_id', 'name', 'is_default', 'shared'])
self._read_address_scope(subnetpool) self._read_address_scope(subnetpool)
self.subnetpool = {'id': self.id, self.subnetpool = {'id': self.id,
'name': self.name, 'name': self.name,
@ -240,6 +240,7 @@ class SubnetPoolReader(object):
'default_prefixlen': self.default_prefixlen, 'default_prefixlen': self.default_prefixlen,
'default_quota': self.default_quota, 'default_quota': self.default_quota,
'address_scope_id': self.address_scope_id, 'address_scope_id': self.address_scope_id,
'is_default': self.is_default,
'shared': self.shared} 'shared': self.shared}
def _read_attrs(self, subnetpool, keys): def _read_attrs(self, subnetpool, keys):

View File

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

View File

@ -2823,6 +2823,30 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
self.assertEqual(res.status_int, webob.exc.HTTPClientError.code) self.assertEqual(res.status_int, webob.exc.HTTPClientError.code)
def test_create_subnet_only_ip_version_v4(self): 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: with self.network() as network:
tenant_id = network['network']['tenant_id'] tenant_id = network['network']['tenant_id']
subnetpool_prefix = '10.0.0.0/8' subnetpool_prefix = '10.0.0.0/8'
@ -2847,6 +2871,30 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
self.assertEqual(subnetpool_id, subnet['subnetpool_id']) self.assertEqual(subnetpool_id, subnet['subnetpool_id'])
def test_create_subnet_only_ip_version_v6(self): 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: with self.network() as network:
tenant_id = network['network']['tenant_id'] tenant_id = network['network']['tenant_id']
subnetpool_prefix = '2000::/56' subnetpool_prefix = '2000::/56'
@ -2856,9 +2904,9 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
tenant_id=tenant_id, tenant_id=tenant_id,
min_prefixlen='64') as subnetpool: min_prefixlen='64') as subnetpool:
subnetpool_id = subnetpool['subnetpool']['id'] subnetpool_id = subnetpool['subnetpool']['id']
cfg.CONF.set_override('ipv6_pd_enabled', False)
cfg.CONF.set_override('default_ipv6_subnet_pool', cfg.CONF.set_override('default_ipv6_subnet_pool',
subnetpool_id) subnetpool_id)
cfg.CONF.set_override('ipv6_pd_enabled', False)
data = {'subnet': {'network_id': network['network']['id'], data = {'subnet': {'network_id': network['network']['id'],
'ip_version': '6', 'ip_version': '6',
'tenant_id': tenant_id}} 'tenant_id': tenant_id}}
@ -4858,6 +4906,9 @@ class TestSubnetPoolsV2(NeutronDbPluginV2TestCase):
def _validate_max_prefix(self, prefix, subnetpool): def _validate_max_prefix(self, prefix, subnetpool):
self.assertEqual(subnetpool['subnetpool']['max_prefixlen'], prefix) 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): def test_create_subnetpool_empty_prefix_list(self):
self.assertRaises(webob.exc.HTTPClientError, self.assertRaises(webob.exc.HTTPClientError,
self._test_create_subnetpool, self._test_create_subnetpool,
@ -4866,6 +4917,38 @@ class TestSubnetPoolsV2(NeutronDbPluginV2TestCase):
tenant_id=self._tenant_id, tenant_id=self._tenant_id,
min_prefixlen='21') 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): def test_create_subnetpool_ipv4_24_with_defaults(self):
subnet = netaddr.IPNetwork('10.10.10.0/24') subnet = netaddr.IPNetwork('10.10.10.0/24')
subnetpool = self._test_create_subnetpool([subnet.cidr], subnetpool = self._test_create_subnetpool([subnet.cidr],

View File

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