diff --git a/doc/hmdl.schema.json b/doc/hmdl.schema.json index 32c4f6c..2804d2b 100644 --- a/doc/hmdl.schema.json +++ b/doc/hmdl.schema.json @@ -605,6 +605,69 @@ "kind", "parameters" ] + }, + { + "title": "check 'generic_remote'", + "type": "object", + "unevaluatedProperties": false, + "properties": { + "kind": { + "type": "string", + "enum": [ + "generic_remote" + ] + }, + "parameters": { + "type": "object", + "additionalProperties": false, + "properties": { + "ssh_host": { + "type": "string" + }, + "ssh_port": { + "type": [ + "null", + "integer" + ], + "default": null + }, + "ssh_user": { + "type": [ + "null", + "string" + ], + "default": null + }, + "ssh_key": { + "type": [ + "null", + "string" + ], + "default": null + }, + "mount_point": { + "type": "string", + "default": "/" + }, + "threshold": { + "type": "integer", + "default": 95, + "description": "maximaler F\u00fcllstand in Prozent" + }, + "strict": { + "type": "boolean", + "default": false + } + }, + "required": [ + "ssh_host" + ] + } + }, + "required": [ + "kind", + "parameters" + ] } ] } diff --git a/examples/generic_remote.hmdl.json b/examples/generic_remote.hmdl.json new file mode 100644 index 0000000..9f21d03 --- /dev/null +++ b/examples/generic_remote.hmdl.json @@ -0,0 +1,14 @@ +{ + "checks": [ + { + "name": "test", + "kind": "generic_remote", + "parameters": { + "host": "bragi.pool.greenscale.de", + "ssh_user": "fenris", + "ssh_port": 8192, + "ssh_key": "/home/fenris/.ssh/keypairs/gs-bragi" + } + } + ] +} diff --git a/examples/main.hmdl.json b/examples/main.hmdl.json index 798bd49..0ea0fb2 100644 --- a/examples/main.hmdl.json +++ b/examples/main.hmdl.json @@ -15,6 +15,7 @@ }, "includes": [ "script.hmdl.json", - "file_state.hmdl.json" + "file_state.hmdl.json", + "generic_remote.hmdl.json" ] } diff --git a/source/localization/de.json b/source/localization/de.json index 3dd8db2..ff9280c 100644 --- a/source/localization/de.json +++ b/source/localization/de.json @@ -21,5 +21,6 @@ "checks.http_request.header_value_mismatch": "Header-Wert für Schlüssel '{{key}}' '{{value_actual}}' stimmt nicht mit erwartetem Wert {{value_expected}} überein", "checks.http_request.body_misses_part": "Rumpf enthält nicht den erwarteten Teil '{{part}}'", "misc.state_file_path": "Pfad zur Zustands-Datei", - "misc.check_procedure_failed": "Prüfungs-Prozedur fehlgeschlagen" + "misc.check_procedure_failed": "Prüfungs-Prozedur fehlgeschlagen", + "checks.generic_remote.overflow": "Festplatte fast voll" } diff --git a/source/localization/en.json b/source/localization/en.json index 676273d..798c20d 100644 --- a/source/localization/en.json +++ b/source/localization/en.json @@ -21,5 +21,6 @@ "checks.http_request.header_value_mismatch": "actual header value for key '{{key}}' '{{value_actual}}' and does not match the expected value {{value_expected}}", "checks.http_request.body_misses_part": "body does not contain the expected part '{{part}}'", "misc.state_file_path": "state file path", - "misc.check_procedure_failed": "check procedure failed" + "misc.check_procedure_failed": "check procedure failed", + "checks.generic_remote.overflow": "harddisk full" } diff --git a/source/logic/checks/generic_remote.py b/source/logic/checks/generic_remote.py new file mode 100644 index 0000000..22a022f --- /dev/null +++ b/source/logic/checks/generic_remote.py @@ -0,0 +1,137 @@ +class implementation_check_kind_generic_remote(interface_check_kind): + + ''' + [implementation] + ''' + def parameters_schema(self): + return { + "type": "object", + "additionalProperties": False, + "properties": { + "host" : { + "type" : "string" + }, + "ssh_port": { + "type": ["null", "integer"], + "default": None + }, + "ssh_user" : { + "type" : ["null", "string"], + "default": None, + }, + "ssh_key" : { + "type" : ["null", "string"], + "default": None, + }, + "mount_point" : { + "type" : "string", + "default" : "/" + }, + "threshold" : { + "type" : "integer", + "default" : 95, + "description" : "maximaler Füllstand in Prozent" + }, + "strict" : { + "type" : "boolean", + "default" : False + } + }, + "required": [ + "host" + ] + } + + + ''' + [implementation] + ''' + def normalize_conf_node(self, node): + if (not "host" in node): + raise ValueError("mandatory parameter \"host\" missing") + else: + return dict_merge( + { + "ssh_port": None, + "ssh_user": None, + "ssh_key": None, + "mount_point": "/", + "threshold": 95, + "strict": False, + }, + node + ) + + + ''' + [implementation] + ''' + def run(self, parameters): + inner_command = string_coin( + "df {{mount_point}} | tr -s \" \"", + { + "mount_point": parameters["mount_point"], + } + ) + + outer_command_parts = [] + if True: + outer_command_parts.append("ssh"); + if True: + outer_command_parts.append(string_coin("{{host}}", {"host": parameters["host"]})); + if (parameters["ssh_port"] is not None): + outer_command_parts.append(string_coin("-p {{port}}", {"port": ("%u" % parameters["ssh_port"])})); + if (parameters["ssh_user"] is not None): + outer_command_parts.append(string_coin("-l {{user}}", {"user": parameters["ssh_user"]})); + if (parameters["ssh_key"] is not None): + outer_command_parts.append(string_coin("-i {{key}}", {"key": parameters["ssh_key"]})); + if True: + outer_command_parts.append(string_coin("-o BatchMode=yes", {})) + if True: + outer_command_parts.append(string_coin("'{{inner_command}}'", {"inner_command": inner_command})) + outer_command = " ".join(outer_command_parts) + + result = shell_command(outer_command) + + if (result["return_code"] > 0): + return { + "condition": enum_condition.unknown, + "info": { + "error": result["stderr"], + } + } + else: + stuff = result["stdout"].split("\n")[-2].split(" ") + data = { + "device": stuff[0], + "used": int(stuff[2]), + "avail": int(stuff[3]), + "perc": int(stuff[4][:-1]), + } + if (data["perc"] > parameters["threshold"]): + return { + "condition": ( + enum_condition.critical + if parameters["strict"] else + enum_condition.warning + ), + "info": { + "data": { + "host": parameters["host"], + "device": data["device"], + "mount_point": parameters["mount_point"], + "used": format_bytes(data["used"]), + "available": format_bytes(data["avail"]), + "percentage": (str(data["perc"]) + "%"), + }, + "faults": [ + translation_get("checks.generic_remote.overflow") + ], + } + } + else: + return { + "condition": enum_condition.ok, + "info": {} + } + diff --git a/source/logic/main.py b/source/logic/main.py index 78841ca..705447f 100644 --- a/source/logic/main.py +++ b/source/logic/main.py @@ -107,6 +107,7 @@ def main(): "script": implementation_check_kind_script(), "file_state": implementation_check_kind_file_state(), "http_request": implementation_check_kind_http_request(), + "generic_remote" : implementation_check_kind_generic_remote(), } ### load notification channel implementations diff --git a/tools/build b/tools/build index 9cc49d7..621c89f 100755 --- a/tools/build +++ b/tools/build @@ -47,6 +47,7 @@ def main(): _os.path.join(dir_source, "logic", "checks", "script.py"), _os.path.join(dir_source, "logic", "checks", "file_state.py"), _os.path.join(dir_source, "logic", "checks", "http_request.py"), + _os.path.join(dir_source, "logic", "checks", "generic_remote.py"), _os.path.join(dir_source, "logic", "channels", "_interface.py"), _os.path.join(dir_source, "logic", "channels", "console.py"), _os.path.join(dir_source, "logic", "channels", "email.py"), @@ -66,6 +67,7 @@ def main(): _os.path.join(dir_source, "logic", "checks", "script.py"), _os.path.join(dir_source, "logic", "checks", "file_state.py"), _os.path.join(dir_source, "logic", "checks", "http_request.py"), + _os.path.join(dir_source, "logic", "checks", "generic_remote.py"), _os.path.join(dir_source, "logic", "channels", "_interface.py"), _os.path.join(dir_source, "logic", "channels", "console.py"), _os.path.join(dir_source, "logic", "channels", "email.py"),