core/source/logic/conf.py

477 lines
10 KiB
Python
Raw Normal View History

2023-03-04 14:08:15 +01:00
def conf_schema_active(
):
return {
2022-12-05 08:16:41 +01:00
"description": "whether the check shall be executed",
"type": "boolean",
2022-12-05 08:16:41 +01:00
"default": True,
}
2023-03-04 14:08:15 +01:00
def conf_schema_threshold(
):
return {
"description": "how often a condition has to occur in order to be reported",
"type": "integer",
"minimum": 1,
2022-12-05 08:16:41 +01:00
"default": 3,
}
2023-03-04 14:08:15 +01:00
def conf_schema_annoy(
):
return {
2023-03-05 15:52:28 +01:00
"description": "whether notifications about non-ok states shall be kept sending after the threshold has been surpassed",
"type": "boolean",
2022-12-05 08:16:41 +01:00
"default": False,
}
2023-03-04 14:08:15 +01:00
def conf_schema_interval(
allow_null,
2023-03-04 14:08:15 +01:00
default
):
return {
"anyOf": [
{
2022-12-05 08:16:41 +01:00
"description": "in seconds",
"type": ("integer" if allow_null else ["null", "integer"]),
2022-12-05 08:16:41 +01:00
"exclusiveMinimum": 0,
},
{
2022-12-05 08:16:41 +01:00
"description": "as text",
"type": "string",
"enum": [
"minute",
"hour",
"day",
"week",
]
},
],
"default": default,
}
2023-03-04 14:08:15 +01:00
def conf_schema_schedule(
):
return {
"type": "object",
"additionalProperties": False,
"properties": {
"regular_interval": conf_schema_interval(False, (60 * 60)),
"attentive_interval": conf_schema_interval(False, (60 * 2)),
"reminding_interval": conf_schema_interval(True, (60 * 60 * 24)),
},
"required": [
2022-12-05 08:16:41 +01:00
],
}
2023-03-04 14:08:15 +01:00
def conf_schema_notifications(
notification_channel_implementations
):
return {
"type": "array",
"items": {
"anyOf": list(
map(
lambda pair: {
2022-12-15 15:48:52 +01:00
"title": ("notification channel '%s'" % pair[0]),
"type": "object",
"unevaluatedProperties": False,
"properties": {
"kind": {
"type": "string",
"enum": [pair[0]]
},
"parameters": pair[1].parameters_schema(),
},
"required": [
"kind",
"parameters"
2022-12-05 08:16:41 +01:00
],
},
notification_channel_implementations.items()
)
)
},
"default": [
{
"kind": "console",
"parameters": {
}
2022-12-05 08:16:41 +01:00
},
],
}
2023-03-04 14:08:15 +01:00
def conf_schema_root(
check_kind_implementations,
notification_channel_implementations
):
return {
"type": "object",
"additionalProperties": False,
"properties": {
"defaults": {
"description": "default values for checks",
"type": "object",
"additionalProperties": False,
"properties": {
"active": conf_schema_active(),
"threshold": conf_schema_threshold(),
"annoy": conf_schema_annoy(),
"schedule": conf_schema_schedule(),
"notifications": conf_schema_notifications(notification_channel_implementations),
},
"required": [
2022-12-05 08:16:41 +01:00
],
},
2023-03-03 18:50:21 +01:00
"includes": {
"type": "array",
"items": {
"type": "string"
},
"default": [],
"description": "list of relative or absolute paths to other hmdl files on the local machine, which shall be subsumed in the overall monitoring task"
},
"checks": {
"type": "array",
"items": {
"allOf": [
{
"description": "should represent a specific check",
"type": "object",
"unevaluatedProperties": False,
"properties": {
"name": {
"type": "string"
},
"title": {
"type": "string"
},
"active": conf_schema_active(),
"threshold": conf_schema_threshold(),
"annoy": conf_schema_annoy(),
"schedule": conf_schema_schedule(),
"notifications": conf_schema_notifications(notification_channel_implementations),
},
"required": [
"name",
2022-12-05 08:16:41 +01:00
],
},
{
"anyOf": list(
map(
lambda pair: {
2023-02-06 16:06:18 +01:00
"title": ("check '%s'" % pair[0]),
"type": "object",
"unevaluatedProperties": False,
"properties": {
"kind": {
"type": "string",
"enum": [pair[0]]
},
"parameters": pair[1].parameters_schema(),
"custom": {
"description": "custom data, which shall be attached to notifications",
"default": None,
},
},
"required": [
"kind",
2022-12-05 08:16:41 +01:00
"parameters",
]
},
check_kind_implementations.items()
)
),
},
]
}
}
},
"required": [
]
}
2023-03-04 14:08:15 +01:00
def conf_normalize_interval(
interval_raw
):
if (interval_raw is None):
return None
else:
if (type(interval_raw) == int):
return interval_raw
elif (type(interval_raw) == str):
map_ = {
"minute": (60),
"hour": (60 * 60),
"day": (60 * 60 * 24),
"week": (60 * 60 * 24 * 7),
}
if (interval_raw not in map_):
raise ValueError("invalid string interval value: %s" % interval_raw)
else:
return map_[interval_raw]
else:
raise ValueError("invalid type for interval value")
2023-03-04 14:08:15 +01:00
def conf_normalize_schedule(
node
):
node_ = dict_merge(
{
"regular_interval": (60 * 60),
"attentive_interval": (60 * 2),
"reminding_interval": (60 * 60 * 24),
},
node
)
return {
2023-03-22 14:29:06 +01:00
"regular_interval": conf_normalize_interval(node_["regular_interval"]),
"attentive_interval": conf_normalize_interval(node_["attentive_interval"]),
"reminding_interval": conf_normalize_interval(node_["reminding_interval"]),
}
2023-03-04 14:08:15 +01:00
def conf_normalize_notification(
notification_channel_implementations,
node
):
if (node["kind"] not in notification_channel_implementations):
raise ValueError("invalid notification kind: %s" % notification["kind"])
else:
return {
"kind": node["kind"],
"parameters": notification_channel_implementations[node["kind"]].normalize_conf_node(node["parameters"]),
}
2023-03-04 14:08:15 +01:00
def conf_normalize_defaults(
notification_channel_implementations,
node
):
node_ = dict_merge(
{
"active": True,
"threshold": 3,
"annoy": False,
"schedule": {
"regular_interval": (60 * 60),
"attentive_interval": (60 * 2),
"reminding_interval": (60 * 60 * 24),
},
"notifications": [
],
},
node
)
return {
"active": node_["active"],
"threshold": node_["threshold"],
"annoy": node_["annoy"],
"schedule": node_["schedule"],
"notifications": list(
map(
lambda x: conf_normalize_notification(notification_channel_implementations, x),
node_["notifications"]
)
),
}
2023-03-04 14:08:15 +01:00
def conf_normalize_check(
check_kind_implementations,
notification_channel_implementations,
defaults,
node
):
if ("name" not in node):
raise ValueError("missing mandatory field in 'check' node: 'name'")
else:
if ("kind" not in node):
raise ValueError("missing mandatory field in 'check' node: 'kind'")
else:
if (node["kind"] not in check_kind_implementations):
raise ValueError("invalid check kind: '%s'" % node["kind"])
else:
node_ = dict_merge(
2023-03-04 14:08:15 +01:00
dict_merge(
defaults,
{
"title": node["name"],
"parameters": {},
"custom": None,
2023-03-04 14:08:15 +01:00
},
),
node
)
2023-03-04 14:08:15 +01:00
node__ = {}
if True:
node__["name"] = node_["name"]
if True:
node__["title"] = node_["title"]
if ("active" in node_):
node__["active"] = node_["active"]
if ("threshold" in node_):
node__["threshold"] = node_["threshold"]
if ("annoy" in node_):
node__["annoy"] = node_["annoy"]
if ("schedule" in node_):
node__["schedule"] = conf_normalize_schedule(node_["schedule"])
2023-03-04 15:20:35 +01:00
if ("notifications" in node_):
node__["notifications"] = list(
2023-03-04 14:08:15 +01:00
map(
lambda x: conf_normalize_notification(notification_channel_implementations, x),
node_["notifications"]
)
)
if ("kind" in node_):
node__["kind"] = node_["kind"]
if True:
node__["parameters"] = check_kind_implementations[node_["kind"]].normalize_conf_node(node_["parameters"])
if ("custom" in node_):
node__["custom"] = node_["custom"]
2023-03-04 14:08:15 +01:00
return node__
2023-03-03 18:50:21 +01:00
def conf_normalize_root(
check_kind_implementations,
notification_channel_implementations,
2023-03-04 14:08:15 +01:00
node,
options = None
2023-03-03 18:50:21 +01:00
):
2023-03-04 14:08:15 +01:00
options = dict_merge(
{
"use_implicit_default_values": True,
},
({} if (options is None) else options)
)
counts = {}
2023-03-03 18:50:21 +01:00
checks_raw = (
node["checks"]
if ("checks" in node) else
[]
)
for node_ in checks_raw:
if (node_["name"] not in counts):
counts[node_["name"]] = 0
counts[node_["name"]] += 1
fails = list(filter(lambda pair: (pair[1] > 1), counts.items()))
if (len(fails) > 0):
raise ValueError(
string_coin(
"ambiguous check names: {{names}}",
{
"names": ",".join(map(lambda pair: pair[0], fails)),
}
)
)
else:
2023-03-04 14:08:15 +01:00
defaults_raw = (
node["defaults"]
if ("defaults" in node) else
{}
)
defaults = (
conf_normalize_defaults(
notification_channel_implementations,
defaults_raw
2023-03-03 18:50:21 +01:00
)
2023-03-04 14:08:15 +01:00
if options["use_implicit_default_values"] else
defaults_raw
2023-03-03 18:50:21 +01:00
)
includes = (
node["includes"]
if ("includes" in node) else
[]
)
return {
"defaults": defaults,
2023-03-03 18:50:21 +01:00
"includes": includes,
"checks": list(
map(
lambda node_: conf_normalize_check(
check_kind_implementations,
notification_channel_implementations,
defaults,
node_
),
2023-03-03 18:50:21 +01:00
checks_raw
)
)
}
2023-03-03 18:50:21 +01:00
def conf_load(
check_kind_implementations,
notification_channel_implementations,
path,
2023-03-04 14:08:15 +01:00
options = None
2023-03-03 18:50:21 +01:00
):
2023-03-04 14:08:15 +01:00
options = dict_merge(
{
"root": True,
"already_included": set([]),
},
({} if (options is None) else options)
)
if (path in options["already_included"]):
2023-03-03 18:50:21 +01:00
raise ValueError("circular dependency detected")
else:
conf_raw = _json.loads(file_read(path))
includes = (
conf_raw["includes"]
if ("includes" in conf_raw) else
[]
)
for index in range(len(includes)):
path_ = includes[index]
sub_conf = conf_load(
check_kind_implementations,
notification_channel_implementations,
(
path_
if _os.path.isabs(path_) else
_os.path.join(_os.path.dirname(path), path_)
),
2023-03-04 14:08:15 +01:00
{
"root": False,
"already_included": (options["already_included"] | {path})
}
2023-03-03 18:50:21 +01:00
)
if (not "checks" in conf_raw):
conf_raw["checks"] = []
conf_raw["checks"].extend(
list(
map(
lambda check: dict_merge(
check,
{
"name": string_coin(
2023-03-04 15:29:53 +01:00
"{{prefix}}.{{original_name}}",
2023-03-03 18:50:21 +01:00
{
2023-03-04 15:29:53 +01:00
"prefix": _os.path.basename(path_).split(".")[0],
2023-03-03 18:50:21 +01:00
"original_name": check["name"],
}
),
}
),
sub_conf["checks"]
)
)
)
conf_raw["includes"] = []
return conf_normalize_root(
check_kind_implementations,
notification_channel_implementations,
2023-03-04 14:08:15 +01:00
conf_raw,
{
"use_implicit_default_values": options["root"],
}
2023-03-03 18:50:21 +01:00
)