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