
- add devstack plugin - update openstack common - move unit tests to unit folder - update infrastructural files Change-Id: Id72006f70110dbd1762f42b582470ac5f3439f2a
224 lines
8.5 KiB
Python
224 lines
8.5 KiB
Python
# Copyright 2014
|
|
# The Cloudscaling Group, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
from oslo_log import log as logging
|
|
from oslo_serialization import jsonutils
|
|
import webob
|
|
|
|
from gceapi import exception
|
|
from gceapi.i18n import _
|
|
from gceapi import wsgi_ext as openstack_wsgi
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class JSONDictSerializer(openstack_wsgi.DictSerializer):
|
|
"""JSON request body serialization."""
|
|
|
|
def serialize(self, data, request):
|
|
params = {'false': False, 'true': True}
|
|
pretty_print = request.params.get("prettyPrint", True)
|
|
if pretty_print in params:
|
|
pretty_print = params[pretty_print]
|
|
ident = None
|
|
if pretty_print:
|
|
ident = 4
|
|
ret = jsonutils.dumps(data,
|
|
default=jsonutils.to_primitive, indent=ident)
|
|
return ret
|
|
|
|
|
|
class GCEResponse(openstack_wsgi.ResponseObject):
|
|
"""GCE Response body serialization."""
|
|
|
|
def serialize(self, request, content_type, default_serializers=None):
|
|
if self.serializer:
|
|
serializer = self.serializer
|
|
else:
|
|
_mtype, _serializer = self.get_serializer(content_type,
|
|
default_serializers)
|
|
serializer = _serializer()
|
|
|
|
response = webob.Response()
|
|
response.status_int = self.code
|
|
for hdr, value in self._headers.items():
|
|
response.headers[hdr] = value
|
|
response.headers['Content-Type'] = content_type
|
|
if self.obj is not None:
|
|
response.body = serializer.serialize(self.obj, request)
|
|
|
|
return response
|
|
|
|
|
|
class GCEFault(webob.exc.HTTPException):
|
|
"""Wrap webob.exc.HTTPException to provide API friendly response."""
|
|
|
|
def __init__(self, exception):
|
|
"""
|
|
Create a Fault for the given webob.exc.exception or gceapi.exception.
|
|
"""
|
|
self.wrapped_exc = exception
|
|
for key, value in self.wrapped_exc.headers.items():
|
|
self.wrapped_exc.headers[key] = str(value)
|
|
|
|
|
|
class GCEResourceExceptionHandler(object):
|
|
"""Context manager to handle Resource exceptions.
|
|
|
|
Used when processing exceptions generated by API implementation
|
|
methods (or their extensions). Converts most exceptions to Fault
|
|
exceptions, with the appropriate logging.
|
|
"""
|
|
|
|
def __enter__(self):
|
|
return None
|
|
|
|
def __exit__(self, ex_type, ex_value, ex_traceback):
|
|
if not ex_value:
|
|
return True
|
|
|
|
if isinstance(ex_value, exception.NotAuthorized):
|
|
msg = unicode(ex_value)
|
|
raise GCEFault(webob.exc.HTTPForbidden(explanation=msg))
|
|
elif isinstance(ex_value, exception.Invalid):
|
|
msg = unicode(ex_value)
|
|
raise GCEFault(exception.ConvertedException(
|
|
code=ex_value.code, explanation=msg))
|
|
|
|
# Under python 2.6, TypeError's exception value is actually a string,
|
|
# so test # here via ex_type instead:
|
|
# http://bugs.python.org/issue7853
|
|
elif issubclass(ex_type, TypeError):
|
|
exc_info = (ex_type, ex_value, ex_traceback)
|
|
LOG.error(_('Exception handling resource: %s') % ex_value,
|
|
exc_info=exc_info)
|
|
raise GCEFault(webob.exc.HTTPBadRequest())
|
|
elif isinstance(ex_value, GCEFault):
|
|
LOG.info(_("Fault thrown: %s"), unicode(ex_value))
|
|
raise ex_value
|
|
elif isinstance(ex_value, webob.exc.HTTPException):
|
|
LOG.info(_("HTTP exception thrown: %s"), unicode(ex_value))
|
|
raise GCEFault(ex_value)
|
|
elif isinstance(ex_value, exception.GceapiException):
|
|
LOG.info(_("Gceapi exception thrown: %s"), unicode(ex_value))
|
|
raise GCEFault(ex_value)
|
|
else:
|
|
msg = unicode(ex_value)
|
|
raise GCEFault(exception.ConvertedException(
|
|
code=500, title=ex_type.__name__, explanation=msg))
|
|
|
|
|
|
class GCEResource(openstack_wsgi.Resource):
|
|
"""Common GCE resource response formatter"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(GCEResource, self).__init__(*args, **kwargs)
|
|
self.default_serializers = dict(json=JSONDictSerializer)
|
|
|
|
def _check_requested_project(self, project_id, context):
|
|
if (not context or project_id is None
|
|
or (project_id not in [context.project_id, context.project_name])):
|
|
msg = _("Project '%s' could not be found") % project_id \
|
|
if project_id is not None \
|
|
else _("Project hasn`t been provided")
|
|
|
|
raise GCEFault(webob.exc.HTTPBadRequest(
|
|
explanation=msg))
|
|
|
|
def _process_stack(self, request, action, action_args,
|
|
content_type, body, accept):
|
|
"""Implement the processing stack."""
|
|
method = None
|
|
try:
|
|
# Get the implementing method
|
|
try:
|
|
method = self.get_method(request, action, content_type, body)
|
|
except (AttributeError, TypeError):
|
|
msg = _("There is no such action: %s") % action
|
|
raise GCEFault(webob.exc.HTTPNotFound(
|
|
explanation=msg))
|
|
except KeyError as ex:
|
|
msg = _("There is no such action: %s") % ex.args[0]
|
|
raise GCEFault(webob.exc.HTTPBadRequest(
|
|
explanation=msg))
|
|
except exception.MalformedRequestBody:
|
|
msg = _("Malformed request body")
|
|
raise GCEFault(webob.exc.HTTPBadRequest(
|
|
explanation=msg))
|
|
|
|
# Now, deserialize the request body...
|
|
try:
|
|
if content_type:
|
|
contents = self.deserialize(method, content_type, body)
|
|
else:
|
|
contents = {}
|
|
except exception.InvalidContentType:
|
|
msg = _("Unsupported Content-Type")
|
|
raise GCEFault(webob.exc.HTTPBadRequest(
|
|
explanation=msg))
|
|
except exception.MalformedRequestBody:
|
|
msg = _("Malformed request body")
|
|
raise GCEFault(webob.exc.HTTPBadRequest(
|
|
explanation=msg))
|
|
|
|
# Update the action args
|
|
action_args.update(contents)
|
|
|
|
# Check project
|
|
project_id = action_args.pop("project_id", None)
|
|
context = request.environ.get('gceapi.context')
|
|
action_result = self._check_requested_project(project_id, context)
|
|
|
|
if action_result is None:
|
|
with GCEResourceExceptionHandler():
|
|
action_result = self.dispatch(method, request, action_args)
|
|
|
|
except GCEFault as ex:
|
|
action_result = ex.wrapped_exc
|
|
|
|
response = None
|
|
resp_obj = None
|
|
if (action_result is None or type(action_result) is dict or
|
|
isinstance(action_result, Exception)):
|
|
action_result, result_code = self.controller.process_result(
|
|
request, action, action_result)
|
|
resp_obj = GCEResponse(action_result, code=result_code)
|
|
elif isinstance(action_result, GCEResponse):
|
|
resp_obj = action_result
|
|
else:
|
|
response = action_result
|
|
|
|
# Serialize response object
|
|
if resp_obj:
|
|
if method is not None:
|
|
serializers = getattr(method, 'wsgi_serializers', {})
|
|
else:
|
|
serializers = {}
|
|
resp_obj._bind_method_serializers(serializers)
|
|
if method is not None and hasattr(method, 'wsgi_code'):
|
|
resp_obj._default_code = method.wsgi_code
|
|
resp_obj.preserialize(accept, self.default_serializers)
|
|
response = resp_obj.serialize(request, accept,
|
|
self.default_serializers)
|
|
|
|
try:
|
|
msg_dict = dict(url=request.url, status=response.status_int)
|
|
msg = _("%(url)s returned with HTTP %(status)d") % msg_dict
|
|
except AttributeError as e:
|
|
msg_dict = dict(url=request.url, e=e)
|
|
msg = _("%(url)s returned a fault: %(e)s") % msg_dict
|
|
|
|
LOG.info(msg)
|
|
return response
|