Add use_default_subnetpool to subnet create requests
This follows up [1] by adding an extension to allow requesting the default subnet pool explicitly through the API thus restoring the convenience that was lost by removing the automatic default subnetpool fallback behavior. ApiImpact [1] https://review.openstack.org/#/c/279378/ Change-Id: Ifff57c0485e4727f352b2cc2bd1bdaabd0f1606b Related-Bug: #1545199 Closes-Bug: #1547705
This commit is contained in:
parent
bc4c3c55e6
commit
26e268db79
@ -593,11 +593,52 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
|
||||
|
||||
:param subnet: The subnet dict from the request
|
||||
"""
|
||||
subnetpool_id = subnet.get('subnetpool_id',
|
||||
attributes.ATTR_NOT_SPECIFIED)
|
||||
if subnetpool_id != attributes.ATTR_NOT_SPECIFIED:
|
||||
use_default_subnetpool = subnet.get('use_default_subnetpool')
|
||||
if use_default_subnetpool == attributes.ATTR_NOT_SPECIFIED:
|
||||
use_default_subnetpool = False
|
||||
subnetpool_id = subnet.get('subnetpool_id')
|
||||
if subnetpool_id == attributes.ATTR_NOT_SPECIFIED:
|
||||
subnetpool_id = None
|
||||
|
||||
if use_default_subnetpool and subnetpool_id:
|
||||
msg = _('subnetpool_id and use_default_subnetpool cannot both be '
|
||||
'specified')
|
||||
raise n_exc.BadRequest(resource='subnets', msg=msg)
|
||||
|
||||
if subnetpool_id:
|
||||
return subnetpool_id
|
||||
|
||||
if not use_default_subnetpool:
|
||||
return
|
||||
|
||||
cidr = subnet.get('cidr')
|
||||
if attributes.is_attr_set(cidr):
|
||||
ip_version = netaddr.IPNetwork(cidr).version
|
||||
else:
|
||||
ip_version = subnet.get('ip_version')
|
||||
if not attributes.is_attr_set(ip_version):
|
||||
msg = _('ip_version must be specified in the absence of '
|
||||
'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 and cfg.CONF.default_ipv4_subnet_pool:
|
||||
return cfg.CONF.default_ipv4_subnet_pool
|
||||
if ip_version == 6 and cfg.CONF.default_ipv6_subnet_pool:
|
||||
return cfg.CONF.default_ipv6_subnet_pool
|
||||
|
||||
msg = _('No default subnetpool found for IPv%s') % ip_version
|
||||
raise n_exc.BadRequest(resource='subnets', msg=msg)
|
||||
|
||||
def create_subnet(self, context, subnet):
|
||||
|
||||
s = subnet['subnet']
|
||||
|
56
neutron/extensions/default_subnetpools.py
Normal file
56
neutron/extensions/default_subnetpools.py
Normal file
@ -0,0 +1,56 @@
|
||||
#
|
||||
# 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.
|
||||
|
||||
from neutron.api import extensions
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.common import constants
|
||||
|
||||
|
||||
EXTENDED_ATTRIBUTES_2_0 = {
|
||||
attributes.SUBNETS: {
|
||||
'use_default_subnetpool': {'allow_post': True,
|
||||
'allow_put': False,
|
||||
'default': False,
|
||||
'convert_to': attributes.convert_to_boolean,
|
||||
'is_visible': False, },
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class Default_subnetpools(extensions.ExtensionDescriptor):
|
||||
"""Extension class supporting default subnetpools."""
|
||||
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return "Default Subnetpools"
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return "default-subnetpools"
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return "Provides ability to mark and use a subnetpool as the default"
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2016-02-18T18:00:00-00:00"
|
||||
|
||||
def get_required_extensions(self):
|
||||
return [constants.SUBNET_ALLOCATION_EXT_ALIAS]
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
if version == "2.0":
|
||||
return EXTENDED_ATTRIBUTES_2_0
|
||||
else:
|
||||
return {}
|
@ -127,7 +127,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
"net-mtu", "vlan-transparent",
|
||||
"address-scope",
|
||||
"availability_zone",
|
||||
"network_availability_zone"]
|
||||
"network_availability_zone",
|
||||
"default-subnetpools"]
|
||||
|
||||
@property
|
||||
def supported_extension_aliases(self):
|
||||
|
@ -42,12 +42,11 @@ class TestL3RpcCallback(testlib_api.SqlTestCase):
|
||||
return self.plugin.create_network(self.ctx, network)
|
||||
|
||||
def _prepare_ipv6_pd_subnet(self):
|
||||
# TODO(Carl) Use the default subnet pool extension when available
|
||||
subnet = {'subnet': {'network_id': self.network['id'],
|
||||
'tenant_id': 'tenant_id',
|
||||
'cidr': None,
|
||||
'ip_version': 6,
|
||||
'subnetpool_id': constants.IPV6_PD_POOL_ID,
|
||||
'use_default_subnetpool': True,
|
||||
'name': 'ipv6_pd',
|
||||
'enable_dhcp': True,
|
||||
'host_routes': None,
|
||||
|
@ -1779,7 +1779,6 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
|
||||
if ipv6_pd:
|
||||
cidr = None
|
||||
gateway = None
|
||||
# TODO(Carl) Use the default subnet pool extension when available
|
||||
subnetpool_id = constants.IPV6_PD_POOL_ID
|
||||
cfg.CONF.set_override('ipv6_pd_enabled', True)
|
||||
return (self._make_subnet(self.fmt, network, gateway=gateway,
|
||||
@ -2929,109 +2928,6 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
|
||||
res = subnet_req.get_response(self.api)
|
||||
self.assertEqual(webob.exc.HTTPClientError.code, res.status_int)
|
||||
|
||||
def test_create_subnet_only_ip_version_v4(self):
|
||||
# TODO(carl_baldwin): add test to allow create_subnet
|
||||
# to work with 'default' flag for subnet pool on
|
||||
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,
|
||||
'subnetpool_id': subnetpool_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'
|
||||
with self.subnetpool(prefixes=[subnetpool_prefix],
|
||||
admin=False,
|
||||
name="My subnet pool",
|
||||
tenant_id=tenant_id,
|
||||
min_prefixlen='25') as subnetpool:
|
||||
subnetpool_id = subnetpool['subnetpool']['id']
|
||||
cfg.CONF.set_override('default_ipv4_subnet_pool',
|
||||
subnetpool_id)
|
||||
data = {'subnet': {'network_id': network['network']['id'],
|
||||
'ip_version': '4',
|
||||
'prefixlen': '27',
|
||||
'tenant_id': tenant_id,
|
||||
'subnetpool_id': subnetpool_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_v6(self):
|
||||
# this test mirrors its v4 counterpart
|
||||
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,
|
||||
'subnetpool_id': subnetpool_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'
|
||||
with self.subnetpool(prefixes=[subnetpool_prefix],
|
||||
admin=False,
|
||||
name="My ipv6 subnet pool",
|
||||
tenant_id=tenant_id,
|
||||
min_prefixlen='64') as subnetpool:
|
||||
subnetpool_id = subnetpool['subnetpool']['id']
|
||||
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,
|
||||
'subnetpool_id': subnetpool_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_bad_V4_cidr_prefix_len(self):
|
||||
with self.network() as network:
|
||||
data = {'subnet': {'network_id': network['network']['id'],
|
||||
@ -3066,41 +2962,6 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
|
||||
res = subnet_req.get_response(self.api)
|
||||
self.assertEqual(webob.exc.HTTPClientError.code, res.status_int)
|
||||
|
||||
def _test_create_subnet_V6_pd_modes(self, ra_addr_mode, expect_fail=False):
|
||||
cfg.CONF.set_override('ipv6_pd_enabled', True)
|
||||
# TODO(carl_baldwin): replace explicit subnetpool_id with request to
|
||||
# default subnetpool
|
||||
with self.network() as network:
|
||||
data = {'subnet': {'network_id': network['network']['id'],
|
||||
'ip_version': '6',
|
||||
'tenant_id': network['network']['tenant_id'],
|
||||
'subnetpool_id': constants.IPV6_PD_POOL_ID}}
|
||||
if ra_addr_mode:
|
||||
data['subnet']['ipv6_ra_mode'] = ra_addr_mode
|
||||
data['subnet']['ipv6_address_mode'] = ra_addr_mode
|
||||
subnet_req = self.new_create_request('subnets', data)
|
||||
res = subnet_req.get_response(self.api)
|
||||
if expect_fail:
|
||||
self.assertEqual(webob.exc.HTTPClientError.code,
|
||||
res.status_int)
|
||||
else:
|
||||
subnet = self.deserialize(self.fmt, res)['subnet']
|
||||
self.assertEqual(constants.IPV6_PD_POOL_ID,
|
||||
subnet['subnetpool_id'])
|
||||
|
||||
def test_create_subnet_V6_pd_slaac(self):
|
||||
self._test_create_subnet_V6_pd_modes('slaac')
|
||||
|
||||
def test_create_subnet_V6_pd_stateless(self):
|
||||
self._test_create_subnet_V6_pd_modes('dhcpv6-stateless')
|
||||
|
||||
def test_create_subnet_V6_pd_statefull(self):
|
||||
self._test_create_subnet_V6_pd_modes('dhcpv6-statefull',
|
||||
expect_fail=True)
|
||||
|
||||
def test_create_subnet_V6_pd_no_mode(self):
|
||||
self._test_create_subnet_V6_pd_modes(None, expect_fail=True)
|
||||
|
||||
def test_create_2_subnets_overlapping_cidr_allowed_returns_200(self):
|
||||
cidr_1 = '10.0.0.0/23'
|
||||
cidr_2 = '10.0.0.0/24'
|
||||
|
@ -292,7 +292,6 @@ class TestDbBasePluginIpam(test_db_base.NeutronDbPluginV2TestCase):
|
||||
cfg.CONF.set_override('ipv6_pd_enabled', True)
|
||||
cidr = constants.PROVISIONAL_IPV6_PD_PREFIX
|
||||
allocation_pools = [netaddr.IPRange('::2', '::ffff:ffff:ffff:ffff')]
|
||||
# TODO(Carl) Use the default subnet pool extension when available
|
||||
with self.subnet(cidr=None, ip_version=6,
|
||||
subnetpool_id=constants.IPV6_PD_POOL_ID,
|
||||
ipv6_ra_mode=constants.IPV6_SLAAC,
|
||||
|
190
neutron/tests/unit/extensions/test_default_subnetpools.py
Normal file
190
neutron/tests/unit/extensions/test_default_subnetpools.py
Normal file
@ -0,0 +1,190 @@
|
||||
# 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 oslo_config import cfg
|
||||
import webob.exc
|
||||
|
||||
from neutron.common import constants
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.extensions import default_subnetpools
|
||||
from neutron.tests.unit.db import test_db_base_plugin_v2
|
||||
|
||||
|
||||
class DefaultSubnetpoolsExtensionManager(object):
|
||||
|
||||
def get_resources(self):
|
||||
return []
|
||||
|
||||
def get_actions(self):
|
||||
return []
|
||||
|
||||
def get_request_extensions(self):
|
||||
return []
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
return default_subnetpools.get_extended_resources(version)
|
||||
|
||||
|
||||
class DefaultSubnetpoolsExtensionTestPlugin(
|
||||
db_base_plugin_v2.NeutronDbPluginV2):
|
||||
"""Test plugin to mixin the default subnet pools extension.
|
||||
"""
|
||||
|
||||
supported_extension_aliases = ["default-subnetpools", "subnet_allocation"]
|
||||
|
||||
|
||||
class DefaultSubnetpoolsExtensionTestCase(
|
||||
test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
|
||||
"""Test API extension default_subnetpools attributes.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
plugin = ('neutron.tests.unit.extensions.test_default_subnetpools.' +
|
||||
'DefaultSubnetpoolsExtensionTestPlugin')
|
||||
ext_mgr = DefaultSubnetpoolsExtensionManager()
|
||||
super(DefaultSubnetpoolsExtensionTestCase,
|
||||
self).setUp(plugin=plugin, ext_mgr=ext_mgr)
|
||||
|
||||
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,
|
||||
'use_default_subnetpool': True}}
|
||||
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'
|
||||
with self.subnetpool(prefixes=[subnetpool_prefix],
|
||||
admin=False,
|
||||
name="My subnet pool",
|
||||
tenant_id=tenant_id,
|
||||
min_prefixlen='25') as subnetpool:
|
||||
subnetpool_id = subnetpool['subnetpool']['id']
|
||||
cfg.CONF.set_override('default_ipv4_subnet_pool',
|
||||
subnetpool_id)
|
||||
data = {'subnet': {'network_id': network['network']['id'],
|
||||
'ip_version': '4',
|
||||
'prefixlen': '27',
|
||||
'tenant_id': tenant_id,
|
||||
'use_default_subnetpool': True}}
|
||||
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_v6(self):
|
||||
# this test mirrors its v4 counterpart
|
||||
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,
|
||||
'use_default_subnetpool': True}}
|
||||
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'
|
||||
with self.subnetpool(prefixes=[subnetpool_prefix],
|
||||
admin=False,
|
||||
name="My ipv6 subnet pool",
|
||||
tenant_id=tenant_id,
|
||||
min_prefixlen='64') as subnetpool:
|
||||
subnetpool_id = subnetpool['subnetpool']['id']
|
||||
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,
|
||||
'use_default_subnetpool': True}}
|
||||
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_V6_pd_modes(self, ra_addr_mode, expect_fail=False):
|
||||
cfg.CONF.set_override('ipv6_pd_enabled', True)
|
||||
with self.network() as network:
|
||||
data = {'subnet': {'network_id': network['network']['id'],
|
||||
'ip_version': '6',
|
||||
'tenant_id': network['network']['tenant_id'],
|
||||
'use_default_subnetpool': True}}
|
||||
if ra_addr_mode:
|
||||
data['subnet']['ipv6_ra_mode'] = ra_addr_mode
|
||||
data['subnet']['ipv6_address_mode'] = ra_addr_mode
|
||||
subnet_req = self.new_create_request('subnets', data)
|
||||
res = subnet_req.get_response(self.api)
|
||||
if expect_fail:
|
||||
self.assertEqual(webob.exc.HTTPClientError.code,
|
||||
res.status_int)
|
||||
else:
|
||||
subnet = self.deserialize(self.fmt, res)['subnet']
|
||||
self.assertEqual(constants.IPV6_PD_POOL_ID,
|
||||
subnet['subnetpool_id'])
|
||||
|
||||
def test_create_subnet_V6_pd_slaac(self):
|
||||
self._test_create_subnet_V6_pd_modes('slaac')
|
||||
|
||||
def test_create_subnet_V6_pd_stateless(self):
|
||||
self._test_create_subnet_V6_pd_modes('dhcpv6-stateless')
|
||||
|
||||
def test_create_subnet_V6_pd_statefull(self):
|
||||
self._test_create_subnet_V6_pd_modes('dhcpv6-statefull',
|
||||
expect_fail=True)
|
||||
|
||||
def test_create_subnet_V6_pd_no_mode(self):
|
||||
self._test_create_subnet_V6_pd_modes(None, expect_fail=True)
|
@ -0,0 +1,27 @@
|
||||
---
|
||||
features:
|
||||
- The subnet API now includes a new
|
||||
use_default_subnetpool attribute. This attribute can
|
||||
be specified on creating a subnet in lieu of a
|
||||
subnetpool_id. The two are mutually exclusive. If
|
||||
it is specified as True, the default subnet pool for
|
||||
the requested ip_version will be looked up and used.
|
||||
If no default exists, an error will be returned.
|
||||
deprecations:
|
||||
- The default_subnet_pools option is now deprecated and
|
||||
will be removed in the Newton release. The same
|
||||
functionality is now provided by setting is_default
|
||||
attribute on subnetpools to True using the API or
|
||||
client.
|
||||
fixes:
|
||||
- Before Mitaka, when a default subnetpool was defined
|
||||
in the configuration, a request to create a subnet
|
||||
would fall back to using it if no specific subnet
|
||||
pool was specified. This behavior broke the
|
||||
semantics of subnet create calls in this scenario and
|
||||
is now considered an API bug. This bug has been
|
||||
fixed so that there is no automatic fallback with the
|
||||
presence of a default subnet pool. Workflows which
|
||||
depended on this new behavior will have to be
|
||||
modified to set the new use_default_subnetpool
|
||||
attribute when creating a subnet.
|
Loading…
x
Reference in New Issue
Block a user