[mod] viele kleine Anpassungen

This commit is contained in:
Christian Fraß 2022-11-30 08:15:35 +01:00
parent be60379e35
commit f7e3357662
9 changed files with 138 additions and 58 deletions

View file

@ -7,17 +7,17 @@
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"kind": { "regular_interval": {
"type": "string", "description": "in seconds",
"enum": [ "type": "integer"
"minutely", },
"hourly", "attentive_interval": {
"daily" "description": "in seconds",
] "type": "integer"
} }
}, },
"required": [ "required": [
"kind" "regular_interval"
] ]
}, },
"notifications": { "notifications": {
@ -171,6 +171,42 @@
}, },
{ {
"anyOf": [ "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", "type": "object",
"additionalProperties": false, "additionalProperties": false,

View file

@ -4,6 +4,6 @@ class interface_check_kind(object):
raise NotImplementedError raise NotImplementedError
def run(self, check_data): def run(self, parameters):
raise NotImplementedError raise NotImplementedError

View file

@ -22,22 +22,22 @@ class implementation_check_kind_http_request(interface_check_kind):
''' '''
[implementation] [implementation]
''' '''
def run(self, check_data): def run(self, parameters):
if (check_data["parameters"]["request"]["method"] == "GET"): if (parameters["request"]["method"] == "GET"):
method_handled = True method_handled = True
try: try:
response = _requests.get( response = _requests.get(
check_data["parameters"]["request"]["target"] parameters["request"]["target"]
) )
error = None error = None
except Exception as error_: except Exception as error_:
error = error_ error = error_
response = None response = None
elif (check_data["parameters"]["request"]["method"] == "POST"): elif (parameters["request"]["method"] == "POST"):
method_handled = True method_handled = True
try: try:
response = _requests.post( response = _requests.post(
check_data["parameters"]["request"]["target"] parameters["request"]["target"]
) )
error = None error = None
except Exception as error_: except Exception as error_:
@ -49,21 +49,21 @@ class implementation_check_kind_http_request(interface_check_kind):
if (not method_handled): if (not method_handled):
return { return {
"condition": enum_condition.unknown, "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: else:
if (response is None): if (response is None):
return { return {
"condition": ( "condition": (
enum_condition.warning enum_condition.warning
if check_data["parameters"]["as_warning"] else if parameters["as_warning"] else
enum_condition.critical enum_condition.critical
), ),
"output": "HTTP request failed", "output": "HTTP request failed",
} }
else: else:
lines = [] lines = []
for (key, value, ) in check_data["parameters"]["response"].items(): for (key, value, ) in parameters["response"].items():
if (key == "status_code"): if (key == "status_code"):
if ((value is None) or (response.status_code == value)): if ((value is None) or (response.status_code == value)):
pass pass
@ -112,7 +112,7 @@ class implementation_check_kind_http_request(interface_check_kind):
if (len(lines) <= 0) else if (len(lines) <= 0) else
( (
enum_condition.warning enum_condition.warning
if check_data["parameters"]["as_warning"] else if parameters["as_warning"] else
enum_condition.critical enum_condition.critical
) )
), ),

View file

@ -14,9 +14,9 @@ class implementation_check_kind_script(interface_check_kind):
''' '''
[implementation] [implementation]
''' '''
def run(self, check_data): def run(self, parameters):
result = _subprocess.run( result = _subprocess.run(
[check_data["parameters"]["path"]] + check_data["parameters"]["arguments"], [parameters["path"]] + parameters["arguments"],
capture_output = True capture_output = True
) )
if (result.returncode == 0): if (result.returncode == 0):

View file

@ -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"]
}

View file

@ -16,7 +16,7 @@ def state_decode(state_encoded):
def conf_normalize_check(check_kind_implementations, defaults, name, node): def conf_normalize_check(check_kind_implementations, defaults, name, node):
if ("kind" not in node): if ("kind" not in node):
raise ValueError("missing mandatory 'member' field 'kind'") raise ValueError("missing mandatory 'check' field 'kind'")
else: else:
if (node["kind"] not in check_kind_implementations): if (node["kind"] not in check_kind_implementations):
raise ValueError("unhandled kind: %s" % node["kind"]) raise ValueError("unhandled kind: %s" % node["kind"])
@ -45,8 +45,12 @@ def conf_normalize_defaults(node):
return dict_merge( return dict_merge(
{ {
"active": True, "active": True,
"schedule": {"kind": "hourly"}, "schedule": {
"notifications": [], "regular_interval": (60 * 60),
"attentive_interval": (60 * 2),
},
"notifications": [
],
}, },
node node
) )
@ -93,6 +97,14 @@ def main():
metavar = "<state-path>", metavar = "<state-path>",
help = "path to the state file, which contains information about the recent checks; default: file in temporary directory, unique for the conf-path input" 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( argumentparser.add_argument(
"-t", "-t",
"--threshold", "--threshold",
@ -102,15 +114,6 @@ def main():
metavar = "<threshold>", metavar = "<threshold>",
help = "how often a condition has to occur in order to be reported" 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 = "<awareness-interval>",
help = "seconds to wait until starting the next run of a check, for which the condition has changed recently"
)
argumentparser.add_argument( argumentparser.add_argument(
"-k", "-k",
"--keep-notifying", "--keep-notifying",
@ -120,7 +123,7 @@ def main():
help = "whether notifications shall be kept sending after the threshold has been surpassed" help = "whether notifications shall be kept sending after the threshold has been surpassed"
) )
argumentparser.add_argument( argumentparser.add_argument(
"-x", "-e",
"--expose-full-conf", "--expose-full-conf",
action = "store_true", action = "store_true",
default = False, default = False,
@ -146,6 +149,7 @@ def main():
### load check kind implementations ### load check kind implementations
check_kind_implementations = { check_kind_implementations = {
"test": implementation_check_kind_test(),
"script": implementation_check_kind_script(), "script": implementation_check_kind_script(),
"http_request": implementation_check_kind_http_request(), "http_request": implementation_check_kind_http_request(),
} }
@ -163,7 +167,11 @@ def main():
_sys.exit(1) _sys.exit(1)
else: else:
### get state data ### get state data
if (not _os.path.exists(state_path)): if (
(not _os.path.exists(state_path))
or
args.erase_state
):
state_data = {} state_data = {}
file_write(state_path, _json.dumps(state_data, indent = "\t")) file_write(state_path, _json.dumps(state_data, indent = "\t"))
else: else:
@ -184,29 +192,15 @@ def main():
due = ( due = (
(old_item_state is None) (old_item_state is None)
or or
(old_item_state["condition"] != enum_condition.ok)
or
( (
(old_item_state["count"] is not None) (old_item_state["count"] is not None)
and and
((timestamp - old_item_state["timestamp"]) >= args.awareness_interval)
)
or
(
( (
(check_data["schedule"]["kind"] == "minutely") ((timestamp - old_item_state["timestamp"]) >= check_data["schedule"]["regular_interval"])
and or
((timestamp - old_item_state["timestamp"]) >= (60)) ((timestamp - old_item_state["timestamp"]) >= check_data["schedule"]["attentive_interval"])
)
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))
) )
) )
) )
@ -223,7 +217,7 @@ def main():
) )
### execute check and set new state ### 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 = { new_item_state = {
"timestamp": timestamp, "timestamp": timestamp,
"condition": result["condition"], "condition": result["condition"],

22
test.hmdl.json Normal file
View file

@ -0,0 +1,22 @@
{
"defaults": {
},
"checks": {
"test": {
"schedule": {
"regular_interval": 12,
"attentive_interval": 5
},
"notifications": [
{
"kind": "console",
"parameters": {
}
}
],
"kind": "test",
"parameters": {
}
}
}
}

11
todo.md
View file

@ -1,4 +1,7 @@
- prevent parallel acces to state file? - parallele Zugriffe auf die Zustands-Datei verhindern
- more resililient checks - fehlertolerantere Implementierung
- self check - Selbst-Test
- notification channel "Matrix" - Matrix als Benachrichtigungs-Kanal
- JSON-Schema für Konfiguration von Programm erzeugen lassen
- Möglichkeit dauerhaft laufen zulassen (evtl. als systemd-Dienst)
- Versionierung

View file

@ -6,6 +6,7 @@ cat \
source/packages.py \ source/packages.py \
source/lib.py \ source/lib.py \
source/check_kinds/_interface.py \ source/check_kinds/_interface.py \
source/check_kinds/test.py \
source/check_kinds/script.py \ source/check_kinds/script.py \
source/check_kinds/http_request.py \ source/check_kinds/http_request.py \
source/notification_channels/_interface.py \ source/notification_channels/_interface.py \