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()