Implements the blueprint use-common-cfg for the quantum service.
More specifically uses global CONF for the quantum.conf file. Added support for the RYU plugin (similar to ovs and lb, which use non-global conf for plugins) patch 27: clean up find_config_file patch 28: for config file use old paths (plugin unit tests) this hopefully will be replaced when we move to common config file patch 30: rebase and merge (utils.py and policy.py) Change-Id: Ic0bf5bdd44f24a557240f7afe4e070dee448c63c
This commit is contained in:
parent
b6cb4316da
commit
0c0d8f00a9
@ -1,3 +0,0 @@
|
||||
[PLUGIN]
|
||||
# Quantum plugin provider module
|
||||
provider = quantum.plugins.sample.SamplePlugin.FakePlugin
|
@ -18,6 +18,9 @@ bind_port = 9696
|
||||
# extensions are in there you don't need to specify them here
|
||||
api_extensions_path =
|
||||
|
||||
# Quantum plugin provider module
|
||||
core_plugin = quantum.plugins.sample.SamplePlugin.FakePlugin
|
||||
|
||||
[composite:quantum]
|
||||
use = egg:Paste#urlmap
|
||||
/: quantumversions
|
||||
|
@ -5,12 +5,12 @@
|
||||
sql_connection = sqlite://
|
||||
|
||||
[OVS]
|
||||
integration-bridge = br-int
|
||||
integration_bridge = br-int
|
||||
|
||||
# openflow-controller = <host IP address of ofp controller>:<port: 6633>
|
||||
# openflow-rest-api = <host IP address of ofp rest api service>:<port: 8080>
|
||||
openflow-controller = 127.0.0.1:6633
|
||||
openflow-rest-api = 127.0.0.1:8080
|
||||
# openflow_controller = <host IP address of ofp controller>:<port: 6633>
|
||||
# openflow_rest_api = <host IP address of ofp rest api service>:<port: 8080>
|
||||
openflow_controller = 127.0.0.1:6633
|
||||
openflow_rest_api = 127.0.0.1:8080
|
||||
|
||||
[AGENT]
|
||||
# Change to "sudo quantum-rootwrap" to limit commands that can be run
|
||||
|
@ -42,23 +42,23 @@ class APIRouter(wsgi.Router):
|
||||
"""
|
||||
_version = None
|
||||
|
||||
def __init__(self, options=None):
|
||||
def __init__(self):
|
||||
mapper = self._mapper()
|
||||
self._setup_routes(mapper, options)
|
||||
self._setup_routes(mapper)
|
||||
super(APIRouter, self).__init__(mapper)
|
||||
|
||||
def _mapper(self):
|
||||
return routes.Mapper()
|
||||
|
||||
def _setup_routes(self, mapper, options):
|
||||
self._setup_base_routes(mapper, options, self._version)
|
||||
def _setup_routes(self, mapper):
|
||||
self._setup_base_routes(mapper, self._version)
|
||||
|
||||
def _setup_base_routes(self, mapper, options, version):
|
||||
def _setup_base_routes(self, mapper, version):
|
||||
"""Routes common to all versions."""
|
||||
# Loads the quantum plugin
|
||||
# Note(salvatore-orlando): Should the plugin be versioned
|
||||
# I don't think so
|
||||
plugin = manager.QuantumManager.get_plugin(options)
|
||||
plugin = manager.QuantumManager.get_plugin()
|
||||
|
||||
uri_prefix = '/tenants/{tenant_id}/'
|
||||
attachment_path = (
|
||||
|
@ -293,7 +293,7 @@ class Controller(object):
|
||||
return body
|
||||
|
||||
|
||||
def create_resource(collection, resource, plugin, conf, params):
|
||||
def create_resource(collection, resource, plugin, params):
|
||||
controller = Controller(plugin, collection, resource, params)
|
||||
|
||||
# NOTE(jkoelker) To anyone wishing to add "proper" xml support
|
||||
|
@ -24,6 +24,7 @@ import webob.exc
|
||||
from quantum import manager
|
||||
from quantum import wsgi
|
||||
from quantum.api.v2 import base
|
||||
from quantum.openstack.common import cfg
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -118,16 +119,14 @@ class APIRouter(wsgi.Router):
|
||||
|
||||
@classmethod
|
||||
def factory(cls, global_config, **local_config):
|
||||
return cls(global_config, **local_config)
|
||||
return cls(**local_config)
|
||||
|
||||
def __init__(self, conf, **local_config):
|
||||
def __init__(self, **local_config):
|
||||
mapper = routes_mapper.Mapper()
|
||||
plugin_provider = manager.get_plugin_provider(conf)
|
||||
plugin_provider = cfg.CONF.core_plugin
|
||||
LOG.debug("Plugin location:%s", plugin_provider)
|
||||
plugin = manager.get_plugin(plugin_provider)
|
||||
|
||||
# NOTE(jkoelker) Merge local_conf into conf after the plugin
|
||||
# is discovered
|
||||
conf.update(local_config)
|
||||
col_kwargs = dict(collection_actions=COLLECTION_ACTIONS,
|
||||
member_actions=MEMBER_ACTIONS)
|
||||
|
||||
@ -137,8 +136,7 @@ class APIRouter(wsgi.Router):
|
||||
|
||||
def _map_resource(collection, resource, params):
|
||||
controller = base.create_resource(collection, resource,
|
||||
plugin, conf,
|
||||
params)
|
||||
plugin, params)
|
||||
mapper_kwargs = dict(controller=controller,
|
||||
requirements=REQUIREMENTS,
|
||||
**col_kwargs)
|
||||
|
@ -20,325 +20,100 @@ Routines for configuring Quantum
|
||||
"""
|
||||
|
||||
import logging
|
||||
import logging.config
|
||||
import logging.handlers
|
||||
import optparse
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
|
||||
from paste import deploy
|
||||
|
||||
from quantum.common import flags
|
||||
from quantum.openstack.common import cfg
|
||||
from quantum.version import version_string
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s"
|
||||
DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
bind_opts = [
|
||||
cfg.StrOpt('bind_host', default='0.0.0.0'),
|
||||
cfg.IntOpt('bind_port', default=9696),
|
||||
cfg.StrOpt('api_extensions_path', default=""),
|
||||
cfg.StrOpt('core_plugin',
|
||||
default='quantum.plugins.sample.SamplePlugin.FakePlugin'),
|
||||
]
|
||||
|
||||
# Register the configuration options
|
||||
cfg.CONF.register_opts(bind_opts)
|
||||
|
||||
|
||||
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 parse(args):
|
||||
cfg.CONF(args=args, project='quantum',
|
||||
version='%%prog %s' % version_string())
|
||||
|
||||
|
||||
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 "\
|
||||
"all quantum 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('--use-syslog', default=False,
|
||||
action="store_true",
|
||||
help="Output logs to syslog.")
|
||||
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)")
|
||||
parser.add_option_group(group)
|
||||
|
||||
|
||||
def setup_logging(options, conf):
|
||||
def setup_logging(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
|
||||
:param conf: a cfg.ConfOpts object
|
||||
"""
|
||||
|
||||
if options.get('log_config', None):
|
||||
if conf.log_config:
|
||||
# Use a logging configuration file for all settings...
|
||||
if os.path.exists(options['log_config']):
|
||||
logging.config.fileConfig(options['log_config'])
|
||||
if os.path.exists(conf.log_config):
|
||||
logging.config.fileConfig(conf.log_config)
|
||||
return
|
||||
else:
|
||||
raise RuntimeError("Unable to locate specified logging "
|
||||
"config file: %s" % options['log_config'])
|
||||
"config file: %s" % conf.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:
|
||||
if conf.debug:
|
||||
root_logger.setLevel(logging.DEBUG)
|
||||
elif verbose:
|
||||
elif conf.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)
|
||||
formatter = logging.Formatter(conf.log_format, conf.log_date_format)
|
||||
|
||||
syslog = options.get('use_syslog')
|
||||
if not syslog:
|
||||
syslog = conf.get('use_syslog')
|
||||
|
||||
if syslog:
|
||||
SysLogHandler = logging.handlers.SysLogHandler
|
||||
if conf.use_syslog:
|
||||
try:
|
||||
handler = SysLogHandler(address='/dev/log',
|
||||
facility=SysLogHandler.LOG_SYSLOG)
|
||||
except socket.error:
|
||||
handler = SysLogHandler(address='/var/run/syslog',
|
||||
facility=SysLogHandler.LOG_SYSLOG)
|
||||
handler.setFormatter(formatter)
|
||||
root_logger.addHandler(handler)
|
||||
facility = getattr(logging.handlers.SysLogHandler,
|
||||
conf.syslog_log_facility)
|
||||
except AttributeError:
|
||||
raise ValueError(_("Invalid syslog facility"))
|
||||
|
||||
logfile = options.get('log_file')
|
||||
if not logfile:
|
||||
logfile = conf.get('log_file')
|
||||
|
||||
if logfile:
|
||||
logdir = options.get('log_dir')
|
||||
if not logdir:
|
||||
logdir = conf.get('log_dir')
|
||||
if logdir:
|
||||
logfile = os.path.join(logdir, logfile)
|
||||
logfile = logging.FileHandler(logfile)
|
||||
logfile.setFormatter(formatter)
|
||||
logfile.setFormatter(formatter)
|
||||
root_logger.addHandler(logfile)
|
||||
handler = logging.handlers.SysLogHandler(address='/dev/log',
|
||||
facility=facility)
|
||||
elif conf.log_file:
|
||||
logfile = conf.log_file
|
||||
if conf.log_dir:
|
||||
logfile = os.path.join(conf.log_dir, logfile)
|
||||
handler = logging.handlers.WatchedFileHandler(logfile)
|
||||
else:
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
handler.setFormatter(formatter)
|
||||
root_logger.addHandler(handler)
|
||||
|
||||
handler.setFormatter(formatter)
|
||||
root_logger.addHandler(handler)
|
||||
|
||||
|
||||
def find_config_file(options, args, config_file='quantum.conf'):
|
||||
"""
|
||||
Return the first config file found.
|
||||
|
||||
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 the configuration file in standard directories:
|
||||
* .
|
||||
* ~.quantum/
|
||||
* ~
|
||||
* $FLAGS.state_path/etc/quantum
|
||||
* $FLAGS.state_path/etc
|
||||
|
||||
:retval Full path to config file, or None if no config file found
|
||||
"""
|
||||
|
||||
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])
|
||||
|
||||
dir_to_common = os.path.dirname(os.path.abspath(__file__))
|
||||
root = os.path.join(dir_to_common, '..', '..', '..', '..')
|
||||
# Handle standard directory search for the config file
|
||||
config_file_dirs = [fix_path(os.path.join(os.getcwd(), 'etc')),
|
||||
fix_path(os.path.join('~', '.quantum-venv', 'etc',
|
||||
'quantum')),
|
||||
fix_path('~'),
|
||||
os.path.join(FLAGS.state_path, 'etc'),
|
||||
os.path.join(FLAGS.state_path, 'etc', 'quantum'),
|
||||
fix_path(os.path.join('~', '.local',
|
||||
'etc', 'quantum')),
|
||||
'/usr/etc/quantum',
|
||||
'/usr/local/etc/quantum',
|
||||
'/etc/quantum/',
|
||||
'/etc']
|
||||
|
||||
if 'plugin' in options:
|
||||
config_file_dirs = [os.path.join(x, 'quantum',
|
||||
'plugins', options['plugin'])
|
||||
for x in config_file_dirs]
|
||||
|
||||
if os.path.exists(os.path.join(root, 'plugins')):
|
||||
plugins = [fix_path(os.path.join(root, 'plugins', p, 'etc'))
|
||||
for p in os.listdir(os.path.join(root, 'plugins'))]
|
||||
plugins = [p for p in plugins if os.path.isdir(p)]
|
||||
config_file_dirs.extend(plugins)
|
||||
|
||||
for cfg_dir in config_file_dirs:
|
||||
cfg_file = os.path.join(cfg_dir, config_file)
|
||||
if os.path.exists(cfg_file):
|
||||
return cfg_file
|
||||
|
||||
|
||||
def load_paste_config(app_name, options, args):
|
||||
"""
|
||||
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 quantum.conf in standard directories:
|
||||
* .
|
||||
* ~.quantum/
|
||||
* ~
|
||||
* /etc/quantum
|
||||
* /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(options, args)
|
||||
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):
|
||||
def load_paste_app(app_name, config_file):
|
||||
"""
|
||||
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 quantum.conf in standard directories:
|
||||
* .
|
||||
* ~.quantum/
|
||||
* ~
|
||||
* /etc/quantum
|
||||
* /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:]
|
||||
|
||||
:param config_file: name of the configuration file
|
||||
: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_path = os.path.abspath(cfg.CONF.find_file(config_file))
|
||||
LOG.info("Config paste file: %s", config_path)
|
||||
|
||||
try:
|
||||
app = deploy.loadapp("config:%s" % conf_file, name=app_name)
|
||||
app = deploy.loadapp("config:%s" % config_path, name=app_name)
|
||||
except (LookupError, ImportError):
|
||||
msg = ("Unable to load %(app_name)s from "
|
||||
"configuration file %(conf_file)s.") % locals()
|
||||
"configuration file %(config_path)s.") % locals()
|
||||
LOG.exception(msg)
|
||||
raise RuntimeError(msg)
|
||||
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)
|
||||
return app
|
||||
|
@ -21,7 +21,8 @@
|
||||
"""Utilities and helper functions."""
|
||||
|
||||
|
||||
import ConfigParser
|
||||
import datetime
|
||||
import inspect
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
@ -75,12 +76,6 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True):
|
||||
return result
|
||||
|
||||
|
||||
def get_plugin_from_config(file="config.ini"):
|
||||
Config = ConfigParser.ConfigParser()
|
||||
Config.read(file)
|
||||
return Config.get("PLUGIN", "provider")
|
||||
|
||||
|
||||
class LazyPluggable(object):
|
||||
"""A pluggable backend loaded lazily based on some value."""
|
||||
|
||||
@ -110,3 +105,51 @@ class LazyPluggable(object):
|
||||
def __getattr__(self, key):
|
||||
backend = self.__get_backend()
|
||||
return getattr(backend, key)
|
||||
|
||||
|
||||
def find_config_file(options, config_file):
|
||||
"""
|
||||
Return the first config file found.
|
||||
|
||||
We search for the paste config file in the following order:
|
||||
* If --config-file option is used, use that
|
||||
* Search for the configuration files via common cfg directories
|
||||
:retval Full path to config file, or None if no config file found
|
||||
"""
|
||||
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'])
|
||||
|
||||
dir_to_common = os.path.dirname(os.path.abspath(__file__))
|
||||
root = os.path.join(dir_to_common, '..', '..', '..', '..')
|
||||
# Handle standard directory search for the config file
|
||||
config_file_dirs = [fix_path(os.path.join(os.getcwd(), 'etc')),
|
||||
fix_path(os.path.join('~', '.quantum-venv', 'etc',
|
||||
'quantum')),
|
||||
fix_path('~'),
|
||||
os.path.join(FLAGS.state_path, 'etc'),
|
||||
os.path.join(FLAGS.state_path, 'etc', 'quantum'),
|
||||
fix_path(os.path.join('~', '.local',
|
||||
'etc', 'quantum')),
|
||||
'/usr/etc/quantum',
|
||||
'/usr/local/etc/quantum',
|
||||
'/etc/quantum/',
|
||||
'/etc']
|
||||
|
||||
if 'plugin' in options:
|
||||
config_file_dirs = [
|
||||
os.path.join(x, 'quantum', 'plugins', options['plugin'])
|
||||
for x in config_file_dirs
|
||||
]
|
||||
|
||||
if os.path.exists(os.path.join(root, 'plugins')):
|
||||
plugins = [fix_path(os.path.join(root, 'plugins', p, 'etc'))
|
||||
for p in os.listdir(os.path.join(root, 'plugins'))]
|
||||
plugins = [p for p in plugins if os.path.isdir(p)]
|
||||
config_file_dirs.extend(plugins)
|
||||
|
||||
for cfg_dir in config_file_dirs:
|
||||
cfg_file = os.path.join(cfg_dir, config_file)
|
||||
if os.path.exists(cfg_file):
|
||||
return cfg_file
|
||||
|
@ -29,6 +29,7 @@ import webob.exc
|
||||
from quantum.common import exceptions
|
||||
import quantum.extensions
|
||||
from quantum.manager import QuantumManager
|
||||
from quantum.openstack.common import cfg
|
||||
from quantum import wsgi
|
||||
|
||||
|
||||
@ -217,12 +218,12 @@ class ExtensionController(wsgi.Controller):
|
||||
|
||||
class ExtensionMiddleware(wsgi.Middleware):
|
||||
"""Extensions middleware for WSGI."""
|
||||
def __init__(self, application, config_params,
|
||||
def __init__(self, application,
|
||||
ext_mgr=None):
|
||||
|
||||
self.ext_mgr = (ext_mgr
|
||||
or ExtensionManager(
|
||||
get_extensions_path(config_params)))
|
||||
get_extensions_path()))
|
||||
mapper = routes.Mapper()
|
||||
|
||||
# extended resources
|
||||
@ -339,10 +340,10 @@ class ExtensionMiddleware(wsgi.Middleware):
|
||||
def plugin_aware_extension_middleware_factory(global_config, **local_config):
|
||||
"""Paste factory."""
|
||||
def _factory(app):
|
||||
extensions_path = get_extensions_path(global_config)
|
||||
extensions_path = get_extensions_path()
|
||||
ext_mgr = PluginAwareExtensionManager(extensions_path,
|
||||
QuantumManager.get_plugin())
|
||||
return ExtensionMiddleware(app, global_config, ext_mgr=ext_mgr)
|
||||
return ExtensionMiddleware(app, ext_mgr=ext_mgr)
|
||||
return _factory
|
||||
|
||||
|
||||
@ -539,9 +540,9 @@ class ResourceExtension(object):
|
||||
|
||||
# Returns the extention paths from a config entry and the __path__
|
||||
# of quantum.extensions
|
||||
def get_extensions_path(config=None):
|
||||
def get_extensions_path():
|
||||
paths = ':'.join(quantum.extensions.__path__)
|
||||
if config:
|
||||
paths = ':'.join([config.get('api_extensions_path', ''), paths])
|
||||
if cfg.CONF.api_extensions_path:
|
||||
paths = ':'.join([cfg.CONF.api_extensions_path, paths])
|
||||
|
||||
return paths
|
||||
|
@ -24,27 +24,15 @@ The caller should make sure that QuantumManager is a singleton.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from quantum.common import utils
|
||||
from quantum.common.config import find_config_file
|
||||
from quantum.common.exceptions import ClassNotFound
|
||||
from quantum.openstack.common import cfg
|
||||
from quantum.openstack.common import importutils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
CONFIG_FILE = "plugins.ini"
|
||||
|
||||
|
||||
def find_config(basepath):
|
||||
for root, dirs, files in os.walk(basepath):
|
||||
if CONFIG_FILE in files:
|
||||
return os.path.join(root, CONFIG_FILE)
|
||||
return None
|
||||
|
||||
|
||||
def get_plugin(plugin_provider):
|
||||
# If the plugin can't be found let them know gracefully
|
||||
try:
|
||||
@ -58,16 +46,6 @@ def get_plugin(plugin_provider):
|
||||
return plugin_klass()
|
||||
|
||||
|
||||
def get_plugin_provider(options, config_file=None):
|
||||
if config_file:
|
||||
config_file = [config_file]
|
||||
|
||||
if not 'plugin_provider' in options:
|
||||
cf = find_config_file(options, config_file, CONFIG_FILE)
|
||||
options['plugin_provider'] = utils.get_plugin_from_config(cf)
|
||||
return options['plugin_provider']
|
||||
|
||||
|
||||
class QuantumManager(object):
|
||||
|
||||
_instance = None
|
||||
@ -81,12 +59,12 @@ class QuantumManager(object):
|
||||
# breaks tach monitoring. It has been removed
|
||||
# intentianally to allow v2 plugins to be monitored
|
||||
# for performance metrics.
|
||||
plugin_provider = get_plugin_provider(options, config_file)
|
||||
plugin_provider = cfg.CONF.core_plugin
|
||||
LOG.debug("Plugin location:%s", plugin_provider)
|
||||
self.plugin = get_plugin(plugin_provider)
|
||||
|
||||
@classmethod
|
||||
def get_plugin(cls, options=None, config_file=None):
|
||||
def get_plugin(cls):
|
||||
if cls._instance is None:
|
||||
cls._instance = cls(options, config_file)
|
||||
cls._instance = cls()
|
||||
return cls._instance.plugin
|
||||
|
@ -95,7 +95,7 @@ and --config-dir::
|
||||
|
||||
class ConfigOpts(object):
|
||||
|
||||
def __init__(self, ...):
|
||||
def __call__(self, ...):
|
||||
|
||||
opts = [
|
||||
MultiStrOpt('config-file',
|
||||
@ -233,6 +233,22 @@ log files:
|
||||
...
|
||||
]
|
||||
|
||||
This module also contains a global instance of the CommonConfigOpts class
|
||||
in order to support a common usage pattern in OpenStack:
|
||||
|
||||
from openstack.common import cfg
|
||||
|
||||
opts = [
|
||||
cfg.StrOpt('bind_host' default='0.0.0.0'),
|
||||
cfg.IntOpt('bind_port', default=9292),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(opts)
|
||||
|
||||
def start(server, app):
|
||||
server.start(app, CONF.bind_port, CONF.bind_host)
|
||||
|
||||
"""
|
||||
|
||||
import collections
|
||||
@ -478,7 +494,8 @@ class Opt(object):
|
||||
multi = False
|
||||
|
||||
def __init__(self, name, dest=None, short=None, default=None,
|
||||
metavar=None, help=None, secret=False, required=False):
|
||||
metavar=None, help=None, secret=False, required=False,
|
||||
deprecated_name=None):
|
||||
"""Construct an Opt object.
|
||||
|
||||
The only required parameter is the option's name. However, it is
|
||||
@ -492,6 +509,7 @@ class Opt(object):
|
||||
:param help: an explanation of how the option is used
|
||||
:param secret: true iff the value should be obfuscated in log output
|
||||
:param required: true iff a value must be supplied for this option
|
||||
:param deprecated_name: deprecated name option. Acts like an alias
|
||||
"""
|
||||
self.name = name
|
||||
if dest is None:
|
||||
@ -504,6 +522,10 @@ class Opt(object):
|
||||
self.help = help
|
||||
self.secret = secret
|
||||
self.required = required
|
||||
if deprecated_name is not None:
|
||||
self.deprecated_name = deprecated_name.replace('-', '_')
|
||||
else:
|
||||
self.deprecated_name = None
|
||||
|
||||
def _get_from_config_parser(self, cparser, section):
|
||||
"""Retrieves the option value from a MultiConfigParser object.
|
||||
@ -515,7 +537,13 @@ class Opt(object):
|
||||
:param cparser: a ConfigParser object
|
||||
:param section: a section name
|
||||
"""
|
||||
return cparser.get(section, self.dest)
|
||||
return self._cparser_get_with_deprecated(cparser, section)
|
||||
|
||||
def _cparser_get_with_deprecated(self, cparser, section):
|
||||
"""If cannot find option as dest try deprecated_name alias."""
|
||||
if self.deprecated_name is not None:
|
||||
return cparser.get(section, [self.dest, self.deprecated_name])
|
||||
return cparser.get(section, [self.dest])
|
||||
|
||||
def _add_to_cli(self, parser, group=None):
|
||||
"""Makes the option available in the command line interface.
|
||||
@ -530,9 +558,11 @@ class Opt(object):
|
||||
container = self._get_optparse_container(parser, group)
|
||||
kwargs = self._get_optparse_kwargs(group)
|
||||
prefix = self._get_optparse_prefix('', group)
|
||||
self._add_to_optparse(container, self.name, self.short, kwargs, prefix)
|
||||
self._add_to_optparse(container, self.name, self.short, kwargs, prefix,
|
||||
self.deprecated_name)
|
||||
|
||||
def _add_to_optparse(self, container, name, short, kwargs, prefix=''):
|
||||
def _add_to_optparse(self, container, name, short, kwargs, prefix='',
|
||||
deprecated_name=None):
|
||||
"""Add an option to an optparse parser or group.
|
||||
|
||||
:param container: an optparse.OptionContainer object
|
||||
@ -545,6 +575,8 @@ class Opt(object):
|
||||
args = ['--' + prefix + name]
|
||||
if short:
|
||||
args += ['-' + short]
|
||||
if deprecated_name:
|
||||
args += ['--' + prefix + deprecated_name]
|
||||
for a in args:
|
||||
if container.has_option(a):
|
||||
raise DuplicateOptError(a)
|
||||
@ -577,7 +609,7 @@ class Opt(object):
|
||||
dest = group.name + '_' + dest
|
||||
kwargs.update({'dest': dest,
|
||||
'metavar': self.metavar,
|
||||
'help': self.help})
|
||||
'help': self.help, })
|
||||
return kwargs
|
||||
|
||||
def _get_optparse_prefix(self, prefix, group):
|
||||
@ -627,7 +659,8 @@ class BoolOpt(Opt):
|
||||
|
||||
return value
|
||||
|
||||
return [convert_bool(v) for v in cparser.get(section, self.dest)]
|
||||
return [convert_bool(v) for v in
|
||||
self._cparser_get_with_deprecated(cparser, section)]
|
||||
|
||||
def _add_to_cli(self, parser, group=None):
|
||||
"""Extends the base class method to add the --nooptname option."""
|
||||
@ -640,7 +673,8 @@ class BoolOpt(Opt):
|
||||
kwargs = self._get_optparse_kwargs(group, action='store_false')
|
||||
prefix = self._get_optparse_prefix('no', group)
|
||||
kwargs["help"] = "The inverse of --" + self.name
|
||||
self._add_to_optparse(container, self.name, None, kwargs, prefix)
|
||||
self._add_to_optparse(container, self.name, None, kwargs, prefix,
|
||||
self.deprecated_name)
|
||||
|
||||
def _get_optparse_kwargs(self, group, action='store_true', **kwargs):
|
||||
"""Extends the base optparse keyword dict for boolean options."""
|
||||
@ -654,7 +688,8 @@ class IntOpt(Opt):
|
||||
|
||||
def _get_from_config_parser(self, cparser, section):
|
||||
"""Retrieve the opt value as a integer from ConfigParser."""
|
||||
return [int(v) for v in cparser.get(section, self.dest)]
|
||||
return [int(v) for v in self._cparser_get_with_deprecated(cparser,
|
||||
section)]
|
||||
|
||||
def _get_optparse_kwargs(self, group, **kwargs):
|
||||
"""Extends the base optparse keyword dict for integer options."""
|
||||
@ -668,7 +703,8 @@ class FloatOpt(Opt):
|
||||
|
||||
def _get_from_config_parser(self, cparser, section):
|
||||
"""Retrieve the opt value as a float from ConfigParser."""
|
||||
return [float(v) for v in cparser.get(section, self.dest)]
|
||||
return [float(v) for v in
|
||||
self._cparser_get_with_deprecated(cparser, section)]
|
||||
|
||||
def _get_optparse_kwargs(self, group, **kwargs):
|
||||
"""Extends the base optparse keyword dict for float options."""
|
||||
@ -685,7 +721,8 @@ class ListOpt(Opt):
|
||||
|
||||
def _get_from_config_parser(self, cparser, section):
|
||||
"""Retrieve the opt value as a list from ConfigParser."""
|
||||
return [v.split(',') for v in cparser.get(section, self.dest)]
|
||||
return [v.split(',') for v in
|
||||
self._cparser_get_with_deprecated(cparser, section)]
|
||||
|
||||
def _get_optparse_kwargs(self, group, **kwargs):
|
||||
"""Extends the base optparse keyword dict for list options."""
|
||||
@ -714,6 +751,13 @@ class MultiStrOpt(Opt):
|
||||
return super(MultiStrOpt,
|
||||
self)._get_optparse_kwargs(group, action='append')
|
||||
|
||||
def _cparser_get_with_deprecated(self, cparser, section):
|
||||
"""If cannot find option as dest try deprecated_name alias."""
|
||||
if self.deprecated_name is not None:
|
||||
return cparser.get(section, [self.dest, self.deprecated_name],
|
||||
multi=True)
|
||||
return cparser.get(section, [self.dest], multi=True)
|
||||
|
||||
|
||||
class OptGroup(object):
|
||||
|
||||
@ -766,6 +810,14 @@ class OptGroup(object):
|
||||
|
||||
return True
|
||||
|
||||
def _unregister_opt(self, opt):
|
||||
"""Remove an opt from this group.
|
||||
|
||||
:param opt: an Opt object
|
||||
"""
|
||||
if opt.dest in self._opts:
|
||||
del self._opts[opt.dest]
|
||||
|
||||
def _get_optparse_group(self, parser):
|
||||
"""Build an optparse.OptionGroup for this group."""
|
||||
if self._optparse_group is None:
|
||||
@ -773,6 +825,10 @@ class OptGroup(object):
|
||||
self.help)
|
||||
return self._optparse_group
|
||||
|
||||
def _clear(self):
|
||||
"""Clear this group's option parsing state."""
|
||||
self._optparse_group = None
|
||||
|
||||
|
||||
class ParseError(iniparser.ParseError):
|
||||
def __init__(self, msg, lineno, line, filename):
|
||||
@ -816,25 +872,38 @@ class ConfigParser(iniparser.BaseParser):
|
||||
|
||||
class MultiConfigParser(object):
|
||||
def __init__(self):
|
||||
self.sections = {}
|
||||
self.parsed = []
|
||||
|
||||
def read(self, config_files):
|
||||
read_ok = []
|
||||
|
||||
for filename in config_files:
|
||||
parser = ConfigParser(filename, self.sections)
|
||||
sections = {}
|
||||
parser = ConfigParser(filename, sections)
|
||||
|
||||
try:
|
||||
parser.parse()
|
||||
except IOError:
|
||||
continue
|
||||
|
||||
self.parsed.insert(0, sections)
|
||||
read_ok.append(filename)
|
||||
|
||||
return read_ok
|
||||
|
||||
def get(self, section, name):
|
||||
return self.sections[section][name]
|
||||
def get(self, section, names, multi=False):
|
||||
rvalue = []
|
||||
for sections in self.parsed:
|
||||
if section not in sections:
|
||||
continue
|
||||
for name in names:
|
||||
if name in sections[section]:
|
||||
if multi:
|
||||
rvalue = sections[section][name] + rvalue
|
||||
else:
|
||||
return sections[section][name]
|
||||
if multi and rvalue != []:
|
||||
return rvalue
|
||||
raise KeyError
|
||||
|
||||
|
||||
class ConfigOpts(collections.Mapping):
|
||||
@ -847,57 +916,41 @@ class ConfigOpts(collections.Mapping):
|
||||
the values of options.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
project=None,
|
||||
prog=None,
|
||||
version=None,
|
||||
usage=None,
|
||||
default_config_files=None):
|
||||
"""Construct a ConfigOpts object.
|
||||
def __init__(self):
|
||||
"""Construct a ConfigOpts object."""
|
||||
self._opts = {} # dict of dicts of (opt:, override:, default:)
|
||||
self._groups = {}
|
||||
|
||||
Automatically registers the --config-file option with either a supplied
|
||||
list of default config files, or a list from find_config_files().
|
||||
self._args = None
|
||||
self._oparser = None
|
||||
self._cparser = None
|
||||
self._cli_values = {}
|
||||
self.__cache = {}
|
||||
self._config_opts = []
|
||||
self._disable_interspersed_args = False
|
||||
|
||||
:param project: the toplevel project name, used to locate config files
|
||||
:param prog: the name of the program (defaults to sys.argv[0] basename)
|
||||
:param version: the program version (for --version)
|
||||
:param usage: a usage string (%prog will be expanded)
|
||||
:param default_config_files: config files to use by default
|
||||
"""
|
||||
def _setup(self, project, prog, version, usage, default_config_files):
|
||||
"""Initialize a ConfigOpts object for option parsing."""
|
||||
if prog is None:
|
||||
prog = os.path.basename(sys.argv[0])
|
||||
|
||||
if default_config_files is None:
|
||||
default_config_files = find_config_files(project, prog)
|
||||
|
||||
self.project = project
|
||||
self.prog = prog
|
||||
self.version = version
|
||||
self.usage = usage
|
||||
self.default_config_files = default_config_files
|
||||
self._oparser = optparse.OptionParser(prog=prog,
|
||||
version=version,
|
||||
usage=usage)
|
||||
if self._disable_interspersed_args:
|
||||
self._oparser.disable_interspersed_args()
|
||||
|
||||
self._opts = {} # dict of dicts of (opt:, override:, default:)
|
||||
self._groups = {}
|
||||
|
||||
self._args = None
|
||||
self._cli_values = {}
|
||||
|
||||
self._oparser = optparse.OptionParser(prog=self.prog,
|
||||
version=self.version,
|
||||
usage=self.usage)
|
||||
self._cparser = None
|
||||
|
||||
self.__cache = {}
|
||||
|
||||
opts = [
|
||||
self._config_opts = [
|
||||
MultiStrOpt('config-file',
|
||||
default=self.default_config_files,
|
||||
default=default_config_files,
|
||||
metavar='PATH',
|
||||
help='Path to a config file to use. Multiple config '
|
||||
'files can be specified, with values in later '
|
||||
'files taking precedence. The default files '
|
||||
' used are: %s' %
|
||||
(self.default_config_files)),
|
||||
' used are: %s' % (default_config_files, )),
|
||||
StrOpt('config-dir',
|
||||
metavar='DIR',
|
||||
help='Path to a config directory to pull *.conf '
|
||||
@ -908,7 +961,13 @@ class ConfigOpts(collections.Mapping):
|
||||
'hence over-ridden options in the directory take '
|
||||
'precedence.'),
|
||||
]
|
||||
self.register_cli_opts(opts)
|
||||
self.register_cli_opts(self._config_opts)
|
||||
|
||||
self.project = project
|
||||
self.prog = prog
|
||||
self.version = version
|
||||
self.usage = usage
|
||||
self.default_config_files = default_config_files
|
||||
|
||||
def __clear_cache(f):
|
||||
@functools.wraps(f)
|
||||
@ -919,7 +978,13 @@ class ConfigOpts(collections.Mapping):
|
||||
|
||||
return __inner
|
||||
|
||||
def __call__(self, args=None):
|
||||
def __call__(self,
|
||||
args=None,
|
||||
project=None,
|
||||
prog=None,
|
||||
version=None,
|
||||
usage=None,
|
||||
default_config_files=None):
|
||||
"""Parse command line arguments and config files.
|
||||
|
||||
Calling a ConfigOpts object causes the supplied command line arguments
|
||||
@ -929,35 +994,34 @@ class ConfigOpts(collections.Mapping):
|
||||
The object may be called multiple times, each time causing the previous
|
||||
set of values to be overwritten.
|
||||
|
||||
Automatically registers the --config-file option with either a supplied
|
||||
list of default config files, or a list from find_config_files().
|
||||
|
||||
If the --config-dir option is set, any *.conf files from this
|
||||
directory are pulled in, after all the file(s) specified by the
|
||||
--config-file option.
|
||||
|
||||
:params args: command line arguments (defaults to sys.argv[1:])
|
||||
:param args: command line arguments (defaults to sys.argv[1:])
|
||||
:param project: the toplevel project name, used to locate config files
|
||||
:param prog: the name of the program (defaults to sys.argv[0] basename)
|
||||
:param version: the program version (for --version)
|
||||
:param usage: a usage string (%prog will be expanded)
|
||||
:param default_config_files: config files to use by default
|
||||
:returns: the list of arguments left over after parsing options
|
||||
:raises: SystemExit, ConfigFilesNotFoundError, ConfigFileParseError,
|
||||
RequiredOptError
|
||||
RequiredOptError, DuplicateOptError
|
||||
"""
|
||||
self.clear()
|
||||
|
||||
self._args = args
|
||||
self._setup(project, prog, version, usage, default_config_files)
|
||||
|
||||
(values, args) = self._oparser.parse_args(self._args)
|
||||
self._cli_values, leftovers = self._parse_cli_opts(args)
|
||||
|
||||
self._cli_values = vars(values)
|
||||
|
||||
def _list_config_dir():
|
||||
return sorted(glob.glob(os.path.join(self.config_dir, '*.conf')))
|
||||
|
||||
from_file = list(self.config_file)
|
||||
|
||||
from_dir = _list_config_dir() if self.config_dir else []
|
||||
|
||||
self._parse_config_files(from_file + from_dir)
|
||||
self._parse_config_files()
|
||||
|
||||
self._check_required_opts()
|
||||
|
||||
return args
|
||||
return leftovers
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Look up an option value and perform string substitution.
|
||||
@ -994,8 +1058,12 @@ class ConfigOpts(collections.Mapping):
|
||||
def clear(self):
|
||||
"""Clear the state of the object to before it was called."""
|
||||
self._args = None
|
||||
self._cli_values = {}
|
||||
self._cli_values.clear()
|
||||
self._oparser = None
|
||||
self._cparser = None
|
||||
self.unregister_opts(self._config_opts)
|
||||
for group in self._groups.values():
|
||||
group._clear()
|
||||
|
||||
@__clear_cache
|
||||
def register_opt(self, opt, group=None):
|
||||
@ -1042,15 +1110,7 @@ class ConfigOpts(collections.Mapping):
|
||||
if self._args is not None:
|
||||
raise ArgsAlreadyParsedError("cannot register CLI option")
|
||||
|
||||
if not self.register_opt(opt, group, clear_cache=False):
|
||||
return False
|
||||
|
||||
if group is not None:
|
||||
group = self._get_group(group, autocreate=True)
|
||||
|
||||
opt._add_to_cli(self._oparser, group)
|
||||
|
||||
return True
|
||||
return self.register_opt(opt, group, clear_cache=False)
|
||||
|
||||
@__clear_cache
|
||||
def register_cli_opts(self, opts, group=None):
|
||||
@ -1071,6 +1131,28 @@ class ConfigOpts(collections.Mapping):
|
||||
|
||||
self._groups[group.name] = copy.copy(group)
|
||||
|
||||
@__clear_cache
|
||||
def unregister_opt(self, opt, group=None):
|
||||
"""Unregister an option.
|
||||
|
||||
:param opt: an Opt object
|
||||
:param group: an optional OptGroup object or group name
|
||||
:raises: ArgsAlreadyParsedError, NoSuchGroupError
|
||||
"""
|
||||
if self._args is not None:
|
||||
raise ArgsAlreadyParsedError("reset before unregistering options")
|
||||
|
||||
if group is not None:
|
||||
self._get_group(group)._unregister_opt(opt)
|
||||
elif opt.dest in self._opts:
|
||||
del self._opts[opt.dest]
|
||||
|
||||
@__clear_cache
|
||||
def unregister_opts(self, opts, group=None):
|
||||
"""Unregister multiple CLI option schemas at once."""
|
||||
for opt in opts:
|
||||
self.unregister_opt(opt, group, clear_cache=False)
|
||||
|
||||
@__clear_cache
|
||||
def set_override(self, name, override, group=None):
|
||||
"""Override an opt value.
|
||||
@ -1101,16 +1183,24 @@ class ConfigOpts(collections.Mapping):
|
||||
opt_info = self._get_opt_info(name, group)
|
||||
opt_info['default'] = default
|
||||
|
||||
def _all_opt_infos(self):
|
||||
"""A generator function for iteration opt infos."""
|
||||
for info in self._opts.values():
|
||||
yield info, None
|
||||
for group in self._groups.values():
|
||||
for info in group._opts.values():
|
||||
yield info, group
|
||||
|
||||
def _all_opts(self):
|
||||
"""A generator function for iteration opts."""
|
||||
for info, group in self._all_opt_infos():
|
||||
yield info['opt'], group
|
||||
|
||||
def _unset_defaults_and_overrides(self):
|
||||
"""Unset any default or override on all options."""
|
||||
def unset(opts):
|
||||
for info in opts.values():
|
||||
info['default'] = None
|
||||
info['override'] = None
|
||||
|
||||
unset(self._opts)
|
||||
for group in self._groups.values():
|
||||
unset(group._opts)
|
||||
for info, group in self._all_opt_infos():
|
||||
info['default'] = None
|
||||
info['override'] = None
|
||||
|
||||
def disable_interspersed_args(self):
|
||||
"""Set parsing to stop on the first non-option.
|
||||
@ -1129,13 +1219,13 @@ class ConfigOpts(collections.Mapping):
|
||||
|
||||
i.e. argument parsing is stopped at the first non-option argument.
|
||||
"""
|
||||
self._oparser.disable_interspersed_args()
|
||||
self._disable_interspersed_args = True
|
||||
|
||||
def enable_interspersed_args(self):
|
||||
"""Set parsing to not stop on the first non-option.
|
||||
|
||||
This it the default behaviour."""
|
||||
self._oparser.enable_interspersed_args()
|
||||
self._disable_interspersed_args = False
|
||||
|
||||
def find_file(self, name):
|
||||
"""Locate a file located alongside the config files.
|
||||
@ -1272,7 +1362,7 @@ class ConfigOpts(collections.Mapping):
|
||||
def _substitute(self, value):
|
||||
"""Perform string template substitution.
|
||||
|
||||
Substititue any template variables (e.g. $foo, ${bar}) in the supplied
|
||||
Substitute any template variables (e.g. $foo, ${bar}) in the supplied
|
||||
string value(s) with opt values.
|
||||
|
||||
:param value: the string value, or list of string values
|
||||
@ -1329,11 +1419,17 @@ class ConfigOpts(collections.Mapping):
|
||||
|
||||
return opts[opt_name]
|
||||
|
||||
def _parse_config_files(self, config_files):
|
||||
"""Parse the supplied configuration files.
|
||||
def _parse_config_files(self):
|
||||
"""Parse the config files from --config-file and --config-dir.
|
||||
|
||||
:raises: ConfigFilesNotFoundError, ConfigFileParseError
|
||||
"""
|
||||
config_files = list(self.config_file)
|
||||
|
||||
if self.config_dir:
|
||||
config_dir_glob = os.path.join(self.config_dir, '*.conf')
|
||||
config_files += sorted(glob.glob(config_dir_glob))
|
||||
|
||||
self._cparser = MultiConfigParser()
|
||||
|
||||
try:
|
||||
@ -1345,8 +1441,12 @@ class ConfigOpts(collections.Mapping):
|
||||
not_read_ok = filter(lambda f: f not in read_ok, config_files)
|
||||
raise ConfigFilesNotFoundError(not_read_ok)
|
||||
|
||||
def _do_check_required_opts(self, opts, group=None):
|
||||
for info in opts.values():
|
||||
def _check_required_opts(self):
|
||||
"""Check that all opts marked as required have values specified.
|
||||
|
||||
:raises: RequiredOptError
|
||||
"""
|
||||
for info, group in self._all_opt_infos():
|
||||
default, opt, override = [info[k] for k in sorted(info.keys())]
|
||||
|
||||
if opt.required:
|
||||
@ -1356,15 +1456,25 @@ class ConfigOpts(collections.Mapping):
|
||||
if self._get(opt.name, group) is None:
|
||||
raise RequiredOptError(opt.name, group)
|
||||
|
||||
def _check_required_opts(self):
|
||||
"""Check that all opts marked as required have values specified.
|
||||
def _parse_cli_opts(self, args):
|
||||
"""Parse command line options.
|
||||
|
||||
Initializes the command line option parser and parses the supplied
|
||||
command line arguments.
|
||||
|
||||
:param args: the command line arguments
|
||||
:returns: a dict of parsed option values
|
||||
:raises: SystemExit, DuplicateOptError
|
||||
|
||||
:raises: RequiredOptError
|
||||
"""
|
||||
self._do_check_required_opts(self._opts)
|
||||
self._args = args
|
||||
|
||||
for group in self._groups.values():
|
||||
self._do_check_required_opts(group._opts, group)
|
||||
for opt, group in self._all_opts():
|
||||
opt._add_to_cli(self._oparser, group)
|
||||
|
||||
values, leftovers = self._oparser.parse_args(args)
|
||||
|
||||
return vars(values), leftovers
|
||||
|
||||
class GroupAttr(collections.Mapping):
|
||||
|
||||
@ -1480,7 +1590,10 @@ class CommonConfigOpts(ConfigOpts):
|
||||
help='syslog facility to receive log lines')
|
||||
]
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(CommonConfigOpts, self).__init__(**kwargs)
|
||||
def __init__(self):
|
||||
super(CommonConfigOpts, self).__init__()
|
||||
self.register_cli_opts(self.common_cli_opts)
|
||||
self.register_cli_opts(self.logging_cli_opts)
|
||||
|
||||
|
||||
CONF = CommonConfigOpts()
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
import logging as LOG
|
||||
|
||||
from quantum.common.config import find_config_file
|
||||
from quantum.common.utils import find_config_file
|
||||
from quantum.plugins.cisco.common import cisco_configparser as confp
|
||||
from quantum.plugins.cisco.common import cisco_constants as const
|
||||
from quantum.plugins.cisco.common import cisco_exceptions as cexc
|
||||
@ -28,7 +28,7 @@ from quantum.plugins.cisco.db import l2network_db as cdb
|
||||
LOG.basicConfig(level=LOG.WARN)
|
||||
LOG.getLogger(const.LOGGER_COMPONENT_NAME)
|
||||
|
||||
CREDENTIALS_FILE = find_config_file({'plugin': 'cisco'}, None,
|
||||
CREDENTIALS_FILE = find_config_file({'plugin': 'cisco'},
|
||||
"credentials.ini")
|
||||
TENANT = const.NETWORK_ADMIN
|
||||
|
||||
|
@ -17,11 +17,11 @@
|
||||
# @author: Sumit Naiksatam, Cisco Systems, Inc.
|
||||
# @author: Rohit Agarwalla, Cisco Systems, Inc.
|
||||
|
||||
from quantum.common.config import find_config_file
|
||||
from quantum.common.utils import find_config_file
|
||||
from quantum.plugins.cisco.common import cisco_configparser as confp
|
||||
|
||||
|
||||
CONF_FILE = find_config_file({'plugin': 'cisco'}, None, "l2network_plugin.ini")
|
||||
CONF_FILE = find_config_file({'plugin': 'cisco'}, "l2network_plugin.ini")
|
||||
CONF_PARSER_OBJ = confp.CiscoConfigParser(CONF_FILE)
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ MAX_NETWORKS = SECTION_CONF['max_networks']
|
||||
SECTION_CONF = CONF_PARSER_OBJ['MODEL']
|
||||
MODEL_CLASS = SECTION_CONF['model_class']
|
||||
|
||||
CONF_FILE = find_config_file({'plugin': 'cisco'}, None, "cisco_plugins.ini")
|
||||
CONF_FILE = find_config_file({'plugin': 'cisco'}, "cisco_plugins.ini")
|
||||
|
||||
SECTION_CONF = CONF_PARSER_OBJ['SEGMENTATION']
|
||||
MANAGER_CLASS = SECTION_CONF['manager_class']
|
||||
@ -55,7 +55,7 @@ CONF_PARSER_OBJ = confp.CiscoConfigParser(CONF_FILE)
|
||||
# Read the config for the device plugins
|
||||
PLUGINS = CONF_PARSER_OBJ.walk(CONF_PARSER_OBJ.dummy)
|
||||
|
||||
CONF_FILE = find_config_file({'plugin': 'cisco'}, None, "db_conn.ini")
|
||||
CONF_FILE = find_config_file({'plugin': 'cisco'}, "db_conn.ini")
|
||||
|
||||
CONF_PARSER_OBJ = confp.CiscoConfigParser(CONF_FILE)
|
||||
|
||||
|
@ -23,11 +23,11 @@ This module will export the configuration parameters
|
||||
from the nexus.ini file
|
||||
"""
|
||||
|
||||
from quantum.common.config import find_config_file
|
||||
from quantum.common.utils import find_config_file
|
||||
from quantum.plugins.cisco.common import cisco_configparser as confp
|
||||
|
||||
|
||||
CP = confp.CiscoConfigParser(find_config_file({'plugin': 'cisco'}, None,
|
||||
CP = confp.CiscoConfigParser(find_config_file({'plugin': 'cisco'},
|
||||
"nexus.ini"))
|
||||
|
||||
SECTION = CP['SWITCH']
|
||||
|
@ -52,7 +52,7 @@ from quantum import wsgi
|
||||
LOG = logging.getLogger('quantum.plugins.cisco.tests.test_cisco_extensions')
|
||||
|
||||
|
||||
TEST_CONF_FILE = config.find_config_file({'plugin': 'cisco'}, None,
|
||||
TEST_CONF_FILE = config.find_config_file({'plugin': 'cisco'},
|
||||
'quantum.conf.ciscoext')
|
||||
EXTENSIONS_PATH = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
|
||||
os.pardir, os.pardir, "extensions")
|
||||
@ -1256,8 +1256,8 @@ def setup_extensions_middleware(extension_manager=None):
|
||||
PluginAwareExtensionManager(EXTENSIONS_PATH,
|
||||
L2Network()))
|
||||
options = {'config_file': TEST_CONF_FILE}
|
||||
conf, app = config.load_paste_app('extensions_test_app', options, None)
|
||||
return ExtensionMiddleware(app, conf, ext_mgr=extension_manager)
|
||||
app = config.load_paste_app('extensions_test_app', options, None)
|
||||
return ExtensionMiddleware(app, ext_mgr=extension_manager)
|
||||
|
||||
|
||||
def setup_extensions_test_app(extension_manager=None):
|
||||
|
@ -17,11 +17,11 @@
|
||||
# @author: Sumit Naiksatam, Cisco Systems, Inc.
|
||||
#
|
||||
|
||||
from quantum.common.config import find_config_file
|
||||
from quantum.common.utils import find_config_file
|
||||
from quantum.plugins.cisco.common import cisco_configparser as confp
|
||||
|
||||
|
||||
CP = confp.CiscoConfigParser(find_config_file({'plugin': 'cisco'}, [],
|
||||
CP = confp.CiscoConfigParser(find_config_file({'plugin': 'cisco'},
|
||||
'ucs.ini'))
|
||||
|
||||
SECTION = CP['UCSM']
|
||||
@ -34,7 +34,7 @@ PROFILE_NAME_PREFIX = SECTION['profile_name_prefix']
|
||||
SECTION = CP['DRIVER']
|
||||
UCSM_DRIVER = SECTION['name']
|
||||
|
||||
CP = confp.CiscoConfigParser(find_config_file({'plugin': 'cisco'}, [],
|
||||
CP = confp.CiscoConfigParser(find_config_file({'plugin': 'cisco'},
|
||||
'ucs_inventory.ini'))
|
||||
|
||||
INVENTORY = CP.walk(CP.dummy)
|
||||
|
@ -17,11 +17,11 @@
|
||||
# @author: Sumit Naiksatam, Cisco Systems, Inc.
|
||||
#
|
||||
|
||||
from quantum.common.config import find_config_file
|
||||
from quantum.common.utils import find_config_file
|
||||
from quantum.plugins.cisco.common import cisco_configparser as confp
|
||||
|
||||
|
||||
CONF_FILE = find_config_file({'plugin': 'cisco'}, None, "ucs_inventory.ini")
|
||||
CONF_FILE = find_config_file({'plugin': 'cisco'}, "ucs_inventory.ini")
|
||||
CP = confp.CiscoConfigParser(CONF_FILE)
|
||||
|
||||
INVENTORY = CP.walk(CP.dummy)
|
||||
|
@ -41,8 +41,8 @@ agent_opts = [
|
||||
|
||||
|
||||
def parse(config_file):
|
||||
conf = cfg.ConfigOpts(default_config_files=[config_file])
|
||||
conf(args=[])
|
||||
conf = cfg.ConfigOpts()
|
||||
conf(args=[], default_config_files=[config_file])
|
||||
conf.register_opts(vlan_opts, "VLANS")
|
||||
conf.register_opts(database_opts, "DATABASE")
|
||||
conf.register_opts(bridge_opts, "LINUX_BRIDGE")
|
||||
|
@ -21,14 +21,14 @@ from sqlalchemy import func
|
||||
from sqlalchemy.orm import exc
|
||||
|
||||
from quantum.common import exceptions as q_exc
|
||||
from quantum.common.config import find_config_file
|
||||
from quantum.common.utils import find_config_file
|
||||
import quantum.db.api as db
|
||||
from quantum.plugins.linuxbridge.common import exceptions as c_exc
|
||||
from quantum.plugins.linuxbridge.db import l2network_models
|
||||
from quantum.plugins.linuxbridge.common import config
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF_FILE = find_config_file({'plugin': 'linuxbridge'}, None,
|
||||
CONF_FILE = find_config_file({'plugin': 'linuxbridge'},
|
||||
"linuxbridge_conf.ini")
|
||||
CONF = config.parse(CONF_FILE)
|
||||
|
||||
|
@ -38,8 +38,8 @@ agent_opts = [
|
||||
|
||||
|
||||
def parse(config_file):
|
||||
conf = cfg.ConfigOpts(default_config_files=[config_file])
|
||||
conf(args=[])
|
||||
conf = cfg.ConfigOpts()
|
||||
conf(args=[], default_config_files=[config_file])
|
||||
conf.register_opts(database_opts, "DATABASE")
|
||||
conf.register_opts(ovs_opts, "OVS")
|
||||
conf.register_opts(agent_opts, "AGENT")
|
||||
|
@ -23,7 +23,7 @@ import os
|
||||
|
||||
from quantum.api.api_common import OperationalStatus
|
||||
from quantum.common import exceptions as q_exc
|
||||
from quantum.common.config import find_config_file
|
||||
from quantum.common.utils import find_config_file
|
||||
import quantum.db.api as db
|
||||
from quantum.plugins.openvswitch import ovs_db
|
||||
from quantum.plugins.openvswitch.common import config
|
||||
@ -33,7 +33,7 @@ LOG = logging.getLogger("ovs_quantum_plugin")
|
||||
|
||||
|
||||
CONF_FILE = find_config_file({"plugin": "openvswitch"},
|
||||
None, "ovs_quantum_plugin.ini")
|
||||
"ovs_quantum_plugin.ini")
|
||||
|
||||
|
||||
# Exception thrown if no more VLANs are available
|
||||
|
@ -20,7 +20,6 @@
|
||||
# under the License.
|
||||
# @author: Isaku Yamahata
|
||||
|
||||
import ConfigParser
|
||||
import logging as LOG
|
||||
from optparse import OptionParser
|
||||
import shlex
|
||||
@ -34,6 +33,7 @@ from ryu.app.client import OFPClient
|
||||
from sqlalchemy.ext.sqlsoup import SqlSoup
|
||||
|
||||
from quantum.agent.linux import utils
|
||||
from quantum.plugins.ryu.common import config
|
||||
|
||||
OP_STATUS_UP = "UP"
|
||||
OP_STATUS_DOWN = "DOWN"
|
||||
@ -286,18 +286,10 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
config_file = args[0]
|
||||
config = ConfigParser.ConfigParser()
|
||||
try:
|
||||
config.read(config_file)
|
||||
except Exception, e:
|
||||
LOG.error("Unable to parse config file \"%s\": %s",
|
||||
config_file, str(e))
|
||||
|
||||
integ_br = config.get("OVS", "integration-bridge")
|
||||
|
||||
root_helper = config.get("AGENT", "root_helper")
|
||||
|
||||
options = {"sql_connection": config.get("DATABASE", "sql_connection")}
|
||||
conf = config.parse(config_file)
|
||||
integ_br = conf.OVS.integration_bridge
|
||||
root_helper = conf.AGENT.root_helper
|
||||
options = {"sql_connection": conf.DATABASE.sql_connection}
|
||||
db = SqlSoup(options["sql_connection"])
|
||||
|
||||
LOG.info("Connecting to database \"%s\" on %s",
|
||||
|
15
quantum/plugins/ryu/common/__init__.py
Normal file
15
quantum/plugins/ryu/common/__init__.py
Normal file
@ -0,0 +1,15 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
43
quantum/plugins/ryu/common/config.py
Normal file
43
quantum/plugins/ryu/common/config.py
Normal file
@ -0,0 +1,43 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from quantum.openstack.common import cfg
|
||||
|
||||
|
||||
database_opts = [
|
||||
cfg.StrOpt('sql_connection', default='sqlite://'),
|
||||
cfg.IntOpt('reconnect_interval', default=2),
|
||||
]
|
||||
|
||||
ovs_opts = [
|
||||
cfg.StrOpt('integration_bridge', default='br-int'),
|
||||
cfg.StrOpt('openflow_controller', default='127.0.0.1:6633'),
|
||||
cfg.StrOpt('openflow_rest_api', default='127.0.0.1:8080'),
|
||||
]
|
||||
|
||||
agent_opts = [
|
||||
cfg.IntOpt('polling_interval', default=2),
|
||||
cfg.StrOpt('root_helper', default='sudo'),
|
||||
]
|
||||
|
||||
|
||||
def parse(config_file):
|
||||
conf = cfg.ConfigOpts()
|
||||
conf(args=[], default_config_files=[config_file])
|
||||
conf.register_opts(database_opts, "DATABASE")
|
||||
conf.register_opts(ovs_opts, "OVS")
|
||||
conf.register_opts(agent_opts, "AGENT")
|
||||
return conf
|
@ -16,15 +16,14 @@
|
||||
# under the License.
|
||||
# @author: Isaku Yamahata
|
||||
|
||||
import ConfigParser
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import logging as LOG
|
||||
import os
|
||||
|
||||
from quantum.api.api_common import OperationalStatus
|
||||
from quantum.common import exceptions as q_exc
|
||||
from quantum.plugins.ryu.common import config
|
||||
import quantum.db.api as db
|
||||
from quantum.manager import find_config
|
||||
from quantum.quantum_plugin_base import QuantumPluginBase
|
||||
|
||||
|
||||
@ -55,24 +54,23 @@ class OVSQuantumPluginBase(QuantumPluginBase):
|
||||
"""
|
||||
def __init__(self, conf_file, mod_file, configfile=None):
|
||||
super(OVSQuantumPluginBase, self).__init__()
|
||||
config = ConfigParser.ConfigParser()
|
||||
if configfile is None:
|
||||
if conf_file and os.path.exists(conf_file):
|
||||
if os.path.exists(conf_file):
|
||||
configfile = conf_file
|
||||
else:
|
||||
configfile = (
|
||||
find_config(os.path.abspath(os.path.dirname(mod_file))))
|
||||
configfile = find_config(os.path.abspath(
|
||||
os.path.dirname(__file__)))
|
||||
if configfile is None:
|
||||
raise Exception("Configuration file \"%s\" doesn't exist" %
|
||||
(configfile))
|
||||
LOG.debug("Using configuration file: %s", configfile)
|
||||
config.read(configfile)
|
||||
LOG.debug("Config: %s", config)
|
||||
|
||||
options = {"sql_connection": config.get("DATABASE", "sql_connection")}
|
||||
LOG.debug("Using configuration file: %s" % configfile)
|
||||
conf = config.parse(configfile)
|
||||
options = {"sql_connection": conf.DATABASE.sql_connection}
|
||||
reconnect_interval = conf.DATABASE.reconnect_interval
|
||||
options.update({"reconnect_interval": reconnect_interval})
|
||||
db.configure_db(options)
|
||||
|
||||
self.config = config
|
||||
self.conf = conf
|
||||
# Subclass must set self.driver to its own OVSQuantumPluginDriverBase
|
||||
self.driver = None
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
from ryu.app import client
|
||||
from ryu.app import rest_nw_id
|
||||
|
||||
from quantum.common.config import find_config_file
|
||||
from quantum.common.utils import find_config_file
|
||||
from quantum.common import exceptions as q_exc
|
||||
import quantum.db.api as db
|
||||
from quantum.plugins.ryu import ofp_service_type
|
||||
@ -27,14 +27,14 @@ from quantum.plugins.ryu import ovs_quantum_plugin_base
|
||||
from quantum.plugins.ryu.db import api as db_api
|
||||
|
||||
|
||||
CONF_FILE = find_config_file({"plugin": "ryu"}, None, "ryu.ini")
|
||||
CONF_FILE = find_config_file({"plugin": "ryu"}, "ryu.ini")
|
||||
|
||||
|
||||
class OFPRyuDriver(ovs_quantum_plugin_base.OVSQuantumPluginDriverBase):
|
||||
def __init__(self, config):
|
||||
def __init__(self, conf):
|
||||
super(OFPRyuDriver, self).__init__()
|
||||
ofp_con_host = config.get("OVS", "openflow-controller")
|
||||
ofp_api_host = config.get("OVS", "openflow-rest-api")
|
||||
ofp_con_host = conf.OVS.openflow_controller
|
||||
ofp_api_host = conf.OVS.openflow_rest_api
|
||||
|
||||
if ofp_con_host is None or ofp_api_host is None:
|
||||
raise q_exc.Invalid("invalid configuration. check ryu.ini")
|
||||
@ -64,4 +64,4 @@ class OFPRyuDriver(ovs_quantum_plugin_base.OVSQuantumPluginDriverBase):
|
||||
class RyuQuantumPlugin(ovs_quantum_plugin_base.OVSQuantumPluginBase):
|
||||
def __init__(self, configfile=None):
|
||||
super(RyuQuantumPlugin, self).__init__(CONF_FILE, __file__, configfile)
|
||||
self.driver = OFPRyuDriver(self.config)
|
||||
self.driver = OFPRyuDriver(self.conf)
|
||||
|
@ -21,6 +21,8 @@ from quantum.plugins.ryu import ovs_quantum_plugin_base
|
||||
class FakePluginDriver(ovs_quantum_plugin_base.OVSQuantumPluginDriverBase):
|
||||
def __init__(self, config):
|
||||
super(FakePluginDriver, self).__init__()
|
||||
conf = config.parse(config)
|
||||
self.conf = conf
|
||||
|
||||
def create_network(self, net):
|
||||
pass
|
||||
@ -32,4 +34,4 @@ class FakePluginDriver(ovs_quantum_plugin_base.OVSQuantumPluginDriverBase):
|
||||
class FakePlugin(ovs_quantum_plugin_base.OVSQuantumPluginBase):
|
||||
def __init__(self, configfile=None):
|
||||
super(FakePlugin, self).__init__(None, __file__, configfile)
|
||||
self.driver = FakePluginDriver(self.config)
|
||||
self.driver = FakePluginDriver(self.conf)
|
||||
|
@ -18,16 +18,21 @@
|
||||
import uuid
|
||||
|
||||
import quantum.db.api as db
|
||||
from quantum.common.utils import find_config_file
|
||||
from quantum.plugins.ryu.common import config
|
||||
from quantum.plugins.ryu.tests.unit.basetest import BaseRyuTest
|
||||
from quantum.plugins.ryu.tests.unit import utils
|
||||
from quantum.plugins.ryu.tests.unit.utils import patch_fake_ryu_client
|
||||
|
||||
|
||||
CONF_FILE = find_config_file({"plugin": "ryu"}, "ryu.ini")
|
||||
|
||||
|
||||
class RyuDriverTest(BaseRyuTest):
|
||||
"""Class conisting of OFPRyuDriver unit tests"""
|
||||
def setUp(self):
|
||||
super(RyuDriverTest, self).setUp()
|
||||
|
||||
self.conf = config.parse(CONF_FILE)
|
||||
# fake up ryu.app.client and ryu.app.rest_nw_id
|
||||
# With those, plugin can be tested without ryu installed
|
||||
self.module_patcher = patch_fake_ryu_client()
|
||||
@ -65,7 +70,7 @@ class RyuDriverTest(BaseRyuTest):
|
||||
db.network_create('test', uuid0)
|
||||
|
||||
from quantum.plugins.ryu import ryu_quantum_plugin
|
||||
ryu_driver = ryu_quantum_plugin.OFPRyuDriver(self.config)
|
||||
ryu_driver = ryu_quantum_plugin.OFPRyuDriver(self.conf)
|
||||
ryu_driver.create_network(net1)
|
||||
ryu_driver.delete_network(net1)
|
||||
self.mox.VerifyAll()
|
||||
|
@ -21,7 +21,7 @@ Policy engine for quantum. Largely copied from nova.
|
||||
|
||||
import os.path
|
||||
|
||||
from quantum.common import config
|
||||
from quantum.common.utils import find_config_file
|
||||
from quantum.common import exceptions
|
||||
from quantum.openstack.common import policy
|
||||
|
||||
@ -38,7 +38,7 @@ def reset():
|
||||
def init():
|
||||
global _POLICY_PATH
|
||||
if not _POLICY_PATH:
|
||||
_POLICY_PATH = config.find_config_file({}, [], 'policy.json')
|
||||
_POLICY_PATH = find_config_file({}, 'policy.json')
|
||||
if not _POLICY_PATH:
|
||||
raise exceptions.PolicyNotFound(path=FLAGS.policy_file)
|
||||
with open(_POLICY_PATH) as f:
|
||||
|
@ -25,28 +25,19 @@ import sys
|
||||
|
||||
from quantum import service
|
||||
from quantum.common import config
|
||||
from quantum.openstack.common import cfg
|
||||
from quantum.version import version_string
|
||||
|
||||
|
||||
def create_options(parser):
|
||||
"""
|
||||
Sets up the CLI and config-file options that may be
|
||||
parsed and program commands.
|
||||
:param parser: The option parser
|
||||
"""
|
||||
config.add_common_options(parser)
|
||||
config.add_log_options(parser)
|
||||
|
||||
|
||||
def main():
|
||||
oparser = optparse.OptionParser(version='%prog ' + version_string())
|
||||
create_options(oparser)
|
||||
(options, args) = config.parse_options(oparser)
|
||||
|
||||
# the configuration will be read into the cfg.CONF global data structure
|
||||
config.parse(sys.argv)
|
||||
if not cfg.CONF.config_file:
|
||||
sys.exit("ERROR: Unable to find configuration file via the default"
|
||||
" search paths (~/.quantum/, ~/, /etc/quantum/, /etc/) and"
|
||||
" the '--config-file' option!")
|
||||
try:
|
||||
quantum_service = service.serve_wsgi(service.QuantumApiService,
|
||||
options=options,
|
||||
args=args)
|
||||
quantum_service = service.serve_wsgi(service.QuantumApiService)
|
||||
quantum_service.wait()
|
||||
except RuntimeError, e:
|
||||
sys.exit("ERROR: %s" % e)
|
||||
|
@ -18,7 +18,7 @@
|
||||
import logging
|
||||
|
||||
from quantum.common import config
|
||||
from quantum.common import exceptions as exception
|
||||
from quantum.openstack.common import cfg
|
||||
from quantum import wsgi
|
||||
|
||||
|
||||
@ -34,14 +34,12 @@ class WsgiService(object):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, app_name, conf_file, conf):
|
||||
def __init__(self, app_name):
|
||||
self.app_name = app_name
|
||||
self.conf_file = conf_file
|
||||
self.conf = conf
|
||||
self.wsgi_app = None
|
||||
|
||||
def start(self):
|
||||
self.wsgi_app = _run_wsgi(self.app_name, self.conf, self.conf_file)
|
||||
self.wsgi_app = _run_wsgi(self.app_name)
|
||||
|
||||
def wait(self):
|
||||
self.wsgi_app.wait()
|
||||
@ -51,13 +49,8 @@ class QuantumApiService(WsgiService):
|
||||
"""Class for quantum-api service."""
|
||||
|
||||
@classmethod
|
||||
def create(cls, conf=None, options=None, args=None):
|
||||
def create(cls):
|
||||
app_name = "quantum"
|
||||
if not conf:
|
||||
conf_file, conf = config.load_paste_config(app_name, options, args)
|
||||
if not conf:
|
||||
message = (_('No paste configuration found for: %s'), app_name)
|
||||
raise exception.Error(message)
|
||||
|
||||
# Setup logging early, supplying both the CLI options and the
|
||||
# configuration mapping from the config file
|
||||
@ -65,32 +58,24 @@ class QuantumApiService(WsgiService):
|
||||
# flags. Everything else must be set up in the conf file...
|
||||
# Log the options used when starting if we're in debug mode...
|
||||
|
||||
config.setup_logging(options, conf)
|
||||
debug = (options.get('debug') or
|
||||
config.get_option(conf, 'debug', type='bool', default=False))
|
||||
verbose = (options.get('verbose') or
|
||||
config.get_option(conf, 'verbose', type='bool',
|
||||
default=False))
|
||||
conf['debug'] = debug
|
||||
conf['verbose'] = verbose
|
||||
config.setup_logging(cfg.CONF)
|
||||
LOG.debug("*" * 80)
|
||||
LOG.debug("Configuration options gathered from config file:")
|
||||
LOG.debug(conf_file)
|
||||
LOG.debug("================================================")
|
||||
items = dict([(k, v) for k, v in conf.items()
|
||||
items = dict([(k, v) for k, v in cfg.CONF.items()
|
||||
if k not in ('__file__', 'here')])
|
||||
for key, value in sorted(items.items()):
|
||||
LOG.debug("%(key)-30s %(value)s" % {'key': key,
|
||||
'value': value,
|
||||
})
|
||||
LOG.debug("*" * 80)
|
||||
service = cls(app_name, conf_file, conf)
|
||||
service = cls(app_name)
|
||||
return service
|
||||
|
||||
|
||||
def serve_wsgi(cls, conf=None, options=None, args=None):
|
||||
def serve_wsgi(cls):
|
||||
try:
|
||||
service = cls.create(conf, options, args)
|
||||
service = cls.create()
|
||||
except Exception:
|
||||
logging.exception('in WsgiService.create()')
|
||||
raise
|
||||
@ -100,15 +85,11 @@ def serve_wsgi(cls, conf=None, options=None, args=None):
|
||||
return service
|
||||
|
||||
|
||||
def _run_wsgi(app_name, paste_conf, paste_config_file):
|
||||
LOG.info(_('Using paste.deploy config at: %s'), paste_config_file)
|
||||
conf, app = config.load_paste_app(app_name,
|
||||
{'config_file': paste_config_file},
|
||||
None)
|
||||
def _run_wsgi(app_name):
|
||||
app = config.load_paste_app(app_name, "quantum.conf")
|
||||
if not app:
|
||||
LOG.error(_('No known API applications configured in %s.'),
|
||||
paste_config_file)
|
||||
LOG.error(_('No known API applications configured.'))
|
||||
return
|
||||
server = wsgi.Server("Quantum")
|
||||
server.start(app, int(paste_conf['bind_port']), paste_conf['bind_host'])
|
||||
server.start(app, cfg.CONF.bind_port, cfg.CONF.bind_host)
|
||||
return server
|
||||
|
@ -21,19 +21,30 @@ import logging
|
||||
import unittest2 as unittest
|
||||
|
||||
import mock
|
||||
import os
|
||||
|
||||
from quantum.api.api_common import APIFaultWrapper
|
||||
from quantum.api.networks import Controller
|
||||
from quantum.common.test_lib import test_config
|
||||
from quantum.common import config
|
||||
from quantum.db import api as db
|
||||
from quantum.openstack.common import importutils
|
||||
import quantum.tests.unit.testlib_api as testlib
|
||||
from quantum.openstack.common import cfg
|
||||
from quantum.wsgi import XMLDeserializer, JSONDeserializer
|
||||
|
||||
|
||||
LOG = logging.getLogger('quantum.tests.test_api')
|
||||
|
||||
|
||||
ROOTDIR = os.path.dirname(os.path.dirname(__file__))
|
||||
ETCDIR = os.path.join(ROOTDIR, 'etc')
|
||||
|
||||
|
||||
def etcdir(*p):
|
||||
return os.path.join(ETCDIR, *p)
|
||||
|
||||
|
||||
NETS = "networks"
|
||||
PORTS = "ports"
|
||||
ATTS = "attachments"
|
||||
@ -105,10 +116,13 @@ class AbstractAPITest(unittest.TestCase):
|
||||
self.assertEqual(put_attachment_res.status_int, expected_res_status)
|
||||
|
||||
def setUp(self, api_router_klass, xml_metadata_dict):
|
||||
options = {}
|
||||
options['plugin_provider'] = test_config['plugin_name']
|
||||
# Create the default configurations
|
||||
args = ['--config-file', etcdir('quantum.conf.test')]
|
||||
config.parse(args=args)
|
||||
# Update the plugin
|
||||
cfg.CONF.set_override('core_plugin', test_config['plugin_name'])
|
||||
api_router_cls = importutils.import_class(api_router_klass)
|
||||
self.api = api_router_cls(options)
|
||||
self.api = api_router_cls()
|
||||
self.tenant_id = "test_tenant"
|
||||
self.network_name = "test_network"
|
||||
|
||||
@ -136,6 +150,7 @@ class AbstractAPITest(unittest.TestCase):
|
||||
"""Clear the test environment"""
|
||||
# Remove database contents
|
||||
db.clear_db()
|
||||
cfg.CONF.reset()
|
||||
|
||||
|
||||
class BaseAPIOperationsTest(AbstractAPITest):
|
||||
|
@ -12,6 +12,7 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the spec
|
||||
|
||||
import os
|
||||
import logging
|
||||
import unittest
|
||||
import uuid
|
||||
@ -26,6 +27,8 @@ from quantum.common import exceptions as q_exc
|
||||
from quantum.api.v2 import resource as wsgi_resource
|
||||
from quantum.api.v2 import router
|
||||
from quantum.api.v2 import views
|
||||
from quantum.common import config
|
||||
from quantum.openstack.common import cfg
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -34,6 +37,13 @@ LOG = logging.getLogger(__name__)
|
||||
def _uuid():
|
||||
return str(uuid.uuid4())
|
||||
|
||||
ROOTDIR = os.path.dirname(os.path.dirname(__file__))
|
||||
ETCDIR = os.path.join(ROOTDIR, 'etc')
|
||||
|
||||
|
||||
def etcdir(*p):
|
||||
return os.path.join(ETCDIR, *p)
|
||||
|
||||
|
||||
def _get_path(resource, id=None, fmt=None):
|
||||
path = '/%s' % resource
|
||||
@ -123,16 +133,23 @@ class APIv2TestCase(unittest.TestCase):
|
||||
# will get around this.
|
||||
def setUp(self):
|
||||
plugin = 'quantum.quantum_plugin_base_v2.QuantumPluginBaseV2'
|
||||
# Create the default configurations
|
||||
args = ['--config-file', etcdir('quantum.conf.test')]
|
||||
config.parse(args=args)
|
||||
# Update the plugin
|
||||
cfg.CONF.set_override('core_plugin', plugin)
|
||||
|
||||
self._plugin_patcher = mock.patch(plugin, autospec=True)
|
||||
self.plugin = self._plugin_patcher.start()
|
||||
|
||||
api = router.APIRouter({'plugin_provider': plugin})
|
||||
api = router.APIRouter()
|
||||
self.api = webtest.TestApp(api)
|
||||
|
||||
def tearDown(self):
|
||||
self._plugin_patcher.stop()
|
||||
self.api = None
|
||||
self.plugin = None
|
||||
cfg.CONF.reset()
|
||||
|
||||
def test_verbose_attr(self):
|
||||
instance = self.plugin.return_value
|
||||
|
@ -13,6 +13,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import logging
|
||||
import unittest
|
||||
import contextlib
|
||||
@ -22,12 +23,21 @@ import quantum
|
||||
from quantum.api.v2.router import APIRouter
|
||||
from quantum.common import exceptions as q_exc
|
||||
from quantum.db import api as db
|
||||
from quantum.common import config
|
||||
from quantum.openstack.common import cfg
|
||||
from quantum.tests.unit.testlib_api import create_request
|
||||
from quantum.wsgi import Serializer, JSONDeserializer
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
ROOTDIR = os.path.dirname(os.path.dirname(__file__))
|
||||
ETCDIR = os.path.join(ROOTDIR, 'etc')
|
||||
|
||||
|
||||
def etcdir(*p):
|
||||
return os.path.join(ETCDIR, *p)
|
||||
|
||||
|
||||
class QuantumDbPluginV2TestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@ -46,7 +56,12 @@ class QuantumDbPluginV2TestCase(unittest.TestCase):
|
||||
}
|
||||
|
||||
plugin = 'quantum.db.db_base_plugin_v2.QuantumDbPluginV2'
|
||||
self.api = APIRouter({'plugin_provider': plugin})
|
||||
# Create the default configurations
|
||||
args = ['--config-file', etcdir('quantum.conf.test')]
|
||||
config.parse(args=args)
|
||||
# Update the plugin
|
||||
cfg.CONF.set_override('core_plugin', plugin)
|
||||
self.api = APIRouter()
|
||||
|
||||
def tearDown(self):
|
||||
super(QuantumDbPluginV2TestCase, self).tearDown()
|
||||
@ -54,6 +69,7 @@ class QuantumDbPluginV2TestCase(unittest.TestCase):
|
||||
# doesn't like when the plugin changes ;)
|
||||
db._ENGINE = None
|
||||
db._MAKER = None
|
||||
cfg.CONF.reset()
|
||||
|
||||
def _req(self, method, resource, data=None, fmt='json', id=None):
|
||||
if id:
|
||||
|
@ -14,6 +14,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import routes
|
||||
@ -41,8 +43,15 @@ from quantum.tests.unit.extension_stubs import (
|
||||
import quantum.tests.unit.extensions
|
||||
from quantum import wsgi
|
||||
|
||||
LOG = logging.getLogger('quantum.tests.test_extensions')
|
||||
|
||||
ROOTDIR = os.path.dirname(os.path.dirname(__file__))
|
||||
ETCDIR = os.path.join(ROOTDIR, 'etc')
|
||||
|
||||
|
||||
def etcdir(*p):
|
||||
return os.path.join(ETCDIR, *p)
|
||||
|
||||
test_conf_file = config.find_config_file({}, None, "quantum.conf.test")
|
||||
extensions_path = ':'.join(quantum.tests.unit.extensions.__path__)
|
||||
|
||||
|
||||
@ -455,8 +464,10 @@ def app_factory(global_conf, **local_conf):
|
||||
|
||||
|
||||
def setup_base_app():
|
||||
options = {'config_file': test_conf_file}
|
||||
conf, app = config.load_paste_app('extensions_test_app', options, None)
|
||||
config_file = 'quantum.conf.test'
|
||||
args = ['--config-file', etcdir(config_file)]
|
||||
config.parse(args=args)
|
||||
app = config.load_paste_app('extensions_test_app', config_file)
|
||||
return app
|
||||
|
||||
|
||||
@ -464,9 +475,11 @@ def setup_extensions_middleware(extension_manager=None):
|
||||
extension_manager = (extension_manager or
|
||||
PluginAwareExtensionManager(extensions_path,
|
||||
QuantumEchoPlugin()))
|
||||
options = {'config_file': test_conf_file}
|
||||
conf, app = config.load_paste_app('extensions_test_app', options, None)
|
||||
return ExtensionMiddleware(app, conf, ext_mgr=extension_manager)
|
||||
config_file = 'quantum.conf.test'
|
||||
args = ['--config-file', etcdir(config_file)]
|
||||
config.parse(args=args)
|
||||
app = config.load_paste_app('extensions_test_app', config_file)
|
||||
return ExtensionMiddleware(app, ext_mgr=extension_manager)
|
||||
|
||||
|
||||
def setup_extensions_test_app(extension_manager=None):
|
||||
|
2
setup.py
2
setup.py
@ -51,7 +51,7 @@ ryu_plugin_config_path = 'etc/quantum/plugins/ryu'
|
||||
|
||||
DataFiles = [
|
||||
(config_path,
|
||||
['etc/quantum.conf', 'etc/quantum.conf.test', 'etc/plugins.ini']),
|
||||
['etc/quantum.conf']),
|
||||
(init_path, ['etc/init.d/quantum-server']),
|
||||
(ovs_plugin_config_path,
|
||||
['etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini']),
|
||||
|
Loading…
Reference in New Issue
Block a user