congress/congress/api/application.py

108 lines
3.6 KiB
Python

# Copyright (c) 2014 VMware, Inc. All rights reserved.
#
# 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 __future__ import print_function
from __future__ import division
from __future__ import absolute_import
import traceback
from oslo_log import log as logging
import webob
import webob.dec
from congress.api import webservice
from congress.dse2 import data_service
LOG = logging.getLogger(__name__)
API_SERVICE_NAME = '__api'
class ApiApplication(object):
"""An API web application that binds REST resources to a wsgi server.
This indirection between the wsgi server and REST resources facilitates
binding the same resource tree to multiple endpoints (e.g. HTTP/HTTPS).
"""
def __init__(self, resource_mgr):
self.resource_mgr = resource_mgr
@webob.dec.wsgify(RequestClass=webob.Request)
def __call__(self, request):
try:
handler = self.resource_mgr.get_handler(request)
if handler:
msg = _("Handling request '%(meth)s %(path)s' with %(hndlr)s")
LOG.info(msg, {"meth": request.method, "path": request.path,
"hndlr": handler})
# TODO(pballand): validation
response = handler.handle_request(request)
else:
response = webservice.NOT_FOUND_RESPONSE
except webservice.DataModelException as e:
# Error raised based on invalid user input
LOG.exception("ApiApplication: found DataModelException")
response = e.rest_response()
except Exception as e:
# Unexpected error raised by API framework or data model
msg = _("Exception caught for request: %s")
LOG.error(msg, request)
LOG.error(traceback.format_exc())
response = webservice.INTERNAL_ERROR_RESPONSE
return response
class ResourceManager(data_service.DataService):
"""A container for REST API resources.
This container is meant to be called from one or more wsgi servers/ports.
Attributes:
handlers: An array of API resource handlers for registered resources.
"""
def __init__(self):
self.handlers = []
super(ResourceManager, self).__init__(API_SERVICE_NAME)
def register_handler(self, handler, search_index=None):
"""Register a new resource handler.
:param: handler: The resource handler to register.
:param: search_index: Priority of resource handler to resolve cases
where a request matches multiple handlers.
"""
if search_index is not None:
self.handlers.insert(search_index, handler)
else:
self.handlers.append(handler)
msg = _("Registered API handler: %s")
LOG.info(msg, handler)
def get_handler(self, request):
"""Find a handler for a REST request.
:param: request: A webob request object.
:returns: A handler instance or None.
"""
for h in self.handlers:
if h.handles_request(request):
return h
return None