From dab1d03d81c538966d03fb9318a4588a9e803b44 Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Sat, 24 Mar 2012 11:20:07 -0500 Subject: Allow to run directly from a git checkout (#1037) Signed-off-by: Sol Jerome --- src/lib/Bcfg2/Options.py | 381 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 381 insertions(+) create mode 100644 src/lib/Bcfg2/Options.py (limited to 'src/lib/Bcfg2/Options.py') diff --git a/src/lib/Bcfg2/Options.py b/src/lib/Bcfg2/Options.py new file mode 100644 index 000000000..02ac8f646 --- /dev/null +++ b/src/lib/Bcfg2/Options.py @@ -0,0 +1,381 @@ +"""Option parsing library for utilities.""" + +import getopt +import os +import sys +import Bcfg2.Client.Tools +# Compatibility imports +from Bcfg2.Bcfg2Py3k import ConfigParser + +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 + +class Option(object): + cfpath = DEFAULT_CONFIG_LOCATION + __cfp = False + + def getCFP(self): + if not self.__cfp: + self.__cfp = ConfigParser.ConfigParser() + self.__cfp.readfp(open(self.cfpath)) + return self.__cfp + cfp = property(getCFP) + + 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): + self.desc = desc + self.default = default + self.cmd = cmd + self.long = long_arg + 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) + self.odesc = odesc + self.env = env + self.cf = cf + self.boolean = False + if not odesc and not cook: + self.boolean = True + self.cook = cook + + 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) + else: + msg += '%-25s' % ('') + msg += "%s\n" % self.desc + return msg + + def buildGetopt(self): + gstr = '' + if self.long: + return gstr + if self.cmd: + gstr = self.cmd[1] + if self.odesc: + gstr += ':' + return gstr + + def buildLongGetopt(self): + if self.odesc: + return self.cmd[2:]+'=' + else: + return self.cmd[2:] + + def parse(self, opts, rawopts): + if self.cmd and opts: + # Processing getopted data + optinfo = [opt[1] for opt in opts if opt[0] == self.cmd] + if optinfo: + if optinfo[0]: + self.value = self.get_cooked_value(optinfo[0]) + else: + self.value = True + return + if self.cmd and self.cmd in rawopts: + data = rawopts[rawopts.index(self.cmd) + 1] + self.value = self.get_cooked_value(data) + return + # No command line option found + if self.env and self.env in os.environ: + self.value = self.get_cooked_value(os.environ[self.env]) + return + if self.cf: + # FIXME: This is potentially masking a lot of errors + try: + self.value = self.get_cooked_value(self.cfp.get(*self.cf)) + return + except: + pass + # Default value not cooked + self.value = self.default + +class OptionSet(dict): + def __init__(self, *args): + dict.__init__(self, *args) + self.hm = self.buildHelpMessage() + + def buildGetopt(self): + return ''.join([opt.buildGetopt() for opt in list(self.values())]) + + def buildLongGetopt(self): + return [opt.buildLongGetopt() for opt in list(self.values()) if opt.long] + + def buildHelpMessage(self): + if hasattr(self, 'hm'): + return self.hm + return ' '.join([opt.buildHelpMessage() for opt in list(self.values())]) + + def helpExit(self, msg='', code=1): + if msg: + print(msg) + print("Usage:\n %s" % self.buildHelpMessage()) + raise SystemExit(code) + + def parse(self, argv, do_getopt=True): + '''Parse options from command line.''' + if do_getopt: + try: + opts, args = getopt.getopt(argv, self.buildGetopt(), + self.buildLongGetopt()) + except getopt.GetoptError: + err = sys.exc_info()[1] + self.helpExit(err) + if '-h' in argv: + self.helpExit('', 0) + self['args'] = args + for key in list(self.keys()): + if key == 'args': + continue + option = self[key] + if do_getopt: + option.parse(opts, []) + else: + option.parse([], argv) + if hasattr(option, 'value'): + val = option.value + self[key] = val + +list_split = lambda x:x.replace(' ','').split(',') +flist_split = lambda x:list_split(x.replace(':', '').lower()) + +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 + truelist = ["1", "yes", "True", "true", "on"] + falselist = ["0", "no", "False", "false", "off"] + if s in truelist: + return True + elif s in falselist: + return False + else: + raise ValueError + +# General options +CFILE = Option('Specify configuration file', DEFAULT_CONFIG_LOCATION, cmd='-C', + odesc='') +LOCKFILE = Option('Specify lockfile', + "/var/lock/bcfg2.run", + cf=('components', 'lockfile'), + odesc='') +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="") +INSTALL_PREFIX = Option('Installation location', cf=('server', 'prefix'), + default=DEFAULT_INSTALL_PREFIX, odesc='') +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='', + cf=('components', 'encoding')) +PARANOID_PATH = Option('Specify path for paranoid file backups', + default='/var/cache/bcfg2', cf=('paranoid', 'path'), + odesc='') +PARANOID_MAX_COPIES = Option('Specify the number of paranoid copies you want', + default=1, cf=('paranoid', 'max_copies'), + odesc='') +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='', + 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', + 'false', cf=('mdata', 'paranoid'), + odesc='Path paranoid setting') +MDATA_SENSITIVE = Option('Default Path sensitive setting', + 'false', cf=('mdata', 'sensitive'), + odesc='Path sensitive setting') + +# Server options +SERVER_REPOSITORY = Option('Server repository path', '/var/lib/bcfg2', + cf=('server', 'repository'), cmd='-Q', + odesc='') +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='', + long_arg=True) +SERVER_CERT = Option('Path to SSL certificate', default='/etc/bcfg2.key', + cf=('communication', 'certificate'), odesc='') +SERVER_CA = Option('Path to SSL CA Cert', default=None, + cf=('communication', 'ca'), odesc='') +SERVER_PASSWORD = Option('Communication Password', cmd='-x', odesc='', + cf=('communication', 'password'), default=False) +SERVER_PROTOCOL = Option('Server Protocol', cf=('communication', 'procotol'), + default='xmlrpc/ssl') +# Client options +CLIENT_KEY = Option('Path to SSL key', cf=('communication', 'key'), + default=None, cmd="--ssl-key", odesc='', + long_arg=True) +CLIENT_CERT = Option('Path to SSL certificate', default=None, cmd="--ssl-cert", + cf=('communication', 'certificate'), odesc='', + long_arg=True) +CLIENT_CA = Option('Path to SSL CA Cert', default=None, cmd="--ca-cert", + cf=('communication', 'ca'), odesc='', + long_arg=True) +CLIENT_SCNS = Option('List of server commonNames', default=None, cmd="--ssl-cns", + cf=('communication', 'serverCommonNames'), + odesc='', cook=list_split, + long_arg=True) +CLIENT_PROFILE = Option('Assert the given profile for the host', + default=False, cmd='-p', odesc="") +CLIENT_RETRIES = Option('The number of times to retry network communication', + default='3', cmd='-R', cf=('communication', 'retries'), + odesc="") +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="", cook=list_split, + default=Bcfg2.Client.Tools.default) +CLIENT_CACHE = Option('Store the configuration in a file', + default=False, cmd='-c', odesc="") +CLIENT_REMOVE = Option('Force removal of additional configuration items', + default=False, cmd='-r', odesc="") +CLIENT_BUNDLE = Option('Only configure the given bundle(s)', default=[], + cmd='-b', odesc='', 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='') +CLIENT_FILE = Option('Configure from a file rather than querying the server', + default=False, cmd='-f', odesc='') +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='') +CLIENT_SERVICE_MODE = Option('Set client service mode', default='default', + cmd='-s', odesc='') +CLIENT_TIMEOUT = Option('Set the client XML-RPC timeout', default=90, + cmd='-t', cf=('communication', 'timeout'), + odesc='') + +# 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') + +# Logging options +LOGGING_FILE_PATH = Option('Set path of file log', default=None, + cmd='-o', odesc='', cf=('logging', 'path')) + +class OptionParser(OptionSet): + """ + OptionParser bootstraps option parsing, + getting the value of the config file + """ + def __init__(self, args): + self.Bootstrap = OptionSet([('configfile', CFILE)]) + self.Bootstrap.parse(sys.argv[1:], do_getopt=False) + if self.Bootstrap['configfile'] != Option.cfpath: + Option.cfpath = self.Bootstrap['configfile'] + Option.__cfp = False + OptionSet.__init__(self, args) + try: + f = open(Option.cfpath, 'r') + f.close() + except IOError: + e = sys.exc_info()[1] + print("Warning! Unable to read specified configuration file: %s" % e) -- cgit v1.2.3-1-g7c22