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 # extensions are in there you don't need to specify them here
api_extensions_path = api_extensions_path =
# Quantum plugin provider module
core_plugin = quantum.plugins.sample.SamplePlugin.FakePlugin
[composite:quantum] [composite:quantum]
use = egg:Paste#urlmap use = egg:Paste#urlmap
/: quantumversions /: quantumversions

View File

@ -5,12 +5,12 @@
sql_connection = sqlite:// sql_connection = sqlite://
[OVS] [OVS]
integration-bridge = br-int integration_bridge = br-int
# openflow-controller = <host IP address of ofp controller>:<port: 6633> # openflow_controller = <host IP address of ofp controller>:<port: 6633>
# openflow-rest-api = <host IP address of ofp rest api service>:<port: 8080> # openflow_rest_api = <host IP address of ofp rest api service>:<port: 8080>
openflow-controller = 127.0.0.1:6633 openflow_controller = 127.0.0.1:6633
openflow-rest-api = 127.0.0.1:8080 openflow_rest_api = 127.0.0.1:8080
[AGENT] [AGENT]
# Change to "sudo quantum-rootwrap" to limit commands that can be run # Change to "sudo quantum-rootwrap" to limit commands that can be run

View File

@ -42,23 +42,23 @@ class APIRouter(wsgi.Router):
""" """
_version = None _version = None
def __init__(self, options=None): def __init__(self):
mapper = self._mapper() mapper = self._mapper()
self._setup_routes(mapper, options) self._setup_routes(mapper)
super(APIRouter, self).__init__(mapper) super(APIRouter, self).__init__(mapper)
def _mapper(self): def _mapper(self):
return routes.Mapper() return routes.Mapper()
def _setup_routes(self, mapper, options): def _setup_routes(self, mapper):
self._setup_base_routes(mapper, options, self._version) 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.""" """Routes common to all versions."""
# Loads the quantum plugin # Loads the quantum plugin
# Note(salvatore-orlando): Should the plugin be versioned # Note(salvatore-orlando): Should the plugin be versioned
# I don't think so # I don't think so
plugin = manager.QuantumManager.get_plugin(options) plugin = manager.QuantumManager.get_plugin()
uri_prefix = '/tenants/{tenant_id}/' uri_prefix = '/tenants/{tenant_id}/'
attachment_path = ( attachment_path = (

View File

@ -293,7 +293,7 @@ class Controller(object):
return body return body
def create_resource(collection, resource, plugin, conf, params): def create_resource(collection, resource, plugin, params):
controller = Controller(plugin, collection, resource, params) controller = Controller(plugin, collection, resource, params)
# NOTE(jkoelker) To anyone wishing to add "proper" xml support # 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 manager
from quantum import wsgi from quantum import wsgi
from quantum.api.v2 import base from quantum.api.v2 import base
from quantum.openstack.common import cfg
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -118,16 +119,14 @@ class APIRouter(wsgi.Router):
@classmethod @classmethod
def factory(cls, global_config, **local_config): 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() 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) 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, col_kwargs = dict(collection_actions=COLLECTION_ACTIONS,
member_actions=MEMBER_ACTIONS) member_actions=MEMBER_ACTIONS)
@ -137,8 +136,7 @@ class APIRouter(wsgi.Router):
def _map_resource(collection, resource, params): def _map_resource(collection, resource, params):
controller = base.create_resource(collection, resource, controller = base.create_resource(collection, resource,
plugin, conf, plugin, params)
params)
mapper_kwargs = dict(controller=controller, mapper_kwargs = dict(controller=controller,
requirements=REQUIREMENTS, requirements=REQUIREMENTS,
**col_kwargs) **col_kwargs)

View File

@ -20,325 +20,100 @@ Routines for configuring Quantum
""" """
import logging import logging
import logging.config
import logging.handlers import logging.handlers
import optparse
import os import os
import socket
import sys import sys
from paste import deploy 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__) 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): def parse(args):
""" cfg.CONF(args=args, project='quantum',
Returns the parsed CLI options, command to run and its arguments, merged version='%%prog %s' % version_string())
with any same-named options found in a configuration file.
The function returns a tuple of (options, args), where options is a
mapping of option key/str(value) pairs, and args is the set of arguments
(not options) supplied on the command-line.
The reason that the option values are returned as strings only is that
ConfigParser and paste.deploy only accept string values...
:param parser: The option parser
:param cli_args: (Optional) Set of arguments to process. If not present,
sys.argv[1:] is used.
:retval tuple of (options, args)
"""
(options, args) = parser.parse_args(cli_args)
return (vars(options), args)
def add_common_options(parser): def setup_logging(conf):
"""
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):
""" """
Sets up the logging options for a log with supplied name Sets up the logging options for a log with supplied name
:param options: Mapping of typed option key/values :param conf: a cfg.ConfOpts object
:param conf: Mapping of untyped key/values from config file
""" """
if options.get('log_config', None): if conf.log_config:
# Use a logging configuration file for all settings... # Use a logging configuration file for all settings...
if os.path.exists(options['log_config']): if os.path.exists(conf.log_config):
logging.config.fileConfig(options['log_config']) logging.config.fileConfig(conf.log_config)
return return
else: else:
raise RuntimeError("Unable to locate specified logging " 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 root_logger = logging.root
if debug: if conf.debug:
root_logger.setLevel(logging.DEBUG) root_logger.setLevel(logging.DEBUG)
elif verbose: elif conf.verbose:
root_logger.setLevel(logging.INFO) root_logger.setLevel(logging.INFO)
else: else:
root_logger.setLevel(logging.WARNING) root_logger.setLevel(logging.WARNING)
# Set log configuration from options... formatter = logging.Formatter(conf.log_format, conf.log_date_format)
# 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)
syslog = options.get('use_syslog') if conf.use_syslog:
if not syslog:
syslog = conf.get('use_syslog')
if syslog:
SysLogHandler = logging.handlers.SysLogHandler
try: try:
handler = SysLogHandler(address='/dev/log', facility = getattr(logging.handlers.SysLogHandler,
facility=SysLogHandler.LOG_SYSLOG) conf.syslog_log_facility)
except socket.error: except AttributeError:
handler = SysLogHandler(address='/var/run/syslog', raise ValueError(_("Invalid syslog facility"))
facility=SysLogHandler.LOG_SYSLOG)
handler.setFormatter(formatter)
root_logger.addHandler(handler)
logfile = options.get('log_file') handler = logging.handlers.SysLogHandler(address='/dev/log',
if not logfile: facility=facility)
logfile = conf.get('log_file') elif conf.log_file:
logfile = conf.log_file
if logfile: if conf.log_dir:
logdir = options.get('log_dir') logfile = os.path.join(conf.log_dir, logfile)
if not logdir: handler = logging.handlers.WatchedFileHandler(logfile)
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)
else: else:
handler = logging.StreamHandler(sys.stdout) 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'): def load_paste_app(app_name, 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
* 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):
""" """
Builds and returns a WSGI app from a paste 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 app_name: Name of the application to load
:param options: Set of typed options returned from parse_options() :param config_file: name of the configuration file
:param args: Command line arguments from argv[1:]
:raises RuntimeError when config file cannot be located or application :raises RuntimeError when config file cannot be located or application
cannot be loaded from config file 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: try:
app = deploy.loadapp("config:%s" % conf_file, name=app_name) app = deploy.loadapp("config:%s" % config_path, name=app_name)
except (LookupError, ImportError): except (LookupError, ImportError):
msg = ("Unable to load %(app_name)s from " msg = ("Unable to load %(app_name)s from "
"configuration file %(conf_file)s.") % locals() "configuration file %(config_path)s.") % locals()
LOG.exception(msg) LOG.exception(msg)
raise RuntimeError(msg) raise RuntimeError(msg)
return conf, app return 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)

View File

@ -21,7 +21,8 @@
"""Utilities and helper functions.""" """Utilities and helper functions."""
import ConfigParser import datetime
import inspect
import logging import logging
import os import os
import subprocess import subprocess
@ -75,12 +76,6 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True):
return result return result
def get_plugin_from_config(file="config.ini"):
Config = ConfigParser.ConfigParser()
Config.read(file)
return Config.get("PLUGIN", "provider")
class LazyPluggable(object): class LazyPluggable(object):
"""A pluggable backend loaded lazily based on some value.""" """A pluggable backend loaded lazily based on some value."""
@ -110,3 +105,51 @@ class LazyPluggable(object):
def __getattr__(self, key): def __getattr__(self, key):
backend = self.__get_backend() backend = self.__get_backend()
return getattr(backend, key) 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 from quantum.common import exceptions
import quantum.extensions import quantum.extensions
from quantum.manager import QuantumManager from quantum.manager import QuantumManager
from quantum.openstack.common import cfg
from quantum import wsgi from quantum import wsgi
@ -217,12 +218,12 @@ class ExtensionController(wsgi.Controller):
class ExtensionMiddleware(wsgi.Middleware): class ExtensionMiddleware(wsgi.Middleware):
"""Extensions middleware for WSGI.""" """Extensions middleware for WSGI."""
def __init__(self, application, config_params, def __init__(self, application,
ext_mgr=None): ext_mgr=None):
self.ext_mgr = (ext_mgr self.ext_mgr = (ext_mgr
or ExtensionManager( or ExtensionManager(
get_extensions_path(config_params))) get_extensions_path()))
mapper = routes.Mapper() mapper = routes.Mapper()
# extended resources # extended resources
@ -339,10 +340,10 @@ class ExtensionMiddleware(wsgi.Middleware):
def plugin_aware_extension_middleware_factory(global_config, **local_config): def plugin_aware_extension_middleware_factory(global_config, **local_config):
"""Paste factory.""" """Paste factory."""
def _factory(app): def _factory(app):
extensions_path = get_extensions_path(global_config) extensions_path = get_extensions_path()
ext_mgr = PluginAwareExtensionManager(extensions_path, ext_mgr = PluginAwareExtensionManager(extensions_path,
QuantumManager.get_plugin()) QuantumManager.get_plugin())
return ExtensionMiddleware(app, global_config, ext_mgr=ext_mgr) return ExtensionMiddleware(app, ext_mgr=ext_mgr)
return _factory return _factory
@ -539,9 +540,9 @@ class ResourceExtension(object):
# Returns the extention paths from a config entry and the __path__ # Returns the extention paths from a config entry and the __path__
# of quantum.extensions # of quantum.extensions
def get_extensions_path(config=None): def get_extensions_path():
paths = ':'.join(quantum.extensions.__path__) paths = ':'.join(quantum.extensions.__path__)
if config: if cfg.CONF.api_extensions_path:
paths = ':'.join([config.get('api_extensions_path', ''), paths]) paths = ':'.join([cfg.CONF.api_extensions_path, paths])
return paths return paths

View File

@ -24,27 +24,15 @@ The caller should make sure that QuantumManager is a singleton.
""" """
import logging 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.common.exceptions import ClassNotFound
from quantum.openstack.common import cfg
from quantum.openstack.common import importutils from quantum.openstack.common import importutils
LOG = logging.getLogger(__name__) 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): def get_plugin(plugin_provider):
# If the plugin can't be found let them know gracefully # If the plugin can't be found let them know gracefully
try: try:
@ -58,16 +46,6 @@ def get_plugin(plugin_provider):
return plugin_klass() 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): class QuantumManager(object):
_instance = None _instance = None
@ -81,12 +59,12 @@ class QuantumManager(object):
# breaks tach monitoring. It has been removed # breaks tach monitoring. It has been removed
# intentianally to allow v2 plugins to be monitored # intentianally to allow v2 plugins to be monitored
# for performance metrics. # for performance metrics.
plugin_provider = get_plugin_provider(options, config_file) plugin_provider = cfg.CONF.core_plugin
LOG.debug("Plugin location:%s", plugin_provider) LOG.debug("Plugin location:%s", plugin_provider)
self.plugin = get_plugin(plugin_provider) self.plugin = get_plugin(plugin_provider)
@classmethod @classmethod
def get_plugin(cls, options=None, config_file=None): def get_plugin(cls):
if cls._instance is None: if cls._instance is None:
cls._instance = cls(options, config_file) cls._instance = cls()
return cls._instance.plugin return cls._instance.plugin

View File

@ -95,7 +95,7 @@ and --config-dir::
class ConfigOpts(object): class ConfigOpts(object):
def __init__(self, ...): def __call__(self, ...):
opts = [ opts = [
MultiStrOpt('config-file', 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 import collections
@ -478,7 +494,8 @@ class Opt(object):
multi = False multi = False
def __init__(self, name, dest=None, short=None, default=None, 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. """Construct an Opt object.
The only required parameter is the option's name. However, it is 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 help: an explanation of how the option is used
:param secret: true iff the value should be obfuscated in log output :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 required: true iff a value must be supplied for this option
:param deprecated_name: deprecated name option. Acts like an alias
""" """
self.name = name self.name = name
if dest is None: if dest is None:
@ -504,6 +522,10 @@ class Opt(object):
self.help = help self.help = help
self.secret = secret self.secret = secret
self.required = required 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): def _get_from_config_parser(self, cparser, section):
"""Retrieves the option value from a MultiConfigParser object. """Retrieves the option value from a MultiConfigParser object.
@ -515,7 +537,13 @@ class Opt(object):
:param cparser: a ConfigParser object :param cparser: a ConfigParser object
:param section: a section name :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): def _add_to_cli(self, parser, group=None):
"""Makes the option available in the command line interface. """Makes the option available in the command line interface.
@ -530,9 +558,11 @@ class Opt(object):
container = self._get_optparse_container(parser, group) container = self._get_optparse_container(parser, group)
kwargs = self._get_optparse_kwargs(group) kwargs = self._get_optparse_kwargs(group)
prefix = self._get_optparse_prefix('', 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. """Add an option to an optparse parser or group.
:param container: an optparse.OptionContainer object :param container: an optparse.OptionContainer object
@ -545,6 +575,8 @@ class Opt(object):
args = ['--' + prefix + name] args = ['--' + prefix + name]
if short: if short:
args += ['-' + short] args += ['-' + short]
if deprecated_name:
args += ['--' + prefix + deprecated_name]
for a in args: for a in args:
if container.has_option(a): if container.has_option(a):
raise DuplicateOptError(a) raise DuplicateOptError(a)
@ -577,7 +609,7 @@ class Opt(object):
dest = group.name + '_' + dest dest = group.name + '_' + dest
kwargs.update({'dest': dest, kwargs.update({'dest': dest,
'metavar': self.metavar, 'metavar': self.metavar,
'help': self.help}) 'help': self.help, })
return kwargs return kwargs
def _get_optparse_prefix(self, prefix, group): def _get_optparse_prefix(self, prefix, group):
@ -627,7 +659,8 @@ class BoolOpt(Opt):
return value 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): def _add_to_cli(self, parser, group=None):
"""Extends the base class method to add the --nooptname option.""" """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') kwargs = self._get_optparse_kwargs(group, action='store_false')
prefix = self._get_optparse_prefix('no', group) prefix = self._get_optparse_prefix('no', group)
kwargs["help"] = "The inverse of --" + self.name 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): def _get_optparse_kwargs(self, group, action='store_true', **kwargs):
"""Extends the base optparse keyword dict for boolean options.""" """Extends the base optparse keyword dict for boolean options."""
@ -654,7 +688,8 @@ class IntOpt(Opt):
def _get_from_config_parser(self, cparser, section): def _get_from_config_parser(self, cparser, section):
"""Retrieve the opt value as a integer from ConfigParser.""" """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): def _get_optparse_kwargs(self, group, **kwargs):
"""Extends the base optparse keyword dict for integer options.""" """Extends the base optparse keyword dict for integer options."""
@ -668,7 +703,8 @@ class FloatOpt(Opt):
def _get_from_config_parser(self, cparser, section): def _get_from_config_parser(self, cparser, section):
"""Retrieve the opt value as a float from ConfigParser.""" """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): def _get_optparse_kwargs(self, group, **kwargs):
"""Extends the base optparse keyword dict for float options.""" """Extends the base optparse keyword dict for float options."""
@ -685,7 +721,8 @@ class ListOpt(Opt):
def _get_from_config_parser(self, cparser, section): def _get_from_config_parser(self, cparser, section):
"""Retrieve the opt value as a list from ConfigParser.""" """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): def _get_optparse_kwargs(self, group, **kwargs):
"""Extends the base optparse keyword dict for list options.""" """Extends the base optparse keyword dict for list options."""
@ -714,6 +751,13 @@ class MultiStrOpt(Opt):
return super(MultiStrOpt, return super(MultiStrOpt,
self)._get_optparse_kwargs(group, action='append') 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): class OptGroup(object):
@ -766,6 +810,14 @@ class OptGroup(object):
return True 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): def _get_optparse_group(self, parser):
"""Build an optparse.OptionGroup for this group.""" """Build an optparse.OptionGroup for this group."""
if self._optparse_group is None: if self._optparse_group is None:
@ -773,6 +825,10 @@ class OptGroup(object):
self.help) self.help)
return self._optparse_group return self._optparse_group
def _clear(self):
"""Clear this group's option parsing state."""
self._optparse_group = None
class ParseError(iniparser.ParseError): class ParseError(iniparser.ParseError):
def __init__(self, msg, lineno, line, filename): def __init__(self, msg, lineno, line, filename):
@ -816,25 +872,38 @@ class ConfigParser(iniparser.BaseParser):
class MultiConfigParser(object): class MultiConfigParser(object):
def __init__(self): def __init__(self):
self.sections = {} self.parsed = []
def read(self, config_files): def read(self, config_files):
read_ok = [] read_ok = []
for filename in config_files: for filename in config_files:
parser = ConfigParser(filename, self.sections) sections = {}
parser = ConfigParser(filename, sections)
try: try:
parser.parse() parser.parse()
except IOError: except IOError:
continue continue
self.parsed.insert(0, sections)
read_ok.append(filename) read_ok.append(filename)
return read_ok return read_ok
def get(self, section, name): def get(self, section, names, multi=False):
return self.sections[section][name] 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): class ConfigOpts(collections.Mapping):
@ -847,57 +916,41 @@ class ConfigOpts(collections.Mapping):
the values of options. the values of options.
""" """
def __init__(self, def __init__(self):
project=None, """Construct a ConfigOpts object."""
prog=None, self._opts = {} # dict of dicts of (opt:, override:, default:)
version=None, self._groups = {}
usage=None,
default_config_files=None):
"""Construct a ConfigOpts object.
Automatically registers the --config-file option with either a supplied self._args = None
list of default config files, or a list from find_config_files(). 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 def _setup(self, project, prog, version, usage, default_config_files):
:param prog: the name of the program (defaults to sys.argv[0] basename) """Initialize a ConfigOpts object for option parsing."""
: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
"""
if prog is None: if prog is None:
prog = os.path.basename(sys.argv[0]) prog = os.path.basename(sys.argv[0])
if default_config_files is None: if default_config_files is None:
default_config_files = find_config_files(project, prog) default_config_files = find_config_files(project, prog)
self.project = project self._oparser = optparse.OptionParser(prog=prog,
self.prog = prog version=version,
self.version = version usage=usage)
self.usage = usage if self._disable_interspersed_args:
self.default_config_files = default_config_files self._oparser.disable_interspersed_args()
self._opts = {} # dict of dicts of (opt:, override:, default:) self._config_opts = [
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 = [
MultiStrOpt('config-file', MultiStrOpt('config-file',
default=self.default_config_files, default=default_config_files,
metavar='PATH', metavar='PATH',
help='Path to a config file to use. Multiple config ' help='Path to a config file to use. Multiple config '
'files can be specified, with values in later ' 'files can be specified, with values in later '
'files taking precedence. The default files ' 'files taking precedence. The default files '
' used are: %s' % ' used are: %s' % (default_config_files, )),
(self.default_config_files)),
StrOpt('config-dir', StrOpt('config-dir',
metavar='DIR', metavar='DIR',
help='Path to a config directory to pull *.conf ' 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 ' 'hence over-ridden options in the directory take '
'precedence.'), '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): def __clear_cache(f):
@functools.wraps(f) @functools.wraps(f)
@ -919,7 +978,13 @@ class ConfigOpts(collections.Mapping):
return __inner 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. """Parse command line arguments and config files.
Calling a ConfigOpts object causes the supplied command line arguments 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 The object may be called multiple times, each time causing the previous
set of values to be overwritten. 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 If the --config-dir option is set, any *.conf files from this
directory are pulled in, after all the file(s) specified by the directory are pulled in, after all the file(s) specified by the
--config-file option. --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 :returns: the list of arguments left over after parsing options
:raises: SystemExit, ConfigFilesNotFoundError, ConfigFileParseError, :raises: SystemExit, ConfigFilesNotFoundError, ConfigFileParseError,
RequiredOptError RequiredOptError, DuplicateOptError
""" """
self.clear() 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) self._parse_config_files()
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._check_required_opts() self._check_required_opts()
return args return leftovers
def __getattr__(self, name): def __getattr__(self, name):
"""Look up an option value and perform string substitution. """Look up an option value and perform string substitution.
@ -994,8 +1058,12 @@ class ConfigOpts(collections.Mapping):
def clear(self): def clear(self):
"""Clear the state of the object to before it was called.""" """Clear the state of the object to before it was called."""
self._args = None self._args = None
self._cli_values = {} self._cli_values.clear()
self._oparser = None
self._cparser = None self._cparser = None
self.unregister_opts(self._config_opts)
for group in self._groups.values():
group._clear()
@__clear_cache @__clear_cache
def register_opt(self, opt, group=None): def register_opt(self, opt, group=None):
@ -1042,15 +1110,7 @@ class ConfigOpts(collections.Mapping):
if self._args is not None: if self._args is not None:
raise ArgsAlreadyParsedError("cannot register CLI option") raise ArgsAlreadyParsedError("cannot register CLI option")
if not self.register_opt(opt, group, clear_cache=False): return 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
@__clear_cache @__clear_cache
def register_cli_opts(self, opts, group=None): def register_cli_opts(self, opts, group=None):
@ -1071,6 +1131,28 @@ class ConfigOpts(collections.Mapping):
self._groups[group.name] = copy.copy(group) 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 @__clear_cache
def set_override(self, name, override, group=None): def set_override(self, name, override, group=None):
"""Override an opt value. """Override an opt value.
@ -1101,16 +1183,24 @@ class ConfigOpts(collections.Mapping):
opt_info = self._get_opt_info(name, group) opt_info = self._get_opt_info(name, group)
opt_info['default'] = default 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): def _unset_defaults_and_overrides(self):
"""Unset any default or override on all options.""" """Unset any default or override on all options."""
def unset(opts): for info, group in self._all_opt_infos():
for info in opts.values(): info['default'] = None
info['default'] = None info['override'] = None
info['override'] = None
unset(self._opts)
for group in self._groups.values():
unset(group._opts)
def disable_interspersed_args(self): def disable_interspersed_args(self):
"""Set parsing to stop on the first non-option. """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. 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): def enable_interspersed_args(self):
"""Set parsing to not stop on the first non-option. """Set parsing to not stop on the first non-option.
This it the default behaviour.""" This it the default behaviour."""
self._oparser.enable_interspersed_args() self._disable_interspersed_args = False
def find_file(self, name): def find_file(self, name):
"""Locate a file located alongside the config files. """Locate a file located alongside the config files.
@ -1272,7 +1362,7 @@ class ConfigOpts(collections.Mapping):
def _substitute(self, value): def _substitute(self, value):
"""Perform string template substitution. """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. string value(s) with opt values.
:param value: the string value, or list of string values :param value: the string value, or list of string values
@ -1329,11 +1419,17 @@ class ConfigOpts(collections.Mapping):
return opts[opt_name] return opts[opt_name]
def _parse_config_files(self, config_files): def _parse_config_files(self):
"""Parse the supplied configuration files. """Parse the config files from --config-file and --config-dir.
:raises: ConfigFilesNotFoundError, ConfigFileParseError :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() self._cparser = MultiConfigParser()
try: try:
@ -1345,8 +1441,12 @@ class ConfigOpts(collections.Mapping):
not_read_ok = filter(lambda f: f not in read_ok, config_files) not_read_ok = filter(lambda f: f not in read_ok, config_files)
raise ConfigFilesNotFoundError(not_read_ok) raise ConfigFilesNotFoundError(not_read_ok)
def _do_check_required_opts(self, opts, group=None): def _check_required_opts(self):
for info in opts.values(): """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())] default, opt, override = [info[k] for k in sorted(info.keys())]
if opt.required: if opt.required:
@ -1356,15 +1456,25 @@ class ConfigOpts(collections.Mapping):
if self._get(opt.name, group) is None: if self._get(opt.name, group) is None:
raise RequiredOptError(opt.name, group) raise RequiredOptError(opt.name, group)
def _check_required_opts(self): def _parse_cli_opts(self, args):
"""Check that all opts marked as required have values specified. """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(): for opt, group in self._all_opts():
self._do_check_required_opts(group._opts, group) opt._add_to_cli(self._oparser, group)
values, leftovers = self._oparser.parse_args(args)
return vars(values), leftovers
class GroupAttr(collections.Mapping): class GroupAttr(collections.Mapping):
@ -1480,7 +1590,10 @@ class CommonConfigOpts(ConfigOpts):
help='syslog facility to receive log lines') help='syslog facility to receive log lines')
] ]
def __init__(self, **kwargs): def __init__(self):
super(CommonConfigOpts, self).__init__(**kwargs) super(CommonConfigOpts, self).__init__()
self.register_cli_opts(self.common_cli_opts) self.register_cli_opts(self.common_cli_opts)
self.register_cli_opts(self.logging_cli_opts) self.register_cli_opts(self.logging_cli_opts)
CONF = CommonConfigOpts()

View File

@ -18,7 +18,7 @@
import logging as LOG 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_configparser as confp
from quantum.plugins.cisco.common import cisco_constants as const from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_exceptions as cexc 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.basicConfig(level=LOG.WARN)
LOG.getLogger(const.LOGGER_COMPONENT_NAME) LOG.getLogger(const.LOGGER_COMPONENT_NAME)
CREDENTIALS_FILE = find_config_file({'plugin': 'cisco'}, None, CREDENTIALS_FILE = find_config_file({'plugin': 'cisco'},
"credentials.ini") "credentials.ini")
TENANT = const.NETWORK_ADMIN TENANT = const.NETWORK_ADMIN

View File

@ -17,11 +17,11 @@
# @author: Sumit Naiksatam, Cisco Systems, Inc. # @author: Sumit Naiksatam, Cisco Systems, Inc.
# @author: Rohit Agarwalla, 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 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) CONF_PARSER_OBJ = confp.CiscoConfigParser(CONF_FILE)
@ -43,7 +43,7 @@ MAX_NETWORKS = SECTION_CONF['max_networks']
SECTION_CONF = CONF_PARSER_OBJ['MODEL'] SECTION_CONF = CONF_PARSER_OBJ['MODEL']
MODEL_CLASS = SECTION_CONF['model_class'] 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'] SECTION_CONF = CONF_PARSER_OBJ['SEGMENTATION']
MANAGER_CLASS = SECTION_CONF['manager_class'] MANAGER_CLASS = SECTION_CONF['manager_class']
@ -55,7 +55,7 @@ CONF_PARSER_OBJ = confp.CiscoConfigParser(CONF_FILE)
# Read the config for the device plugins # Read the config for the device plugins
PLUGINS = CONF_PARSER_OBJ.walk(CONF_PARSER_OBJ.dummy) 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) 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 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 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")) "nexus.ini"))
SECTION = CP['SWITCH'] SECTION = CP['SWITCH']

View File

@ -52,7 +52,7 @@ from quantum import wsgi
LOG = logging.getLogger('quantum.plugins.cisco.tests.test_cisco_extensions') 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') 'quantum.conf.ciscoext')
EXTENSIONS_PATH = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, EXTENSIONS_PATH = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
os.pardir, os.pardir, "extensions") os.pardir, os.pardir, "extensions")
@ -1256,8 +1256,8 @@ def setup_extensions_middleware(extension_manager=None):
PluginAwareExtensionManager(EXTENSIONS_PATH, PluginAwareExtensionManager(EXTENSIONS_PATH,
L2Network())) L2Network()))
options = {'config_file': TEST_CONF_FILE} options = {'config_file': TEST_CONF_FILE}
conf, app = config.load_paste_app('extensions_test_app', options, None) app = config.load_paste_app('extensions_test_app', options, None)
return ExtensionMiddleware(app, conf, ext_mgr=extension_manager) return ExtensionMiddleware(app, ext_mgr=extension_manager)
def setup_extensions_test_app(extension_manager=None): def setup_extensions_test_app(extension_manager=None):

View File

@ -17,11 +17,11 @@
# @author: Sumit Naiksatam, Cisco Systems, Inc. # @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 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')) 'ucs.ini'))
SECTION = CP['UCSM'] SECTION = CP['UCSM']
@ -34,7 +34,7 @@ PROFILE_NAME_PREFIX = SECTION['profile_name_prefix']
SECTION = CP['DRIVER'] SECTION = CP['DRIVER']
UCSM_DRIVER = SECTION['name'] UCSM_DRIVER = SECTION['name']
CP = confp.CiscoConfigParser(find_config_file({'plugin': 'cisco'}, [], CP = confp.CiscoConfigParser(find_config_file({'plugin': 'cisco'},
'ucs_inventory.ini')) 'ucs_inventory.ini'))
INVENTORY = CP.walk(CP.dummy) INVENTORY = CP.walk(CP.dummy)

View File

@ -17,11 +17,11 @@
# @author: Sumit Naiksatam, Cisco Systems, Inc. # @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 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) CP = confp.CiscoConfigParser(CONF_FILE)
INVENTORY = CP.walk(CP.dummy) INVENTORY = CP.walk(CP.dummy)

View File

@ -41,8 +41,8 @@ agent_opts = [
def parse(config_file): def parse(config_file):
conf = cfg.ConfigOpts(default_config_files=[config_file]) conf = cfg.ConfigOpts()
conf(args=[]) conf(args=[], default_config_files=[config_file])
conf.register_opts(vlan_opts, "VLANS") conf.register_opts(vlan_opts, "VLANS")
conf.register_opts(database_opts, "DATABASE") conf.register_opts(database_opts, "DATABASE")
conf.register_opts(bridge_opts, "LINUX_BRIDGE") conf.register_opts(bridge_opts, "LINUX_BRIDGE")

View File

@ -21,14 +21,14 @@ from sqlalchemy import func
from sqlalchemy.orm import exc from sqlalchemy.orm import exc
from quantum.common import exceptions as q_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 import quantum.db.api as db
from quantum.plugins.linuxbridge.common import exceptions as c_exc from quantum.plugins.linuxbridge.common import exceptions as c_exc
from quantum.plugins.linuxbridge.db import l2network_models from quantum.plugins.linuxbridge.db import l2network_models
from quantum.plugins.linuxbridge.common import config from quantum.plugins.linuxbridge.common import config
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
CONF_FILE = find_config_file({'plugin': 'linuxbridge'}, None, CONF_FILE = find_config_file({'plugin': 'linuxbridge'},
"linuxbridge_conf.ini") "linuxbridge_conf.ini")
CONF = config.parse(CONF_FILE) CONF = config.parse(CONF_FILE)

View File

@ -38,8 +38,8 @@ agent_opts = [
def parse(config_file): def parse(config_file):
conf = cfg.ConfigOpts(default_config_files=[config_file]) conf = cfg.ConfigOpts()
conf(args=[]) conf(args=[], default_config_files=[config_file])
conf.register_opts(database_opts, "DATABASE") conf.register_opts(database_opts, "DATABASE")
conf.register_opts(ovs_opts, "OVS") conf.register_opts(ovs_opts, "OVS")
conf.register_opts(agent_opts, "AGENT") conf.register_opts(agent_opts, "AGENT")

View File

@ -23,7 +23,7 @@ import os
from quantum.api.api_common import OperationalStatus from quantum.api.api_common import OperationalStatus
from quantum.common import exceptions as q_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 import quantum.db.api as db
from quantum.plugins.openvswitch import ovs_db from quantum.plugins.openvswitch import ovs_db
from quantum.plugins.openvswitch.common import config from quantum.plugins.openvswitch.common import config
@ -33,7 +33,7 @@ LOG = logging.getLogger("ovs_quantum_plugin")
CONF_FILE = find_config_file({"plugin": "openvswitch"}, CONF_FILE = find_config_file({"plugin": "openvswitch"},
None, "ovs_quantum_plugin.ini") "ovs_quantum_plugin.ini")
# Exception thrown if no more VLANs are available # Exception thrown if no more VLANs are available

View File

@ -20,7 +20,6 @@
# under the License. # under the License.
# @author: Isaku Yamahata # @author: Isaku Yamahata
import ConfigParser
import logging as LOG import logging as LOG
from optparse import OptionParser from optparse import OptionParser
import shlex import shlex
@ -34,6 +33,7 @@ from ryu.app.client import OFPClient
from sqlalchemy.ext.sqlsoup import SqlSoup from sqlalchemy.ext.sqlsoup import SqlSoup
from quantum.agent.linux import utils from quantum.agent.linux import utils
from quantum.plugins.ryu.common import config
OP_STATUS_UP = "UP" OP_STATUS_UP = "UP"
OP_STATUS_DOWN = "DOWN" OP_STATUS_DOWN = "DOWN"
@ -286,18 +286,10 @@ def main():
sys.exit(1) sys.exit(1)
config_file = args[0] config_file = args[0]
config = ConfigParser.ConfigParser() conf = config.parse(config_file)
try: integ_br = conf.OVS.integration_bridge
config.read(config_file) root_helper = conf.AGENT.root_helper
except Exception, e: options = {"sql_connection": conf.DATABASE.sql_connection}
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")}
db = SqlSoup(options["sql_connection"]) db = SqlSoup(options["sql_connection"])
LOG.info("Connecting to database \"%s\" on %s", 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. # under the License.
# @author: Isaku Yamahata # @author: Isaku Yamahata
import ConfigParser
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
import logging as LOG import logging as LOG
import os import os
from quantum.api.api_common import OperationalStatus from quantum.api.api_common import OperationalStatus
from quantum.common import exceptions as q_exc from quantum.common import exceptions as q_exc
from quantum.plugins.ryu.common import config
import quantum.db.api as db import quantum.db.api as db
from quantum.manager import find_config
from quantum.quantum_plugin_base import QuantumPluginBase from quantum.quantum_plugin_base import QuantumPluginBase
@ -55,24 +54,23 @@ class OVSQuantumPluginBase(QuantumPluginBase):
""" """
def __init__(self, conf_file, mod_file, configfile=None): def __init__(self, conf_file, mod_file, configfile=None):
super(OVSQuantumPluginBase, self).__init__() super(OVSQuantumPluginBase, self).__init__()
config = ConfigParser.ConfigParser()
if configfile is None: if configfile is None:
if conf_file and os.path.exists(conf_file): if os.path.exists(conf_file):
configfile = conf_file configfile = conf_file
else: else:
configfile = ( configfile = find_config(os.path.abspath(
find_config(os.path.abspath(os.path.dirname(mod_file)))) os.path.dirname(__file__)))
if configfile is None: if configfile is None:
raise Exception("Configuration file \"%s\" doesn't exist" % raise Exception("Configuration file \"%s\" doesn't exist" %
(configfile)) (configfile))
LOG.debug("Using configuration file: %s", configfile) LOG.debug("Using configuration file: %s" % configfile)
config.read(configfile) conf = config.parse(configfile)
LOG.debug("Config: %s", config) options = {"sql_connection": conf.DATABASE.sql_connection}
reconnect_interval = conf.DATABASE.reconnect_interval
options = {"sql_connection": config.get("DATABASE", "sql_connection")} options.update({"reconnect_interval": reconnect_interval})
db.configure_db(options) db.configure_db(options)
self.config = config self.conf = conf
# Subclass must set self.driver to its own OVSQuantumPluginDriverBase # Subclass must set self.driver to its own OVSQuantumPluginDriverBase
self.driver = None self.driver = None

View File

@ -19,7 +19,7 @@
from ryu.app import client from ryu.app import client
from ryu.app import rest_nw_id 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 from quantum.common import exceptions as q_exc
import quantum.db.api as db import quantum.db.api as db
from quantum.plugins.ryu import ofp_service_type 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 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): class OFPRyuDriver(ovs_quantum_plugin_base.OVSQuantumPluginDriverBase):
def __init__(self, config): def __init__(self, conf):
super(OFPRyuDriver, self).__init__() super(OFPRyuDriver, self).__init__()
ofp_con_host = config.get("OVS", "openflow-controller") ofp_con_host = conf.OVS.openflow_controller
ofp_api_host = config.get("OVS", "openflow-rest-api") ofp_api_host = conf.OVS.openflow_rest_api
if ofp_con_host is None or ofp_api_host is None: if ofp_con_host is None or ofp_api_host is None:
raise q_exc.Invalid("invalid configuration. check ryu.ini") 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): class RyuQuantumPlugin(ovs_quantum_plugin_base.OVSQuantumPluginBase):
def __init__(self, configfile=None): def __init__(self, configfile=None):
super(RyuQuantumPlugin, self).__init__(CONF_FILE, __file__, configfile) 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): class FakePluginDriver(ovs_quantum_plugin_base.OVSQuantumPluginDriverBase):
def __init__(self, config): def __init__(self, config):
super(FakePluginDriver, self).__init__() super(FakePluginDriver, self).__init__()
conf = config.parse(config)
self.conf = conf
def create_network(self, net): def create_network(self, net):
pass pass
@ -32,4 +34,4 @@ class FakePluginDriver(ovs_quantum_plugin_base.OVSQuantumPluginDriverBase):
class FakePlugin(ovs_quantum_plugin_base.OVSQuantumPluginBase): class FakePlugin(ovs_quantum_plugin_base.OVSQuantumPluginBase):
def __init__(self, configfile=None): def __init__(self, configfile=None):
super(FakePlugin, self).__init__(None, __file__, configfile) 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 uuid
import quantum.db.api as db 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.basetest import BaseRyuTest
from quantum.plugins.ryu.tests.unit import utils from quantum.plugins.ryu.tests.unit import utils
from quantum.plugins.ryu.tests.unit.utils import patch_fake_ryu_client 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 RyuDriverTest(BaseRyuTest):
"""Class conisting of OFPRyuDriver unit tests""" """Class conisting of OFPRyuDriver unit tests"""
def setUp(self): def setUp(self):
super(RyuDriverTest, self).setUp() super(RyuDriverTest, self).setUp()
self.conf = config.parse(CONF_FILE)
# fake up ryu.app.client and ryu.app.rest_nw_id # fake up ryu.app.client and ryu.app.rest_nw_id
# With those, plugin can be tested without ryu installed # With those, plugin can be tested without ryu installed
self.module_patcher = patch_fake_ryu_client() self.module_patcher = patch_fake_ryu_client()
@ -65,7 +70,7 @@ class RyuDriverTest(BaseRyuTest):
db.network_create('test', uuid0) db.network_create('test', uuid0)
from quantum.plugins.ryu import ryu_quantum_plugin 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.create_network(net1)
ryu_driver.delete_network(net1) ryu_driver.delete_network(net1)
self.mox.VerifyAll() self.mox.VerifyAll()

View File

@ -21,7 +21,7 @@ Policy engine for quantum. Largely copied from nova.
import os.path import os.path
from quantum.common import config from quantum.common.utils import find_config_file
from quantum.common import exceptions from quantum.common import exceptions
from quantum.openstack.common import policy from quantum.openstack.common import policy
@ -38,7 +38,7 @@ def reset():
def init(): def init():
global _POLICY_PATH global _POLICY_PATH
if not _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: if not _POLICY_PATH:
raise exceptions.PolicyNotFound(path=FLAGS.policy_file) raise exceptions.PolicyNotFound(path=FLAGS.policy_file)
with open(_POLICY_PATH) as f: with open(_POLICY_PATH) as f:

View File

@ -25,28 +25,19 @@ import sys
from quantum import service from quantum import service
from quantum.common import config from quantum.common import config
from quantum.openstack.common import cfg
from quantum.version import version_string 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(): def main():
oparser = optparse.OptionParser(version='%prog ' + version_string()) # the configuration will be read into the cfg.CONF global data structure
create_options(oparser) config.parse(sys.argv)
(options, args) = config.parse_options(oparser) 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: try:
quantum_service = service.serve_wsgi(service.QuantumApiService, quantum_service = service.serve_wsgi(service.QuantumApiService)
options=options,
args=args)
quantum_service.wait() quantum_service.wait()
except RuntimeError, e: except RuntimeError, e:
sys.exit("ERROR: %s" % e) sys.exit("ERROR: %s" % e)

View File

@ -18,7 +18,7 @@
import logging import logging
from quantum.common import config from quantum.common import config
from quantum.common import exceptions as exception from quantum.openstack.common import cfg
from quantum import wsgi 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.app_name = app_name
self.conf_file = conf_file
self.conf = conf
self.wsgi_app = None self.wsgi_app = None
def start(self): 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): def wait(self):
self.wsgi_app.wait() self.wsgi_app.wait()
@ -51,13 +49,8 @@ class QuantumApiService(WsgiService):
"""Class for quantum-api service.""" """Class for quantum-api service."""
@classmethod @classmethod
def create(cls, conf=None, options=None, args=None): def create(cls):
app_name = "quantum" 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 # Setup logging early, supplying both the CLI options and the
# configuration mapping from the config file # configuration mapping from the config file
@ -65,32 +58,24 @@ class QuantumApiService(WsgiService):
# flags. Everything else must be set up in the conf file... # flags. Everything else must be set up in the conf file...
# Log the options used when starting if we're in debug mode... # Log the options used when starting if we're in debug mode...
config.setup_logging(options, conf) config.setup_logging(cfg.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
LOG.debug("*" * 80) LOG.debug("*" * 80)
LOG.debug("Configuration options gathered from config file:") LOG.debug("Configuration options gathered from config file:")
LOG.debug(conf_file)
LOG.debug("================================================") 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')]) if k not in ('__file__', 'here')])
for key, value in sorted(items.items()): for key, value in sorted(items.items()):
LOG.debug("%(key)-30s %(value)s" % {'key': key, LOG.debug("%(key)-30s %(value)s" % {'key': key,
'value': value, 'value': value,
}) })
LOG.debug("*" * 80) LOG.debug("*" * 80)
service = cls(app_name, conf_file, conf) service = cls(app_name)
return service return service
def serve_wsgi(cls, conf=None, options=None, args=None): def serve_wsgi(cls):
try: try:
service = cls.create(conf, options, args) service = cls.create()
except Exception: except Exception:
logging.exception('in WsgiService.create()') logging.exception('in WsgiService.create()')
raise raise
@ -100,15 +85,11 @@ def serve_wsgi(cls, conf=None, options=None, args=None):
return service return service
def _run_wsgi(app_name, paste_conf, paste_config_file): def _run_wsgi(app_name):
LOG.info(_('Using paste.deploy config at: %s'), paste_config_file) app = config.load_paste_app(app_name, "quantum.conf")
conf, app = config.load_paste_app(app_name,
{'config_file': paste_config_file},
None)
if not app: if not app:
LOG.error(_('No known API applications configured in %s.'), LOG.error(_('No known API applications configured.'))
paste_config_file)
return return
server = wsgi.Server("Quantum") 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 return server

View File

@ -21,19 +21,30 @@ import logging
import unittest2 as unittest import unittest2 as unittest
import mock import mock
import os
from quantum.api.api_common import APIFaultWrapper from quantum.api.api_common import APIFaultWrapper
from quantum.api.networks import Controller from quantum.api.networks import Controller
from quantum.common.test_lib import test_config from quantum.common.test_lib import test_config
from quantum.common import config
from quantum.db import api as db from quantum.db import api as db
from quantum.openstack.common import importutils from quantum.openstack.common import importutils
import quantum.tests.unit.testlib_api as testlib import quantum.tests.unit.testlib_api as testlib
from quantum.openstack.common import cfg
from quantum.wsgi import XMLDeserializer, JSONDeserializer from quantum.wsgi import XMLDeserializer, JSONDeserializer
LOG = logging.getLogger('quantum.tests.test_api') 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" NETS = "networks"
PORTS = "ports" PORTS = "ports"
ATTS = "attachments" ATTS = "attachments"
@ -105,10 +116,13 @@ class AbstractAPITest(unittest.TestCase):
self.assertEqual(put_attachment_res.status_int, expected_res_status) self.assertEqual(put_attachment_res.status_int, expected_res_status)
def setUp(self, api_router_klass, xml_metadata_dict): def setUp(self, api_router_klass, xml_metadata_dict):
options = {} # Create the default configurations
options['plugin_provider'] = test_config['plugin_name'] 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) 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.tenant_id = "test_tenant"
self.network_name = "test_network" self.network_name = "test_network"
@ -136,6 +150,7 @@ class AbstractAPITest(unittest.TestCase):
"""Clear the test environment""" """Clear the test environment"""
# Remove database contents # Remove database contents
db.clear_db() db.clear_db()
cfg.CONF.reset()
class BaseAPIOperationsTest(AbstractAPITest): class BaseAPIOperationsTest(AbstractAPITest):

View File

@ -12,6 +12,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the spec # License for the spec
import os
import logging import logging
import unittest import unittest
import uuid 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 resource as wsgi_resource
from quantum.api.v2 import router from quantum.api.v2 import router
from quantum.api.v2 import views from quantum.api.v2 import views
from quantum.common import config
from quantum.openstack.common import cfg
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -34,6 +37,13 @@ LOG = logging.getLogger(__name__)
def _uuid(): def _uuid():
return str(uuid.uuid4()) 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): def _get_path(resource, id=None, fmt=None):
path = '/%s' % resource path = '/%s' % resource
@ -123,16 +133,23 @@ class APIv2TestCase(unittest.TestCase):
# will get around this. # will get around this.
def setUp(self): def setUp(self):
plugin = 'quantum.quantum_plugin_base_v2.QuantumPluginBaseV2' 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_patcher = mock.patch(plugin, autospec=True)
self.plugin = self._plugin_patcher.start() self.plugin = self._plugin_patcher.start()
api = router.APIRouter({'plugin_provider': plugin}) api = router.APIRouter()
self.api = webtest.TestApp(api) self.api = webtest.TestApp(api)
def tearDown(self): def tearDown(self):
self._plugin_patcher.stop() self._plugin_patcher.stop()
self.api = None self.api = None
self.plugin = None self.plugin = None
cfg.CONF.reset()
def test_verbose_attr(self): def test_verbose_attr(self):
instance = self.plugin.return_value instance = self.plugin.return_value

View File

@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import os
import logging import logging
import unittest import unittest
import contextlib import contextlib
@ -22,12 +23,21 @@ import quantum
from quantum.api.v2.router import APIRouter from quantum.api.v2.router import APIRouter
from quantum.common import exceptions as q_exc from quantum.common import exceptions as q_exc
from quantum.db import api as db 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.tests.unit.testlib_api import create_request
from quantum.wsgi import Serializer, JSONDeserializer from quantum.wsgi import Serializer, JSONDeserializer
LOG = logging.getLogger(__name__) 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): class QuantumDbPluginV2TestCase(unittest.TestCase):
def setUp(self): def setUp(self):
@ -46,7 +56,12 @@ class QuantumDbPluginV2TestCase(unittest.TestCase):
} }
plugin = 'quantum.db.db_base_plugin_v2.QuantumDbPluginV2' 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): def tearDown(self):
super(QuantumDbPluginV2TestCase, self).tearDown() super(QuantumDbPluginV2TestCase, self).tearDown()
@ -54,6 +69,7 @@ class QuantumDbPluginV2TestCase(unittest.TestCase):
# doesn't like when the plugin changes ;) # doesn't like when the plugin changes ;)
db._ENGINE = None db._ENGINE = None
db._MAKER = None db._MAKER = None
cfg.CONF.reset()
def _req(self, method, resource, data=None, fmt='json', id=None): def _req(self, method, resource, data=None, fmt='json', id=None):
if id: if id:

View File

@ -14,6 +14,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import logging
import os
import unittest import unittest
import routes import routes
@ -41,8 +43,15 @@ from quantum.tests.unit.extension_stubs import (
import quantum.tests.unit.extensions import quantum.tests.unit.extensions
from quantum import wsgi 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__) extensions_path = ':'.join(quantum.tests.unit.extensions.__path__)
@ -455,8 +464,10 @@ def app_factory(global_conf, **local_conf):
def setup_base_app(): def setup_base_app():
options = {'config_file': test_conf_file} config_file = 'quantum.conf.test'
conf, app = config.load_paste_app('extensions_test_app', options, None) args = ['--config-file', etcdir(config_file)]
config.parse(args=args)
app = config.load_paste_app('extensions_test_app', config_file)
return app return app
@ -464,9 +475,11 @@ def setup_extensions_middleware(extension_manager=None):
extension_manager = (extension_manager or extension_manager = (extension_manager or
PluginAwareExtensionManager(extensions_path, PluginAwareExtensionManager(extensions_path,
QuantumEchoPlugin())) QuantumEchoPlugin()))
options = {'config_file': test_conf_file} config_file = 'quantum.conf.test'
conf, app = config.load_paste_app('extensions_test_app', options, None) args = ['--config-file', etcdir(config_file)]
return ExtensionMiddleware(app, conf, ext_mgr=extension_manager) 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): def setup_extensions_test_app(extension_manager=None):

View File

@ -51,7 +51,7 @@ ryu_plugin_config_path = 'etc/quantum/plugins/ryu'
DataFiles = [ DataFiles = [
(config_path, (config_path,
['etc/quantum.conf', 'etc/quantum.conf.test', 'etc/plugins.ini']), ['etc/quantum.conf']),
(init_path, ['etc/init.d/quantum-server']), (init_path, ['etc/init.d/quantum-server']),
(ovs_plugin_config_path, (ovs_plugin_config_path,
['etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini']), ['etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini']),