summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorJason Kölker <jason@koelker.net>2011-09-27 13:21:47 -0500
committerJason Kölker <jason@koelker.net>2011-09-27 13:21:47 -0500
commita21940e2d325136661b5283bb15f1fbf9942477b (patch)
treee0b3a7fe84342adfc7770e05abd7314d13956c54 /common
parentf90e85378df8f8afe22acd530223c09cb9f7bc4c (diff)
reog from import merge
Diffstat (limited to 'common')
-rw-r--r--common/__init__.py19
-rw-r--r--common/config.py333
-rw-r--r--common/context.py40
-rw-r--r--common/exception.py143
-rw-r--r--common/middleware/__init__.py0
-rw-r--r--common/middleware/context.py64
-rw-r--r--common/utils.py89
-rw-r--r--common/wsgi.py353
8 files changed, 0 insertions, 1041 deletions
diff --git a/common/__init__.py b/common/__init__.py
deleted file mode 100644
index b22496f..0000000
--- a/common/__init__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2011 OpenStack LLC.
-# 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.
-
-# TODO(jaypipes) Code in this module is intended to be ported to the eventual
-# openstack-common library
diff --git a/common/config.py b/common/config.py
deleted file mode 100644
index e954609..0000000
--- a/common/config.py
+++ /dev/null
@@ -1,333 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2011 OpenStack LLC.
-# 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.
-
-"""
-Routines for configuring Openstack Projects
-"""
-
-import ConfigParser
-import logging
-import logging.config
-import logging.handlers
-import optparse
-import os
-import re
-import sys
-
-from paste import deploy
-
-DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s"
-DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
-
-
-def parse_options(parser, cli_args=None):
- """
- Returns the parsed CLI options, command to run and its arguments, merged
- with any same-named options found in a configuration file.
-
- The function returns a tuple of (options, args), where options is a
- mapping of option key/str(value) pairs, and args is the set of arguments
- (not options) supplied on the command-line.
-
- The reason that the option values are returned as strings only is that
- ConfigParser and paste.deploy only accept string values...
-
- :param parser: The option parser
- :param cli_args: (Optional) Set of arguments to process. If not present,
- sys.argv[1:] is used.
- :retval tuple of (options, args)
- """
-
- (options, args) = parser.parse_args(cli_args)
-
- return (vars(options), args)
-
-
-def add_common_options(parser):
- """
- Given a supplied optparse.OptionParser, adds an OptionGroup that
- represents all common configuration options.
-
- :param parser: optparse.OptionParser
- """
- help_text = "The following configuration options are common to "\
- "this app's programs."
-
- group = optparse.OptionGroup(parser, "Common Options", help_text)
- group.add_option('-v', '--verbose', default=False, dest="verbose",
- action="store_true",
- help="Print more verbose output")
- group.add_option('-d', '--debug', default=False, dest="debug",
- action="store_true",
- help="Print debugging output")
- group.add_option('--config-file', default=None, metavar="PATH",
- help="Path to the config file to use. When not specified "
- "(the default), we generally look at the first "
- "argument specified to be a config file, and if "
- "that is also missing, we search standard "
- "directories for a config file.")
- parser.add_option_group(group)
-
-
-def add_log_options(parser):
- """
- Given a supplied optparse.OptionParser, adds an OptionGroup that
- represents all the configuration options around logging.
-
- :param parser: optparse.OptionParser
- """
- help_text = "The following configuration options are specific to logging "\
- "functionality for this program."
-
- group = optparse.OptionGroup(parser, "Logging Options", help_text)
- group.add_option('--log-config', default=None, metavar="PATH",
- help="If this option is specified, the logging "
- "configuration file specified is used and overrides "
- "any other logging options specified. Please see "
- "the Python logging module documentation for "
- "details on logging configuration files.")
- group.add_option('--log-date-format', metavar="FORMAT",
- default=DEFAULT_LOG_DATE_FORMAT,
- help="Format string for %(asctime)s in log records. "
- "Default: %default")
- group.add_option('--log-file', default=None, metavar="PATH",
- help="(Optional) Name of log file to output to. "
- "If not set, logging will go to stdout.")
- group.add_option("--log-dir", default=None,
- help="(Optional) The directory to keep log files in "
- "(will be prepended to --logfile)")
- group.add_option('--use-syslog', default=False, dest="use_syslog",
- action="store_true",
- help="Use syslog for logging.")
- parser.add_option_group(group)
-
-
-def setup_logging(options, conf):
- """
- Sets up the logging options for a log with supplied name
-
- :param options: Mapping of typed option key/values
- :param conf: Mapping of untyped key/values from config file
- """
-
- if options.get('log_config', None):
- # Use a logging configuration file for all settings...
- if os.path.exists(options['log_config']):
- logging.config.fileConfig(options['log_config'])
- return
- else:
- raise RuntimeError("Unable to locate specified logging "
- "config file: %s" % options['log_config'])
-
- # If either the CLI option or the conf value
- # is True, we set to True
- debug = options.get('debug') or \
- get_option(conf, 'debug', type='bool', default=False)
- verbose = options.get('verbose') or \
- get_option(conf, 'verbose', type='bool', default=False)
- root_logger = logging.root
- if debug:
- root_logger.setLevel(logging.DEBUG)
- elif verbose:
- root_logger.setLevel(logging.INFO)
- else:
- root_logger.setLevel(logging.WARNING)
-
- # Set log configuration from options...
- # Note that we use a hard-coded log format in the options
- # because of Paste.Deploy bug #379
- # http://trac.pythonpaste.org/pythonpaste/ticket/379
- log_format = options.get('log_format', DEFAULT_LOG_FORMAT)
- log_date_format = options.get('log_date_format', DEFAULT_LOG_DATE_FORMAT)
- formatter = logging.Formatter(log_format, log_date_format)
-
- logfile = options.get('log_file')
- if not logfile:
- logfile = conf.get('log_file')
-
- use_syslog = options.get('use_syslog') or \
- get_option(conf, 'use_syslog', type='bool', default=False)
-
- if use_syslog:
- handler = logging.handlers.SysLogHandler(address='/dev/log')
- elif logfile:
- logdir = options.get('log_dir')
- if not logdir:
- logdir = conf.get('log_dir')
- if logdir:
- logfile = os.path.join(logdir, logfile)
- handler = logging.FileHandler(logfile)
- else:
- handler = logging.StreamHandler(sys.stdout)
-
- handler.setFormatter(formatter)
- root_logger.addHandler(handler)
-
-
-def find_config_file(app_name, options, args, config_dir=None):
- """
- Return the first config file found for an application.
-
- We search for the paste config file in the following order:
- * If --config-file option is used, use that
- * If args[0] is a file, use that
- * Search for $app.conf in standard directories:
- * .
- * ~.config_dir/
- * ~
- * /etc/config_dir
- * /etc
-
- :retval Full path to config file, or None if no config file found
- """
- config_dir = config_dir or app_name
-
- fix_path = lambda p: os.path.abspath(os.path.expanduser(p))
- if options.get('config_file'):
- if os.path.exists(options['config_file']):
- return fix_path(options['config_file'])
- elif args:
- if os.path.exists(args[0]):
- return fix_path(args[0])
-
- # Handle standard directory search for $app_name.conf
- config_file_dirs = [fix_path(os.getcwd()),
- fix_path(os.path.join('~', '.' + config_dir)),
- fix_path('~'),
- os.path.join('/etc', config_dir),
- '/etc']
-
- for cfg_dir in config_file_dirs:
- cfg_file = os.path.join(cfg_dir, '%s.conf' % app_name)
- if os.path.exists(cfg_file):
- return cfg_file
-
-
-def load_paste_config(app_name, options, args, config_dir=None):
- """
- Looks for a config file to use for an app and returns the
- config file path and a configuration mapping from a paste config file.
-
- We search for the paste config file in the following order:
- * If --config-file option is used, use that
- * If args[0] is a file, use that
- * Search for $app_name.conf in standard directories:
- * .
- * ~.config_dir/
- * ~
- * /etc/config_dir
- * /etc
-
- :param app_name: Name of the application to load config for, or None.
- None signifies to only load the [DEFAULT] section of
- the config file.
- :param options: Set of typed options returned from parse_options()
- :param args: Command line arguments from argv[1:]
- :retval Tuple of (conf_file, conf)
-
- :raises RuntimeError when config file cannot be located or there was a
- problem loading the configuration file.
- """
- conf_file = find_config_file(app_name, options, args, config_dir)
- if not conf_file:
- raise RuntimeError("Unable to locate any configuration file. "
- "Cannot load application %s" % app_name)
- try:
- conf = deploy.appconfig("config:%s" % conf_file, name=app_name)
- return conf_file, conf
- except Exception, e:
- raise RuntimeError("Error trying to load config %s: %s"
- % (conf_file, e))
-
-
-def load_paste_app(app_name, options, args, config_dir=None):
- """
- Builds and returns a WSGI app from a paste config file.
-
- We search for the paste config file in the following order:
- * If --config-file option is used, use that
- * If args[0] is a file, use that
- * Search for $app_name.conf in standard directories:
- * .
- * ~.config_dir/
- * ~
- * /etc/config_dir
- * /etc
-
- :param app_name: Name of the application to load
- :param options: Set of typed options returned from parse_options()
- :param args: Command line arguments from argv[1:]
-
- :raises RuntimeError when config file cannot be located or application
- cannot be loaded from config file
- """
- conf_file, conf = load_paste_config(app_name, options,
- args, config_dir)
-
- try:
- # Setup logging early, supplying both the CLI options and the
- # configuration mapping from the config file
- setup_logging(options, conf)
-
- # We only update the conf dict for the verbose and debug
- # flags. Everything else must be set up in the conf file...
- debug = options.get('debug') or \
- get_option(conf, 'debug', type='bool', default=False)
- verbose = options.get('verbose') or \
- get_option(conf, 'verbose', type='bool', default=False)
- conf['debug'] = debug
- conf['verbose'] = verbose
-
- # Log the options used when starting if we're in debug mode...
- if debug:
- logger = logging.getLogger(app_name)
- logger.debug("*" * 80)
- logger.debug("Configuration options gathered from config file:")
- logger.debug(conf_file)
- logger.debug("================================================")
- items = dict([(k, v) for k, v in conf.items()
- if k not in ('__file__', 'here')])
- for key, value in sorted(items.items()):
- logger.debug("%(key)-30s %(value)s" % locals())
- logger.debug("*" * 80)
- app = deploy.loadapp("config:%s" % conf_file, name=app_name)
- except (LookupError, ImportError), e:
- raise RuntimeError("Unable to load %(app_name)s from "
- "configuration file %(conf_file)s."
- "\nGot: %(e)r" % locals())
- return conf, app
-
-
-def get_option(options, option, **kwargs):
- if option in options:
- value = options[option]
- type_ = kwargs.get('type', 'str')
- if type_ == 'bool':
- if hasattr(value, 'lower'):
- return value.lower() == 'true'
- else:
- return value
- elif type_ == 'int':
- return int(value)
- elif type_ == 'float':
- return float(value)
- else:
- return value
- elif 'default' in kwargs:
- return kwargs['default']
- else:
- raise KeyError("option '%s' not found" % option)
diff --git a/common/context.py b/common/context.py
deleted file mode 100644
index a9a16f8..0000000
--- a/common/context.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2011 OpenStack LLC.
-# 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.
-
-"""
-Simple class that stores security context information in the web request.
-
-Projects should subclass this class if they wish to enhance the request
-context or provide additional information in their specific WSGI pipeline.
-"""
-
-
-class RequestContext(object):
-
- """
- Stores information about the security context under which the user
- accesses the system, as well as additional request information.
- """
-
- def __init__(self, auth_tok=None, user=None, tenant=None, is_admin=False,
- read_only=False, show_deleted=False):
- self.auth_tok = auth_tok
- self.user = user
- self.tenant = tenant
- self.is_admin = is_admin
- self.read_only = read_only
- self.show_deleted = show_deleted
diff --git a/common/exception.py b/common/exception.py
deleted file mode 100644
index a81355e..0000000
--- a/common/exception.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2011 OpenStack LLC.
-# 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.
-
-"""
-Exceptions common to OpenStack projects
-"""
-
-import logging
-
-
-class ProcessExecutionError(IOError):
- def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None,
- description=None):
- if description is None:
- description = "Unexpected error while running command."
- if exit_code is None:
- exit_code = '-'
- message = "%s\nCommand: %s\nExit code: %s\nStdout: %r\nStderr: %r" % (
- description, cmd, exit_code, stdout, stderr)
- IOError.__init__(self, message)
-
-
-class Error(Exception):
- def __init__(self, message=None):
- super(Error, self).__init__(message)
-
-
-class ApiError(Error):
- def __init__(self, message='Unknown', code='Unknown'):
- self.message = message
- self.code = code
- super(ApiError, self).__init__('%s: %s' % (code, message))
-
-
-class NotFound(Error):
- pass
-
-
-class UnknownScheme(Error):
-
- msg = "Unknown scheme '%s' found in URI"
-
- def __init__(self, scheme):
- msg = self.__class__.msg % scheme
- super(UnknownScheme, self).__init__(msg)
-
-
-class BadStoreUri(Error):
-
- msg = "The Store URI %s was malformed. Reason: %s"
-
- def __init__(self, uri, reason):
- msg = self.__class__.msg % (uri, reason)
- super(BadStoreUri, self).__init__(msg)
-
-
-class Duplicate(Error):
- pass
-
-
-class NotAuthorized(Error):
- pass
-
-
-class NotEmpty(Error):
- pass
-
-
-class Invalid(Error):
- pass
-
-
-class BadInputError(Exception):
- """Error resulting from a client sending bad input to a server"""
- pass
-
-
-class MissingArgumentError(Error):
- pass
-
-
-class DatabaseMigrationError(Error):
- pass
-
-
-class ClientConnectionError(Exception):
- """Error resulting from a client connecting to a server"""
- pass
-
-
-def wrap_exception(f):
- def _wrap(*args, **kw):
- try:
- return f(*args, **kw)
- except Exception, e:
- if not isinstance(e, Error):
- #exc_type, exc_value, exc_traceback = sys.exc_info()
- logging.exception('Uncaught exception')
- #logging.error(traceback.extract_stack(exc_traceback))
- raise Error(str(e))
- raise
- _wrap.func_name = f.func_name
- return _wrap
-
-
-class OpenstackException(Exception):
- """
- Base Exception
-
- To correctly use this class, inherit from it and define
- a 'message' property. That message will get printf'd
- with the keyword arguments provided to the constructor.
- """
- message = "An unknown exception occurred"
-
- def __init__(self, **kwargs):
- try:
- self._error_string = self.message % kwargs
-
- except Exception:
- # at least get the core message out if something happened
- self._error_string = self.message
-
- def __str__(self):
- return self._error_string
-
-
-class InvalidContentType(OpenstackException):
- message = "Invalid content type %(content_type)s"
diff --git a/common/middleware/__init__.py b/common/middleware/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/common/middleware/__init__.py
+++ /dev/null
diff --git a/common/middleware/context.py b/common/middleware/context.py
deleted file mode 100644
index be7dafe..0000000
--- a/common/middleware/context.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2011 OpenStack LLC.
-# 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.
-
-"""
-Middleware that attaches a context to the WSGI request
-"""
-
-from openstack.common import utils
-from openstack.common import wsgi
-from openstack.common import context
-
-
-class ContextMiddleware(wsgi.Middleware):
- def __init__(self, app, options):
- self.options = options
- super(ContextMiddleware, self).__init__(app)
-
- def make_context(self, *args, **kwargs):
- """
- Create a context with the given arguments.
- """
-
- # Determine the context class to use
- ctxcls = context.RequestContext
- if 'context_class' in self.options:
- ctxcls = utils.import_class(self.options['context_class'])
-
- return ctxcls(*args, **kwargs)
-
- def process_request(self, req):
- """
- Extract any authentication information in the request and
- construct an appropriate context from it.
- """
- # Use the default empty context, with admin turned on for
- # backwards compatibility
- req.context = self.make_context(is_admin=True)
-
-
-def filter_factory(global_conf, **local_conf):
- """
- Factory method for paste.deploy
- """
- conf = global_conf.copy()
- conf.update(local_conf)
-
- def filter(app):
- return ContextMiddleware(app, conf)
-
- return filter
diff --git a/common/utils.py b/common/utils.py
deleted file mode 100644
index 0d2f89e..0000000
--- a/common/utils.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2011 OpenStack LLC.
-# 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.
-
-"""
-System-level utilities and helper functions.
-"""
-
-import datetime
-import sys
-
-from openstack.common import exception
-
-
-TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
-
-
-def int_from_bool_as_string(subject):
- """
- Interpret a string as a boolean and return either 1 or 0.
-
- Any string value in:
- ('True', 'true', 'On', 'on', '1')
- is interpreted as a boolean True.
-
- Useful for JSON-decoded stuff and config file parsing
- """
- return bool_from_string(subject) and 1 or 0
-
-
-def bool_from_string(subject):
- """
- Interpret a string as a boolean.
-
- Any string value in:
- ('True', 'true', 'On', 'on', '1')
- is interpreted as a boolean True.
-
- Useful for JSON-decoded stuff and config file parsing
- """
- if type(subject) == type(bool):
- return subject
- if hasattr(subject, 'startswith'): # str or unicode...
- if subject.strip().lower() in ('true', 'on', '1'):
- return True
- return False
-
-
-def import_class(import_str):
- """Returns a class from a string including module and class"""
- mod_str, _sep, class_str = import_str.rpartition('.')
- try:
- __import__(mod_str)
- return getattr(sys.modules[mod_str], class_str)
- except (ImportError, ValueError, AttributeError):
- raise exception.NotFound('Class %s cannot be found' % class_str)
-
-
-def import_object(import_str):
- """Returns an object including a module or module and class"""
- try:
- __import__(import_str)
- return sys.modules[import_str]
- except ImportError:
- cls = import_class(import_str)
- return cls()
-
-
-def isotime(at=None):
- if not at:
- at = datetime.datetime.utcnow()
- return at.strftime(TIME_FORMAT)
-
-
-def parse_isotime(timestr):
- return datetime.datetime.strptime(timestr, TIME_FORMAT)
diff --git a/common/wsgi.py b/common/wsgi.py
deleted file mode 100644
index 8faa6dc..0000000
--- a/common/wsgi.py
+++ /dev/null
@@ -1,353 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2011 OpenStack LLC.
-# 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 json
-import logging
-import sys
-import datetime
-
-import eventlet
-import eventlet.wsgi
-eventlet.patcher.monkey_patch(all=False, socket=True)
-import routes
-import routes.middleware
-import webob.dec
-import webob.exc
-
-from openstack.common import exception
-
-
-class WritableLogger(object):
- """A thin wrapper that responds to `write` and logs."""
-
- def __init__(self, logger, level=logging.DEBUG):
- self.logger = logger
- self.level = level
-
- def write(self, msg):
- self.logger.log(self.level, msg.strip("\n"))
-
-
-def run_server(application, port):
- """Run a WSGI server with the given application."""
- sock = eventlet.listen(('0.0.0.0', port))
- eventlet.wsgi.server(sock, application)
-
-
-class Server(object):
- """Server class to manage multiple WSGI sockets and applications."""
-
- def __init__(self, threads=1000):
- self.pool = eventlet.GreenPool(threads)
-
- def start(self, application, port, host='0.0.0.0', backlog=128):
- """Run a WSGI server with the given application."""
- socket = eventlet.listen((host, port), backlog=backlog)
- self.pool.spawn_n(self._run, application, socket)
-
- def wait(self):
- """Wait until all servers have completed running."""
- try:
- self.pool.waitall()
- except KeyboardInterrupt:
- pass
-
- def _run(self, application, socket):
- """Start a WSGI server in a new green thread."""
- logger = logging.getLogger('eventlet.wsgi.server')
- eventlet.wsgi.server(socket, application, custom_pool=self.pool,
- log=WritableLogger(logger))
-
-
-class Middleware(object):
- """
- Base WSGI middleware wrapper. 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.
- """
-
- 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
- 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 that can be inserted into any WSGI application chain
- to get information about the request and response.
- """
-
- @webob.dec.wsgify
- def __call__(self, req):
- print ("*" * 40) + " REQUEST ENVIRON"
- for key, value in req.environ.items():
- print key, "=", value
- print
- resp = req.get_response(self.application)
-
- print ("*" * 40) + " RESPONSE HEADERS"
- for (key, value) in resp.headers.iteritems():
- print key, "=", value
- print
-
- 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 iterator
- when iterated.
- """
- print ("*" * 40) + " BODY"
- for part in app_iter:
- sys.stdout.write(part)
- sys.stdout.flush()
- yield part
- print
-
-
-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 a wsgi.Controller, who will route
- the request to the action 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
- 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
- def _dispatch(req):
- """
- 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 Request(webob.Request):
-
- """Add some Openstack API-specific logic to the base webob.Request."""
-
- def best_match_content_type(self):
- """Determine the requested response content-type."""
- supported = ('application/json',)
- bm = self.accept.best_match(supported)
- return bm or 'application/json'
-
- def get_content_type(self, allowed_content_types):
- """Determine content type of the request body."""
- if not "Content-Type" in self.headers:
- raise exception.InvalidContentType(content_type=None)
-
- content_type = self.content_type
-
- if content_type not in allowed_content_types:
- raise exception.InvalidContentType(content_type=content_type)
- else:
- return content_type
-
-
-class JSONRequestDeserializer(object):
- def has_body(self, request):
- """
- Returns whether a Webob.Request object will possess an entity body.
-
- :param request: Webob.Request object
- """
- if 'transfer-encoding' in request.headers:
- return True
- elif request.content_length > 0:
- return True
-
- return False
-
- def from_json(self, datastring):
- return json.loads(datastring)
-
- def default(self, request):
- if self.has_body(request):
- return {'body': self.from_json(request.body)}
- else:
- return {}
-
-
-class JSONResponseSerializer(object):
-
- def to_json(self, data):
- def sanitizer(obj):
- if isinstance(obj, datetime.datetime):
- return obj.isoformat()
- return obj
-
- return json.dumps(data, default=sanitizer)
-
- def default(self, response, result):
- response.headers.add('Content-Type', 'application/json')
- response.body = self.to_json(result)
-
-
-class Resource(object):
- """
- WSGI app that handles (de)serialization and controller dispatch.
-
- Reads routing information supplied by RoutesMiddleware and calls
- the requested action method upon its deserializer, controller,
- and serializer. Those three objects may implement any of the basic
- controller action methods (create, update, show, index, delete)
- along with any that may be specified in the api router. A 'default'
- method may also be implemented to be used in place of any
- non-implemented actions. Deserializer methods must accept a request
- argument and return a dictionary. Controller methods must accept a
- request argument. Additionally, they must also accept keyword
- arguments that represent the keys returned by the Deserializer. They
- may raise a webob.exc exception or return a dict, which will be
- serialized by requested content type.
- """
- def __init__(self, controller, deserializer, serializer):
- """
- :param controller: object that implement methods created by routes lib
- :param deserializer: object that supports webob request deserialization
- through controller-like actions
- :param serializer: object that supports webob response serialization
- through controller-like actions
- """
- self.controller = controller
- self.serializer = serializer
- self.deserializer = deserializer
-
- @webob.dec.wsgify(RequestClass=Request)
- def __call__(self, request):
- """WSGI method that controls (de)serialization and method dispatch."""
- action_args = self.get_action_args(request.environ)
- action = action_args.pop('action', None)
-
- deserialized_params = self.deserialize_request(action, request)
- action_args.update(deserialized_params)
- action_result = self.execute_action(action, request, **action_args)
- try:
- return self.serialize_response(action, action_result, request)
-
- # return unserializable result (typically a webob exc)
- except Exception:
- return action_result
-
- def deserialize_request(self, action, request):
- return self.dispatch(self.deserializer, action, request)
-
- def serialize_response(self, action, action_result, request):
- response = webob.Response()
- self.dispatch(self.serializer, action, response,
- action_result, request)
- return response
-
- def execute_action(self, action, request, **action_args):
- return self.dispatch(self.controller, action, request, **action_args)
-
- def dispatch(self, obj, action, *args, **kwargs):
- """Find action-specific method on self and call it."""
- try:
- method = getattr(obj, action)
- except AttributeError:
- method = getattr(obj, 'default')
-
- return method(*args, **kwargs)
-
- def get_action_args(self, request_environment):
- """Parse dictionary created by routes library."""
- try:
- args = request_environment['wsgiorg.routing_args'][1].copy()
- except Exception:
- return {}
-
- try:
- del args['controller']
- except KeyError:
- pass
-
- try:
- del args['format']
- except KeyError:
- pass
-
- return args