8fa310cc7a
This patch uses cfg.CONF as ConfigOpts in common/config instead of creating a new instance of it. This is needed since most of Oslo's modules use the global CONF object to register their config parameters. The patch also moves log.setup call into Bootstrap and calls it after configs are loaded. Fixes bug: #1190524 Change-Id: Ib601418a7dbcad84c79b640b93bc5798ec4a62c4
201 lines
5.7 KiB
Python
201 lines
5.7 KiB
Python
# Copyright (c) 2013 Rackspace, 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.
|
|
|
|
"""Decentralized configuration module.
|
|
|
|
A config variable `foo` is a read-only property accessible through
|
|
|
|
CFG.foo
|
|
|
|
, where `CFG` is either a global configuration accessible through
|
|
|
|
CFG = config.project('marconi').from_options(
|
|
foo=("bar", "usage"),
|
|
...)
|
|
|
|
, or a local configuration associated with a namespace
|
|
|
|
CFG = config.namespace('drivers:transport:wsgi').from_options(
|
|
port=80,
|
|
...)
|
|
|
|
The `from_options` call accepts a list of option definition, where each
|
|
option is represented as a keyword argument, in the form of either
|
|
`name=default` or `name=(default, description)`, where `name` is the
|
|
name of the option in a valid Python identifier, and `default` is the
|
|
default value of that option.
|
|
|
|
Configurations can be read from an INI file, where the global options
|
|
are under the `[DEFAULT]` section, and the local options are under the
|
|
sections named by their associated namespaces.
|
|
|
|
To load the configurations from a file:
|
|
|
|
PROJECT_CFG = config.project('marconi')
|
|
PROJECT_CFG.load(filename="/path/to/example.conf")
|
|
|
|
A call to `.load` without a filename looks up for the default ones:
|
|
|
|
~/.marconi/marconi.conf
|
|
/etc/marconi/marconi.conf
|
|
|
|
Global config variables, if any, can also be read from the command line
|
|
arguments:
|
|
|
|
PROJECT_CFG.load(filename="example.conf", args=sys.argv[1:])
|
|
"""
|
|
|
|
from oslo.config import cfg
|
|
|
|
|
|
def _init():
|
|
"""Enclose an API specific config object."""
|
|
|
|
class ConfigProxy(object):
|
|
"""Prototype of the opaque config variable accessors."""
|
|
pass
|
|
|
|
class Obj(dict):
|
|
__getattr__ = dict.__getitem__
|
|
__setattr__ = dict.__setitem__
|
|
|
|
conf = cfg.CONF
|
|
|
|
def namespace(name, title=None):
|
|
"""Create a config namespace.
|
|
|
|
:param name: the section name appears in the .ini file
|
|
:param title: an optional description
|
|
:returns: the option object for the namespace
|
|
"""
|
|
|
|
grp = cfg.OptGroup(name, title)
|
|
conf.register_group(grp)
|
|
|
|
def from_options(**opts):
|
|
"""Define options under the associated namespace.
|
|
|
|
:returns: ConfigProxy of the associated namespace
|
|
"""
|
|
|
|
for k, v in opts.items():
|
|
conf.register_opt(_make_opt(k, v), group=grp)
|
|
|
|
def from_class(cls):
|
|
grant_access_to_class(conf[grp.name], cls)
|
|
return cls
|
|
|
|
return from_class(opaque_type_of(ConfigProxy, grp.name))()
|
|
|
|
return Obj(from_options=from_options)
|
|
|
|
def project(name=None):
|
|
"""Access the global namespace.
|
|
|
|
:param name: the name of the project
|
|
:returns: a global option object
|
|
"""
|
|
|
|
def from_options(**opts):
|
|
"""Define options under the global namespace.
|
|
|
|
:returns: ConfigProxy of the global namespace
|
|
"""
|
|
|
|
for k, v in opts.items():
|
|
conf.register_cli_opt(_make_opt(k, v))
|
|
|
|
def from_class(cls):
|
|
grant_access_to_class(conf, cls)
|
|
return cls
|
|
|
|
return from_class(opaque_type_of(ConfigProxy, name))()
|
|
|
|
def load(filename=None, args=None):
|
|
"""Load the configurations from a config file.
|
|
|
|
If the file name is not supplied, look for
|
|
|
|
~/.%project/%project.conf
|
|
|
|
and
|
|
|
|
/etc/%project/%project.conf
|
|
|
|
:param filename: the name of an alternative config file
|
|
:param args: command line arguments
|
|
"""
|
|
|
|
args = [] if args is None else args
|
|
|
|
if filename is None:
|
|
conf(args=args, project=name, prog=name)
|
|
else:
|
|
conf(args=args, default_config_files=[filename])
|
|
|
|
return Obj(from_options=from_options, load=load, conf=conf)
|
|
|
|
def opaque_type_of(base, postfix):
|
|
return type('%s of %s' % (base.__name__, postfix), (base,), {})
|
|
|
|
def grant_access_to_class(pairs, cls):
|
|
for k in pairs:
|
|
# A closure is needed for each %k to let
|
|
# different properties access different %k.
|
|
def let(k=k):
|
|
setattr(cls, k, property(lambda obj: pairs[k]))
|
|
let()
|
|
|
|
return namespace, project
|
|
|
|
|
|
namespace, project = _init()
|
|
|
|
|
|
def _make_opt(name, default):
|
|
"""Create an oslo.config option with type deduction
|
|
|
|
The type for the option is deduced from the %default value given
|
|
for that option. A default value of None is deduced to Opt.
|
|
|
|
Note: MultiStrOpt is not supported.
|
|
|
|
:param name: the name of the option in a valid Python identifier
|
|
:param default: the default value of the option, or (default, description)
|
|
:raises: cfg.Error if the type can not be deduced.
|
|
"""
|
|
|
|
deduction = {
|
|
str: cfg.StrOpt,
|
|
bool: cfg.BoolOpt,
|
|
int: cfg.IntOpt,
|
|
long: cfg.IntOpt,
|
|
float: cfg.FloatOpt,
|
|
list: cfg.ListOpt,
|
|
}
|
|
|
|
if type(default) is tuple:
|
|
default, help = default
|
|
else:
|
|
help = None
|
|
|
|
if default is None:
|
|
return cfg.Opt(name, help=help)
|
|
|
|
try:
|
|
return deduction[type(default)](name, help=help, default=default)
|
|
except KeyError:
|
|
raise cfg.Error("unrecognized option type")
|