summaryrefslogtreecommitdiffstats
path: root/openstack
diff options
context:
space:
mode:
Diffstat (limited to 'openstack')
-rw-r--r--openstack/common/config.py337
-rw-r--r--openstack/common/extensions.py539
2 files changed, 0 insertions, 876 deletions
diff --git a/openstack/common/config.py b/openstack/common/config.py
deleted file mode 100644
index 12645f3..0000000
--- a/openstack/common/config.py
+++ /dev/null
@@ -1,337 +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 logging
-import logging.config
-import logging.handlers
-import optparse
-import os
-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 fix_path(path):
- """
- Return the full absolute path
- """
- return os.path.abspath(os.path.expanduser(path))
-
-
-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
-
- 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/common/extensions.py b/openstack/common/extensions.py
deleted file mode 100644
index 906906d..0000000
--- a/openstack/common/extensions.py
+++ /dev/null
@@ -1,539 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2011 OpenStack LLC.
-# Copyright 2011 Justin Santa Barbara
-# 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.
-
-import imp
-import logging
-from lxml import etree
-import os
-import routes
-import webob.dec
-import webob.exc
-
-from openstack.common import exception
-from openstack.common.gettextutils import _
-from openstack.common import wsgi
-
-
-LOG = logging.getLogger('extensions')
-DEFAULT_XMLNS = "http://docs.openstack.org/"
-XMLNS_ATOM = "http://www.w3.org/2005/Atom"
-
-
-class ExtensionDescriptor(object):
- """Base class that defines the contract for extensions.
-
- Note that you don't have to derive from this class to have a valid
- extension; it is purely a convenience.
-
- """
-
- def get_name(self):
- """The name of the extension.
-
- e.g. 'Fox In Socks'
-
- """
- raise NotImplementedError()
-
- def get_alias(self):
- """The alias for the extension.
-
- e.g. 'FOXNSOX'
-
- """
- raise NotImplementedError()
-
- def get_description(self):
- """Friendly description for the extension.
-
- e.g. 'The Fox In Socks Extension'
-
- """
- raise NotImplementedError()
-
- def get_namespace(self):
- """The XML namespace for the extension.
-
- e.g. 'http://www.fox.in.socks/api/ext/pie/v1.0'
-
- """
- raise NotImplementedError()
-
- def get_updated(self):
- """The timestamp when the extension was last updated.
-
- e.g. '2011-01-22T13:25:27-06:00'
-
- """
- # NOTE(justinsb): Not sure of the purpose of this is, vs the XML NS
- raise NotImplementedError()
-
- def get_resources(self):
- """List of extensions.ResourceExtension extension objects.
-
- Resources define new nouns, and are accessible through URLs.
-
- """
- resources = []
- return resources
-
- def get_actions(self):
- """List of extensions.ActionExtension extension objects.
-
- Actions are verbs callable from the API.
-
- """
- actions = []
- return actions
-
- def get_request_extensions(self):
- """List of extensions.RequestException extension objects.
-
- Request extensions are used to handle custom request data.
-
- """
- request_exts = []
- return request_exts
-
-
-class ActionExtensionController(object):
- def __init__(self, application):
- self.application = application
- self.action_handlers = {}
-
- def add_action(self, action_name, handler):
- self.action_handlers[action_name] = handler
-
- def action(self, req, id, body):
- for action_name, handler in self.action_handlers.iteritems():
- if action_name in body:
- return handler(body, req, id)
- # no action handler found (bump to downstream application)
- res = self.application
- return res
-
-
-class ActionExtensionResource(wsgi.Resource):
-
- def __init__(self, application):
- controller = ActionExtensionController(application)
- wsgi.Resource.__init__(self, controller)
-
- def add_action(self, action_name, handler):
- self.controller.add_action(action_name, handler)
-
-
-class RequestExtensionController(object):
-
- def __init__(self, application):
- self.application = application
- self.handlers = []
-
- def add_handler(self, handler):
- self.handlers.append(handler)
-
- def process(self, req, *args, **kwargs):
- res = req.get_response(self.application)
- # currently request handlers are un-ordered
- for handler in self.handlers:
- res = handler(req, res)
- return res
-
-
-class RequestExtensionResource(wsgi.Resource):
-
- def __init__(self, application):
- controller = RequestExtensionController(application)
- wsgi.Resource.__init__(self, controller)
-
- def add_handler(self, handler):
- self.controller.add_handler(handler)
-
-
-class ExtensionsResource(wsgi.Resource):
-
- def __init__(self, extension_manager):
- self.extension_manager = extension_manager
- body_serializers = {'application/xml': ExtensionsXMLSerializer()}
- serializer = wsgi.ResponseSerializer(body_serializers=body_serializers)
- super(ExtensionsResource, self).__init__(self, None, serializer)
-
- def _translate(self, ext):
- ext_data = {}
- ext_data['name'] = ext.get_name()
- ext_data['alias'] = ext.get_alias()
- ext_data['description'] = ext.get_description()
- ext_data['namespace'] = ext.get_namespace()
- ext_data['updated'] = ext.get_updated()
- ext_data['links'] = [] # TODO(dprince): implement extension links
- return ext_data
-
- def index(self, req):
- extensions = []
- for _alias, ext in self.extension_manager.extensions.iteritems():
- extensions.append(self._translate(ext))
- return dict(extensions=extensions)
-
- def show(self, req, id):
- # NOTE(dprince): the extensions alias is used as the 'id' for show
- ext = self.extension_manager.extensions.get(id, None)
- if not ext:
- raise webob.exc.HTTPNotFound(
- _("Extension with alias %s does not exist") % id)
-
- return dict(extension=self._translate(ext))
-
- def delete(self, req, id):
- raise webob.exc.HTTPNotFound()
-
- def create(self, req):
- raise webob.exc.HTTPNotFound()
-
-
-class ExtensionMiddleware(wsgi.Middleware):
- """Extensions middleware for WSGI."""
-
- @classmethod
- def factory(cls, global_config, **local_config):
- """Paste factory."""
- def _factory(app):
- return cls(app, global_config, **local_config)
- return _factory
-
- def _action_ext_resources(self, application, ext_mgr, mapper):
- """Return a dict of ActionExtensionResource-s by collection."""
- action_resources = {}
- for action in ext_mgr.get_actions():
- if not action.collection in action_resources.keys():
- resource = ActionExtensionResource(application)
- mapper.connect("/%s/:(id)/action.:(format)" %
- action.collection,
- action='action',
- controller=resource,
- conditions=dict(method=['POST']))
- mapper.connect("/%s/:(id)/action" %
- action.collection,
- action='action',
- controller=resource,
- conditions=dict(method=['POST']))
- action_resources[action.collection] = resource
-
- return action_resources
-
- def _request_ext_resources(self, application, ext_mgr, mapper):
- """Returns a dict of RequestExtensionResource-s by collection."""
- request_ext_resources = {}
- for req_ext in ext_mgr.get_request_extensions():
- if not req_ext.key in request_ext_resources.keys():
- resource = RequestExtensionResource(application)
- mapper.connect(req_ext.url_route + '.:(format)',
- action='process',
- controller=resource,
- conditions=req_ext.conditions)
-
- mapper.connect(req_ext.url_route,
- action='process',
- controller=resource,
- conditions=req_ext.conditions)
- request_ext_resources[req_ext.key] = resource
-
- return request_ext_resources
-
- def __init__(self, application, config, ext_mgr=None):
- ext_mgr = ext_mgr or ExtensionManager(config['api_extensions_path'])
- mapper = routes.Mapper()
-
- # extended resources
- for resource_ext in ext_mgr.get_resources():
- LOG.debug(_('Extended resource: %s'), resource_ext.collection)
- controller_resource = wsgi.Resource(resource_ext.controller,
- resource_ext.deserializer,
- resource_ext.serializer)
- self._map_custom_collection_actions(resource_ext, mapper,
- controller_resource)
- kargs = dict(controller=controller_resource,
- collection=resource_ext.collection_actions,
- member=resource_ext.member_actions)
- if resource_ext.parent:
- kargs['parent_resource'] = resource_ext.parent
- mapper.resource(resource_ext.collection,
- resource_ext.collection, **kargs)
-
- # extended actions
- action_resources = self._action_ext_resources(application, ext_mgr,
- mapper)
- for action in ext_mgr.get_actions():
- LOG.debug(_('Extended action: %s'), action.action_name)
- resource = action_resources[action.collection]
- resource.add_action(action.action_name, action.handler)
-
- # extended requests
- req_controllers = self._request_ext_resources(application, ext_mgr,
- mapper)
- for request_ext in ext_mgr.get_request_extensions():
- LOG.debug(_('Extended request: %s'), request_ext.key)
- controller = req_controllers[request_ext.key]
- controller.add_handler(request_ext.handler)
-
- self._router = routes.middleware.RoutesMiddleware(self._dispatch,
- mapper)
-
- super(ExtensionMiddleware, self).__init__(application)
-
- def _map_custom_collection_actions(self, resource_ext, mapper,
- controller_resource):
- for action, method in resource_ext.collection_actions.iteritems():
- parent = resource_ext.parent
- conditions = dict(method=[method])
- path = "/%s/%s" % (resource_ext.collection, action)
-
- path_prefix = ""
- if parent:
- path_prefix = "/%s/{%s_id}" % (parent["collection_name"],
- parent["member_name"])
-
- with mapper.submapper(controller=controller_resource,
- action=action,
- path_prefix=path_prefix,
- conditions=conditions) as submap:
- submap.connect(path)
- submap.connect("%s.:(format)" % path)
-
- @webob.dec.wsgify(RequestClass=wsgi.Request)
- def __call__(self, req):
- """Route the incoming request with router."""
- req.environ['extended.app'] = self.application
- return self._router
-
- @staticmethod
- @webob.dec.wsgify(RequestClass=wsgi.Request)
- def _dispatch(req):
- """Dispatch the request.
-
- Returns the routed WSGI app's response or defers to the extended
- application.
-
- """
- match = req.environ['wsgiorg.routing_args'][1]
- if not match:
- return req.environ['extended.app']
- app = match['controller']
- return app
-
-
-class ExtensionManager(object):
- """Load extensions from the configured extension path.
-
- See nova/tests/api/openstack/extensions/foxinsocks/extension.py for an
- example extension implementation.
-
- """
-
- def __init__(self, path):
- LOG.debug(_('Initializing extension manager.'))
-
- self.path = path
- self.extensions = {}
- self._load_all_extensions()
-
- def get_resources(self):
- """Returns a list of ResourceExtension objects."""
- resources = []
- extension_resource = ExtensionsResource(self)
- res_ext = ResourceExtension('extensions',
- extension_resource,
- serializer=extension_resource.serializer)
- resources.append(res_ext)
- for alias, ext in self.extensions.iteritems():
- try:
- resources.extend(ext.get_resources())
- except AttributeError:
- # NOTE(dprince): Extension aren't required to have resource
- # extensions
- pass
- return resources
-
- def get_actions(self):
- """Returns a list of ActionExtension objects."""
- actions = []
- for alias, ext in self.extensions.iteritems():
- try:
- actions.extend(ext.get_actions())
- except AttributeError:
- # NOTE(dprince): Extension aren't required to have action
- # extensions
- pass
- return actions
-
- def get_request_extensions(self):
- """Returns a list of RequestExtension objects."""
- request_exts = []
- for alias, ext in self.extensions.iteritems():
- try:
- request_exts.extend(ext.get_request_extensions())
- except AttributeError:
- # NOTE(dprince): Extension aren't required to have request
- # extensions
- pass
- return request_exts
-
- def _check_extension(self, extension):
- """Checks for required methods in extension objects."""
- try:
- LOG.debug(_('Ext name: %s'), extension.get_name())
- LOG.debug(_('Ext alias: %s'), extension.get_alias())
- LOG.debug(_('Ext description: %s'), extension.get_description())
- LOG.debug(_('Ext namespace: %s'), extension.get_namespace())
- LOG.debug(_('Ext updated: %s'), extension.get_updated())
- except AttributeError as ex:
- LOG.exception(_("Exception loading extension: %s"), unicode(ex))
- return False
- return True
-
- def _load_all_extensions(self):
- """Load extensions from the configured path.
-
- Load extensions from the configured path. The extension name is
- constructed from the module_name. If your extension module was named
- widgets.py the extension class within that module should be
- 'Widgets'.
-
- In addition, extensions are loaded from the 'contrib' directory.
-
- See nova/tests/api/openstack/extensions/foxinsocks.py for an example
- extension implementation.
-
- """
- if os.path.exists(self.path):
- self._load_all_extensions_from_path(self.path)
-
- contrib_path = os.path.join(os.path.dirname(__file__), "contrib")
- if os.path.exists(contrib_path):
- self._load_all_extensions_from_path(contrib_path)
-
- def _load_all_extensions_from_path(self, path):
- for f in os.listdir(path):
- LOG.debug(_('Loading extension file: %s'), f)
- mod_name, file_ext = os.path.splitext(os.path.split(f)[-1])
- ext_path = os.path.join(path, f)
- if file_ext.lower() == '.py' and not mod_name.startswith('_'):
- mod = imp.load_source(mod_name, ext_path)
- ext_name = mod_name[0].upper() + mod_name[1:]
- new_ext_class = getattr(mod, ext_name, None)
- if not new_ext_class:
- LOG.warn(_('Did not find expected name '
- '"%(ext_name)s" in %(file)s'),
- {'ext_name': ext_name,
- 'file': ext_path})
- continue
- new_ext = new_ext_class()
- self.add_extension(new_ext)
-
- def add_extension(self, ext):
- # Do nothing if the extension doesn't check out
- if not self._check_extension(ext):
- return
-
- alias = ext.get_alias()
- LOG.debug(_('Loaded extension: %s'), alias)
-
- if alias in self.extensions:
- raise exception.Error("Found duplicate extension: %s" % alias)
- self.extensions[alias] = ext
-
-
-class RequestExtension(object):
- """Extend requests and responses of core nova OpenStack API resources.
-
- Provide a way to add data to responses and handle custom request data
- that is sent to core nova OpenStack API controllers.
-
- """
- def __init__(self, method, url_route, handler):
- self.url_route = url_route
- self.handler = handler
- self.conditions = dict(method=[method])
- self.key = "%s-%s" % (method, url_route)
-
-
-class ActionExtension(object):
- """Add custom actions to core nova OpenStack API resources."""
-
- def __init__(self, collection, action_name, handler):
- self.collection = collection
- self.action_name = action_name
- self.handler = handler
-
-
-class ResourceExtension(object):
- """Add top level resources to the OpenStack API in nova."""
-
- def __init__(self, collection, controller, parent=None,
- collection_actions=None, member_actions=None,
- deserializer=None, serializer=None):
- if not collection_actions:
- collection_actions = {}
- if not member_actions:
- member_actions = {}
- self.collection = collection
- self.controller = controller
- self.parent = parent
- self.collection_actions = collection_actions
- self.member_actions = member_actions
- self.deserializer = deserializer
- self.serializer = serializer
-
-
-class ExtensionsXMLSerializer(wsgi.XMLDictSerializer):
-
- def __init__(self):
- self.nsmap = {None: DEFAULT_XMLNS, 'atom': XMLNS_ATOM}
-
- def show(self, ext_dict):
- ext = etree.Element('extension', nsmap=self.nsmap)
- self._populate_ext(ext, ext_dict['extension'])
- return self._to_xml(ext)
-
- def index(self, exts_dict):
- exts = etree.Element('extensions', nsmap=self.nsmap)
- for ext_dict in exts_dict['extensions']:
- ext = etree.SubElement(exts, 'extension')
- self._populate_ext(ext, ext_dict)
- return self._to_xml(exts)
-
- def _populate_ext(self, ext_elem, ext_dict):
- """Populate an extension xml element from a dict."""
-
- ext_elem.set('name', ext_dict['name'])
- ext_elem.set('namespace', ext_dict['namespace'])
- ext_elem.set('alias', ext_dict['alias'])
- ext_elem.set('updated', ext_dict['updated'])
- desc = etree.Element('description')
- desc.text = ext_dict['description']
- ext_elem.append(desc)
- for link in ext_dict.get('links', []):
- elem = etree.SubElement(ext_elem, '{%s}link' % XMLNS_ATOM)
- elem.set('rel', link['rel'])
- elem.set('href', link['href'])
- elem.set('type', link['type'])
- return ext_elem
-
- def _to_xml(self, root):
- """Convert the xml object to an xml string."""
-
- return etree.tostring(root, encoding='UTF-8')