#!/usr/bin/env python """This tool examines your Bcfg2 specifications for errors.""" __revision__ = '$Revision$' import sys import inspect import logging import Bcfg2.Logger import Bcfg2.Options import Bcfg2.Server.Core import Bcfg2.Server.Lint # Compatibility imports from Bcfg2.Bcfg2Py3k import ConfigParser logger = logging.getLogger('bcfg2-lint') def run_serverless_plugins(plugins, config=None, setup=None, errorhandler=None): logger.debug("Running serverless plugins") for plugin_name, plugin in list(plugins.items()): run_plugin(plugin, plugin_name, errorhandler=errorhandler, setup=setup, config=config, files=files) def run_server_plugins(plugins, config=None, setup=None, errorhandler=None): core = load_server(setup) logger.debug("Running server plugins") for plugin_name, plugin in list(plugins.items()): run_plugin(plugin, plugin_name, args=[core], errorhandler=errorhandler, setup=setup, config=config, files=files) def run_plugin(plugin, plugin_name, setup=None, errorhandler=None, args=None, config=None, files=None): logger.debug(" Running %s" % plugin_name) if args is None: args = [] if errorhandler is None: errorhandler = get_errorhandler(config) if config is not None and config.has_section(plugin_name): arg = setup for key, val in config.items(plugin_name): arg[key] = val args.append(arg) else: args.append(setup) # older versions of python do not support mixing *-magic and # non-*-magic (e.g., "plugin(*args, files=files)", so we do this # all with *-magic kwargs = dict(files=files, errorhandler=errorhandler) return plugin(*args, **kwargs).Run() def get_errorhandler(config): """ get a Bcfg2.Server.Lint.ErrorHandler object """ if config.has_section("errors"): conf = dict(config.items("errors")) else: conf = None return Bcfg2.Server.Lint.ErrorHandler(config=conf) def load_server(setup): """ load server """ core = Bcfg2.Server.Core.Core(setup['repo'], setup['plugins'], setup['password'], setup['encoding']) if setup['event debug']: core.fam.debug = True core.fam.handle_events_in_interval(4) return core if __name__ == '__main__': optinfo = { 'configfile': Bcfg2.Options.CFILE, 'help': Bcfg2.Options.HELP, 'verbose': Bcfg2.Options.VERBOSE, } optinfo.update({ 'event debug': Bcfg2.Options.DEBUG, 'encoding': Bcfg2.Options.ENCODING, # Server options 'repo': Bcfg2.Options.SERVER_REPOSITORY, 'plugins': Bcfg2.Options.SERVER_PLUGINS, 'mconnect': Bcfg2.Options.SERVER_MCONNECT, 'filemonitor': Bcfg2.Options.SERVER_FILEMONITOR, 'location': Bcfg2.Options.SERVER_LOCATION, 'static': Bcfg2.Options.SERVER_STATIC, 'key': Bcfg2.Options.SERVER_KEY, 'cert': Bcfg2.Options.SERVER_CERT, 'ca': Bcfg2.Options.SERVER_CA, 'password': Bcfg2.Options.SERVER_PASSWORD, 'protocol': Bcfg2.Options.SERVER_PROTOCOL, # More options 'logging': Bcfg2.Options.LOGGING_FILE_PATH, 'stdin': Bcfg2.Options.FILES_ON_STDIN, 'schema': Bcfg2.Options.SCHEMA_PATH, 'config': Bcfg2.Options.Option('Specify bcfg2-lint configuration file', '/etc/bcfg2-lint.conf', cmd='--lint-config', odesc='', long_arg=True), 'showerrors': Bcfg2.Options.Option('Show error handling', False, cmd='--list-errors', long_arg=True), }) setup = Bcfg2.Options.OptionParser(optinfo) setup.parse(sys.argv[1:]) log_args = dict(to_syslog=False, to_console=logging.WARNING) if setup['verbose']: log_args['to_console'] = logging.DEBUG Bcfg2.Logger.setup_logging('bcfg2-info', **log_args) config = ConfigParser.SafeConfigParser() config.read(setup['configfile']) config.read(setup['config']) if setup['showerrors']: if config.has_section("errors"): econf = dict(config.items("errors")) else: econf = dict() print("%-35s %-35s" % ("Error name", "Handler (Default)")) for err, default in Bcfg2.Server.Lint.ErrorHandler._errors.items(): if err in econf and econf[err] != default: handler = "%s (%s)" % (econf[err], default) else: handler = default print("%-35s %-35s" % (err, handler)) raise SystemExit(0) # get list of plugins to run if setup['args']: allplugins = setup['args'] elif "bcfg2-repo-validate" in sys.argv[0]: allplugins = 'Duplicates,RequiredAttrs,Validate'.split(',') else: try: allplugins = config.get('lint', 'plugins').split(',') except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): allplugins = Bcfg2.Server.Lint.__all__ if setup['stdin']: files = [s.strip() for s in sys.stdin.readlines()] else: files = None # load plugins serverplugins = {} serverlessplugins = {} for plugin_name in allplugins: try: mod = getattr(__import__("Bcfg2.Server.Lint.%s" % (plugin_name)).Server.Lint, plugin_name) except ImportError: try: mod = __import__(plugin_name) except Exception: err = sys.exc_info()[1] logger.error("Failed to load plugin %s: %s" % (plugin_name, err)) raise SystemExit(1) plugin = getattr(mod, plugin_name) if [c for c in inspect.getmro(plugin) if c == Bcfg2.Server.Lint.ServerPlugin]: serverplugins[plugin_name] = plugin else: serverlessplugins[plugin_name] = plugin errorhandler = get_errorhandler(config) run_serverless_plugins(serverlessplugins, errorhandler=errorhandler, config=config, setup=setup) if serverplugins: if errorhandler.errors: # it would be swell if we could try to start the server # even if there were errors with the serverless plugins, # but since XML parsing errors occur in the FAM thread # (not in the core server thread), there's no way we can # start the server and try to catch exceptions -- # bcfg2-lint isn't in the same stack as the exceptions. # so we're forced to assume that a serverless plugin error # will prevent the server from starting print("Serverless plugins encountered errors, skipping server " "plugins") else: run_server_plugins(serverplugins, errorhandler=errorhandler, config=config, setup=setup) if errorhandler.errors or errorhandler.warnings or setup['verbose']: print("%d errors" % errorhandler.errors) print("%d warnings" % errorhandler.warnings) if errorhandler.errors: raise SystemExit(2) elif errorhandler.warnings: raise SystemExit(3)