From: Pavel Bondar Date: Wed, 17 Jun 2015 12:48:09 +0000 (+0300) Subject: Add request factory for pluggable IPAM X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=432567f9477eadd840e407c5c057fc664e46d731;p=openstack-build%2Fneutron-build.git Add request factory for pluggable IPAM Pluggable IPAM implementation requires separation between requesting address/subnet and it's actual allocation, which can happen on third-party IPAM servers. Request factory stands for simplifying building right request from input. Added AddressRequestFactory and SubnetRequestFactory. AddressRequestFactory creates instance of AnyAddressRequest or SpecificAddressRequest depending on presence of ip address in input. SubnetRequestFactory creates instance of AnySubnetRequest or SpecificSubnetRequest depending on input. get_subnet_request_factory and get_address_request_factory can be redefined on driver level to use custom request factories. Partially-Implements: blueprint neutron-ipam Change-Id: Iedc0cfa326d60810099148f0ef8a1edac9e8aa12 --- diff --git a/neutron/ipam/__init__.py b/neutron/ipam/__init__.py index 4a8e6d1c3..c756946ba 100644 --- a/neutron/ipam/__init__.py +++ b/neutron/ipam/__init__.py @@ -241,3 +241,30 @@ class AutomaticAddressRequest(SpecificAddressRequest): class RouterGatewayAddressRequest(AddressRequest): """Used to request allocating the special router gateway address.""" + + +class BaseRequestFactory(object): + """Factory class to create right request based on input""" + any_request = None + specific_request = None + address_index = 0 + + @classmethod + def get_request(cls, *args, **kwargs): + args_list = [a for a in args] + address = args_list.pop(cls.address_index) + if not address: + return cls.any_request(*args_list, **kwargs) + else: + return cls.specific_request(*args, **kwargs) + + +class AddressRequestFactory(BaseRequestFactory): + any_request = AnyAddressRequest + specific_request = SpecificAddressRequest + + +class SubnetRequestFactory(BaseRequestFactory): + any_request = AnySubnetRequest + specific_request = SpecificSubnetRequest + address_index = 2 diff --git a/neutron/ipam/driver.py b/neutron/ipam/driver.py index 0e54e8856..30e28cc25 100644 --- a/neutron/ipam/driver.py +++ b/neutron/ipam/driver.py @@ -16,6 +16,7 @@ from oslo_config import cfg from oslo_log import log import six +from neutron import ipam from neutron import manager LOG = log.getLogger(__name__) @@ -95,6 +96,20 @@ class Pool(object): :raises: IPAMAllocationNotFound """ + def get_subnet_request_factory(self): + """Returns default SubnetRequestFactory + + Can be overridden on driver level to return custom factory + """ + return ipam.SubnetRequestFactory + + def get_address_request_factory(self): + """Returns default AddressRequestFactory + + Can be overridden on driver level to return custom factory + """ + return ipam.AddressRequestFactory + @six.add_metaclass(abc.ABCMeta) class Subnet(object): diff --git a/neutron/tests/unit/test_ipam.py b/neutron/tests/unit/test_ipam.py index bb8759f63..ffaa100cf 100755 --- a/neutron/tests/unit/test_ipam.py +++ b/neutron/tests/unit/test_ipam.py @@ -285,3 +285,82 @@ class TestIpamDriverLoader(base.BaseTestCase): subnet_pool_id = 'SomePoolID' self._load_ipam_driver('fake', subnet_pool_id) ipam_mock.assert_called_once_with(subnet_pool_id, self.ctx) + + +class TestAddressRequestFactory(base.BaseTestCase): + + def test_specific_address_request_is_loaded(self): + for address in ('10.12.0.15', 'fffe::1'): + self.assertIsInstance( + ipam.AddressRequestFactory.get_request(address), + ipam.SpecificAddressRequest) + + def test_any_address_request_is_loaded(self): + for addr in [None, '']: + self.assertIsInstance( + ipam.AddressRequestFactory.get_request(addr), + ipam.AnyAddressRequest) + + +class TestSubnetRequestFactory(IpamSubnetRequestTestCase): + + def test_specific_subnet_request_is_loaded(self): + addresses = [ + '10.12.0.15/24', + '10.12.0.0/24', + 'fffe::1/64', + 'fffe::/64'] + for address in addresses: + self.assertIsInstance( + ipam.SubnetRequestFactory.get_request(self.tenant_id, + self.subnet_id, + address), + ipam.SpecificSubnetRequest) + + def test_any_address_request_is_loaded_for_ipv4(self): + self.assertIsInstance( + ipam.SubnetRequestFactory.get_request(self.tenant_id, + self.subnet_id, + None, + constants.IPv4, + 8), + ipam.AnySubnetRequest) + + def test_any_address_request_is_loaded_for_ipv6(self): + self.assertIsInstance( + ipam.SubnetRequestFactory.get_request(self.tenant_id, + self.subnet_id, + None, + constants.IPv6, + 64), + ipam.AnySubnetRequest) + + def test_args_are_passed_to_specific_request(self): + request = ipam.SubnetRequestFactory.get_request( + self.tenant_id, + self.subnet_id, + '192.168.1.0/24') + self.assertIsInstance(request, + ipam.SpecificSubnetRequest) + self.assertEqual(self.tenant_id, request.tenant_id) + self.assertEqual(self.subnet_id, request.subnet_id) + self.assertEqual(None, request.gateway_ip) + self.assertEqual(None, request.allocation_pools) + + +class TestGetRequestFactory(base.BaseTestCase): + + def setUp(self): + super(TestGetRequestFactory, self).setUp() + cfg.CONF.set_override('ipam_driver', 'fake') + self.driver = driver.Pool.get_instance(None, None) + + def test_get_subnet_request_factory(self): + self.assertEqual( + self.driver.get_subnet_request_factory(), + ipam.SubnetRequestFactory) + + def test_get_address_request_factory(self): + self.assertEqual( + self.driver.get_address_request_factory(), + ipam.AddressRequestFactory)