Handle neutron exception on bad floating ip create request

This commit adds a new nova exception to handle a floating ip create
failure with a BadRequest exception from neutronclient. This is then
used in all 3 nova api implementations to ensure a 400 response is
returned just as neutron is returning to nova.

Change-Id: I36e8ef0113ae91e3b15c846c6d10e0b766cf2a37
Closes-Bug: #1482816
This commit is contained in:
Matthew Treinish 2015-08-07 23:17:39 -04:00
parent 19810750b4
commit f500f99aad
No known key found for this signature in database
GPG Key ID: FD12A0F214C9E177
8 changed files with 49 additions and 1 deletions

View File

@ -616,6 +616,7 @@ class Executor(wsgi.Application):
except (exception.CannotDisassociateAutoAssignedFloatingIP,
exception.FloatingIpAssociated,
exception.FloatingIpNotFound,
exception.FloatingIpBadRequest,
exception.ImageNotActive,
exception.InvalidInstanceIDMalformed,
exception.InvalidVolumeIDMalformed,

View File

@ -118,7 +118,7 @@ class FloatingIPController(object):
return _translate_floating_ips_view(floating_ips)
@extensions.expected_errors((403, 404))
@extensions.expected_errors((400, 403, 404))
def create(self, req, body=None):
context = req.environ['nova.context']
authorize(context)
@ -143,6 +143,8 @@ class FloatingIPController(object):
raise webob.exc.HTTPForbidden(explanation=msg)
except exception.FloatingIpPoolNotFound as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
except exception.FloatingIpBadRequest as e:
raise webob.exc.HTTPBadRequest(explanation=e.format_message())
return _translate_floating_ip_view(ip)

View File

@ -128,6 +128,8 @@ class FloatingIPController(object):
raise webob.exc.HTTPForbidden(explanation=msg)
except exception.FloatingIpPoolNotFound as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
except exception.FloatingIpBadRequest as e:
raise webob.exc.HTTPBadRequest(explanation=e.format_message())
return _translate_floating_ip_view(ip)

View File

@ -900,6 +900,11 @@ class FloatingIpAssociateFailed(NovaException):
msg_fmt = _("Floating IP %(address)s association has failed.")
class FloatingIpBadRequest(Invalid):
ec2_code = "UnsupportedOperation"
msg_fmt = _("The floating IP request failed with a BadRequest")
class CannotDisassociateAutoAssignedFloatingIP(NovaException):
ec2_code = "UnsupportedOperation"
msg_fmt = _("Cannot disassociate auto assigned floating ip")

View File

@ -1399,6 +1399,8 @@ class API(base_api.NetworkAPI):
raise exception.NoMoreFloatingIps(six.text_type(e))
except neutron_client_exc.OverQuotaClient as e:
raise exception.FloatingIpLimitExceeded(six.text_type(e))
except neutron_client_exc.BadRequest as e:
raise exception.FloatingIpBadRequest(six.text_type(e))
return fip['floatingip']['floating_ip_address']

View File

@ -150,6 +150,13 @@ class ExecutorTestCase(test.NoDBTestCase):
self.assertIn('vol-00000005', self._extract_message(result))
self.assertEqual('InvalidVolume.NotFound', self._extract_code(result))
def test_floating_ip_bad_create_request(self):
def bad_request(context):
raise exception.FloatingIpBadRequest()
result = self._execute(bad_request)
self.assertIn('BadRequest', self._extract_message(result))
self.assertEqual('UnsupportedOperation', self._extract_code(result))
class FakeResponse(object):
reason = "Test Reason"

View File

@ -18,6 +18,7 @@ import contextlib
import uuid
import mock
import six
import webob
from nova.api.openstack.compute import floating_ips as fips_v21
@ -428,6 +429,18 @@ class FloatingIpTestV21(test.TestCase):
self.assertIn('No more floating ips in pool non_existent_pool',
ex.explanation)
@mock.patch.object(network.api.API, 'allocate_floating_ip',
side_effect=exception.FloatingIpBadRequest(
'Bad floatingip request: Network '
'c8f0e88f-ae41-47cb-be6c-d8256ba80576 does not contain any '
'IPv4 subnet'))
def test_floating_ip_allocate_no_ipv4_subnet(self, allocate_mock):
ex = self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.create, self.fake_req,
{'pool': 'non_existent_pool'})
self.assertIn("does not contain any IPv4 subnet",
six.text_type(ex))
@mock.patch('nova.network.api.API.allocate_floating_ip',
side_effect=exception.FloatingIpLimitExceeded())
def test_floating_ip_allocate_over_quota(self, allocate_mock):

View File

@ -3018,6 +3018,22 @@ class TestNeutronv2WithMock(test.TestCase):
api.allocate_floating_ip,
self.context, pool_name)
def test_allocate_floating_ip_no_ipv4_subnet(self):
api = neutronapi.API()
net_id = uuid.uuid4()
error_msg = ('Bad floatingip request: Network %s does not contain '
'any IPv4 subnet' % net_id)
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 = exceptions.BadRequest(error_msg)
self.assertRaises(exception.FloatingIpBadRequest,
api.allocate_floating_ip, self.context,
'ext_net')
def test_create_port_for_instance_no_more_ip(self):
instance = fake_instance.fake_instance_obj(self.context)
net = {'id': 'my_netid1',