diff --git a/hmdl.schema.json b/hmdl.schema.json index 8e6b49d..b6fde4e 100644 --- a/hmdl.schema.json +++ b/hmdl.schema.json @@ -7,17 +7,17 @@ "type": "object", "additionalProperties": false, "properties": { - "kind": { - "type": "string", - "enum": [ - "minutely", - "hourly", - "daily" - ] + "regular_interval": { + "description": "in seconds", + "type": "integer" + }, + "attentive_interval": { + "description": "in seconds", + "type": "integer" } }, "required": [ - "kind" + "regular_interval" ] }, "notifications": { @@ -171,6 +171,42 @@ }, { "anyOf": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "kind": { + "type": "string", + "const": "test" + }, + "parameters": { + "type": "object", + "additionalProperties": false, + "properties": { + "condition": { + "type": "string", + "enum": [ + "unknown", + "ok", + "warning", + "critical" + ], + "default": "warning" + }, + "output": { + "type": "string", + "default": "" + } + }, + "required": [ + ] + } + }, + "required": [ + "kind", + "parameters" + ] + }, { "type": "object", "additionalProperties": false, diff --git a/source/check_kinds/_interface.py b/source/check_kinds/_interface.py index c7b376b..00943d0 100644 --- a/source/check_kinds/_interface.py +++ b/source/check_kinds/_interface.py @@ -4,6 +4,6 @@ class interface_check_kind(object): raise NotImplementedError - def run(self, check_data): + def run(self, parameters): raise NotImplementedError diff --git a/source/check_kinds/http_request.py b/source/check_kinds/http_request.py index b674617..e78f734 100644 --- a/source/check_kinds/http_request.py +++ b/source/check_kinds/http_request.py @@ -22,22 +22,22 @@ class implementation_check_kind_http_request(interface_check_kind): ''' [implementation] ''' - def run(self, check_data): - if (check_data["parameters"]["request"]["method"] == "GET"): + def run(self, parameters): + if (parameters["request"]["method"] == "GET"): method_handled = True try: response = _requests.get( - check_data["parameters"]["request"]["target"] + parameters["request"]["target"] ) error = None except Exception as error_: error = error_ response = None - elif (check_data["parameters"]["request"]["method"] == "POST"): + elif (parameters["request"]["method"] == "POST"): method_handled = True try: response = _requests.post( - check_data["parameters"]["request"]["target"] + parameters["request"]["target"] ) error = None except Exception as error_: @@ -49,21 +49,21 @@ class implementation_check_kind_http_request(interface_check_kind): if (not method_handled): return { "condition": enum_condition.unknown, - "output": ("invalid HTTP request method: %s" % check_data["parameters"]["request"]["method"]) + "output": ("invalid HTTP request method: %s" % parameters["request"]["method"]) } else: if (response is None): return { "condition": ( enum_condition.warning - if check_data["parameters"]["as_warning"] else + if parameters["as_warning"] else enum_condition.critical ), "output": "HTTP request failed", } else: lines = [] - for (key, value, ) in check_data["parameters"]["response"].items(): + for (key, value, ) in parameters["response"].items(): if (key == "status_code"): if ((value is None) or (response.status_code == value)): pass @@ -112,7 +112,7 @@ class implementation_check_kind_http_request(interface_check_kind): if (len(lines) <= 0) else ( enum_condition.warning - if check_data["parameters"]["as_warning"] else + if parameters["as_warning"] else enum_condition.critical ) ), diff --git a/source/check_kinds/script.py b/source/check_kinds/script.py index 5df3e2e..7085048 100644 --- a/source/check_kinds/script.py +++ b/source/check_kinds/script.py @@ -14,9 +14,9 @@ class implementation_check_kind_script(interface_check_kind): ''' [implementation] ''' - def run(self, check_data): + def run(self, parameters): result = _subprocess.run( - [check_data["parameters"]["path"]] + check_data["parameters"]["arguments"], + [parameters["path"]] + parameters["arguments"], capture_output = True ) if (result.returncode == 0): diff --git a/source/check_kinds/test.py b/source/check_kinds/test.py new file mode 100644 index 0000000..c7a1946 --- /dev/null +++ b/source/check_kinds/test.py @@ -0,0 +1,24 @@ +class implementation_check_kind_test(interface_check_kind): + + ''' + [implementation] + ''' + def normalize_conf_node(self, node): + return dict_merge( + { + "condition": condition_encode(enum_condition.warning), + "output": "", + }, + node + ) + + + ''' + [implementation] + ''' + def run(self, parameters): + return { + "condition": condition_decode(parameters["condition"]), + "output": parameters["output"] + } + diff --git a/source/main.py b/source/main.py index 94e70d0..596807e 100644 --- a/source/main.py +++ b/source/main.py @@ -16,7 +16,7 @@ def state_decode(state_encoded): def conf_normalize_check(check_kind_implementations, defaults, name, node): if ("kind" not in node): - raise ValueError("missing mandatory 'member' field 'kind'") + raise ValueError("missing mandatory 'check' field 'kind'") else: if (node["kind"] not in check_kind_implementations): raise ValueError("unhandled kind: %s" % node["kind"]) @@ -45,8 +45,12 @@ def conf_normalize_defaults(node): return dict_merge( { "active": True, - "schedule": {"kind": "hourly"}, - "notifications": [], + "schedule": { + "regular_interval": (60 * 60), + "attentive_interval": (60 * 2), + }, + "notifications": [ + ], }, node ) @@ -93,6 +97,14 @@ def main(): metavar = "", help = "path to the state file, which contains information about the recent checks; default: file in temporary directory, unique for the conf-path input" ) + argumentparser.add_argument( + "-x", + "--erase-state", + action = "store_true", + default = False, + dest = "erase_state", + help = "whether the state shall be deleted on start; this will cause that all checks are executed" + ) argumentparser.add_argument( "-t", "--threshold", @@ -102,15 +114,6 @@ def main(): metavar = "", help = "how often a condition has to occur in order to be reported" ) - argumentparser.add_argument( - "-a", - "--awareness-interval", - type = int, - default = 120, - dest = "awareness_interval", - metavar = "", - help = "seconds to wait until starting the next run of a check, for which the condition has changed recently" - ) argumentparser.add_argument( "-k", "--keep-notifying", @@ -120,7 +123,7 @@ def main(): help = "whether notifications shall be kept sending after the threshold has been surpassed" ) argumentparser.add_argument( - "-x", + "-e", "--expose-full-conf", action = "store_true", default = False, @@ -146,6 +149,7 @@ def main(): ### load check kind implementations check_kind_implementations = { + "test": implementation_check_kind_test(), "script": implementation_check_kind_script(), "http_request": implementation_check_kind_http_request(), } @@ -163,7 +167,11 @@ def main(): _sys.exit(1) else: ### get state data - if (not _os.path.exists(state_path)): + if ( + (not _os.path.exists(state_path)) + or + args.erase_state + ): state_data = {} file_write(state_path, _json.dumps(state_data, indent = "\t")) else: @@ -184,29 +192,15 @@ def main(): due = ( (old_item_state is None) or + (old_item_state["condition"] != enum_condition.ok) + or ( (old_item_state["count"] is not None) and - ((timestamp - old_item_state["timestamp"]) >= args.awareness_interval) - ) - or - ( ( - (check_data["schedule"]["kind"] == "minutely") - and - ((timestamp - old_item_state["timestamp"]) >= (60)) - ) - or - ( - (check_data["schedule"]["kind"] == "hourly") - and - ((timestamp - old_item_state["timestamp"]) >= (60 * 60)) - ) - or - ( - (check_data["schedule"]["kind"] == "daily") - and - ((timestamp - old_item_state["timestamp"]) >= (60 * 60 * 24)) + ((timestamp - old_item_state["timestamp"]) >= check_data["schedule"]["regular_interval"]) + or + ((timestamp - old_item_state["timestamp"]) >= check_data["schedule"]["attentive_interval"]) ) ) ) @@ -223,7 +217,7 @@ def main(): ) ### execute check and set new state - result = check_kind_implementations[check_data["kind"]].run(check_data) + result = check_kind_implementations[check_data["kind"]].run(check_data["parameters"]) new_item_state = { "timestamp": timestamp, "condition": result["condition"], diff --git a/test.hmdl.json b/test.hmdl.json new file mode 100644 index 0000000..24a107a --- /dev/null +++ b/test.hmdl.json @@ -0,0 +1,22 @@ +{ + "defaults": { + }, + "checks": { + "test": { + "schedule": { + "regular_interval": 12, + "attentive_interval": 5 + }, + "notifications": [ + { + "kind": "console", + "parameters": { + } + } + ], + "kind": "test", + "parameters": { + } + } + } +} diff --git a/todo.md b/todo.md index 76e0678..246cfab 100644 --- a/todo.md +++ b/todo.md @@ -1,4 +1,7 @@ -- prevent parallel acces to state file? -- more resililient checks -- self check -- notification channel "Matrix" +- parallele Zugriffe auf die Zustands-Datei verhindern +- fehlertolerantere Implementierung +- Selbst-Test +- Matrix als Benachrichtigungs-Kanal +- JSON-Schema für Konfiguration von Programm erzeugen lassen +- Möglichkeit dauerhaft laufen zulassen (evtl. als systemd-Dienst) +- Versionierung diff --git a/tools/build b/tools/build index 7bc5afa..525ddb3 100755 --- a/tools/build +++ b/tools/build @@ -6,6 +6,7 @@ cat \ source/packages.py \ source/lib.py \ source/check_kinds/_interface.py \ + source/check_kinds/test.py \ source/check_kinds/script.py \ source/check_kinds/http_request.py \ source/notification_channels/_interface.py \