From 8ff0fae42bce9f98fd5897888c3c4c739b4b5f45 Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Thu, 4 Apr 2019 14:44:02 -0400 Subject: [PATCH] Handle PortLimitExceeded in POST /servers/{server_id}/os-interface When attaching an interface to a server, if an existing port is not specified, nova-compute will attempt to create a port on either the user-specified network or the network that is available to the tenant. If the tenant exceeds their port quota in neutron, a PortLimitExceeded exception is raised up from nova-compute [1] which is not being handled in the API controller code - which is fixed in this change. Note that this is one of the few synchronous RPC call operations [2] in the compute API so exceptions from the compute service will leak back to the API like in this case and need to be handled to avoid a 500 response to the user. The 403 response used here matches how PortLimitExceeded is handled in the server create API [3]. [1] https://github.com/openstack/nova/blob/6ebb2c4ca/nova/network/neutronv2/api.py#L565 [2] https://github.com/openstack/nova/blob/6ebb2c4ca/nova/compute/rpcapi.py#L489 [3] https://github.com/openstack/nova/blob/6ebb2c4ca/nova/api/openstack/compute/servers.py#L688 Change-Id: I5687480a22542eea31d299442837bd64bf731285 Closes-Bug: #1823203 --- nova/api/openstack/compute/attach_interfaces.py | 4 +++- .../openstack/compute/test_attach_interfaces.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/nova/api/openstack/compute/attach_interfaces.py b/nova/api/openstack/compute/attach_interfaces.py index 91040781e887..a027b33664bb 100644 --- a/nova/api/openstack/compute/attach_interfaces.py +++ b/nova/api/openstack/compute/attach_interfaces.py @@ -133,7 +133,7 @@ class InterfaceAttachmentController(wsgi.Controller): context, port_info['port'], show_tag=api_version_request.is_supported(req, '2.70'))} - @wsgi.expected_errors((400, 404, 409, 500, 501)) + @wsgi.expected_errors((400, 403, 404, 409, 500, 501)) @validation.schema(attach_interfaces.create, '2.0', '2.48') @validation.schema(attach_interfaces.create_v249, '2.49') def create(self, req, server_id, body): @@ -183,6 +183,8 @@ class InterfaceAttachmentController(wsgi.Controller): except (exception.PortNotFound, exception.NetworkNotFound) as e: raise exc.HTTPNotFound(explanation=e.format_message()) + except exception.PortLimitExceeded as e: + raise exc.HTTPForbidden(explanation=e.format_message()) except exception.InterfaceAttachFailed as e: raise webob.exc.HTTPInternalServerError( explanation=e.format_message()) diff --git a/nova/tests/unit/api/openstack/compute/test_attach_interfaces.py b/nova/tests/unit/api/openstack/compute/test_attach_interfaces.py index 5492b1aac187..c1d61679828c 100644 --- a/nova/tests/unit/api/openstack/compute/test_attach_interfaces.py +++ b/nova/tests/unit/api/openstack/compute/test_attach_interfaces.py @@ -14,6 +14,7 @@ # under the License. import mock +import six from webob import exc from nova.api.openstack import common @@ -308,6 +309,20 @@ class InterfaceAttachTestsV21(test.NoDBTestCase): self.attachments.create, self.req, FAKE_UUID1, body=body) + def test_attach_interface_port_limit_exceeded(self): + """Tests the scenario where nova-compute attempts to create a port to + attach but the tenant port quota is exceeded and PortLimitExceeded + is raised from the neutron API code which results in a 403 response. + """ + with mock.patch.object(self.attachments.compute_api, + 'attach_interface', + side_effect=exception.PortLimitExceeded): + body = {'interfaceAttachment': {}} + ex = self.assertRaises( + exc.HTTPForbidden, self.attachments.create, + self.req, FAKE_UUID1, body=body) + self.assertIn('Maximum number of ports exceeded', six.text_type(ex)) + def test_detach_interface_with_invalid_state(self): def fake_detach_interface_invalid_state(*args, **kwargs): raise exception.InstanceInvalidState(