~ubuntu-branches/ubuntu/quantal/keystone/quantal-security

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2012-06-22 12:27:50 UTC
  • mto: (35.1.1 quantal-proposed)
  • mto: This revision was merged to the branch mainline in revision 28.
  • Revision ID: package-import@ubuntu.com-20120622122750-4urdq17en1990apn
Tags: upstream-2012.2~f2~20120622.2353
ImportĀ upstreamĀ versionĀ 2012.2~f2~20120622.2353

Show diffs side-by-side

added added

removed removed

Lines of Context:
95
95
 
96
96
    class ConfigOpts(object):
97
97
 
98
 
        def __init__(self, ...):
 
98
        def __call__(self, ...):
99
99
 
100
100
            opts = [
101
101
                MultiStrOpt('config-file',
149
149
        conf.register_opt(rabbit_host_opt, group=rabbit_group)
150
150
        conf.register_opt(rabbit_port_opt, group='rabbit')
151
151
 
 
152
If it no group attributes are required other than the group name, the group
 
153
need not be explicitly registered e.g.
 
154
 
 
155
    def register_rabbit_opts(conf):
 
156
        # The group will automatically be created, equivalent calling::
 
157
        #   conf.register_group(OptGroup(name='rabbit'))
 
158
        conf.register_opt(rabbit_port_opt, group='rabbit')
 
159
 
152
160
If no group is specified, options belong to the 'DEFAULT' section of config
153
161
files::
154
162
 
213
221
 
214
222
i.e. argument parsing is stopped at the first non-option argument.
215
223
 
 
224
Options may be declared as required so that an error is raised if the user
 
225
does not supply a value for the option.
 
226
 
216
227
Options may be declared as secret so that their values are not leaked into
217
228
log files:
218
229
 
222
233
        ...
223
234
     ]
224
235
 
 
236
This module also contains a global instance of the CommonConfigOpts class
 
237
in order to support a common usage pattern in OpenStack:
 
238
 
 
239
  from openstack.common import cfg
 
240
 
 
241
  opts = [
 
242
    cfg.StrOpt('bind_host' default='0.0.0.0'),
 
243
    cfg.IntOpt('bind_port', default=9292),
 
244
  ]
 
245
 
 
246
  CONF = cfg.CONF
 
247
  CONF.register_opts(opts)
 
248
 
 
249
  def start(server, app):
 
250
      server.start(app, CONF.bind_port, CONF.bind_host)
 
251
 
225
252
"""
226
253
 
227
254
import collections
228
255
import copy
 
256
import functools
229
257
import glob
230
 
import functools
231
258
import optparse
232
259
import os
233
260
import string
291
318
        return "duplicate option: %s" % self.opt_name
292
319
 
293
320
 
 
321
class RequiredOptError(Error):
 
322
    """Raised if an option is required but no value is supplied by the user."""
 
323
 
 
324
    def __init__(self, opt_name, group=None):
 
325
        self.opt_name = opt_name
 
326
        self.group = group
 
327
 
 
328
    def __str__(self):
 
329
        if self.group is None:
 
330
            return "value required for option: %s" % self.opt_name
 
331
        else:
 
332
            return "value required for option: %s.%s" % (self.group.name,
 
333
                                                         self.opt_name)
 
334
 
 
335
 
294
336
class TemplateSubstitutionError(Error):
295
337
    """Raised if an error occurs substituting a variable in an opt value."""
296
338
 
349
391
        fix_path('~'),
350
392
        os.path.join('/etc', project) if project else None,
351
393
        '/etc'
352
 
        ]
 
394
    ]
353
395
 
354
396
    return filter(bool, cfg_dirs)
355
397
 
452
494
    multi = False
453
495
 
454
496
    def __init__(self, name, dest=None, short=None, default=None,
455
 
                 metavar=None, help=None, secret=False):
 
497
                 metavar=None, help=None, secret=False, required=False):
456
498
        """Construct an Opt object.
457
499
 
458
500
        The only required parameter is the option's name. However, it is
465
507
        :param metavar: the option argument to show in --help
466
508
        :param help: an explanation of how the option is used
467
509
        :param secret: true iff the value should be obfuscated in log output
 
510
        :param required: true iff a value must be supplied for this option
468
511
        """
469
512
        self.name = name
470
513
        if dest is None:
476
519
        self.metavar = metavar
477
520
        self.help = help
478
521
        self.secret = secret
 
522
        self.required = required
479
523
 
480
524
    def _get_from_config_parser(self, cparser, section):
481
525
        """Retrieves the option value from a MultiConfigParser object.
548
592
        if group is not None:
549
593
            dest = group.name + '_' + dest
550
594
        kwargs.update({
551
 
                'dest': dest,
552
 
                'metavar': self.metavar,
553
 
                'help': self.help,
554
 
                })
 
595
            'dest': dest,
 
596
            'metavar': self.metavar,
 
597
            'help': self.help,
 
598
        })
555
599
        return kwargs
556
600
 
557
601
    def _get_optparse_prefix(self, prefix, group):
740
784
 
741
785
        return True
742
786
 
 
787
    def _unregister_opt(self, opt):
 
788
        """Remove an opt from this group.
 
789
 
 
790
        :param opt: an Opt object
 
791
        """
 
792
        if opt.dest in self._opts:
 
793
            del self._opts[opt.dest]
 
794
 
743
795
    def _get_optparse_group(self, parser):
744
796
        """Build an optparse.OptionGroup for this group."""
745
797
        if self._optparse_group is None:
747
799
                                                        self.help)
748
800
        return self._optparse_group
749
801
 
 
802
    def _clear(self):
 
803
        """Clear this group's option parsing state."""
 
804
        self._optparse_group = None
 
805
 
750
806
 
751
807
class ParseError(iniparser.ParseError):
752
808
    def __init__(self, msg, lineno, line, filename):
821
877
    the values of options.
822
878
    """
823
879
 
824
 
    def __init__(self,
825
 
                 project=None,
826
 
                 prog=None,
827
 
                 version=None,
828
 
                 usage=None,
829
 
                 default_config_files=None):
830
 
        """Construct a ConfigOpts object.
831
 
 
832
 
        Automatically registers the --config-file option with either a supplied
833
 
        list of default config files, or a list from find_config_files().
834
 
 
835
 
        :param project: the toplevel project name, used to locate config files
836
 
        :param prog: the name of the program (defaults to sys.argv[0] basename)
837
 
        :param version: the program version (for --version)
838
 
        :param usage: a usage string (%prog will be expanded)
839
 
        :param default_config_files: config files to use by default
840
 
        """
 
880
    def __init__(self):
 
881
        """Construct a ConfigOpts object."""
 
882
        self._opts = {}  # dict of dicts of (opt:, override:, default:)
 
883
        self._groups = {}
 
884
 
 
885
        self._args = None
 
886
        self._oparser = None
 
887
        self._cparser = None
 
888
        self._cli_values = {}
 
889
        self.__cache = {}
 
890
        self._config_opts = []
 
891
        self._disable_interspersed_args = False
 
892
 
 
893
    def _setup(self, project, prog, version, usage, default_config_files):
 
894
        """Initialize a ConfigOpts object for option parsing."""
841
895
        if prog is None:
842
896
            prog = os.path.basename(sys.argv[0])
843
897
 
844
898
        if default_config_files is None:
845
899
            default_config_files = find_config_files(project, prog)
846
900
 
847
 
        self.project = project
848
 
        self.prog = prog
849
 
        self.version = version
850
 
        self.usage = usage
851
 
        self.default_config_files = default_config_files
852
 
 
853
 
        self._opts = {}  # dict of dicts of (opt:, override:, default:)
854
 
        self._groups = {}
855
 
 
856
 
        self._args = None
857
 
        self._cli_values = {}
858
 
 
859
 
        self._oparser = optparse.OptionParser(prog=self.prog,
860
 
                                              version=self.version,
861
 
                                              usage=self.usage)
862
 
        self._cparser = None
863
 
 
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, )),
 
901
        self._oparser = optparse.OptionParser(prog=prog,
 
902
                                              version=version,
 
903
                                              usage=usage)
 
904
        if self._disable_interspersed_args:
 
905
            self._oparser.disable_interspersed_args()
 
906
 
 
907
        self._config_opts = [
 
908
            MultiStrOpt('config-file',
 
909
                        default=default_config_files,
 
910
                        metavar='PATH',
 
911
                        help='Path to a config file to use. Multiple config '
 
912
                             'files can be specified, with values in later '
 
913
                             'files taking precedence. The default files '
 
914
                             ' used are: %s' % (default_config_files, )),
875
915
            StrOpt('config-dir',
876
916
                   metavar='DIR',
877
917
                   help='Path to a config directory to pull *.conf '
881
921
                        'the file(s), if any, specified via --config-file, '
882
922
                        'hence over-ridden options in the directory take '
883
923
                        'precedence.'),
884
 
            ]
885
 
        self.register_cli_opts(opts)
 
924
        ]
 
925
        self.register_cli_opts(self._config_opts)
 
926
 
 
927
        self.project = project
 
928
        self.prog = prog
 
929
        self.version = version
 
930
        self.usage = usage
 
931
        self.default_config_files = default_config_files
886
932
 
887
933
    def __clear_cache(f):
888
934
        @functools.wraps(f)
893
939
 
894
940
        return __inner
895
941
 
896
 
    def __call__(self, args=None):
 
942
    def __call__(self,
 
943
                 args=None,
 
944
                 project=None,
 
945
                 prog=None,
 
946
                 version=None,
 
947
                 usage=None,
 
948
                 default_config_files=None):
897
949
        """Parse command line arguments and config files.
898
950
 
899
951
        Calling a ConfigOpts object causes the supplied command line arguments
903
955
        The object may be called multiple times, each time causing the previous
904
956
        set of values to be overwritten.
905
957
 
 
958
        Automatically registers the --config-file option with either a supplied
 
959
        list of default config files, or a list from find_config_files().
 
960
 
906
961
        If the --config-dir option is set, any *.conf files from this
907
962
        directory are pulled in, after all the file(s) specified by the
908
963
        --config-file option.
909
964
 
910
 
        :params args: command line arguments (defaults to sys.argv[1:])
 
965
        :param args: command line arguments (defaults to sys.argv[1:])
 
966
        :param project: the toplevel project name, used to locate config files
 
967
        :param prog: the name of the program (defaults to sys.argv[0] basename)
 
968
        :param version: the program version (for --version)
 
969
        :param usage: a usage string (%prog will be expanded)
 
970
        :param default_config_files: config files to use by default
911
971
        :returns: the list of arguments left over after parsing options
912
 
        :raises: SystemExit, ConfigFilesNotFoundError, ConfigFileParseError
 
972
        :raises: SystemExit, ConfigFilesNotFoundError, ConfigFileParseError,
 
973
                 RequiredOptError, DuplicateOptError
913
974
        """
914
 
        self.reset()
915
 
 
916
 
        self._args = args
917
 
 
918
 
        (values, args) = self._oparser.parse_args(self._args)
919
 
 
920
 
        self._cli_values = vars(values)
921
 
 
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)
930
 
 
931
 
        return args
 
975
        self.clear()
 
976
 
 
977
        self._setup(project, prog, version, usage, default_config_files)
 
978
 
 
979
        self._cli_values, leftovers = self._parse_cli_opts(args)
 
980
 
 
981
        self._parse_config_files()
 
982
 
 
983
        self._check_required_opts()
 
984
 
 
985
        return leftovers
932
986
 
933
987
    def __getattr__(self, name):
934
988
        """Look up an option value and perform string substitution.
956
1010
        """Return the number of options and option groups."""
957
1011
        return len(self._opts) + len(self._groups)
958
1012
 
959
 
    @__clear_cache
960
1013
    def reset(self):
961
 
        """Reset the state of the object to before it was called."""
 
1014
        """Clear the object state and unset overrides and defaults."""
 
1015
        self._unset_defaults_and_overrides()
 
1016
        self.clear()
 
1017
 
 
1018
    @__clear_cache
 
1019
    def clear(self):
 
1020
        """Clear the state of the object to before it was called."""
962
1021
        self._args = None
963
 
        self._cli_values = None
 
1022
        self._cli_values.clear()
 
1023
        self._oparser = None
964
1024
        self._cparser = None
 
1025
        self.unregister_opts(self._config_opts)
 
1026
        for group in self._groups.values():
 
1027
            group._clear()
965
1028
 
966
1029
    @__clear_cache
967
1030
    def register_opt(self, opt, group=None):
977
1040
        :raises: DuplicateOptError
978
1041
        """
979
1042
        if group is not None:
980
 
            return self._get_group(group)._register_opt(opt)
 
1043
            return self._get_group(group, autocreate=True)._register_opt(opt)
981
1044
 
982
1045
        if _is_opt_registered(self._opts, opt):
983
1046
            return False
1008
1071
        if self._args is not None:
1009
1072
            raise ArgsAlreadyParsedError("cannot register CLI option")
1010
1073
 
1011
 
        if not self.register_opt(opt, group, clear_cache=False):
1012
 
            return False
1013
 
 
1014
 
        if group is not None:
1015
 
            group = self._get_group(group)
1016
 
 
1017
 
        opt._add_to_cli(self._oparser, group)
1018
 
 
1019
 
        return True
 
1074
        return self.register_opt(opt, group, clear_cache=False)
1020
1075
 
1021
1076
    @__clear_cache
1022
1077
    def register_cli_opts(self, opts, group=None):
1038
1093
        self._groups[group.name] = copy.copy(group)
1039
1094
 
1040
1095
    @__clear_cache
 
1096
    def unregister_opt(self, opt, group=None):
 
1097
        """Unregister an option.
 
1098
 
 
1099
        :param opt: an Opt object
 
1100
        :param group: an optional OptGroup object or group name
 
1101
        :raises: ArgsAlreadyParsedError, NoSuchGroupError
 
1102
        """
 
1103
        if self._args is not None:
 
1104
            raise ArgsAlreadyParsedError("reset before unregistering options")
 
1105
 
 
1106
        if group is not None:
 
1107
            self._get_group(group)._unregister_opt(opt)
 
1108
        elif opt.dest in self._opts:
 
1109
            del self._opts[opt.dest]
 
1110
 
 
1111
    @__clear_cache
 
1112
    def unregister_opts(self, opts, group=None):
 
1113
        """Unregister multiple CLI option schemas at once."""
 
1114
        for opt in opts:
 
1115
            self.unregister_opt(opt, group, clear_cache=False)
 
1116
 
 
1117
    @__clear_cache
1041
1118
    def set_override(self, name, override, group=None):
1042
1119
        """Override an opt value.
1043
1120
 
1067
1144
        opt_info = self._get_opt_info(name, group)
1068
1145
        opt_info['default'] = default
1069
1146
 
 
1147
    def _all_opt_infos(self):
 
1148
        """A generator function for iteration opt infos."""
 
1149
        for info in self._opts.values():
 
1150
            yield info, None
 
1151
        for group in self._groups.values():
 
1152
            for info in group._opts.values():
 
1153
                yield info, group
 
1154
 
 
1155
    def _all_opts(self):
 
1156
        """A generator function for iteration opts."""
 
1157
        for info, group in self._all_opt_infos():
 
1158
            yield info['opt'], group
 
1159
 
 
1160
    def _unset_defaults_and_overrides(self):
 
1161
        """Unset any default or override on all options."""
 
1162
        for info, group in self._all_opt_infos():
 
1163
            info['default'] = None
 
1164
            info['override'] = None
 
1165
 
1070
1166
    def disable_interspersed_args(self):
1071
1167
        """Set parsing to stop on the first non-option.
1072
1168
 
1084
1180
 
1085
1181
        i.e. argument parsing is stopped at the first non-option argument.
1086
1182
        """
1087
 
        self._oparser.disable_interspersed_args()
 
1183
        self._disable_interspersed_args = True
1088
1184
 
1089
1185
    def enable_interspersed_args(self):
1090
1186
        """Set parsing to not stop on the first non-option.
1091
1187
 
1092
1188
        This it the default behaviour."""
1093
 
        self._oparser.enable_interspersed_args()
 
1189
        self._disable_interspersed_args = False
1094
1190
 
1095
1191
    def find_file(self, name):
1096
1192
        """Locate a file located alongside the config files.
1188
1284
            return self.GroupAttr(self, self._get_group(name))
1189
1285
 
1190
1286
        info = self._get_opt_info(name, group)
1191
 
        default, opt, override = map(lambda k: info[k], sorted(info.keys()))
 
1287
        default, opt, override = [info[k] for k in sorted(info.keys())]
1192
1288
 
1193
1289
        if override is not None:
1194
1290
            return override
1241
1337
        else:
1242
1338
            return value
1243
1339
 
1244
 
    def _get_group(self, group_or_name):
 
1340
    def _get_group(self, group_or_name, autocreate=False):
1245
1341
        """Looks up a OptGroup object.
1246
1342
 
1247
1343
        Helper function to return an OptGroup given a parameter which can
1252
1348
        the API have access to.
1253
1349
 
1254
1350
        :param group_or_name: the group's name or the OptGroup object itself
 
1351
        :param autocreate: whether to auto-create the group if it's not found
1255
1352
        :raises: NoSuchGroupError
1256
1353
        """
1257
 
        if isinstance(group_or_name, OptGroup):
1258
 
            group_name = group_or_name.name
1259
 
        else:
1260
 
            group_name = group_or_name
 
1354
        group = group_or_name if isinstance(group_or_name, OptGroup) else None
 
1355
        group_name = group.name if group else group_or_name
1261
1356
 
1262
1357
        if not group_name in self._groups:
1263
 
            raise NoSuchGroupError(group_name)
 
1358
            if not group is None or not autocreate:
 
1359
                raise NoSuchGroupError(group_name)
 
1360
 
 
1361
            self.register_group(OptGroup(name=group_name))
1264
1362
 
1265
1363
        return self._groups[group_name]
1266
1364
 
1282
1380
 
1283
1381
        return opts[opt_name]
1284
1382
 
1285
 
    def _parse_config_files(self, config_files):
1286
 
        """Parse the supplied configuration files.
 
1383
    def _parse_config_files(self):
 
1384
        """Parse the config files from --config-file and --config-dir.
1287
1385
 
1288
1386
        :raises: ConfigFilesNotFoundError, ConfigFileParseError
1289
1387
        """
 
1388
        config_files = list(self.config_file)
 
1389
 
 
1390
        if self.config_dir:
 
1391
            config_dir_glob = os.path.join(self.config_dir, '*.conf')
 
1392
            config_files += sorted(glob.glob(config_dir_glob))
 
1393
 
1290
1394
        self._cparser = MultiConfigParser()
1291
1395
 
1292
1396
        try:
1298
1402
            not_read_ok = filter(lambda f: f not in read_ok, config_files)
1299
1403
            raise ConfigFilesNotFoundError(not_read_ok)
1300
1404
 
 
1405
    def _check_required_opts(self):
 
1406
        """Check that all opts marked as required have values specified.
 
1407
 
 
1408
        :raises: RequiredOptError
 
1409
        """
 
1410
        for info, group in self._all_opt_infos():
 
1411
            default, opt, override = [info[k] for k in sorted(info.keys())]
 
1412
 
 
1413
            if opt.required:
 
1414
                if (default is not None or override is not None):
 
1415
                    continue
 
1416
 
 
1417
                if self._get(opt.name, group) is None:
 
1418
                    raise RequiredOptError(opt.name, group)
 
1419
 
 
1420
    def _parse_cli_opts(self, args):
 
1421
        """Parse command line options.
 
1422
 
 
1423
        Initializes the command line option parser and parses the supplied
 
1424
        command line arguments.
 
1425
 
 
1426
        :param args: the command line arguments
 
1427
        :returns: a dict of parsed option values
 
1428
        :raises: SystemExit, DuplicateOptError
 
1429
 
 
1430
        """
 
1431
        self._args = args
 
1432
 
 
1433
        for opt, group in self._all_opts():
 
1434
            opt._add_to_cli(self._oparser, group)
 
1435
 
 
1436
        values, leftovers = self._oparser.parse_args(args)
 
1437
 
 
1438
        return vars(values), leftovers
 
1439
 
1301
1440
    class GroupAttr(collections.Mapping):
1302
1441
 
1303
1442
        """
1376
1515
                short='v',
1377
1516
                default=False,
1378
1517
                help='Print more verbose output'),
1379
 
        ]
 
1518
    ]
1380
1519
 
1381
1520
    logging_cli_opts = [
1382
1521
        StrOpt('log-config',
1410
1549
        StrOpt('syslog-log-facility',
1411
1550
               default='LOG_USER',
1412
1551
               help='syslog facility to receive log lines')
1413
 
        ]
 
1552
    ]
1414
1553
 
1415
 
    def __init__(self, **kwargs):
1416
 
        super(CommonConfigOpts, self).__init__(**kwargs)
 
1554
    def __init__(self):
 
1555
        super(CommonConfigOpts, self).__init__()
1417
1556
        self.register_cli_opts(self.common_cli_opts)
1418
1557
        self.register_cli_opts(self.logging_cli_opts)
 
1558
 
 
1559
 
 
1560
CONF = CommonConfigOpts()