~ubuntu-branches/ubuntu/saucy/nova/saucy-proposed

« back to all changes in this revision

Viewing changes to nova/openstack/common/cfg.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2012-05-24 13:12:53 UTC
  • mfrom: (1.1.55)
  • Revision ID: package-import@ubuntu.com-20120524131253-ommql08fg1en06ut
Tags: 2012.2~f1-0ubuntu1
* New upstream release.
* Prepare for quantal:
  - Dropped debian/patches/upstream/0006-Use-project_id-in-ec2.cloud._format_image.patch
  - Dropped debian/patches/upstream/0005-Populate-image-properties-with-project_id-again.patch
  - Dropped debian/patches/upstream/0004-Fixed-bug-962840-added-a-test-case.patch
  - Dropped debian/patches/upstream/0003-Allow-unprivileged-RADOS-users-to-access-rbd-volumes.patch
  - Dropped debian/patches/upstream/0002-Stop-libvirt-test-from-deleting-instances-dir.patch
  - Dropped debian/patches/upstream/0001-fix-bug-where-nova-ignores-glance-host-in-imageref.patch 
  - Dropped debian/patches/0001-fix-useexisting-deprecation-warnings.patch
* debian/control: Add python-keystone as a dependency. (LP: #907197)
* debian/patches/kombu_tests_timeout.patch: Refreshed.
* debian/nova.conf, debian/nova-common.postinst: Convert to new ini
  file configuration
* debian/patches/nova-manage_flagfile_location.patch: Refreshed

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
2
 
3
 
# Copyright 2011 Red Hat, Inc.
 
3
# Copyright 2012 Red Hat, Inc.
4
4
#
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)
92
92
 
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
 
94
and --config-dir::
94
95
 
95
96
    class ConfigOpts(object):
96
97
 
97
 
        config_file_opt = MultiStrOpt('config-file',
98
 
                                      ...
99
 
 
100
98
        def __init__(self, ...):
101
 
            ...
102
 
            self.register_cli_opt(self.config_file_opt)
 
99
 
 
100
            opts = [
 
101
                MultiStrOpt('config-file',
 
102
                        ...),
 
103
                StrOpt('config-dir',
 
104
                       ...),
 
105
            ]
 
106
 
 
107
            self.register_cli_opts(opts)
103
108
 
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
128
133
 
129
134
Options can be registered as belonging to a group::
130
135
 
131
 
    rabbit_group = cfg.OptionGroup(name='rabbit',
132
 
                                   title='RabbitMQ options')
 
136
    rabbit_group = cfg.OptGroup(name='rabbit',
 
137
                                title='RabbitMQ options')
133
138
 
134
139
    rabbit_host_opt = cfg.StrOpt('host',
135
140
                                 default='localhost',
221
226
 
222
227
import collections
223
228
import copy
 
229
import functools
 
230
import glob
224
231
import optparse
225
232
import os
226
233
import string
227
234
import sys
228
235
 
229
 
import iniparser
 
236
from nova.openstack.common import iniparser
230
237
 
231
238
 
232
239
class Error(Exception):
318
325
    pass
319
326
 
320
327
 
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.
 
330
 
 
331
    :param project: an optional project name
 
332
 
 
333
    If a project is specified, following directories are returned::
 
334
 
 
335
      ~/.${project}/
 
336
      ~/
 
337
      /etc/${project}/
 
338
      /etc/
 
339
 
 
340
    Otherwise, these directories::
 
341
 
 
342
      ~/
 
343
      /etc/
 
344
    """
 
345
    fix_path = lambda p: os.path.abspath(os.path.expanduser(p))
 
346
 
 
347
    cfg_dirs = [
 
348
        fix_path(os.path.join('~', '.' + project)) if project else None,
 
349
        fix_path('~'),
 
350
        os.path.join('/etc', project) if project else None,
 
351
        '/etc'
 
352
        ]
 
353
 
 
354
    return filter(bool, cfg_dirs)
 
355
 
 
356
 
 
357
def _search_dirs(dirs, basename, extension=""):
 
358
    """Search a list of directories for a given filename.
 
359
 
 
360
    Iterator over the supplied directories, returning the first file
 
361
    found with the supplied name and extension.
 
362
 
 
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
 
367
    """
 
368
    for d in dirs:
 
369
        path = os.path.join(d, '%s%s' % (basename, extension))
 
370
        if os.path.exists(path):
 
371
            return path
 
372
 
 
373
 
 
374
def find_config_files(project=None, prog=None, extension='.conf'):
322
375
    """Return a list of default configuration files.
323
376
 
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
326
380
 
327
381
    We default to two config files: [${project}.conf, ${prog}.conf]
328
382
 
345
399
    if prog is None:
346
400
        prog = os.path.basename(sys.argv[0])
347
401
 
348
 
    fix_path = lambda p: os.path.abspath(os.path.expanduser(p))
349
 
 
350
 
    cfg_dirs = [
351
 
        fix_path(os.path.join('~', '.' + project)) if project else None,
352
 
        fix_path('~'),
353
 
        os.path.join('/etc', project) if project else None,
354
 
        '/etc'
355
 
        ]
356
 
    cfg_dirs = filter(bool, cfg_dirs)
357
 
 
358
 
    def search_dirs(dirs, basename):
359
 
        for d in dirs:
360
 
            path = os.path.join(d, basename)
361
 
            if os.path.exists(path):
362
 
                return path
 
402
    cfg_dirs = _get_config_dirs(project)
363
403
 
364
404
    config_files = []
365
405
    if project:
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))
368
408
 
369
409
    return filter(bool, config_files)
370
410
 
821
861
                                              usage=self.usage)
822
862
        self._cparser = None
823
863
 
824
 
        self.register_cli_opt(
825
 
            MultiStrOpt('config-file',
826
 
                        default=self.default_config_files,
827
 
                        metavar='PATH',
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, )))
 
864
        self.__cache = {}
 
865
 
 
866
        opts = [
 
867
             MultiStrOpt('config-file',
 
868
                         default=self.default_config_files,
 
869
                         metavar='PATH',
 
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 '
 
873
                              ' used are: %s' %
 
874
                              (self.default_config_files, )),
 
875
            StrOpt('config-dir',
 
876
                   metavar='DIR',
 
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 '
 
883
                        'precedence.'),
 
884
            ]
 
885
        self.register_cli_opts(opts)
 
886
 
 
887
    def __clear_cache(f):
 
888
        @functools.wraps(f)
 
889
        def __inner(self, *args, **kwargs):
 
890
            if kwargs.pop('clear_cache', True):
 
891
                self.__cache.clear()
 
892
            return f(self, *args, **kwargs)
 
893
 
 
894
        return __inner
832
895
 
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.
842
905
 
 
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.
 
909
 
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
852
919
 
853
920
        self._cli_values = vars(values)
854
921
 
855
 
        if self.config_file:
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')))
 
924
 
 
925
        from_file = list(self.config_file)
 
926
 
 
927
        from_dir = _list_config_dir() if self.config_dir else []
 
928
 
 
929
        self._parse_config_files(from_file + from_dir)
857
930
 
858
931
        return args
859
932
 
864
937
        :returns: the option value (after string subsititution) or a GroupAttr
865
938
        :raises: NoSuchOptError,ConfigFileValueError,TemplateSubstitutionError
866
939
        """
867
 
        return self._substitute(self._get(name))
 
940
        return self._get(name)
868
941
 
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)
885
958
 
 
959
    @__clear_cache
886
960
    def reset(self):
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
891
965
 
 
966
    @__clear_cache
892
967
    def register_opt(self, opt, group=None):
893
968
        """Register an option schema.
894
969
 
911
986
 
912
987
        return True
913
988
 
 
989
    @__clear_cache
914
990
    def register_opts(self, opts, group=None):
915
991
        """Register multiple option schemas at once."""
916
992
        for opt in opts:
917
 
            self.register_opt(opt, group)
 
993
            self.register_opt(opt, group, clear_cache=False)
918
994
 
 
995
    @__clear_cache
919
996
    def register_cli_opt(self, opt, group=None):
920
997
        """Register a CLI option schema.
921
998
 
931
1008
        if self._args is not None:
932
1009
            raise ArgsAlreadyParsedError("cannot register CLI option")
933
1010
 
934
 
        if not self.register_opt(opt, group):
 
1011
        if not self.register_opt(opt, group, clear_cache=False):
935
1012
            return False
936
1013
 
937
1014
        if group is not None:
941
1018
 
942
1019
        return True
943
1020
 
 
1021
    @__clear_cache
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)
948
1026
 
949
1027
    def register_group(self, group):
950
1028
        """Register an option group.
959
1037
 
960
1038
        self._groups[group.name] = copy.copy(group)
961
1039
 
 
1040
    @__clear_cache
962
1041
    def set_override(self, name, override, group=None):
963
1042
        """Override an opt value.
964
1043
 
973
1052
        opt_info = self._get_opt_info(name, group)
974
1053
        opt_info['override'] = override
975
1054
 
 
1055
    @__clear_cache
976
1056
    def set_default(self, name, default, group=None):
977
1057
        """Override an opt's default value.
978
1058
 
1012
1092
        This it the default behaviour."""
1013
1093
        self._oparser.enable_interspersed_args()
1014
1094
 
 
1095
    def find_file(self, name):
 
1096
        """Locate a file located alongside the config files.
 
1097
 
 
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.
 
1101
 
 
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.
 
1108
 
 
1109
        :param basename: the filename, e.g. 'policy.json'
 
1110
        :returns: the path to a matching file, or None
 
1111
        """
 
1112
        dirs = []
 
1113
        if self.config_dir:
 
1114
            dirs.append(self.config_dir)
 
1115
 
 
1116
        for cf in reversed(self.config_file):
 
1117
            dirs.append(os.path.dirname(cf))
 
1118
 
 
1119
        dirs.extend(_get_config_dirs(self.project))
 
1120
 
 
1121
        return _search_dirs(dirs, name)
 
1122
 
1015
1123
    def log_opt_values(self, logger, lvl):
1016
1124
        """Log the value of all registered opts.
1017
1125
 
1056
1164
        self._oparser.print_help(file)
1057
1165
 
1058
1166
    def _get(self, name, group=None):
 
1167
        if isinstance(group, OptGroup):
 
1168
            key = (group.name, name)
 
1169
        else:
 
1170
            key = (group, name)
 
1171
        try:
 
1172
            return self.__cache[key]
 
1173
        except KeyError:
 
1174
            value = self._substitute(self._do_get(name, group))
 
1175
            self.__cache[key] = value
 
1176
            return value
 
1177
 
 
1178
    def _do_get(self, name, group=None):
1059
1179
        """Look up an option value.
1060
1180
 
1061
1181
        :param name: the opt name (or 'dest', more precisely)
1196
1316
 
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)
1200
1320
 
1201
1321
        def __getitem__(self, key):
1202
1322
            """Look up an option value and perform string substitution."""