Handle OpenStack API faults
We should start handling the OpenStack Faults, in case they exist. The easiest way is checking if the status code belongs to one of the HTTP error codes. If this is the case, we convert it back to a webob exception and raise it so that it is eventually handled in our WSGI middleware.
This commit is contained in:
parent
c96b600452
commit
f53f9744da
|
@ -16,6 +16,8 @@
|
|||
|
||||
from ooi.wsgi import utils
|
||||
|
||||
import webob.exc
|
||||
|
||||
|
||||
class Controller(object):
|
||||
def __init__(self, app, openstack_version):
|
||||
|
@ -31,3 +33,53 @@ class Controller(object):
|
|||
if body is not None:
|
||||
req.body = utils.utf8(body)
|
||||
return req
|
||||
|
||||
@staticmethod
|
||||
def get_from_response(response, element, default):
|
||||
"""Get a JSON element from a valid response or raise an exception.
|
||||
|
||||
This method will extract an element a JSON response (falling back to a
|
||||
default value) if the response has a code of 200, otherwise it will
|
||||
raise a webob.exc.exception
|
||||
|
||||
:param response: The webob.Response object
|
||||
:param element: The element to look for in the JSON body
|
||||
:param default: The default element to be returned if not found.
|
||||
"""
|
||||
if response.status_int == 200:
|
||||
return response.json_body.get(element, default)
|
||||
else:
|
||||
raise exception_from_response(response)
|
||||
|
||||
|
||||
def exception_from_response(response):
|
||||
"""Convert an OpenStack V2 Fault into a webob exception.
|
||||
|
||||
Since we are calling the OpenStack API we should process the Faults
|
||||
produced by them. Extract the Fault information according to [1] and
|
||||
convert it back to a webob exception.
|
||||
|
||||
[1] http://docs.openstack.org/developer/nova/v2/faults.html
|
||||
|
||||
:param response: a webob.Response containing an exception
|
||||
:returns: a webob.exc.exception object
|
||||
"""
|
||||
exceptions = {
|
||||
400: webob.exc.HTTPBadRequest,
|
||||
401: webob.exc.HTTPUnauthorized,
|
||||
403: webob.exc.HTTPForbidden,
|
||||
404: webob.exc.HTTPNotFound,
|
||||
405: webob.exc.HTTPMethodNotAllowed,
|
||||
406: webob.exc.HTTPNotAcceptable,
|
||||
409: webob.exc.HTTPConflict,
|
||||
413: webob.exc.HTTPRequestEntityTooLarge,
|
||||
415: webob.exc.HTTPUnsupportedMediaType,
|
||||
429: webob.exc.HTTPTooManyRequests,
|
||||
501: webob.exc.HTTPNotImplemented,
|
||||
503: webob.exc.HTTPServiceUnavailable,
|
||||
}
|
||||
code = response.status_int
|
||||
message = response.json_body.popitem()[1].get("message")
|
||||
|
||||
exc = exceptions.get(code, webob.exc.HTTPInternalServerError)
|
||||
return exc(explanation=message)
|
||||
|
|
|
@ -38,7 +38,7 @@ class Controller(ooi.api.base.Controller):
|
|||
tenant_id = req.environ["keystone.token_auth"].user.project_id
|
||||
req = self._get_req(req, path="/%s/servers" % tenant_id)
|
||||
response = req.get_response(self.app)
|
||||
servers = response.json_body.get("servers", [])
|
||||
servers = self.get_from_response(response, "servers", [])
|
||||
occi_compute_resources = self._get_compute_resources(servers)
|
||||
|
||||
return collection.Collection(resources=occi_compute_resources)
|
||||
|
@ -63,7 +63,7 @@ class Controller(ooi.api.base.Controller):
|
|||
}}))
|
||||
response = req.get_response(self.app)
|
||||
# We only get one server
|
||||
server = response.json_body.get("server", {})
|
||||
server = self.get_from_response(response, "server", {})
|
||||
|
||||
# The returned JSON does not contain the server name
|
||||
server["name"] = params["/occi/infrastructure"]
|
||||
|
@ -77,13 +77,13 @@ class Controller(ooi.api.base.Controller):
|
|||
# get info from server
|
||||
req = self._get_req(req, path="/%s/servers/%s" % (tenant_id, id))
|
||||
response = req.get_response(self.app)
|
||||
s = response.json_body.get("server", {})
|
||||
s = self.get_from_response(response, "server", {})
|
||||
|
||||
# get info from flavor
|
||||
req = self._get_req(req, path="/%s/flavors/%s" % (tenant_id,
|
||||
s["flavor"]["id"]))
|
||||
response = req.get_response(self.app)
|
||||
flavor = response.json_body.get("flavor", {})
|
||||
flavor = self.get_from_response(response, "flavor", {})
|
||||
res_tpl = templates.OpenStackResourceTemplate(flavor["name"],
|
||||
flavor["vcpus"],
|
||||
flavor["ram"],
|
||||
|
@ -93,7 +93,7 @@ class Controller(ooi.api.base.Controller):
|
|||
req = self._get_req(req, path="/%s/images/%s" % (tenant_id,
|
||||
s["image"]["id"]))
|
||||
response = req.get_response(self.app)
|
||||
image = response.json_body.get("image", {})
|
||||
image = self.get_from_response(response, "image", {})
|
||||
os_tpl = templates.OpenStackOSTemplate(image["id"],
|
||||
image["name"])
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ class Controller(base.Controller):
|
|||
tenant_id = req.environ["keystone.token_auth"].user.project_id
|
||||
req = self._get_req(req, path="/%s/flavors/detail" % tenant_id)
|
||||
response = req.get_response(self.app)
|
||||
flavors = response.json_body.get("flavors", [])
|
||||
flavors = self.get_from_response(response, "flavors", [])
|
||||
occi_resource_templates = []
|
||||
if flavors:
|
||||
for f in flavors:
|
||||
|
@ -44,7 +44,7 @@ class Controller(base.Controller):
|
|||
tenant_id = req.environ["keystone.token_auth"].user.project_id
|
||||
req = self._get_req(req, path="/%s/images/detail" % tenant_id)
|
||||
response = req.get_response(self.app)
|
||||
images = response.json_body.get("images", [])
|
||||
images = self.get_from_response(response, "images", [])
|
||||
occi_os_templates = []
|
||||
if images:
|
||||
for i in images:
|
||||
|
|
|
@ -18,6 +18,10 @@ import json
|
|||
import uuid
|
||||
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
|
||||
import ooi.wsgi
|
||||
import ooi.wsgi.utils
|
||||
|
||||
|
||||
tenants = {
|
||||
|
@ -156,6 +160,35 @@ def fake_query_results():
|
|||
return result
|
||||
|
||||
|
||||
class FakeOpenStackFault(ooi.wsgi.Fault):
|
||||
_fault_names = {
|
||||
400: "badRequest",
|
||||
401: "unauthorized",
|
||||
403: "forbidden",
|
||||
404: "itemNotFound",
|
||||
405: "badMethod",
|
||||
406: "notAceptable",
|
||||
409: "conflictingRequest",
|
||||
413: "overLimit",
|
||||
415: "badMediaType",
|
||||
429: "overLimit",
|
||||
501: "notImplemented",
|
||||
503: "serviceUnavailable"}
|
||||
|
||||
@webob.dec.wsgify()
|
||||
def __call__(self, req):
|
||||
code = self.wrapped_exc.status_int
|
||||
fault_name = self._fault_names.get(code)
|
||||
explanation = self.wrapped_exc.explanation
|
||||
fault_data = {
|
||||
fault_name: {
|
||||
'code': code,
|
||||
'message': explanation}}
|
||||
self.wrapped_exc.body = ooi.wsgi.utils.utf8(json.dumps(fault_data))
|
||||
self.wrapped_exc.content_type = "application/json"
|
||||
return self.wrapped_exc
|
||||
|
||||
|
||||
class FakeApp(object):
|
||||
"""Poor man's fake application."""
|
||||
|
||||
|
@ -205,8 +238,9 @@ class FakeApp(object):
|
|||
def _do_get(self, req):
|
||||
try:
|
||||
ret = self.routes[req.path_info]
|
||||
except Exception:
|
||||
raise
|
||||
except KeyError:
|
||||
exc = webob.exc.HTTPNotFound()
|
||||
ret = FakeOpenStackFault(exc)
|
||||
return ret
|
||||
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
|
||||
import mock
|
||||
|
||||
from ooi.tests import fakes
|
||||
|
@ -115,6 +117,15 @@ class TestComputeController(test_middleware.TestMiddleware):
|
|||
self.assertExpectedResult(expected, resp)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
def test_vm_not_found(self):
|
||||
tenant = fakes.tenants["foo"]
|
||||
|
||||
app = self.get_app()
|
||||
req = self._build_req("/compute/%s" % uuid.uuid4().hex,
|
||||
tenant["id"], method="GET")
|
||||
resp = req.get_response(app)
|
||||
self.assertEqual(404, resp.status_code)
|
||||
|
||||
def test_create_vm(self):
|
||||
tenant = fakes.tenants["foo"]
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
import mock
|
||||
import webob
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
|
||||
from ooi.tests import base
|
||||
from ooi.tests import fakes
|
||||
|
@ -63,6 +65,16 @@ class TestMiddleware(base.TestCase):
|
|||
result = self._build_req("/", "tenant").get_response(self.get_app())
|
||||
self.assertEqual(404, result.status_code)
|
||||
|
||||
def test_400_from_openstack(self):
|
||||
@webob.dec.wsgify()
|
||||
def _fake_app(req):
|
||||
exc = webob.exc.HTTPBadRequest()
|
||||
resp = fakes.FakeOpenStackFault(exc)
|
||||
return resp
|
||||
|
||||
result = self._build_req("/-/", "tenant").get_response(_fake_app)
|
||||
self.assertEqual(400, result.status_code)
|
||||
|
||||
|
||||
class TestMiddlewareTextPlain(TestMiddleware):
|
||||
"""OCCI middleware test with Accept: text/plain."""
|
||||
|
|
Loading…
Reference in New Issue