diff --git a/ceilometer/api/__init__.py b/ceilometer/api/__init__.py index ffaed1b1b..a3265747f 100644 --- a/ceilometer/api/__init__.py +++ b/ceilometer/api/__init__.py @@ -27,12 +27,6 @@ OPTS = [ default='0.0.0.0', help='The listen IP for the ceilometer API server.', ), - cfg.BoolOpt('enable_reverse_dns_lookup', - default=False, - help=('Set it to False if your environment does not need ' - 'or have a DNS server, otherwise it will delay the ' - 'response from the API.') - ), ] CONF = cfg.CONF diff --git a/ceilometer/api/app.py b/ceilometer/api/app.py index 89857d608..b507eeb3b 100644 --- a/ceilometer/api/app.py +++ b/ceilometer/api/app.py @@ -15,19 +15,20 @@ import logging import os -import socket -from wsgiref import simple_server -import netaddr + from oslo_config import cfg from paste import deploy import pecan +from werkzeug import serving from ceilometer.api import config as api_config from ceilometer.api import hooks from ceilometer.api import middleware from ceilometer.i18n import _ +from ceilometer.i18n import _LW from ceilometer.openstack.common import log +from ceilometer import service from ceilometer import storage LOG = log.getLogger(__name__) @@ -39,6 +40,8 @@ OPTS = [ default="api_paste.ini", help="Configuration file for WSGI definition of API." ), + cfg.IntOpt('api_workers', default=1, + help='Number of workers for Ceilometer API server.'), ] API_OPTS = [ @@ -77,9 +80,16 @@ def setup_app(pecan_config=None, extra_hooks=None): cfg.set_defaults(API_OPTS, pecan_debug=CONF.debug) + # NOTE(sileht): pecan debug won't work in multi-process environment + pecan_debug = CONF.api.pecan_debug + if service.get_workers('api') != 1 and pecan_debug: + pecan_debug = False + LOG.warning(_LW('pecan_debug cannot be enabled, if workers is > 1, ' + 'the value is overrided with False')) + app = pecan.make_app( pecan_config.app.root, - debug=CONF.api.pecan_debug, + debug=pecan_debug, force_canonical=getattr(pecan_config.app, 'force_canonical', True), hooks=app_hooks, wrap_app=middleware.ParsableErrorMiddleware, @@ -106,36 +116,6 @@ class VersionSelectorApplication(object): return self.v2(environ, start_response) -def get_server_cls(host): - """Return an appropriate WSGI server class base on provided host - - :param host: The listen host for the ceilometer API server. - """ - server_cls = simple_server.WSGIServer - if netaddr.valid_ipv6(host): - # NOTE(dzyu) make sure use IPv6 sockets if host is in IPv6 pattern - if getattr(server_cls, 'address_family') == socket.AF_INET: - class ipv6_server_cls(server_cls): - address_family = socket.AF_INET6 - return ipv6_server_cls - return server_cls - - -def get_handler_cls(): - cls = simple_server.WSGIRequestHandler - - # old-style class doesn't support super - class CeilometerHandler(cls, object): - def address_string(self): - if cfg.CONF.api.enable_reverse_dns_lookup: - return super(CeilometerHandler, self).address_string() - else: - # disable reverse dns lookup, directly return ip address - return self.client_address[0] - - return CeilometerHandler - - def load_app(): # Build the WSGI app cfg_file = None @@ -155,10 +135,6 @@ def build_server(): app = load_app() # Create the WSGI server and start it host, port = cfg.CONF.api.host, cfg.CONF.api.port - server_cls = get_server_cls(host) - - srv = simple_server.make_server(host, port, app, - server_cls, get_handler_cls()) LOG.info(_('Starting server in PID %s') % os.getpid()) LOG.info(_("Configuration:")) @@ -172,7 +148,9 @@ def build_server(): LOG.info(_("serving on http://%(host)s:%(port)s") % ( {'host': host, 'port': port})) - return srv + workers = service.get_workers('api') + serving.run_simple(cfg.CONF.api.host, cfg.CONF.api.port, + app, processes=workers) def app_factory(global_config, **local_conf): diff --git a/ceilometer/cmd/api.py b/ceilometer/cmd/api.py index 464f97078..cdcdb46ad 100644 --- a/ceilometer/cmd/api.py +++ b/ceilometer/cmd/api.py @@ -20,5 +20,4 @@ from ceilometer import service def main(): service.prepare_service() - srv = app.build_server() - srv.serve_forever() + app.build_server() diff --git a/ceilometer/tests/api/test_app.py b/ceilometer/tests/api/test_app.py index b77926b53..ca473ee64 100644 --- a/ceilometer/tests/api/test_app.py +++ b/ceilometer/tests/api/test_app.py @@ -15,8 +15,6 @@ # License for the specific language governing permissions and limitations # under the License. -import socket - import mock from oslo_config import cfg from oslo_config import fixture as fixture_config @@ -31,19 +29,6 @@ class TestApp(base.BaseTestCase): super(TestApp, self).setUp() self.CONF = self.useFixture(fixture_config.Config()).conf - def test_WSGI_address_family(self): - self.CONF.set_override('host', '::', group='api') - server_cls = app.get_server_cls(cfg.CONF.api.host) - self.assertEqual(server_cls.address_family, socket.AF_INET6) - - self.CONF.set_override('host', '127.0.0.1', group='api') - server_cls = app.get_server_cls(cfg.CONF.api.host) - self.assertEqual(server_cls.address_family, socket.AF_INET) - - self.CONF.set_override('host', 'ddddd', group='api') - server_cls = app.get_server_cls(cfg.CONF.api.host) - self.assertEqual(server_cls.address_family, socket.AF_INET) - def test_api_paste_file_not_exist(self): self.CONF.set_override('api_paste_config', 'non-existent-file') with mock.patch.object(self.CONF, 'find_file') as ff: @@ -55,10 +40,11 @@ class TestApp(base.BaseTestCase): @mock.patch('ceilometer.api.hooks.PipelineHook', mock.MagicMock()) @mock.patch('pecan.make_app') def test_pecan_debug(self, mocked): - def _check_pecan_debug(g_debug, p_debug, expected): + def _check_pecan_debug(g_debug, p_debug, expected, workers=1): self.CONF.set_override('debug', g_debug) if p_debug is not None: self.CONF.set_override('pecan_debug', p_debug, group='api') + self.CONF.set_override('api_workers', workers) app.setup_app() args, kwargs = mocked.call_args self.assertEqual(expected, kwargs.get('debug')) @@ -67,3 +53,7 @@ class TestApp(base.BaseTestCase): _check_pecan_debug(g_debug=True, p_debug=None, expected=True) _check_pecan_debug(g_debug=True, p_debug=False, expected=False) _check_pecan_debug(g_debug=False, p_debug=True, expected=True) + _check_pecan_debug(g_debug=True, p_debug=None, expected=False, + workers=5) + _check_pecan_debug(g_debug=False, p_debug=True, expected=False, + workers=5) diff --git a/doc/source/install/mod_wsgi.rst b/doc/source/install/mod_wsgi.rst index 2defcb53a..5f24a8bc0 100644 --- a/doc/source/install/mod_wsgi.rst +++ b/doc/source/install/mod_wsgi.rst @@ -63,3 +63,6 @@ multiple processes, there is no way to set debug mode in the multiprocessing case. To allow multiple processes the DebugMiddleware may be turned off by setting ``pecan_debug`` to ``False`` in the ``api`` section of ``ceilometer.conf``. + +For other WSGI setup you can refer to the `pecan deployement`_ documentation. +.. _`pecan deployement`: http://pecan.readthedocs.org/en/latest/deployment.html#deployment diff --git a/requirements.txt b/requirements.txt index 1d1fe8eec..1da5fc4cd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -44,5 +44,6 @@ SQLAlchemy>=0.9.7,<=0.9.99 sqlalchemy-migrate>=0.9.1,!=0.9.2 stevedore>=1.1.0 # Apache-2.0 tooz>=0.3 # Apache-2.0 +werkzeug>=0.7 # BSD License WebOb>=1.2.3 WSME>=0.6