From 305dcb1dbccfcaff92c39bfb5763d5b9660e91cb Mon Sep 17 00:00:00 2001 From: John Davidge Date: Thu, 13 Aug 2015 18:51:57 +0100 Subject: [PATCH] Add IPv6 Prefix Delegation compatibility to ipam_pluggable_backend This patch makes the necessary changes for IPv6 PD to function when pluggable ipam is enabled with the reference driver. Includes a unit test for this functionality. Change-Id: I4227cc08fdd62922632629c424dbeb542a48a67f Partially-Implements: blueprint ipv6-prefix-delegation --- neutron/db/ipam_pluggable_backend.py | 2 +- neutron/ipam/drivers/neutrondb_ipam/driver.py | 20 ++++++++++++------- .../unit/db/test_ipam_pluggable_backend.py | 18 +++++++++++++++++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/neutron/db/ipam_pluggable_backend.py b/neutron/db/ipam_pluggable_backend.py index 17e1371c3..7abd621a9 100644 --- a/neutron/db/ipam_pluggable_backend.py +++ b/neutron/db/ipam_pluggable_backend.py @@ -407,7 +407,7 @@ class IpamPluggableBackend(ipam_backend_mixin.IpamBackendMixin): def allocate_subnet(self, context, network, subnet, subnetpool_id): subnetpool = None - if subnetpool_id: + if subnetpool_id and not subnetpool_id == constants.IPV6_PD_POOL_ID: subnetpool = self._get_subnetpool(context, subnetpool_id) self._validate_ip_version_with_subnetpool(subnet, subnetpool) diff --git a/neutron/ipam/drivers/neutrondb_ipam/driver.py b/neutron/ipam/drivers/neutrondb_ipam/driver.py index 1ddab8434..da2da230f 100644 --- a/neutron/ipam/drivers/neutrondb_ipam/driver.py +++ b/neutron/ipam/drivers/neutrondb_ipam/driver.py @@ -44,12 +44,16 @@ class NeutronDbSubnet(ipam_base.Subnet): """ @classmethod - def create_allocation_pools(cls, subnet_manager, session, pools): + def create_allocation_pools(cls, subnet_manager, session, pools, cidr): for pool in pools: + # IPv6 addresses that start '::1', '::2', etc cause IP version + # ambiguity when converted to integers by pool.first and pool.last. + # Infer the IP version from the subnet cidr. + ip_version = cidr.version subnet_manager.create_pool( session, - netaddr.IPAddress(pool.first).format(), - netaddr.IPAddress(pool.last).format()) + netaddr.IPAddress(pool.first, ip_version).format(), + netaddr.IPAddress(pool.last, ip_version).format()) @classmethod def create_from_subnet_request(cls, subnet_request, ctx): @@ -68,7 +72,8 @@ class NeutronDbSubnet(ipam_base.Subnet): else: pools = subnet_request.allocation_pools # Create IPAM allocation pools and availability ranges - cls.create_allocation_pools(subnet_manager, session, pools) + cls.create_allocation_pools(subnet_manager, session, pools, + subnet_request.subnet_cidr) return cls(ipam_subnet_id, ctx, @@ -347,13 +352,13 @@ class NeutronDbSubnet(ipam_base.Subnet): subnet_id=self.subnet_manager.neutron_id, ip_address=address) - def update_allocation_pools(self, pools): + def update_allocation_pools(self, pools, cidr): # Pools have already been validated in the subnet request object which # was sent to the subnet pool driver. Further validation should not be # required. session = db_api.get_session() self.subnet_manager.delete_allocation_pools(session) - self.create_allocation_pools(self.subnet_manager, session, pools) + self.create_allocation_pools(self.subnet_manager, session, pools, cidr) self._pools = pools def get_details(self): @@ -414,7 +419,8 @@ class NeutronDbPool(subnet_alloc.SubnetAllocator): subnet_request.subnet_id) return subnet = NeutronDbSubnet.load(subnet_request.subnet_id, self._context) - subnet.update_allocation_pools(subnet_request.allocation_pools) + cidr = netaddr.IPNetwork(subnet._cidr) + subnet.update_allocation_pools(subnet_request.allocation_pools, cidr) return subnet def remove_subnet(self, subnet_id): diff --git a/neutron/tests/unit/db/test_ipam_pluggable_backend.py b/neutron/tests/unit/db/test_ipam_pluggable_backend.py index 80d826c79..dd5d11112 100644 --- a/neutron/tests/unit/db/test_ipam_pluggable_backend.py +++ b/neutron/tests/unit/db/test_ipam_pluggable_backend.py @@ -20,6 +20,7 @@ import webob.exc from oslo_config import cfg from oslo_utils import uuidutils +from neutron.common import constants from neutron.common import exceptions as n_exc from neutron.common import ipv6_utils from neutron.db import ipam_backend_mixin @@ -283,6 +284,23 @@ class TestDbBasePluginIpam(test_db_base.NeutronDbPluginV2TestCase): self.assertIsInstance(request, ipam_req.SpecificSubnetRequest) self.assertEqual(netaddr.IPNetwork(cidr), request.subnet_cidr) + @mock.patch('neutron.ipam.driver.Pool') + def test_create_ipv6_pd_subnet_over_ipam(self, pool_mock): + mocks = self._prepare_mocks_with_pool_mock(pool_mock) + cfg.CONF.set_override('default_ipv6_subnet_pool', + constants.IPV6_PD_POOL_ID) + cidr = constants.PROVISIONAL_IPV6_PD_PREFIX + allocation_pools = [netaddr.IPRange('::2', '::ffff:ffff:ffff:ffff')] + with self.subnet(cidr=None, ip_version=6, + ipv6_ra_mode=constants.IPV6_SLAAC, + ipv6_address_mode=constants.IPV6_SLAAC): + pool_mock.get_instance.assert_called_once_with(None, mock.ANY) + self.assertTrue(mocks['driver'].allocate_subnet.called) + request = mocks['driver'].allocate_subnet.call_args[0][0] + self.assertIsInstance(request, ipam_req.SpecificSubnetRequest) + self.assertEqual(netaddr.IPNetwork(cidr), request.subnet_cidr) + self.assertEqual(allocation_pools, request.allocation_pools) + @mock.patch('neutron.ipam.driver.Pool') def test_create_subnet_over_ipam_with_rollback(self, pool_mock): mocks = self._prepare_mocks_with_pool_mock(pool_mock) -- 2.45.2