diff options
Diffstat (limited to 'src/lib/Bcfg2/Options.py')
-rw-r--r-- | src/lib/Bcfg2/Options.py | 1073 |
1 files changed, 844 insertions, 229 deletions
diff --git a/src/lib/Bcfg2/Options.py b/src/lib/Bcfg2/Options.py index dfb062341..1883bc222 100644 --- a/src/lib/Bcfg2/Options.py +++ b/src/lib/Bcfg2/Options.py @@ -1,25 +1,24 @@ """Option parsing library for utilities.""" +import copy import getopt -import re +import inspect import os -import sys +import re import shlex +import sys import Bcfg2.Client.Tools # Compatibility imports from Bcfg2.Bcfg2Py3k import ConfigParser +from Bcfg2.version import __version__ -def bool_cook(x): - if x: - return True - else: - return False class OptionFailure(Exception): pass -DEFAULT_CONFIG_LOCATION = '/etc/bcfg2.conf' #/etc/bcfg2.conf -DEFAULT_INSTALL_PREFIX = '/usr' #/usr +DEFAULT_CONFIG_LOCATION = '/etc/bcfg2.conf' +DEFAULT_INSTALL_PREFIX = '/usr' + class DefaultConfigParser(ConfigParser.ConfigParser): def get(self, section, option, **kwargs): @@ -55,16 +54,9 @@ class DefaultConfigParser(ConfigParser.ConfigParser): class Option(object): - def get_cooked_value(self, value): - if self.boolean: - return True - if self.cook: - return self.cook(value) - else: - return value - def __init__(self, desc, default, cmd=False, odesc=False, - env=False, cf=False, cook=False, long_arg=False): + env=False, cf=False, cook=False, long_arg=False, + deprecated_cf=None): self.desc = desc self.default = default self.cmd = cmd @@ -72,33 +64,38 @@ class Option(object): if not self.long: if cmd and (cmd[0] != '-' or len(cmd) != 2): raise OptionFailure("Poorly formed command %s" % cmd) - else: - if cmd and (not cmd.startswith('--')): - raise OptionFailure("Poorly formed command %s" % cmd) + elif cmd and (not cmd.startswith('--')): + raise OptionFailure("Poorly formed command %s" % cmd) self.odesc = odesc self.env = env self.cf = cf + self.deprecated_cf = deprecated_cf self.boolean = False - if not odesc and not cook: + if not odesc and not cook and isinstance(self.default, bool): self.boolean = True self.cook = cook + def get_cooked_value(self, value): + if self.boolean: + return True + if self.cook: + return self.cook(value) + else: + return value + def buildHelpMessage(self): - msg = '' - if self.cmd: - if not self.long: - msg = self.cmd.ljust(3) - else: - msg = self.cmd - if self.odesc: - if self.long: - msg = "%-28s" % ("%s=%s" % (self.cmd, self.odesc)) - else: - msg += '%-25s' % (self.odesc) + vals = [] + if not self.cmd: + return '' + if self.odesc: + if self.long: + vals.append("%s=%s" % (self.cmd, self.odesc)) else: - msg += '%-25s' % ('') - msg += "%s\n" % self.desc - return msg + vals.append("%s %s" % (self.cmd, self.odesc)) + else: + vals.append(self.cmd) + vals.append(self.desc) + return " %-28s %s\n" % tuple(vals) def buildGetopt(self): gstr = '' @@ -112,7 +109,7 @@ class Option(object): def buildLongGetopt(self): if self.odesc: - return self.cmd[2:]+'=' + return self.cmd[2:] + '=' else: return self.cmd[2:] @@ -127,7 +124,10 @@ class Option(object): self.value = True return if self.cmd and self.cmd in rawopts: - data = rawopts[rawopts.index(self.cmd) + 1] + if self.odesc: + data = rawopts[rawopts.index(self.cmd) + 1] + else: + data = True self.value = self.get_cooked_value(data) return # No command line option found @@ -140,9 +140,20 @@ class Option(object): return except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): pass + if self.deprecated_cf: + try: + self.value = self.get_cooked_value(configparser.get(*self.deprecated_cf)) + print("Warning: [%s] %s is deprecated, use [%s] %s instead" + % (self.deprecated_cf[0], self.deprecated_cf[1], + self.cf[0], self.cf[1])) + return + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): + pass + # Default value not cooked self.value = self.default + class OptionSet(dict): def __init__(self, *args, **kwargs): dict.__init__(self, *args) @@ -154,8 +165,14 @@ class OptionSet(dict): self.cfp = DefaultConfigParser() if (len(self.cfp.read(self.cfile)) == 0 and ('quiet' not in kwargs or not kwargs['quiet'])): - print("Warning! Unable to read specified configuration file: %s" % - self.cfile) + # suppress warnings if called from bcfg2-admin init + caller = inspect.stack()[-1][1].split('/')[-1] + if caller == 'bcfg2-admin' and len(sys.argv) > 1: + if sys.argv[1] == 'init': + return + else: + print("Warning! Unable to read specified configuration file: %s" % + self.cfile) def buildGetopt(self): return ''.join([opt.buildGetopt() for opt in list(self.values())]) @@ -170,18 +187,28 @@ class OptionSet(dict): hlist = [] # list of _non-empty_ help messages for opt in list(self.values()): hm = opt.buildHelpMessage() - if hm != '': + if hm: hlist.append(hm) - return ' '.join(hlist) + return ''.join(hlist) def helpExit(self, msg='', code=1): if msg: print(msg) - print("Usage:\n %s" % self.buildHelpMessage()) + print("Usage:") + print(self.buildHelpMessage()) + raise SystemExit(code) + + def versionExit(self, code=0): + print("%s %s on Python %s" % + (os.path.basename(sys.argv[0]), + __version__, + ".".join(str(v) for v in sys.version_info[0:3]))) raise SystemExit(code) def parse(self, argv, do_getopt=True): '''Parse options from command line.''' + if VERSION not in self.values(): + self['__version__'] = VERSION if do_getopt: try: opts, args = getopt.getopt(argv, self.buildGetopt(), @@ -191,6 +218,8 @@ class OptionSet(dict): self.helpExit(err) if '-h' in argv: self.helpExit('', 0) + if '--version' in argv: + self.versionExit() self['args'] = args for key in list(self.keys()): if key == 'args': @@ -203,17 +232,22 @@ class OptionSet(dict): if hasattr(option, 'value'): val = option.value self[key] = val + if "__version__" in self: + del self['__version__'] + def list_split(c_string): if c_string: return re.split("\s*,\s*", c_string) return [] + def colon_split(c_string): if c_string: return c_string.split(':') return [] + def get_bool(s): # these values copied from ConfigParser.RawConfigParser.getboolean # with the addition of True and False @@ -225,210 +259,784 @@ def get_bool(s): return False else: raise ValueError + + +""" +Options: + + Accepts keyword argument list with the following values: + default: default value for the option + cmd: command line switch + odesc: option description + cf: tuple containing section/option + cook: method for parsing option + long_arg: (True|False) specifies whether cmd is a long argument +""" # General options -CFILE = Option('Specify configuration file', DEFAULT_CONFIG_LOCATION, cmd='-C', - odesc='<conffile>') -LOCKFILE = Option('Specify lockfile', - "/var/lock/bcfg2.run", - cf=('components', 'lockfile'), - odesc='<Path to lockfile>') -HELP = Option('Print this usage message', False, cmd='-h') -DEBUG = Option("Enable debugging output", False, cmd='-d') -VERBOSE = Option("Enable verbose output", False, cmd='-v') -DAEMON = Option("Daemonize process, storing pid", False, - cmd='-D', odesc="<pidfile>") -INSTALL_PREFIX = Option('Installation location', cf=('server', 'prefix'), - default=DEFAULT_INSTALL_PREFIX, odesc='</path>') -SENDMAIL_PATH = Option('Path to sendmail', cf=('reports', 'sendmailpath'), - default='/usr/lib/sendmail') -INTERACTIVE = Option('Run interactively, prompting the user for each change', - default=False, - cmd='-I', ) -ENCODING = Option('Encoding of cfg files', - default='UTF-8', - cmd='-E', - odesc='<encoding>', - cf=('components', 'encoding')) -PARANOID_PATH = Option('Specify path for paranoid file backups', - default='/var/cache/bcfg2', cf=('paranoid', 'path'), - odesc='<paranoid backup path>') -PARANOID_MAX_COPIES = Option('Specify the number of paranoid copies you want', - default=1, cf=('paranoid', 'max_copies'), - odesc='<max paranoid copies>') -OMIT_LOCK_CHECK = Option('Omit lock check', default=False, cmd='-O') -CORE_PROFILE = Option('profile', - default=False, cmd='-p', ) -FILES_ON_STDIN = Option('Operate on a list of files supplied on stdin', - cmd='--stdin', default=False, long_arg=True) -SCHEMA_PATH = Option('Path to XML Schema files', cmd='--schema', - odesc='<schema path>', - default="%s/share/bcfg2/schemas" % DEFAULT_INSTALL_PREFIX, - long_arg=True) -REQUIRE_SCHEMA = Option("Require property files to have matching schema files", - cmd="--require-schema", default=False, long_arg=True) - -# Metadata options -MDATA_OWNER = Option('Default Path owner', - default='root', cf=('mdata', 'owner'), - odesc='owner permissions') -MDATA_GROUP = Option('Default Path group', - default='root', cf=('mdata', 'group'), - odesc='group permissions') -MDATA_IMPORTANT = Option('Default Path priority (importance)', - default='False', cf=('mdata', 'important'), - odesc='Important entries are installed first') -MDATA_PERMS = Option('Default Path permissions', - '644', cf=('mdata', 'perms'), - odesc='octal permissions') -MDATA_PARANOID = Option('Default Path paranoid setting', - 'true', cf=('mdata', 'paranoid'), - odesc='Path paranoid setting') -MDATA_SENSITIVE = Option('Default Path sensitive setting', - 'false', cf=('mdata', 'sensitive'), - odesc='Path sensitive setting') +CFILE = \ + Option('Specify configuration file', + default=DEFAULT_CONFIG_LOCATION, + cmd='-C', + odesc='<conffile>') +LOCKFILE = \ + Option('Specify lockfile', + default='/var/lock/bcfg2.run', + odesc='<Path to lockfile>', + cf=('components', 'lockfile')) +HELP = \ + Option('Print this usage message', + default=False, + cmd='-h') +VERSION = \ + Option('Print the version and exit', + default=False, + cmd='--version', long_arg=True) +DAEMON = \ + Option("Daemonize process, storing pid", + default=None, + cmd='-D', + odesc='<pidfile>') +INSTALL_PREFIX = \ + Option('Installation location', + default=DEFAULT_INSTALL_PREFIX, + odesc='</path>', + cf=('server', 'prefix')) +SENDMAIL_PATH = \ + Option('Path to sendmail', + default='/usr/lib/sendmail', + cf=('reports', 'sendmailpath')) +INTERACTIVE = \ + Option('Run interactively, prompting the user for each change', + default=False, + cmd='-I', ) +ENCODING = \ + Option('Encoding of cfg files', + default='UTF-8', + cmd='-E', + odesc='<encoding>', + cf=('components', 'encoding')) +PARANOID_PATH = \ + Option('Specify path for paranoid file backups', + default='/var/cache/bcfg2', + odesc='<paranoid backup path>', + cf=('paranoid', 'path')) +PARANOID_MAX_COPIES = \ + Option('Specify the number of paranoid copies you want', + default=1, + odesc='<max paranoid copies>', + cf=('paranoid', 'max_copies')) +OMIT_LOCK_CHECK = \ + Option('Omit lock check', + default=False, + cmd='-O') +CORE_PROFILE = \ + Option('profile', + default=False, + cmd='-p') +SCHEMA_PATH = \ + Option('Path to XML Schema files', + default='%s/share/bcfg2/schemas' % DEFAULT_INSTALL_PREFIX, + cmd='--schema', + odesc='<schema path>', + cf=('lint', 'schema'), + long_arg=True) +INTERPRETER = \ + Option("Python interpreter to use", + default='best', + cmd="--interpreter", + odesc='<python|bpython|ipython|best>', + cf=('bcfg2-info', 'interpreter'), + long_arg=True) + +# Metadata options (mdata section) +MDATA_OWNER = \ + Option('Default Path owner', + default='root', + odesc='owner permissions', + cf=('mdata', 'owner')) +MDATA_GROUP = \ + Option('Default Path group', + default='root', + odesc='group permissions', + cf=('mdata', 'group')) +MDATA_IMPORTANT = \ + Option('Default Path priority (importance)', + default='False', + odesc='Important entries are installed first', + cf=('mdata', 'important')) +MDATA_PERMS = \ + Option('Default Path permissions', + default='644', + odesc='octal permissions', + cf=('mdata', 'perms')) +MDATA_SECONTEXT = \ + Option('Default SELinux context', + default='__default__', + odesc='SELinux context', + cf=('mdata', 'secontext')) +MDATA_PARANOID = \ + Option('Default Path paranoid setting', + default='true', + odesc='Path paranoid setting', + cf=('mdata', 'paranoid')) +MDATA_SENSITIVE = \ + Option('Default Path sensitive setting', + default='false', + odesc='Path sensitive setting', + cf=('mdata', 'sensitive')) # Server options -SERVER_REPOSITORY = Option('Server repository path', '/var/lib/bcfg2', - cf=('server', 'repository'), cmd='-Q', - odesc='<repository path>') -SERVER_PLUGINS = Option('Server plugin list', cf=('server', 'plugins'), - # default server plugins - default=[ - 'Bundler', - 'Cfg', - 'Metadata', - 'Pkgmgr', - 'Rules', - 'SSHbase', - ], - cook=list_split) -SERVER_MCONNECT = Option('Server Metadata Connector list', cook=list_split, - cf=('server', 'connectors'), default=['Probes'], ) -SERVER_FILEMONITOR = Option('Server file monitor', cf=('server', 'filemonitor'), - default='default', odesc='File monitoring driver') -SERVER_LISTEN_ALL = Option('Listen on all interfaces', - cf=('server', 'listen_all'), - cmd='--listen-all', - default=False, - long_arg=True, - cook=get_bool, - odesc='True|False') -SERVER_LOCATION = Option('Server Location', cf=('components', 'bcfg2'), - default='https://localhost:6789', cmd='-S', - odesc='https://server:port') -SERVER_STATIC = Option('Server runs on static port', cf=('components', 'bcfg2'), - default=False, cook=bool_cook) -SERVER_KEY = Option('Path to SSL key', cf=('communication', 'key'), - default=False, cmd='--ssl-key', odesc='<ssl key>', - long_arg=True) -SERVER_CERT = Option('Path to SSL certificate', default='/etc/bcfg2.key', - cf=('communication', 'certificate'), odesc='<ssl cert>') -SERVER_CA = Option('Path to SSL CA Cert', default=None, - cf=('communication', 'ca'), odesc='<ca cert>') -SERVER_PASSWORD = Option('Communication Password', cmd='-x', odesc='<password>', - cf=('communication', 'password'), default=False) -SERVER_PROTOCOL = Option('Server Protocol', cf=('communication', 'procotol'), - default='xmlrpc/ssl') +SERVER_REPOSITORY = \ + Option('Server repository path', + default='/var/lib/bcfg2', + cmd='-Q', + odesc='<repository path>', + cf=('server', 'repository')) +SERVER_PLUGINS = \ + Option('Server plugin list', + # default server plugins + default=['Bundler', 'Cfg', 'Metadata', 'Pkgmgr', 'Rules', 'SSHbase'], + cf=('server', 'plugins'), + cook=list_split) +SERVER_MCONNECT = \ + Option('Server Metadata Connector list', + default=['Probes'], + cf=('server', 'connectors'), + cook=list_split) +SERVER_FILEMONITOR = \ + Option('Server file monitor', + default='default', + odesc='File monitoring driver', + cf=('server', 'filemonitor')) +SERVER_FAM_IGNORE = \ + Option('File globs to ignore', + default=['*~', '*#', '.#*', '*.swp', '.*.swx', 'SCCS', '.svn', + '4913', '.gitignore',], + cf=('server', 'ignore_files'), + cook=list_split) +SERVER_LISTEN_ALL = \ + Option('Listen on all interfaces', + default=False, + cmd='--listen-all', + cf=('server', 'listen_all'), + cook=get_bool, + long_arg=True) +SERVER_LOCATION = \ + Option('Server Location', + default='https://localhost:6789', + cmd='-S', + odesc='https://server:port', + cf=('components', 'bcfg2')) +SERVER_STATIC = \ + Option('Server runs on static port', + default=False, + cf=('components', 'bcfg2')) +SERVER_KEY = \ + Option('Path to SSL key', + default=None, + cmd='--ssl-key', + odesc='<ssl key>', + cf=('communication', 'key'), + long_arg=True) +SERVER_CERT = \ + Option('Path to SSL certificate', + default='/etc/bcfg2.key', + odesc='<ssl cert>', + cf=('communication', 'certificate')) +SERVER_CA = \ + Option('Path to SSL CA Cert', + default=None, + odesc='<ca cert>', + cf=('communication', 'ca')) +SERVER_PASSWORD = \ + Option('Communication Password', + default=None, + cmd='-x', + odesc='<password>', + cf=('communication', 'password')) +SERVER_PROTOCOL = \ + Option('Server Protocol', + default='xmlrpc/ssl', + cf=('communication', 'procotol')) +SERVER_BACKEND = \ + Option('Server Backend', + default='best', + cf=('server', 'backend')) + +# database options +DB_ENGINE = \ + Option('Database engine', + default='sqlite3', + cf=('database', 'engine'), + deprecated_cf=('statistics', 'database_engine')) +DB_NAME = \ + Option('Database name', + default=os.path.join(SERVER_REPOSITORY.default, "bcfg2.sqlite"), + cf=('database', 'name'), + deprecated_cf=('statistics', 'database_name')) +DB_USER = \ + Option('Database username', + default=None, + cf=('database', 'user'), + deprecated_cf=('statistics', 'database_user')) +DB_PASSWORD = \ + Option('Database password', + default=None, + cf=('database', 'password'), + deprecated_cf=('statistics', 'database_password')) +DB_HOST = \ + Option('Database host', + default='localhost', + cf=('database', 'host'), + deprecated_cf=('statistics', 'database_host')) +DB_PORT = \ + Option('Database port', + default='', + cf=('database', 'port'), + deprecated_cf=('statistics', 'database_port')) + +# Django options +WEB_CFILE = \ + Option('Web interface configuration file', + default="/etc/bcfg2-web.conf", + cmd='-W', + cf=('statistics', 'config'),) +DJANGO_TIME_ZONE = \ + Option('Django timezone', + default=None, + cf=('statistics', 'time_zone'),) +DJANGO_DEBUG = \ + Option('Django debug', + default=None, + cf=('statistics', 'web_debug'), + cook=get_bool,) +# Django options +DJANGO_WEB_PREFIX = \ + Option('Web prefix', + default=None, + cf=('statistics', 'web_prefix'),) + + # Client options -CLIENT_KEY = Option('Path to SSL key', cf=('communication', 'key'), - default=None, cmd="--ssl-key", odesc='<ssl key>', - long_arg=True) -CLIENT_CERT = Option('Path to SSL certificate', default=None, cmd="--ssl-cert", - cf=('communication', 'certificate'), odesc='<ssl cert>', - long_arg=True) -CLIENT_CA = Option('Path to SSL CA Cert', default=None, cmd="--ca-cert", - cf=('communication', 'ca'), odesc='<ca cert>', - long_arg=True) -CLIENT_SCNS = Option('List of server commonNames', default=None, cmd="--ssl-cns", - cf=('communication', 'serverCommonNames'), - odesc='<commonName1:commonName2>', cook=list_split, - long_arg=True) -CLIENT_PROFILE = Option('Assert the given profile for the host', - default=False, cmd='-p', odesc="<profile>") -CLIENT_RETRIES = Option('The number of times to retry network communication', - default='3', cmd='-R', cf=('communication', 'retries'), - odesc="<retry count>") -CLIENT_DRYRUN = Option('Do not actually change the system', - default=False, cmd='-n', ) -CLIENT_EXTRA_DISPLAY = Option('enable extra entry output', - default=False, cmd='-e', ) -CLIENT_PARANOID = Option('Make automatic backups of config files', - default=False, - cmd='-P', - cook=get_bool, - cf=('client', 'paranoid')) -CLIENT_DRIVERS = Option('Specify tool driver set', cmd='-D', - cf=('client', 'drivers'), - odesc="<driver1,driver2>", cook=list_split, - default=Bcfg2.Client.Tools.default) -CLIENT_CACHE = Option('Store the configuration in a file', - default=False, cmd='-c', odesc="<cache path>") -CLIENT_REMOVE = Option('Force removal of additional configuration items', - default=False, cmd='-r', odesc="<entry type|all>") -CLIENT_BUNDLE = Option('Only configure the given bundle(s)', default=[], - cmd='-b', odesc='<bundle:bundle>', cook=colon_split) -CLIENT_BUNDLEQUICK = Option('only verify/configure the given bundle(s)', default=False, - cmd='-Q') -CLIENT_INDEP = Option('Only configure independent entries, ignore bundles', default=False, - cmd='-z') -CLIENT_KEVLAR = Option('Run in kevlar (bulletproof) mode', default=False, - cmd='-k', ) -CLIENT_DLIST = Option('Run client in server decision list mode', default='none', - cf=('client', 'decision'), - cmd='-l', odesc='<whitelist|blacklist|none>') -CLIENT_FILE = Option('Configure from a file rather than querying the server', - default=False, cmd='-f', odesc='<specification path>') -CLIENT_QUICK = Option('Disable some checksum verification', default=False, - cmd='-q', ) -CLIENT_USER = Option('The user to provide for authentication', default='root', - cmd='-u', cf=('communication', 'user'), odesc='<user>') -CLIENT_SERVICE_MODE = Option('Set client service mode', default='default', - cmd='-s', odesc='<default|disabled|build>') -CLIENT_TIMEOUT = Option('Set the client XML-RPC timeout', default=90, - cmd='-t', cf=('communication', 'timeout'), - odesc='<timeout>') - -# bcfg2-test options -TEST_NOSEOPTS = Option('Options to pass to nosetests', default=[], - cmd='--nose-options', cf=('bcfg2_test', 'nose_options'), - odesc='<opts>', long_arg=True, cook=shlex.split) -TEST_IGNORE = Option('Ignore these entries if they fail to build.', default=[], - cmd='--ignore', - cf=('bcfg2_test', 'ignore_entries'), long_arg=True, - odesc='<Type>:<name>,<Type>:<name>', cook=list_split) - -# APT client tool options -CLIENT_APT_TOOLS_INSTALL_PATH = Option('Apt tools install path', - cf=('APT', 'install_path'), - default='/usr') -CLIENT_APT_TOOLS_VAR_PATH = Option('Apt tools var path', - cf=('APT', 'var_path'), default='/var') -CLIENT_SYSTEM_ETC_PATH = Option('System etc path', cf=('APT', 'etc_path'), - default='/etc') +CLIENT_KEY = \ + Option('Path to SSL key', + default=None, + cmd='--ssl-key', + odesc='<ssl key>', + cf=('communication', 'key'), + long_arg=True) +CLIENT_CERT = \ + Option('Path to SSL certificate', + default=None, + cmd='--ssl-cert', + odesc='<ssl cert>', + cf=('communication', 'certificate'), + long_arg=True) +CLIENT_CA = \ + Option('Path to SSL CA Cert', + default=None, + cmd='--ca-cert', + odesc='<ca cert>', + cf=('communication', 'ca'), + long_arg=True) +CLIENT_SCNS = \ + Option('List of server commonNames', + default=None, + cmd='--ssl-cns', + odesc='<CN1:CN2>', + cf=('communication', 'serverCommonNames'), + cook=list_split, + long_arg=True) +CLIENT_PROFILE = \ + Option('Assert the given profile for the host', + default=None, + cmd='-p', + odesc='<profile>') +CLIENT_RETRIES = \ + Option('The number of times to retry network communication', + default='3', + cmd='-R', + odesc='<retry count>', + cf=('communication', 'retries')) +CLIENT_RETRY_DELAY = \ + Option('The time in seconds to wait between retries', + default='1', + cmd='-y', + odesc='<retry delay>', + cf=('communication', 'retry_delay')) +CLIENT_DRYRUN = \ + Option('Do not actually change the system', + default=False, + cmd='-n') +CLIENT_EXTRA_DISPLAY = \ + Option('enable extra entry output', + default=False, + cmd='-e') +CLIENT_PARANOID = \ + Option('Make automatic backups of config files', + default=False, + cmd='-P', + cf=('client', 'paranoid'), + cook=get_bool) +CLIENT_DRIVERS = \ + Option('Specify tool driver set', + default=Bcfg2.Client.Tools.default, + cmd='-D', + odesc='<driver1,driver2>', + cf=('client', 'drivers'), + cook=list_split) +CLIENT_CACHE = \ + Option('Store the configuration in a file', + default=None, + cmd='-c', + odesc='<cache path>') +CLIENT_REMOVE = \ + Option('Force removal of additional configuration items', + default=None, + cmd='-r', + odesc='<entry type|all>') +CLIENT_BUNDLE = \ + Option('Only configure the given bundle(s)', + default=[], + cmd='-b', + odesc='<bundle:bundle>', + cook=colon_split) +CLIENT_SKIPBUNDLE = \ + Option('Configure everything except the given bundle(s)', + default=[], + cmd='-B', + odesc='<bundle:bundle>', + cook=colon_split) +CLIENT_BUNDLEQUICK = \ + Option('Only verify/configure the given bundle(s)', + default=False, + cmd='-Q') +CLIENT_INDEP = \ + Option('Only configure independent entries, ignore bundles', + default=False, + cmd='-z') +CLIENT_SKIPINDEP = \ + Option('Do not configure independent entries', + default=False, + cmd='-Z') +CLIENT_KEVLAR = \ + Option('Run in kevlar (bulletproof) mode', + default=False, + cmd='-k', ) +CLIENT_FILE = \ + Option('Configure from a file rather than querying the server', + default=None, + cmd='-f', + odesc='<specification path>') +CLIENT_QUICK = \ + Option('Disable some checksum verification', + default=False, + cmd='-q') +CLIENT_USER = \ + Option('The user to provide for authentication', + default='root', + cmd='-u', + odesc='<user>', + cf=('communication', 'user')) +CLIENT_SERVICE_MODE = \ + Option('Set client service mode', + default='default', + cmd='-s', + odesc='<default|disabled|build>') +CLIENT_TIMEOUT = \ + Option('Set the client XML-RPC timeout', + default=90, + cmd='-t', + odesc='<timeout>', + cf=('communication', 'timeout')) +CLIENT_DLIST = \ + Option('Run client in server decision list mode', + default='none', + cmd='-l', + odesc='<whitelist|blacklist|none>', + cf=('client', 'decision')) +CLIENT_DECISION_LIST = \ + Option('Decision List', + default=False, + cmd='--decision-list', + odesc='<file>', + long_arg=True) + +# bcfg2-test and bcfg2-lint options +TEST_NOSEOPTS = \ + Option('Options to pass to nosetests', + default=[], + cmd='--nose-options', + odesc='<opts>', + cf=('bcfg2_test', 'nose_options'), + cook=shlex.split, + long_arg=True) +TEST_IGNORE = \ + Option('Ignore these entries if they fail to build.', + default=[], + cmd='--ignore', + odesc='<Type>:<name>,<Type>:<name>', + cf=('bcfg2_test', 'ignore_entries'), + cook=list_split, + long_arg=True) +LINT_CONFIG = \ + Option('Specify bcfg2-lint configuration file', + default='/etc/bcfg2-lint.conf', + cmd='--lint-config', + odesc='<conffile>', + long_arg=True) +LINT_SHOW_ERRORS = \ + Option('Show error handling', + default=False, + cmd='--list-errors', + long_arg=True) +LINT_FILES_ON_STDIN = \ + Option('Operate on a list of files supplied on stdin', + default=False, + cmd='--stdin', + long_arg=True) + +# individual client tool options +CLIENT_APT_TOOLS_INSTALL_PATH = \ + Option('Apt tools install path', + default='/usr', + cf=('APT', 'install_path')) +CLIENT_APT_TOOLS_VAR_PATH = \ + Option('Apt tools var path', + default='/var', + cf=('APT', 'var_path')) +CLIENT_SYSTEM_ETC_PATH = \ + Option('System etc path', + default='/etc', + cf=('APT', 'etc_path')) +CLIENT_PORTAGE_BINPKGONLY = \ + Option('Portage binary packages only', + default=False, + cf=('Portage', 'binpkgonly'), + cook=get_bool) +CLIENT_RPMNG_INSTALLONLY = \ + Option('RPMng install-only packages', + default=['kernel', 'kernel-bigmem', 'kernel-enterprise', + 'kernel-smp', 'kernel-modules', 'kernel-debug', + 'kernel-unsupported', 'kernel-devel', 'kernel-source', + 'kernel-default', 'kernel-largesmp-devel', + 'kernel-largesmp', 'kernel-xen', 'gpg-pubkey'], + cf=('RPMng', 'installonlypackages'), + cook=list_split) +CLIENT_RPMNG_PKG_CHECKS = \ + Option("Perform RPMng package checks", + default=True, + cf=('RPMng', 'pkg_checks'), + cook=get_bool) +CLIENT_RPMNG_PKG_VERIFY = \ + Option("Perform RPMng package verify", + default=True, + cf=('RPMng', 'pkg_verify'), + cook=get_bool) +CLIENT_RPMNG_INSTALLED_ACTION = \ + Option("RPMng installed action", + default="install", + cf=('RPMng', 'installed_action')) +CLIENT_RPMNG_ERASE_FLAGS = \ + Option("RPMng erase flags", + default=["allmatches"], + cf=('RPMng', 'erase_flags'), + cook=list_split) +CLIENT_RPMNG_VERSION_FAIL_ACTION = \ + Option("RPMng version fail action", + default="upgrade", + cf=('RPMng', 'version_fail_action')) +CLIENT_RPMNG_VERIFY_FAIL_ACTION = \ + Option("RPMng verify fail action", + default="reinstall", + cf=('RPMng', 'verify_fail_action')) +CLIENT_RPMNG_VERIFY_FLAGS = \ + Option("RPMng verify flags", + default=[], + cf=('RPMng', 'verify_flags'), + cook=list_split) +CLIENT_YUM24_INSTALLONLY = \ + Option('RPMng install-only packages', + default=['kernel', 'kernel-bigmem', 'kernel-enterprise', + 'kernel-smp', 'kernel-modules', 'kernel-debug', + 'kernel-unsupported', 'kernel-devel', 'kernel-source', + 'kernel-default', 'kernel-largesmp-devel', + 'kernel-largesmp', 'kernel-xen', 'gpg-pubkey'], + cf=('RPMng', 'installonlypackages'), + cook=list_split) +CLIENT_YUM24_PKG_CHECKS = \ + Option("Perform YUM24 package checks", + default=True, + cf=('YUM24', 'pkg_checks'), + cook=get_bool) +CLIENT_YUM24_PKG_VERIFY = \ + Option("Perform YUM24 package verify", + default=True, + cf=('YUM24', 'pkg_verify'), + cook=get_bool) +CLIENT_YUM24_INSTALLED_ACTION = \ + Option("YUM24 installed action", + default="install", + cf=('YUM24', 'installed_action')) +CLIENT_YUM24_ERASE_FLAGS = \ + Option("YUM24 erase flags", + default=["allmatches"], + cf=('YUM24', 'erase_flags'), + cook=list_split) +CLIENT_YUM24_VERSION_FAIL_ACTION = \ + Option("YUM24 version fail action", + cf=('YUM24', 'version_fail_action'), + default="upgrade") +CLIENT_YUM24_VERIFY_FAIL_ACTION = \ + Option("YUM24 verify fail action", + default="reinstall", + cf=('YUM24', 'verify_fail_action')) +CLIENT_YUM24_VERIFY_FLAGS = \ + Option("YUM24 verify flags", + default=[], + cf=('YUM24', 'verify_flags'), + cook=list_split) +CLIENT_YUM24_AUTODEP = \ + Option("YUM24 autodependency processing", + default=True, + cf=('YUM24', 'autodep'), + cook=get_bool) +CLIENT_YUMNG_PKG_CHECKS = \ + Option("Perform YUMng package checks", + default=True, + cf=('YUMng', 'pkg_checks'), + cook=get_bool) +CLIENT_YUMNG_PKG_VERIFY = \ + Option("Perform YUMng package verify", + default=True, + cf=('YUMng', 'pkg_verify'), + cook=get_bool) +CLIENT_YUMNG_INSTALLED_ACTION = \ + Option("YUMng installed action", + default="install", + cf=('YUMng', 'installed_action')) +CLIENT_YUMNG_VERSION_FAIL_ACTION = \ + Option("YUMng version fail action", + default="upgrade", + cf=('YUMng', 'version_fail_action')) +CLIENT_YUMNG_VERIFY_FAIL_ACTION = \ + Option("YUMng verify fail action", + default="reinstall", + cf=('YUMng', 'verify_fail_action')) +CLIENT_YUMNG_VERIFY_FLAGS = \ + Option("YUMng verify flags", + default=[], + cf=('YUMng', 'verify_flags'), + cook=list_split) # Logging options -LOGGING_FILE_PATH = Option('Set path of file log', default=None, - cmd='-o', odesc='<path>', cf=('logging', 'path')) +LOGGING_FILE_PATH = \ + Option('Set path of file log', + default=None, + cmd='-o', + odesc='<path>', + cf=('logging', 'path')) +LOGGING_SYSLOG = \ + Option('Log to syslog', + default=True, + cook=get_bool, + cf=('logging', 'syslog')) +DEBUG = \ + Option("Enable debugging output", + default=False, + cmd='-d', + cook=get_bool, + cf=('logging', 'debug')) +VERBOSE = \ + Option("Enable verbose output", + default=False, + cmd='-v', + cook=get_bool, + cf=('logging', 'verbose')) # Plugin-specific options -CFG_VALIDATION = Option('Run validation on Cfg files', default=True, - cf=('cfg', 'validation'), cmd='--cfg-validation', - long_arg=True, cook=get_bool) +CFG_VALIDATION = \ + Option('Run validation on Cfg files', + default=True, + cmd='--cfg-validation', + cf=('cfg', 'validation'), + long_arg=True, + cook=get_bool) + +# bcfg2-crypt options +ENCRYPT = \ + Option('Encrypt the specified file', + default=False, + cmd='--encrypt', + long_arg=True) +DECRYPT = \ + Option('Decrypt the specified file', + default=False, + cmd='--decrypt', + long_arg=True) +CRYPT_PASSPHRASE = \ + Option('Encryption passphrase (name or passphrase)', + default=None, + cmd='-p', + odesc='<passphrase>') +CRYPT_XPATH = \ + Option('XPath expression to select elements to encrypt', + default=None, + cmd='--xpath', + odesc='<xpath>', + long_arg=True) +CRYPT_PROPERTIES = \ + Option('Encrypt the specified file as a Properties file', + default=False, + cmd="--properties", + long_arg=True) +CRYPT_CFG = \ + Option('Encrypt the specified file as a Cfg file', + default=False, + cmd="--cfg", + long_arg=True) +CRYPT_REMOVE = \ + Option('Remove the plaintext file after encrypting', + default=False, + cmd="--remove", + long_arg=True) + +# Option groups +CLI_COMMON_OPTIONS = dict(configfile=CFILE, + debug=DEBUG, + help=HELP, + version=VERSION, + verbose=VERBOSE, + encoding=ENCODING, + logging=LOGGING_FILE_PATH, + syslog=LOGGING_SYSLOG) + +DAEMON_COMMON_OPTIONS = dict(daemon=DAEMON, + listen_all=SERVER_LISTEN_ALL) + +SERVER_COMMON_OPTIONS = dict(repo=SERVER_REPOSITORY, + plugins=SERVER_PLUGINS, + password=SERVER_PASSWORD, + filemonitor=SERVER_FILEMONITOR, + ignore=SERVER_FAM_IGNORE, + location=SERVER_LOCATION, + static=SERVER_STATIC, + key=SERVER_KEY, + cert=SERVER_CERT, + ca=SERVER_CA, + protocol=SERVER_PROTOCOL, + web_configfile=WEB_CFILE, + backend=SERVER_BACKEND) + +CRYPT_OPTIONS = dict(encrypt=ENCRYPT, + decrypt=DECRYPT, + passphrase=CRYPT_PASSPHRASE, + xpath=CRYPT_XPATH, + properties=CRYPT_PROPERTIES, + cfg=CRYPT_CFG, + remove=CRYPT_REMOVE) + +DRIVER_OPTIONS = \ + dict(apt_install_path=CLIENT_APT_TOOLS_INSTALL_PATH, + apt_var_path=CLIENT_APT_TOOLS_VAR_PATH, + apt_etc_path=CLIENT_SYSTEM_ETC_PATH, + portage_binpkgonly=CLIENT_PORTAGE_BINPKGONLY, + rpmng_installonly=CLIENT_RPMNG_INSTALLONLY, + rpmng_pkg_checks=CLIENT_RPMNG_PKG_CHECKS, + rpmng_pkg_verify=CLIENT_RPMNG_PKG_VERIFY, + rpmng_installed_action=CLIENT_RPMNG_INSTALLED_ACTION, + rpmng_erase_flags=CLIENT_RPMNG_ERASE_FLAGS, + rpmng_version_fail_action=CLIENT_RPMNG_VERSION_FAIL_ACTION, + rpmng_verify_fail_action=CLIENT_RPMNG_VERIFY_FAIL_ACTION, + rpmng_verify_flags=CLIENT_RPMNG_VERIFY_FLAGS, + yum24_installonly=CLIENT_YUM24_INSTALLONLY, + yum24_pkg_checks=CLIENT_YUM24_PKG_CHECKS, + yum24_pkg_verify=CLIENT_YUM24_PKG_VERIFY, + yum24_installed_action=CLIENT_YUM24_INSTALLED_ACTION, + yum24_erase_flags=CLIENT_YUM24_ERASE_FLAGS, + yum24_version_fail_action=CLIENT_YUM24_VERSION_FAIL_ACTION, + yum24_verify_fail_action=CLIENT_YUM24_VERIFY_FAIL_ACTION, + yum24_verify_flags=CLIENT_YUM24_VERIFY_FLAGS, + yum24_autodep=CLIENT_YUM24_AUTODEP, + yumng_pkg_checks=CLIENT_YUMNG_PKG_CHECKS, + yumng_pkg_verify=CLIENT_YUMNG_PKG_VERIFY, + yumng_installed_action=CLIENT_YUMNG_INSTALLED_ACTION, + yumng_version_fail_action=CLIENT_YUMNG_VERSION_FAIL_ACTION, + yumng_verify_fail_action=CLIENT_YUMNG_VERIFY_FAIL_ACTION, + yumng_verify_flags=CLIENT_YUMNG_VERIFY_FLAGS) + +CLIENT_COMMON_OPTIONS = \ + dict(extra=CLIENT_EXTRA_DISPLAY, + quick=CLIENT_QUICK, + lockfile=LOCKFILE, + drivers=CLIENT_DRIVERS, + dryrun=CLIENT_DRYRUN, + paranoid=CLIENT_PARANOID, + ppath=PARANOID_PATH, + max_copies=PARANOID_MAX_COPIES, + bundle=CLIENT_BUNDLE, + skipbundle=CLIENT_SKIPBUNDLE, + bundle_quick=CLIENT_BUNDLEQUICK, + indep=CLIENT_INDEP, + skipindep=CLIENT_SKIPINDEP, + file=CLIENT_FILE, + interactive=INTERACTIVE, + cache=CLIENT_CACHE, + profile=CLIENT_PROFILE, + remove=CLIENT_REMOVE, + server=SERVER_LOCATION, + user=CLIENT_USER, + password=SERVER_PASSWORD, + retries=CLIENT_RETRIES, + retry_delay=CLIENT_RETRY_DELAY, + kevlar=CLIENT_KEVLAR, + omit_lock_check=OMIT_LOCK_CHECK, + decision=CLIENT_DLIST, + servicemode=CLIENT_SERVICE_MODE, + key=CLIENT_KEY, + certificate=CLIENT_CERT, + ca=CLIENT_CA, + serverCN=CLIENT_SCNS, + timeout=CLIENT_TIMEOUT, + decision_list=CLIENT_DECISION_LIST) +CLIENT_COMMON_OPTIONS.update(DRIVER_OPTIONS) +CLIENT_COMMON_OPTIONS.update(CLI_COMMON_OPTIONS) + +DATABASE_COMMON_OPTIONS = dict(web_configfile=WEB_CFILE, + db_engine=DB_ENGINE, + db_name=DB_NAME, + db_user=DB_USER, + db_password=DB_PASSWORD, + db_host=DB_HOST, + db_port=DB_PORT, + time_zone=DJANGO_TIME_ZONE, + django_debug=DJANGO_DEBUG, + web_prefix=DJANGO_WEB_PREFIX) + class OptionParser(OptionSet): """ OptionParser bootstraps option parsing, getting the value of the config file """ - def __init__(self, args): + def __init__(self, args, argv=None, quiet=False): + if argv is None: + argv = sys.argv[1:] + # the bootstrap is always quiet, since it's running with a + # default config file and so might produce warnings otherwise self.Bootstrap = OptionSet([('configfile', CFILE)], quiet=True) - self.Bootstrap.parse(sys.argv[1:], do_getopt=False) - OptionSet.__init__(self, args, configfile=self.Bootstrap['configfile']) - self.optinfo = args + self.Bootstrap.parse(argv, do_getopt=False) + OptionSet.__init__(self, args, configfile=self.Bootstrap['configfile'], + quiet=quiet) + self.optinfo = copy.copy(args) def HandleEvent(self, event): - if not self['configfile'].endswith(event.filename): + if 'configfile' not in self or not isinstance(self['configfile'], str): + # we haven't parsed options yet, or CFILE wasn't included + # in the options + return + if event.filename != self['configfile']: print("Got event for unknown file: %s" % event.filename) return if event.code2str() == 'deleted': @@ -447,3 +1055,10 @@ class OptionParser(OptionSet): self.do_getopt = do_getopt OptionSet.parse(self, self.argv, do_getopt=self.do_getopt) + def add_option(self, name, opt): + self[name] = opt + self.optinfo[name] = opt + + def update(self, optdict): + dict.update(self, optdict) + self.optinfo.update(optdict) |