Configuration options may be set on the command line or in config files.
The schema for each option is defined using the Opt class or its sub-classes, for example:
from oslo_config import cfg
from oslo_config import types
PortType = types.Integer(1, 65535)
common_opts = [
cfg.StrOpt('bind_host',
default='0.0.0.0',
help='IP address to listen on.'),
cfg.Opt('bind_port',
type=PortType(),
default=9292,
help='Port number to listen on.')
]
Options can have arbitrary types via the type constructor to Opt. The type constructor is a callable object that takes a string and either returns a value of that particular type or raises ValueError if the value can not be converted.
There are predefined types in oslo_config.cfg : strings, integers, floats, booleans, lists, ‘multi strings’ and ‘key/value pairs’ (dictionary)
enabled_apis_opt = cfg.ListOpt('enabled_apis',
default=['ec2', 'osapi_compute'],
help='List of APIs to enable by default.')
DEFAULT_EXTENSIONS = [
'nova.api.openstack.compute.contrib.standard_extensions'
]
osapi_compute_extension_opt = cfg.MultiStrOpt('osapi_compute_extension',
default=DEFAULT_EXTENSIONS)
Option schemas are registered with the config manager at runtime, but before the option is referenced:
class ExtensionManager(object):
enabled_apis_opt = cfg.ListOpt(...)
def __init__(self, conf):
self.conf = conf
self.conf.register_opt(enabled_apis_opt)
...
def _load_extensions(self):
for ext_factory in self.conf.osapi_compute_extension:
....
A common usage pattern is for each option schema to be defined in the module or class which uses the option:
opts = ...
def add_common_opts(conf):
conf.register_opts(opts)
def get_bind_host(conf):
return conf.bind_host
def get_bind_port(conf):
return conf.bind_port
An option may optionally be made available via the command line. Such options must be registered with the config manager before the command line is parsed (for the purposes of –help and CLI arg validation):
cli_opts = [
cfg.BoolOpt('verbose',
short='v',
default=False,
help='Print more verbose output.'),
cfg.BoolOpt('debug',
short='d',
default=False,
help='Print debugging output.'),
]
def add_common_opts(conf):
conf.register_cli_opts(cli_opts)
The config manager has two CLI options defined by default, –config-file and –config-dir:
class ConfigOpts(object):
def __call__(self, ...):
opts = [
MultiStrOpt('config-file',
...),
StrOpt('config-dir',
...),
]
self.register_cli_opts(opts)
Option values are parsed from any supplied config files using oslo_config.iniparser. If none are specified, a default set is used for example glance-api.conf and glance-common.conf:
glance-api.conf:
[DEFAULT]
bind_port = 9292
glance-common.conf:
[DEFAULT]
bind_host = 0.0.0.0
Option values in config files and those on the command line are parsed in order. The same option can appear many times, in config files or on the command line. Later values always override earlier ones.
The parsing of CLI args and config files is initiated by invoking the config manager for example:
conf = ConfigOpts()
conf.register_opt(BoolOpt('verbose', ...))
conf(sys.argv[1:])
if conf.verbose:
...
Options can be registered as belonging to a group:
rabbit_group = cfg.OptGroup(name='rabbit',
title='RabbitMQ options')
rabbit_host_opt = cfg.StrOpt('host',
default='localhost',
help='IP/hostname to listen on.'),
rabbit_port_opt = cfg.IntOpt('port',
default=5672,
help='Port number to listen on.')
def register_rabbit_opts(conf):
conf.register_group(rabbit_group)
# options can be registered under a group in either of these ways:
conf.register_opt(rabbit_host_opt, group=rabbit_group)
conf.register_opt(rabbit_port_opt, group='rabbit')
If no group attributes are required other than the group name, the group need not be explicitly registered for example:
def register_rabbit_opts(conf):
# The group will automatically be created, equivalent calling::
# conf.register_group(OptGroup(name='rabbit'))
conf.register_opt(rabbit_port_opt, group='rabbit')
If no group is specified, options belong to the ‘DEFAULT’ section of config files:
glance-api.conf:
[DEFAULT]
bind_port = 9292
...
[rabbit]
host = localhost
port = 5672
use_ssl = False
userid = guest
password = guest
virtual_host = /
Command-line options in a group are automatically prefixed with the group name:
--rabbit-host localhost --rabbit-port 9999
Option values in the default group are referenced as attributes/properties on the config manager; groups are also attributes on the config manager, with attributes for each of the options associated with the group:
server.start(app, conf.bind_port, conf.bind_host, conf)
self.connection = kombu.connection.BrokerConnection(
hostname=conf.rabbit.host,
port=conf.rabbit.port,
...)
Option values may reference other values using PEP 292 string substitution:
opts = [
cfg.StrOpt('state_path',
default=os.path.join(os.path.dirname(__file__), '../'),
help='Top-level directory for maintaining nova state.'),
cfg.StrOpt('sqlite_db',
default='nova.sqlite',
help='File name for SQLite.'),
cfg.StrOpt('sql_connection',
default='sqlite:///$state_path/$sqlite_db',
help='Connection string for SQL database.'),
]
Note
Interpolation can be avoided by using $$.
Warning
Interpolation using the values of options in groups is not yet supported. The interpolated option must be in the DEFAULT group (i.e., "$state_path" works but "$database.state_path" does not).
Options may be declared as required so that an error is raised if the user does not supply a value for the option:
opts = [
cfg.StrOpt('service_name', required=True),
cfg.StrOpt('image_id', required=True),
...
]
Options may be declared as secret so that their values are not leaked into log files:
opts = [
cfg.StrOpt('s3_store_access_key', secret=True),
cfg.StrOpt('s3_store_secret_key', secret=True),
...
]
This module also contains a global instance of the ConfigOpts class in order to support a common usage pattern in OpenStack:
from oslo_config 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)
Positional command line arguments are supported via a ‘positional’ Opt constructor argument:
>>> conf = ConfigOpts()
>>> conf.register_cli_opt(MultiStrOpt('bar', positional=True))
True
>>> conf(['a', 'b'])
>>> conf.bar
['a', 'b']
It is also possible to use argparse “sub-parsers” to parse additional command line arguments using the SubCommandOpt class:
>>> def add_parsers(subparsers):
... list_action = subparsers.add_parser('list')
... list_action.add_argument('id')
...
>>> conf = ConfigOpts()
>>> conf.register_cli_opt(SubCommandOpt('action', handler=add_parsers))
True
>>> conf(args=['list', '10'])
>>> conf.action.name, conf.action.id
('list', '10')