From 0c078f20a6a3d57d9b30b8dcab83741d9f42ccaf Mon Sep 17 00:00:00 2001 From: Ryan Tidwell Date: Mon, 4 May 2015 15:56:41 -0700 Subject: [PATCH] Block subnet create when a network hosts subnets allocated from different pools This change will ensure that all subnets with the same ip_version on a given network have been allocated from the same subnet pool or no pool. This provides cleaner subnet overlap detection. Cherry-picked from 251f551a5fe8fe05cdc8c9b9cfad357245b39bb9 Change-Id: I3c7366c69b10c202c0511126fbee6b3aac36759e Closes-Bug: #1451559 --- neutron/common/exceptions.py | 5 +++++ neutron/db/db_base_plugin_v2.py | 13 ++++++++++++ neutron/tests/api/test_subnetpools.py | 17 +++++++++++++++ .../tests/api/test_subnetpools_negative.py | 21 +++++++++++++++++++ .../tests/unit/db/test_db_base_plugin_v2.py | 9 ++++++++ 5 files changed, 65 insertions(+) diff --git a/neutron/common/exceptions.py b/neutron/common/exceptions.py index d5b64c52e..db7a3d045 100644 --- a/neutron/common/exceptions.py +++ b/neutron/common/exceptions.py @@ -453,3 +453,8 @@ class SubnetPoolDeleteError(BadRequest): class SubnetPoolQuotaExceeded(OverQuota): message = _("Per-tenant subnet pool prefix quota exceeded") + + +class NetworkSubnetPoolAffinityError(BadRequest): + message = _("Subnets hosted on the same network must be allocated from " + "the same subnet pool") diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py index ab940f42f..016e3d3b0 100644 --- a/neutron/db/db_base_plugin_v2.py +++ b/neutron/db/db_base_plugin_v2.py @@ -646,6 +646,16 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, 'cidr': subnet.cidr}) raise n_exc.InvalidInput(error_message=err_msg) + def _validate_network_subnetpools(self, network, + new_subnetpool_id, ip_version): + """Validate all subnets on the given network have been allocated from + the same subnet pool as new_subnetpool_id + """ + for subnet in network.subnets: + if (subnet.ip_version == ip_version and + new_subnetpool_id != subnet.subnetpool_id): + raise n_exc.NetworkSubnetPoolAffinityError() + def _validate_allocation_pools(self, ip_pools, subnet_cidr): """Validate IP allocation pools. @@ -1196,6 +1206,9 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, allocation_pools) self._validate_subnet_cidr(context, network, subnet_args['cidr']) + self._validate_network_subnetpools(network, + subnet_args['subnetpool_id'], + subnet_args['ip_version']) subnet = models_v2.Subnet(**subnet_args) context.session.add(subnet) diff --git a/neutron/tests/api/test_subnetpools.py b/neutron/tests/api/test_subnetpools.py index 73aad6806..454dfb86b 100644 --- a/neutron/tests/api/test_subnetpools.py +++ b/neutron/tests/api/test_subnetpools.py @@ -256,3 +256,20 @@ class SubnetPoolsTestV6(SubnetPoolsTest): prefixes = [u'2001:db8:3::/48'] cls._subnetpool_data = {'subnetpool': {'min_prefixlen': min_prefixlen, 'prefixes': prefixes}} + + @test.attr(type='smoke') + @test.idempotent_id('f62d73dc-cf6f-4879-b94b-dab53982bf3b') + def test_create_dual_stack_subnets_from_subnetpools(self): + pool_id_v6, subnet_v6 = self._create_subnet_from_pool() + self.addCleanup(self.client.delete_subnet, subnet_v6['id']) + pool_values_v4 = {'prefixes': ['192.168.0.0/16'], + 'min_prefixlen': 21, + 'max_prefixlen': 32} + pool_name_v4, pool_id_v4 = self._create_subnetpool(self.client, + pool_values=pool_values_v4) + subnet_v4 = self.client.create_subnet( + network_id=subnet_v6['network_id'], + ip_version=4, + subnetpool_id=pool_id_v4)['subnet'] + self.addCleanup(self.client.delete_subnet, subnet_v4['id']) + self.assertEqual(subnet_v4['network_id'], subnet_v6['network_id']) diff --git a/neutron/tests/api/test_subnetpools_negative.py b/neutron/tests/api/test_subnetpools_negative.py index 52424499a..c82c6f872 100644 --- a/neutron/tests/api/test_subnetpools_negative.py +++ b/neutron/tests/api/test_subnetpools_negative.py @@ -118,3 +118,24 @@ class SubnetPoolsNegativeTestJSON(base.BaseNetworkTest): self.assertRaises(lib_exc.BadRequest, self.client.update_subnetpool, pool_id, subnetpool_data) + + @test.attr(type=['negative', 'smoke']) + @test.idempotent_id('fc011824-153e-4469-97ad-9808eb88cae1') + def test_create_subnet_different_pools_same_network(self): + network = self.create_network(network_name='smoke-network') + subnetpool_data = {'prefixes': ['192.168.0.0/16'], + 'name': 'test-pool'} + pool_id = self._create_subnetpool(self.admin_client, subnetpool_data) + subnet = self.admin_client.create_subnet( + network_id=network['id'], + cidr='10.10.10.0/24', + ip_version=4, + gateway_ip=None) + subnet_id = subnet['subnet']['id'] + self.addCleanup(self.admin_client.delete_subnet, subnet_id) + self.addCleanup(self.admin_client.delete_subnetpool, pool_id) + self.assertRaises(lib_exc.BadRequest, + self.admin_client.create_subnet, + network_id=network['id'], + ip_version=4, + subnetpool_id=pool_id) diff --git a/neutron/tests/unit/db/test_db_base_plugin_v2.py b/neutron/tests/unit/db/test_db_base_plugin_v2.py index d67adddbf..9f74dc2de 100644 --- a/neutron/tests/unit/db/test_db_base_plugin_v2.py +++ b/neutron/tests/unit/db/test_db_base_plugin_v2.py @@ -5531,6 +5531,15 @@ class NeutronDbPluginV2AsMixinTestCase(testlib_api.SqlTestCase): net = self.plugin.create_network(self.context, self.net_data) self.assertEqual(net['status'], 'BUILD') + def test__validate_network_subnetpools(self): + network = models_v2.Network() + network.subnets = [models_v2.Subnet(subnetpool_id='test_id', + ip_version=4)] + new_subnetpool_id = None + self.assertRaises(n_exc.NetworkSubnetPoolAffinityError, + self.plugin._validate_network_subnetpools, + network, new_subnetpool_id, 4) + class TestNetworks(testlib_api.SqlTestCase): def setUp(self): -- 2.45.2