diff --git a/neutron/common/exceptions.py b/neutron/common/exceptions.py index d3ac999638b..bbfbb2ff275 100644 --- a/neutron/common/exceptions.py +++ b/neutron/common/exceptions.py @@ -504,6 +504,12 @@ class IllegalSubnetPoolAssociationToAddressScope(BadRequest): "%(address_scope_id)s.") +class IllegalSubnetPoolIpVersionAssociationToAddressScope(BadRequest): + message = _("Illegal subnetpool association: subnetpool %(subnetpool_id)s " + "cannot associate with address scope %(address_scope_id)s " + "because subnetpool ip_version is not %(ip_version)s.") + + class IllegalSubnetPoolUpdate(BadRequest): message = _("Illegal subnetpool update : %(reason)s.") diff --git a/neutron/db/address_scope_db.py b/neutron/db/address_scope_db.py index 4bf6778add5..d6b5892c904 100644 --- a/neutron/db/address_scope_db.py +++ b/neutron/db/address_scope_db.py @@ -33,6 +33,7 @@ class AddressScope(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant): name = sa.Column(sa.String(attr.NAME_MAX_LEN), nullable=False) shared = sa.Column(sa.Boolean, nullable=False) + ip_version = sa.Column(sa.Integer(), nullable=False) class AddressScopeDbMixin(ext_address_scope.AddressScopePluginBase): @@ -44,7 +45,8 @@ class AddressScopeDbMixin(ext_address_scope.AddressScopePluginBase): res = {'id': address_scope['id'], 'name': address_scope['name'], 'tenant_id': address_scope['tenant_id'], - 'shared': address_scope['shared']} + 'shared': address_scope['shared'], + 'ip_version': address_scope['ip_version']} return self._fields(res, fields) def _get_address_scope(self, context, id): @@ -68,6 +70,10 @@ class AddressScopeDbMixin(ext_address_scope.AddressScopePluginBase): return context.is_admin or ( address_scope.tenant_id == context.tenant_id) + def get_ip_version_for_address_scope(self, context, id): + address_scope = self._get_address_scope(context, id) + return address_scope.ip_version + def create_address_scope(self, context, address_scope): """Create a address scope.""" a_s = address_scope['address_scope'] @@ -77,7 +83,8 @@ class AddressScopeDbMixin(ext_address_scope.AddressScopePluginBase): pool_args = {'tenant_id': tenant_id, 'id': address_scope_id, 'name': a_s['name'], - 'shared': a_s['shared']} + 'shared': a_s['shared'], + 'ip_version': a_s['ip_version']} address_scope = AddressScope(**pool_args) context.session.add(address_scope) diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py index 4f80f372d06..fdc92fa2955 100644 --- a/neutron/db/db_base_plugin_v2.py +++ b/neutron/db/db_base_plugin_v2.py @@ -874,7 +874,7 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon, context.session.add(subnetpool_prefix) def _validate_address_scope_id(self, context, address_scope_id, - subnetpool_id, sp_prefixes): + subnetpool_id, sp_prefixes, ip_version): """Validate the address scope before associating. Subnetpool can associate with an address scope if @@ -884,6 +884,8 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon, address scope - there is no prefix conflict with the existing subnetpools associated with the address scope. + - the address family of the subnetpool and address scope + are the same """ if not attributes.is_attr_set(address_scope_id): return @@ -893,6 +895,14 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon, raise n_exc.IllegalSubnetPoolAssociationToAddressScope( subnetpool_id=subnetpool_id, address_scope_id=address_scope_id) + as_ip_version = self.get_ip_version_for_address_scope(context, + address_scope_id) + + if ip_version != as_ip_version: + raise n_exc.IllegalSubnetPoolIpVersionAssociationToAddressScope( + subnetpool_id=subnetpool_id, address_scope_id=address_scope_id, + ip_version=as_ip_version) + subnetpools = self._get_subnetpools_by_address_scope_id( context, address_scope_id) @@ -943,7 +953,8 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon, 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) + id, sp_reader.prefixes, + sp_reader.ip_version) tenant_id = self._get_tenant_id_for_create(context, sp) with context.session.begin(subtransactions=True): pool_args = {'tenant_id': tenant_id, @@ -1023,7 +1034,8 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon, orig_sp.address_scope_id) self._validate_address_scope_id(context, reader.address_scope_id, - id, reader.prefixes) + id, reader.prefixes, + reader.ip_version) orig_sp.update(self._filter_non_model_columns( reader.subnetpool, models_v2.SubnetPool)) diff --git a/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD b/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD index a38beeac26c..d6602ab23ba 100644 --- a/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD +++ b/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD @@ -1 +1 @@ -dce3ec7a25c9 +c3a73f615e4 diff --git a/neutron/db/migration/alembic_migrations/versions/mitaka/expand/c3a73f615e4_add_ip_version_to_address_scope.py b/neutron/db/migration/alembic_migrations/versions/mitaka/expand/c3a73f615e4_add_ip_version_to_address_scope.py new file mode 100644 index 00000000000..e039f700cbf --- /dev/null +++ b/neutron/db/migration/alembic_migrations/versions/mitaka/expand/c3a73f615e4_add_ip_version_to_address_scope.py @@ -0,0 +1,33 @@ +# +# 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 ip_version to AddressScope + +Revision ID: c3a73f615e4 +Revises: 13cfb89f881a +Create Date: 2015-10-08 17:34:32.231256 + +""" + +# revision identifiers, used by Alembic. +revision = 'c3a73f615e4' +down_revision = 'dce3ec7a25c9' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('address_scopes', + sa.Column('ip_version', sa.Integer(), nullable=False)) diff --git a/neutron/extensions/address_scope.py b/neutron/extensions/address_scope.py index 802537bd213..2fe75141eb0 100644 --- a/neutron/extensions/address_scope.py +++ b/neutron/extensions/address_scope.py @@ -51,6 +51,10 @@ RESOURCE_ATTRIBUTE_MAP = { 'is_visible': True, 'required_by_policy': True, 'enforce_policy': True}, + 'ip_version': {'allow_post': True, 'allow_put': False, + 'convert_to': attr.convert_to_int, + 'validate': {'type:values': [4, 6]}, + 'is_visible': True}, }, attr.SUBNETPOOLS: { ADDRESS_SCOPE_ID: {'allow_post': True, diff --git a/neutron/tests/api/test_address_scopes.py b/neutron/tests/api/test_address_scopes.py index b68e909962b..8696e09dcaf 100644 --- a/neutron/tests/api/test_address_scopes.py +++ b/neutron/tests/api/test_address_scopes.py @@ -47,7 +47,8 @@ class AddressScopeTestBase(base.BaseNetworkTest): **kwargs) def _test_update_address_scope_helper(self, is_admin=False, shared=None): - address_scope = self._create_address_scope(is_admin=is_admin) + address_scope = self._create_address_scope(is_admin=is_admin, + ip_version=4) if is_admin: client = self.admin_client @@ -70,7 +71,7 @@ class AddressScopeTest(AddressScopeTestBase): @test.attr(type='smoke') @test.idempotent_id('045f9294-8b1a-4848-b6a8-edf1b41e9d06') def test_tenant_create_list_address_scope(self): - address_scope = self._create_address_scope() + address_scope = self._create_address_scope(ip_version=4) body = self.client.list_address_scopes() returned_address_scopes = body['address_scopes'] self.assertIn(address_scope['id'], @@ -83,7 +84,7 @@ class AddressScopeTest(AddressScopeTestBase): @test.attr(type='smoke') @test.idempotent_id('85e0326b-4c75-4b92-bd6e-7c7de6aaf05c') def test_show_address_scope(self): - address_scope = self._create_address_scope() + address_scope = self._create_address_scope(ip_version=4) body = self.client.show_address_scope( address_scope['id']) returned_address_scope = body['address_scope'] @@ -100,7 +101,7 @@ class AddressScopeTest(AddressScopeTestBase): @test.attr(type='smoke') @test.idempotent_id('22b3b600-72a8-4b60-bc94-0f29dd6271df') def test_delete_address_scope(self): - address_scope = self._create_address_scope() + address_scope = self._create_address_scope(ip_version=4) self.client.delete_address_scope(address_scope['id']) self.assertRaises(lib_exc.NotFound, self.client.show_address_scope, address_scope['id']) @@ -108,7 +109,8 @@ class AddressScopeTest(AddressScopeTestBase): @test.attr(type='smoke') @test.idempotent_id('5a06c287-8036-4d04-9d78-def8e06d43df') def test_admin_create_shared_address_scope(self): - address_scope = self._create_address_scope(is_admin=True, shared=True) + address_scope = self._create_address_scope(is_admin=True, shared=True, + ip_version=4) body = self.admin_client.show_address_scope( address_scope['id']) returned_address_scope = body['address_scope'] diff --git a/neutron/tests/api/test_address_scopes_negative.py b/neutron/tests/api/test_address_scopes_negative.py index 97f954a4242..e84a84a0603 100644 --- a/neutron/tests/api/test_address_scopes_negative.py +++ b/neutron/tests/api/test_address_scopes_negative.py @@ -25,7 +25,7 @@ class AddressScopeTestNegative(test_address_scopes.AddressScopeTestBase): @test.idempotent_id('9c92ec34-0c50-4104-aa47-9ce98d5088df') def test_tenant_create_shared_address_scope(self): self.assertRaises(lib_exc.Forbidden, self._create_address_scope, - shared=True) + shared=True, ip_version=4) @test.attr(type=['negative', 'smoke']) @test.idempotent_id('a857b61e-bf53-4fab-b21a-b0daaf81b5bd') @@ -49,7 +49,8 @@ class AddressScopeTestNegative(test_address_scopes.AddressScopeTestBase): @test.attr(type=['negative', 'smoke']) @test.idempotent_id('ef213552-f2da-487d-bf4a-e1705d115ff1') def test_tenant_get_not_shared_admin_address_scope(self): - address_scope = self._create_address_scope(is_admin=True) + address_scope = self._create_address_scope(is_admin=True, + ip_version=4) # None-shared admin address scope cannot be retrieved by tenant user. self.assertRaises(lib_exc.NotFound, self.client.show_address_scope, address_scope['id']) @@ -71,7 +72,8 @@ class AddressScopeTestNegative(test_address_scopes.AddressScopeTestBase): @test.attr(type=['negative', 'smoke']) @test.idempotent_id('702d0515-82cb-4207-b0d9-703336e54665') def test_update_shared_address_scope_to_unshare(self): - address_scope = self._create_address_scope(is_admin=True, shared=True) + address_scope = self._create_address_scope(is_admin=True, shared=True, + ip_version=4) self.assertRaises(lib_exc.BadRequest, self.admin_client.update_address_scope, address_scope['id'], name='new-name', shared=False) @@ -79,7 +81,7 @@ class AddressScopeTestNegative(test_address_scopes.AddressScopeTestBase): @test.attr(type=['negative', 'smoke']) @test.idempotent_id('1e471e5c-6f9c-437a-9257-fd9bc4b6f0fb') def test_delete_address_scope_associated_with_subnetpool(self): - address_scope = self._create_address_scope() + address_scope = self._create_address_scope(ip_version=4) prefixes = [u'10.11.12.0/24'] subnetpool_data = {'subnetpool': { 'name': 'foo-subnetpool', diff --git a/neutron/tests/api/test_subnetpools.py b/neutron/tests/api/test_subnetpools.py index 66241b31160..0025205c7b1 100644 --- a/neutron/tests/api/test_subnetpools.py +++ b/neutron/tests/api/test_subnetpools.py @@ -244,7 +244,8 @@ class SubnetPoolsTest(base.BaseNetworkTest): @test.requires_ext(extension='address-scope', service='network') def test_create_subnetpool_associate_address_scope(self): address_scope = self.create_address_scope( - name=data_utils.rand_name('smoke-address-scope')) + name=data_utils.rand_name('smoke-address-scope'), + ip_version=self._ip_version) name, pool_id = self._create_subnetpool( self.client, pool_values={'address_scope_id': address_scope['id']}) self.addCleanup(self.client.delete_subnetpool, pool_id) @@ -257,7 +258,8 @@ class SubnetPoolsTest(base.BaseNetworkTest): @test.requires_ext(extension='address-scope', service='network') def test_update_subnetpool_associate_address_scope(self): address_scope = self.create_address_scope( - name=data_utils.rand_name('smoke-address-scope')) + name=data_utils.rand_name('smoke-address-scope'), + ip_version=self._ip_version) name, pool_id = self._create_subnetpool(self.client) self.addCleanup(self.client.delete_subnetpool, pool_id) body = self.client.get_subnetpool(pool_id) @@ -274,9 +276,11 @@ class SubnetPoolsTest(base.BaseNetworkTest): @test.requires_ext(extension='address-scope', service='network') def test_update_subnetpool_associate_another_address_scope(self): address_scope = self.create_address_scope( - name=data_utils.rand_name('smoke-address-scope')) + name=data_utils.rand_name('smoke-address-scope'), + ip_version=self._ip_version) another_address_scope = self.create_address_scope( - name=data_utils.rand_name('smoke-address-scope')) + name=data_utils.rand_name('smoke-address-scope'), + ip_version=self._ip_version) name, pool_id = self._create_subnetpool( self.client, pool_values={'address_scope_id': address_scope['id']}) @@ -297,7 +301,8 @@ class SubnetPoolsTest(base.BaseNetworkTest): @test.requires_ext(extension='address-scope', service='network') def test_update_subnetpool_disassociate_address_scope(self): address_scope = self.create_address_scope( - name=data_utils.rand_name('smoke-address-scope')) + name=data_utils.rand_name('smoke-address-scope'), + ip_version=self._ip_version) name, pool_id = self._create_subnetpool( self.client, pool_values={'address_scope_id': address_scope['id']}) self.addCleanup(self.client.delete_subnetpool, pool_id) diff --git a/neutron/tests/api/test_subnetpools_negative.py b/neutron/tests/api/test_subnetpools_negative.py index d71a3a9ed7c..b5945fae211 100644 --- a/neutron/tests/api/test_subnetpools_negative.py +++ b/neutron/tests/api/test_subnetpools_negative.py @@ -166,7 +166,8 @@ class SubnetPoolsNegativeTestJSON(base.BaseNetworkTest): @test.requires_ext(extension='address-scope', service='network') def test_create_subnetpool_associate_address_scope_prefix_intersect(self): address_scope = self.create_address_scope( - name=data_utils.rand_name('smoke-address-scope')) + name=data_utils.rand_name('smoke-address-scope'), + ip_version=4) addr_scope_id = address_scope['id'] pool_id = self._create_subnetpool( self.client, pool_values={'address_scope_id': addr_scope_id}) @@ -183,7 +184,8 @@ class SubnetPoolsNegativeTestJSON(base.BaseNetworkTest): @test.requires_ext(extension='address-scope', service='network') def test_create_sp_associate_address_scope_multiple_prefix_intersect(self): address_scope = self.create_address_scope( - name=data_utils.rand_name('smoke-address-scope')) + name=data_utils.rand_name('smoke-address-scope'), + ip_version=4) addr_scope_id = address_scope['id'] pool_values = {'address_scope_id': addr_scope_id, 'prefixes': [u'20.0.0.0/18', u'30.0.0.0/18']} @@ -204,7 +206,8 @@ class SubnetPoolsNegativeTestJSON(base.BaseNetworkTest): @test.requires_ext(extension='address-scope', service='network') def test_create_subnetpool_associate_address_scope_of_other_owner(self): address_scope = self.create_address_scope( - name=data_utils.rand_name('smoke-address-scope'), is_admin=True) + name=data_utils.rand_name('smoke-address-scope'), is_admin=True, + ip_version=4) address_scope_id = address_scope['id'] subnetpool_data = copy.deepcopy(self._subnetpool_data) subnetpool_data['subnetpool']['address_scope_id'] = address_scope_id @@ -217,7 +220,7 @@ class SubnetPoolsNegativeTestJSON(base.BaseNetworkTest): def test_tenant_create_subnetpool_associate_shared_address_scope(self): address_scope = self.create_address_scope( name=data_utils.rand_name('smoke-address-scope'), is_admin=True, - shared=True) + shared=True, ip_version=4) subnetpool_data = copy.deepcopy(self._subnetpool_data) subnetpool_data['subnetpool']['address_scope_id'] = ( address_scope['id']) @@ -229,7 +232,8 @@ class SubnetPoolsNegativeTestJSON(base.BaseNetworkTest): @test.requires_ext(extension='address-scope', service='network') def test_update_subnetpool_associate_address_scope_of_other_owner(self): address_scope = self.create_address_scope( - name=data_utils.rand_name('smoke-address-scope'), is_admin=True) + name=data_utils.rand_name('smoke-address-scope'), is_admin=True, + ip_version=4) address_scope_id = address_scope['id'] pool_id = self._create_subnetpool(self.client) self.addCleanup(self.client.delete_subnetpool, pool_id) @@ -244,7 +248,7 @@ class SubnetPoolsNegativeTestJSON(base.BaseNetworkTest): # Updating the first subnet pool with the prefix intersecting # with the second one should be a failure address_scope = self.create_address_scope( - name=data_utils.rand_name('smoke-address-scope')) + name=data_utils.rand_name('smoke-address-scope'), ip_version=4) addr_scope_id = address_scope['id'] pool_values = {'address_scope_id': addr_scope_id, 'prefixes': pool_1_prefixes} @@ -292,7 +296,7 @@ class SubnetPoolsNegativeTestJSON(base.BaseNetworkTest): def test_tenant_update_sp_prefix_associated_with_shared_addr_scope(self): address_scope = self.create_address_scope( name=data_utils.rand_name('smoke-address-scope'), is_admin=True, - shared=True) + shared=True, ip_version=4) addr_scope_id = address_scope['id'] pool_values = {'prefixes': [u'20.0.0.0/18', u'30.0.0.0/18']} @@ -320,3 +324,16 @@ class SubnetPoolsNegativeTestJSON(base.BaseNetworkTest): body = self.admin_client.get_subnetpool(pool_id) self.assertEqual(update_prefixes, body['subnetpool']['prefixes']) + + @test.attr(type='smoke') + @test.idempotent_id('648fee7d-a909-4ced-bad3-3a169444c0a8') + def test_update_subnetpool_associate_address_scope_wrong_ip_version(self): + address_scope = self.create_address_scope( + name=data_utils.rand_name('smoke-address-scope'), + ip_version=6) + pool_id = self._create_subnetpool(self.client) + self.addCleanup(self.client.delete_subnetpool, pool_id) + subnetpool_data = {'subnetpool': {'address_scope_id': + address_scope['id']}} + self.assertRaises(lib_exc.BadRequest, self.client.update_subnetpool, + pool_id, subnetpool_data) diff --git a/neutron/tests/unit/extensions/test_address_scope.py b/neutron/tests/unit/extensions/test_address_scope.py index 66793fc0803..be3f816e63b 100644 --- a/neutron/tests/unit/extensions/test_address_scope.py +++ b/neutron/tests/unit/extensions/test_address_scope.py @@ -18,6 +18,7 @@ import netaddr import webob.exc from neutron.api.v2 import attributes as attr +from neutron.common import constants from neutron import context from neutron.db import address_scope_db from neutron.db import db_base_plugin_v2 @@ -48,9 +49,10 @@ class AddressScopeTestExtensionManager(object): class AddressScopeTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): - def _create_address_scope(self, fmt, expected_res_status=None, - admin=False, **kwargs): + def _create_address_scope(self, fmt, ip_version=constants.IP_VERSION_4, + expected_res_status=None, admin=False, **kwargs): address_scope = {'address_scope': {}} + address_scope['address_scope']['ip_version'] = ip_version for k, v in kwargs.items(): address_scope['address_scope'][k] = str(v) @@ -67,21 +69,27 @@ class AddressScopeTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): self.assertEqual(address_scope_res.status_int, expected_res_status) return address_scope_res - def _make_address_scope(self, fmt, admin=False, **kwargs): - res = self._create_address_scope(fmt, admin=admin, **kwargs) + def _make_address_scope(self, fmt, ip_version, admin=False, **kwargs): + res = self._create_address_scope(fmt, ip_version, + admin=admin, **kwargs) if res.status_int >= webob.exc.HTTPClientError.code: raise webob.exc.HTTPClientError(code=res.status_int) return self.deserialize(fmt, res) @contextlib.contextmanager - def address_scope(self, admin=False, **kwargs): - addr_scope = self._make_address_scope(self.fmt, admin, **kwargs) + def address_scope(self, ip_version=constants.IP_VERSION_4, + admin=False, **kwargs): + addr_scope = self._make_address_scope(self.fmt, ip_version, + admin, **kwargs) yield addr_scope - def _test_create_address_scope(self, admin=False, expected=None, **kwargs): + def _test_create_address_scope(self, ip_version=constants.IP_VERSION_4, + admin=False, expected=None, **kwargs): keys = kwargs.copy() keys.setdefault('tenant_id', self._tenant_id) - with self.address_scope(admin=admin, **keys) as addr_scope: + with self.address_scope(ip_version, + admin=admin, **keys) as addr_scope: + keys['ip_version'] = ip_version self._validate_resource(addr_scope, keys, 'address_scope') if expected: self._compare_resource(addr_scope, expected, 'address_scope') @@ -119,13 +127,23 @@ class TestAddressScope(AddressScopeTestCase): ext_mgr = AddressScopeTestExtensionManager() super(TestAddressScope, self).setUp(plugin=plugin, ext_mgr=ext_mgr) - def test_create_address_scope(self): + def test_create_address_scope_ipv4(self): expected_addr_scope = {'name': 'foo-address-scope', 'tenant_id': self._tenant_id, - 'shared': False} + 'shared': False, + 'ip_version': constants.IP_VERSION_4} self._test_create_address_scope(name='foo-address-scope', expected=expected_addr_scope) + def test_create_address_scope_ipv6(self): + expected_addr_scope = {'name': 'foo-address-scope', + 'tenant_id': self._tenant_id, + 'shared': False, + 'ip_version': constants.IP_VERSION_6} + self._test_create_address_scope(constants.IP_VERSION_6, + name='foo-address-scope', + expected=expected_addr_scope) + def test_create_address_scope_empty_name(self): expected_addr_scope = {'name': '', 'tenant_id': self._tenant_id, @@ -216,7 +234,8 @@ class TestAddressScope(AddressScopeTestCase): def test_list_address_scopes(self): self._test_create_address_scope(name='foo-address-scope') - self._test_create_address_scope(name='bar-address-scope') + self._test_create_address_scope(constants.IP_VERSION_6, + name='bar-address-scope') res = self._list('address-scopes') self.assertEqual(2, len(res['address_scopes'])) @@ -231,7 +250,8 @@ class TestAddressScope(AddressScopeTestCase): self.assertEqual(1, len(mortal_res['address_scopes'])) def test_list_address_scopes_different_tenants_not_shared(self): - self._test_create_address_scope(name='foo-address-scope') + self._test_create_address_scope(constants.IP_VERSION_6, + name='foo-address-scope') admin_res = self._list('address-scopes') mortal_res = self._list( 'address-scopes', @@ -361,3 +381,36 @@ class TestSubnetPoolsWithAddressScopes(AddressScopeTestCase): address_scope_id=address_scope_id) self._delete('address-scopes', address_scope_id, expected_code=webob.exc.HTTPConflict.code) + + def test_add_subnetpool_address_scope_wrong_address_family(self): + with self.address_scope(constants.IP_VERSION_6, + name='foo-address-scope') as addr_scope: + address_scope_id = addr_scope['address_scope']['id'] + subnet = netaddr.IPNetwork('10.10.10.0/24') + self.assertRaises(webob.exc.HTTPClientError, + self._test_create_subnetpool, + [subnet.cidr], name='foo-subnetpool', + min_prefixlen='21', + address_scope_id=address_scope_id) + + def test_update_subnetpool_associate_address_scope_wrong_family(self): + with self.address_scope(constants.IP_VERSION_6, + name='foo-address-scope') as addr_scope: + address_scope_id = addr_scope['address_scope']['id'] + subnet = netaddr.IPNetwork('2001:db8::/64') + expected = {'address_scope_id': address_scope_id} + initial_subnetpool = self._test_create_subnetpool( + [subnet.cidr], expected=expected, name='foo-sp', + min_prefixlen='64', address_scope_id=address_scope_id) + + with self.address_scope(name='foo-address-scope') as other_a_s: + other_a_s_id = other_a_s['address_scope']['id'] + update_data = {'subnetpool': {'address_scope_id': + other_a_s_id}} + req = self.new_update_request( + 'subnetpools', update_data, + initial_subnetpool['subnetpool']['id']) + api = self._api_for_resource('subnetpools') + res = req.get_response(api) + self.assertEqual(webob.exc.HTTPBadRequest.code, + res.status_int)