From 44bd9a6446f76a121c1ec4f4433d0c151094c8e9 Mon Sep 17 00:00:00 2001 From: Benjamin Renard Date: Mon, 10 Jul 2023 11:56:03 +0200 Subject: [PATCH] Telltale: add check_entrypoint() to easily implement Icinga checker script --- mylib/__init__.py | 23 ++++++ mylib/scripts/helpers.py | 6 +- mylib/scripts/telltale_check_test.py | 12 +++ mylib/scripts/telltale_test.py | 40 ++++++++++ mylib/telltale.py | 113 +++++++++++++++++++++++++++ setup.py | 2 + 6 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 mylib/scripts/telltale_check_test.py create mode 100644 mylib/scripts/telltale_test.py diff --git a/mylib/__init__.py b/mylib/__init__.py index 8284814..711c95a 100644 --- a/mylib/__init__.py +++ b/mylib/__init__.py @@ -62,3 +62,26 @@ def pretty_format_list(row, encoding="utf8", prefix=None): + pretty_format_value_in_list(values, encoding=encoding, prefix=prefix) ) return "\n".join(result) + + +def pretty_format_timedelta(timedelta): + """Format timedelta object""" + seconds = int(timedelta.total_seconds()) + if seconds < 1: + return "less than one second" + periods = [ + ("year", 60 * 60 * 24 * 365), + ("month", 60 * 60 * 24 * 30), + ("day", 60 * 60 * 24), + ("hour", 60 * 60), + ("minute", 60), + ("second", 1), + ] + + strings = [] + for period_name, period_seconds in periods: + if seconds >= period_seconds: + period_value, seconds = divmod(seconds, period_seconds) + strings.append(f'{period_value} {period_name}{"s" if period_value > 1 else ""}') + + return ", ".join(strings) diff --git a/mylib/scripts/helpers.py b/mylib/scripts/helpers.py index 3373a80..d7870f6 100644 --- a/mylib/scripts/helpers.py +++ b/mylib/scripts/helpers.py @@ -37,11 +37,13 @@ def get_default_opt_value(config, default_config, key): return default_config.get(key) -def get_opts_parser(desc=None, just_try=False, just_one=False, progress=False, config=None): +def get_opts_parser( + desc=None, just_try=False, just_one=False, progress=False, config=None, **kwargs +): """Retrieve options parser""" default_config = {"logfile": None} - parser = argparse.ArgumentParser(description=desc) + parser = argparse.ArgumentParser(description=desc, **kwargs) parser.add_argument( "-v", "--verbose", action="store_true", dest="verbose", help="Enable verbose mode" diff --git a/mylib/scripts/telltale_check_test.py b/mylib/scripts/telltale_check_test.py new file mode 100644 index 0000000..1d56da1 --- /dev/null +++ b/mylib/scripts/telltale_check_test.py @@ -0,0 +1,12 @@ +""" Test telltale file """ +import logging + +from mylib.scripts.telltale_test import default_filepath +from mylib.telltale import TelltaleFile + +log = logging.getLogger(__name__) + + +def main(argv=None): + """Script main""" + TelltaleFile.check_entrypoint(argv=argv, default_filepath=default_filepath) diff --git a/mylib/scripts/telltale_test.py b/mylib/scripts/telltale_test.py new file mode 100644 index 0000000..22f732c --- /dev/null +++ b/mylib/scripts/telltale_test.py @@ -0,0 +1,40 @@ +""" Test telltale file """ +import logging +import os.path +import sys +import tempfile + +from mylib.scripts.helpers import get_opts_parser, init_logging +from mylib.telltale import TelltaleFile + +log = logging.getLogger(__name__) + +default_filepath = os.path.join(tempfile.gettempdir(), f"{__name__}.last") + + +def main(argv=None): + """Script main""" + if argv is None: + argv = sys.argv[1:] + + # Options parser + parser = get_opts_parser() + options = parser.parse_args() + + parser.add_argument( + "-p", + "--telltale-file-path", + action="store", + type=str, + dest="telltale_file_path", + help=f"Telltale file path (default: {default_filepath})", + default=default_filepath, + ) + + options = parser.parse_args() + + # Initialize logs + init_logging(options, __doc__) + + telltale_file = TelltaleFile(filepath=options.telltale_file_path) + telltale_file.update() diff --git a/mylib/telltale.py b/mylib/telltale.py index 75e5c9c..70cf9b7 100644 --- a/mylib/telltale.py +++ b/mylib/telltale.py @@ -1,11 +1,19 @@ """ Telltale files helpers """ +import argparse import datetime import logging import os +import sys + +from mylib import pretty_format_timedelta +from mylib.scripts.helpers import get_opts_parser, init_logging log = logging.getLogger(__name__) +DEFAULT_WARNING_THRESHOLD = 90 +DEFAULT_CRITICAL_THRESHOLD = 240 + class TelltaleFile: """Telltale file helper class""" @@ -50,3 +58,108 @@ class TelltaleFile: return True except FileNotFoundError: return True + + @classmethod + def check_entrypoint( + cls, + argv=None, + description=None, + default_filepath=None, + default_warning_threshold=None, + default_critical_threshold=None, + fail_message=None, + success_message=None, + ): + """Entry point of the script to check a telltale file last update""" + argv = argv if argv else sys.argv + description = description if description else "Check last execution date" + parser = get_opts_parser(desc=description, exit_on_error=False) + + parser.add_argument( + "-p", + "--telltale-file-path", + action="store", + type=str, + dest="telltale_file_path", + help=f"Telltale file path (default: {default_filepath})", + default=default_filepath, + required=not default_filepath, + ) + + default_warning_threshold = ( + default_warning_threshold + if default_warning_threshold is not None + else DEFAULT_WARNING_THRESHOLD + ) + default_critical_threshold = ( + default_critical_threshold + if default_critical_threshold is not None + else DEFAULT_CRITICAL_THRESHOLD + ) + + parser.add_argument( + "-w", + "--warning", + type=int, + dest="warning", + help=( + "Specify warning threshold (in minutes, default: " + f"{default_warning_threshold} minutes)" + ), + default=default_warning_threshold, + ) + + parser.add_argument( + "-c", + "--critical", + type=int, + dest="critical", + help=( + "Specify critical threshold (in minutes, default: " + f"{default_critical_threshold} minutes)" + ), + default=default_critical_threshold, + ) + + try: + options = parser.parse_args(argv[1:]) + except argparse.ArgumentError as err: + print(f"UNKNOWN - {err}") + sys.exit(3) + + # Initialize logs + init_logging(options, argv[0]) + + telltale_file = cls(filepath=options.telltale_file_path) + last = telltale_file.last_update + if not last: + status = "UNKNOWN" + exit_code = 3 + msg = ( + fail_message + if fail_message + else "Fail to retrieve last successful date of execution" + ) + else: + delay = datetime.datetime.now() - last + msg = ( + success_message + if success_message + else "Last successful execution was {last_delay} ago ({last_date})" + ).format( + last_delay=pretty_format_timedelta(delay), + last_date=last.strftime("%Y/%m/%d %H:%M:%S"), + ) + + if delay >= datetime.timedelta(minutes=options.critical): + status = "CRITICAL" + exit_code = 2 + elif delay >= datetime.timedelta(minutes=options.warning): + status = "WARNING" + exit_code = 1 + else: + status = "OK" + exit_code = 0 + + print(f"{status} - {msg}") + sys.exit(exit_code) diff --git a/setup.py b/setup.py index a900f98..5ff5393 100644 --- a/setup.py +++ b/setup.py @@ -69,6 +69,8 @@ setup( "mylib-test-report = mylib.scripts.report_test:main", "mylib-test-ldap = mylib.scripts.ldap_test:main", "mylib-test-sftp = mylib.scripts.sftp_test:main", + "mylib-test-telltale = mylib.scripts.telltale_test:main", + "mylib-test-telltale-check = mylib.scripts.telltale_check_test:main", ], }, )