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:
@@ -616,6 +616,7 @@ class Executor(wsgi.Application):
|
|||||||
except (exception.CannotDisassociateAutoAssignedFloatingIP,
|
except (exception.CannotDisassociateAutoAssignedFloatingIP,
|
||||||
exception.FloatingIpAssociated,
|
exception.FloatingIpAssociated,
|
||||||
exception.FloatingIpNotFound,
|
exception.FloatingIpNotFound,
|
||||||
|
exception.FloatingIpBadRequest,
|
||||||
exception.ImageNotActive,
|
exception.ImageNotActive,
|
||||||
exception.InvalidInstanceIDMalformed,
|
exception.InvalidInstanceIDMalformed,
|
||||||
exception.InvalidVolumeIDMalformed,
|
exception.InvalidVolumeIDMalformed,
|
||||||
|
@@ -118,7 +118,7 @@ class FloatingIPController(object):
|
|||||||
|
|
||||||
return _translate_floating_ips_view(floating_ips)
|
return _translate_floating_ips_view(floating_ips)
|
||||||
|
|
||||||
@extensions.expected_errors((403, 404))
|
@extensions.expected_errors((400, 403, 404))
|
||||||
def create(self, req, body=None):
|
def create(self, req, body=None):
|
||||||
context = req.environ['nova.context']
|
context = req.environ['nova.context']
|
||||||
authorize(context)
|
authorize(context)
|
||||||
@@ -143,6 +143,8 @@ class FloatingIPController(object):
|
|||||||
raise webob.exc.HTTPForbidden(explanation=msg)
|
raise webob.exc.HTTPForbidden(explanation=msg)
|
||||||
except exception.FloatingIpPoolNotFound as e:
|
except exception.FloatingIpPoolNotFound as e:
|
||||||
raise webob.exc.HTTPNotFound(explanation=e.format_message())
|
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)
|
return _translate_floating_ip_view(ip)
|
||||||
|
|
||||||
|
@@ -128,6 +128,8 @@ class FloatingIPController(object):
|
|||||||
raise webob.exc.HTTPForbidden(explanation=msg)
|
raise webob.exc.HTTPForbidden(explanation=msg)
|
||||||
except exception.FloatingIpPoolNotFound as e:
|
except exception.FloatingIpPoolNotFound as e:
|
||||||
raise webob.exc.HTTPNotFound(explanation=e.format_message())
|
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)
|
return _translate_floating_ip_view(ip)
|
||||||
|
|
||||||
|
@@ -900,6 +900,11 @@ class FloatingIpAssociateFailed(NovaException):
|
|||||||
msg_fmt = _("Floating IP %(address)s association has failed.")
|
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):
|
class CannotDisassociateAutoAssignedFloatingIP(NovaException):
|
||||||
ec2_code = "UnsupportedOperation"
|
ec2_code = "UnsupportedOperation"
|
||||||
msg_fmt = _("Cannot disassociate auto assigned floating ip")
|
msg_fmt = _("Cannot disassociate auto assigned floating ip")
|
||||||
|
@@ -1399,6 +1399,8 @@ class API(base_api.NetworkAPI):
|
|||||||
raise exception.NoMoreFloatingIps(six.text_type(e))
|
raise exception.NoMoreFloatingIps(six.text_type(e))
|
||||||
except neutron_client_exc.OverQuotaClient as e:
|
except neutron_client_exc.OverQuotaClient as e:
|
||||||
raise exception.FloatingIpLimitExceeded(six.text_type(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']
|
return fip['floatingip']['floating_ip_address']
|
||||||
|
|
||||||
|
@@ -150,6 +150,13 @@ class ExecutorTestCase(test.NoDBTestCase):
|
|||||||
self.assertIn('vol-00000005', self._extract_message(result))
|
self.assertIn('vol-00000005', self._extract_message(result))
|
||||||
self.assertEqual('InvalidVolume.NotFound', self._extract_code(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):
|
class FakeResponse(object):
|
||||||
reason = "Test Reason"
|
reason = "Test Reason"
|
||||||
|
@@ -18,6 +18,7 @@ import contextlib
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
import six
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from nova.api.openstack.compute import floating_ips as fips_v21
|
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',
|
self.assertIn('No more floating ips in pool non_existent_pool',
|
||||||
ex.explanation)
|
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',
|
@mock.patch('nova.network.api.API.allocate_floating_ip',
|
||||||
side_effect=exception.FloatingIpLimitExceeded())
|
side_effect=exception.FloatingIpLimitExceeded())
|
||||||
def test_floating_ip_allocate_over_quota(self, allocate_mock):
|
def test_floating_ip_allocate_over_quota(self, allocate_mock):
|
||||||
|
@@ -3018,6 +3018,22 @@ class TestNeutronv2WithMock(test.TestCase):
|
|||||||
api.allocate_floating_ip,
|
api.allocate_floating_ip,
|
||||||
self.context, pool_name)
|
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):
|
def test_create_port_for_instance_no_more_ip(self):
|
||||||
instance = fake_instance.fake_instance_obj(self.context)
|
instance = fake_instance.fake_instance_obj(self.context)
|
||||||
net = {'id': 'my_netid1',
|
net = {'id': 'my_netid1',
|
||||||
|
Reference in New Issue
Block a user