3
3
# Copyright 2010 United States Government as represented by the
4
4
# Administrator of the National Aeronautics and Space Administration.
5
# Copyright 2013 IBM Corp.
5
6
# All Rights Reserved.
7
8
# Licensed under the Apache License, Version 2.0 (the "License"); you may
45
46
from heat.common import exception
47
from heat.openstack.common import gettextutils
48
from heat.openstack.common.gettextutils import _
46
49
from heat.openstack.common import importutils
47
from heat.openstack.common.gettextutils import _
50
52
URL_LENGTH_LIMIT = 50000
52
# TODO(shadower) remove this once eventlet 0.9.17 is in distros Heat
53
# supports (notably Fedora 17 and Ubuntu 12.04 and newer)
54
eventlet.wsgi.MAX_REQUEST_LINE = URL_LENGTH_LIMIT
57
cfg.StrOpt('bind_host', default='0.0.0.0'),
58
cfg.IntOpt('bind_port'),
55
cfg.StrOpt('bind_host', default='0.0.0.0',
56
help=_('Address to bind the server. Useful when '
57
'selecting a particular network interface.')),
58
cfg.IntOpt('bind_port',
59
help=_('The port on which the server will listen.'))
62
cfg.CONF.register_opts(bind_opts)
62
cfg.IntOpt('backlog', default=4096),
63
cfg.StrOpt('cert_file'),
64
cfg.StrOpt('key_file'),
65
cfg.IntOpt('backlog', default=4096,
66
help=_("Number of backlog requests "
67
"to configure the socket with")),
68
cfg.StrOpt('cert_file', default=None,
69
help=_("Location of the SSL Certificate File "
70
"to use for SSL mode")),
71
cfg.StrOpt('key_file', default=None,
72
help=_("Location of the SSL Key File to use "
73
"for enabling SSL mode")),
67
workers_opt = cfg.IntOpt('workers', default=0)
76
cfg.CONF.register_opts(socket_opts)
78
workers_opts = cfg.IntOpt('workers', default=0,
79
help=_("Number of workers for Heat service"))
81
cfg.CONF.register_opt(workers_opts)
70
84
class WritableLogger(object):
179
193
self.application = application
180
194
self.sock = get_socket(conf, default_port)
181
conf.register_opt(workers_opt)
183
196
self.logger = logging.getLogger('eventlet.wsgi.server')
206
219
if err.errno not in (errno.EINTR, errno.ECHILD):
208
221
except KeyboardInterrupt:
210
222
self.logger.info(_('Caught keyboard interrupt. Exiting.'))
212
224
eventlet.greenio.shutdown_safe(self.sock)
242
254
eventlet.patcher.monkey_patch(all=False, socket=True)
243
255
self.pool = eventlet.GreenPool(size=self.threads)
245
eventlet_wsgi_server(self.sock,
257
eventlet.wsgi.server(self.sock,
246
258
self.application,
247
259
custom_pool=self.pool,
248
260
url_length_limit=URL_LENGTH_LIMIT,
255
267
def _single_run(self, application, sock):
256
268
"""Start a WSGI server in a new green thread."""
257
269
self.logger.info(_("Starting single process server"))
258
eventlet_wsgi_server(sock, application,
270
eventlet.wsgi.server(sock, application,
259
271
custom_pool=self.pool,
260
272
url_length_limit=URL_LENGTH_LIMIT,
261
273
log=WritableLogger(self.logger))
264
def eventlet_wsgi_server(sock, application, **kwargs):
266
Return a new instance of the eventlet wsgi server with the proper url limit
267
in a way that's compatible with eventlet 0.9.16 and 0.9.17.
270
return eventlet.wsgi.server(sock, application, **kwargs)
271
# TODO(shadower) remove this when we don't support eventlet 0.9.16 anymore
273
kwargs.pop('url_length_limit', None)
274
return eventlet.wsgi.server(sock, application, **kwargs)
277
276
class Middleware(object):
279
278
Base WSGI middleware wrapper. These classes require an application to be
318
317
@webob.dec.wsgify
319
318
def __call__(self, req):
320
print ("*" * 40) + " REQUEST ENVIRON"
319
print(("*" * 40) + " REQUEST ENVIRON")
321
320
for key, value in req.environ.items():
322
321
print(key, "=", value)
324
323
resp = req.get_response(self.application)
326
print ("*" * 40) + " RESPONSE HEADERS"
325
print(("*" * 40) + " RESPONSE HEADERS")
327
326
for (key, value) in resp.headers.iteritems():
328
327
print(key, "=", value)
428
427
return content_type
429
def best_match_language(self):
430
"""Determine language for returned response."""
431
all_languages = gettextutils.get_available_languages('heat')
432
return self.accept_language.best_match(all_languages,
433
default_match='en_US')
431
436
def is_json_content_type(request):
432
437
if request.method == 'GET':
585
590
request, **action_args)
586
591
except TypeError as err:
587
592
logging.error(_('Exception handling resource: %s') % str(err))
588
raise webob.exc.HTTPBadRequest()
593
msg = _('The server could not comply with the request since\r\n'
594
'it is either malformed or otherwise incorrect.\r\n')
595
err = webob.exc.HTTPBadRequest(msg)
596
raise translate_exception(err, request.best_match_language())
597
except webob.exc.HTTPException as err:
598
logging.error(_("Returning %(code)s to user: %(explanation)s"),
599
{'code': err.code, 'explanation': err.explanation})
600
raise translate_exception(err, request.best_match_language())
601
except Exception as err:
602
logging.error(_("Unexpected error occurred serving API: %s") % err)
603
raise translate_exception(err, request.best_match_language())
590
605
# Here we support either passing in a serializer or detecting it
591
606
# based on the content type.
667
def translate_exception(exc, locale):
668
"""Translates all translatable elements of the given exception."""
669
exc.message = gettextutils.get_localized_message(exc.message, locale)
670
if isinstance(exc, webob.exc.HTTPError):
671
# If the explanation is not a Message, that means that the
672
# explanation is the default, generic and not translatable explanation
673
# from webop.exc. Since the explanation is the error shown when the
674
# exception is converted to a response, let's actually swap it with
675
# message, since message is what gets passed in at construction time
677
if not isinstance(exc.explanation, gettextutils.Message):
678
exc.explanation = exc.message
682
gettextutils.get_localized_message(exc.explanation, locale)
683
exc.detail = gettextutils.get_localized_message(exc.detail, locale)
652
687
class BasePasteFactory(object):
654
689
"""A base class for paste app and filter factories.