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:
Gary Kotton 2012-06-03 07:55:09 -04:00
parent b6cb4316da
commit 0c0d8f00a9
37 changed files with 555 additions and 559 deletions

View File

@ -1,3 +0,0 @@
[PLUGIN]
# Quantum plugin provider module
provider = quantum.plugins.sample.SamplePlugin.FakePlugin

View File

@ -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

View File

@ -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

View File

@ -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 = (

View File

@ -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

View File

@ -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)

View File

@ -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)
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

View File

@ -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

View 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

View File

@ -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

View File

@ -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,17 +1183,25 @@ 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():
for info, group in self._all_opt_infos():
info['default'] = None
info['override'] = None
unset(self._opts)
for group in self._groups.values():
unset(group._opts)
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()

View File

@ -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

View File

@ -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)

View 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']

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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")

View File

@ -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)

View 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")

View File

@ -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

View File

@ -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",

View 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.

View 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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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:

View File

@ -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)

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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:

View File

@ -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):

View File

@ -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']),