Merge "Basic subnetpool CRUD"
This commit is contained in:
commit
906ede185c
|
@ -8,6 +8,7 @@
|
|||
"shared": "field:networks:shared=True",
|
||||
"shared_firewalls": "field:firewalls:shared=True",
|
||||
"shared_firewall_policies": "field:firewall_policies:shared=True",
|
||||
"shared_subnetpools": "field:subnetpools:shared=True",
|
||||
"external": "field:networks:router:external=True",
|
||||
"default": "rule:admin_or_owner",
|
||||
|
||||
|
@ -16,6 +17,12 @@
|
|||
"update_subnet": "rule:admin_or_network_owner",
|
||||
"delete_subnet": "rule:admin_or_network_owner",
|
||||
|
||||
"create_subnetpool": "",
|
||||
"create_subnetpool:shared": "rule:admin_only",
|
||||
"get_subnetpool": "rule:admin_or_owner or rule:shared_subnetpools",
|
||||
"update_subnetpool": "rule:admin_or_owner",
|
||||
"delete_subnetpool": "rule:admin_or_owner",
|
||||
|
||||
"create_network": "",
|
||||
"get_network": "rule:admin_or_owner or rule:shared or rule:external or rule:context_is_advsvc",
|
||||
"get_network:router:external": "rule:regular_user",
|
||||
|
|
|
@ -651,6 +651,8 @@ PORT = 'port'
|
|||
PORTS = '%ss' % PORT
|
||||
SUBNET = 'subnet'
|
||||
SUBNETS = '%ss' % SUBNET
|
||||
SUBNETPOOL = 'subnetpool'
|
||||
SUBNETPOOLS = '%ss' % SUBNETPOOL
|
||||
# Note: a default of ATTR_NOT_SPECIFIED indicates that an
|
||||
# attribute is not required, but will be generated by the plugin
|
||||
# if it is not specified. Particularly, a value of ATTR_NOT_SPECIFIED
|
||||
|
@ -812,6 +814,59 @@ RESOURCE_ATTRIBUTE_MAP = {
|
|||
'is_visible': False,
|
||||
'required_by_policy': True,
|
||||
'enforce_policy': True},
|
||||
},
|
||||
SUBNETPOOLS: {
|
||||
'id': {'allow_post': False,
|
||||
'allow_put': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'is_visible': True,
|
||||
'primary_key': True},
|
||||
'name': {'allow_post': True,
|
||||
'allow_put': True,
|
||||
'validate': {'type:not_empty_string': None},
|
||||
'is_visible': True},
|
||||
'tenant_id': {'allow_post': True,
|
||||
'allow_put': False,
|
||||
'validate': {'type:string': None},
|
||||
'required_by_policy': True,
|
||||
'is_visible': True},
|
||||
'prefixes': {'allow_post': True,
|
||||
'allow_put': True,
|
||||
'validate': {'type:subnet_list': None},
|
||||
'is_visible': True},
|
||||
'ip_version': {'allow_post': False,
|
||||
'allow_put': False,
|
||||
'is_visible': True},
|
||||
'allow_overlap': {'allow_post': True,
|
||||
'allow_put': False,
|
||||
'default': False,
|
||||
'convert_to': convert_to_boolean,
|
||||
'is_visible': True},
|
||||
'default_prefixlen': {'allow_post': True,
|
||||
'allow_put': True,
|
||||
'validate': {'type:non_negative': None},
|
||||
'convert_to': convert_to_int,
|
||||
'default': ATTR_NOT_SPECIFIED,
|
||||
'is_visible': True},
|
||||
'min_prefixlen': {'allow_post': True,
|
||||
'allow_put': True,
|
||||
'default': ATTR_NOT_SPECIFIED,
|
||||
'validate': {'type:non_negative': None},
|
||||
'convert_to': convert_to_int,
|
||||
'is_visible': True},
|
||||
'max_prefixlen': {'allow_post': True,
|
||||
'allow_put': True,
|
||||
'default': ATTR_NOT_SPECIFIED,
|
||||
'validate': {'type:non_negative': None},
|
||||
'convert_to': convert_to_int,
|
||||
'is_visible': True},
|
||||
SHARED: {'allow_post': True,
|
||||
'allow_put': False,
|
||||
'default': False,
|
||||
'convert_to': convert_to_boolean,
|
||||
'is_visible': True,
|
||||
'required_by_policy': True,
|
||||
'enforce_policy': True},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -824,6 +879,7 @@ RESOURCE_FOREIGN_KEYS = {
|
|||
PLURALS = {NETWORKS: NETWORK,
|
||||
PORTS: PORT,
|
||||
SUBNETS: SUBNET,
|
||||
SUBNETPOOLS: SUBNETPOOL,
|
||||
'dns_nameservers': 'dns_nameserver',
|
||||
'host_routes': 'host_route',
|
||||
'allocation_pools': 'allocation_pool',
|
||||
|
|
|
@ -33,6 +33,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
RESOURCES = {'network': 'networks',
|
||||
'subnet': 'subnets',
|
||||
'subnetpool': 'subnetpools',
|
||||
'port': 'ports'}
|
||||
SUB_RESOURCES = {}
|
||||
COLLECTION_ACTIONS = ['index', 'create']
|
||||
|
|
|
@ -79,6 +79,10 @@ class SubnetNotFound(NotFound):
|
|||
message = _("Subnet %(subnet_id)s could not be found")
|
||||
|
||||
|
||||
class SubnetPoolNotFound(NotFound):
|
||||
message = _("Subnet pool %(subnetpool_id)s could not be found")
|
||||
|
||||
|
||||
class PortNotFound(NotFound):
|
||||
message = _("Port %(port_id)s could not be found")
|
||||
|
||||
|
@ -397,3 +401,28 @@ class FirewallInternalDriverError(NeutronException):
|
|||
raise this exception to the agent
|
||||
"""
|
||||
message = _("%(driver)s: Internal driver error.")
|
||||
|
||||
|
||||
class MissingMinSubnetPoolPrefix(BadRequest):
|
||||
message = _("Unspecified minimum subnet pool prefix")
|
||||
|
||||
|
||||
class EmptySubnetPoolPrefixList(BadRequest):
|
||||
message = _("Empty subnet pool prefix list")
|
||||
|
||||
|
||||
class PrefixVersionMismatch(BadRequest):
|
||||
message = _("Cannot mix IPv4 and IPv6 prefixes in a subnet pool")
|
||||
|
||||
|
||||
class UnsupportedMinSubnetPoolPrefix(BadRequest):
|
||||
message = _("Prefix '%(prefix)s' not supported in IPv%(version)s pool")
|
||||
|
||||
|
||||
class IllegalSubnetPoolPrefixBounds(BadRequest):
|
||||
message = _("Illegal prefix bounds: %(prefix_type)s=%(prefixlen)s, "
|
||||
"%(base_prefix_type)s=%(base_prefixlen)s")
|
||||
|
||||
|
||||
class IllegalSubnetPoolPrefixUpdate(BadRequest):
|
||||
message = _("Illegal update to prefixes: %(msg)s")
|
||||
|
|
|
@ -34,6 +34,7 @@ from neutron.db import models_v2
|
|||
from neutron.db import sqlalchemyutils
|
||||
from neutron.extensions import l3
|
||||
from neutron.i18n import _LE, _LI
|
||||
from neutron.ipam import subnet_alloc
|
||||
from neutron import manager
|
||||
from neutron import neutron_plugin_base_v2
|
||||
from neutron.openstack.common import uuidutils
|
||||
|
@ -96,6 +97,16 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
|
|||
raise n_exc.SubnetNotFound(subnet_id=id)
|
||||
return subnet
|
||||
|
||||
def _get_subnetpool(self, context, id):
|
||||
try:
|
||||
return self._get_by_id(context, models_v2.SubnetPool, id)
|
||||
except exc.NoResultFound:
|
||||
raise n_exc.SubnetPoolNotFound(subnetpool_id=id)
|
||||
|
||||
def _get_all_subnetpools(self, context):
|
||||
# NOTE(tidwellr): see note in _get_all_subnets()
|
||||
return context.session.query(models_v2.SubnetPool).all()
|
||||
|
||||
def _get_port(self, context, id):
|
||||
try:
|
||||
port = self._get_by_id(context, models_v2.Port, id)
|
||||
|
@ -846,6 +857,23 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
|
|||
self._apply_dict_extend_functions(attributes.SUBNETS, res, subnet)
|
||||
return self._fields(res, fields)
|
||||
|
||||
def _make_subnetpool_dict(self, subnetpool, fields=None):
|
||||
default_prefixlen = str(subnetpool['default_prefixlen'])
|
||||
min_prefixlen = str(subnetpool['min_prefixlen'])
|
||||
max_prefixlen = str(subnetpool['max_prefixlen'])
|
||||
res = {'id': subnetpool['id'],
|
||||
'name': subnetpool['name'],
|
||||
'tenant_id': subnetpool['tenant_id'],
|
||||
'default_prefixlen': default_prefixlen,
|
||||
'min_prefixlen': min_prefixlen,
|
||||
'max_prefixlen': max_prefixlen,
|
||||
'shared': subnetpool['shared'],
|
||||
'allow_overlap': subnetpool['allow_overlap'],
|
||||
'prefixes': [prefix['cidr']
|
||||
for prefix in subnetpool['prefixes']],
|
||||
'ip_version': subnetpool['ip_version']}
|
||||
return self._fields(res, fields)
|
||||
|
||||
def _make_port_dict(self, port, fields=None,
|
||||
process_extensions=True):
|
||||
res = {"id": port["id"],
|
||||
|
@ -1326,6 +1354,120 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
|
|||
return self._get_collection_count(context, models_v2.Subnet,
|
||||
filters=filters)
|
||||
|
||||
def _create_subnetpool_prefix(self, context, cidr, subnetpool_id):
|
||||
prefix_args = {'cidr': cidr, 'subnetpool_id': subnetpool_id}
|
||||
subnetpool_prefix = models_v2.SubnetPoolPrefix(**prefix_args)
|
||||
context.session.add(subnetpool_prefix)
|
||||
|
||||
def create_subnetpool(self, context, subnetpool):
|
||||
"""Create a subnetpool"""
|
||||
sp = subnetpool['subnetpool']
|
||||
sp_reader = subnet_alloc.SubnetPoolReader(sp)
|
||||
tenant_id = self._get_tenant_id_for_create(context, sp)
|
||||
with context.session.begin(subtransactions=True):
|
||||
pool_args = {'tenant_id': tenant_id,
|
||||
'id': sp_reader.id,
|
||||
'name': sp_reader.name,
|
||||
'ip_version': sp_reader.ip_version,
|
||||
'default_prefixlen':
|
||||
sp_reader.default_prefixlen,
|
||||
'min_prefixlen': sp_reader.min_prefixlen,
|
||||
'max_prefixlen': sp_reader.max_prefixlen,
|
||||
'shared': sp_reader.shared,
|
||||
'allow_overlap': sp_reader.allow_overlap}
|
||||
subnetpool = models_v2.SubnetPool(**pool_args)
|
||||
context.session.add(subnetpool)
|
||||
for prefix in sp_reader.prefixes:
|
||||
self._create_subnetpool_prefix(context,
|
||||
prefix,
|
||||
subnetpool.id)
|
||||
|
||||
return self._make_subnetpool_dict(subnetpool)
|
||||
|
||||
def _update_subnetpool_prefixes(self, context, prefix_list, id):
|
||||
with context.session.begin(subtransactions=True):
|
||||
context.session.query(models_v2.SubnetPoolPrefix).filter_by(
|
||||
subnetpool_id=id).delete()
|
||||
for prefix in prefix_list:
|
||||
model_prefix = models_v2.SubnetPoolPrefix(cidr=prefix,
|
||||
subnetpool_id=id)
|
||||
context.session.add(model_prefix)
|
||||
|
||||
def _updated_subnetpool_dict(self, model, new_pool):
|
||||
updated = {}
|
||||
new_prefixes = new_pool.get('prefixes', attributes.ATTR_NOT_SPECIFIED)
|
||||
orig_prefixes = [str(x.cidr) for x in model['prefixes']]
|
||||
if new_prefixes is not attributes.ATTR_NOT_SPECIFIED:
|
||||
orig_set = netaddr.IPSet(orig_prefixes)
|
||||
new_set = netaddr.IPSet(new_prefixes)
|
||||
if not orig_set.issubset(new_set):
|
||||
msg = _("Existing prefixes must be "
|
||||
"a subset of the new prefixes")
|
||||
raise n_exc.IllegalSubnetPoolPrefixUpdate(msg=msg)
|
||||
new_set.compact()
|
||||
updated['prefixes'] = [str(x.cidr) for x in new_set.iter_cidrs()]
|
||||
else:
|
||||
updated['prefixes'] = orig_prefixes
|
||||
|
||||
for key in ['id', 'name', 'ip_version', 'min_prefixlen',
|
||||
'max_prefixlen', 'default_prefixlen', 'allow_overlap',
|
||||
'shared']:
|
||||
self._write_key(key, updated, model, new_pool)
|
||||
|
||||
return updated
|
||||
|
||||
def _write_key(self, key, update, orig, new_dict):
|
||||
new_val = new_dict.get(key, attributes.ATTR_NOT_SPECIFIED)
|
||||
if new_val is not attributes.ATTR_NOT_SPECIFIED:
|
||||
update[key] = new_dict[key]
|
||||
else:
|
||||
update[key] = orig[key]
|
||||
|
||||
def update_subnetpool(self, context, id, subnetpool):
|
||||
"""Update a subnetpool"""
|
||||
new_sp = subnetpool['subnetpool']
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
orig_sp = self._get_subnetpool(context, id)
|
||||
updated = self._updated_subnetpool_dict(orig_sp, new_sp)
|
||||
updated['tenant_id'] = orig_sp.tenant_id
|
||||
reader = subnet_alloc.SubnetPoolReader(updated)
|
||||
orig_sp.update(self._filter_non_model_columns(
|
||||
reader.subnetpool,
|
||||
models_v2.SubnetPool))
|
||||
self._update_subnetpool_prefixes(context,
|
||||
reader.prefixes,
|
||||
id)
|
||||
for key in ['min_prefixlen', 'max_prefixlen', 'default_prefixlen']:
|
||||
updated['key'] = str(updated[key])
|
||||
|
||||
return updated
|
||||
|
||||
def get_subnetpool(self, context, id, fields=None):
|
||||
"""Retrieve a subnetpool."""
|
||||
subnetpool = self._get_subnetpool(context, id)
|
||||
return self._make_subnetpool_dict(subnetpool, fields)
|
||||
|
||||
def get_subnetpools(self, context, filters=None, fields=None,
|
||||
sorts=None, limit=None, marker=None,
|
||||
page_reverse=False):
|
||||
"""Retrieve list of subnetpools."""
|
||||
marker_obj = self._get_marker_obj(context, 'subnetpool', limit, marker)
|
||||
collection = self._get_collection(context, models_v2.SubnetPool,
|
||||
self._make_subnetpool_dict,
|
||||
filters=filters, fields=fields,
|
||||
sorts=sorts,
|
||||
limit=limit,
|
||||
marker_obj=marker_obj,
|
||||
page_reverse=page_reverse)
|
||||
return collection
|
||||
|
||||
def delete_subnetpool(self, context, id):
|
||||
"""Delete a subnetpool."""
|
||||
with context.session.begin(subtransactions=True):
|
||||
subnetpool = self._get_subnetpool(context, id)
|
||||
context.session.delete(subnetpool)
|
||||
|
||||
def _check_mac_addr_update(self, context, port, new_mac, device_owner):
|
||||
if (device_owner and device_owner.startswith('network:')):
|
||||
raise n_exc.UnsupportedPortDeviceOwner(
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
# Copyright 2015 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""Initial operations for subnetpools
|
||||
|
||||
Revision ID: 51c54792158e
|
||||
Revises: 341ee8a4ccb5
|
||||
Create Date: 2015-01-27 13:07:50.713838
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '51c54792158e'
|
||||
down_revision = '1955efc66455'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table('subnetpools',
|
||||
sa.Column('tenant_id',
|
||||
sa.String(length=255),
|
||||
nullable=True,
|
||||
index=True),
|
||||
sa.Column('id', sa.String(length=36), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=True),
|
||||
sa.Column('ip_version', sa.Integer(), nullable=False),
|
||||
sa.Column('default_prefixlen',
|
||||
sa.Integer(),
|
||||
nullable=False),
|
||||
sa.Column('min_prefixlen', sa.Integer(), nullable=False),
|
||||
sa.Column('max_prefixlen', sa.Integer(), nullable=False),
|
||||
sa.Column('shared', sa.Boolean(), nullable=False),
|
||||
sa.Column('allow_overlap', sa.Boolean(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'))
|
||||
op.create_table('subnetpoolprefixes',
|
||||
sa.Column('cidr', sa.String(length=64), nullable=False),
|
||||
sa.Column('subnetpool_id',
|
||||
sa.String(length=36),
|
||||
nullable=False),
|
||||
sa.ForeignKeyConstraint(['subnetpool_id'],
|
||||
['subnetpools.id'],
|
||||
ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('cidr', 'subnetpool_id'))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('subnetpoolprefixes')
|
||||
op.drop_table('subnetpools')
|
|
@ -1 +1 @@
|
|||
1955efc66455
|
||||
51c54792158e
|
||||
|
|
|
@ -209,6 +209,36 @@ class Subnet(model_base.BASEV2, HasId, HasTenant):
|
|||
name='ipv6_address_modes'), nullable=True)
|
||||
|
||||
|
||||
class SubnetPoolPrefix(model_base.BASEV2):
|
||||
"""Represents a neutron subnet pool prefix
|
||||
"""
|
||||
|
||||
__tablename__ = 'subnetpoolprefixes'
|
||||
|
||||
cidr = sa.Column(sa.String(64), nullable=False, primary_key=True)
|
||||
subnetpool_id = sa.Column(sa.String(36),
|
||||
sa.ForeignKey('subnetpools.id'),
|
||||
nullable=False,
|
||||
primary_key=True)
|
||||
|
||||
|
||||
class SubnetPool(model_base.BASEV2, HasId, HasTenant):
|
||||
"""Represents a neutron subnet pool.
|
||||
"""
|
||||
|
||||
name = sa.Column(sa.String(255))
|
||||
ip_version = sa.Column(sa.Integer, nullable=False)
|
||||
default_prefixlen = sa.Column(sa.Integer, nullable=False)
|
||||
min_prefixlen = sa.Column(sa.Integer, nullable=False)
|
||||
max_prefixlen = sa.Column(sa.Integer, nullable=False)
|
||||
shared = sa.Column(sa.Boolean, nullable=False)
|
||||
allow_overlap = sa.Column(sa.Boolean, nullable=False)
|
||||
prefixes = orm.relationship(SubnetPoolPrefix,
|
||||
backref='subnetpools',
|
||||
cascade='all, delete, delete-orphan',
|
||||
lazy='joined')
|
||||
|
||||
|
||||
class Network(model_base.BASEV2, HasId, HasTenant):
|
||||
"""Represents a v2 neutron network."""
|
||||
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
# Copyright (c) 2015 Hewlett-Packard Co.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import netaddr
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.common import constants
|
||||
from neutron.common import exceptions as n_exc
|
||||
from neutron.openstack.common import uuidutils
|
||||
|
||||
|
||||
class SubnetPoolReader(object):
|
||||
'''Class to assist with reading a subnetpool, loading defaults, and
|
||||
inferring IP version from prefix list. Provides a common way of
|
||||
reading a stored model or a create request with defaultable attributes.
|
||||
'''
|
||||
MIN_PREFIX_TYPE = 'min'
|
||||
MAX_PREFIX_TYPE = 'max'
|
||||
DEFAULT_PREFIX_TYPE = 'default'
|
||||
|
||||
_sp_helper = None
|
||||
|
||||
def __init__(self, subnetpool):
|
||||
self._read_prefix_list(subnetpool)
|
||||
self._sp_helper = SubnetPoolHelper()
|
||||
self._read_id(subnetpool)
|
||||
self._read_prefix_bounds(subnetpool)
|
||||
self._read_attrs(subnetpool,
|
||||
['tenant_id', 'name', 'allow_overlap', 'shared'])
|
||||
self.subnetpool = {'id': self.id,
|
||||
'name': self.name,
|
||||
'tenant_id': self.tenant_id,
|
||||
'prefixes': self.prefixes,
|
||||
'min_prefix': self.min_prefix,
|
||||
'min_prefixlen': self.min_prefixlen,
|
||||
'max_prefix': self.max_prefix,
|
||||
'max_prefixlen': self.max_prefixlen,
|
||||
'default_prefix': self.default_prefix,
|
||||
'default_prefixlen': self.default_prefixlen,
|
||||
'allow_overlap': self.allow_overlap,
|
||||
'shared': self.shared}
|
||||
|
||||
def _read_attrs(self, subnetpool, keys):
|
||||
for key in keys:
|
||||
setattr(self, key, subnetpool[key])
|
||||
|
||||
def _ip_version_from_cidr(self, cidr):
|
||||
return netaddr.IPNetwork(cidr).version
|
||||
|
||||
def _prefixlen_from_cidr(self, cidr):
|
||||
return netaddr.IPNetwork(cidr).prefixlen
|
||||
|
||||
def _read_id(self, subnetpool):
|
||||
id = subnetpool.get('id', attributes.ATTR_NOT_SPECIFIED)
|
||||
if id is attributes.ATTR_NOT_SPECIFIED:
|
||||
id = uuidutils.generate_uuid()
|
||||
self.id = id
|
||||
|
||||
def _read_prefix_bounds(self, subnetpool):
|
||||
ip_version = self.ip_version
|
||||
default_min = self._sp_helper.default_min_prefixlen(ip_version)
|
||||
default_max = self._sp_helper.default_max_prefixlen(ip_version)
|
||||
|
||||
self._read_prefix_bound(self.MIN_PREFIX_TYPE,
|
||||
subnetpool,
|
||||
default_min)
|
||||
self._read_prefix_bound(self.MAX_PREFIX_TYPE,
|
||||
subnetpool,
|
||||
default_max)
|
||||
self._read_prefix_bound(self.DEFAULT_PREFIX_TYPE,
|
||||
subnetpool,
|
||||
self.min_prefixlen)
|
||||
|
||||
self._sp_helper.validate_min_prefixlen(self.min_prefixlen,
|
||||
self.max_prefixlen)
|
||||
self._sp_helper.validate_max_prefixlen(self.max_prefixlen,
|
||||
ip_version)
|
||||
self._sp_helper.validate_default_prefixlen(self.min_prefixlen,
|
||||
self.max_prefixlen,
|
||||
self.default_prefixlen)
|
||||
|
||||
def _read_prefix_bound(self, type, subnetpool, default_bound=None):
|
||||
prefixlen_attr = type + '_prefixlen'
|
||||
prefix_attr = type + '_prefix'
|
||||
prefixlen = subnetpool.get(prefixlen_attr,
|
||||
attributes.ATTR_NOT_SPECIFIED)
|
||||
wildcard = self._sp_helper.wildcard(self.ip_version)
|
||||
|
||||
if prefixlen is attributes.ATTR_NOT_SPECIFIED and default_bound:
|
||||
prefixlen = default_bound
|
||||
|
||||
if prefixlen is not attributes.ATTR_NOT_SPECIFIED:
|
||||
prefix_cidr = '/'.join((wildcard,
|
||||
str(prefixlen)))
|
||||
setattr(self, prefix_attr, prefix_cidr)
|
||||
setattr(self, prefixlen_attr, prefixlen)
|
||||
|
||||
def _read_prefix_list(self, subnetpool):
|
||||
prefix_list = subnetpool['prefixes']
|
||||
if not prefix_list:
|
||||
raise n_exc.EmptySubnetPoolPrefixList()
|
||||
|
||||
ip_version = None
|
||||
for prefix in prefix_list:
|
||||
if not ip_version:
|
||||
ip_version = netaddr.IPNetwork(prefix).version
|
||||
elif netaddr.IPNetwork(prefix).version != ip_version:
|
||||
raise n_exc.PrefixVersionMismatch()
|
||||
|
||||
self.ip_version = ip_version
|
||||
self.prefixes = self._compact_subnetpool_prefix_list(prefix_list)
|
||||
|
||||
def _compact_subnetpool_prefix_list(self, prefix_list):
|
||||
"""Compact any overlapping prefixes in prefix_list and return the
|
||||
result
|
||||
"""
|
||||
ip_set = netaddr.IPSet()
|
||||
for prefix in prefix_list:
|
||||
ip_set.add(netaddr.IPNetwork(prefix))
|
||||
ip_set.compact()
|
||||
return [str(x.cidr) for x in ip_set.iter_cidrs()]
|
||||
|
||||
|
||||
class SubnetPoolHelper(object):
|
||||
|
||||
PREFIX_VERSION_INFO = {4: {'max_prefixlen': constants.IPv4_BITS,
|
||||
'wildcard': '0.0.0.0',
|
||||
'default_min_prefixlen': 8},
|
||||
6: {'max_prefixlen': constants.IPv6_BITS,
|
||||
'wildcard': '::',
|
||||
'default_min_prefixlen': 64}}
|
||||
|
||||
def validate_min_prefixlen(self, min_prefixlen, max_prefixlen):
|
||||
if min_prefixlen < 0:
|
||||
raise n_exc.UnsupportedMinSubnetPoolPrefix(prefix=min_prefixlen,
|
||||
version=4)
|
||||
if min_prefixlen > max_prefixlen:
|
||||
raise n_exc.IllegalSubnetPoolPrefixBounds(
|
||||
prefix_type='min_prefixlen',
|
||||
prefixlen=min_prefixlen,
|
||||
base_prefix_type='max_prefixlen',
|
||||
base_prefixlen=max_prefixlen)
|
||||
|
||||
def validate_max_prefixlen(self, prefixlen, ip_version):
|
||||
max = self.PREFIX_VERSION_INFO[ip_version]['max_prefixlen']
|
||||
if prefixlen > max:
|
||||
raise n_exc.IllegalSubnetPoolPrefixBounds(
|
||||
prefix_type='max_prefixlen',
|
||||
prefixlen=prefixlen,
|
||||
base_prefix_type='ip_version_max',
|
||||
base_prefixlen=max)
|
||||
|
||||
def validate_default_prefixlen(self,
|
||||
min_prefixlen,
|
||||
max_prefixlen,
|
||||
default_prefixlen):
|
||||
if default_prefixlen < min_prefixlen:
|
||||
raise n_exc.IllegalSubnetPoolPrefixBounds(
|
||||
prefix_type='default_prefixlen',
|
||||
prefixlen=default_prefixlen,
|
||||
base_prefix_type='min_prefixlen',
|
||||
base_prefixlen=min_prefixlen)
|
||||
if default_prefixlen > max_prefixlen:
|
||||
raise n_exc.IllegalSubnetPoolPrefixBounds(
|
||||
prefix_type='default_prefixlen',
|
||||
prefixlen=default_prefixlen,
|
||||
base_prefix_type='max_prefixlen',
|
||||
base_prefixlen=max_prefixlen)
|
||||
|
||||
def wildcard(self, ip_version):
|
||||
return self.PREFIX_VERSION_INFO[ip_version]['wildcard']
|
||||
|
||||
def default_max_prefixlen(self, ip_version):
|
||||
return self.PREFIX_VERSION_INFO[ip_version]['max_prefixlen']
|
||||
|
||||
def default_min_prefixlen(self, ip_version):
|
||||
return self.PREFIX_VERSION_INFO[ip_version]['default_min_prefixlen']
|
|
@ -127,6 +127,45 @@ class NeutronPluginBaseV2(object):
|
|||
"""
|
||||
pass
|
||||
|
||||
def create_subnetpool(self, context, subnetpool):
|
||||
"""Create a subnet pool.
|
||||
|
||||
:param context: neutron api request context
|
||||
:param subnetpool: Dictionary representing the subnetpool to create.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def update_subnetpool(self, context, id, subnetpool):
|
||||
"""Update a subnet pool.
|
||||
|
||||
:param context: neutron api request context
|
||||
:param subnetpool: Dictionary representing the subnetpool attributes
|
||||
to update.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_subnetpool(self, context, id, fields=None):
|
||||
"""Show a subnet pool.
|
||||
|
||||
:param context: neutron api request context
|
||||
:param id: The UUID of the subnetpool to show.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_subnetpools(self, context, filters=None, fields=None,
|
||||
sorts=None, limit=None, marker=None,
|
||||
page_reverse=False):
|
||||
"""Retrieve list of subnet pools."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def delete_subnetpool(self, context, id):
|
||||
"""Delete a subnet pool.
|
||||
|
||||
:param context: neutron api request context
|
||||
:param id: The UUID of the subnet pool to delete.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_network(self, context, network):
|
||||
"""Create a network.
|
||||
|
|
|
@ -18,6 +18,7 @@ import copy
|
|||
import itertools
|
||||
|
||||
import mock
|
||||
import netaddr
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import importutils
|
||||
from testtools import matchers
|
||||
|
@ -357,6 +358,23 @@ class NeutronDbPluginV2TestCase(testlib_api.WebTestCase,
|
|||
kwargs.update({'override': overrides})
|
||||
return self._create_bulk(fmt, number, 'subnet', base_data, **kwargs)
|
||||
|
||||
def _create_subnetpool(self, fmt, prefixes,
|
||||
expected_res_status=None, admin=False, **kwargs):
|
||||
subnetpool = {'subnetpool': {'prefixes': prefixes}}
|
||||
for k, v in kwargs.items():
|
||||
subnetpool['subnetpool'][k] = str(v)
|
||||
|
||||
api = self._api_for_resource('subnetpools')
|
||||
subnetpools_req = self.new_create_request('subnetpools',
|
||||
subnetpool, fmt)
|
||||
if not admin:
|
||||
neutron_context = context.Context('', kwargs['tenant_id'])
|
||||
subnetpools_req.environ['neutron.context'] = neutron_context
|
||||
subnetpool_res = subnetpools_req.get_response(api)
|
||||
if expected_res_status:
|
||||
self.assertEqual(subnetpool_res.status_int, expected_res_status)
|
||||
return subnetpool_res
|
||||
|
||||
def _create_port(self, fmt, net_id, expected_res_status=None,
|
||||
arg_list=None, **kwargs):
|
||||
data = {'port': {'network_id': net_id,
|
||||
|
@ -447,6 +465,18 @@ class NeutronDbPluginV2TestCase(testlib_api.WebTestCase,
|
|||
raise webob.exc.HTTPClientError(code=res.status_int)
|
||||
return self.deserialize(fmt, res)
|
||||
|
||||
def _make_subnetpool(self, fmt, prefixes, admin=False, **kwargs):
|
||||
res = self._create_subnetpool(fmt,
|
||||
prefixes,
|
||||
None,
|
||||
admin,
|
||||
**kwargs)
|
||||
# Things can go wrong - raise HTTP exc with res code only
|
||||
# so it can be caught by unit tests
|
||||
if res.status_int >= webob.exc.HTTPClientError.code:
|
||||
raise webob.exc.HTTPClientError(code=res.status_int)
|
||||
return self.deserialize(fmt, res)
|
||||
|
||||
def _make_port(self, fmt, net_id, expected_res_status=None, **kwargs):
|
||||
res = self._create_port(fmt, net_id, expected_res_status, **kwargs)
|
||||
# Things can go wrong - raise HTTP exc with res code only
|
||||
|
@ -456,7 +486,7 @@ class NeutronDbPluginV2TestCase(testlib_api.WebTestCase,
|
|||
return self.deserialize(fmt, res)
|
||||
|
||||
def _api_for_resource(self, resource):
|
||||
if resource in ['networks', 'subnets', 'ports']:
|
||||
if resource in ['networks', 'subnets', 'ports', 'subnetpools']:
|
||||
return self.api
|
||||
else:
|
||||
return self.ext_api
|
||||
|
@ -538,8 +568,8 @@ class NeutronDbPluginV2TestCase(testlib_api.WebTestCase,
|
|||
neutron_context=neutron_context,
|
||||
query_params=query_params)
|
||||
resource = resource.replace('-', '_')
|
||||
self.assertEqual(sorted([i['id'] for i in res['%ss' % resource]]),
|
||||
sorted([i[resource]['id'] for i in items]))
|
||||
self.assertItemsEqual([i['id'] for i in res['%ss' % resource]],
|
||||
[i[resource]['id'] for i in items])
|
||||
|
||||
@contextlib.contextmanager
|
||||
def network(self, name='net1',
|
||||
|
@ -578,6 +608,14 @@ class NeutronDbPluginV2TestCase(testlib_api.WebTestCase,
|
|||
ipv6_address_mode=ipv6_address_mode)
|
||||
yield subnet
|
||||
|
||||
@contextlib.contextmanager
|
||||
def subnetpool(self, prefixes, admin=False, **kwargs):
|
||||
subnetpool = self._make_subnetpool(self.fmt,
|
||||
prefixes,
|
||||
admin,
|
||||
**kwargs)
|
||||
yield subnetpool
|
||||
|
||||
@contextlib.contextmanager
|
||||
def port(self, subnet=None, fmt=None, **kwargs):
|
||||
with optional_ctx(subnet, self.subnet) as subnet_to_use:
|
||||
|
@ -676,6 +714,27 @@ class NeutronDbPluginV2TestCase(testlib_api.WebTestCase,
|
|||
expected_res.reverse()
|
||||
self.assertEqual(expected_res, [n['id'] for n in item_res])
|
||||
|
||||
def _compare_resource(self, observed_res, expected_res, res_name):
|
||||
'''
|
||||
Compare the observed and expected resources (ie compare subnets)
|
||||
'''
|
||||
for k in expected_res:
|
||||
self.assertIn(k, observed_res[res_name])
|
||||
if isinstance(expected_res[k], list):
|
||||
self.assertEqual(sorted(observed_res[res_name][k]),
|
||||
sorted(expected_res[k]))
|
||||
else:
|
||||
self.assertEqual(observed_res[res_name][k], expected_res[k])
|
||||
|
||||
def _validate_resource(self, resource, keys, res_name):
|
||||
for k in keys:
|
||||
self.assertIn(k, resource[res_name])
|
||||
if isinstance(keys[k], list):
|
||||
self.assertEqual(sorted(resource[res_name][k]),
|
||||
sorted(keys[k]))
|
||||
else:
|
||||
self.assertEqual(resource[res_name][k], keys[k])
|
||||
|
||||
|
||||
class TestBasicGet(NeutronDbPluginV2TestCase):
|
||||
|
||||
|
@ -2674,22 +2733,10 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
|
|||
keys.setdefault('enable_dhcp', True)
|
||||
with self.subnet(network=network, **keys) as subnet:
|
||||
# verify the response has each key with the correct value
|
||||
for k in keys:
|
||||
self.assertIn(k, subnet['subnet'])
|
||||
if isinstance(keys[k], list):
|
||||
self.assertEqual(sorted(subnet['subnet'][k]),
|
||||
sorted(keys[k]))
|
||||
else:
|
||||
self.assertEqual(subnet['subnet'][k], keys[k])
|
||||
self._validate_resource(subnet, keys, 'subnet')
|
||||
# verify the configured validations are correct
|
||||
if expected:
|
||||
for k in expected:
|
||||
self.assertIn(k, subnet['subnet'])
|
||||
if isinstance(expected[k], list):
|
||||
self.assertEqual(sorted(subnet['subnet'][k]),
|
||||
sorted(expected[k]))
|
||||
else:
|
||||
self.assertEqual(subnet['subnet'][k], expected[k])
|
||||
self._compare_resource(subnet, expected, 'subnet')
|
||||
self._delete('subnets', subnet['subnet']['id'])
|
||||
return subnet
|
||||
|
||||
|
@ -4385,6 +4432,378 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
|
|||
expected_code=webob.exc.HTTPConflict.code)
|
||||
|
||||
|
||||
class TestSubnetPoolsV2(NeutronDbPluginV2TestCase):
|
||||
|
||||
_POOL_NAME = 'test-pool'
|
||||
|
||||
def _test_create_subnetpool(self, prefixes, expected=None,
|
||||
admin=False, **kwargs):
|
||||
keys = kwargs.copy()
|
||||
keys.setdefault('tenant_id', self._tenant_id)
|
||||
with self.subnetpool(prefixes, admin, **keys) as subnetpool:
|
||||
self._validate_resource(subnetpool, keys, 'subnetpool')
|
||||
if expected:
|
||||
self._compare_resource(subnetpool, expected, 'subnetpool')
|
||||
return subnetpool
|
||||
|
||||
def _validate_default_prefix(self, prefix, subnetpool):
|
||||
self.assertEqual(subnetpool['subnetpool']['default_prefixlen'], prefix)
|
||||
|
||||
def _validate_min_prefix(self, prefix, subnetpool):
|
||||
self.assertEqual(subnetpool['subnetpool']['min_prefixlen'], prefix)
|
||||
|
||||
def _validate_max_prefix(self, prefix, subnetpool):
|
||||
self.assertEqual(subnetpool['subnetpool']['max_prefixlen'], prefix)
|
||||
|
||||
def test_create_subnetpool_empty_prefix_list(self):
|
||||
self.assertRaises(webob.exc.HTTPClientError,
|
||||
self._test_create_subnetpool,
|
||||
[],
|
||||
name=self._POOL_NAME,
|
||||
tenant_id=self._tenant_id,
|
||||
min_prefixlen='21')
|
||||
|
||||
def test_create_subnetpool_ipv4_24_with_defaults(self):
|
||||
subnet = netaddr.IPNetwork('10.10.10.0/24')
|
||||
subnetpool = self._test_create_subnetpool([subnet.cidr],
|
||||
name=self._POOL_NAME,
|
||||
tenant_id=self._tenant_id,
|
||||
min_prefixlen='21')
|
||||
self._validate_default_prefix('21', subnetpool)
|
||||
self._validate_min_prefix('21', subnetpool)
|
||||
|
||||
def test_create_subnetpool_ipv4_21_with_defaults(self):
|
||||
subnet = netaddr.IPNetwork('10.10.10.0/21')
|
||||
subnetpool = self._test_create_subnetpool([subnet.cidr],
|
||||
name=self._POOL_NAME,
|
||||
tenant_id=self._tenant_id,
|
||||
min_prefixlen='21')
|
||||
self._validate_default_prefix('21', subnetpool)
|
||||
self._validate_min_prefix('21', subnetpool)
|
||||
|
||||
def test_create_subnetpool_ipv4_default_prefix_too_small(self):
|
||||
subnet = netaddr.IPNetwork('10.10.10.0/21')
|
||||
self.assertRaises(webob.exc.HTTPClientError,
|
||||
self._test_create_subnetpool,
|
||||
[subnet.cidr],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='21',
|
||||
default_prefixlen='20')
|
||||
|
||||
def test_create_subnetpool_ipv4_default_prefix_too_large(self):
|
||||
subnet = netaddr.IPNetwork('10.10.10.0/21')
|
||||
self.assertRaises(webob.exc.HTTPClientError,
|
||||
self._test_create_subnetpool,
|
||||
[subnet.cidr],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
max_prefixlen=24,
|
||||
default_prefixlen='32')
|
||||
|
||||
def test_create_subnetpool_ipv4_default_prefix_bounds(self):
|
||||
subnet = netaddr.IPNetwork('10.10.10.0/21')
|
||||
subnetpool = self._test_create_subnetpool([subnet.cidr],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME)
|
||||
self._validate_min_prefix('8', subnetpool)
|
||||
self._validate_default_prefix('8', subnetpool)
|
||||
self._validate_max_prefix('32', subnetpool)
|
||||
|
||||
def test_create_subnetpool_ipv6_default_prefix_bounds(self):
|
||||
subnet = netaddr.IPNetwork('fe80::/48')
|
||||
subnetpool = self._test_create_subnetpool([subnet.cidr],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME)
|
||||
self._validate_min_prefix('64', subnetpool)
|
||||
self._validate_default_prefix('64', subnetpool)
|
||||
self._validate_max_prefix('128', subnetpool)
|
||||
|
||||
def test_create_subnetpool_ipv4_supported_default_prefix(self):
|
||||
subnet = netaddr.IPNetwork('10.10.10.0/21')
|
||||
subnetpool = self._test_create_subnetpool([subnet.cidr],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='21',
|
||||
default_prefixlen='26')
|
||||
self._validate_default_prefix('26', subnetpool)
|
||||
|
||||
def test_create_subnetpool_ipv4_supported_min_prefix(self):
|
||||
subnet = netaddr.IPNetwork('10.10.10.0/24')
|
||||
subnetpool = self._test_create_subnetpool([subnet.cidr],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='26')
|
||||
self._validate_min_prefix('26', subnetpool)
|
||||
self._validate_default_prefix('26', subnetpool)
|
||||
|
||||
def test_create_subnetpool_ipv4_default_prefix_smaller_than_min(self):
|
||||
subnet = netaddr.IPNetwork('10.10.10.0/21')
|
||||
self.assertRaises(webob.exc.HTTPClientError,
|
||||
self._test_create_subnetpool,
|
||||
[subnet.cidr],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
default_prefixlen='22',
|
||||
min_prefixlen='23')
|
||||
|
||||
def test_create_subnetpool_mixed_ip_version(self):
|
||||
subnet_v4 = netaddr.IPNetwork('10.10.10.0/21')
|
||||
subnet_v6 = netaddr.IPNetwork('fe80::/48')
|
||||
self.assertRaises(webob.exc.HTTPClientError,
|
||||
self._test_create_subnetpool,
|
||||
[subnet_v4.cidr, subnet_v6.cidr],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='21')
|
||||
|
||||
def test_create_subnetpool_ipv6_with_defaults(self):
|
||||
subnet = netaddr.IPNetwork('fe80::/48')
|
||||
subnetpool = self._test_create_subnetpool([subnet.cidr],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='48')
|
||||
self._validate_default_prefix('48', subnetpool)
|
||||
self._validate_min_prefix('48', subnetpool)
|
||||
|
||||
def test_get_subnetpool(self):
|
||||
subnetpool = self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='24')
|
||||
req = self.new_show_request('subnetpools',
|
||||
subnetpool['subnetpool']['id'])
|
||||
res = self.deserialize(self.fmt, req.get_response(self.api))
|
||||
self.assertEqual(subnetpool['subnetpool']['id'],
|
||||
res['subnetpool']['id'])
|
||||
|
||||
def test_get_subnetpool_different_tenants_not_shared(self):
|
||||
subnetpool = self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
shared=False,
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='24')
|
||||
req = self.new_show_request('subnetpools',
|
||||
subnetpool['subnetpool']['id'])
|
||||
neutron_context = context.Context('', 'not-the-owner')
|
||||
req.environ['neutron.context'] = neutron_context
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(res.status_int, 404)
|
||||
|
||||
def test_get_subnetpool_different_tenants_shared(self):
|
||||
subnetpool = self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
None,
|
||||
True,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='24',
|
||||
shared=True)
|
||||
req = self.new_show_request('subnetpools',
|
||||
subnetpool['subnetpool']['id'])
|
||||
neutron_context = context.Context('', self._tenant_id)
|
||||
req.environ['neutron.context'] = neutron_context
|
||||
res = self.deserialize(self.fmt, req.get_response(self.api))
|
||||
self.assertEqual(subnetpool['subnetpool']['id'],
|
||||
res['subnetpool']['id'])
|
||||
|
||||
def test_list_subnetpools_different_tenants_shared(self):
|
||||
self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
None,
|
||||
True,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='24',
|
||||
shared=True)
|
||||
admin_res = self._list('subnetpools')
|
||||
mortal_res = self._list('subnetpools',
|
||||
neutron_context=context.Context('', 'not-the-owner'))
|
||||
self.assertEqual(len(admin_res['subnetpools']), 1)
|
||||
self.assertEqual(len(mortal_res['subnetpools']), 1)
|
||||
|
||||
def test_list_subnetpools_different_tenants_not_shared(self):
|
||||
self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
None,
|
||||
True,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='24',
|
||||
shared=False)
|
||||
admin_res = self._list('subnetpools')
|
||||
mortal_res = self._list('subnetpools',
|
||||
neutron_context=context.Context('', 'not-the-owner'))
|
||||
self.assertEqual(len(admin_res['subnetpools']), 1)
|
||||
self.assertEqual(len(mortal_res['subnetpools']), 0)
|
||||
|
||||
def test_delete_subnetpool(self):
|
||||
subnetpool = self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='24')
|
||||
req = self.new_delete_request('subnetpools',
|
||||
subnetpool['subnetpool']['id'])
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(res.status_int, 204)
|
||||
|
||||
def test_delete_nonexistent_subnetpool(self):
|
||||
req = self.new_delete_request('subnetpools',
|
||||
'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa')
|
||||
res = req.get_response(self._api_for_resource('subnetpools'))
|
||||
self.assertEqual(res.status_int, 404)
|
||||
|
||||
def test_update_subnetpool_prefix_list_append(self):
|
||||
initial_subnetpool = self._test_create_subnetpool(['10.10.8.0/21'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='24')
|
||||
|
||||
data = {'subnetpool': {'prefixes': ['10.10.8.0/21', '3.3.3.0/24',
|
||||
'2.2.2.0/24']}}
|
||||
req = self.new_update_request('subnetpools', data,
|
||||
initial_subnetpool['subnetpool']['id'])
|
||||
api = self._api_for_resource('subnetpools')
|
||||
res = self.deserialize(self.fmt, req.get_response(api))
|
||||
self.assertItemsEqual(res['subnetpool']['prefixes'],
|
||||
['10.10.8.0/21', '3.3.3.0/24', '2.2.2.0/24'])
|
||||
|
||||
def test_update_subnetpool_prefix_list_compaction(self):
|
||||
initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='24')
|
||||
|
||||
data = {'subnetpool': {'prefixes': ['10.10.10.0/24',
|
||||
'10.10.11.0/24']}}
|
||||
req = self.new_update_request('subnetpools', data,
|
||||
initial_subnetpool['subnetpool']['id'])
|
||||
api = self._api_for_resource('subnetpools')
|
||||
res = self.deserialize(self.fmt, req.get_response(api))
|
||||
self.assertItemsEqual(res['subnetpool']['prefixes'],
|
||||
['10.10.10.0/23'])
|
||||
|
||||
def test_illegal_subnetpool_prefix_list_update(self):
|
||||
initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='24')
|
||||
|
||||
data = {'subnetpool': {'prefixes': ['10.10.11.0/24']}}
|
||||
req = self.new_update_request('subnetpools', data,
|
||||
initial_subnetpool['subnetpool']['id'])
|
||||
api = self._api_for_resource('subnetpools')
|
||||
res = req.get_response(api)
|
||||
self.assertEqual(res.status_int, 400)
|
||||
|
||||
def test_update_subnetpool_default_prefix(self):
|
||||
initial_subnetpool = self._test_create_subnetpool(['10.10.8.0/21'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='24')
|
||||
|
||||
data = {'subnetpool': {'default_prefixlen': '26'}}
|
||||
req = self.new_update_request('subnetpools', data,
|
||||
initial_subnetpool['subnetpool']['id'])
|
||||
api = self._api_for_resource('subnetpools')
|
||||
res = self.deserialize(self.fmt, req.get_response(api))
|
||||
self.assertEqual(res['subnetpool']['default_prefixlen'], 26)
|
||||
|
||||
def test_update_subnetpool_min_prefix(self):
|
||||
initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='24')
|
||||
|
||||
data = {'subnetpool': {'min_prefixlen': '21'}}
|
||||
req = self.new_update_request('subnetpools', data,
|
||||
initial_subnetpool['subnetpool']['id'])
|
||||
res = self.deserialize(self.fmt, req.get_response(self.api))
|
||||
self.assertEqual(res['subnetpool']['min_prefixlen'], 21)
|
||||
|
||||
def test_update_subnetpool_min_prefix_larger_than_max(self):
|
||||
initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='21',
|
||||
max_prefixlen='24')
|
||||
|
||||
data = {'subnetpool': {'min_prefixlen': '28'}}
|
||||
req = self.new_update_request('subnetpools', data,
|
||||
initial_subnetpool['subnetpool']['id'])
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(res.status_int, 400)
|
||||
|
||||
def test_update_subnetpool_max_prefix(self):
|
||||
initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='21',
|
||||
max_prefixlen='24')
|
||||
|
||||
data = {'subnetpool': {'max_prefixlen': '26'}}
|
||||
req = self.new_update_request('subnetpools', data,
|
||||
initial_subnetpool['subnetpool']['id'])
|
||||
res = self.deserialize(self.fmt, req.get_response(self.api))
|
||||
self.assertEqual(res['subnetpool']['max_prefixlen'], 26)
|
||||
|
||||
def test_update_subnetpool_max_prefix_less_than_min(self):
|
||||
initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='24')
|
||||
|
||||
data = {'subnetpool': {'max_prefixlen': '21'}}
|
||||
req = self.new_update_request('subnetpools', data,
|
||||
initial_subnetpool['subnetpool']['id'])
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(res.status_int, 400)
|
||||
|
||||
def test_update_subnetpool_max_prefix_less_than_default(self):
|
||||
initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='21',
|
||||
default_prefixlen='24')
|
||||
|
||||
data = {'subnetpool': {'max_prefixlen': '22'}}
|
||||
req = self.new_update_request('subnetpools', data,
|
||||
initial_subnetpool['subnetpool']['id'])
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(res.status_int, 400)
|
||||
|
||||
def test_update_subnetpool_default_prefix_less_than_min(self):
|
||||
initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='21')
|
||||
|
||||
data = {'subnetpool': {'default_prefixlen': '20'}}
|
||||
req = self.new_update_request('subnetpools', data,
|
||||
initial_subnetpool['subnetpool']['id'])
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(res.status_int, 400)
|
||||
|
||||
def test_update_subnetpool_default_prefix_larger_than_max(self):
|
||||
initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='21',
|
||||
max_prefixlen='24')
|
||||
|
||||
data = {'subnetpool': {'default_prefixlen': '28'}}
|
||||
req = self.new_update_request('subnetpools', data,
|
||||
initial_subnetpool['subnetpool']['id'])
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(res.status_int, 400)
|
||||
|
||||
def test_update_subnetpool_prefix_list_mixed_ip_version(self):
|
||||
initial_subnetpool = self._test_create_subnetpool(['10.10.10.0/24'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='24')
|
||||
|
||||
data = {'subnetpool': {'prefixes': ['fe80::/48']}}
|
||||
req = self.new_update_request('subnetpools', data,
|
||||
initial_subnetpool['subnetpool']['id'])
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(res.status_int, 400)
|
||||
|
||||
|
||||
class DbModelTestCase(base.BaseTestCase):
|
||||
"""DB model tests."""
|
||||
def test_repr(self):
|
||||
|
|
Loading…
Reference in New Issue