From 032e5a095c9460292d0363477bbfd91e4fa70a8d Mon Sep 17 00:00:00 2001 From: Kristian Krsnik Date: Wed, 4 Oct 2023 17:39:03 +0200 Subject: [PATCH] added new command line interface --- README.md | 87 ++++++++++++++--------------------------- nix/module.nix | 11 +++--- outage_detector/main.py | 65 ++++++++++++++---------------- 3 files changed, 64 insertions(+), 99 deletions(-) diff --git a/README.md b/README.md index 4f5a123..a04cedc 100644 --- a/README.md +++ b/README.md @@ -6,75 +6,46 @@ It is mainly used as a tool for statistical analysis of residential internet con ## Usage -You can set these options in a json file and pass it with the `--config` option. -Additional command line parameters override options defined in a config. +The program has two subcommands + +### log + +Get the current connection status to the default host (1.1.1.1) and timeout (2). ```txt -If no argument is passed then de defaults are assumed. - ---config Path to a config file in JSON format. - To set a command line argument, use it as a key and set its value. - Config is not used unless explicitly set. - ---log Path to a the file where results should be appended to. - Created the file if it does not already exist. - Default: ./connection.log - ---host IP or hostname of a server to query. - Default: 1.1.1.1 - ---timeout Set the timeout in seconds to use for the connection test - Default: 2 - ---outages Print the outages to stdout. - Set the lenght in minutes where a connection loss is condidered an outage - This option can only be used with --log. - Default: 3 - ---stdout Return the resulting logline in the terminal. - Default Behaviour: Do not print to stdout. - ---help Print this menu -``` - -### Config - -Save an example config options as a `.json` file with this format: - -```json -{ - "host": "1.1.1.1", - "timeout": null, // To use the default value of 2 - "log": "./connection.log", -} -``` - -### Example 1 - -```txt -$ outage_detector --config ./config.json --host 1.1.1.1 --log --stdout +$ outage_detector log > [YYYY-MM-DD HH:MM:SS][1.1.1.1][OK] ``` -Loads values from `./config.json`. -Overrides `host` with `example.com`. -Appends the result to `log.txt` and prints it to `stdout` +Or use a custom host and timeout with ```txt -$ outage_detector --config ./config.json --host example.com --log another.log --stdout +$ outage_detector log --host 1.0.0.1 --timeout 4 > [YYYY-MM-DD HH:MM:SS][1.1.1.1][FAIL] ``` -Loads values from `./config.json`. -Overrides `host` with `example.com`. -Appends the result to `another.log` and prints it to `stdout` - -### Example 2 +To append a log to `outages.log`. +Will create the file if it does not exist. +This will suppress output to stdout. ```txt -$ outage_detector --log ./outage-detector.log --outages 5 -> [YYYY-MM-DD HH:MM:SS][1.1.1.1] lasting for X Hours and Y Minutes -> [YYYY-MM-DD HH:MM:SS][1.1.1.1] lasting for X Hours and Y Minutes +$ outage_detector log outages.log --host 1.0.0.1 --timeout 4 +> [YYYY-MM-DD HH:MM:SS][1.1.1.1][FAIL] ``` -Print to `stdout` all outages from `./outage-detector.log` in chronological order that lasted at least 5 minutes. +### outages + +Print the major outages to stdout. + +```txt +$ outage_detector log outages.log +> [YYYY-MM-DD HH:MM:SS][1.1.1.1] lasting for 3 Minutes +``` + +You can also specify the time in minutes a connection loss should count as an outage. +Default is 3 Minutes. + +```txt +$ outage_detector log outages.log --time 6 +> +``` diff --git a/nix/module.nix b/nix/module.nix index c3ce995..9842678 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -8,9 +8,6 @@ inputs: { cfg = config.outage-detector; package = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.default; inherit (lib) mkIf mkEnableOption mkOption types; - - format = pkgs.formats.json {}; - configFile = format.generate "config.json" cfg.settings; in { options.outage-detector = { enable = mkEnableOption "outage-detector"; @@ -37,7 +34,11 @@ in { ]); in valueType; - default = throw "Please specify outage-detector.settings"; + default = { + filename = throw "Please specify outage-detector.settings."; + host = "1.1.1.1"; + timeout = 2; + }; }; }; @@ -46,7 +47,7 @@ in { systemd.services.outage-detector = mkIf (cfg.timer != null) { - script = "${package}/bin/outage_detector --config ${configFile}"; + script = "${package}/bin/outage_detector log ${cfg.settings.filename} --host ${cfg.settings.host} --timeout ${cfg.settings.timeout}"; serviceConfig = { Type = "oneshot"; diff --git a/outage_detector/main.py b/outage_detector/main.py index 27b0c45..6c5c6cb 100644 --- a/outage_detector/main.py +++ b/outage_detector/main.py @@ -1,5 +1,5 @@ import sys -import json +import argparse from datetime import datetime import requests @@ -14,10 +14,10 @@ class LogEntry: self.failed = 'FAIL' in log_entry -def printOutages(config: dict) -> None: +def printOutages(filepath: str, time: int) -> None: """Print outages from a log file""" - with open(config['log'], 'r') as file: + with open(filepath, 'r') as file: log = list(map(lambda x: LogEntry(x), file.readlines())) # cluster log entries by host @@ -29,7 +29,7 @@ def printOutages(config: dict) -> None: log_by_host[entry.host].append(entry) # cluster fails to outages - # we consider the time of subsequent failed connection attemts to the same host an outage + # we consider the time of subsequent failed connection attempts to the same host an outage outages: list = [] last_failed = False for entries in log_by_host.values(): @@ -49,7 +49,7 @@ def printOutages(config: dict) -> None: outage_duration = round( (outage[-1].date - outage[0].date).seconds / 60) # continue if outage is shorter than the minimum duration - if outage_duration < config['outages']: + if outage_duration < time: continue hours = outage_duration // 60 minutes = outage_duration - (hours * 60) @@ -69,74 +69,67 @@ def isOnline(host: str, timeout: int) -> bool: return False -def log(host: int, timeout: int, no_stdout: bool, log_path: None | str = None) -> None: +def log(host: str, timeout: int, filepath: None | str = None) -> None: """Log the connection status of a host""" - logline = f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}][{host}][{'OK' if isOnline(host, timeout) else 'FAIL'}]\n" + logline = f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}][{host}][{'OK' if isOnline(host, timeout) else 'FAIL'}]" - if log_path is not None: - with open(log_path, 'a') as file: - file.write(logline) - - if not no_stdout: + if filepath is not None: + with open(filepath, 'a') as file: + file.write(logline + '\n') + else: print(logline) -def parseArgs(args: list[str]): - import argparse +def parseArgs(args: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser( - prog='outage_detector', description='Log outages and print statistics.', formatter_class=argparse.ArgumentDefaultsHelpFormatter + prog='outage_detector', description='Log outages and print statistics.' ) - subparsers = parser.add_subparsers() + subparsers = parser.add_subparsers(dest='command') # Arguments for the log command parser_log = subparsers.add_parser( - 'log', help='Log the connection status.', formatter_class=argparse.ArgumentDefaultsHelpFormatter + 'log', help='Log the connection status.' ) parser_log.add_argument( - '--path', type=str, default=None, - help='Path to a the file where results should be appended to. Creates the file if it does not exist.' + 'filename', type=str, default=None, nargs='?', + help='Path to a the file where results should be appended to. Creates the file if it does not exist. If not specified, results are printed to stdout.' ) parser_log.add_argument( '--host', type=str, default='1.1.1.1', - help='IP or hostname of a server to query.' + help='IP or hostname of a server to query. (default: %(default)s)' ) parser_log.add_argument( '--timeout', type=int, default=2, - help='Set the timeout in seconds to use for the connection test.' - ) - parser_log.add_argument( - '--no-stdout', action='store_true', default=argparse.SUPPRESS, - help='A flag that disables printing to stdout.' + help='Set the timeout in seconds to use for the connection test. (default: %(default)s)' ) # Arguments for the outages command parser_outages = subparsers.add_parser( - 'outages', help='Print outages', formatter_class=argparse.ArgumentDefaultsHelpFormatter + 'outages', help='Print/log outages' ) parser_outages.add_argument( - '--log-path', type=str, default='./connection.log', + 'filename', type=str, nargs='?', help='Path to a file with connection logs.' ) parser_outages.add_argument( '--time', type=int, default=3, - help='Minimum duration for a connection loss to be considered an outage.' + help='Minimum duration for a connection loss to be considered an outage. (default: %(default)s)' ) - print(parser.parse_args(args)) + return parser.parse_args(args) def main(): - from sys import argv - parseArgs(argv[1:]) - # config, mode = loadConfig() - # if mode == 'log': - # log(config) - # elif mode == 'outages': - # printOutages(config) + args = parseArgs(sys.argv[1:]) + + if args.command == 'log': + log(args.host, args.timeout, args.filename) + elif args.command == 'outages': + printOutages(args.filename, args.time) if __name__ == '__main__':