diff options
| author | Jason Kölker <jason@koelker.net> | 2011-09-27 14:34:16 -0500 |
|---|---|---|
| committer | Jason Kölker <jason@koelker.net> | 2011-09-27 14:34:16 -0500 |
| commit | f9e64838086cb13fc1b88fbd7ff3d1560cc721b2 (patch) | |
| tree | 70836f02bb3eadf1385328ce69a9ade0a996a5e4 /openstack | |
| parent | 43b31973c0b7da171c0fd6bcbb56d8cc8326dca7 (diff) | |
| download | oslo-f9e64838086cb13fc1b88fbd7ff3d1560cc721b2.tar.gz oslo-f9e64838086cb13fc1b88fbd7ff3d1560cc721b2.tar.xz oslo-f9e64838086cb13fc1b88fbd7ff3d1560cc721b2.zip | |
make the skeleton project a template
Diffstat (limited to 'openstack')
23 files changed, 94 insertions, 1126 deletions
diff --git a/openstack/paste/templates/openstack/openstack/common/middleware/__init__.py b/openstack/paste/templates/openstack/+package+/__init__.py index e69de29..e69de29 100644 --- a/openstack/paste/templates/openstack/openstack/common/middleware/__init__.py +++ b/openstack/paste/templates/openstack/+package+/__init__.py diff --git a/openstack/paste/templates/openstack/skeleton/__init__.py b/openstack/paste/templates/openstack/+package+/api/__init__.py index e69de29..e69de29 100644 --- a/openstack/paste/templates/openstack/skeleton/__init__.py +++ b/openstack/paste/templates/openstack/+package+/api/__init__.py diff --git a/openstack/paste/templates/openstack/skeleton/api/__init__.py b/openstack/paste/templates/openstack/+package+/api/middleware/__init__.py index e69de29..e69de29 100644 --- a/openstack/paste/templates/openstack/skeleton/api/__init__.py +++ b/openstack/paste/templates/openstack/+package+/api/middleware/__init__.py diff --git a/openstack/paste/templates/openstack/skeleton/api/middleware/__init__.py b/openstack/paste/templates/openstack/+package+/api/v1/__init__.py index e69de29..e69de29 100644 --- a/openstack/paste/templates/openstack/skeleton/api/middleware/__init__.py +++ b/openstack/paste/templates/openstack/+package+/api/v1/__init__.py diff --git a/openstack/paste/templates/openstack/skeleton/version.py b/openstack/paste/templates/openstack/+package+/version.py index 943c019..943c019 100644 --- a/openstack/paste/templates/openstack/skeleton/version.py +++ b/openstack/paste/templates/openstack/+package+/version.py diff --git a/openstack/paste/templates/openstack/README b/openstack/paste/templates/openstack/README deleted file mode 100644 index bfabbe4..0000000 --- a/openstack/paste/templates/openstack/README +++ /dev/null @@ -1,10 +0,0 @@ -This is the OpenStack Skeleton project. It serves two main purposes: - -* As a template for new projects wanting to join the OpenStack project -* As a proving ground for determining best practices and common code - for all OpenStack projects - -The skeleton project is designed to show a recommended set of modules -for creating a simple WSGI server, with a versioned API, clean separation -of modules, a standard way of structuring both unit and functional tests, -configuration files, and documentation. diff --git a/openstack/paste/templates/openstack/README_tmpl b/openstack/paste/templates/openstack/README_tmpl new file mode 100644 index 0000000..edbdc5d --- /dev/null +++ b/openstack/paste/templates/openstack/README_tmpl @@ -0,0 +1,3 @@ +{{package}} + +{{description}} diff --git a/openstack/paste/templates/openstack/bin/skeleton-api b/openstack/paste/templates/openstack/bin/+package+-api_tmpl index 0bed84a..c457504 100755 --- a/openstack/paste/templates/openstack/bin/skeleton-api +++ b/openstack/paste/templates/openstack/bin/+package+-api_tmpl @@ -17,24 +17,24 @@ # under the License. """ -Simple Skeleton API Server +Simple {{project}} API Server """ import optparse import os import sys -# If ../skeleton/__init__.py exists, add ../ to Python search path, so that +# If ../{{package}}/__init__.py exists, add ../ to Python search path, so that # it will override what happens to be installed in /usr/(local/)lib/python... possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), os.pardir, os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'skeleton', '__init__.py')): +if os.path.exists(os.path.join(possible_topdir, '{{package}}', '__init__.py')): sys.path.insert(0, possible_topdir) from openstack.common import config from openstack.common import wsgi -from skeleton import version +from {{package}} import version def create_options(parser): @@ -55,8 +55,8 @@ if __name__ == '__main__': (options, args) = config.parse_options(oparser) try: - conf, app = config.load_paste_app('skeleton-api', options, args, - config_dir='skeleton') + conf, app = config.load_paste_app('{{package}}-api', options, args, + config_dir='{{package}}') server = wsgi.Server() server.start(app, int(conf['bind_port']), conf['bind_host']) diff --git a/openstack/paste/templates/openstack/doc/source/conf.py b/openstack/paste/templates/openstack/doc/source/conf.py_tmpl index cca6b7f..e4fec52 100644 --- a/openstack/paste/templates/openstack/doc/source/conf.py +++ b/openstack/paste/templates/openstack/doc/source/conf.py_tmpl @@ -15,7 +15,7 @@ # limitations under the License. # -# Skeleton documentation build configuration file, created by +# {{project}} documentation build configuration file, created by # sphinx-quickstart on Tue May 18 13:50:15 2010. # # This file is execfile()'d with the current directory set to it's containing @@ -33,7 +33,7 @@ import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.append([os.path.abspath('../skeleton'), +sys.path.append([os.path.abspath('../{{package}}'), os.path.abspath('..'), os.path.abspath('../bin') ]) @@ -69,7 +69,7 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'Skeleton' +project = u'{{project}}' copyright = u'2011-present, OpenStack, LLC.' # The version info for the project you're documenting, acts as replacement for @@ -77,11 +77,11 @@ copyright = u'2011-present, OpenStack, LLC.' # built documents. # # The short X.Y version. -from skeleton import version as skeleton_version +from {{package}} import version as {{package}}_version # The full version, including alpha/beta/rc tags. -release = skeleton_version.version_string() +release = {{package}}_version.version_string() # The short X.Y version. -version = skeleton_version.canonical_version_string() +version = {{package}}_version.canonical_version_string() # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -118,7 +118,7 @@ show_authors = True pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -modindex_common_prefix = ['skeleton.'] +modindex_common_prefix = ['{{package}}.'] # -- Options for man page output -------------------------------------------- @@ -126,12 +126,8 @@ modindex_common_prefix = ['skeleton.'] # List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual' man_pages = [ - ('man/skeletonapi', 'skeleton-api', u'Skeleton API Server', + ('man/{{package}}api', '{{package}}-api', u'{{project}} API Server', [u'OpenStack'], 1), - ('man/skeletonregistry', 'skeleton-registry', u'Skeleton Registry Server', - [u'OpenStack'], 1), - ('man/skeletonmanage', 'skeleton-manage', u'Skeleton Management Utility', - [u'OpenStack'], 1) ] @@ -207,7 +203,7 @@ html_static_path = ['_static'] #html_file_suffix = '' # Output file base name for HTML help builder. -htmlhelp_basename = 'skeletondoc' +htmlhelp_basename = '{{package}}doc' # -- Options for LaTeX output ------------------------------------------------ @@ -222,8 +218,8 @@ htmlhelp_basename = 'skeletondoc' # (source start file, target name, title, author, # documentclass [howto/manual]). latex_documents = [ - ('index', 'Skeleton.tex', u'Skeleton Documentation', - u'Skeleton Team', 'manual'), + ('index', '{{package}}.tex', u'{{project}} Documentation', + u'{{project}} Team', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of diff --git a/openstack/paste/templates/openstack/doc/source/index.rst b/openstack/paste/templates/openstack/doc/source/index.rst_tmpl index a103a89..ddbd518 100644 --- a/openstack/paste/templates/openstack/doc/source/index.rst +++ b/openstack/paste/templates/openstack/doc/source/index.rst_tmpl @@ -14,10 +14,10 @@ License for the specific language governing permissions and limitations under the License. -Welcome to Skeleton's documentation! +Welcome to {{project}}'s documentation! =================================== -Description of Skeleton project +Description of {{project}} project Concepts ======== @@ -25,7 +25,7 @@ Concepts .. toctree:: :maxdepth: 1 -Using Skeleton +Using {{project}} ============== .. toctree:: diff --git a/openstack/paste/templates/openstack/etc/skeleton-api.conf b/openstack/paste/templates/openstack/etc/+package+-api.conf index 2035721..8574a4e 100644 --- a/openstack/paste/templates/openstack/etc/skeleton-api.conf +++ b/openstack/paste/templates/openstack/etc/+package+-api.conf @@ -13,25 +13,25 @@ bind_port = 80 # Log to this file. Make sure the user running skeleton-api has # permissions to write to this file! -log_file = /var/log/skeleton/api.log +log_file = /var/log/{{package}}/api.log # Send logs to syslog (/dev/log) instead of to file specified by `log_file` use_syslog = False -[pipeline:skeleton-api] +[pipeline:{{package}}-api] pipeline = versionnegotiation context apiv1app [pipeline:versions] pipeline = versionsapp [app:versionsapp] -paste.app_factory = skeleton.api.versions:app_factory +paste.app_factory = {{package}}.api.versions:app_factory [app:apiv1app] -paste.app_factory = skeleton.api.v1:app_factory +paste.app_factory = {{package}}.api.v1:app_factory [filter:versionnegotiation] -paste.filter_factory = skeleton.api.middleware.version_negotiation:filter_factory +paste.filter_factory = {{package}}.api.middleware.version_negotiation:filter_factory [filter:context] paste.filter_factory = openstack.common.middleware.context:filter_factory diff --git a/openstack/paste/templates/openstack/openstack/__init__.py b/openstack/paste/templates/openstack/openstack/__init__.py deleted file mode 100644 index d65c689..0000000 --- a/openstack/paste/templates/openstack/openstack/__init__.py +++ /dev/null @@ -1,16 +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. diff --git a/openstack/paste/templates/openstack/openstack/common/__init__.py b/openstack/paste/templates/openstack/openstack/common/__init__.py deleted file mode 100644 index b22496f..0000000 --- a/openstack/paste/templates/openstack/openstack/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/openstack/paste/templates/openstack/openstack/common/config.py b/openstack/paste/templates/openstack/openstack/common/config.py deleted file mode 100644 index e954609..0000000 --- a/openstack/paste/templates/openstack/openstack/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/openstack/paste/templates/openstack/openstack/common/context.py b/openstack/paste/templates/openstack/openstack/common/context.py deleted file mode 100644 index a9a16f8..0000000 --- a/openstack/paste/templates/openstack/openstack/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/openstack/paste/templates/openstack/openstack/common/exception.py b/openstack/paste/templates/openstack/openstack/common/exception.py deleted file mode 100644 index a81355e..0000000 --- a/openstack/paste/templates/openstack/openstack/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/openstack/paste/templates/openstack/openstack/common/middleware/context.py b/openstack/paste/templates/openstack/openstack/common/middleware/context.py deleted file mode 100644 index be7dafe..0000000 --- a/openstack/paste/templates/openstack/openstack/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/openstack/paste/templates/openstack/openstack/common/utils.py b/openstack/paste/templates/openstack/openstack/common/utils.py deleted file mode 100644 index 0d2f89e..0000000 --- a/openstack/paste/templates/openstack/openstack/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/openstack/paste/templates/openstack/openstack/common/wsgi.py b/openstack/paste/templates/openstack/openstack/common/wsgi.py deleted file mode 100644 index 8faa6dc..0000000 --- a/openstack/paste/templates/openstack/openstack/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 diff --git a/openstack/paste/templates/openstack/setup.cfg b/openstack/paste/templates/openstack/setup.cfg deleted file mode 100644 index d53addc..0000000 --- a/openstack/paste/templates/openstack/setup.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[build_sphinx] -all_files = 1 -build-dir = doc/build -source-dir = doc/source - -[egg_info] -tag_build = -tag_date = 0 -tag_svn_revision = 0 diff --git a/openstack/paste/templates/openstack/setup.cfg_tmpl b/openstack/paste/templates/openstack/setup.cfg_tmpl new file mode 100644 index 0000000..b4bb949 --- /dev/null +++ b/openstack/paste/templates/openstack/setup.cfg_tmpl @@ -0,0 +1,13 @@ +[nosetests] +where=tests/ +verbosity=3 +debug=0 +detailed-errors=1 +with-coverage=1 +cover-package={{package}} +cover-erase=1 + +[build_sphinx] +all_files = 1 +build-dir = doc/build +source-dir = doc/source diff --git a/openstack/paste/templates/openstack/setup.py b/openstack/paste/templates/openstack/setup.py_tmpl index c6d3c69..81a3537 100644 --- a/openstack/paste/templates/openstack/setup.py +++ b/openstack/paste/templates/openstack/setup.py_tmpl @@ -22,33 +22,47 @@ import subprocess from setuptools import setup, find_packages from setuptools.command.sdist import sdist -from skeleton import version - - -if os.path.isdir('.bzr'): - with open("skeleton/vcsversion.py", 'w') as version_file: - vcs_cmd = subprocess.Popen(["bzr", "version-info", "--python"], - stdout=subprocess.PIPE) - vcsversion = vcs_cmd.communicate()[0] - version_file.write(vcsversion) +from openstack.common.utils import parse_mailmap, str_dict_replace +from {{package}} import version + + +def run_git_command(cmd): + output = subprocess.Popen(["/bin/sh", "-c", cmd], + stdout=subprocess.PIPE) + return output.communicate()[0].strip() + +if os.path.isdir('.git'): + branch_nick_cmd = 'git branch | grep -Ei "\* (.*)" | cut -f2 -d" "' + branch_nick = run_git_command(branch_nick_cmd) + revid_cmd = "git --no-pager log --max-count=1 | cut -f2 -d' ' | head -1" revid = run_git_command(revid_cmd) + revno_cmd = "git --no-pager log --oneline | wc -l" + revno = run_git_command(revno_cmd) + with open("{{package}}/vcsversion.py", 'w') as version_file: + version_file.write("""\ +# This file is automatically generated by setup.py, So don't edit it. :) +version_info = { + 'branch_nick': '%s', + 'revision_id': '%s', + 'revno': %s +} +""" % (branch_nick, revid, revno)) class local_sdist(sdist): """Customized sdist hook - builds the ChangeLog file from VC first""" - def run(self): - if os.path.isdir('.bzr'): - # We're in a bzr branch - - log_cmd = subprocess.Popen(["bzr", "log", "--gnu"], - stdout=subprocess.PIPE) - changelog = log_cmd.communicate()[0] + if os.path.isdir('.git'): + git_log_gnu = 'git log --format="%ai %aN %n%n%x09* %s%d%n"' + changelog = run_git_command(git_log_gnu) + mailmap = parse_mailmap() with open("ChangeLog", "w") as changelog_file: - changelog_file.write(changelog) + changelog_file.write(str_dict_replace(changelog, mailmap)) sdist.run(self) + cmdclass = {'sdist': local_sdist} + # If Sphinx is installed on the box running setup.py, # enable setup.py to build the documentation, otherwise, # just ignore it @@ -67,18 +81,36 @@ except: pass +requires = ['eventlet' + 'pep8', + 'pylint', + 'sphinx', + 'openstack.common', + ] + + +setup_requires = ['nose', + 'coverage', + ] + + +scripts = ['bin/{{package}}-api', + ] + setup( - name='skeleton', + name='{{project}}', version=version.canonical_version_string(), - description='The Skeleton project provides a simple WSGI server demo', + description='{{description}}', license='Apache License (2.0)', author='OpenStack', author_email='openstack@lists.launchpad.net', - url='http://skeleton.openstack.org/', + url='http://{{package}}.openstack.org/', packages=find_packages(exclude=['tests', 'bin']), test_suite='nose.collector', cmdclass=cmdclass, include_package_data=True, + install_requires=requires, + setup_requires=setup_requires, classifiers=[ 'Development Status :: 4 - Beta', 'License :: OSI Approved :: Apache Software License', @@ -86,4 +118,4 @@ setup( 'Programming Language :: Python :: 2.6', 'Environment :: No Input/Output (Daemon)', ], - scripts=['bin/skeleton-api']) + scripts=scripts) diff --git a/openstack/paste/templates/openstack/skeleton/api/v1/__init__.py b/openstack/paste/templates/openstack/skeleton/api/v1/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/openstack/paste/templates/openstack/skeleton/api/v1/__init__.py +++ /dev/null |
