From 68804df4b5ccc251c788a99b5682bb9aba973cb9 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Thu, 17 Jan 2013 12:53:20 -0500 Subject: read default Path metadata from config file properly --- src/lib/Bcfg2/Server/Plugin/helpers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py index acf6af1f8..41c450b4e 100644 --- a/src/lib/Bcfg2/Server/Plugin/helpers.py +++ b/src/lib/Bcfg2/Server/Plugin/helpers.py @@ -25,6 +25,7 @@ except ImportError: #: A dict containing default metadata for Path entries from bcfg2.conf DEFAULT_FILE_METADATA = Bcfg2.Options.OptionParser(dict( + configfile=Bcfg2.Options.CFILE, owner=Bcfg2.Options.MDATA_OWNER, group=Bcfg2.Options.MDATA_GROUP, mode=Bcfg2.Options.MDATA_MODE, @@ -32,8 +33,9 @@ DEFAULT_FILE_METADATA = Bcfg2.Options.OptionParser(dict( important=Bcfg2.Options.MDATA_IMPORTANT, paranoid=Bcfg2.Options.MDATA_PARANOID, sensitive=Bcfg2.Options.MDATA_SENSITIVE)) -DEFAULT_FILE_METADATA.parse(sys.argv[1:]) +DEFAULT_FILE_METADATA.parse([Bcfg2.Options.CFILE.cmd, Bcfg2.Options.CFILE]) del DEFAULT_FILE_METADATA['args'] +del DEFAULT_FILE_METADATA['configfile'] LOGGER = logging.getLogger(__name__) -- cgit v1.2.3-1-g7c22 From 9be9cfec322518f764be9766b27d24132fc6a66f Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Wed, 16 Jan 2013 13:28:06 -0500 Subject: added module-level OptionParser to avoid passing it as an argument or global all over --- src/lib/Bcfg2/Client/Client.py | 4 +- src/lib/Bcfg2/Encryption.py | 53 ++++++----------- src/lib/Bcfg2/Options.py | 68 +++++++++++++++++++--- src/lib/Bcfg2/Server/Admin/Compare.py | 4 +- src/lib/Bcfg2/Server/Admin/Init.py | 24 ++++---- src/lib/Bcfg2/Server/Admin/Perf.py | 22 +++---- src/lib/Bcfg2/Server/Admin/Pull.py | 4 +- src/lib/Bcfg2/Server/Admin/Reports.py | 4 +- src/lib/Bcfg2/Server/Admin/Snapshots.py | 4 +- src/lib/Bcfg2/Server/Admin/Syncdb.py | 10 ++-- src/lib/Bcfg2/Server/Admin/Xcmd.py | 29 +++++---- src/lib/Bcfg2/Server/Admin/__init__.py | 20 +++---- src/lib/Bcfg2/Server/BuiltinCore.py | 4 +- src/lib/Bcfg2/Server/CherryPyCore.py | 4 +- src/lib/Bcfg2/Server/Core.py | 65 +++++++++++---------- src/lib/Bcfg2/Server/Plugin/helpers.py | 35 +++++------ src/lib/Bcfg2/Server/Plugins/Bundler.py | 19 +++--- .../Plugins/Cfg/CfgAuthorizedKeysGenerator.py | 8 +-- .../Server/Plugins/Cfg/CfgCheetahGenerator.py | 4 +- .../Server/Plugins/Cfg/CfgEncryptedGenerator.py | 8 +-- .../Plugins/Cfg/CfgEncryptedGenshiGenerator.py | 7 +-- .../Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py | 14 ++--- .../Server/Plugins/Cfg/CfgPrivateKeyCreator.py | 34 +++++------ src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py | 29 ++++----- src/lib/Bcfg2/Server/Plugins/Properties.py | 22 +++---- src/lib/Bcfg2/Server/models.py | 13 ++--- src/lib/Bcfg2/settings.py | 28 ++++----- src/sbin/bcfg2 | 7 +-- src/sbin/bcfg2-admin | 4 +- src/sbin/bcfg2-crypt | 34 +++++------ src/sbin/bcfg2-info | 10 ++-- src/sbin/bcfg2-lint | 40 ++++++------- src/sbin/bcfg2-report-collector | 4 +- src/sbin/bcfg2-server | 6 +- src/sbin/bcfg2-test | 4 +- 35 files changed, 322 insertions(+), 327 deletions(-) (limited to 'src') diff --git a/src/lib/Bcfg2/Client/Client.py b/src/lib/Bcfg2/Client/Client.py index 45e0b64e6..ea898c42b 100644 --- a/src/lib/Bcfg2/Client/Client.py +++ b/src/lib/Bcfg2/Client/Client.py @@ -22,12 +22,12 @@ from subprocess import Popen, PIPE class Client(object): """ The main Bcfg2 client class """ - def __init__(self, setup): + def __init__(self): self.toolset = None self.tools = None self.config = None self._proxy = None - self.setup = setup + self.setup = Bcfg2.Options.get_option_parser() if self.setup['debug']: level = logging.DEBUG diff --git a/src/lib/Bcfg2/Encryption.py b/src/lib/Bcfg2/Encryption.py index 1eb5aaeb2..341956e03 100755 --- a/src/lib/Bcfg2/Encryption.py +++ b/src/lib/Bcfg2/Encryption.py @@ -3,6 +3,7 @@ handling encryption in Bcfg2. See :ref:`server-encryption` for more details. """ import os +import Bcfg2.Options from M2Crypto import Rand from M2Crypto.EVP import Cipher, EVPError from Bcfg2.Compat import StringIO, md5, b64encode, b64decode @@ -17,13 +18,6 @@ ENCRYPT = 1 #: makes our code more readable. DECRYPT = 0 -#: Default cipher algorithm. To get a full list of valid algorithms, -#: you can run:: -#: -#: openssl list-cipher-algorithms | grep -v ' => ' | \ -#: tr 'A-Z-' 'a-z_' | sort -u -ALGORITHM = "aes_256_cbc" - #: Default initialization vector. For best security, you should use a #: unique IV for each message. :func:`ssl_encrypt` does this in an #: automated fashion. @@ -36,6 +30,16 @@ CFG_SECTION = "encryption" #: The config option used to store the algorithm CFG_ALGORITHM = "algorithm" +#: Default cipher algorithm. To get a full list of valid algorithms, +#: you can run:: +#: +#: openssl list-cipher-algorithms | grep -v ' => ' | \ +#: tr 'A-Z-' 'a-z_' | sort -u +ALGORITHM = Bcfg2.Options.get_option_parser().cfp.get( # pylint: disable=E1103 + CFG_SECTION, + CFG_ALGORITHM, + default="aes_256_cbc").lower().replace("-", "_") + Rand.rand_seed(os.urandom(1024)) @@ -152,58 +156,37 @@ def ssl_encrypt(plaintext, passwd, algorithm=ALGORITHM, salt=None): return b64encode("Salted__" + salt + crypted) + "\n" -def get_algorithm(setup): - """ Get the cipher algorithm from the config file. This is used - in case someone uses the OpenSSL algorithm name (e.g., - "AES-256-CBC") instead of the M2Crypto name (e.g., "aes_256_cbc"), - and to handle errors in a sensible way and deduplicate this code. - - :param setup: The Bcfg2 option set to extract passphrases from - :type setup: Bcfg2.Options.OptionParser - :returns: dict - a dict of ````: ```` - """ - return setup.cfp.get(CFG_SECTION, CFG_ALGORITHM, - default=ALGORITHM).lower().replace("-", "_") - - -def get_passphrases(setup): +def get_passphrases(): """ Get all candidate encryption passphrases from the config file. - :param setup: The Bcfg2 option set to extract passphrases from - :type setup: Bcfg2.Options.OptionParser :returns: dict - a dict of ````: ```` """ - section = CFG_SECTION - if setup.cfp.has_section(section): - return dict([(o, setup.cfp.get(section, o)) - for o in setup.cfp.options(section) + setup = Bcfg2.Options.get_option_parser() + if setup.cfp.has_section(CFG_SECTION): + return dict([(o, setup.cfp.get(CFG_SECTION, o)) + for o in setup.cfp.options(CFG_SECTION) if o != CFG_ALGORITHM]) else: return dict() -def bruteforce_decrypt(crypted, passphrases=None, setup=None, - algorithm=ALGORITHM): +def bruteforce_decrypt(crypted, passphrases=None, algorithm=ALGORITHM): """ Convenience method to decrypt the given encrypted string by trying the given passphrases or all passphrases (as returned by :func:`get_passphrases`) sequentially until one is found that works. - Either ``passphrases`` or ``setup`` must be provided. - :param crypted: The data to decrypt :type crypted: string :param passphrases: The passphrases to try. :type passphrases: list - :param setup: A Bcfg2 option set to extract passphrases from - :type setup: Bcfg2.Options.OptionParser :param algorithm: The cipher algorithm to use :type algorithm: string :returns: string - The decrypted data :raises: :class:`M2Crypto.EVP.EVPError`, if the data cannot be decrypted """ if passphrases is None: - passphrases = get_passphrases(setup).values() + passphrases = get_passphrases().values() for passwd in passphrases: try: return ssl_decrypt(crypted, passwd, algorithm=algorithm) diff --git a/src/lib/Bcfg2/Options.py b/src/lib/Bcfg2/Options.py index c0a274e23..780619f3b 100644 --- a/src/lib/Bcfg2/Options.py +++ b/src/lib/Bcfg2/Options.py @@ -1106,6 +1106,14 @@ CRYPT_OPTIONS = dict(encrypt=ENCRYPT, cfg=CRYPT_CFG, remove=CRYPT_REMOVE) +PATH_METADATA_OPTIONS = dict(owner=MDATA_OWNER, + group=MDATA_GROUP, + mode=MDATA_MODE, + secontext=MDATA_SECONTEXT, + important=MDATA_IMPORTANT, + paranoid=MDATA_PARANOID, + sensitive=MDATA_SENSITIVE) + DRIVER_OPTIONS = \ dict(apt_install_path=CLIENT_APT_TOOLS_INSTALL_PATH, apt_var_path=CLIENT_APT_TOOLS_VAR_PATH, @@ -1190,10 +1198,11 @@ REPORTING_COMMON_OPTIONS = dict(reporting_file_limit=REPORTING_FILE_LIMIT, class OptionParser(OptionSet): - """ - OptionParser bootstraps option parsing, - getting the value of the config file - """ + """ OptionParser bootstraps option parsing, getting the value of + the config file. This should only be instantiated by + :func:`get_option_parser`, below, not by individual plugins or + scripts. """ + def __init__(self, args, argv=None, quiet=False): if argv is None: argv = sys.argv[1:] @@ -1208,17 +1217,21 @@ class OptionParser(OptionSet): self.argv = [] self.do_getopt = True - def reparse(self): + def reparse(self, argv=None, do_getopt=None): """ parse the options again, taking any changes (e.g., to the config file) into account """ for key, opt in self.optinfo.items(): self[key] = opt - if "args" not in self.optinfo: + if "args" not in self.optinfo and "args" in self: del self['args'] - self.parse(self.argv, self.do_getopt) + if do_getopt is None: + do_getopt = self.do_getopt + if argv is None: + argv = self.argv + self.parse(argv, do_getopt) - def parse(self, argv, do_getopt=True): - self.argv = argv + def parse(self, argv=None, do_getopt=True): + self.argv = argv or sys.argv[1:] self.do_getopt = do_getopt OptionSet.parse(self, self.argv, do_getopt=self.do_getopt) @@ -1227,6 +1240,43 @@ class OptionParser(OptionSet): self[name] = opt self.optinfo[name] = opt + def add_options(self, options): + """ Add a set of options to the parser """ + self.update(options) + self.optinfo.update(options) + def update(self, optdict): dict.update(self, optdict) self.optinfo.update(optdict) + + +#: A module-level OptionParser object that all plugins, etc., can use. +#: This should not be used directly, but retrieved via +#: :func:`get_option_parser`. +_PARSER = None + + +def get_option_parser(args=None, argv=None, quiet=False): + """ Get an OptionParser object. If :attr:`_PARSER` already + exists, that will be used; if not, a new OptionParser object will + be created. + + If ``args`` or ``argv`` are given, then a new OptionParser object + will be instantiated regardless of whether one already exists. + + :param args: The argument set to parse. This is required on the + first invocation of :func:`get_option_parser`. + :type args: dict of :class:`Bcfg2.Options.Option` objects + :param argv: The command-line argument list. If this is not + provided, :attr:`sys.argv` will be used. + :type argv: list of strings + :param quiet: Be quiet when bootstrapping the argument parser. + :type quiet: bool + :returns: :class:`Bcfg2.Options.OptionParser` + """ + global _PARSER # pylint: disable=W0603 + if _PARSER is None or args is not None or argv is not None: + if args is None: + args = CLI_COMMON_OPTIONS + _PARSER = OptionParser(args, argv=argv, quiet=quiet) + return _PARSER diff --git a/src/lib/Bcfg2/Server/Admin/Compare.py b/src/lib/Bcfg2/Server/Admin/Compare.py index c56dd0a8f..820271a2f 100644 --- a/src/lib/Bcfg2/Server/Admin/Compare.py +++ b/src/lib/Bcfg2/Server/Admin/Compare.py @@ -9,8 +9,8 @@ class Compare(Bcfg2.Server.Admin.Mode): __usage__ = (" \n\n" " -r\trecursive") - def __init__(self, setup): - Bcfg2.Server.Admin.Mode.__init__(self, setup) + def __init__(self): + Bcfg2.Server.Admin.Mode.__init__(self) self.important = {'Path': ['name', 'type', 'owner', 'group', 'mode', 'important', 'paranoid', 'sensitive', 'dev_type', 'major', 'minor', 'prune', diff --git a/src/lib/Bcfg2/Server/Admin/Init.py b/src/lib/Bcfg2/Server/Admin/Init.py index 14065980d..cf4bd4c0c 100644 --- a/src/lib/Bcfg2/Server/Admin/Init.py +++ b/src/lib/Bcfg2/Server/Admin/Init.py @@ -143,14 +143,9 @@ def create_conf(confpath, confdata): class Init(Bcfg2.Server.Admin.Mode): """Interactively initialize a new repository.""" - options = {'configfile': Bcfg2.Options.CFILE, - 'plugins': Bcfg2.Options.SERVER_PLUGINS, - 'proto': Bcfg2.Options.SERVER_PROTOCOL, - 'repo': Bcfg2.Options.SERVER_REPOSITORY, - 'sendmail': Bcfg2.Options.SENDMAIL_PATH} - - def __init__(self, setup): - Bcfg2.Server.Admin.Mode.__init__(self, setup) + + def __init__(self): + Bcfg2.Server.Admin.Mode.__init__(self) self.data = dict() self.plugins = Bcfg2.Options.SERVER_PLUGINS.default @@ -177,9 +172,16 @@ class Init(Bcfg2.Server.Admin.Mode): Bcfg2.Server.Admin.Mode.__call__(self, args) # Parse options - opts = Bcfg2.Options.OptionParser(self.options) - opts.parse(args) - self._set_defaults(opts) + setup = Bcfg2.Options.get_option_parser() + setup.add_options(dict(configfile=Bcfg2.Options.CFILE, + plugins=Bcfg2.Options.SERVER_PLUGINS, + proto=Bcfg2.Options.SERVER_PROTOCOL, + repo=Bcfg2.Options.SERVER_REPOSITORY, + sendmail=Bcfg2.Options.SENDMAIL_PATH)) + opts = sys.argv[1:] + opts.remove(self.__class__.__name__.lower()) + setup.reparse(argv=opts) + self._set_defaults(setup) # Prompt the user for input self._prompt_config() diff --git a/src/lib/Bcfg2/Server/Admin/Perf.py b/src/lib/Bcfg2/Server/Admin/Perf.py index 86eb6810d..7448855ce 100644 --- a/src/lib/Bcfg2/Server/Admin/Perf.py +++ b/src/lib/Bcfg2/Server/Admin/Perf.py @@ -11,17 +11,17 @@ class Perf(Bcfg2.Server.Admin.Mode): def __call__(self, args): output = [('Name', 'Min', 'Max', 'Mean', 'Count')] - optinfo = { - 'ca': Bcfg2.Options.CLIENT_CA, - 'certificate': Bcfg2.Options.CLIENT_CERT, - 'key': Bcfg2.Options.SERVER_KEY, - 'password': Bcfg2.Options.SERVER_PASSWORD, - 'server': Bcfg2.Options.SERVER_LOCATION, - 'user': Bcfg2.Options.CLIENT_USER, - 'timeout': Bcfg2.Options.CLIENT_TIMEOUT, - } - setup = Bcfg2.Options.OptionParser(optinfo) - setup.parse(sys.argv[1:]) + setup = Bcfg2.Options.get_option_parser() + setup.add_options(dict(ca=Bcfg2.Options.CLIENT_CA, + certificate=Bcfg2.Options.CLIENT_CERT, + key=Bcfg2.Options.SERVER_KEY, + password=Bcfg2.Options.SERVER_PASSWORD, + server=Bcfg2.Options.SERVER_LOCATION, + user=Bcfg2.Options.CLIENT_USER, + timeout=Bcfg2.Options.CLIENT_TIMEOUT)) + opts = sys.argv[1:] + opts.remove(self.__class__.__name__.lower()) + setup.reparse(argv=opts) proxy = Bcfg2.Proxy.ComponentProxy(setup['server'], setup['user'], setup['password'], diff --git a/src/lib/Bcfg2/Server/Admin/Pull.py b/src/lib/Bcfg2/Server/Admin/Pull.py index 130e85b67..1905fac3c 100644 --- a/src/lib/Bcfg2/Server/Admin/Pull.py +++ b/src/lib/Bcfg2/Server/Admin/Pull.py @@ -22,8 +22,8 @@ class Pull(Bcfg2.Server.Admin.MetadataCore): "-I", "interactive", "-s", "stdin")) - def __init__(self, setup): - Bcfg2.Server.Admin.MetadataCore.__init__(self, setup) + def __init__(self): + Bcfg2.Server.Admin.MetadataCore.__init__(self) self.log = False self.mode = 'interactive' diff --git a/src/lib/Bcfg2/Server/Admin/Reports.py b/src/lib/Bcfg2/Server/Admin/Reports.py index 6e313e84b..bb5ee352b 100644 --- a/src/lib/Bcfg2/Server/Admin/Reports.py +++ b/src/lib/Bcfg2/Server/Admin/Reports.py @@ -69,8 +69,8 @@ class Reports(Bcfg2.Server.Admin.Mode): " Django commands:\n " \ + "\n ".join(django_commands)) - def __init__(self, setup): - Bcfg2.Server.Admin.Mode.__init__(self, setup) + def __init__(self): + Bcfg2.Server.Admin.Mode.__init__(self) try: import south except ImportError: diff --git a/src/lib/Bcfg2/Server/Admin/Snapshots.py b/src/lib/Bcfg2/Server/Admin/Snapshots.py index c2d279391..bf44d1451 100644 --- a/src/lib/Bcfg2/Server/Admin/Snapshots.py +++ b/src/lib/Bcfg2/Server/Admin/Snapshots.py @@ -21,8 +21,8 @@ class Snapshots(Bcfg2.Server.Admin.Mode): 'package': Package, 'snapshot': Snapshot} - def __init__(self, setup): - Bcfg2.Server.Admin.Mode.__init__(self, setup) + def __init__(self): + Bcfg2.Server.Admin.Mode.__init__(self) self.session = Bcfg2.Server.Snapshots.setup_session(self.configfile) self.cfile = self.configfile diff --git a/src/lib/Bcfg2/Server/Admin/Syncdb.py b/src/lib/Bcfg2/Server/Admin/Syncdb.py index 4ba840b86..84ad93ae0 100644 --- a/src/lib/Bcfg2/Server/Admin/Syncdb.py +++ b/src/lib/Bcfg2/Server/Admin/Syncdb.py @@ -8,15 +8,17 @@ from django.core.management import setup_environ, call_command class Syncdb(Bcfg2.Server.Admin.Mode): """ Sync the Django ORM with the configured database """ - options = {'configfile': Bcfg2.Options.WEB_CFILE} def __call__(self, args): # Parse options - opts = Bcfg2.Options.OptionParser(self.options) - opts.parse(args) + setup = Bcfg2.Options.get_option_parser() + setup.add_option("web_configfile", Bcfg2.Options.WEB_CFILE) + opts = sys.argv[1:] + opts.remove(self.__class__.__name__.lower()) + setup.reparse(argv=opts) setup_environ(Bcfg2.settings) - Bcfg2.Server.models.load_models(cfile=opts['configfile']) + Bcfg2.Server.models.load_models(cfile=setup['web_configfile']) try: call_command("syncdb", interactive=False, verbosity=0) diff --git a/src/lib/Bcfg2/Server/Admin/Xcmd.py b/src/lib/Bcfg2/Server/Admin/Xcmd.py index 79eeebc7c..7f9f32816 100644 --- a/src/lib/Bcfg2/Server/Admin/Xcmd.py +++ b/src/lib/Bcfg2/Server/Admin/Xcmd.py @@ -12,17 +12,17 @@ class Xcmd(Bcfg2.Server.Admin.Mode): __usage__ = "" def __call__(self, args): - optinfo = { - 'server': Bcfg2.Options.SERVER_LOCATION, - 'user': Bcfg2.Options.CLIENT_USER, - 'password': Bcfg2.Options.SERVER_PASSWORD, - 'key': Bcfg2.Options.SERVER_KEY, - 'certificate': Bcfg2.Options.CLIENT_CERT, - 'ca': Bcfg2.Options.CLIENT_CA, - 'timeout': Bcfg2.Options.CLIENT_TIMEOUT, - } - setup = Bcfg2.Options.OptionParser(optinfo) - setup.parse(args) + setup = Bcfg2.Options.get_option_parser() + setup.add_options(dict(ca=Bcfg2.Options.CLIENT_CA, + certificate=Bcfg2.Options.CLIENT_CERT, + key=Bcfg2.Options.SERVER_KEY, + password=Bcfg2.Options.SERVER_PASSWORD, + server=Bcfg2.Options.SERVER_LOCATION, + user=Bcfg2.Options.CLIENT_USER, + timeout=Bcfg2.Options.CLIENT_TIMEOUT)) + opts = sys.argv[1:] + opts.remove(self.__class__.__name__.lower()) + setup.reparse(argv=opts) Bcfg2.Proxy.RetryMethod.max_retries = 1 proxy = Bcfg2.Proxy.ComponentProxy(setup['server'], setup['user'], @@ -34,12 +34,9 @@ class Xcmd(Bcfg2.Server.Admin.Mode): if len(setup['args']) == 0: print("Usage: xcmd ") return - cmd = setup['args'][0] - args = () - if len(setup['args']) > 1: - args = tuple(setup['args'][1:]) + cmd = args[0] try: - data = getattr(proxy, cmd)(*args) + data = getattr(proxy, cmd)(*setup['args']) except xmlrpclib.Fault: flt = sys.exc_info()[1] if flt.faultCode == 7: diff --git a/src/lib/Bcfg2/Server/Admin/__init__.py b/src/lib/Bcfg2/Server/Admin/__init__.py index 19175533f..20577633a 100644 --- a/src/lib/Bcfg2/Server/Admin/__init__.py +++ b/src/lib/Bcfg2/Server/Admin/__init__.py @@ -35,15 +35,15 @@ class Mode(object): __usage__ = None __args__ = [] - def __init__(self, setup): - self.setup = setup - self.configfile = setup['configfile'] + def __init__(self): + self.setup = Bcfg2.Options.get_option_parser() + self.configfile = self.setup['configfile'] self.__cfp = False self.log = logging.getLogger('Bcfg2.Server.Admin.Mode') usage = "bcfg2-admin %s" % self.__class__.__name__.lower() if self.__usage__ is not None: usage += " " + self.__usage__ - setup.hm = usage + self.setup.hm = usage def getCFP(self): """ get a config parser for the Bcfg2 config file """ @@ -129,19 +129,19 @@ class MetadataCore(Mode): __plugin_whitelist__ = None __plugin_blacklist__ = None - def __init__(self, setup): - Mode.__init__(self, setup) + def __init__(self): + Mode.__init__(self) if self.__plugin_whitelist__ is not None: - setup['plugins'] = [p for p in setup['plugins'] + self.setup['plugins'] = [p for p in self.setup['plugins'] if p in self.__plugin_whitelist__] elif self.__plugin_blacklist__ is not None: - setup['plugins'] = [p for p in setup['plugins'] + self.setup['plugins'] = [p for p in self.setup['plugins'] if p not in self.__plugin_blacklist__] # admin modes don't need to watch for changes. one shot is fine here. - setup['filemonitor'] = 'pseudo' + self.setup['filemonitor'] = 'pseudo' try: - self.bcore = Bcfg2.Server.Core.BaseCore(setup) + self.bcore = Bcfg2.Server.Core.BaseCore() except Bcfg2.Server.Core.CoreInitError: msg = sys.exc_info()[1] self.errExit("Core load failed: %s" % msg) diff --git a/src/lib/Bcfg2/Server/BuiltinCore.py b/src/lib/Bcfg2/Server/BuiltinCore.py index 4d7453840..14b64ff40 100644 --- a/src/lib/Bcfg2/Server/BuiltinCore.py +++ b/src/lib/Bcfg2/Server/BuiltinCore.py @@ -22,8 +22,8 @@ class Core(BaseCore): """ The built-in server core """ name = 'bcfg2-server' - def __init__(self, setup): - BaseCore.__init__(self, setup) + def __init__(self): + BaseCore.__init__(self) #: The :class:`Bcfg2.SSLServer.XMLRPCServer` instance powering #: this server core diff --git a/src/lib/Bcfg2/Server/CherryPyCore.py b/src/lib/Bcfg2/Server/CherryPyCore.py index d097fd08f..79768df20 100644 --- a/src/lib/Bcfg2/Server/CherryPyCore.py +++ b/src/lib/Bcfg2/Server/CherryPyCore.py @@ -36,8 +36,8 @@ class Core(BaseCore): _cp_config = {'tools.xmlrpc_error.on': True, 'tools.bcfg2_authn.on': True} - def __init__(self, setup): - BaseCore.__init__(self, setup) + def __init__(self): + BaseCore.__init__(self) cherrypy.tools.bcfg2_authn = cherrypy.Tool('on_start_resource', self.do_authn) diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py index 37da4a4b6..0ef20dfac 100644 --- a/src/lib/Bcfg2/Server/Core.py +++ b/src/lib/Bcfg2/Server/Core.py @@ -2,21 +2,22 @@ implementations inherit from. """ import os -import atexit -import logging -import select import sys -import threading import time +import select +import atexit +import logging import inspect +import threading import lxml.etree -import Bcfg2.settings import Bcfg2.Server import Bcfg2.Logger -import Bcfg2.Server.FileMonitor -from Bcfg2.Cache import Cache +import Bcfg2.settings import Bcfg2.Statistics +import Bcfg2.Server.FileMonitor from itertools import chain +from Bcfg2.Cache import Cache +from Bcfg2.Options import get_option_parser from Bcfg2.Compat import xmlrpclib # pylint: disable=W0622 from Bcfg2.Server.Plugin import PluginInitError, PluginExecutionError, \ track_statistics @@ -85,23 +86,23 @@ class BaseCore(object): and modules. All core implementations must inherit from ``BaseCore``. """ - def __init__(self, setup): # pylint: disable=R0912,R0915 + def __init__(self): # pylint: disable=R0912,R0915 """ - :param setup: A Bcfg2 options dict - :type setup: Bcfg2.Options.OptionParser - .. automethod:: _daemonize .. automethod:: _run .. automethod:: _block .. ----- .. automethod:: _file_monitor_thread """ + #: The Bcfg2 options dict + self.setup = get_option_parser() + #: The Bcfg2 repository directory - self.datastore = setup['repo'] + self.datastore = self.setup['repo'] - if setup['debug']: + if self.setup['debug']: level = logging.DEBUG - elif setup['verbose']: + elif self.setup['verbose']: level = logging.INFO else: level = logging.WARNING @@ -112,8 +113,8 @@ class BaseCore(object): # setup_logging and the console will get DEBUG output. Bcfg2.Logger.setup_logging('bcfg2-server', to_console=logging.INFO, - to_syslog=setup['syslog'], - to_file=setup['logging'], + to_syslog=self.setup['syslog'], + to_file=self.setup['logging'], level=level) #: A :class:`logging.Logger` object for use by the core @@ -121,16 +122,16 @@ class BaseCore(object): try: filemonitor = \ - Bcfg2.Server.FileMonitor.available[setup['filemonitor']] + Bcfg2.Server.FileMonitor.available[self.setup['filemonitor']] except KeyError: self.logger.error("File monitor driver %s not available; " - "forcing to default" % setup['filemonitor']) + "forcing to default" % self.setup['filemonitor']) filemonitor = Bcfg2.Server.FileMonitor.available['default'] famargs = dict(ignore=[], debug=False) - if 'ignore' in setup: - famargs['ignore'] = setup['ignore'] - if 'debug' in setup: - famargs['debug'] = setup['debug'] + if 'ignore' in self.setup: + famargs['ignore'] = self.setup['ignore'] + if 'debug' in self.setup: + famargs['debug'] = self.setup['debug'] try: #: The :class:`Bcfg2.Server.FileMonitor.FileMonitor` @@ -138,12 +139,13 @@ class BaseCore(object): #: changes. self.fam = filemonitor(**famargs) except IOError: - msg = "Failed to instantiate fam driver %s" % setup['filemonitor'] + msg = "Failed to instantiate fam driver %s" % \ + self.setup['filemonitor'] self.logger.error(msg, exc_info=1) raise CoreInitError(msg) #: Path to bcfg2.conf - self.cfile = setup['configfile'] + self.cfile = self.setup['configfile'] #: Dict of plugins that are enabled. Keys are the plugin #: names (just the plugin name, in the correct case; e.g., @@ -161,9 +163,6 @@ class BaseCore(object): #: :class:`Bcfg2.Server.Plugin.interfaces.Version` plugin. self.revision = '-1' - #: The Bcfg2 options dict - self.setup = setup - atexit.register(self.shutdown) #: Threading event to signal worker threads (e.g., @@ -217,10 +216,10 @@ class BaseCore(object): self.logger.error("Failed to set ownership of database " "at %s: %s" % (db_settings['NAME'], err)) - if '' in setup['plugins']: - setup['plugins'].remove('') + if '' in self.setup['plugins']: + self.setup['plugins'].remove('') - for plugin in setup['plugins']: + for plugin in self.setup['plugins']: if not plugin in self.plugins: self.init_plugin(plugin) # Remove blacklisted plugins @@ -283,12 +282,12 @@ class BaseCore(object): self.connectors = self.plugins_by_type(Bcfg2.Server.Plugin.Connector) #: The CA that signed the server cert - self.ca = setup['ca'] + self.ca = self.setup['ca'] #: The FAM :class:`threading.Thread`, #: :func:`_file_monitor_thread` self.fam_thread = \ - threading.Thread(name="%sFAMThread" % setup['filemonitor'], + threading.Thread(name="%sFAMThread" % self.setup['filemonitor'], target=self._file_monitor_thread) #: A :func:`threading.Lock` for use by @@ -393,8 +392,10 @@ class BaseCore(object): """ Get the client :attr:`metadata_cache` mode. Options are off, initial, cautious, aggressive, on (synonym for cautious). See :ref:`server-caching` for more details. """ + # pylint: disable=E1103 mode = self.setup.cfp.get("caching", "client_metadata", default="off").lower() + # pylint: enable=E1103 if mode == "on": return "cautious" else: diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py index 41c450b4e..0df568045 100644 --- a/src/lib/Bcfg2/Server/Plugin/helpers.py +++ b/src/lib/Bcfg2/Server/Plugin/helpers.py @@ -23,20 +23,6 @@ try: except ImportError: HAS_DJANGO = False -#: A dict containing default metadata for Path entries from bcfg2.conf -DEFAULT_FILE_METADATA = Bcfg2.Options.OptionParser(dict( - configfile=Bcfg2.Options.CFILE, - owner=Bcfg2.Options.MDATA_OWNER, - group=Bcfg2.Options.MDATA_GROUP, - mode=Bcfg2.Options.MDATA_MODE, - secontext=Bcfg2.Options.MDATA_SECONTEXT, - important=Bcfg2.Options.MDATA_IMPORTANT, - paranoid=Bcfg2.Options.MDATA_PARANOID, - sensitive=Bcfg2.Options.MDATA_SENSITIVE)) -DEFAULT_FILE_METADATA.parse([Bcfg2.Options.CFILE.cmd, Bcfg2.Options.CFILE]) -del DEFAULT_FILE_METADATA['args'] -del DEFAULT_FILE_METADATA['configfile'] - LOGGER = logging.getLogger(__name__) #: a compiled regular expression for parsing info and :info files @@ -51,7 +37,20 @@ INFO_REGEX = re.compile('owner:(\s)*(?P\S+)|' + 'mtime:(\s)*(?P\w+)|') -def bind_info(entry, metadata, infoxml=None, default=DEFAULT_FILE_METADATA): +def default_path_metadata(): + """ Get the default Path entry metadata from the config. + + :returns: dict of metadata attributes and their default values + """ + attrs = Bcfg2.Options.PATH_METADATA_OPTIONS.keys() + setup = Bcfg2.Options.get_option_parser() + if not set(attrs).issubset(setup.keys()): + setup.add_options(Bcfg2.Options.PATH_METADATA_OPTIONS) + setup.reparse(argv=[Bcfg2.Options.CFILE.cmd, Bcfg2.Options.CFILE]) + return dict([(k, setup[k]) for k in attrs]) + + +def bind_info(entry, metadata, infoxml=None, default=None): """ Bind the file metadata in the given :class:`Bcfg2.Server.Plugin.helpers.InfoXML` object to the given entry. @@ -68,6 +67,8 @@ def bind_info(entry, metadata, infoxml=None, default=DEFAULT_FILE_METADATA): :returns: None :raises: :class:`Bcfg2.Server.Plugin.exceptions.PluginExecutionError` """ + if default is None: + default = default_path_metadata() for attr, val in list(default.items()): entry.set(attr, val) if infoxml: @@ -1156,7 +1157,7 @@ class EntrySet(Debuggable): self.path = path self.entry_type = entry_type self.entries = {} - self.metadata = DEFAULT_FILE_METADATA.copy() + self.metadata = default_path_metadata() self.infoxml = None self.encoding = encoding @@ -1378,7 +1379,7 @@ class EntrySet(Debuggable): if event.filename == 'info.xml': self.infoxml = None elif event.filename in [':info', 'info']: - self.metadata = DEFAULT_FILE_METADATA.copy() + self.metadata = default_path_metadata() def bind_info_to_entry(self, entry, metadata): """ Shortcut to call :func:`bind_info` with the base diff --git a/src/lib/Bcfg2/Server/Plugins/Bundler.py b/src/lib/Bcfg2/Server/Plugins/Bundler.py index b200346bc..6dc3c2b1d 100644 --- a/src/lib/Bcfg2/Server/Plugins/Bundler.py +++ b/src/lib/Bcfg2/Server/Plugins/Bundler.py @@ -1,15 +1,15 @@ """This provides bundle clauses with translation functionality.""" -import copy -import logging -import lxml.etree import os -import os.path import re import sys +import copy +import logging +import lxml.etree import Bcfg2.Server import Bcfg2.Server.Plugin import Bcfg2.Server.Lint +from Bcfg2.Options import get_option_parser try: import genshi.template.base @@ -19,9 +19,6 @@ except ImportError: HAS_GENSHI = False -SETUP = None - - class BundleFile(Bcfg2.Server.Plugin.StructFile): """ Representation of a bundle XML file """ def get_xml_value(self, metadata): @@ -52,8 +49,9 @@ if HAS_GENSHI: msg = "No parsed template information for %s" % self.name self.logger.error(msg) raise Bcfg2.Server.Plugin.PluginExecutionError(msg) - stream = self.template.generate(metadata=metadata, - repo=SETUP['repo']).filter( + stream = self.template.generate( + metadata=metadata, + repo=get_option_parser()['repo']).filter( Bcfg2.Server.Plugins.TGenshi.removecomment) data = lxml.etree.XML(stream.render('xml', strip_whitespace=False), @@ -102,9 +100,6 @@ class Bundler(Bcfg2.Server.Plugin.Plugin, self.logger.error(msg) raise Bcfg2.Server.Plugin.PluginInitError(msg) - global SETUP - SETUP = core.setup - def template_dispatch(self, name, _): """ Add the correct child entry type to Bundler depending on whether the XML file in question is a plain XML file or a diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgAuthorizedKeysGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgAuthorizedKeysGenerator.py index 824d01023..11c60ad2c 100644 --- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgAuthorizedKeysGenerator.py +++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgAuthorizedKeysGenerator.py @@ -4,7 +4,7 @@ access. """ import lxml.etree from Bcfg2.Server.Plugin import StructFile, PluginExecutionError -from Bcfg2.Server.Plugins.Cfg import CfgGenerator, SETUP, CFG +from Bcfg2.Server.Plugins.Cfg import CfgGenerator, CFG from Bcfg2.Server.Plugins.Metadata import ClientMetadata @@ -35,9 +35,9 @@ class CfgAuthorizedKeysGenerator(CfgGenerator, StructFile): def category(self): """ The name of the metadata category that generated keys are specific to """ - if (SETUP.cfp.has_section("sshkeys") and - SETUP.cfp.has_option("sshkeys", "category")): - return SETUP.cfp.get("sshkeys", "category") + if (self.setup.cfp.has_section("sshkeys") and + self.setup.cfp.has_option("sshkeys", "category")): + return self.setup.cfp.get("sshkeys", "category") return None def handle_event(self, event): diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py index 724164cf5..4c8adceec 100644 --- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py +++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py @@ -3,7 +3,7 @@ :ref:`server-plugins-generators-cfg` files. """ from Bcfg2.Server.Plugin import PluginExecutionError -from Bcfg2.Server.Plugins.Cfg import CfgGenerator, SETUP +from Bcfg2.Server.Plugins.Cfg import CfgGenerator try: from Cheetah.Template import Template @@ -40,6 +40,6 @@ class CfgCheetahGenerator(CfgGenerator): template.name = entry.get('realname', entry.get('name')) template.path = entry.get('realname', entry.get('name')) template.source_path = self.name - template.repo = SETUP['repo'] + template.repo = self.setup['repo'] return template.respond() get_data.__doc__ = CfgGenerator.get_data.__doc__ diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenerator.py index 3b4703ddb..3b3b95ff5 100644 --- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenerator.py +++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenerator.py @@ -2,10 +2,9 @@ :ref:`server-plugins-generators-cfg` files on the server. """ from Bcfg2.Server.Plugin import PluginExecutionError -from Bcfg2.Server.Plugins.Cfg import CfgGenerator, SETUP +from Bcfg2.Server.Plugins.Cfg import CfgGenerator try: - from Bcfg2.Encryption import bruteforce_decrypt, EVPError, \ - get_algorithm + from Bcfg2.Encryption import bruteforce_decrypt, EVPError HAS_CRYPTO = True except ImportError: HAS_CRYPTO = False @@ -34,8 +33,7 @@ class CfgEncryptedGenerator(CfgGenerator): return # todo: let the user specify a passphrase by name try: - self.data = bruteforce_decrypt(self.data, setup=SETUP, - algorithm=get_algorithm(SETUP)) + self.data = bruteforce_decrypt(self.data) except EVPError: raise PluginExecutionError("Failed to decrypt %s" % self.name) handle_event.__doc__ = CfgGenerator.handle_event.__doc__ diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenshiGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenshiGenerator.py index 130652aef..215e4c1f1 100644 --- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenshiGenerator.py +++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenshiGenerator.py @@ -3,11 +3,10 @@ files) """ from Bcfg2.Compat import StringIO from Bcfg2.Server.Plugin import PluginExecutionError -from Bcfg2.Server.Plugins.Cfg import SETUP from Bcfg2.Server.Plugins.Cfg.CfgGenshiGenerator import CfgGenshiGenerator try: - from Bcfg2.Encryption import bruteforce_decrypt, get_algorithm + from Bcfg2.Encryption import bruteforce_decrypt HAS_CRYPTO = True except ImportError: HAS_CRYPTO = False @@ -24,9 +23,7 @@ class EncryptedTemplateLoader(TemplateLoader): the data on the fly as it's read in using :func:`Bcfg2.Encryption.bruteforce_decrypt` """ def _instantiate(self, cls, fileobj, filepath, filename, encoding=None): - plaintext = \ - StringIO(bruteforce_decrypt(fileobj.read(), - algorithm=get_algorithm(SETUP))) + plaintext = StringIO(bruteforce_decrypt(fileobj.read())) return TemplateLoader._instantiate(self, cls, plaintext, filepath, filename, encoding=encoding) diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py index 73550cd9d..b58349fe0 100644 --- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py +++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py @@ -6,7 +6,7 @@ import re import sys import traceback from Bcfg2.Server.Plugin import PluginExecutionError -from Bcfg2.Server.Plugins.Cfg import CfgGenerator, SETUP +from Bcfg2.Server.Plugins.Cfg import CfgGenerator try: import genshi.core @@ -102,12 +102,12 @@ class CfgGenshiGenerator(CfgGenerator): def get_data(self, entry, metadata): fname = entry.get('realname', entry.get('name')) - stream = \ - self.template.generate(name=fname, - metadata=metadata, - path=self.name, - source_path=self.name, - repo=SETUP['repo']).filter(removecomment) + stream = self.template.generate( + name=fname, + metadata=metadata, + path=self.name, + source_path=self.name, + repo=self.setup['repo']).filter(removecomment) try: try: return stream.render('text', encoding=self.encoding, diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py index aaeb65cd6..54fa75b41 100644 --- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py +++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py @@ -4,8 +4,9 @@ import os import shutil import tempfile import subprocess +from Bcfg2.Options import get_option_parser from Bcfg2.Server.Plugin import PluginExecutionError, StructFile -from Bcfg2.Server.Plugins.Cfg import CfgCreator, CfgCreationError, SETUP +from Bcfg2.Server.Plugins.Cfg import CfgCreator, CfgCreationError from Bcfg2.Server.Plugins.Cfg.CfgPublicKeyCreator import CfgPublicKeyCreator try: import Bcfg2.Encryption @@ -31,24 +32,25 @@ class CfgPrivateKeyCreator(CfgCreator, StructFile): pubkey_path = os.path.dirname(self.name) + ".pub" pubkey_name = os.path.join(pubkey_path, os.path.basename(pubkey_path)) self.pubkey_creator = CfgPublicKeyCreator(pubkey_name) + self.setup = get_option_parser() __init__.__doc__ = CfgCreator.__init__.__doc__ @property def category(self): """ The name of the metadata category that generated keys are specific to """ - if (SETUP.cfp.has_section("sshkeys") and - SETUP.cfp.has_option("sshkeys", "category")): - return SETUP.cfp.get("sshkeys", "category") + if (self.setup.cfp.has_section("sshkeys") and + self.setup.cfp.has_option("sshkeys", "category")): + return self.setup.cfp.get("sshkeys", "category") return None @property def passphrase(self): """ The passphrase used to encrypt private keys """ if (HAS_CRYPTO and - SETUP.cfp.has_section("sshkeys") and - SETUP.cfp.has_option("sshkeys", "passphrase")): - return Bcfg2.Encryption.get_passphrases(SETUP)[SETUP.cfp.get( + self.setup.cfp.has_section("sshkeys") and + self.setup.cfp.has_option("sshkeys", "passphrase")): + return Bcfg2.Encryption.get_passphrases()[self.setup.cfp.get( "sshkeys", "passphrase")] return None @@ -196,10 +198,8 @@ class CfgPrivateKeyCreator(CfgCreator, StructFile): privkey = open(filename).read() if HAS_CRYPTO and self.passphrase: self.debug_log("Cfg: Encrypting key data at %s" % filename) - privkey = Bcfg2.Encryption.ssl_encrypt( - privkey, - self.passphrase, - algorithm=Bcfg2.Encryption.get_algorithm(SETUP)) + privkey = Bcfg2.Encryption.ssl_encrypt(privkey, + self.passphrase) specificity['ext'] = '.crypt' self.write_data(privkey, **specificity) @@ -239,22 +239,16 @@ class CfgPrivateKeyCreator(CfgCreator, StructFile): """ Decrypt a single encrypted element """ if not element.text or not element.text.strip(): return - passes = Bcfg2.Encryption.get_passphrases(SETUP) + passes = Bcfg2.Encryption.get_passphrases() try: passphrase = passes[element.get("encrypted")] try: - return Bcfg2.Encryption.ssl_decrypt( - element.text, - passphrase, - algorithm=Bcfg2.Encryption.get_algorithm(SETUP)) + return Bcfg2.Encryption.ssl_decrypt(element.text, passphrase) except Bcfg2.Encryption.EVPError: # error is raised below pass except KeyError: # bruteforce_decrypt raises an EVPError with a sensible # error message, so we just let it propagate up the stack - return Bcfg2.Encryption.bruteforce_decrypt( - element.text, - passphrases=passes.values(), - algorithm=Bcfg2.Encryption.get_algorithm(SETUP)) + return Bcfg2.Encryption.bruteforce_decrypt(element.text) raise Bcfg2.Encryption.EVPError("Failed to decrypt") diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py index fcfaa393b..53cc90094 100644 --- a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py +++ b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py @@ -16,17 +16,6 @@ from Bcfg2.Compat import u_str, unicode, b64encode, walk_packages, \ any, oct_mode # pylint: enable=W0622 -#: SETUP contains a reference to the -#: :class:`Bcfg2.Options.OptionParser` created by the Bcfg2 core for -#: parsing command-line and config file options. -#: :class:`Bcfg2.Server.Plugins.Cfg.Cfg` stores it in a module global -#: so that the handler objects can access it, because there is no other -#: facility for passing a setup object from a -#: :class:`Bcfg2.Server.Plugin.helpers.GroupSpool` to its -#: :class:`Bcfg2.Server.Plugin.helpers.EntrySet` objects and thence to -#: the EntrySet children. -SETUP = None - #: CFG is a reference to the :class:`Bcfg2.Server.Plugins.Cfg.Cfg` #: plugin object created by the Bcfg2 core. This is provided so that #: the handler objects can access it as necessary, since the existing @@ -86,6 +75,7 @@ class CfgBaseFileMatcher(Bcfg2.Server.Plugin.SpecificData, encoding) Bcfg2.Server.Plugin.Debuggable.__init__(self) self.encoding = encoding + self.setup = Bcfg2.Options.get_option_parser() __init__.__doc__ = Bcfg2.Server.Plugin.SpecificData.__init__.__doc__ + \ """ .. ----- @@ -442,11 +432,11 @@ class CfgDefaultInfo(CfgInfo): bind_info_to_entry.__doc__ = CfgInfo.bind_info_to_entry.__doc__ #: A :class:`CfgDefaultInfo` object instantiated with -#: :attr:`Bcfg2.Server.Plugin.helper.DEFAULT_FILE_METADATA` as its +#: :func:`Bcfg2.Server.Plugin.helper.default_path_metadata` as its #: default metadata. This is used to set a default file metadata set #: on an entry before a "real" :class:`CfgInfo` handler applies its #: metadata to the entry. -DEFAULT_INFO = CfgDefaultInfo(Bcfg2.Server.Plugin.DEFAULT_FILE_METADATA) +DEFAULT_INFO = CfgDefaultInfo(Bcfg2.Server.Plugin.default_path_metadata()) class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet, @@ -460,6 +450,7 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet, Bcfg2.Server.Plugin.Debuggable.__init__(self) self.specific = None self._handlers = None + self.setup = Bcfg2.Options.get_option_parser() __init__.__doc__ = Bcfg2.Server.Plugin.EntrySet.__doc__ def set_debug(self, debug): @@ -585,7 +576,7 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet, for fltr in self.get_handlers(metadata, CfgFilter): data = fltr.modify_data(entry, metadata, data) - if SETUP['validate']: + if self.setup['validate']: try: self._validate_data(entry, metadata, data) except CfgVerificationError: @@ -833,16 +824,16 @@ class Cfg(Bcfg2.Server.Plugin.GroupSpool, es_child_cls = Bcfg2.Server.Plugin.SpecificData def __init__(self, core, datastore): - global SETUP, CFG # pylint: disable=W0603 + global CFG # pylint: disable=W0603 Bcfg2.Server.Plugin.GroupSpool.__init__(self, core, datastore) Bcfg2.Server.Plugin.PullTarget.__init__(self) CFG = self - SETUP = core.setup - if 'validate' not in SETUP: - SETUP.add_option('validate', Bcfg2.Options.CFG_VALIDATION) - SETUP.reparse() + setup = Bcfg2.Options.get_option_parser() + if 'validate' not in setup: + setup.add_option('validate', Bcfg2.Options.CFG_VALIDATION) + setup.reparse() __init__.__doc__ = Bcfg2.Server.Plugin.GroupSpool.__init__.__doc__ def has_generator(self, entry, metadata): diff --git a/src/lib/Bcfg2/Server/Plugins/Properties.py b/src/lib/Bcfg2/Server/Plugins/Properties.py index a51dd8adc..c5b5ea2d1 100644 --- a/src/lib/Bcfg2/Server/Plugins/Properties.py +++ b/src/lib/Bcfg2/Server/Plugins/Properties.py @@ -7,6 +7,7 @@ import sys import copy import logging import lxml.etree +from Bcfg2.Options import get_option_parser import Bcfg2.Server.Plugin from Bcfg2.Server.Plugin import PluginExecutionError try: @@ -33,8 +34,6 @@ except ImportError: LOGGER = logging.getLogger(__name__) -SETUP = None - class PropertyFile(object): """ Base Properties file handler """ @@ -46,13 +45,14 @@ class PropertyFile(object): .. automethod:: _write """ self.name = name + self.setup = get_option_parser() def write(self): """ Write the data in this data structure back to the property file. This public method performs checking to ensure that writing is possible and then calls :func:`_write`. """ - if not SETUP.cfp.getboolean("properties", "writes_enabled", - default=True): + if not self.setup.cfp.getboolean("properties", "writes_enabled", + default=True): msg = "Properties files write-back is disabled in the " + \ "configuration" LOGGER.error(msg) @@ -232,26 +232,22 @@ class XMLPropertyFile(Bcfg2.Server.Plugin.StructFile, PropertyFile): """ Decrypt a single encrypted properties file element """ if not element.text or not element.text.strip(): return - passes = Bcfg2.Encryption.get_passphrases(SETUP) + passes = Bcfg2.Encryption.get_passphrases() try: passphrase = passes[element.get("encrypted")] try: - return Bcfg2.Encryption.ssl_decrypt( - element.text, passphrase, - algorithm=Bcfg2.Encryption.get_algorithm(SETUP)) + return Bcfg2.Encryption.ssl_decrypt(element.text, passphrase) except Bcfg2.Encryption.EVPError: # error is raised below pass except KeyError: # bruteforce_decrypt raises an EVPError with a sensible # error message, so we just let it propagate up the stack - return Bcfg2.Encryption.bruteforce_decrypt( - element.text, passphrases=passes.values(), - algorithm=Bcfg2.Encryption.get_algorithm(SETUP)) + return Bcfg2.Encryption.bruteforce_decrypt(element.text) raise Bcfg2.Encryption.EVPError("Failed to decrypt") def get_additional_data(self, metadata): - if SETUP.cfp.getboolean("properties", "automatch", default=False): + if self.setup.cfp.getboolean("properties", "automatch", default=False): default_automatch = "true" else: default_automatch = "false" @@ -323,10 +319,8 @@ class Properties(Bcfg2.Server.Plugin.Plugin, instances. """ def __init__(self, core, datastore): - global SETUP # pylint: disable=W0603 Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore) Bcfg2.Server.Plugin.Connector.__init__(self) - SETUP = core.setup try: self.store = PropDirectoryBacked(self.data, core.fam) except OSError: diff --git a/src/lib/Bcfg2/Server/models.py b/src/lib/Bcfg2/Server/models.py index 0328c6bea..4ac2be43b 100644 --- a/src/lib/Bcfg2/Server/models.py +++ b/src/lib/Bcfg2/Server/models.py @@ -1,6 +1,7 @@ """ Django database models for all plugins """ import sys +import copy import logging import Bcfg2.Options import Bcfg2.Server.Plugins @@ -19,14 +20,12 @@ def load_models(plugins=None, cfile='/etc/bcfg2.conf', quiet=True): # we want to provide a different default plugin list -- # namely, _all_ plugins, so that the database is guaranteed to # work, even if /etc/bcfg2.conf isn't set up properly - plugin_opt = Bcfg2.Options.SERVER_PLUGINS + plugin_opt = copy.deepcopy(Bcfg2.Options.SERVER_PLUGINS) plugin_opt.default = Bcfg2.Server.Plugins.__all__ - - setup = \ - Bcfg2.Options.OptionParser(dict(plugins=plugin_opt, - configfile=Bcfg2.Options.CFILE), - quiet=quiet) - setup.parse([Bcfg2.Options.CFILE.cmd, cfile]) + setup = Bcfg2.Options.get_option_parser() + setup.add_option("plugins", plugin_opt) + setup.add_option("configfile", Bcfg2.Options.CFILE) + setup.reparse(argv=[Bcfg2.Options.CFILE.cmd, cfile]) plugins = setup['plugins'] if MODELS: diff --git a/src/lib/Bcfg2/settings.py b/src/lib/Bcfg2/settings.py index 190bdff58..da6e2036e 100644 --- a/src/lib/Bcfg2/settings.py +++ b/src/lib/Bcfg2/settings.py @@ -39,20 +39,20 @@ def _default_config(): """ get the default config file. returns /etc/bcfg2-web.conf, UNLESS /etc/bcfg2.conf exists AND /etc/bcfg2-web.conf does not exist. """ - optinfo = dict(cfile=Bcfg2.Options.CFILE, - web_cfile=Bcfg2.Options.WEB_CFILE) - setup = Bcfg2.Options.OptionParser(optinfo, quiet=True) - setup.parse(sys.argv[1:], do_getopt=False) - if (not os.path.exists(setup['web_cfile']) and - os.path.exists(setup['cfile'])): - return setup['cfile'] + setup = Bcfg2.Options.get_option_parser() + setup.add_option("configfile", Bcfg2.Options.CFILE) + setup.add_option("web_configfile", Bcfg2.Options.WEB_CFILE) + setup.reparse(argv=sys.argv[1:], do_getopt=False) + if (not os.path.exists(setup['web_configfile']) and + os.path.exists(setup['configfile'])): + return setup['configfile'] else: - return setup['web_cfile'] + return setup['web_configfile'] DEFAULT_CONFIG = _default_config() -def read_config(cfile=DEFAULT_CONFIG, repo=None, quiet=False): +def read_config(cfile=DEFAULT_CONFIG, repo=None): """ read the config file and set django settings based on it """ # pylint: disable=W0603 global DATABASE_ENGINE, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD, \ @@ -65,15 +65,15 @@ def read_config(cfile=DEFAULT_CONFIG, repo=None, quiet=False): (cfile, DEFAULT_CONFIG)) cfile = DEFAULT_CONFIG - optinfo = Bcfg2.Options.DATABASE_COMMON_OPTIONS - optinfo['repo'] = Bcfg2.Options.SERVER_REPOSITORY # when setting a different config file, it has to be set in either # sys.argv or in the OptionSet() constructor AS WELL AS the argv # that's passed to setup.parse() argv = [Bcfg2.Options.CFILE.cmd, cfile, Bcfg2.Options.WEB_CFILE.cmd, cfile] - setup = Bcfg2.Options.OptionParser(optinfo, argv=argv, quiet=quiet) - setup.parse(argv) + setup = Bcfg2.Options.get_option_parser() + setup.add_options(Bcfg2.Options.DATABASE_COMMON_OPTIONS) + setup.add_option("repo", Bcfg2.Options.SERVER_REPOSITORY) + setup.reparse(argv=argv) if repo is None: repo = setup['repo'] @@ -110,7 +110,7 @@ def read_config(cfile=DEFAULT_CONFIG, repo=None, quiet=False): # initialize settings from /etc/bcfg2-web.conf or /etc/bcfg2.conf, or # set up basic defaults. this lets manage.py work in all cases -read_config(quiet=True) +read_config() ADMINS = (('Root', 'root')) MANAGERS = ADMINS diff --git a/src/sbin/bcfg2 b/src/sbin/bcfg2 index 444e86a7c..e9beb47cd 100755 --- a/src/sbin/bcfg2 +++ b/src/sbin/bcfg2 @@ -3,8 +3,8 @@ import sys import signal -import Bcfg2.Options from Bcfg2.Client.Client import Client +from Bcfg2.Options import get_option_parser, CLIENT_COMMON_OPTIONS def cb_sigint_handler(signum, frame): @@ -13,8 +13,7 @@ def cb_sigint_handler(signum, frame): def main(): - optinfo = Bcfg2.Options.CLIENT_COMMON_OPTIONS - setup = Bcfg2.Options.OptionParser(optinfo) + setup = get_option_parser(CLIENT_COMMON_OPTIONS) setup.parse(sys.argv[1:]) if setup['args']: @@ -23,7 +22,7 @@ def main(): raise SystemExit(1) signal.signal(signal.SIGINT, cb_sigint_handler) - return Client(setup).run() + return Client().run() if __name__ == '__main__': sys.exit(main()) diff --git a/src/sbin/bcfg2-admin b/src/sbin/bcfg2-admin index fa4307702..8f6643ee0 100755 --- a/src/sbin/bcfg2-admin +++ b/src/sbin/bcfg2-admin @@ -41,7 +41,7 @@ def main(): optinfo = dict() optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS) - setup = Bcfg2.Options.OptionParser(optinfo) + setup = Bcfg2.Options.get_option_parser(optinfo) # override default help message to include description of all modes setup.hm = "Usage:\n\n%s\n%s" % (setup.buildHelpMessage(), create_description()) @@ -80,7 +80,7 @@ def main(): err = sys.exc_info()[1] log.error("Failed to load admin mode %s: %s" % (modname, err)) raise SystemExit(1) - mode = mode_cls(setup) + mode = mode_cls() try: mode(setup['args'][1:]) finally: diff --git a/src/sbin/bcfg2-crypt b/src/sbin/bcfg2-crypt index fde6af582..6a13b9bc5 100755 --- a/src/sbin/bcfg2-crypt +++ b/src/sbin/bcfg2-crypt @@ -27,8 +27,8 @@ class EncryptionChunkingError(Exception): class Encryptor(object): """ Generic encryptor for all files """ - def __init__(self, setup): - self.setup = setup + def __init__(self): + self.setup = Bcfg2.Options.get_option_parser() self.passphrase = None self.pname = None self.logger = logging.getLogger(self.__class__.__name__) @@ -56,7 +56,7 @@ class Encryptor(object): def set_passphrase(self): """ set the passphrase for the current file """ if (not self.setup.cfp.has_section(Bcfg2.Encryption.CFG_SECTION) or - len(Bcfg2.Encryption.get_passphrases(self.setup)) == 0): + len(Bcfg2.Encryption.get_passphrases()) == 0): self.logger.error("No passphrases available in %s" % self.setup['configfile']) return False @@ -83,7 +83,7 @@ class Encryptor(object): (self.pname, self.setup['configfile'])) return False else: - pnames = Bcfg2.Encryption.get_passphrases(self.setup) + pnames = Bcfg2.Encryption.get_passphrases() if len(pnames) == 1: self.pname = pnames.keys()[0] self.passphrase = pnames[self.pname] @@ -127,9 +127,7 @@ class Encryptor(object): # pylint: disable=W0613 def _encrypt(self, plaintext, passphrase, name=None): """ encrypt a single chunk of a file """ - return Bcfg2.Encryption.ssl_encrypt( - plaintext, passphrase, - Bcfg2.Encryption.get_algorithm(self.setup)) + return Bcfg2.Encryption.ssl_encrypt(plaintext, passphrase) # pylint: enable=W0613 def decrypt(self, fname): @@ -162,7 +160,7 @@ class Encryptor(object): except TypeError: pchunk = None for pname, passphrase in \ - Bcfg2.Encryption.get_passphrases(self.setup).items(): + Bcfg2.Encryption.get_passphrases().items(): self.logger.debug("Trying passphrase %s" % pname) try: pchunk = self._decrypt(chunk, passphrase) @@ -196,9 +194,7 @@ class Encryptor(object): def _decrypt(self, crypted, passphrase): """ decrypt a single chunk """ - return Bcfg2.Encryption.ssl_decrypt( - crypted, passphrase, - Bcfg2.Encryption.get_algorithm(self.setup)) + return Bcfg2.Encryption.ssl_decrypt(crypted, passphrase) def write_encrypted(self, fname, data=None): """ write encrypted data to disk """ @@ -287,10 +283,8 @@ class PropertiesEncryptor(Encryptor): if name is None: name = "true" if plaintext.text and plaintext.text.strip(): - plaintext.text = Bcfg2.Encryption.ssl_encrypt( - plaintext.text, - passphrase, - Bcfg2.Encryption.get_algorithm(self.setup)).strip() + plaintext.text = Bcfg2.Encryption.ssl_encrypt(plaintext.text, + passphrase).strip() plaintext.set("encrypted", name) return plaintext @@ -358,10 +352,8 @@ class PropertiesEncryptor(Encryptor): if not crypted.text or not crypted.text.strip(): self.logger.warning("Skipping empty element %s" % crypted.tag) return crypted - decrypted = Bcfg2.Encryption.ssl_decrypt( - crypted.text, - passphrase, - Bcfg2.Encryption.get_algorithm(self.setup)).strip() + decrypted = Bcfg2.Encryption.ssl_decrypt(crypted.text, + passphrase).strip() try: crypted.text = decrypted.encode('ascii', 'xmlcharrefreplace') except UnicodeDecodeError: @@ -379,10 +371,10 @@ def main(): # pylint: disable=R0912,R0915 optinfo = dict(interactive=Bcfg2.Options.INTERACTIVE) optinfo.update(Bcfg2.Options.CRYPT_OPTIONS) optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) - setup = Bcfg2.Options.OptionParser(optinfo) + setup = Bcfg2.Options.get_option_parser(optinfo) setup.hm = " bcfg2-crypt [options] \nOptions:\n%s" % \ setup.buildHelpMessage() - setup.parse(sys.argv[1:]) + setup.parse() if not setup['args']: print(setup.hm) diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info index fa8c89b46..c697e9fca 100755 --- a/src/sbin/bcfg2-info +++ b/src/sbin/bcfg2-info @@ -115,9 +115,9 @@ def load_interpreters(): class InfoCore(cmd.Cmd, Bcfg2.Server.Core.BaseCore): """Main class for bcfg2-info.""" - def __init__(self, setup): + def __init__(self): cmd.Cmd.__init__(self) - Bcfg2.Server.Core.BaseCore.__init__(self, setup=setup) + Bcfg2.Server.Core.BaseCore.__init__(self) self.prompt = '> ' self.cont = True self.fam.handle_events_in_interval(4) @@ -751,7 +751,7 @@ def main(): interpreter=Bcfg2.Options.INTERPRETER) optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS) - setup = Bcfg2.Options.OptionParser(optinfo) + setup = Bcfg2.Options.get_option_parser(optinfo) setup.hm = "\n".join([" bcfg2-info [options] [command ]", "Options:", setup.buildHelpMessage(), @@ -772,12 +772,12 @@ def main(): sys.exit(0) elif setup['profile'] and HAS_PROFILE: prof = profile.Profile() - loop = prof.runcall(InfoCore, setup) + loop = prof.runcall(InfoCore) display_trace(prof) else: if setup['profile']: print("Profiling functionality not available.") - loop = InfoCore(setup) + loop = InfoCore() loop.run(setup['args']) diff --git a/src/sbin/bcfg2-lint b/src/sbin/bcfg2-lint index 959a2d8ed..f12fcc6c4 100755 --- a/src/sbin/bcfg2-lint +++ b/src/sbin/bcfg2-lint @@ -13,37 +13,36 @@ import Bcfg2.Server.Lint LOGGER = logging.getLogger('bcfg2-lint') -def run_serverless_plugins(plugins, setup=None, errorhandler=None, files=None): +def run_serverless_plugins(plugins, errorhandler=None, files=None): """ Run serverless plugins """ LOGGER.debug("Running serverless plugins") for plugin_name, plugin in list(plugins.items()): - run_plugin(plugin, plugin_name, errorhandler=errorhandler, - setup=setup, files=files) + run_plugin(plugin, plugin_name, errorhandler=errorhandler, files=files) -def run_server_plugins(plugins, setup=None, errorhandler=None, files=None): +def run_server_plugins(plugins, errorhandler=None, files=None): """ run plugins that require a running server to run """ - core = load_server(setup) + core = load_server() try: LOGGER.debug("Running server plugins") for plugin_name, plugin in list(plugins.items()): run_plugin(plugin, plugin_name, args=[core], - errorhandler=errorhandler, setup=setup, files=files) + errorhandler=errorhandler, files=files) finally: core.shutdown() -def run_plugin(plugin, plugin_name, setup=None, errorhandler=None, - args=None, files=None): +def run_plugin(plugin, plugin_name, errorhandler=None, args=None, files=None): """ run a single plugin, server-ful or serverless. """ LOGGER.debug(" Running %s" % plugin_name) if args is None: args = [] if errorhandler is None: - errorhandler = get_errorhandler(setup) + errorhandler = get_errorhandler() - if setup is not None and setup.cfp.has_section(plugin_name): + setup = Bcfg2.Options.get_option_parser() + if setup.cfp.has_section(plugin_name): arg = setup for key, val in setup.cfp.items(plugin_name): arg[key] = val @@ -54,8 +53,9 @@ def run_plugin(plugin, plugin_name, setup=None, errorhandler=None, return plugin(*args, files=files, errorhandler=errorhandler).Run() -def get_errorhandler(setup): +def get_errorhandler(): """ get a Bcfg2.Server.Lint.ErrorHandler object """ + setup = Bcfg2.Options.get_option_parser() if setup.cfp.has_section("errors"): conf = dict(setup.cfp.items("errors")) else: @@ -63,9 +63,9 @@ def get_errorhandler(setup): return Bcfg2.Server.Lint.ErrorHandler(config=conf) -def load_server(setup): +def load_server(): """ load server """ - core = Bcfg2.Server.Core.BaseCore(setup) + core = Bcfg2.Server.Core.BaseCore() core.fam.handle_events_in_interval(4) return core @@ -82,8 +82,9 @@ def load_plugin(module, obj_name=None): return getattr(mod, obj_name) -def load_plugins(setup): +def load_plugins(): """ get list of plugins to run """ + setup = Bcfg2.Options.get_option_parser() if setup['args']: plugin_list = setup['args'] elif "bcfg2-repo-validate" in sys.argv[0]: @@ -145,7 +146,7 @@ def main(): lint_plugins=Bcfg2.Options.LINT_PLUGINS) optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS) - setup = Bcfg2.Options.OptionParser(optinfo) + setup = Bcfg2.Options.get_option_parser(optinfo) setup.parse(sys.argv[1:]) log_args = dict(to_syslog=setup['syslog'], to_console=logging.WARNING) @@ -161,9 +162,8 @@ def main(): else: files = None - (serverlessplugins, serverplugins) = load_plugins(setup) - - errorhandler = get_errorhandler(setup) + (serverlessplugins, serverplugins) = load_plugins() + errorhandler = get_errorhandler() if setup['showerrors']: for plugin in serverplugins.values() + serverlessplugins.values(): @@ -175,7 +175,7 @@ def main(): raise SystemExit(0) run_serverless_plugins(serverlessplugins, errorhandler=errorhandler, - setup=setup, files=files) + files=files) if serverplugins: if errorhandler.errors: @@ -191,7 +191,7 @@ def main(): "plugins") else: run_server_plugins(serverplugins, errorhandler=errorhandler, - setup=setup, files=files) + files=files) if errorhandler.errors or errorhandler.warnings or setup['verbose']: print("%d errors" % errorhandler.errors) diff --git a/src/sbin/bcfg2-report-collector b/src/sbin/bcfg2-report-collector index a0ee2259a..d925023d1 100755 --- a/src/sbin/bcfg2-report-collector +++ b/src/sbin/bcfg2-report-collector @@ -20,8 +20,8 @@ def main(): ) optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.REPORTING_COMMON_OPTIONS) - setup = Bcfg2.Options.OptionParser(optinfo) - setup.parse(sys.argv[1:]) + setup = Bcfg2.Options.get_option_parser(optinfo) + setup.parse() # run collector try: diff --git a/src/sbin/bcfg2-server b/src/sbin/bcfg2-server index 8322edeaa..7e7dede30 100755 --- a/src/sbin/bcfg2-server +++ b/src/sbin/bcfg2-server @@ -16,13 +16,13 @@ def main(): optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.DAEMON_COMMON_OPTIONS) - setup = Bcfg2.Options.OptionParser(optinfo) + setup = Bcfg2.Options.get_option_parser(args=optinfo) setup.parse(sys.argv[1:]) # check whether the specified bcfg2.conf exists if not os.path.exists(setup['configfile']): print("Could not read %s" % setup['configfile']) sys.exit(1) - + if setup['backend'] not in ['best', 'cherrypy', 'builtin']: print("Unknown server backend %s, using 'best'" % setup['backend']) setup['backend'] = 'best' @@ -37,7 +37,7 @@ def main(): from Bcfg2.Server.BuiltinCore import Core try: - core = Core(setup) + core = Core() core.run() except CoreInitError: msg = sys.exc_info()[1] diff --git a/src/sbin/bcfg2-test b/src/sbin/bcfg2-test index 815d2740c..a708a233a 100755 --- a/src/sbin/bcfg2-test +++ b/src/sbin/bcfg2-test @@ -69,7 +69,7 @@ def main(): validate=Bcfg2.Options.CFG_VALIDATION) optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS) - setup = Bcfg2.Options.OptionParser(optinfo) + setup = Bcfg2.Options.get_option_parser(args=optinfo) setup.hm = \ "bcfg2-test [options] [client] [client] [...]\nOptions:\n %s" % \ setup.buildHelpMessage() @@ -78,7 +78,7 @@ def main(): if setup['verbose']: Bcfg2.Logger.setup_logging("bcfg2-test", to_syslog=setup['syslog']) - core = Bcfg2.Server.Core.BaseCore(setup) + core = Bcfg2.Server.Core.BaseCore() ignore = dict() for entry in setup['test_ignore']: -- cgit v1.2.3-1-g7c22 From 1a031fc131d950dd49dc425ac1726337d8e93910 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Thu, 17 Jan 2013 08:01:44 -0500 Subject: abstracted encryption support from Properties/CfgPrivateKeyCreator to StructFile --- src/lib/Bcfg2/Server/Plugin/helpers.py | 64 +++++++++++++++++++++- .../Server/Plugins/Cfg/CfgPrivateKeyCreator.py | 43 +-------------- .../Server/Plugins/Cfg/CfgPublicKeyCreator.py | 3 + src/lib/Bcfg2/Server/Plugins/FileProbes.py | 13 +++-- src/lib/Bcfg2/Server/Plugins/NagiosGen.py | 4 +- .../Server/Plugins/Packages/PackagesSources.py | 2 + src/lib/Bcfg2/Server/Plugins/Properties.py | 43 --------------- src/lib/Bcfg2/Server/Plugins/SSLCA.py | 1 + 8 files changed, 81 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py index 0df568045..c8e09d3d1 100644 --- a/src/lib/Bcfg2/Server/Plugin/helpers.py +++ b/src/lib/Bcfg2/Server/Plugin/helpers.py @@ -17,6 +17,12 @@ from Bcfg2.Server.Plugin.interfaces import Generator from Bcfg2.Server.Plugin.exceptions import SpecificityError, \ PluginExecutionError +try: + import Bcfg2.Encryption + HAS_CRYPTO = True +except ImportError: + HAS_CRYPTO = False + try: import django # pylint: disable=W0611 HAS_DJANGO = True @@ -571,13 +577,69 @@ class XMLFileBacked(FileBacked): class StructFile(XMLFileBacked): """ StructFiles are XML files that contain a set of structure file formatting logic for handling ```` and ```` - tags. """ + tags. + + .. ----- + .. autoattribute:: __identifier__ + """ #: If ``__identifier__`` is not None, then it must be the name of #: an XML attribute that will be required on the top-level tag of #: the file being cached __identifier__ = None + #: Whether or not encryption support is enabled in this file + encryption = True + + def __init__(self, filename, fam=None, should_monitor=False): + XMLFileBacked.__init__(self, filename, fam=fam, + should_monitor=should_monitor) + self.setup = Bcfg2.Options.get_option_parser() + + def Index(self): + Bcfg2.Server.Plugin.XMLFileBacked.Index(self) + if self.encryption: + strict = self.xdata.get( + "decrypt", + self.setup.cfp.get(Bcfg2.Encryption.CFG_SECTION, "decrypt", + default="strict")) == "strict" + for el in self.xdata.xpath("//*[@encrypted]"): + if not HAS_CRYPTO: + raise PluginExecutionError("Properties: M2Crypto is not " + "available: %s" % self.name) + try: + el.text = self._decrypt(el).encode('ascii', + 'xmlcharrefreplace') + except UnicodeDecodeError: + LOGGER.info("%s: Decrypted %s to gibberish, skipping" % + (self.name, el.tag)) + except Bcfg2.Encryption.EVPError: + msg = "Failed to decrypt %s element in %s" % (el.tag, + self.name) + if strict: + raise PluginExecutionError(msg) + else: + LOGGER.warning(msg) + Index.__doc__ = XMLFileBacked.Index.__doc__ + + def _decrypt(self, element): + """ Decrypt a single encrypted properties file element """ + if not element.text or not element.text.strip(): + return + passes = Bcfg2.Encryption.get_passphrases() + try: + passphrase = passes[element.get("encrypted")] + try: + return Bcfg2.Encryption.ssl_decrypt(element.text, passphrase) + except Bcfg2.Encryption.EVPError: + # error is raised below + pass + except KeyError: + # bruteforce_decrypt raises an EVPError with a sensible + # error message, so we just let it propagate up the stack + return Bcfg2.Encryption.bruteforce_decrypt(element.text) + raise Bcfg2.Encryption.EVPError("Failed to decrypt") + def _include_element(self, item, metadata): """ determine if an XML element matches the metadata """ if isinstance(item, lxml.etree._Comment): # pylint: disable=W0212 diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py index 54fa75b41..4d6639e4d 100644 --- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py +++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py @@ -5,7 +5,7 @@ import shutil import tempfile import subprocess from Bcfg2.Options import get_option_parser -from Bcfg2.Server.Plugin import PluginExecutionError, StructFile +from Bcfg2.Server.Plugin import StructFile from Bcfg2.Server.Plugins.Cfg import CfgCreator, CfgCreationError from Bcfg2.Server.Plugins.Cfg.CfgPublicKeyCreator import CfgPublicKeyCreator try: @@ -211,44 +211,3 @@ class CfgPrivateKeyCreator(CfgCreator, StructFile): finally: shutil.rmtree(os.path.dirname(filename)) # pylint: enable=W0221 - - def Index(self): - StructFile.Index(self) - if HAS_CRYPTO: - strict = self.xdata.get( - "decrypt", - SETUP.cfp.get(Bcfg2.Encryption.CFG_SECTION, "decrypt", - default="strict")) == "strict" - for el in self.xdata.xpath("//*[@encrypted]"): - try: - el.text = self._decrypt(el).encode('ascii', - 'xmlcharrefreplace') - except UnicodeDecodeError: - self.logger.info("Cfg: Decrypted %s to gibberish, skipping" - % el.tag) - except Bcfg2.Encryption.EVPError: - msg = "Cfg: Failed to decrypt %s element in %s" % \ - (el.tag, self.name) - if strict: - raise PluginExecutionError(msg) - else: - self.logger.warning(msg) - Index.__doc__ = StructFile.Index.__doc__ - - def _decrypt(self, element): - """ Decrypt a single encrypted element """ - if not element.text or not element.text.strip(): - return - passes = Bcfg2.Encryption.get_passphrases() - try: - passphrase = passes[element.get("encrypted")] - try: - return Bcfg2.Encryption.ssl_decrypt(element.text, passphrase) - except Bcfg2.Encryption.EVPError: - # error is raised below - pass - except KeyError: - # bruteforce_decrypt raises an EVPError with a sensible - # error message, so we just let it propagate up the stack - return Bcfg2.Encryption.bruteforce_decrypt(element.text) - raise Bcfg2.Encryption.EVPError("Failed to decrypt") diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPublicKeyCreator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPublicKeyCreator.py index 6be438462..4c61e338e 100644 --- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPublicKeyCreator.py +++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPublicKeyCreator.py @@ -23,6 +23,9 @@ class CfgPublicKeyCreator(CfgCreator, StructFile): #: Handle XML specifications of private keys __basenames__ = ['pubkey.xml'] + #: No text content on any tags, so encryption support disabled + encryption = False + def __init__(self, fname): CfgCreator.__init__(self, fname) StructFile.__init__(self, fname) diff --git a/src/lib/Bcfg2/Server/Plugins/FileProbes.py b/src/lib/Bcfg2/Server/Plugins/FileProbes.py index 5ec0d7280..365549e85 100644 --- a/src/lib/Bcfg2/Server/Plugins/FileProbes.py +++ b/src/lib/Bcfg2/Server/Plugins/FileProbes.py @@ -51,6 +51,11 @@ print(Bcfg2.Client.XML.tostring(data, xml_declaration=False).decode('UTF-8')) """ +class FileProbesConfig(Bcfg2.Server.Plugin.StructFile): + """ Config file for FileProbes """ + encryption = False + + class FileProbes(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.Probing): """ This module allows you to probe a client for a file, which is then @@ -63,11 +68,9 @@ class FileProbes(Bcfg2.Server.Plugin.Plugin, def __init__(self, core, datastore): Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore) Bcfg2.Server.Plugin.Probing.__init__(self) - self.config = \ - Bcfg2.Server.Plugin.StructFile(os.path.join(self.data, - 'config.xml'), - fam=core.fam, - should_monitor=True) + self.config = FileProbesConfig(os.path.join(self.data, 'config.xml'), + fam=core.fam, + should_monitor=True) self.entries = dict() self.probes = dict() diff --git a/src/lib/Bcfg2/Server/Plugins/NagiosGen.py b/src/lib/Bcfg2/Server/Plugins/NagiosGen.py index c39bd4c42..baea5fe23 100644 --- a/src/lib/Bcfg2/Server/Plugins/NagiosGen.py +++ b/src/lib/Bcfg2/Server/Plugins/NagiosGen.py @@ -15,12 +15,14 @@ LOGGER = logging.getLogger(__name__) class NagiosGenConfig(Bcfg2.Server.Plugin.StructFile): """ NagiosGen config file handler """ + encryption = False + def __init__(self, filename, fam): # create config.xml if missing if not os.path.exists(filename): LOGGER.warning("NagiosGen: %s missing. " "Creating empty one for you." % filename) - open(filename, "w").write("") + open(filename, "w").write("") Bcfg2.Server.Plugin.StructFile.__init__(self, filename, fam=fam, should_monitor=True) diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py index 2735e389a..afa5da4c5 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py @@ -17,6 +17,8 @@ class PackagesSources(Bcfg2.Server.Plugin.StructFile, __identifier__ = None + encryption = False + def __init__(self, filename, cachepath, fam, packages, setup): """ :param filename: The full path to ``sources.xml`` diff --git a/src/lib/Bcfg2/Server/Plugins/Properties.py b/src/lib/Bcfg2/Server/Plugins/Properties.py index c5b5ea2d1..24daa2107 100644 --- a/src/lib/Bcfg2/Server/Plugins/Properties.py +++ b/src/lib/Bcfg2/Server/Plugins/Properties.py @@ -203,49 +203,6 @@ class XMLPropertyFile(Bcfg2.Server.Plugin.StructFile, PropertyFile): return True validate_data.__doc__ = PropertyFile.validate_data.__doc__ - def Index(self): - Bcfg2.Server.Plugin.StructFile.Index(self) - strict = self.xdata.get( - "decrypt", - SETUP.cfp.get(Bcfg2.Encryption.CFG_SECTION, "decrypt", - default="strict")) == "strict" - for el in self.xdata.xpath("//*[@encrypted]"): - if not HAS_CRYPTO: - raise PluginExecutionError("Properties: M2Crypto is not " - "available: %s" % self.name) - try: - el.text = self._decrypt(el).encode('ascii', - 'xmlcharrefreplace') - except UnicodeDecodeError: - LOGGER.info("Properties: Decrypted %s to gibberish, " - "skipping" % el.tag) - except Bcfg2.Encryption.EVPError: - msg = "Properties: Failed to decrypt %s element in %s" % \ - (el.tag, self.name) - if strict: - raise PluginExecutionError(msg) - else: - LOGGER.warning(msg) - Index.__doc__ = Bcfg2.Server.Plugin.StructFile.Index.__doc__ - - def _decrypt(self, element): - """ Decrypt a single encrypted properties file element """ - if not element.text or not element.text.strip(): - return - passes = Bcfg2.Encryption.get_passphrases() - try: - passphrase = passes[element.get("encrypted")] - try: - return Bcfg2.Encryption.ssl_decrypt(element.text, passphrase) - except Bcfg2.Encryption.EVPError: - # error is raised below - pass - except KeyError: - # bruteforce_decrypt raises an EVPError with a sensible - # error message, so we just let it propagate up the stack - return Bcfg2.Encryption.bruteforce_decrypt(element.text) - raise Bcfg2.Encryption.EVPError("Failed to decrypt") - def get_additional_data(self, metadata): if self.setup.cfp.getboolean("properties", "automatch", default=False): default_automatch = "true" diff --git a/src/lib/Bcfg2/Server/Plugins/SSLCA.py b/src/lib/Bcfg2/Server/Plugins/SSLCA.py index 0d51adf18..cc1a2ceac 100644 --- a/src/lib/Bcfg2/Server/Plugins/SSLCA.py +++ b/src/lib/Bcfg2/Server/Plugins/SSLCA.py @@ -17,6 +17,7 @@ LOGGER = logging.getLogger(__name__) class SSLCAXMLSpec(Bcfg2.Server.Plugin.StructFile): """ Base class to handle key.xml and cert.xml """ + encryption = False attrs = dict() tag = None -- cgit v1.2.3-1-g7c22 From f8b31cfa1596a17b248e28b82362e2d5e542b8ff Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Thu, 17 Jan 2013 09:27:58 -0500 Subject: StructFile: minor cleanup --- src/lib/Bcfg2/Server/Plugin/helpers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py index c8e09d3d1..399ab6679 100644 --- a/src/lib/Bcfg2/Server/Plugin/helpers.py +++ b/src/lib/Bcfg2/Server/Plugin/helpers.py @@ -597,7 +597,7 @@ class StructFile(XMLFileBacked): self.setup = Bcfg2.Options.get_option_parser() def Index(self): - Bcfg2.Server.Plugin.XMLFileBacked.Index(self) + XMLFileBacked.Index(self) if self.encryption: strict = self.xdata.get( "decrypt", @@ -605,8 +605,8 @@ class StructFile(XMLFileBacked): default="strict")) == "strict" for el in self.xdata.xpath("//*[@encrypted]"): if not HAS_CRYPTO: - raise PluginExecutionError("Properties: M2Crypto is not " - "available: %s" % self.name) + raise PluginExecutionError("%s: M2Crypto is not available" + % self.name) try: el.text = self._decrypt(el).encode('ascii', 'xmlcharrefreplace') -- cgit v1.2.3-1-g7c22 From 4ea3569a0b396622a6c156588c4c8752a8dd7738 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Thu, 17 Jan 2013 10:00:20 -0500 Subject: Options: split loading a new OptionParser from fetching an existing parser --- src/lib/Bcfg2/Options.py | 31 ++++++++++++++++++------------- src/sbin/bcfg2 | 4 ++-- src/sbin/bcfg2-admin | 2 +- src/sbin/bcfg2-crypt | 2 +- src/sbin/bcfg2-info | 2 +- src/sbin/bcfg2-lint | 2 +- src/sbin/bcfg2-report-collector | 2 +- src/sbin/bcfg2-server | 2 +- src/sbin/bcfg2-test | 2 +- 9 files changed, 27 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/lib/Bcfg2/Options.py b/src/lib/Bcfg2/Options.py index 780619f3b..4818c67f9 100644 --- a/src/lib/Bcfg2/Options.py +++ b/src/lib/Bcfg2/Options.py @@ -1256,16 +1256,12 @@ class OptionParser(OptionSet): _PARSER = None -def get_option_parser(args=None, argv=None, quiet=False): - """ Get an OptionParser object. If :attr:`_PARSER` already - exists, that will be used; if not, a new OptionParser object will - be created. +def load_option_parser(args, argv=None, quiet=False): + """ Load an :class:`Bcfg2.Options.OptionParser` object, caching it + in :attr:`_PARSER` for later retrieval via + :func:`get_option_parser`. - If ``args`` or ``argv`` are given, then a new OptionParser object - will be instantiated regardless of whether one already exists. - - :param args: The argument set to parse. This is required on the - first invocation of :func:`get_option_parser`. + :param args: The argument set to parse. :type args: dict of :class:`Bcfg2.Options.Option` objects :param argv: The command-line argument list. If this is not provided, :attr:`sys.argv` will be used. @@ -1275,8 +1271,17 @@ def get_option_parser(args=None, argv=None, quiet=False): :returns: :class:`Bcfg2.Options.OptionParser` """ global _PARSER # pylint: disable=W0603 - if _PARSER is None or args is not None or argv is not None: - if args is None: - args = CLI_COMMON_OPTIONS - _PARSER = OptionParser(args, argv=argv, quiet=quiet) + _PARSER = OptionParser(args, argv=argv, quiet=quiet) + return _PARSER + + +def get_option_parser(): + """ Get an already-created :class:`Bcfg2.Options.OptionParser` object. If + :attr:`_PARSER` has not been populated, then a new OptionParser + will be created with basic arguments. + + :returns: :class:`Bcfg2.Options.OptionParser` + """ + if _PARSER is None: + return load_option_parser(CLI_COMMON_OPTIONS) return _PARSER diff --git a/src/sbin/bcfg2 b/src/sbin/bcfg2 index e9beb47cd..62f749b80 100755 --- a/src/sbin/bcfg2 +++ b/src/sbin/bcfg2 @@ -4,7 +4,7 @@ import sys import signal from Bcfg2.Client.Client import Client -from Bcfg2.Options import get_option_parser, CLIENT_COMMON_OPTIONS +from Bcfg2.Options import load_option_parser, CLIENT_COMMON_OPTIONS def cb_sigint_handler(signum, frame): @@ -13,7 +13,7 @@ def cb_sigint_handler(signum, frame): def main(): - setup = get_option_parser(CLIENT_COMMON_OPTIONS) + setup = load_option_parser(CLIENT_COMMON_OPTIONS) setup.parse(sys.argv[1:]) if setup['args']: diff --git a/src/sbin/bcfg2-admin b/src/sbin/bcfg2-admin index 8f6643ee0..3c63cd3f5 100755 --- a/src/sbin/bcfg2-admin +++ b/src/sbin/bcfg2-admin @@ -41,7 +41,7 @@ def main(): optinfo = dict() optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS) - setup = Bcfg2.Options.get_option_parser(optinfo) + setup = Bcfg2.Options.load_option_parser(optinfo) # override default help message to include description of all modes setup.hm = "Usage:\n\n%s\n%s" % (setup.buildHelpMessage(), create_description()) diff --git a/src/sbin/bcfg2-crypt b/src/sbin/bcfg2-crypt index 6a13b9bc5..0bee7e9b9 100755 --- a/src/sbin/bcfg2-crypt +++ b/src/sbin/bcfg2-crypt @@ -371,7 +371,7 @@ def main(): # pylint: disable=R0912,R0915 optinfo = dict(interactive=Bcfg2.Options.INTERACTIVE) optinfo.update(Bcfg2.Options.CRYPT_OPTIONS) optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) - setup = Bcfg2.Options.get_option_parser(optinfo) + setup = Bcfg2.Options.load_option_parser(optinfo) setup.hm = " bcfg2-crypt [options] \nOptions:\n%s" % \ setup.buildHelpMessage() setup.parse() diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info index c697e9fca..60b099284 100755 --- a/src/sbin/bcfg2-info +++ b/src/sbin/bcfg2-info @@ -751,7 +751,7 @@ def main(): interpreter=Bcfg2.Options.INTERPRETER) optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS) - setup = Bcfg2.Options.get_option_parser(optinfo) + setup = Bcfg2.Options.load_option_parser(optinfo) setup.hm = "\n".join([" bcfg2-info [options] [command ]", "Options:", setup.buildHelpMessage(), diff --git a/src/sbin/bcfg2-lint b/src/sbin/bcfg2-lint index f12fcc6c4..557a03405 100755 --- a/src/sbin/bcfg2-lint +++ b/src/sbin/bcfg2-lint @@ -146,7 +146,7 @@ def main(): lint_plugins=Bcfg2.Options.LINT_PLUGINS) optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS) - setup = Bcfg2.Options.get_option_parser(optinfo) + setup = Bcfg2.Options.load_option_parser(optinfo) setup.parse(sys.argv[1:]) log_args = dict(to_syslog=setup['syslog'], to_console=logging.WARNING) diff --git a/src/sbin/bcfg2-report-collector b/src/sbin/bcfg2-report-collector index d925023d1..403775251 100755 --- a/src/sbin/bcfg2-report-collector +++ b/src/sbin/bcfg2-report-collector @@ -20,7 +20,7 @@ def main(): ) optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.REPORTING_COMMON_OPTIONS) - setup = Bcfg2.Options.get_option_parser(optinfo) + setup = Bcfg2.Options.load_option_parser(optinfo) setup.parse() # run collector diff --git a/src/sbin/bcfg2-server b/src/sbin/bcfg2-server index 7e7dede30..8e96d65f2 100755 --- a/src/sbin/bcfg2-server +++ b/src/sbin/bcfg2-server @@ -16,7 +16,7 @@ def main(): optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.DAEMON_COMMON_OPTIONS) - setup = Bcfg2.Options.get_option_parser(args=optinfo) + setup = Bcfg2.Options.load_option_parser(optinfo) setup.parse(sys.argv[1:]) # check whether the specified bcfg2.conf exists if not os.path.exists(setup['configfile']): diff --git a/src/sbin/bcfg2-test b/src/sbin/bcfg2-test index a708a233a..28023a0f2 100755 --- a/src/sbin/bcfg2-test +++ b/src/sbin/bcfg2-test @@ -69,7 +69,7 @@ def main(): validate=Bcfg2.Options.CFG_VALIDATION) optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS) - setup = Bcfg2.Options.get_option_parser(args=optinfo) + setup = Bcfg2.Options.load_option_parser(optinfo) setup.hm = \ "bcfg2-test [options] [client] [client] [...]\nOptions:\n %s" % \ setup.buildHelpMessage() -- cgit v1.2.3-1-g7c22