From 26b24b7e2305d9aa179a9d257b715e6d67b75573 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 4 Jan 2012 12:47:40 -0800 Subject: Adds support for floating ip pools * Implements blueprint multiple-floating-ip-ranges * Adds pool and interface fields to floating ip tables * Adds extension to get a list of available pools * Optionally allows a pool to be specified when allocating * Changes nova-manage command to allow pool and interface * Ip binding uses the interface from table instead of flag * Adds default pool flag to use when pool is not specified * updates test to work with new fields * adds tests for extension Change-Id: Ieb4cbbf07b211697d08178b1cf2252caf75049a2 --- nova/tests/api/ec2/test_cloud.py | 11 ++-- .../openstack/v2/contrib/test_floating_ip_pools.py | 73 ++++++++++++++++++++++ .../api/openstack/v2/contrib/test_floating_ips.py | 17 ++++- nova/tests/api/openstack/v2/test_extensions.py | 2 + nova/tests/db/fakes.py | 7 ++- nova/tests/test_network.py | 19 ++++++ 6 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 nova/tests/api/openstack/v2/contrib/test_floating_ip_pools.py (limited to 'nova/tests') diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py index 421c16adb..6015069bc 100644 --- a/nova/tests/api/ec2/test_cloud.py +++ b/nova/tests/api/ec2/test_cloud.py @@ -1,5 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. # Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. @@ -153,7 +154,7 @@ class CloudTestCase(test.TestCase): address = "10.10.10.10" db.floating_ip_create(self.context, {'address': address, - 'host': self.network.host}) + 'pool': 'nova'}) self.cloud.allocate_address(self.context) self.cloud.describe_addresses(self.context) self.cloud.release_address(self.context, @@ -165,7 +166,7 @@ class CloudTestCase(test.TestCase): allocate = self.cloud.allocate_address db.floating_ip_create(self.context, {'address': address, - 'host': self.network.host}) + 'pool': 'nova'}) self.assertEqual(allocate(self.context)['publicIp'], address) db.floating_ip_destroy(self.context, address) self.assertRaises(exception.NoMoreFloatingIps, @@ -177,7 +178,7 @@ class CloudTestCase(test.TestCase): allocate = self.cloud.allocate_address db.floating_ip_create(self.context, {'address': address, - 'host': self.network.host, + 'pool': 'nova', 'project_id': self.project_id}) result = self.cloud.release_address(self.context, address) self.assertEqual(result['releaseResponse'], ['Address released.']) @@ -185,7 +186,9 @@ class CloudTestCase(test.TestCase): def test_associate_disassociate_address(self): """Verifies associate runs cleanly without raising an exception""" address = "10.10.10.10" - db.floating_ip_create(self.context, {'address': address}) + db.floating_ip_create(self.context, + {'address': address, + 'pool': 'nova'}) self.cloud.allocate_address(self.context) # TODO(jkoelker) Probably need to query for instance_type_id and # make sure we get a valid one diff --git a/nova/tests/api/openstack/v2/contrib/test_floating_ip_pools.py b/nova/tests/api/openstack/v2/contrib/test_floating_ip_pools.py new file mode 100644 index 000000000..d061f9af3 --- /dev/null +++ b/nova/tests/api/openstack/v2/contrib/test_floating_ip_pools.py @@ -0,0 +1,73 @@ +# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. +# All Rights Reserved. +# +# 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 lxml import etree + +from nova.api.openstack.v2.contrib import floating_ip_pools +from nova import context +from nova import network +from nova import test +from nova.tests.api.openstack import fakes + + +def fake_get_floating_ip_pools(self, context): + return [{'name': 'nova'}, + {'name': 'other'}] + + +class FloatingIpPoolTest(test.TestCase): + def setUp(self): + super(FloatingIpPoolTest, self).setUp() + self.stubs.Set(network.api.API, "get_floating_ip_pools", + fake_get_floating_ip_pools) + + self.context = context.RequestContext('fake', 'fake') + self.controller = floating_ip_pools.FloatingIPPoolsController() + + def test_translate_floating_ip_pools_view(self): + pools = fake_get_floating_ip_pools(None, self.context) + view = floating_ip_pools._translate_floating_ip_pools_view(pools) + self.assertTrue('floating_ip_pools' in view) + self.assertEqual(view['floating_ip_pools'][0]['name'], + pools[0]['name']) + self.assertEqual(view['floating_ip_pools'][1]['name'], + pools[1]['name']) + + def test_floating_ips_pools_list(self): + req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ip-pools') + res_dict = self.controller.index(req) + + pools = fake_get_floating_ip_pools(None, self.context) + response = {'floating_ip_pools': pools} + self.assertEqual(res_dict, response) + + +class FloatingIpPoolSerializerTest(test.TestCase): + def test_index_serializer(self): + serializer = floating_ip_pools.FloatingIPPoolsSerializer() + text = serializer.serialize(dict( + floating_ip_pools=[ + dict(name='nova'), + dict(name='other') + ]), 'index') + + tree = etree.fromstring(text) + + self.assertEqual('floating_ip_pools', tree.tag) + self.assertEqual(2, len(tree)) + self.assertEqual('floating_ip_pool', tree[0].tag) + self.assertEqual('floating_ip_pool', tree[1].tag) + self.assertEqual('nova', tree[0].get('name')) + self.assertEqual('other', tree[1].get('name')) diff --git a/nova/tests/api/openstack/v2/contrib/test_floating_ips.py b/nova/tests/api/openstack/v2/contrib/test_floating_ips.py index d1a0d5795..ffdace53c 100644 --- a/nova/tests/api/openstack/v2/contrib/test_floating_ips.py +++ b/nova/tests/api/openstack/v2/contrib/test_floating_ips.py @@ -1,3 +1,4 @@ +# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. # Copyright 2011 Eldar Nugaev # All Rights Reserved. # @@ -30,11 +31,13 @@ FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' def network_api_get_floating_ip(self, context, id): return {'id': 1, 'address': '10.10.10.10', + 'pool': 'nova', 'fixed_ip': None} def network_api_get_floating_ip_by_address(self, context, address): return {'id': 1, 'address': '10.10.10.10', + 'pool': 'nova', 'fixed_ip': {'address': '10.0.0.1', 'instance': {'uuid': FAKE_UUID}}} @@ -42,9 +45,11 @@ def network_api_get_floating_ip_by_address(self, context, address): def network_api_get_floating_ips_by_project(self, context): return [{'id': 1, 'address': '10.10.10.10', + 'pool': 'nova', 'fixed_ip': {'address': '10.0.0.1', 'instance': {'uuid': FAKE_UUID}}}, {'id': 2, + 'pool': 'nova', 'interface': 'eth0', 'address': '10.10.10.11'}] @@ -107,6 +112,7 @@ class FloatingIpTest(test.TestCase): host = "fake_host" return db.floating_ip_create(self.context, {'address': self.floating_ip, + 'pool': 'nova', 'host': host}) def _delete_floating_ip(self): @@ -151,7 +157,8 @@ class FloatingIpTest(test.TestCase): self.assertEqual(view['floating_ip']['instance_id'], None) def test_translate_floating_ip_view_dict(self): - floating_ip = {'id': 0, 'address': '10.0.0.10', 'fixed_ip': None} + floating_ip = {'id': 0, 'address': '10.0.0.10', 'pool': 'nova', + 'fixed_ip': None} view = floating_ips._translate_floating_ip_view(floating_ip) self.assertTrue('floating_ip' in view) @@ -161,10 +168,12 @@ class FloatingIpTest(test.TestCase): response = {'floating_ips': [{'instance_id': FAKE_UUID, 'ip': '10.10.10.10', + 'pool': 'nova', 'fixed_ip': '10.0.0.1', 'id': 1}, {'instance_id': None, 'ip': '10.10.10.11', + 'pool': 'nova', 'fixed_ip': None, 'id': 2}]} self.assertEqual(res_dict, response) @@ -180,6 +189,7 @@ class FloatingIpTest(test.TestCase): def test_show_associated_floating_ip(self): def get_floating_ip(self, context, id): return {'id': 1, 'address': '10.10.10.10', + 'pool': 'nova', 'fixed_ip': {'address': '10.0.0.1', 'instance': {'uuid': FAKE_UUID}}} self.stubs.Set(network.api.API, "get_floating_ip", get_floating_ip) @@ -207,7 +217,7 @@ class FloatingIpTest(test.TestCase): pass def fake2(*args, **kwargs): - return {'id': 1, 'address': '10.10.10.10'} + return {'id': 1, 'address': '10.10.10.10', 'pool': 'nova'} self.stubs.Set(network.api.API, "allocate_floating_ip", fake1) @@ -223,7 +233,8 @@ class FloatingIpTest(test.TestCase): "id": 1, "instance_id": None, "ip": "10.10.10.10", - "fixed_ip": None} + "fixed_ip": None, + "pool": 'nova'} self.assertEqual(ip, expected) def test_floating_ip_release(self): diff --git a/nova/tests/api/openstack/v2/test_extensions.py b/nova/tests/api/openstack/v2/test_extensions.py index 51af89da3..70a936727 100644 --- a/nova/tests/api/openstack/v2/test_extensions.py +++ b/nova/tests/api/openstack/v2/test_extensions.py @@ -1,5 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. # Copyright 2011 OpenStack LLC. # All Rights Reserved. # @@ -110,6 +111,7 @@ class ExtensionControllerTest(ExtensionTestCase): "FlavorExtraData", "Floating_ips", "Floating_ip_dns", + "Floating_ip_pools", "Fox In Socks", "Hosts", "Keypairs", diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index a28d6ded0..ef3162eb4 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -1,5 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. # Copyright 2010 OpenStack, LLC # All Rights Reserved. # @@ -88,6 +89,7 @@ def stub_out_db_network_api(stubs): 'fixed_ip_id': None, 'fixed_ip': None, 'project_id': None, + 'pool': 'nova', 'auto_assigned': False} virtual_interface_fields = {'id': 0, @@ -101,9 +103,10 @@ def stub_out_db_network_api(stubs): virtual_interfacees = [virtual_interface_fields] networks = [network_fields] - def fake_floating_ip_allocate_address(context, project_id): + def fake_floating_ip_allocate_address(context, project_id, pool): ips = filter(lambda i: i['fixed_ip_id'] is None \ - and i['project_id'] is None, + and i['project_id'] is None \ + and i['pool'] == pool, floating_ips) if not ips: raise exception.NoMoreFloatingIps() diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 4eee3c8d4..274d5dd86 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2011 Rackspace +# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -108,6 +109,8 @@ flavor = {'id': 0, floating_ip_fields = {'id': 0, 'address': '192.168.10.100', + 'pool': 'nova', + 'interface': 'eth0', 'fixed_ip_id': 0, 'project_id': None, 'auto_assigned': False} @@ -580,21 +583,29 @@ class VlanNetworkTestCase(test.TestCase): # floating ip that's already associated def fake2(*args, **kwargs): return {'address': '10.0.0.1', + 'pool': 'nova', + 'interface': 'eth0', 'fixed_ip_id': 1} # floating ip that isn't associated def fake3(*args, **kwargs): return {'address': '10.0.0.1', + 'pool': 'nova', + 'interface': 'eth0', 'fixed_ip_id': None} # fixed ip with remote host def fake4(*args, **kwargs): return {'address': '10.0.0.1', + 'pool': 'nova', + 'interface': 'eth0', 'network': {'multi_host': False, 'host': 'jibberjabber'}} # fixed ip with local host def fake5(*args, **kwargs): return {'address': '10.0.0.1', + 'pool': 'nova', + 'interface': 'eth0', 'network': {'multi_host': False, 'host': 'testhost'}} def fake6(*args, **kwargs): @@ -641,21 +652,29 @@ class VlanNetworkTestCase(test.TestCase): # floating ip that isn't associated def fake2(*args, **kwargs): return {'address': '10.0.0.1', + 'pool': 'nova', + 'interface': 'eth0', 'fixed_ip_id': None} # floating ip that is associated def fake3(*args, **kwargs): return {'address': '10.0.0.1', + 'pool': 'nova', + 'interface': 'eth0', 'fixed_ip_id': 1} # fixed ip with remote host def fake4(*args, **kwargs): return {'address': '10.0.0.1', + 'pool': 'nova', + 'interface': 'eth0', 'network': {'multi_host': False, 'host': 'jibberjabber'}} # fixed ip with local host def fake5(*args, **kwargs): return {'address': '10.0.0.1', + 'pool': 'nova', + 'interface': 'eth0', 'network': {'multi_host': False, 'host': 'testhost'}} def fake6(*args, **kwargs): -- cgit