|
|
0f405b |
From 952fc6f6dee99523360c9826ad865086cd31474f Mon Sep 17 00:00:00 2001
|
|
|
0f405b |
From: Rob Crittenden <rcritten@redhat.com>
|
|
|
0f405b |
Date: Mon, 18 Nov 2019 14:33:32 -0500
|
|
|
0f405b |
Subject: [PATCH 4/5] Move the abstracted plugin runner code into a separate
|
|
|
0f405b |
file
|
|
|
0f405b |
|
|
|
0f405b |
This way main.py contains just the ipa-healthcheck main code
|
|
|
0f405b |
and not the rest of the abstracted code.
|
|
|
0f405b |
|
|
|
0f405b |
Also replace sys.exit(1) with return 1.
|
|
|
0f405b |
---
|
|
|
0f405b |
src/ipahealthcheck/core/core.py | 272 ++++++++++++++++++++++++++++++++
|
|
|
0f405b |
src/ipahealthcheck/core/main.py | 267 +------------------------------
|
|
|
0f405b |
2 files changed, 273 insertions(+), 266 deletions(-)
|
|
|
0f405b |
create mode 100644 src/ipahealthcheck/core/core.py
|
|
|
0f405b |
|
|
|
0f405b |
diff --git a/src/ipahealthcheck/core/core.py b/src/ipahealthcheck/core/core.py
|
|
|
0f405b |
new file mode 100644
|
|
|
0f405b |
index 0000000..182eac3
|
|
|
0f405b |
--- /dev/null
|
|
|
0f405b |
+++ b/src/ipahealthcheck/core/core.py
|
|
|
0f405b |
@@ -0,0 +1,272 @@
|
|
|
0f405b |
+#
|
|
|
0f405b |
+# Copyright (C) 2019 FreeIPA Contributors see COPYING for license
|
|
|
0f405b |
+#
|
|
|
0f405b |
+
|
|
|
0f405b |
+import argparse
|
|
|
0f405b |
+import json
|
|
|
0f405b |
+import logging
|
|
|
0f405b |
+import pkg_resources
|
|
|
0f405b |
+
|
|
|
0f405b |
+from datetime import datetime
|
|
|
0f405b |
+
|
|
|
0f405b |
+from ipahealthcheck.core.config import read_config
|
|
|
0f405b |
+from ipahealthcheck.core.plugin import Result, Results, json_to_results
|
|
|
0f405b |
+from ipahealthcheck.core.output import output_registry
|
|
|
0f405b |
+from ipahealthcheck.core import constants
|
|
|
0f405b |
+from ipahealthcheck.core.service import ServiceCheck
|
|
|
0f405b |
+
|
|
|
0f405b |
+logging.basicConfig(format='%(message)s')
|
|
|
0f405b |
+logger = logging.getLogger()
|
|
|
0f405b |
+
|
|
|
0f405b |
+
|
|
|
0f405b |
+def find_registries(entry_points):
|
|
|
0f405b |
+ registries = {}
|
|
|
0f405b |
+ for entry_point in entry_points:
|
|
|
0f405b |
+ registries.update({
|
|
|
0f405b |
+ ep.name: ep.resolve()
|
|
|
0f405b |
+ for ep in pkg_resources.iter_entry_points(entry_point)
|
|
|
0f405b |
+ })
|
|
|
0f405b |
+ return registries
|
|
|
0f405b |
+
|
|
|
0f405b |
+
|
|
|
0f405b |
+def find_plugins(name, registry):
|
|
|
0f405b |
+ for ep in pkg_resources.iter_entry_points(name):
|
|
|
0f405b |
+ # load module
|
|
|
0f405b |
+ ep.load()
|
|
|
0f405b |
+ return registry.get_plugins()
|
|
|
0f405b |
+
|
|
|
0f405b |
+
|
|
|
0f405b |
+def run_plugin(plugin, available=()):
|
|
|
0f405b |
+ # manually calculate duration when we create results of our own
|
|
|
0f405b |
+ start = datetime.utcnow()
|
|
|
0f405b |
+ try:
|
|
|
0f405b |
+ for result in plugin.check():
|
|
|
0f405b |
+ if result is None:
|
|
|
0f405b |
+ # Treat no result as success, fudge start time
|
|
|
0f405b |
+ result = Result(plugin, constants.SUCCESS, start=start)
|
|
|
0f405b |
+ yield result
|
|
|
0f405b |
+ except Exception as e:
|
|
|
0f405b |
+ logger.debug('Exception raised: %s', e)
|
|
|
0f405b |
+ yield Result(plugin, constants.CRITICAL, exception=str(e),
|
|
|
0f405b |
+ start=start)
|
|
|
0f405b |
+
|
|
|
0f405b |
+
|
|
|
0f405b |
+def source_or_check_matches(plugin, source, check):
|
|
|
0f405b |
+ """Determine whether a given a plugin matches if a source
|
|
|
0f405b |
+ and optional check are provided.
|
|
|
0f405b |
+ """
|
|
|
0f405b |
+ if source is not None and plugin.__module__ != source:
|
|
|
0f405b |
+ return False
|
|
|
0f405b |
+
|
|
|
0f405b |
+ if check and plugin.__class__.__name__ != check:
|
|
|
0f405b |
+ return False
|
|
|
0f405b |
+
|
|
|
0f405b |
+ return True
|
|
|
0f405b |
+
|
|
|
0f405b |
+
|
|
|
0f405b |
+def run_service_plugins(plugins, config, source, check):
|
|
|
0f405b |
+ """Execute plugins with the base class of ServiceCheck
|
|
|
0f405b |
+
|
|
|
0f405b |
+ This is a specialized check to use systemd to determine
|
|
|
0f405b |
+ if a service is running or not.
|
|
|
0f405b |
+ """
|
|
|
0f405b |
+ results = Results()
|
|
|
0f405b |
+ available = []
|
|
|
0f405b |
+
|
|
|
0f405b |
+ for plugin in plugins:
|
|
|
0f405b |
+ if not isinstance(plugin, ServiceCheck):
|
|
|
0f405b |
+ continue
|
|
|
0f405b |
+
|
|
|
0f405b |
+ logger.debug('Calling check %s', plugin)
|
|
|
0f405b |
+ for result in plugin.check():
|
|
|
0f405b |
+ # always run the service checks so dependencies work
|
|
|
0f405b |
+ if result is not None and result.result == constants.SUCCESS:
|
|
|
0f405b |
+ available.append(plugin.service.service_name)
|
|
|
0f405b |
+ if not source_or_check_matches(plugin, source, check):
|
|
|
0f405b |
+ continue
|
|
|
0f405b |
+ if result is not None:
|
|
|
0f405b |
+ results.add(result)
|
|
|
0f405b |
+
|
|
|
0f405b |
+ return results, set(available)
|
|
|
0f405b |
+
|
|
|
0f405b |
+
|
|
|
0f405b |
+def run_plugins(plugins, config, available, source, check):
|
|
|
0f405b |
+ """Execute plugins without the base class of ServiceCheck
|
|
|
0f405b |
+
|
|
|
0f405b |
+ These are the remaining, non-service checking checks
|
|
|
0f405b |
+ that do validation for various parts of a system.
|
|
|
0f405b |
+ """
|
|
|
0f405b |
+ results = Results()
|
|
|
0f405b |
+
|
|
|
0f405b |
+ for plugin in plugins:
|
|
|
0f405b |
+ if isinstance(plugin, ServiceCheck):
|
|
|
0f405b |
+ continue
|
|
|
0f405b |
+
|
|
|
0f405b |
+ if not source_or_check_matches(plugin, source, check):
|
|
|
0f405b |
+ continue
|
|
|
0f405b |
+
|
|
|
0f405b |
+ logger.debug('Calling check %s' % plugin)
|
|
|
0f405b |
+ plugin.config = config
|
|
|
0f405b |
+ if not set(plugin.requires).issubset(available):
|
|
|
0f405b |
+ logger.debug('Skipping %s:%s because %s service(s) not running',
|
|
|
0f405b |
+ plugin.__class__.__module__,
|
|
|
0f405b |
+ plugin.__class__.__name__,
|
|
|
0f405b |
+ ', '.join(set(plugin.requires) - available))
|
|
|
0f405b |
+ # Not providing a Result in this case because if a required
|
|
|
0f405b |
+ # service isn't available then this could generate a lot of
|
|
|
0f405b |
+ # false positives.
|
|
|
0f405b |
+ else:
|
|
|
0f405b |
+ for result in run_plugin(plugin, available):
|
|
|
0f405b |
+ results.add(result)
|
|
|
0f405b |
+
|
|
|
0f405b |
+ return results
|
|
|
0f405b |
+
|
|
|
0f405b |
+
|
|
|
0f405b |
+def list_sources(plugins):
|
|
|
0f405b |
+ """Print list of all sources and checks"""
|
|
|
0f405b |
+ source = None
|
|
|
0f405b |
+ for plugin in plugins:
|
|
|
0f405b |
+ if source != plugin.__class__.__module__:
|
|
|
0f405b |
+ print(plugin.__class__.__module__)
|
|
|
0f405b |
+ source = plugin.__class__.__module__
|
|
|
0f405b |
+ print(" ", plugin.__class__.__name__)
|
|
|
0f405b |
+
|
|
|
0f405b |
+ return 0
|
|
|
0f405b |
+
|
|
|
0f405b |
+
|
|
|
0f405b |
+def parse_options(output_registry):
|
|
|
0f405b |
+ output_names = [plugin.__name__.lower() for
|
|
|
0f405b |
+ plugin in output_registry.plugins]
|
|
|
0f405b |
+ parser = argparse.ArgumentParser()
|
|
|
0f405b |
+ parser.add_argument('--debug', dest='debug', action='store_true',
|
|
|
0f405b |
+ default=False, help='Include debug output')
|
|
|
0f405b |
+ parser.add_argument('--list-sources', dest='list_sources',
|
|
|
0f405b |
+ action='store_true', default=False,
|
|
|
0f405b |
+ help='List all available sources')
|
|
|
0f405b |
+ parser.add_argument('--source', dest='source',
|
|
|
0f405b |
+ default=None,
|
|
|
0f405b |
+ help='Source of checks, e.g. ipahealthcheck.foo.bar')
|
|
|
0f405b |
+ parser.add_argument('--check', dest='check',
|
|
|
0f405b |
+ default=None,
|
|
|
0f405b |
+ help='Check to execute, e.g. BazCheck')
|
|
|
0f405b |
+ parser.add_argument('--output-type', dest='output', choices=output_names,
|
|
|
0f405b |
+ default='json', help='Output method')
|
|
|
0f405b |
+ parser.add_argument('--output-file', dest='outfile', default=None,
|
|
|
0f405b |
+ help='File to store output')
|
|
|
0f405b |
+ parser.add_argument('--input-file', dest='infile',
|
|
|
0f405b |
+ help='File to read as input')
|
|
|
0f405b |
+ parser.add_argument('--failures-only', dest='failures_only',
|
|
|
0f405b |
+ action='store_true', default=False,
|
|
|
0f405b |
+ help='Exclude SUCCESS results on output')
|
|
|
0f405b |
+ parser.add_argument('--severity', dest='severity', action="append",
|
|
|
0f405b |
+ help='Include only the selected severity(s)',
|
|
|
0f405b |
+ choices=[key for key in constants._nameToLevel])
|
|
|
0f405b |
+ for plugin in output_registry.plugins:
|
|
|
0f405b |
+ onelinedoc = plugin.__doc__.split('\n\n', 1)[0].strip()
|
|
|
0f405b |
+ group = parser.add_argument_group(plugin.__name__.lower(),
|
|
|
0f405b |
+ onelinedoc)
|
|
|
0f405b |
+ for option in plugin.options:
|
|
|
0f405b |
+ group.add_argument(option[0], **option[1])
|
|
|
0f405b |
+
|
|
|
0f405b |
+ options = parser.parse_args()
|
|
|
0f405b |
+
|
|
|
0f405b |
+ # Validation
|
|
|
0f405b |
+ if options.check and not options.source:
|
|
|
0f405b |
+ print("--source is required when --check is used")
|
|
|
0f405b |
+ return 1
|
|
|
0f405b |
+
|
|
|
0f405b |
+ return options
|
|
|
0f405b |
+
|
|
|
0f405b |
+
|
|
|
0f405b |
+def limit_results(results, source, check):
|
|
|
0f405b |
+ """Return ony those results which match source and/or check"""
|
|
|
0f405b |
+ new_results = Results()
|
|
|
0f405b |
+ for result in results.results:
|
|
|
0f405b |
+ if result.source == source:
|
|
|
0f405b |
+ if check is None or result.check == check:
|
|
|
0f405b |
+ new_results.add(result)
|
|
|
0f405b |
+ return new_results
|
|
|
0f405b |
+
|
|
|
0f405b |
+
|
|
|
0f405b |
+def run_healthcheck(entry_points, configfile):
|
|
|
0f405b |
+ framework = object()
|
|
|
0f405b |
+ plugins = []
|
|
|
0f405b |
+ output = constants.DEFAULT_OUTPUT
|
|
|
0f405b |
+
|
|
|
0f405b |
+ logger.setLevel(logging.INFO)
|
|
|
0f405b |
+
|
|
|
0f405b |
+ options = parse_options(output_registry)
|
|
|
0f405b |
+
|
|
|
0f405b |
+ if options.debug:
|
|
|
0f405b |
+ logger.setLevel(logging.DEBUG)
|
|
|
0f405b |
+
|
|
|
0f405b |
+ config = read_config(configfile)
|
|
|
0f405b |
+ if config is None:
|
|
|
0f405b |
+ return 1
|
|
|
0f405b |
+
|
|
|
0f405b |
+ for name, registry in find_registries(entry_points).items():
|
|
|
0f405b |
+ try:
|
|
|
0f405b |
+ registry.initialize(framework)
|
|
|
0f405b |
+ except Exception as e:
|
|
|
0f405b |
+ print("Unable to initialize %s: %s" % (name, e))
|
|
|
0f405b |
+ return 1
|
|
|
0f405b |
+ for plugin in find_plugins(name, registry):
|
|
|
0f405b |
+ plugins.append(plugin)
|
|
|
0f405b |
+
|
|
|
0f405b |
+ for out in output_registry.plugins:
|
|
|
0f405b |
+ if out.__name__.lower() == options.output:
|
|
|
0f405b |
+ output = out(options)
|
|
|
0f405b |
+
|
|
|
0f405b |
+ if options.list_sources:
|
|
|
0f405b |
+ return list_sources(plugins)
|
|
|
0f405b |
+
|
|
|
0f405b |
+ if options.infile:
|
|
|
0f405b |
+ try:
|
|
|
0f405b |
+ with open(options.infile, 'r') as f:
|
|
|
0f405b |
+ raw_data = f.read()
|
|
|
0f405b |
+
|
|
|
0f405b |
+ json_data = json.loads(raw_data)
|
|
|
0f405b |
+ results = json_to_results(json_data)
|
|
|
0f405b |
+ available = ()
|
|
|
0f405b |
+ except Exception as e:
|
|
|
0f405b |
+ print("Unable to import '%s': %s" % (options.infile, e))
|
|
|
0f405b |
+ return 1
|
|
|
0f405b |
+ if options.source:
|
|
|
0f405b |
+ results = limit_results(results, options.source, options.check)
|
|
|
0f405b |
+ else:
|
|
|
0f405b |
+ results, available = run_service_plugins(plugins, config,
|
|
|
0f405b |
+ options.source,
|
|
|
0f405b |
+ options.check)
|
|
|
0f405b |
+ results.extend(run_plugins(plugins, config, available,
|
|
|
0f405b |
+ options.source, options.check))
|
|
|
0f405b |
+
|
|
|
0f405b |
+ if options.source and len(results.results) == 0:
|
|
|
0f405b |
+ for plugin in plugins:
|
|
|
0f405b |
+ if not source_or_check_matches(plugin, options.source,
|
|
|
0f405b |
+ options.check):
|
|
|
0f405b |
+ continue
|
|
|
0f405b |
+
|
|
|
0f405b |
+ if not set(plugin.requires).issubset(available):
|
|
|
0f405b |
+ print("Source '%s' is missing one or more requirements '%s'" %
|
|
|
0f405b |
+ (options.source, ', '.join(plugin.requires)))
|
|
|
0f405b |
+ return 1
|
|
|
0f405b |
+
|
|
|
0f405b |
+ if options.check:
|
|
|
0f405b |
+ print("Check '%s' not found in Source '%s'" %
|
|
|
0f405b |
+ (options.check, options.source))
|
|
|
0f405b |
+ else:
|
|
|
0f405b |
+ print("Source '%s' not found" % options.source)
|
|
|
0f405b |
+ return 1
|
|
|
0f405b |
+
|
|
|
0f405b |
+ try:
|
|
|
0f405b |
+ output.render(results)
|
|
|
0f405b |
+ except Exception as e:
|
|
|
0f405b |
+ logger.error('Output raised %s: %s', e.__class__.__name__, e)
|
|
|
0f405b |
+
|
|
|
0f405b |
+ return_value = 0
|
|
|
0f405b |
+ for result in results.results:
|
|
|
0f405b |
+ if result.result != constants.SUCCESS:
|
|
|
0f405b |
+ return_value = 1
|
|
|
0f405b |
+ break
|
|
|
0f405b |
+
|
|
|
0f405b |
+ return return_value
|
|
|
0f405b |
diff --git a/src/ipahealthcheck/core/main.py b/src/ipahealthcheck/core/main.py
|
|
|
0f405b |
index 2b818d4..d9e85d7 100644
|
|
|
0f405b |
--- a/src/ipahealthcheck/core/main.py
|
|
|
0f405b |
+++ b/src/ipahealthcheck/core/main.py
|
|
|
0f405b |
@@ -2,276 +2,11 @@
|
|
|
0f405b |
# Copyright (C) 2019 FreeIPA Contributors see COPYING for license
|
|
|
0f405b |
#
|
|
|
0f405b |
|
|
|
0f405b |
-import argparse
|
|
|
0f405b |
-import json
|
|
|
0f405b |
-import logging
|
|
|
0f405b |
from os import environ
|
|
|
0f405b |
-import pkg_resources
|
|
|
0f405b |
import sys
|
|
|
0f405b |
|
|
|
0f405b |
-from datetime import datetime
|
|
|
0f405b |
-
|
|
|
0f405b |
-from ipahealthcheck.core.config import read_config
|
|
|
0f405b |
-from ipahealthcheck.core.plugin import Result, Results, json_to_results
|
|
|
0f405b |
-from ipahealthcheck.core.output import output_registry
|
|
|
0f405b |
from ipahealthcheck.core import constants
|
|
|
0f405b |
-from ipahealthcheck.core.service import ServiceCheck
|
|
|
0f405b |
-
|
|
|
0f405b |
-logging.basicConfig(format='%(message)s')
|
|
|
0f405b |
-logger = logging.getLogger()
|
|
|
0f405b |
-
|
|
|
0f405b |
-
|
|
|
0f405b |
-def find_registries(entry_points):
|
|
|
0f405b |
- registries = {}
|
|
|
0f405b |
- for entry_point in entry_points:
|
|
|
0f405b |
- registries.update({
|
|
|
0f405b |
- ep.name: ep.resolve()
|
|
|
0f405b |
- for ep in pkg_resources.iter_entry_points(entry_point)
|
|
|
0f405b |
- })
|
|
|
0f405b |
- return registries
|
|
|
0f405b |
-
|
|
|
0f405b |
-
|
|
|
0f405b |
-def find_plugins(name, registry):
|
|
|
0f405b |
- for ep in pkg_resources.iter_entry_points(name):
|
|
|
0f405b |
- # load module
|
|
|
0f405b |
- ep.load()
|
|
|
0f405b |
- return registry.get_plugins()
|
|
|
0f405b |
-
|
|
|
0f405b |
-
|
|
|
0f405b |
-def run_plugin(plugin, available=()):
|
|
|
0f405b |
- # manually calculate duration when we create results of our own
|
|
|
0f405b |
- start = datetime.utcnow()
|
|
|
0f405b |
- try:
|
|
|
0f405b |
- for result in plugin.check():
|
|
|
0f405b |
- if result is None:
|
|
|
0f405b |
- # Treat no result as success, fudge start time
|
|
|
0f405b |
- result = Result(plugin, constants.SUCCESS, start=start)
|
|
|
0f405b |
- yield result
|
|
|
0f405b |
- except Exception as e:
|
|
|
0f405b |
- logger.debug('Exception raised: %s', e)
|
|
|
0f405b |
- yield Result(plugin, constants.CRITICAL, exception=str(e),
|
|
|
0f405b |
- start=start)
|
|
|
0f405b |
-
|
|
|
0f405b |
-
|
|
|
0f405b |
-def source_or_check_matches(plugin, source, check):
|
|
|
0f405b |
- """Determine whether a given a plugin matches if a source
|
|
|
0f405b |
- and optional check are provided.
|
|
|
0f405b |
- """
|
|
|
0f405b |
- if source is not None and plugin.__module__ != source:
|
|
|
0f405b |
- return False
|
|
|
0f405b |
-
|
|
|
0f405b |
- if check and plugin.__class__.__name__ != check:
|
|
|
0f405b |
- return False
|
|
|
0f405b |
-
|
|
|
0f405b |
- return True
|
|
|
0f405b |
-
|
|
|
0f405b |
-
|
|
|
0f405b |
-def run_service_plugins(plugins, config, source, check):
|
|
|
0f405b |
- """Execute plugins with the base class of ServiceCheck
|
|
|
0f405b |
-
|
|
|
0f405b |
- This is a specialized check to use systemd to determine
|
|
|
0f405b |
- if a service is running or not.
|
|
|
0f405b |
- """
|
|
|
0f405b |
- results = Results()
|
|
|
0f405b |
- available = []
|
|
|
0f405b |
-
|
|
|
0f405b |
- for plugin in plugins:
|
|
|
0f405b |
- if not isinstance(plugin, ServiceCheck):
|
|
|
0f405b |
- continue
|
|
|
0f405b |
-
|
|
|
0f405b |
- logger.debug('Calling check %s', plugin)
|
|
|
0f405b |
- for result in plugin.check():
|
|
|
0f405b |
- # always run the service checks so dependencies work
|
|
|
0f405b |
- if result is not None and result.result == constants.SUCCESS:
|
|
|
0f405b |
- available.append(plugin.service.service_name)
|
|
|
0f405b |
- if not source_or_check_matches(plugin, source, check):
|
|
|
0f405b |
- continue
|
|
|
0f405b |
- if result is not None:
|
|
|
0f405b |
- results.add(result)
|
|
|
0f405b |
-
|
|
|
0f405b |
- return results, set(available)
|
|
|
0f405b |
-
|
|
|
0f405b |
-
|
|
|
0f405b |
-def run_plugins(plugins, config, available, source, check):
|
|
|
0f405b |
- """Execute plugins without the base class of ServiceCheck
|
|
|
0f405b |
-
|
|
|
0f405b |
- These are the remaining, non-service checking checks
|
|
|
0f405b |
- that do validation for various parts of a system.
|
|
|
0f405b |
- """
|
|
|
0f405b |
- results = Results()
|
|
|
0f405b |
-
|
|
|
0f405b |
- for plugin in plugins:
|
|
|
0f405b |
- if isinstance(plugin, ServiceCheck):
|
|
|
0f405b |
- continue
|
|
|
0f405b |
-
|
|
|
0f405b |
- if not source_or_check_matches(plugin, source, check):
|
|
|
0f405b |
- continue
|
|
|
0f405b |
-
|
|
|
0f405b |
- logger.debug('Calling check %s' % plugin)
|
|
|
0f405b |
- plugin.config = config
|
|
|
0f405b |
- if not set(plugin.requires).issubset(available):
|
|
|
0f405b |
- logger.debug('Skipping %s:%s because %s service(s) not running',
|
|
|
0f405b |
- plugin.__class__.__module__,
|
|
|
0f405b |
- plugin.__class__.__name__,
|
|
|
0f405b |
- ', '.join(set(plugin.requires) - available))
|
|
|
0f405b |
- # Not providing a Result in this case because if a required
|
|
|
0f405b |
- # service isn't available then this could generate a lot of
|
|
|
0f405b |
- # false positives.
|
|
|
0f405b |
- else:
|
|
|
0f405b |
- for result in run_plugin(plugin, available):
|
|
|
0f405b |
- results.add(result)
|
|
|
0f405b |
-
|
|
|
0f405b |
- return results
|
|
|
0f405b |
-
|
|
|
0f405b |
-
|
|
|
0f405b |
-def list_sources(plugins):
|
|
|
0f405b |
- """Print list of all sources and checks"""
|
|
|
0f405b |
- source = None
|
|
|
0f405b |
- for plugin in plugins:
|
|
|
0f405b |
- if source != plugin.__class__.__module__:
|
|
|
0f405b |
- print(plugin.__class__.__module__)
|
|
|
0f405b |
- source = plugin.__class__.__module__
|
|
|
0f405b |
- print(" ", plugin.__class__.__name__)
|
|
|
0f405b |
-
|
|
|
0f405b |
- return 0
|
|
|
0f405b |
-
|
|
|
0f405b |
-
|
|
|
0f405b |
-def parse_options(output_registry):
|
|
|
0f405b |
- output_names = [plugin.__name__.lower() for
|
|
|
0f405b |
- plugin in output_registry.plugins]
|
|
|
0f405b |
- parser = argparse.ArgumentParser()
|
|
|
0f405b |
- parser.add_argument('--debug', dest='debug', action='store_true',
|
|
|
0f405b |
- default=False, help='Include debug output')
|
|
|
0f405b |
- parser.add_argument('--list-sources', dest='list_sources',
|
|
|
0f405b |
- action='store_true', default=False,
|
|
|
0f405b |
- help='List all available sources')
|
|
|
0f405b |
- parser.add_argument('--source', dest='source',
|
|
|
0f405b |
- default=None,
|
|
|
0f405b |
- help='Source of checks, e.g. ipahealthcheck.foo.bar')
|
|
|
0f405b |
- parser.add_argument('--check', dest='check',
|
|
|
0f405b |
- default=None,
|
|
|
0f405b |
- help='Check to execute, e.g. BazCheck')
|
|
|
0f405b |
- parser.add_argument('--output-type', dest='output', choices=output_names,
|
|
|
0f405b |
- default='json', help='Output method')
|
|
|
0f405b |
- parser.add_argument('--output-file', dest='outfile', default=None,
|
|
|
0f405b |
- help='File to store output')
|
|
|
0f405b |
- parser.add_argument('--input-file', dest='infile',
|
|
|
0f405b |
- help='File to read as input')
|
|
|
0f405b |
- parser.add_argument('--failures-only', dest='failures_only',
|
|
|
0f405b |
- action='store_true', default=False,
|
|
|
0f405b |
- help='Exclude SUCCESS results on output')
|
|
|
0f405b |
- parser.add_argument('--severity', dest='severity', action="append",
|
|
|
0f405b |
- help='Include only the selected severity(s)',
|
|
|
0f405b |
- choices=[key for key in constants._nameToLevel])
|
|
|
0f405b |
- for plugin in output_registry.plugins:
|
|
|
0f405b |
- onelinedoc = plugin.__doc__.split('\n\n', 1)[0].strip()
|
|
|
0f405b |
- group = parser.add_argument_group(plugin.__name__.lower(),
|
|
|
0f405b |
- onelinedoc)
|
|
|
0f405b |
- for option in plugin.options:
|
|
|
0f405b |
- group.add_argument(option[0], **option[1])
|
|
|
0f405b |
-
|
|
|
0f405b |
- options = parser.parse_args()
|
|
|
0f405b |
-
|
|
|
0f405b |
- # Validation
|
|
|
0f405b |
- if options.check and not options.source:
|
|
|
0f405b |
- print("--source is required when --check is used")
|
|
|
0f405b |
- sys.exit(1)
|
|
|
0f405b |
-
|
|
|
0f405b |
- return options
|
|
|
0f405b |
-
|
|
|
0f405b |
-
|
|
|
0f405b |
-def limit_results(results, source, check):
|
|
|
0f405b |
- """Return ony those results which match source and/or check"""
|
|
|
0f405b |
- new_results = Results()
|
|
|
0f405b |
- for result in results.results:
|
|
|
0f405b |
- if result.source == source:
|
|
|
0f405b |
- if check is None or result.check == check:
|
|
|
0f405b |
- new_results.add(result)
|
|
|
0f405b |
- return new_results
|
|
|
0f405b |
-
|
|
|
0f405b |
-
|
|
|
0f405b |
-def run_healthcheck(entry_points, configfile):
|
|
|
0f405b |
- framework = object()
|
|
|
0f405b |
- plugins = []
|
|
|
0f405b |
- output = constants.DEFAULT_OUTPUT
|
|
|
0f405b |
-
|
|
|
0f405b |
- logger.setLevel(logging.INFO)
|
|
|
0f405b |
-
|
|
|
0f405b |
- options = parse_options(output_registry)
|
|
|
0f405b |
-
|
|
|
0f405b |
- if options.debug:
|
|
|
0f405b |
- logger.setLevel(logging.DEBUG)
|
|
|
0f405b |
-
|
|
|
0f405b |
- config = read_config(configfile)
|
|
|
0f405b |
- if config is None:
|
|
|
0f405b |
- sys.exit(1)
|
|
|
0f405b |
-
|
|
|
0f405b |
- for name, registry in find_registries(entry_points).items():
|
|
|
0f405b |
- try:
|
|
|
0f405b |
- registry.initialize(framework)
|
|
|
0f405b |
- except Exception as e:
|
|
|
0f405b |
- print("Unable to initialize %s: %s" % (name, e))
|
|
|
0f405b |
- sys.exit(1)
|
|
|
0f405b |
- for plugin in find_plugins(name, registry):
|
|
|
0f405b |
- plugins.append(plugin)
|
|
|
0f405b |
-
|
|
|
0f405b |
- for out in output_registry.plugins:
|
|
|
0f405b |
- if out.__name__.lower() == options.output:
|
|
|
0f405b |
- output = out(options)
|
|
|
0f405b |
-
|
|
|
0f405b |
- if options.list_sources:
|
|
|
0f405b |
- return list_sources(plugins)
|
|
|
0f405b |
-
|
|
|
0f405b |
- if options.infile:
|
|
|
0f405b |
- try:
|
|
|
0f405b |
- with open(options.infile, 'r') as f:
|
|
|
0f405b |
- raw_data = f.read()
|
|
|
0f405b |
-
|
|
|
0f405b |
- json_data = json.loads(raw_data)
|
|
|
0f405b |
- results = json_to_results(json_data)
|
|
|
0f405b |
- available = ()
|
|
|
0f405b |
- except Exception as e:
|
|
|
0f405b |
- print("Unable to import '%s': %s" % (options.infile, e))
|
|
|
0f405b |
- sys.exit(1)
|
|
|
0f405b |
- if options.source:
|
|
|
0f405b |
- results = limit_results(results, options.source, options.check)
|
|
|
0f405b |
- else:
|
|
|
0f405b |
- results, available = run_service_plugins(plugins, config,
|
|
|
0f405b |
- options.source,
|
|
|
0f405b |
- options.check)
|
|
|
0f405b |
- results.extend(run_plugins(plugins, config, available,
|
|
|
0f405b |
- options.source, options.check))
|
|
|
0f405b |
-
|
|
|
0f405b |
- if options.source and len(results.results) == 0:
|
|
|
0f405b |
- for plugin in plugins:
|
|
|
0f405b |
- if not source_or_check_matches(plugin, options.source,
|
|
|
0f405b |
- options.check):
|
|
|
0f405b |
- continue
|
|
|
0f405b |
-
|
|
|
0f405b |
- if not set(plugin.requires).issubset(available):
|
|
|
0f405b |
- print("Source '%s' is missing one or more requirements '%s'" %
|
|
|
0f405b |
- (options.source, ', '.join(plugin.requires)))
|
|
|
0f405b |
- sys.exit(1)
|
|
|
0f405b |
-
|
|
|
0f405b |
- if options.check:
|
|
|
0f405b |
- print("Check '%s' not found in Source '%s'" %
|
|
|
0f405b |
- (options.check, options.source))
|
|
|
0f405b |
- else:
|
|
|
0f405b |
- print("Source '%s' not found" % options.source)
|
|
|
0f405b |
- sys.exit(1)
|
|
|
0f405b |
-
|
|
|
0f405b |
- try:
|
|
|
0f405b |
- output.render(results)
|
|
|
0f405b |
- except Exception as e:
|
|
|
0f405b |
- logger.error('Output raised %s: %s', e.__class__.__name__, e)
|
|
|
0f405b |
-
|
|
|
0f405b |
- return_value = 0
|
|
|
0f405b |
- for result in results.results:
|
|
|
0f405b |
- if result.result != constants.SUCCESS:
|
|
|
0f405b |
- return_value = 1
|
|
|
0f405b |
- break
|
|
|
0f405b |
-
|
|
|
0f405b |
- return return_value
|
|
|
0f405b |
+from ipahealthcheck.core.core import run_healthcheck
|
|
|
0f405b |
|
|
|
0f405b |
|
|
|
0f405b |
def main():
|
|
|
0f405b |
--
|
|
|
0f405b |
2.20.1
|
|
|
0f405b |
|