[mod] JSON-schema für conf von Programm erzeugen lassen

This commit is contained in:
Christian Fraß 2022-11-30 23:03:24 +01:00
parent 453171c7f0
commit 09f043ef8d
14 changed files with 1318 additions and 642 deletions

665
doc/hmdl.schema.json Normal file
View file

@ -0,0 +1,665 @@
{
"type": "object",
"additionalProperties": false,
"properties": {
"defaults": {
"description": "default values for checks",
"type": "object",
"additionalProperties": false,
"properties": {
"active": {
"type": "boolean",
"default": true
},
"threshold": {
"description": "how often a condition has to occur in order to be reported",
"type": "integer",
"minimum": 1,
"default": 3
},
"annoy": {
"description": "whether notifications shall be kept sending after the threshold has been surpassed",
"type": "boolean",
"default": false
},
"schedule": {
"type": "object",
"additionalProperties": false,
"properties": {
"regular_interval": {
"description": "in seconds or as text",
"anyOf": [
{
"type": "integer",
"exclusiveMinimum": 0
},
{
"type": "string",
"enum": [
"minute",
"hour",
"day",
"week"
]
}
],
"default": 3600
},
"attentive_interval": {
"description": "in seconds or as text",
"anyOf": [
{
"type": "integer",
"exclusiveMinimum": 0
},
{
"type": "string",
"enum": [
"minute",
"hour",
"day",
"week"
]
}
],
"default": 120
}
},
"required": []
},
"notifications": {
"type": "array",
"items": {
"anyOf": [
{
"title": "check kind 'console'",
"type": "object",
"unevaluatedProperties": false,
"properties": {
"kind": {
"type": "string",
"enum": [
"console"
]
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {},
"required": []
}
},
"required": [
"kind",
"parameters"
]
},
{
"title": "check kind 'file_touch'",
"type": "object",
"unevaluatedProperties": false,
"properties": {
"kind": {
"type": "string",
"enum": [
"file_touch"
]
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"path": {
"type": "string"
}
},
"required": [
"path"
]
}
},
"required": [
"kind",
"parameters"
]
},
{
"title": "check kind 'email'",
"type": "object",
"unevaluatedProperties": false,
"properties": {
"kind": {
"type": "string",
"enum": [
"email"
]
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"access": {
"type": "object",
"additionalProperties": false,
"properties": {
"host": {
"type": "string"
},
"port": {
"type": "integer"
},
"username": {
"type": "string"
},
"password": {
"type": "string"
}
},
"required": [
"host",
"port",
"username",
"password"
]
},
"sender": {
"type": "string"
},
"receivers": {
"type": "array",
"items": {
"type": "string"
}
},
"tags": {
"description": "list of strings, which will be placed in the e-mail subject",
"type": "array",
"items": {
"type": "string"
},
"default": []
}
},
"required": [
"access",
"sender",
"receivers"
]
}
},
"required": [
"kind",
"parameters"
]
},
{
"title": "check kind 'libnotify'",
"type": "object",
"unevaluatedProperties": false,
"properties": {
"kind": {
"type": "string",
"enum": [
"libnotify"
]
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"icon": {
"type": "string"
}
},
"required": []
}
},
"required": [
"kind",
"parameters"
]
}
]
},
"default": [
{
"kind": "console",
"parameters": {}
}
]
}
},
"required": []
},
"checks": {
"type": "array",
"items": {
"allOf": [
{
"description": "should represent a specific check",
"type": "object",
"unevaluatedProperties": false,
"properties": {
"name": {
"type": "string"
},
"title": {
"type": "string"
},
"active": {
"type": "boolean",
"default": true
},
"threshold": {
"description": "how often a condition has to occur in order to be reported",
"type": "integer",
"minimum": 1,
"default": 3
},
"annoy": {
"description": "whether notifications shall be kept sending after the threshold has been surpassed",
"type": "boolean",
"default": false
},
"schedule": {
"type": "object",
"additionalProperties": false,
"properties": {
"regular_interval": {
"description": "in seconds or as text",
"anyOf": [
{
"type": "integer",
"exclusiveMinimum": 0
},
{
"type": "string",
"enum": [
"minute",
"hour",
"day",
"week"
]
}
],
"default": 3600
},
"attentive_interval": {
"description": "in seconds or as text",
"anyOf": [
{
"type": "integer",
"exclusiveMinimum": 0
},
{
"type": "string",
"enum": [
"minute",
"hour",
"day",
"week"
]
}
],
"default": 120
}
},
"required": []
},
"notifications": {
"type": "array",
"items": {
"anyOf": [
{
"title": "check kind 'console'",
"type": "object",
"unevaluatedProperties": false,
"properties": {
"kind": {
"type": "string",
"enum": [
"console"
]
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {},
"required": []
}
},
"required": [
"kind",
"parameters"
]
},
{
"title": "check kind 'file_touch'",
"type": "object",
"unevaluatedProperties": false,
"properties": {
"kind": {
"type": "string",
"enum": [
"file_touch"
]
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"path": {
"type": "string"
}
},
"required": [
"path"
]
}
},
"required": [
"kind",
"parameters"
]
},
{
"title": "check kind 'email'",
"type": "object",
"unevaluatedProperties": false,
"properties": {
"kind": {
"type": "string",
"enum": [
"email"
]
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"access": {
"type": "object",
"additionalProperties": false,
"properties": {
"host": {
"type": "string"
},
"port": {
"type": "integer"
},
"username": {
"type": "string"
},
"password": {
"type": "string"
}
},
"required": [
"host",
"port",
"username",
"password"
]
},
"sender": {
"type": "string"
},
"receivers": {
"type": "array",
"items": {
"type": "string"
}
},
"tags": {
"description": "list of strings, which will be placed in the e-mail subject",
"type": "array",
"items": {
"type": "string"
},
"default": []
}
},
"required": [
"access",
"sender",
"receivers"
]
}
},
"required": [
"kind",
"parameters"
]
},
{
"title": "check kind 'libnotify'",
"type": "object",
"unevaluatedProperties": false,
"properties": {
"kind": {
"type": "string",
"enum": [
"libnotify"
]
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"icon": {
"type": "string"
}
},
"required": []
}
},
"required": [
"kind",
"parameters"
]
}
]
},
"default": [
{
"kind": "console",
"parameters": {}
}
]
}
},
"required": [
"name"
]
},
{
"anyOf": [
{
"title": "notification channel 'script'",
"type": "object",
"unevaluatedProperties": false,
"properties": {
"kind": {
"type": "string",
"enum": [
"script"
]
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"path": {
"type": "string"
},
"arguments": {
"type": "array",
"item": {
"type": "string"
}
}
},
"required": [
"path"
]
}
},
"required": [
"kind",
"parameters"
]
},
{
"title": "notification channel 'file_timestamp'",
"type": "object",
"unevaluatedProperties": false,
"properties": {
"kind": {
"type": "string",
"enum": [
"file_timestamp"
]
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"path": {
"type": "string"
},
"warning_age": {
"type": "integer",
"exclusiveMinimum": 0,
"default": 3600
},
"critical_age": {
"type": "integer",
"exclusiveMinimum": 0,
"default": 86400
},
"condition_on_missing": {
"description": "which condition to report if file is missing",
"type": "string",
"enum": [
"unknown",
"ok",
"warning",
"critical"
],
"default": "warning"
},
"condition_on_implausible": {
"description": "which condition to report if the age is negative, i.e. the file is apparently from the future",
"type": "string",
"enum": [
"unknown",
"ok",
"warning",
"critical"
],
"default": "warning"
}
},
"required": [
"path"
]
}
},
"required": [
"kind",
"parameters"
]
},
{
"title": "notification channel 'http_request'",
"type": "object",
"unevaluatedProperties": false,
"properties": {
"kind": {
"type": "string",
"enum": [
"http_request"
]
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"request": {
"type": "object",
"additionalProperties": false,
"properties": {
"target": {
"description": "URL",
"type": "string"
},
"method": {
"type": "string",
"enum": [
"GET",
"POST"
],
"default": "GET"
}
},
"required": [
"target"
]
},
"response": {
"type": "object",
"additionalProperties": false,
"properties": {
"status_code": {
"description": "checks whether the response status code is this",
"type": [
"null",
"integer"
],
"default": 200
},
"headers": {
"description": "conjunctively checks header key-value pairs",
"type": "object",
"additionalProperties": {
"description": "header value",
"type": "string"
},
"properties": {},
"required": [],
"default": {}
},
"body_part": {
"description": "checks whether the response body contains this string",
"type": "string"
}
},
"required": []
},
"as_warning": {
"description": "whether a violation of this check shall be exposed as warning instead of critical; default: false",
"type": "boolean",
"default": false
}
},
"required": [
"request"
]
}
},
"required": [
"kind",
"parameters"
]
}
]
}
]
}
}
},
"required": [
"defaults",
"checks"
]
}

View file

@ -1,12 +1,13 @@
{ {
"defaults": { "defaults": {
}, },
"checks": { "checks": [
"test": { {
"name": "test",
"threshold": 1, "threshold": 1,
"annoy": true, "annoy": true,
"schedule": { "schedule": {
"regular_interval": 60, "regular_interval": 15,
"attentive_interval": 5 "attentive_interval": 5
}, },
"notifications": [ "notifications": [
@ -18,7 +19,6 @@
{ {
"kind": "libnotify", "kind": "libnotify",
"parameters": { "parameters": {
"icon": "/home/fenris/bilder/zeug/heimdall.png"
} }
} }
], ],
@ -29,5 +29,5 @@
"critical_age": 120 "critical_age": 120
} }
} }
} ]
} }

View file

@ -1,489 +0,0 @@
{
"$defs": {
"active": {
"type": "boolean",
"default": true
},
"threshold": {
"description": "how often a condition has to occur in order to be reported",
"type": "integer",
"minimum": 1,
"default": 3
},
"annoy": {
"description": "whether notifications shall be kept sending after the threshold has been surpassed",
"type": "boolean",
"default": false
},
"schedule": {
"type": "object",
"additionalProperties": false,
"properties": {
"regular_interval": {
"description": "in seconds or as text: minute, hour, day, week",
"type": ["integer", "string"]
},
"attentive_interval": {
"description": "in seconds or as text: minute, hour, day, week",
"type": ["integer", "string"]
}
},
"required": [
"regular_interval"
]
},
"check_kind_test": {
"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"
]
},
"check_kind_script": {
"type": "object",
"additionalProperties": false,
"properties": {
"kind": {
"type": "string",
"const": "script"
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"path": {
"type": "string"
},
"arguments": {
"type": "array",
"item": {
"type": "string"
}
}
},
"required": [
"path"
]
}
},
"required": [
"kind",
"parameters"
]
},
"check_kind_file_timestamp": {
"type": "object",
"additionalProperties": false,
"properties": {
"kind": {
"type": "string",
"const": "file_timestamp"
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"path": {
"type": "string"
},
"warning_age": {
"type": "integer",
"exclusiveMinimum": 0,
"default": 3600
},
"critical_age": {
"type": "integer",
"exclusiveMinimum": 0,
"default": 86400
},
"condition_on_missing": {
"description": "which condition to report if file is missing",
"type": "string",
"enum": [
"unknown",
"ok",
"warning",
"critical"
],
"default": "warning"
},
"condition_on_implausible": {
"description": "which condition to report if the age is negative, i.e. the file is apparently from the future",
"type": "string",
"enum": [
"unknown",
"ok",
"warning",
"critical"
],
"default": "warning"
}
},
"required": [
"path"
]
}
},
"required": [
"kind",
"parameters"
]
},
"check_kind_http_request": {
"type": "object",
"additionalProperties": false,
"properties": {
"kind": {
"type": "string",
"const": "http_request"
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"request": {
"type": "object",
"additionalProperties": false,
"properties": {
"target": {
"description": "URL",
"type": "string"
},
"method": {
"type": "string",
"enum": [
"GET",
"POST"
],
"default": "GET"
}
},
"required": [
"target"
]
},
"response": {
"type": "object",
"additionalProperties": false,
"properties": {
"status_code": {
"description": "checks whether the response status code is this",
"type": ["null", "integer"],
"default": 200
},
"headers": {
"description": "conjunctively checks header key-value pairs",
"type": "object",
"additionalProperties": {
"description": "header value",
"type": "string"
},
"properties": {
},
"required": [
],
"default": {}
},
"body_part": {
"description": "checks whether the response body contains this string",
"type": "string"
}
},
"required": [
]
},
"as_warning": {
"description": "whether a violation of this check shall be exposed as warning instead of critical; default: false",
"type": "boolean",
"default": false
}
},
"required": [
"request"
]
}
},
"required": [
"kind",
"parameters"
]
},
"notification_channel_console": {
"type": "object",
"additionalProperties": false,
"properties": {
"kind": {
"type": "string",
"const": "console"
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
},
"required": [
]
}
},
"required": [
"kind",
"parameters"
]
},
"notification_channel_file_touch": {
"type": "object",
"additionalProperties": false,
"properties": {
"kind": {
"type": "string",
"const": "file_touch"
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"path": {
"type": "string"
}
},
"required": [
"path"
]
}
},
"required": [
"kind",
"parameters"
]
},
"notification_channel_libnotify": {
"type": "object",
"additionalProperties": false,
"properties": {
"kind": {
"type": "string",
"const": "libnotify"
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"icon": {
"type": "string"
}
},
"required": [
]
}
},
"required": [
"kind",
"parameters"
]
},
"notification_channel_email": {
"type": "object",
"additionalProperties": false,
"properties": {
"kind": {
"type": "string",
"const": "email"
},
"parameters": {
"type": "object",
"additionalProperties": false,
"properties": {
"access": {
"type": "object",
"additionalProperties": false,
"properties": {
"host": {
"type": "string"
},
"port": {
"type": "integer"
},
"username": {
"type": "string"
},
"password": {
"type": "string"
}
},
"required": [
"host",
"port",
"username",
"password"
]
},
"sender": {
"type": "string"
},
"receivers": {
"type": "array",
"item": {
"type": "string"
}
},
"tags": {
"description": "list of strings, which will be placed in the e-mail subject",
"type": "array",
"item": {
"type": "string"
},
"default": []
}
},
"required": [
"access",
"sender",
"receivers"
]
}
},
"required": [
"kind",
"parameters"
]
},
"notifications": {
"type": "array",
"item": {
"anyOf": [
{
"$ref": "#/$defs/notification_channel_console"
},
{
"$ref": "#/$defs/notification_channel_file_touch"
},
{
"$ref": "#/$defs/notification_channel_email"
}
]
},
"default": [
{
"kind": "console",
"parameters": {
}
}
]
}
},
"type": "object",
"additionalProperties": false,
"properties": {
"defaults": {
"description": "default values for checks",
"type": "object",
"additionalProperties": false,
"properties": {
"active": {
"$ref": "#/$defs/active"
},
"threshold": {
"$ref": "#/$defs/threshold"
},
"annoy": {
"$ref": "#/$defs/annoy"
},
"schedule": {
"$ref": "#/$defs/schedule"
},
"notifications": {
"$ref": "#/$defs/notifications"
}
},
"required": [
]
},
"checks": {
"type": "object",
"additionalProperties": {
"allOf": [
{
"description": "should represent a specific check",
"type": "object",
"additionalProperties": false,
"properties": {
"title": {
"type": "string"
},
"active": {
"$ref": "#/$defs/active"
},
"threshold": {
"$ref": "#/$defs/threshold"
},
"annoy": {
"$ref": "#/$defs/annoy"
},
"schedule": {
"$ref": "#/$defs/schedule"
},
"notifications": {
"$ref": "#/$defs/notifications"
}
},
"required": [
]
},
{
"anyOf": [
{
"$ref": "#/$defs/check_kind_test"
},
{
"$ref": "#/$defs/check_kind_script"
},
{
"$ref": "#/$defs/check_kind_file_timestamp"
},
{
"$ref": "#/$defs/check_kind_http_request"
}
]
}
]
},
"properties": {
},
"required": [
]
}
},
"required": [
"defaults",
"groups"
]
}

View file

@ -1,5 +1,9 @@
class interface_check_kind(object): class interface_check_kind(object):
def parameters_schema(self):
raise NotImplementedError
def normalize_conf_node(self, node): def normalize_conf_node(self, node):
raise NotImplementedError raise NotImplementedError

View file

@ -1,5 +1,55 @@
class implementation_check_kind_file_timestamp(interface_check_kind): class implementation_check_kind_file_timestamp(interface_check_kind):
'''
[implementation]
'''
def parameters_schema(self):
return {
"type": "object",
"additionalProperties": False,
"properties": {
"path": {
"type": "string"
},
"warning_age": {
"type": "integer",
"exclusiveMinimum": 0,
"default": (60 * 60)
},
"critical_age": {
"type": "integer",
"exclusiveMinimum": 0,
"default": (60 * 60 * 24)
},
"condition_on_missing": {
"description": "which condition to report if file is missing",
"type": "string",
"enum": [
"unknown",
"ok",
"warning",
"critical"
],
"default": "warning"
},
"condition_on_implausible": {
"description": "which condition to report if the age is negative, i.e. the file is apparently from the future",
"type": "string",
"enum": [
"unknown",
"ok",
"warning",
"critical"
],
"default": "warning"
}
},
"required": [
"path"
]
}
''' '''
[implementation] [implementation]
''' '''
@ -62,4 +112,3 @@ class implementation_check_kind_file_timestamp(interface_check_kind):
), ),
} }

View file

@ -1,5 +1,76 @@
class implementation_check_kind_http_request(interface_check_kind): class implementation_check_kind_http_request(interface_check_kind):
'''
[implementation]
'''
def parameters_schema(self):
return {
"type": "object",
"additionalProperties": False,
"properties": {
"request": {
"type": "object",
"additionalProperties": False,
"properties": {
"target": {
"description": "URL",
"type": "string"
},
"method": {
"type": "string",
"enum": [
"GET",
"POST"
],
"default": "GET"
}
},
"required": [
"target"
]
},
"response": {
"type": "object",
"additionalProperties": False,
"properties": {
"status_code": {
"description": "checks whether the response status code is this",
"type": ["null", "integer"],
"default": 200
},
"headers": {
"description": "conjunctively checks header key-value pairs",
"type": "object",
"additionalProperties": {
"description": "header value",
"type": "string"
},
"properties": {
},
"required": [
],
"default": {}
},
"body_part": {
"description": "checks whether the response body contains this string",
"type": "string"
}
},
"required": [
]
},
"as_warning": {
"description": "whether a violation of this check shall be exposed as warning instead of critical; default: false",
"type": "boolean",
"default": False
}
},
"required": [
"request"
]
}
''' '''
[implementation] [implementation]
''' '''
@ -118,3 +189,4 @@ class implementation_check_kind_http_request(interface_check_kind):
), ),
"output": "\n".join(lines), "output": "\n".join(lines),
} }

View file

@ -1,5 +1,29 @@
class implementation_check_kind_script(interface_check_kind): class implementation_check_kind_script(interface_check_kind):
'''
[implementation]
'''
def parameters_schema(self):
return {
"type": "object",
"additionalProperties": False,
"properties": {
"path": {
"type": "string"
},
"arguments": {
"type": "array",
"item": {
"type": "string"
}
}
},
"required": [
"path"
]
}
''' '''
[implementation] [implementation]
''' '''

View file

@ -14,6 +14,179 @@ def state_decode(state_encoded):
} }
def schema_active():
return {
"type": "boolean",
"default": True
}
def schema_threshold():
return {
"description": "how often a condition has to occur in order to be reported",
"type": "integer",
"minimum": 1,
"default": 3
}
def schema_annoy():
return {
"description": "whether notifications shall be kept sending after the threshold has been surpassed",
"type": "boolean",
"default": False
}
def schema_interval(default):
return {
"description": "in seconds or as text",
"anyOf": [
{
"type": "integer",
"exclusiveMinimum": 0
},
{
"type": "string",
"enum": [
"minute",
"hour",
"day",
"week",
]
},
],
"default": default,
}
def schema_schedule():
return {
"type": "object",
"additionalProperties": False,
"properties": {
"regular_interval": schema_interval(60 * 60),
"attentive_interval": schema_interval(60 * 2),
},
"required": [
]
}
def schema_notifications(notification_channel_implementations):
return {
"type": "array",
"items": {
"anyOf": list(
map(
lambda pair: {
"title": ("check kind '%s'" % pair[0]),
"type": "object",
"unevaluatedProperties": False,
"properties": {
"kind": {
"type": "string",
"enum": [pair[0]]
},
"parameters": pair[1].parameters_schema(),
},
"required": [
"kind",
"parameters"
]
},
notification_channel_implementations.items()
)
)
},
"default": [
{
"kind": "console",
"parameters": {
}
}
]
}
def 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": schema_active(),
"threshold": schema_threshold(),
"annoy": schema_annoy(),
"schedule": schema_schedule(),
"notifications": schema_notifications(notification_channel_implementations),
},
"required": [
]
},
"checks": {
"type": "array",
"items": {
"allOf": [
{
"description": "should represent a specific check",
"type": "object",
"unevaluatedProperties": False,
"properties": {
"name": {
"type": "string"
},
"title": {
"type": "string"
},
"active": schema_active(),
"threshold": schema_threshold(),
"annoy": schema_annoy(),
"schedule": schema_schedule(),
"notifications": schema_notifications(notification_channel_implementations),
},
"required": [
"name",
]
},
{
"anyOf": list(
map(
lambda pair: {
"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"
]
},
check_kind_implementations.items()
)
),
},
]
}
}
},
"required": [
"defaults",
"checks",
]
}
def conf_normalize_interval(interval_raw): def conf_normalize_interval(interval_raw):
if (type(interval_raw) == int): if (type(interval_raw) == int):
return interval_raw return interval_raw
@ -46,8 +219,15 @@ def conf_normalize_schedule(node):
} }
def conf_normalize_defaults(node): def conf_normalize_notification(notification_channel_implementations, node):
return dict_merge( return {
"kind": node["kind"],
"parameters": notification_channel_implementations[node["kind"]].normalize_conf_node(node["parameters"]),
}
def conf_normalize_defaults(notification_channel_implementations, node):
node_ = dict_merge(
{ {
"active": True, "active": True,
"threshold": 3, "threshold": 3,
@ -61,9 +241,24 @@ def conf_normalize_defaults(node):
}, },
node 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"]
)
),
}
def conf_normalize_check(check_kind_implementations, defaults, name, node): def conf_normalize_check(check_kind_implementations, notification_channel_implementations, defaults, node):
if ("name" not in node):
raise ValueError("missing mandatory 'check' field 'name'")
else:
if ("kind" not in node): if ("kind" not in node):
raise ValueError("missing mandatory 'check' field 'kind'") raise ValueError("missing mandatory 'check' field 'kind'")
else: else:
@ -72,7 +267,7 @@ def conf_normalize_check(check_kind_implementations, defaults, name, node):
else: else:
node_ = dict_merge( node_ = dict_merge(
{ {
"title": name, "title": node["name"],
"active": defaults["active"], "active": defaults["active"],
"threshold": defaults["threshold"], "threshold": defaults["threshold"],
"annoy": defaults["annoy"], "annoy": defaults["annoy"],
@ -83,30 +278,49 @@ def conf_normalize_check(check_kind_implementations, defaults, name, node):
node node
) )
return { return {
"name": node_["name"],
"title": node_["title"], "title": node_["title"],
"active": node_["active"], "active": node_["active"],
"threshold": node_["threshold"], "threshold": node_["threshold"],
"annoy": node_["annoy"], "annoy": node_["annoy"],
"schedule": conf_normalize_schedule(node_["schedule"]), "schedule": conf_normalize_schedule(node_["schedule"]),
"notifications": node_["notifications"], "notifications": list(
map(
lambda x: conf_normalize_notification(notification_channel_implementations, x),
node_["notifications"]
)
),
"kind": node_["kind"], "kind": node_["kind"],
"parameters": check_kind_implementations[node_["kind"]].normalize_conf_node(node_["parameters"]), "parameters": check_kind_implementations[node_["kind"]].normalize_conf_node(node_["parameters"]),
} }
def conf_normalize_root(check_kind_implementations, node): def conf_normalize_root(check_kind_implementations, notification_channel_implementations, node):
return dict( counts = {}
for node_ in node["checks"]:
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(counts.keys()),
}
)
)
else:
return list(
map( map(
lambda check_pair: ( lambda node_: conf_normalize_check(
check_pair[0],
conf_normalize_check(
check_kind_implementations, check_kind_implementations,
conf_normalize_defaults(node["defaults"]), notification_channel_implementations,
check_pair[0], conf_normalize_defaults(notification_channel_implementations, node["defaults"]),
check_pair[1] node_
), ),
), node["checks"]
node["checks"].items()
) )
) )
@ -127,7 +341,7 @@ def main():
help = "path to the configuration file" help = "path to the configuration file"
) )
argumentparser.add_argument( argumentparser.add_argument(
"-s", "-f",
"--state-path", "--state-path",
type = str, type = str,
default = None, default = None,
@ -143,6 +357,14 @@ def main():
dest = "erase_state", dest = "erase_state",
help = "whether the state shall be deleted on start; this will cause that all checks are executed" help = "whether the state shall be deleted on start; this will cause that all checks are executed"
) )
argumentparser.add_argument(
"-s",
"--show-schema",
action = "store_true",
default = False,
dest = "show_schema",
help = "print the hmdl JSON schema to stdout and exit"
)
argumentparser.add_argument( argumentparser.add_argument(
"-e", "-e",
"--expose-full-conf", "--expose-full-conf",
@ -166,8 +388,6 @@ def main():
## exec ## exec
_sys.stderr.write(">> state file path: %s\n" % state_path)
### load check kind implementations ### load check kind implementations
check_kind_implementations = { check_kind_implementations = {
"script": implementation_check_kind_script(), "script": implementation_check_kind_script(),
@ -183,8 +403,27 @@ def main():
"libnotify": implementation_notification_channel_libnotify(), "libnotify": implementation_notification_channel_libnotify(),
} }
if (args.show_schema):
_sys.stdout.write(
_json.dumps(
schema_root(
check_kind_implementations,
notification_channel_implementations
),
indent = "\t"
)
+
"\n"
)
else:
_sys.stderr.write(">> state file path: %s\n" % state_path)
### get configuration data ### get configuration data
checks = conf_normalize_root(check_kind_implementations, _json.loads(file_read(args.conf_path))) checks = conf_normalize_root(
check_kind_implementations,
notification_channel_implementations,
_json.loads(file_read(args.conf_path))
)
if (args.expose_full_conf): if (args.expose_full_conf):
_sys.stdout.write(_json.dumps(checks, indent = "\t") + "\n") _sys.stdout.write(_json.dumps(checks, indent = "\t") + "\n")
_sys.exit(1) _sys.exit(1)
@ -201,15 +440,15 @@ def main():
state_data = _json.loads(file_read(state_path)) state_data = _json.loads(file_read(state_path))
### iterate through checks ### iterate through checks
for (check_name, check_data, ) in checks.items(): for check_data in checks:
if (not check_data["active"]): if (not check_data["active"]):
pass pass
else: else:
### get old state and examine whether the check shall be executed ### get old state and examine whether the check shall be executed
old_item_state = ( old_item_state = (
None None
if (check_name not in state_data) else if (check_data["name"] not in state_data) else
state_decode(state_data[check_name]) state_decode(state_data[check_data["name"]])
) )
timestamp = get_current_timestamp() timestamp = get_current_timestamp()
due = ( due = (
@ -232,7 +471,7 @@ def main():
string_coin( string_coin(
"-- {{check_name}}\n", "-- {{check_name}}\n",
{ {
"check_name": check_name, "check_name": check_data["name"],
} }
) )
) )
@ -260,7 +499,7 @@ def main():
) )
), ),
} }
state_data[check_name] = state_encode(new_item_state) state_data[check_data["name"]] = state_encode(new_item_state)
file_write(state_path, _json.dumps(state_data, indent = "\t")) file_write(state_path, _json.dumps(state_data, indent = "\t"))
### send notifications ### send notifications
@ -281,7 +520,7 @@ def main():
if (notification["kind"] in notification_channel_implementations): if (notification["kind"] in notification_channel_implementations):
notification_channel_implementations[notification["kind"]].notify( notification_channel_implementations[notification["kind"]].notify(
notification["parameters"], notification["parameters"],
check_name, check_data["name"],
check_data, check_data,
new_item_state, new_item_state,
result["output"] result["output"]
@ -291,3 +530,4 @@ def main():
main() main()

View file

@ -1,5 +1,9 @@
class interface_notification_channel(object): class interface_notification_channel(object):
def parameters_schema(self):
raise NotImplementedError
def normalize_conf_node(self, node): def normalize_conf_node(self, node):
raise NotImplementedError raise NotImplementedError

View file

@ -1,5 +1,19 @@
class implementation_notification_channel_console(interface_notification_channel): class implementation_notification_channel_console(interface_notification_channel):
'''
[implementation]
'''
def parameters_schema(self):
return {
"type": "object",
"additionalProperties": False,
"properties": {
},
"required": [
]
}
''' '''
[implementation] [implementation]
''' '''

View file

@ -1,5 +1,63 @@
class implementation_notification_channel_email(interface_notification_channel): class implementation_notification_channel_email(interface_notification_channel):
'''
[implementation]
'''
def parameters_schema(self):
return {
"type": "object",
"additionalProperties": False,
"properties": {
"access": {
"type": "object",
"additionalProperties": False,
"properties": {
"host": {
"type": "string"
},
"port": {
"type": "integer"
},
"username": {
"type": "string"
},
"password": {
"type": "string"
}
},
"required": [
"host",
"port",
"username",
"password"
]
},
"sender": {
"type": "string"
},
"receivers": {
"type": "array",
"items": {
"type": "string"
}
},
"tags": {
"description": "list of strings, which will be placed in the e-mail subject",
"type": "array",
"items": {
"type": "string"
},
"default": []
}
},
"required": [
"access",
"sender",
"receivers"
]
}
''' '''
[implementation] [implementation]
''' '''

View file

@ -1,5 +1,23 @@
class implementation_notification_channel_file_touch(interface_notification_channel): class implementation_notification_channel_file_touch(interface_notification_channel):
'''
[implementation]
'''
def parameters_schema(self):
return {
"type": "object",
"additionalProperties": False,
"properties": {
"path": {
"type": "string"
}
},
"required": [
"path"
]
}
''' '''
[implementation] [implementation]
''' '''

View file

@ -1,12 +1,29 @@
class implementation_notification_channel_libnotify(interface_notification_channel): class implementation_notification_channel_libnotify(interface_notification_channel):
'''
[implementation]
'''
def parameters_schema(self):
return {
"type": "object",
"additionalProperties": False,
"properties": {
"icon": {
"type": "string"
}
},
"required": [
]
}
''' '''
[implementation] [implementation]
''' '''
def normalize_conf_node(self, node): def normalize_conf_node(self, node):
return dict_merge( return dict_merge(
{ {
"icon": None, "icon": "/usr/local/share/icons/heimdall.png",
}, },
node node
) )

View file

@ -3,8 +3,8 @@
- Selbst-Test - Selbst-Test
- Benachrichtigungs-Kanäle: - Benachrichtigungs-Kanäle:
- Matrix - Matrix
- JSON-Schema für Konfiguration von Programm erzeugen lassen
- Möglichkeit dauerhaft laufen zulassen (evtl. als systemd-Dienst) - Möglichkeit dauerhaft laufen zulassen (evtl. als systemd-Dienst)
- Versionierung - Versionierung
- Umbenennung: `output` zu `info` - Umbenennung: `output` zu `info`
- Test-Routinen - Test-Routinen