#!/usr/bin/env python """This tool examines your Bcfg2 specifications for errors.""" import sys import time import logging import Bcfg2.Logger import Bcfg2.Options import Bcfg2.Server.Core import Bcfg2.Server.Lint LOGGER = logging.getLogger('bcfg2-lint') def run_serverless_plugins(plugins, setup=None, 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) def run_server_plugins(plugins, setup=None, errorhandler=None, files=None): """ run plugins that require a running server to run """ core = load_server(setup) 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) finally: core.shutdown() def run_plugin(plugin, plugin_name, setup=None, 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) if setup is not None and setup.cfp.has_section(plugin_name): arg = setup for key, val in setup.cfp.items(plugin_name): arg[key] = val args.append(arg) else: args.append(setup) # python 2.5 doesn't support mixing *magic and keyword arguments start = time.time() rv = plugin(*args, **dict(files=files, errorhandler=errorhandler)).Run() LOGGER.debug(" Ran %s in %0.2f seconds" % (plugin_name, time.time() - start)) return rv def get_errorhandler(setup): """ get a Bcfg2.Server.Lint.ErrorHandler object """ if setup.cfp.has_section("errors"): errors = dict(setup.cfp.items("errors")) else: errors = None return Bcfg2.Server.Lint.ErrorHandler(errors=errors) def load_server(setup): """ load server """ core = Bcfg2.Server.Core.BaseCore(setup) core.load_plugins() core.block_for_fam_events(handle_events=True) return core def load_plugin(module, obj_name=None): """ load a single plugin """ parts = module.split(".") if obj_name is None: obj_name = parts[-1] mod = __import__(module) for part in parts[1:]: mod = getattr(mod, part) return getattr(mod, obj_name) def load_plugins(setup): """ get list of plugins to run """ if setup['args']: plugin_list = setup['args'] elif "bcfg2-repo-validate" in sys.argv[0]: plugin_list = 'RequiredAttrs,Validate'.split(',') elif setup['lint_plugins']: plugin_list = setup['lint_plugins'] else: plugin_list = Bcfg2.Server.Lint.plugins allplugins = dict() for plugin in plugin_list: try: allplugins[plugin] = load_plugin("Bcfg2.Server.Lint." + plugin) except ImportError: try: allplugins[plugin] = \ load_plugin("Bcfg2.Server.Plugins." + plugin, obj_name=plugin + "Lint") except (ImportError, AttributeError): err = sys.exc_info()[1] LOGGER.error("Failed to load plugin %s: %s" % (plugin + "Lint", err)) except AttributeError: err = sys.exc_info()[1] LOGGER.error("Failed to load plugin %s: %s" % (plugin, err)) for plugin in setup['plugins']: if plugin in allplugins: # already loaded continue try: allplugins[plugin] = \ load_plugin("Bcfg2.Server.Plugins." + plugin, obj_name=plugin + "Lint") except AttributeError: pass except ImportError: err = sys.exc_info()[1] LOGGER.error("Failed to load plugin %s: %s" % (plugin + "Lint", err)) serverplugins = dict() serverlessplugins = dict() for plugin_name, plugin in allplugins.items(): if issubclass(plugin, Bcfg2.Server.Lint.ServerPlugin): serverplugins[plugin_name] = plugin else: serverlessplugins[plugin_name] = plugin return (serverlessplugins, serverplugins) def main(): optinfo = dict(lint_config=Bcfg2.Options.LINT_CONFIG, showerrors=Bcfg2.Options.LINT_SHOW_ERRORS, stdin=Bcfg2.Options.LINT_FILES_ON_STDIN, schema=Bcfg2.Options.SCHEMA_PATH, 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.parse(sys.argv[1:]) log_args = dict(to_syslog=setup['syslog'], to_console=logging.WARNING) if setup['verbose']: log_args['to_console'] = logging.DEBUG Bcfg2.Logger.setup_logging('bcfg2-info', **log_args) setup.cfp.read(setup['lint_config']) setup.reparse() if setup['stdin']: files = [s.strip() for s in sys.stdin.readlines()] else: files = None (serverlessplugins, serverplugins) = load_plugins(setup) errorhandler = get_errorhandler(setup) if setup['showerrors']: for plugin in serverplugins.values() + serverlessplugins.values(): errorhandler.RegisterErrors(getattr(plugin, 'Errors')()) print("%-35s %-35s" % ("Error name", "Handler")) for err, handler in errorhandler.errortypes.items(): print("%-35s %-35s" % (err, handler.__name__)) raise SystemExit(0) run_serverless_plugins(serverlessplugins, errorhandler=errorhandler, setup=setup, files=files) 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, setup=setup, files=files) 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) if __name__ == '__main__': sys.exit(main())