1
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright 2011 Red Hat, Inc.
3
# Copyright 2012 Red Hat, Inc.
5
5
# Licensed under the Apache License, Version 2.0 (the "License"); you may
6
6
# not use this file except in compliance with the License. You may obtain
90
90
def add_common_opts(conf):
91
91
conf.register_cli_opts(cli_opts)
93
The config manager has a single CLI option defined by default, --config-file::
93
The config manager has two CLI options defined by default, --config-file
95
96
class ConfigOpts(object):
97
config_file_opt = MultiStrOpt('config-file',
100
98
def __init__(self, ...):
102
self.register_cli_opt(self.config_file_opt)
101
MultiStrOpt('config-file',
107
self.register_cli_opts(opts)
104
109
Option values are parsed from any supplied config files using
105
110
openstack.common.iniparser. If none are specified, a default set is used
129
134
Options can be registered as belonging to a group::
131
rabbit_group = cfg.OptionGroup(name='rabbit',
132
title='RabbitMQ options')
136
rabbit_group = cfg.OptGroup(name='rabbit',
137
title='RabbitMQ options')
134
139
rabbit_host_opt = cfg.StrOpt('host',
135
140
default='localhost',
321
def find_config_files(project=None, prog=None):
328
def _get_config_dirs(project=None):
329
"""Return a list of directors where config files may be located.
331
:param project: an optional project name
333
If a project is specified, following directories are returned::
340
Otherwise, these directories::
345
fix_path = lambda p: os.path.abspath(os.path.expanduser(p))
348
fix_path(os.path.join('~', '.' + project)) if project else None,
350
os.path.join('/etc', project) if project else None,
354
return filter(bool, cfg_dirs)
357
def _search_dirs(dirs, basename, extension=""):
358
"""Search a list of directories for a given filename.
360
Iterator over the supplied directories, returning the first file
361
found with the supplied name and extension.
363
:param dirs: a list of directories
364
:param basename: the filename, e.g. 'glance-api'
365
:param extension: the file extension, e.g. '.conf'
366
:returns: the path to a matching file, or None
369
path = os.path.join(d, '%s%s' % (basename, extension))
370
if os.path.exists(path):
374
def find_config_files(project=None, prog=None, extension='.conf'):
322
375
"""Return a list of default configuration files.
324
377
:param project: an optional project name
325
378
:param prog: the program name, defaulting to the basename of sys.argv[0]
379
:param extension: the type of the config file
327
381
We default to two config files: [${project}.conf, ${prog}.conf]
346
400
prog = os.path.basename(sys.argv[0])
348
fix_path = lambda p: os.path.abspath(os.path.expanduser(p))
351
fix_path(os.path.join('~', '.' + project)) if project else None,
353
os.path.join('/etc', project) if project else None,
356
cfg_dirs = filter(bool, cfg_dirs)
358
def search_dirs(dirs, basename):
360
path = os.path.join(d, basename)
361
if os.path.exists(path):
402
cfg_dirs = _get_config_dirs(project)
364
404
config_files = []
366
config_files.append(search_dirs(cfg_dirs, '%s.conf' % project))
367
config_files.append(search_dirs(cfg_dirs, '%s.conf' % prog))
406
config_files.append(_search_dirs(cfg_dirs, project, extension))
407
config_files.append(_search_dirs(cfg_dirs, prog, extension))
369
409
return filter(bool, config_files)
821
861
usage=self.usage)
822
862
self._cparser = None
824
self.register_cli_opt(
825
MultiStrOpt('config-file',
826
default=self.default_config_files,
828
help='Path to a config file to use. Multiple config '
829
'files can be specified, with values in later '
830
'files taking precedence. The default files used '
831
'are: %s' % (self.default_config_files, )))
867
MultiStrOpt('config-file',
868
default=self.default_config_files,
870
help='Path to a config file to use. Multiple config '
871
'files can be specified, with values in later '
872
'files taking precedence. The default files '
874
(self.default_config_files, )),
877
help='Path to a config directory to pull *.conf '
878
'files from. This file set is sorted, so as to '
879
'provide a predictable parse order if individual '
880
'options are over-ridden. The set is parsed after '
881
'the file(s), if any, specified via --config-file, '
882
'hence over-ridden options in the directory take '
885
self.register_cli_opts(opts)
887
def __clear_cache(f):
889
def __inner(self, *args, **kwargs):
890
if kwargs.pop('clear_cache', True):
892
return f(self, *args, **kwargs)
833
896
def __call__(self, args=None):
834
897
"""Parse command line arguments and config files.
840
903
The object may be called multiple times, each time causing the previous
841
904
set of values to be overwritten.
906
If the --config-dir option is set, any *.conf files from this
907
directory are pulled in, after all the file(s) specified by the
908
--config-file option.
843
910
:params args: command line arguments (defaults to sys.argv[1:])
844
911
:returns: the list of arguments left over after parsing options
845
912
:raises: SystemExit, ConfigFilesNotFoundError, ConfigFileParseError
853
920
self._cli_values = vars(values)
856
self._parse_config_files(self.config_file)
922
def _list_config_dir():
923
return sorted(glob.glob(os.path.join(self.config_dir, '*.conf')))
925
from_file = list(self.config_file)
927
from_dir = _list_config_dir() if self.config_dir else []
929
self._parse_config_files(from_file + from_dir)
864
937
:returns: the option value (after string subsititution) or a GroupAttr
865
938
:raises: NoSuchOptError,ConfigFileValueError,TemplateSubstitutionError
867
return self._substitute(self._get(name))
940
return self._get(name)
869
942
def __getitem__(self, key):
870
943
"""Look up an option value and perform string substitution."""
883
956
"""Return the number of options and option groups."""
884
957
return len(self._opts) + len(self._groups)
887
961
"""Reset the state of the object to before it was called."""
888
962
self._args = None
889
963
self._cli_values = None
890
964
self._cparser = None
892
967
def register_opt(self, opt, group=None):
893
968
"""Register an option schema.
914
990
def register_opts(self, opts, group=None):
915
991
"""Register multiple option schemas at once."""
917
self.register_opt(opt, group)
993
self.register_opt(opt, group, clear_cache=False)
919
996
def register_cli_opt(self, opt, group=None):
920
997
"""Register a CLI option schema.
931
1008
if self._args is not None:
932
1009
raise ArgsAlreadyParsedError("cannot register CLI option")
934
if not self.register_opt(opt, group):
1011
if not self.register_opt(opt, group, clear_cache=False):
937
1014
if group is not None:
944
1022
def register_cli_opts(self, opts, group=None):
945
1023
"""Register multiple CLI option schemas at once."""
946
1024
for opt in opts:
947
self.register_cli_opt(opt, group)
1025
self.register_cli_opt(opt, group, clear_cache=False)
949
1027
def register_group(self, group):
950
1028
"""Register an option group.
960
1038
self._groups[group.name] = copy.copy(group)
962
1041
def set_override(self, name, override, group=None):
963
1042
"""Override an opt value.
973
1052
opt_info = self._get_opt_info(name, group)
974
1053
opt_info['override'] = override
976
1056
def set_default(self, name, default, group=None):
977
1057
"""Override an opt's default value.
1012
1092
This it the default behaviour."""
1013
1093
self._oparser.enable_interspersed_args()
1095
def find_file(self, name):
1096
"""Locate a file located alongside the config files.
1098
Search for a file with the supplied basename in the directories
1099
which we have already loaded config files from and other known
1100
configuration directories.
1102
The directory, if any, supplied by the config_dir option is
1103
searched first. Then the config_file option is iterated over
1104
and each of the base directories of the config_files values
1105
are searched. Failing both of these, the standard directories
1106
searched by the module level find_config_files() function is
1107
used. The first matching file is returned.
1109
:param basename: the filename, e.g. 'policy.json'
1110
:returns: the path to a matching file, or None
1114
dirs.append(self.config_dir)
1116
for cf in reversed(self.config_file):
1117
dirs.append(os.path.dirname(cf))
1119
dirs.extend(_get_config_dirs(self.project))
1121
return _search_dirs(dirs, name)
1015
1123
def log_opt_values(self, logger, lvl):
1016
1124
"""Log the value of all registered opts.
1056
1164
self._oparser.print_help(file)
1058
1166
def _get(self, name, group=None):
1167
if isinstance(group, OptGroup):
1168
key = (group.name, name)
1172
return self.__cache[key]
1174
value = self._substitute(self._do_get(name, group))
1175
self.__cache[key] = value
1178
def _do_get(self, name, group=None):
1059
1179
"""Look up an option value.
1061
1181
:param name: the opt name (or 'dest', more precisely)
1197
1317
def __getattr__(self, name):
1198
1318
"""Look up an option value and perform template substitution."""
1199
return self.conf._substitute(self.conf._get(name, self.group))
1319
return self.conf._get(name, self.group)
1201
1321
def __getitem__(self, key):
1202
1322
"""Look up an option value and perform string substitution."""