diff --git a/cinder/api/middleware/auth.py b/cinder/api/middleware/auth.py index 9789e1b146a..110e728a21c 100644 --- a/cinder/api/middleware/auth.py +++ b/cinder/api/middleware/auth.py @@ -30,7 +30,7 @@ import webob.exc from cinder.api.openstack import wsgi from cinder import context from cinder.i18n import _ -from cinder import wsgi as base_wsgi +from cinder.wsgi import common as base_wsgi use_forwarded_for_opt = cfg.BoolOpt( diff --git a/cinder/api/middleware/fault.py b/cinder/api/middleware/fault.py index 0575b46f108..ce56d5f903a 100644 --- a/cinder/api/middleware/fault.py +++ b/cinder/api/middleware/fault.py @@ -23,7 +23,7 @@ from cinder.api.openstack import wsgi from cinder import exception from cinder.i18n import _, _LE, _LI from cinder import utils -from cinder import wsgi as base_wsgi +from cinder.wsgi import common as base_wsgi LOG = logging.getLogger(__name__) diff --git a/cinder/api/openstack/__init__.py b/cinder/api/openstack/__init__.py index 3b8dc07c2d0..966a8d0a832 100644 --- a/cinder/api/openstack/__init__.py +++ b/cinder/api/openstack/__init__.py @@ -23,7 +23,7 @@ import routes from cinder.api.openstack import wsgi from cinder.i18n import _, _LW -from cinder import wsgi as base_wsgi +from cinder.wsgi import common as base_wsgi LOG = logging.getLogger(__name__) diff --git a/cinder/api/openstack/wsgi.py b/cinder/api/openstack/wsgi.py index 7fb76813c51..fdb752c35ac 100644 --- a/cinder/api/openstack/wsgi.py +++ b/cinder/api/openstack/wsgi.py @@ -31,7 +31,7 @@ from cinder import exception from cinder import i18n from cinder.i18n import _, _LE, _LI from cinder import utils -from cinder import wsgi +from cinder.wsgi import common as wsgi XML_NS_V1 = 'http://docs.openstack.org/api/openstack-block-storage/1.0/content' diff --git a/cinder/api/v1/limits.py b/cinder/api/v1/limits.py index 16f09a37d8c..b0124deaebf 100644 --- a/cinder/api/v1/limits.py +++ b/cinder/api/v1/limits.py @@ -34,7 +34,7 @@ from cinder.api.views import limits as limits_views from cinder.api import xmlutil from cinder.i18n import _ from cinder import quota -from cinder import wsgi as base_wsgi +from cinder.wsgi import common as base_wsgi QUOTAS = quota.QUOTAS LIMITS_PREFIX = "limits." diff --git a/cinder/api/v2/limits.py b/cinder/api/v2/limits.py index b6c21b4da45..0b6fb61397e 100644 --- a/cinder/api/v2/limits.py +++ b/cinder/api/v2/limits.py @@ -34,7 +34,7 @@ from cinder.api.views import limits as limits_views from cinder.api import xmlutil from cinder.i18n import _ from cinder import quota -from cinder import wsgi as base_wsgi +from cinder.wsgi import common as base_wsgi QUOTAS = quota.QUOTAS LIMITS_PREFIX = "limits." diff --git a/cinder/service.py b/cinder/service.py index 54eadb66ce4..9fce42cdfd7 100644 --- a/cinder/service.py +++ b/cinder/service.py @@ -41,8 +41,8 @@ from cinder.i18n import _, _LE, _LI, _LW from cinder.objects import base as objects_base from cinder import rpc from cinder import version -from cinder import wsgi - +from cinder.wsgi import common as wsgi_common +from cinder.wsgi import eventlet_server as wsgi LOG = logging.getLogger(__name__) @@ -356,7 +356,7 @@ class WSGIService(service.ServiceBase): """ self.name = name self.manager = self._get_manager() - self.loader = loader or wsgi.Loader() + self.loader = loader or wsgi_common.Loader() self.app = self.loader.load_app(name) self.host = getattr(CONF, '%s_listen' % name, "0.0.0.0") self.port = getattr(CONF, '%s_listen_port' % name, 0) diff --git a/cinder/tests/unit/api/fakes.py b/cinder/tests/unit/api/fakes.py index a87a0ed827e..e567dbc3a96 100644 --- a/cinder/tests/unit/api/fakes.py +++ b/cinder/tests/unit/api/fakes.py @@ -29,7 +29,7 @@ from cinder.api.v2 import limits from cinder.api.v2 import router from cinder.api import versions from cinder import context -from cinder import wsgi +from cinder.wsgi import common as wsgi FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' diff --git a/cinder/tests/unit/test_service.py b/cinder/tests/unit/test_service.py index 2d54e7245b0..2c95d65e558 100644 --- a/cinder/tests/unit/test_service.py +++ b/cinder/tests/unit/test_service.py @@ -31,7 +31,7 @@ from cinder import manager from cinder import rpc from cinder import service from cinder import test -from cinder import wsgi +from cinder.wsgi import common as wsgi test_service_opts = [ @@ -275,25 +275,25 @@ class TestWSGIService(test.TestCase): self.assertEqual(1000, test_service.server._pool.size) self.assertTrue(mock_load_app.called) - @mock.patch('cinder.wsgi.Server') + @mock.patch('cinder.wsgi.eventlet_server.Server') def test_workers_set_default(self, wsgi_server): test_service = service.WSGIService("osapi_volume") self.assertEqual(processutils.get_worker_count(), test_service.workers) - @mock.patch('cinder.wsgi.Server') + @mock.patch('cinder.wsgi.eventlet_server.Server') def test_workers_set_good_user_setting(self, wsgi_server): self.override_config('osapi_volume_workers', 8) test_service = service.WSGIService("osapi_volume") self.assertEqual(8, test_service.workers) - @mock.patch('cinder.wsgi.Server') + @mock.patch('cinder.wsgi.eventlet_server.Server') def test_workers_set_zero_user_setting(self, wsgi_server): self.override_config('osapi_volume_workers', 0) test_service = service.WSGIService("osapi_volume") # If a value less than 1 is used, defaults to number of procs available self.assertEqual(processutils.get_worker_count(), test_service.workers) - @mock.patch('cinder.wsgi.Server') + @mock.patch('cinder.wsgi.eventlet_server.Server') def test_workers_set_negative_user_setting(self, wsgi_server): self.override_config('osapi_volume_workers', -1) self.assertRaises(exception.InvalidInput, diff --git a/cinder/tests/unit/wsgi/__init__.py b/cinder/tests/unit/wsgi/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cinder/tests/unit/test_wsgi.py b/cinder/tests/unit/wsgi/test_eventlet_server.py similarity index 89% rename from cinder/tests/unit/test_wsgi.py rename to cinder/tests/unit/wsgi/test_eventlet_server.py index e60964ab40c..c014deb2737 100644 --- a/cinder/tests/unit/test_wsgi.py +++ b/cinder/tests/unit/wsgi/test_eventlet_server.py @@ -34,12 +34,13 @@ import webob.dec from cinder import exception from cinder.i18n import _ from cinder import test -import cinder.wsgi +from cinder.wsgi import common as wsgi_common +from cinder.wsgi import eventlet_server as wsgi CONF = cfg.CONF TEST_VAR_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), - 'var')) + '../var')) def open_no_proxy(*args, **kwargs): @@ -67,8 +68,8 @@ class TestLoaderNothingExists(test.TestCase): def test_config_not_found(self): self.assertRaises( - cinder.exception.ConfigNotFound, - cinder.wsgi.Loader, + exception.ConfigNotFound, + wsgi_common.Loader, ) @@ -87,7 +88,7 @@ document_root = /tmp self.config.write(self._paste_config.lstrip()) self.config.seek(0) self.config.flush() - self.loader = cinder.wsgi.Loader(self.config.name) + self.loader = wsgi_common.Loader(self.config.name) self.addCleanup(self.config.close) def test_config_found(self): @@ -95,7 +96,7 @@ document_root = /tmp def test_app_not_found(self): self.assertRaises( - cinder.exception.PasteAppNotFound, + exception.PasteAppNotFound, self.loader.load_app, "non-existent app", ) @@ -115,12 +116,12 @@ class TestWSGIServer(test.TestCase): return False def test_no_app(self): - server = cinder.wsgi.Server("test_app", None, - host="127.0.0.1", port=0) + server = wsgi.Server("test_app", None, + host="127.0.0.1", port=0) self.assertEqual("test_app", server.name) def test_start_random_port(self): - server = cinder.wsgi.Server("test_random_port", None, host="127.0.0.1") + server = wsgi.Server("test_random_port", None, host="127.0.0.1") server.start() self.assertNotEqual(0, server.port) server.stop() @@ -129,9 +130,9 @@ class TestWSGIServer(test.TestCase): @testtools.skipIf(not _ipv6_configured(), "Test requires an IPV6 configured interface") def test_start_random_port_with_ipv6(self): - server = cinder.wsgi.Server("test_random_port", - None, - host="::1") + server = wsgi.Server("test_random_port", + None, + host="::1") server.start() self.assertEqual("::1", server.host) self.assertNotEqual(0, server.port) @@ -140,8 +141,8 @@ class TestWSGIServer(test.TestCase): def test_server_pool_waitall(self): # test pools waitall method gets called while stopping server - server = cinder.wsgi.Server("test_server", None, - host="127.0.0.1") + server = wsgi.Server("test_server", None, + host="127.0.0.1") server.start() with mock.patch.object(server._pool, 'waitall') as mock_waitall: @@ -160,8 +161,8 @@ class TestWSGIServer(test.TestCase): start_response('200 OK', [('Content-Type', 'text/plain')]) return [greetings] - server = cinder.wsgi.Server("test_app", hello_world, - host="127.0.0.1", port=0) + server = wsgi.Server("test_app", hello_world, + host="127.0.0.1", port=0) server.start() response = open_no_proxy('http://127.0.0.1:%d/' % server.port) @@ -176,8 +177,8 @@ class TestWSGIServer(test.TestCase): start_response('200 OK', [('Content-Type', 'text/plain')]) return [greetings] - server = cinder.wsgi.Server("test_app", hello_world, - host="127.0.0.1", port=0) + server = wsgi.Server("test_app", hello_world, + host="127.0.0.1", port=0) server.start() s = socket.socket() @@ -215,8 +216,8 @@ class TestWSGIServer(test.TestCase): def hello_world(req): return greetings - server = cinder.wsgi.Server("test_app", hello_world, - host="127.0.0.1", port=0) + server = wsgi.Server("test_app", hello_world, + host="127.0.0.1", port=0) server.start() @@ -239,10 +240,10 @@ class TestWSGIServer(test.TestCase): def hello_world(req): return greetings - server = cinder.wsgi.Server("test_app", - hello_world, - host="::1", - port=0) + server = wsgi.Server("test_app", + hello_world, + host="::1", + port=0) server.start() response = open_no_proxy('https://[::1]:%d/' % server.port) @@ -251,7 +252,7 @@ class TestWSGIServer(test.TestCase): server.stop() def test_reset_pool_size_to_default(self): - server = cinder.wsgi.Server("test_resize", None, host="127.0.0.1") + server = wsgi.Server("test_resize", None, host="127.0.0.1") server.start() # Stopping the server, which in turn sets pool size to 0 diff --git a/cinder/tests/unit/api/test_wsgi.py b/cinder/tests/unit/wsgi/test_wsgi.py similarity index 98% rename from cinder/tests/unit/api/test_wsgi.py rename to cinder/tests/unit/wsgi/test_wsgi.py index 2eafc544d43..63f0550165a 100644 --- a/cinder/tests/unit/api/test_wsgi.py +++ b/cinder/tests/unit/wsgi/test_wsgi.py @@ -27,7 +27,7 @@ from cinder import test import routes import webob -from cinder import wsgi +from cinder.wsgi import common as wsgi class Test(test.TestCase): diff --git a/cinder/wsgi/__init__.py b/cinder/wsgi/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cinder/wsgi/common.py b/cinder/wsgi/common.py new file mode 100644 index 00000000000..2fd4e6abedf --- /dev/null +++ b/cinder/wsgi/common.py @@ -0,0 +1,284 @@ +# 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.""" + +import sys + +from oslo_config import cfg +from oslo_log import log as logging +from paste import deploy +import routes.middleware +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 Debug(Middleware): + """Helper class for debugging a WSGI application. + + Can be inserted into any WSGI application chain to get information + about the request and response. + + """ + + @webob.dec.wsgify(RequestClass=Request) + def __call__(self, req): + print(('*' * 40) + ' REQUEST ENVIRON') # noqa + for key, value in req.environ.items(): + print(key, '=', value) # noqa + print() # noqa + resp = req.get_response(self.application) + + print(('*' * 40) + ' RESPONSE HEADERS') # noqa + for (key, value) in resp.headers.items(): + print(key, '=', value) # noqa + print() # noqa + + resp.app_iter = self.print_generator(resp.app_iter) + + return resp + + @staticmethod + def print_generator(app_iter): + """Iterator that prints the contents of a wrapper string.""" + print(('*' * 40) + ' BODY') # noqa + for part in app_iter: + sys.stdout.write(part) + sys.stdout.flush() + yield part + print() # noqa + + +class Router(object): + """WSGI middleware that maps incoming requests to WSGI apps.""" + + def __init__(self, mapper): + """Create a router for the given routes.Mapper. + + Each route in `mapper` must specify a 'controller', which is a + WSGI app to call. You'll probably want to specify an 'action' as + well and have your controller be an object that can route + the request to the action-specific method. + + Examples: + mapper = routes.Mapper() + sc = ServerController() + + # Explicit mapping of one route to a controller+action + mapper.connect(None, '/svrlist', controller=sc, action='list') + + # Actions are all implicitly defined + mapper.resource('server', 'servers', controller=sc) + + # Pointing to an arbitrary WSGI app. You can specify the + # {path_info:.*} parameter so the target app can be handed just that + # section of the URL. + mapper.connect(None, '/v1.0/{path_info:.*}', controller=BlogApp()) + + """ + self.map = mapper + self._router = routes.middleware.RoutesMiddleware(self._dispatch, + self.map) + + @webob.dec.wsgify(RequestClass=Request) + def __call__(self, req): + """Route the incoming request to a controller based on self.map. + + If no match, return a 404. + + """ + return self._router + + @staticmethod + @webob.dec.wsgify(RequestClass=Request) + def _dispatch(req): + """Dispatch the request to the appropriate controller. + + Called by self._router after matching the incoming request to a route + and putting the information into req.environ. Either returns 404 + or the routed WSGI app's response. + + """ + match = req.environ['wsgiorg.routing_args'][1] + if not match: + return webob.exc.HTTPNotFound() + app = match['controller'] + return app + + +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 + + """ + 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) diff --git a/cinder/wsgi.py b/cinder/wsgi/eventlet_server.py similarity index 54% rename from cinder/wsgi.py rename to cinder/wsgi/eventlet_server.py index 6b851227714..f4059568078 100644 --- a/cinder/wsgi.py +++ b/cinder/wsgi/eventlet_server.py @@ -1,8 +1,3 @@ -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2010 OpenStack Foundation -# 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 @@ -15,7 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. -"""Utility methods for working with WSGI servers.""" +"""Methods for working with eventlet WSGI servers.""" from __future__ import print_function @@ -23,7 +18,6 @@ import errno import os import socket import ssl -import sys import time import eventlet @@ -34,14 +28,10 @@ from oslo_log import log as logging from oslo_service import service from oslo_utils import excutils from oslo_utils import netutils -from paste import deploy -import routes.middleware -import webob.dec -import webob.exc + from cinder import exception from cinder.i18n import _, _LE, _LI -from cinder import utils socket_opts = [ @@ -231,7 +221,7 @@ class Server(service.ServiceBase): **ssl_kwargs) except Exception: with excutils.save_and_reraise_exception(): - LOG.error(_LE("Failed to start %(name)s on %(_host)s:" + LOG.error(_LE("Failed to start %(name)s on %(_host)s: " "%(_port)s with SSL " "support."), self.__dict__) @@ -293,256 +283,3 @@ class Server(service.ServiceBase): """ self._pool.resize(self.pool_size) - - -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 Debug(Middleware): - """Helper class for debugging a WSGI application. - - Can be inserted into any WSGI application chain to get information - about the request and response. - - """ - - @webob.dec.wsgify(RequestClass=Request) - def __call__(self, req): - print(('*' * 40) + ' REQUEST ENVIRON') # noqa - for key, value in req.environ.items(): - print(key, '=', value) # noqa - print() # noqa - resp = req.get_response(self.application) - - print(('*' * 40) + ' RESPONSE HEADERS') # noqa - for (key, value) in resp.headers.items(): - print(key, '=', value) # noqa - print() # noqa - - resp.app_iter = self.print_generator(resp.app_iter) - - return resp - - @staticmethod - def print_generator(app_iter): - """Iterator that prints the contents of a wrapper string.""" - print(('*' * 40) + ' BODY') # noqa - for part in app_iter: - sys.stdout.write(part) - sys.stdout.flush() - yield part - print() # noqa - - -class Router(object): - """WSGI middleware that maps incoming requests to WSGI apps.""" - - def __init__(self, mapper): - """Create a router for the given routes.Mapper. - - Each route in `mapper` must specify a 'controller', which is a - WSGI app to call. You'll probably want to specify an 'action' as - well and have your controller be an object that can route - the request to the action-specific method. - - Examples: - mapper = routes.Mapper() - sc = ServerController() - - # Explicit mapping of one route to a controller+action - mapper.connect(None, '/svrlist', controller=sc, action='list') - - # Actions are all implicitly defined - mapper.resource('server', 'servers', controller=sc) - - # Pointing to an arbitrary WSGI app. You can specify the - # {path_info:.*} parameter so the target app can be handed just that - # section of the URL. - mapper.connect(None, '/v1.0/{path_info:.*}', controller=BlogApp()) - - """ - self.map = mapper - self._router = routes.middleware.RoutesMiddleware(self._dispatch, - self.map) - - @webob.dec.wsgify(RequestClass=Request) - def __call__(self, req): - """Route the incoming request to a controller based on self.map. - - If no match, return a 404. - - """ - return self._router - - @staticmethod - @webob.dec.wsgify(RequestClass=Request) - def _dispatch(req): - """Dispatch the request to the appropriate controller. - - Called by self._router after matching the incoming request to a route - and putting the information into req.environ. Either returns 404 - or the routed WSGI app's response. - - """ - match = req.environ['wsgiorg.routing_args'][1] - if not match: - return webob.exc.HTTPNotFound() - app = match['controller'] - return app - - -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 - - """ - 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) diff --git a/cinder/wsgi/wsgi.py b/cinder/wsgi/wsgi.py new file mode 100644 index 00000000000..d51bec40df9 --- /dev/null +++ b/cinder/wsgi/wsgi.py @@ -0,0 +1,48 @@ +# 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. + +"""Cinder OS API WSGI application.""" + + +import sys +import warnings + +from cinder import objects + +warnings.simplefilter('once', DeprecationWarning) + +from oslo_config import cfg +from oslo_log import log as logging + +from cinder import i18n +i18n.enable_lazy() + +# Need to register global_opts +from cinder.common import config # noqa +from cinder import rpc +from cinder import version +from cinder.wsgi import common as wsgi_common + +CONF = cfg.CONF + + +def _application(): + objects.register_all() + CONF(sys.argv[1:], project='cinder', + version=version.version_string()) + logging.setup(CONF, "cinder") + + rpc.init(CONF) + return wsgi_common.Loader().load_app(name='osapi_volume') + + +application = _application() diff --git a/etc/cinder/api-httpd.conf b/etc/cinder/api-httpd.conf new file mode 100644 index 00000000000..f3555477acc --- /dev/null +++ b/etc/cinder/api-httpd.conf @@ -0,0 +1,16 @@ +Listen 8776 +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" %D(us)" cinder_combined + + + WSGIDaemonProcess osapi_volume processes=2 threads=1 user=cinder display-name=%{GROUP} + WSGIProcessGroup osapi_volume + WSGIScriptAlias / /var/www/cgi-bin/cinder/osapi_volume + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + = 2.4> + ErrorLogFormat "%{cu}t %M" + + ErrorLog /var/log/apache2/cinder_error.log + CustomLog /var/log/apache2/cinder.log cinder_combined + +