
This commit removes eventlet WSGI server and starts using generic eventlet WSGI server from oslo.service library. The change where the wsgi functionality was added to oslo.service can be referenced here: If8840168f10cc3561f4f01e6d456d6b4fd1de8b5. DocImpact Depends-on: If8840168f10cc3561f4f01e6d456d6b4fd1de8b5 Implements blueprint: reusing-wsgi-from-oslo.service Closes-Bug: #1499658 Change-Id: Iea8af7b4a569fedc99e126524f18f98097c95a6d
191 lines
6.0 KiB
Python
191 lines
6.0 KiB
Python
# 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.
|
|
|
|
"""Utility methods for working with WSGI servers."""
|
|
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
from oslo_service import wsgi
|
|
from paste import deploy
|
|
import webob.dec
|
|
import webob.exc
|
|
|
|
from cinder import exception
|
|
from cinder.i18n import _, _LE
|
|
from cinder import utils
|
|
|
|
CONF = cfg.CONF
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class Request(webob.Request):
|
|
pass
|
|
|
|
|
|
class Application(object):
|
|
"""Base WSGI application wrapper. Subclasses need to implement __call__."""
|
|
|
|
@classmethod
|
|
def factory(cls, global_config, **local_config):
|
|
"""Used for paste app factories in paste.deploy config files.
|
|
|
|
Any local configuration (that is, values under the [app:APPNAME]
|
|
section of the paste config) will be passed into the `__init__` method
|
|
as kwargs.
|
|
|
|
A hypothetical configuration would look like:
|
|
|
|
[app:wadl]
|
|
latest_version = 1.3
|
|
paste.app_factory = cinder.api.fancy_api:Wadl.factory
|
|
|
|
which would result in a call to the `Wadl` class as
|
|
|
|
import cinder.api.fancy_api
|
|
fancy_api.Wadl(latest_version='1.3')
|
|
|
|
You could of course re-implement the `factory` method in subclasses,
|
|
but using the kwarg passing it shouldn't be necessary.
|
|
|
|
"""
|
|
return cls(**local_config)
|
|
|
|
def __call__(self, environ, start_response):
|
|
r"""Subclasses will probably want to implement __call__ like this:
|
|
|
|
@webob.dec.wsgify(RequestClass=Request)
|
|
def __call__(self, req):
|
|
# Any of the following objects work as responses:
|
|
|
|
# Option 1: simple string
|
|
res = 'message\n'
|
|
|
|
# Option 2: a nicely formatted HTTP exception page
|
|
res = exc.HTTPForbidden(explanation='Nice try')
|
|
|
|
# Option 3: a webob Response object (in case you need to play with
|
|
# headers, or you want to be treated like an iterable)
|
|
res = Response();
|
|
res.app_iter = open('somefile')
|
|
|
|
# Option 4: any wsgi app to be run next
|
|
res = self.application
|
|
|
|
# Option 5: you can get a Response object for a wsgi app, too, to
|
|
# play with headers etc
|
|
res = req.get_response(self.application)
|
|
|
|
# You can then just return your response...
|
|
return res
|
|
# ... or set req.response and return None.
|
|
req.response = res
|
|
|
|
See the end of http://pythonpaste.org/webob/modules/dec.html
|
|
for more info.
|
|
|
|
"""
|
|
raise NotImplementedError(_('You must implement __call__'))
|
|
|
|
|
|
class Middleware(Application):
|
|
"""Base WSGI middleware.
|
|
|
|
These classes require an application to be
|
|
initialized that will be called next. By default the middleware will
|
|
simply call its wrapped app, or you can override __call__ to customize its
|
|
behavior.
|
|
|
|
"""
|
|
|
|
@classmethod
|
|
def factory(cls, global_config, **local_config):
|
|
"""Used for paste app factories in paste.deploy config files.
|
|
|
|
Any local configuration (that is, values under the [filter:APPNAME]
|
|
section of the paste config) will be passed into the `__init__` method
|
|
as kwargs.
|
|
|
|
A hypothetical configuration would look like:
|
|
|
|
[filter:analytics]
|
|
redis_host = 127.0.0.1
|
|
paste.filter_factory = cinder.api.analytics:Analytics.factory
|
|
|
|
which would result in a call to the `Analytics` class as
|
|
|
|
import cinder.api.analytics
|
|
analytics.Analytics(app_from_paste, redis_host='127.0.0.1')
|
|
|
|
You could of course re-implement the `factory` method in subclasses,
|
|
but using the kwarg passing it shouldn't be necessary.
|
|
|
|
"""
|
|
def _factory(app):
|
|
return cls(app, **local_config)
|
|
return _factory
|
|
|
|
def __init__(self, application):
|
|
self.application = application
|
|
|
|
def process_request(self, req):
|
|
"""Called on each request.
|
|
|
|
If this returns None, the next application down the stack will be
|
|
executed. If it returns a response then that response will be returned
|
|
and execution will stop here.
|
|
|
|
"""
|
|
return None
|
|
|
|
def process_response(self, response):
|
|
"""Do whatever you'd like to the response."""
|
|
return response
|
|
|
|
@webob.dec.wsgify(RequestClass=Request)
|
|
def __call__(self, req):
|
|
response = self.process_request(req)
|
|
if response:
|
|
return response
|
|
response = req.get_response(self.application)
|
|
return self.process_response(response)
|
|
|
|
|
|
class Loader(object):
|
|
"""Used to load WSGI applications from paste configurations."""
|
|
|
|
def __init__(self, config_path=None):
|
|
"""Initialize the loader, and attempt to find the config.
|
|
|
|
:param config_path: Full or relative path to the paste config.
|
|
:returns: None
|
|
|
|
"""
|
|
wsgi.register_opts(CONF) # noqa
|
|
config_path = config_path or CONF.api_paste_config
|
|
self.config_path = utils.find_config(config_path)
|
|
|
|
def load_app(self, name):
|
|
"""Return the paste URLMap wrapped WSGI application.
|
|
|
|
:param name: Name of the application to load.
|
|
:returns: Paste URLMap object wrapping the requested application.
|
|
:raises: `cinder.exception.PasteAppNotFound`
|
|
|
|
"""
|
|
try:
|
|
return deploy.loadapp("config:%s" % self.config_path, name=name)
|
|
except LookupError:
|
|
LOG.exception(_LE("Error loading app %s"), name)
|
|
raise exception.PasteAppNotFound(name=name, path=self.config_path)
|