From 6478554f531f6ee2fa86226fbc79dd31e556bc06 Mon Sep 17 00:00:00 2001 From: liu-sheng Date: Wed, 19 Mar 2014 11:44:22 +0800 Subject: [PATCH] Allow admin user to get all tenant's floating IPs When getting floatingips by Nova API, the results will be filtered with the 'tenant_id'. So, we can only get the floatingips belonging the tenant of current context. When ceilometer invokes novaclient to list floatingips, it will get an empty list because the tenant is 'service'. we should allow an admin user to index all tenants's floatingip by adding a parameter 'all_tenants'. Part1: part1 try to implement this and part2 will remove the unused codes. Change-Id: I7ab1d5ff463fc29928f6811f846c9e204390a412 Closes-bug: #1262124 --- etc/nova/policy.json | 2 + nova/api/ec2/cloud.py | 2 +- .../openstack/compute/contrib/floating_ips.py | 15 +++++- nova/network/api.py | 8 +++ nova/network/neutronv2/api.py | 14 +++++ .../compute/contrib/test_floating_ips.py | 51 +++++++++++++++++++ nova/tests/fake_policy.py | 2 + nova/tests/network/test_api.py | 18 +++++++ nova/tests/network/test_neutronv2.py | 46 +++++++++++++++++ 9 files changed, 155 insertions(+), 3 deletions(-) diff --git a/etc/nova/policy.json b/etc/nova/policy.json index 949c9e9f1e..a9933f50cd 100644 --- a/etc/nova/policy.json +++ b/etc/nova/policy.json @@ -141,6 +141,7 @@ "compute_extension:floating_ip_dns": "", "compute_extension:floating_ip_pools": "", "compute_extension:floating_ips": "", + "compute_extension:floating_ips:all_tenants": "rule:admin_api", "compute_extension:floating_ips_bulk": "rule:admin_api", "compute_extension:fping": "", "compute_extension:fping:all_tenants": "rule:admin_api", @@ -292,6 +293,7 @@ "network:get_backdoor_port": "", "network:get_floating_ip": "", + "network:get_floating_ips": "", "network:get_floating_ip_pools": "", "network:get_floating_ip_by_address": "", "network:get_floating_ips_by_project": "", diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 6f6046e6fe..a7cc37c459 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -1212,7 +1212,7 @@ class CloudController(object): address) floatings.append(floating) else: - floatings = self.network_api.get_floating_ips_by_project(context) + floatings = self.network_api.get_floating_ips(context) addresses = [self._format_address(context, f) for f in floatings] return {'addressesSet': addresses} diff --git a/nova/api/openstack/compute/contrib/floating_ips.py b/nova/api/openstack/compute/contrib/floating_ips.py index 0bf9bd3ba3..bc741ce2d8 100644 --- a/nova/api/openstack/compute/contrib/floating_ips.py +++ b/nova/api/openstack/compute/contrib/floating_ips.py @@ -27,11 +27,14 @@ from nova import exception from nova import network from nova.openstack.common.gettextutils import _ from nova.openstack.common import log as logging +from nova.openstack.common import strutils from nova.openstack.common import uuidutils LOG = logging.getLogger(__name__) authorize = extensions.extension_authorizer('compute', 'floating_ips') +authorize_all_tenants = extensions.extension_authorizer( + 'compute', 'floating_ips:all_tenants') def make_float_ip(elem): @@ -135,11 +138,19 @@ class FloatingIPController(object): @wsgi.serializers(xml=FloatingIPsTemplate) def index(self, req): - """Return a list of floating ips allocated to a project.""" + """Return a list of floating ips.""" context = req.environ['nova.context'] authorize(context) + all_tenants = False + if 'all_tenants' in req.GET: + try: + if strutils.bool_from_string(req.GET['all_tenants'], True): + authorize_all_tenants(context) + all_tenants = True + except ValueError as err: + raise webob.exc.HTTPBadRequest(explanation=str(err)) - floating_ips = self.network_api.get_floating_ips_by_project(context) + floating_ips = self.network_api.get_floating_ips(context, all_tenants) for floating_ip in floating_ips: self._normalize_ip(floating_ip) diff --git a/nova/network/api.py b/nova/network/api.py index 7e5ee250e3..d6ba4afd1c 100644 --- a/nova/network/api.py +++ b/nova/network/api.py @@ -128,6 +128,14 @@ class API(base_api.NetworkAPI): return self.db.floating_ip_get_all_by_project(context, context.project_id) + @wrap_check_policy + def get_floating_ips(self, context, all_tenants=False): + if all_tenants: + return self.db.floating_ip_get_all(context) + else: + return self.db.floating_ip_get_all_by_project(context, + context.project_id) + @wrap_check_policy def get_floating_ips_by_fixed_address(self, context, fixed_address): floating_ips = self.db.floating_ip_get_by_fixed_address(context, diff --git a/nova/network/neutronv2/api.py b/nova/network/neutronv2/api.py index f397cbe8ef..a562834be9 100644 --- a/nova/network/neutronv2/api.py +++ b/nova/network/neutronv2/api.py @@ -877,6 +877,20 @@ class API(base_api.NetworkAPI): return [self._format_floating_ip_model(fip, pool_dict, port_dict) for fip in fips] + def get_floating_ips(self, context, all_tenants=False): + client = neutronv2.get_client(context) + project_id = context.project_id + if all_tenants: + fips = client.list_floatingips()['floatingips'] + port_dict = self._setup_ports_dict(client) + else: + fips = client.list_floatingips( + tenant_id=project_id)['floatingips'] + port_dict = self._setup_ports_dict(client, project_id) + pool_dict = self._setup_pools_dict(client) + return [self._format_floating_ip_model(fip, pool_dict, port_dict) + for fip in fips] + def get_floating_ips_by_fixed_address(self, context, fixed_address): raise NotImplementedError() diff --git a/nova/tests/api/openstack/compute/contrib/test_floating_ips.py b/nova/tests/api/openstack/compute/contrib/test_floating_ips.py index 20194cf74f..9766fe4f23 100644 --- a/nova/tests/api/openstack/compute/contrib/test_floating_ips.py +++ b/nova/tests/api/openstack/compute/contrib/test_floating_ips.py @@ -42,6 +42,24 @@ def network_api_get_floating_ip(self, context, id): 'fixed_ip_id': None} +def network_api_get_floating_ips(self, context, all_tenants=False): + ret = [{'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', + 'fixed_ip': None}] + if all_tenants: + ret.append({'id': 3, + 'pool': 'nova', 'interface': 'eth1', + 'address': '10.10.10.12', + 'fixed_ip': None}) + return ret + + def network_api_get_floating_ip_by_address(self, context, address): return {'id': 1, 'address': '10.10.10.10', 'pool': 'nova', 'fixed_ip_id': 10} @@ -132,6 +150,8 @@ class FloatingIpTest(test.TestCase): compute_api_get) self.stubs.Set(network.api.API, "get_floating_ip", network_api_get_floating_ip) + self.stubs.Set(network.api.API, "get_floating_ips", + network_api_get_floating_ips) self.stubs.Set(network.api.API, "get_floating_ip_by_address", network_api_get_floating_ip_by_address) self.stubs.Set(network.api.API, "get_floating_ips_by_project", @@ -202,6 +222,35 @@ class FloatingIpTest(test.TestCase): 'id': 2}]} self.assertEqual(res_dict, response) + def test_floating_ips_list_all_tenants_admin(self): + req = fakes.HTTPRequest.blank( + '/v2/fake/os-floating-ips/?all_tenants=1', + use_admin_context=True) + res_dict = self.controller.index(req) + + 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}, + {'instance_id': None, + 'ip': '10.10.10.12', + 'pool': 'nova', + 'fixed_ip': None, + 'id': 3}]} + self.assertEqual(response, res_dict) + + def test_floating_ips_list_all_tenants_not_admin(self): + req = fakes.HTTPRequest.blank( + '/v2/fake/os-floating-ips/?all_tenants=1', + use_admin_context=False) + self.assertRaises(exception.Forbidden, self.controller.index, req) + def test_floating_ip_release_nonexisting(self): def fake_get_floating_ip(*args, **kwargs): raise exception.FloatingIpNotFound(id=id) @@ -625,6 +674,8 @@ class ExtendedFloatingIpTest(test.TestCase): compute_api_get) self.stubs.Set(network.api.API, "get_floating_ip", network_api_get_floating_ip) + self.stubs.Set(network.api.API, "get_floating_ips", + network_api_get_floating_ips) self.stubs.Set(network.api.API, "get_floating_ip_by_address", network_api_get_floating_ip_by_address) self.stubs.Set(network.api.API, "get_floating_ips_by_project", diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py index 1044a93619..3ad3f6a7d5 100644 --- a/nova/tests/fake_policy.py +++ b/nova/tests/fake_policy.py @@ -199,6 +199,7 @@ policy_data = """ "compute_extension:floating_ip_dns": "", "compute_extension:floating_ip_pools": "", "compute_extension:floating_ips": "", + "compute_extension:floating_ips:all_tenants": "rule:admin_api", "compute_extension:floating_ips_bulk": "", "compute_extension:fping": "", "compute_extension:fping:all_tenants": "is_admin:True", @@ -345,6 +346,7 @@ policy_data = """ "network:setup_networks_on_host": "", "network:get_floating_ip": "", + "network:get_floating_ips": "", "network:get_floating_ip_pools": "", "network:get_floating_ip_by_address": "", "network:get_floating_ips_by_project": "", diff --git a/nova/tests/network/test_api.py b/nova/tests/network/test_api.py index 7ecd832b15..2a5f0b9d03 100644 --- a/nova/tests/network/test_api.py +++ b/nova/tests/network/test_api.py @@ -155,6 +155,24 @@ class ApiTestCase(test.TestCase): def test_associate_unassociated_floating_ip(self): self._do_test_associate_floating_ip(None) + def test_get_floating_ips(self): + self.mox.StubOutWithMock(self.network_api.db, 'floating_ip_get_all') + self.network_api.db.floating_ip_get_all(self.context).AndReturn( + ['project1-floating-ip', 'project2-floating-ip']) + self.mox.StubOutWithMock(self.network_api.db, + 'floating_ip_get_all_by_project') + self.network_api.db.\ + floating_ip_get_all_by_project(self.context, + self.context.project_id).\ + AndReturn(['project1-floating-ip']) + self.mox.ReplayAll() + floating_ips = self.network_api.get_floating_ips(self.context) + floating_ips_all = self.network_api.get_floating_ips(self.context, + all_tenants=True) + self.assertEqual(['project1-floating-ip'], floating_ips) + self.assertEqual(['project1-floating-ip', 'project2-floating-ip'], + floating_ips_all) + def test_get_floating_ip_invalid_id(self): self.assertRaises(exception.InvalidID, self.network_api.get_floating_ip, diff --git a/nova/tests/network/test_neutronv2.py b/nova/tests/network/test_neutronv2.py index ea30b851bf..93d7bfadcf 100644 --- a/nova/tests/network/test_neutronv2.py +++ b/nova/tests/network/test_neutronv2.py @@ -268,6 +268,14 @@ class TestNeutronv2Base(test.TestCase): 'port_id': None, 'fixed_ip_address': None, 'router_id': None} + self.fip_unassociated_not_my = {'tenant_id': 'not_my_tenantid', + 'id': 'fip_id3', + 'floating_ip_address': '172.24.4.227', + 'floating_network_id': self.fip_pool[ + 'id'], + 'port_id': None, + 'fixed_ip_address': None, + 'router_id': None} fixed_ip_address = self.port_data2[1]['fixed_ips'][0]['ip_address'] self.fip_associated = {'tenant_id': 'my_tenantid', 'id': 'fip_id2', @@ -1615,6 +1623,44 @@ class TestNeutronv2(TestNeutronv2Base): fips = api.get_floating_ips_by_project(self.context) self.assertEqual(expected, fips) + def test_get_floating_ips(self): + api = neutronapi.API() + project_id = self.context.project_id + self.moxed_client.list_floatingips(tenant_id=project_id).\ + AndReturn({'floatingips': [self.fip_unassociated, + self.fip_associated]}) + + self.moxed_client.list_ports(tenant_id=project_id). \ + AndReturn({'ports': self.port_data2}) + search_opts = {'router:external': True} + self.moxed_client.list_networks(**search_opts). \ + AndReturn({'networks': [self.fip_pool, self.fip_pool_nova]}) + self.mox.ReplayAll() + + expected = [self._get_expected_fip_model(self.fip_unassociated), + self._get_expected_fip_model(self.fip_associated, idx=1)] + fips = api.get_floating_ips(self.context) + self.assertEqual(expected, fips) + + def test_get_floating_ips_all_tenants(self): + api = neutronapi.API() + self.moxed_client.list_floatingips(). \ + AndReturn({'floatingips': [self.fip_unassociated, + self.fip_associated, + self.fip_unassociated_not_my]}) + self.moxed_client.list_ports().AndReturn({'ports': self.port_data2}) + search_opts = {'router:external': True} + self.moxed_client.list_networks(**search_opts). \ + AndReturn({'networks': [self.fip_pool, self.fip_pool_nova]}) + self.mox.ReplayAll() + + expected = [self._get_expected_fip_model(self.fip_unassociated), + self._get_expected_fip_model(self.fip_associated, idx=1), + self._get_expected_fip_model(self.fip_unassociated_not_my, + idx=2)] + fips = api.get_floating_ips(self.context, all_tenants=True) + self.assertEqual(expected, fips) + def _test_get_instance_id_by_floating_address(self, fip_data, associated=False): api = neutronapi.API()