diff --git a/nova/api/openstack/compute/contrib/floating_ips.py b/nova/api/openstack/compute/contrib/floating_ips.py index 33f7deae0902..bc1f18eb56f4 100644 --- a/nova/api/openstack/compute/contrib/floating_ips.py +++ b/nova/api/openstack/compute/contrib/floating_ips.py @@ -163,6 +163,12 @@ class FloatingIPController(object): else: msg = _("No more floating ips available.") raise webob.exc.HTTPNotFound(explanation=msg) + except exception.FloatingIpLimitExceeded: + if pool: + msg = _("IP allocation over quota in pool %s.") % pool + else: + msg = _("IP allocation over quota.") + raise webob.exc.HTTPForbidden(explanation=msg) return _translate_floating_ip_view(ip) diff --git a/nova/network/neutronv2/api.py b/nova/network/neutronv2/api.py index 6dc6b66a1121..d70cff7ebc64 100644 --- a/nova/network/neutronv2/api.py +++ b/nova/network/neutronv2/api.py @@ -975,15 +975,15 @@ class API(base_api.NetworkAPI): pool = pool or CONF.default_floating_pool pool_id = self._get_floating_ip_pool_id_by_name_or_id(client, pool) - # TODO(amotoki): handle exception during create_floatingip() - # At this timing it is ensured that a network for pool exists. - # quota error may be returned. param = {'floatingip': {'floating_network_id': pool_id}} try: fip = client.create_floatingip(param) except (neutron_client_exc.IpAddressGenerationFailureClient, neutron_client_exc.ExternalIpAddressExhaustedClient) as e: raise exception.NoMoreFloatingIps(unicode(e)) + except neutron_client_exc.OverQuotaClient as e: + raise exception.FloatingIpLimitExceeded(unicode(e)) + return fip['floatingip']['floating_ip_address'] def _get_floating_ip_by_address(self, client, address): 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 d03150d0055e..e82c5d84a9ed 100644 --- a/nova/tests/api/openstack/compute/contrib/test_floating_ips.py +++ b/nova/tests/api/openstack/compute/contrib/test_floating_ips.py @@ -325,6 +325,25 @@ class FloatingIpTest(test.TestCase): self.assertIn('No more floating ips in pool non_existent_pool', ex.explanation) + @mock.patch('nova.network.api.API.allocate_floating_ip', + side_effect=exception.FloatingIpLimitExceeded()) + def test_floating_ip_allocate_over_quota(self, allocate_mock): + req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips') + ex = self.assertRaises(webob.exc.HTTPForbidden, + self.controller.create, req) + + self.assertIn('IP allocation over quota', ex.explanation) + + @mock.patch('nova.network.api.API.allocate_floating_ip', + side_effect=exception.FloatingIpLimitExceeded()) + def test_floating_ip_allocate_quota_exceed_in_pool(self, allocate_mock): + req = fakes.HTTPRequest.blank('/v2/fake/os-floating-ips') + ex = self.assertRaises(webob.exc.HTTPForbidden, + self.controller.create, req, {'pool': 'non_existent_pool'}) + + self.assertIn('IP allocation over quota in pool non_existent_pool.', + ex.explanation) + def test_floating_ip_allocate(self): def fake1(*args, **kwargs): pass diff --git a/nova/tests/network/test_neutronv2.py b/nova/tests/network/test_neutronv2.py index dab3bc8ff500..ff8cf3c29a6e 100644 --- a/nova/tests/network/test_neutronv2.py +++ b/nova/tests/network/test_neutronv2.py @@ -2311,6 +2311,21 @@ class TestNeutronv2WithMock(test.TestCase): list_ports_mock.assert_called_once_with(**list_port_mock_params) + def test_allocate_floating_ip_exceed_limit(self): + # Verify that the correct exception is thrown when quota exceed + pool_name = 'dummy' + api = neutronapi.API() + with contextlib.nested( + mock.patch.object(client.Client, 'create_floatingip'), + mock.patch.object(api, + '_get_floating_ip_pool_id_by_name_or_id')) as ( + create_mock, get_mock): + create_mock.side_effect = neutronv2.exceptions.OverQuotaClient() + + self.assertRaises(exception.FloatingIpLimitExceeded, + api.allocate_floating_ip, + self.context, pool_name) + class TestNeutronv2ModuleMethods(test.TestCase):