[mod] source
This commit is contained in:
parent
e168077ef9
commit
cf9c4f009d
|
|
@ -17,6 +17,8 @@
|
|||
"help.args.expose_full_order": "nur den Pfad zur Datenbank-Datei zur Standard-Ausgabe schreiben und beenden (nützlich für Fehlersuche)",
|
||||
"help.args.show_version": "nur die Version zur Standard-Ausgabe schreiben und beenden",
|
||||
"help.args.verbosity": "Schwellwert für Log-Ausgaben",
|
||||
"checks.script.execution_failed": "Ausführung gescheitert",
|
||||
"checks.script.invalid_return_code": "ungültiger Rückgabe-Code",
|
||||
"checks.file_state.exists": "Datei existiert (soll aber nicht)",
|
||||
"checks.file_state.missing": "Datei existiert nicht (soll aber)",
|
||||
"checks.file_state.timestamp_implausible": "Datei ist scheinbar aus der Zukunft",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
"help.args.expose_full_order": "only print the extended order to stdout and exit (useful for debugging)",
|
||||
"help.args.show_version": "only print the version to stdout and exit",
|
||||
"help.args.verbosity": "threshold for log outputs",
|
||||
"checks.script.execution_failed": "execution failed",
|
||||
"checks.script.invalid_return_code": "invalid return code",
|
||||
"checks.file_state.exists": "file exists (but shall not)",
|
||||
"checks.file_state.missing": "file does not exist (but shall)",
|
||||
"checks.file_state.timestamp_implausible": "file is apparently from the future",
|
||||
|
|
|
|||
|
|
@ -1,125 +0,0 @@
|
|||
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]
|
||||
'''
|
||||
def normalize_order_node(self, node):
|
||||
return dict_merge(
|
||||
{
|
||||
},
|
||||
node
|
||||
)
|
||||
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def notify(self, parameters, name, data, state, info):
|
||||
datetime = _datetime.datetime.utcnow()
|
||||
smtp_connection = _smtplib.SMTP(
|
||||
parameters["access"]["host"]
|
||||
)
|
||||
smtp_connection.login(
|
||||
parameters["access"]["username"],
|
||||
parameters["access"]["password"]
|
||||
)
|
||||
message = MIMEText(
|
||||
_json.dumps(info, indent = "\t", ensure_ascii = False)
|
||||
)
|
||||
message["Subject"] = string_coin(
|
||||
"{{tags}} {{title}}",
|
||||
{
|
||||
"tags": " ".join(
|
||||
map(
|
||||
lambda tag: ("[%s]" % tag.upper()),
|
||||
(
|
||||
parameters["tags"]
|
||||
+
|
||||
[condition_show(state["condition"])]
|
||||
)
|
||||
)
|
||||
),
|
||||
"title": data["name"],
|
||||
}
|
||||
)
|
||||
message["From"] = parameters["sender"]
|
||||
message["To"] = ",".join(parameters["receivers"])
|
||||
message["Date"] = string_coin(
|
||||
"{{day_of_week}}, {{day_of_month}} {{month}} {{year}} {{hour}}:{{minute}}:{{second}} {{time_offset}}",
|
||||
{
|
||||
"day_of_week": datetime.strftime("%a"),
|
||||
"day_of_month": datetime.strftime("%d"),
|
||||
"month": datetime.strftime("%b"),
|
||||
"year": datetime.strftime("%Y"),
|
||||
"hour": datetime.strftime("%H"),
|
||||
"minute": datetime.strftime("%M"),
|
||||
"second": datetime.strftime("%S"),
|
||||
# "time_offset": datetime.strftime("%z"),
|
||||
"time_offset": "+0000",
|
||||
}
|
||||
)
|
||||
smtp_connection.sendmail(
|
||||
parameters["sender"],
|
||||
parameters["receivers"],
|
||||
message.as_string()
|
||||
)
|
||||
smtp_connection.quit()
|
||||
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
class implementation_notification_channel_libnotify(interface_notification_channel):
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def parameters_schema(self):
|
||||
return {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"properties": {
|
||||
"icon": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def normalize_order_node(self, node):
|
||||
return dict_merge(
|
||||
{
|
||||
"icon": "/usr/local/share/icons/heimdall.png",
|
||||
},
|
||||
node
|
||||
)
|
||||
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def notify(self, parameters, name, data, state, info):
|
||||
def condition_translate(condition):
|
||||
return {
|
||||
enum_condition.unknown: "normal",
|
||||
enum_condition.ok: "low",
|
||||
enum_condition.concerning: "normal",
|
||||
enum_condition.critical: "critical",
|
||||
}[condition]
|
||||
parts = []
|
||||
parts.append(
|
||||
"notify-send"
|
||||
)
|
||||
## app name
|
||||
parts.append(
|
||||
string_coin(
|
||||
"--app-name={{app_name}}",
|
||||
{
|
||||
"app_name": "heimdall",
|
||||
}
|
||||
)
|
||||
)
|
||||
## urgency
|
||||
parts.append(
|
||||
string_coin(
|
||||
"--urgency={{urgency}}",
|
||||
{
|
||||
"urgency": condition_translate(state["condition"]),
|
||||
}
|
||||
)
|
||||
)
|
||||
## icon
|
||||
if ("icon" not in parameters):
|
||||
pass
|
||||
else:
|
||||
parts.append(
|
||||
string_coin(
|
||||
"--icon={{icon}}",
|
||||
{
|
||||
"icon": parameters["icon"],
|
||||
}
|
||||
)
|
||||
)
|
||||
## summary
|
||||
parts.append(
|
||||
string_coin(
|
||||
"{{condition}} | {{title}}",
|
||||
{
|
||||
"title": data["title"],
|
||||
"condition": condition_show(state["condition"]).upper(),
|
||||
}
|
||||
)
|
||||
)
|
||||
## body
|
||||
parts.append(_json.dumps(info, ensure_ascii = False))
|
||||
_subprocess.run(parts)
|
||||
|
||||
|
|
@ -1,178 +0,0 @@
|
|||
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"
|
||||
},
|
||||
"critical": {
|
||||
"description": "whether a violation of this check shall be leveled as critical instead of concerning",
|
||||
"type": "boolean",
|
||||
"default": True
|
||||
},
|
||||
"strict": {
|
||||
"deprecated": True,
|
||||
"description": "alias for 'critical'",
|
||||
"type": "boolean",
|
||||
"default": True
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"host",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def normalize_order_node(self, node):
|
||||
version = (
|
||||
"v1"
|
||||
if (not ("critical" in node)) else
|
||||
"v2"
|
||||
)
|
||||
|
||||
if (version == "v1"):
|
||||
if (not "host" in node):
|
||||
raise ValueError("mandatory parameter \"host\" missing")
|
||||
else:
|
||||
node_ = dict_merge(
|
||||
{
|
||||
"ssh_port": None,
|
||||
"ssh_user": None,
|
||||
"ssh_key": None,
|
||||
"mount_point": "/",
|
||||
"threshold": 95,
|
||||
"strict": False,
|
||||
},
|
||||
node
|
||||
)
|
||||
return {
|
||||
"ssh_port": node_["ssh_port"],
|
||||
"ssh_user": node_["ssh_user"],
|
||||
"ssh_key": node_["ssh_key"],
|
||||
"mount_point": node_["ssh_path"],
|
||||
"threshold": node_["ssh_threshold"],
|
||||
"critical": node_["strict"],
|
||||
}
|
||||
elif (version == "v2"):
|
||||
if (not "host" in node):
|
||||
raise ValueError("mandatory parameter \"host\" missing")
|
||||
else:
|
||||
node_ = dict_merge(
|
||||
{
|
||||
"ssh_port": None,
|
||||
"ssh_user": None,
|
||||
"ssh_key": None,
|
||||
"mount_point": "/",
|
||||
"threshold": 95,
|
||||
"critical": False,
|
||||
},
|
||||
node
|
||||
)
|
||||
return node_
|
||||
else:
|
||||
raise ValueError("unhandled")
|
||||
|
||||
|
||||
'''
|
||||
[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]),
|
||||
}
|
||||
faults = []
|
||||
if (data["perc"] > parameters["threshold"]):
|
||||
faults.append(translation_get("checks.generic_remote.overflow"))
|
||||
else:
|
||||
pass
|
||||
return {
|
||||
"condition": (
|
||||
enum_condition.ok
|
||||
if (len(faults) <= 0) else
|
||||
(
|
||||
enum_condition.critical
|
||||
if parameters["strict"] else
|
||||
enum_condition.concerning
|
||||
)
|
||||
),
|
||||
"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": faults
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
def dict_merge(core_dict, mantle_dict, recursive = False):
|
||||
result_dict = {}
|
||||
for current_dict in [core_dict, mantle_dict]:
|
||||
for (key, value, ) in current_dict.items():
|
||||
if (not (key in result_dict)):
|
||||
result_dict[key] = value
|
||||
else:
|
||||
if (recursive and (type(result_dict[key]) == dict) and (type(value) == dict)):
|
||||
result_dict[key] = dict_merge(result_dict[key], value)
|
||||
elif (recursive and (type(result_dict[key]) == list) and (type(value) == list)):
|
||||
result_dict[key] = (result_dict[key] + value)
|
||||
else:
|
||||
result_dict[key] = value
|
||||
return result_dict
|
||||
|
||||
|
||||
def file_read(path):
|
||||
handle = open(path, "r")
|
||||
content = handle.read()
|
||||
handle.close()
|
||||
return content
|
||||
|
||||
|
||||
def file_write(path, content, options = None):
|
||||
options = dict_merge(
|
||||
{
|
||||
"append": False,
|
||||
},
|
||||
({} if (options is None) else options)
|
||||
)
|
||||
handle = open(path, "a" if options["append"] else "w")
|
||||
handle.write(content)
|
||||
handle.close()
|
||||
|
||||
|
||||
def string_coin(template, arguments):
|
||||
result = template
|
||||
for (key, value, ) in arguments.items():
|
||||
result = result.replace("{{%s}}" % key, value)
|
||||
return result
|
||||
|
||||
|
||||
def get_current_timestamp():
|
||||
return int(round(_time.time(), 0))
|
||||
|
||||
|
||||
def env_get_language():
|
||||
try:
|
||||
env_lang = _os.environ.get("LANG")
|
||||
locale = env_lang.split(".")[0]
|
||||
language = locale.split("_")[0]
|
||||
return language
|
||||
except Exception as error:
|
||||
return None
|
||||
|
||||
|
||||
def shell_command(command):
|
||||
result = _subprocess.run(
|
||||
command,
|
||||
capture_output = True,
|
||||
shell = True,
|
||||
)
|
||||
return {
|
||||
"return_code": result.returncode,
|
||||
"stdout": result.stdout.decode(),
|
||||
"stderr": result.stderr.decode(),
|
||||
}
|
||||
|
||||
|
||||
def format_bytes(bytes_):
|
||||
units = [
|
||||
{"label": "B", "digits": 0},
|
||||
{"label": "KB", "digits": 1},
|
||||
{"label": "MB", "digits": 1},
|
||||
{"label": "GB", "digits": 1},
|
||||
{"label": "TB", "digits": 1},
|
||||
{"label": "PB", "digits": 1},
|
||||
]
|
||||
number = bytes_
|
||||
index = 0
|
||||
while ((number >= 1000) and (index < (len(units) - 1))):
|
||||
number /= 1000
|
||||
index += 1
|
||||
return (
|
||||
("%." + ("%u" % units[index]["digits"]) + "f %s")
|
||||
% (
|
||||
number,
|
||||
units[index]["label"],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def sqlite_query_set(database_path, template, arguments):
|
||||
connection = _sqlite3.connect(database_path)
|
||||
cursor = connection.cursor()
|
||||
result = cursor.execute(template, arguments)
|
||||
connection.commit()
|
||||
connection.close()
|
||||
return result
|
||||
|
||||
|
||||
def sqlite_query_put(database_path, template, arguments):
|
||||
connection = _sqlite3.connect(database_path)
|
||||
cursor = connection.cursor()
|
||||
result = cursor.execute(template, arguments)
|
||||
connection.commit()
|
||||
connection.close()
|
||||
return result
|
||||
|
||||
|
||||
def sqlite_query_get(database_path, template, arguments):
|
||||
connection = _sqlite3.connect(database_path)
|
||||
cursor = connection.cursor()
|
||||
result = cursor.execute(template, arguments)
|
||||
rows = result.fetchall()
|
||||
connection.close()
|
||||
return rows
|
||||
|
||||
|
|
@ -1,388 +0,0 @@
|
|||
def main():
|
||||
## setup translation for the first time
|
||||
translation_initialize("en", env_get_language())
|
||||
|
||||
## args
|
||||
argumentparser = _argparse.ArgumentParser(
|
||||
description = translation_get("help.title"),
|
||||
formatter_class = _argparse.ArgumentDefaultsHelpFormatter
|
||||
)
|
||||
argumentparser.add_argument(
|
||||
type = str,
|
||||
default = "monitoring.hmdl.json",
|
||||
dest = "order_path",
|
||||
metavar = "<order-path>",
|
||||
help = translation_get("help.args.order_path"),
|
||||
)
|
||||
argumentparser.add_argument(
|
||||
"-x",
|
||||
"--erase-state",
|
||||
action = "store_true",
|
||||
default = False,
|
||||
dest = "erase_state",
|
||||
help = translation_get("help.args.erase_state"),
|
||||
)
|
||||
argumentparser.add_argument(
|
||||
"-s",
|
||||
"--show-schema",
|
||||
action = "store_true",
|
||||
default = False,
|
||||
dest = "show_schema",
|
||||
help = translation_get("help.args.show_schema"),
|
||||
)
|
||||
argumentparser.add_argument(
|
||||
"-e",
|
||||
"--expose-full-order",
|
||||
action = "store_true",
|
||||
default = False,
|
||||
dest = "expose_full_order",
|
||||
help = translation_get("help.args.expose_full_order"),
|
||||
)
|
||||
### v conf stuff v
|
||||
argumentparser.add_argument(
|
||||
"-d",
|
||||
"--database-path",
|
||||
type = str,
|
||||
default = None,
|
||||
dest = "database_path",
|
||||
metavar = "<database-path>",
|
||||
help = translation_get("help.args.database_path"),
|
||||
)
|
||||
argumentparser.add_argument(
|
||||
"-m",
|
||||
"--mutex-path",
|
||||
type = str,
|
||||
default = "/tmp/heimdall.lock",
|
||||
dest = "mutex_path",
|
||||
metavar = "<mutex-path>",
|
||||
help = translation_get("help.args.mutex_path"),
|
||||
)
|
||||
argumentparser.add_argument(
|
||||
"-y",
|
||||
"--send-ok-notifications",
|
||||
action = "store_true",
|
||||
default = False,
|
||||
dest = "send_ok_notifications",
|
||||
help = translation_get("help.args.send_ok_notifications", {"condition_name": translation_get("conditions.ok")}),
|
||||
)
|
||||
argumentparser.add_argument(
|
||||
"-l",
|
||||
"--language",
|
||||
type = str,
|
||||
choices = localization_data.keys(),
|
||||
default = None,
|
||||
dest = "language",
|
||||
metavar = "<language>",
|
||||
help = translation_get("help.args.language"),
|
||||
)
|
||||
argumentparser.add_argument(
|
||||
"-t",
|
||||
"--time-to-live",
|
||||
type = int,
|
||||
default = (60 * 60 * 24 * 7),
|
||||
dest = "time_to_live",
|
||||
metavar = "<time-to-live>",
|
||||
help = translation_get("help.args.time_to_live"),
|
||||
)
|
||||
args = argumentparser.parse_args()
|
||||
|
||||
## vars
|
||||
id_ = _hashlib.sha256(_os.path.abspath(args.order_path).encode("ascii")).hexdigest()[:8]
|
||||
database_path = (
|
||||
args.database_path
|
||||
if (args.database_path is not None) else
|
||||
_os.path.join(
|
||||
_tempfile.gettempdir(),
|
||||
string_coin("monitoring-state-{{id}}.sqlite", {"id": id_})
|
||||
)
|
||||
)
|
||||
|
||||
## exec
|
||||
|
||||
### setup translation for the second time
|
||||
if (args.language is not None):
|
||||
translation_initialize("en", args.language)
|
||||
|
||||
### load check kind implementations
|
||||
check_kind_implementations = {
|
||||
"script": implementation_check_kind_script(),
|
||||
"file_state": implementation_check_kind_file_state(),
|
||||
"tls_certificate": implementation_check_kind_tls_certificate(),
|
||||
"http_request": implementation_check_kind_http_request(),
|
||||
"generic_remote" : implementation_check_kind_generic_remote(),
|
||||
}
|
||||
|
||||
### load notification channel implementations
|
||||
notification_channel_implementations = {
|
||||
"console": implementation_notification_channel_console(),
|
||||
"email": implementation_notification_channel_email(),
|
||||
"libnotify": implementation_notification_channel_libnotify(),
|
||||
}
|
||||
|
||||
if (args.show_schema):
|
||||
_sys.stdout.write(
|
||||
_json.dumps(
|
||||
order_schema_root(
|
||||
check_kind_implementations,
|
||||
notification_channel_implementations
|
||||
),
|
||||
indent = "\t"
|
||||
)
|
||||
+
|
||||
"\n"
|
||||
)
|
||||
else:
|
||||
### get order data
|
||||
order = order_load(
|
||||
check_kind_implementations,
|
||||
notification_channel_implementations,
|
||||
_os.path.abspath(args.order_path)
|
||||
)
|
||||
|
||||
if (args.expose_full_order):
|
||||
_sys.stdout.write(_json.dumps(order, indent = "\t") + "\n")
|
||||
_sys.exit(1)
|
||||
else:
|
||||
_sys.stderr.write(
|
||||
string_coin(
|
||||
"[info] {{label}}: {{path}}\n",
|
||||
{
|
||||
"label": translation_get("misc.state_file_path"),
|
||||
"path": database_path,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
### mutex check
|
||||
if (_os.path.exists(args.mutex_path)):
|
||||
_sys.stderr.write(
|
||||
string_coin(
|
||||
"[error] {{message}} ({{path}})\n",
|
||||
{
|
||||
"message": translation_get("misc.still_running"),
|
||||
"path": args.mutex_path,
|
||||
}
|
||||
)
|
||||
)
|
||||
_sys.exit(2)
|
||||
else:
|
||||
### setup database
|
||||
sqlite_query_set(
|
||||
database_path,
|
||||
"CREATE TABLE IF NOT EXISTS results(check_name TEXT NOT NULL, timestamp INTEGER NOT NULL, condition TEXT NOT NULL, notification_sent BOOLEAN NOT NULL, info TEXT NOT NULL);",
|
||||
{}
|
||||
)
|
||||
|
||||
### clean database
|
||||
result = sqlite_query_put(
|
||||
database_path,
|
||||
"DELETE FROM results WHERE ((timestamp < :timestamp_min) OR :erase_state);",
|
||||
{
|
||||
"timestamp_min": (get_current_timestamp() - args.time_to_live),
|
||||
"erase_state": args.erase_state,
|
||||
}
|
||||
)
|
||||
_sys.stderr.write(
|
||||
string_coin(
|
||||
"[info] {{text}}\n",
|
||||
{
|
||||
"text": translation_get(
|
||||
"misc.cleanup_info",
|
||||
{
|
||||
"count": ("%u" % result.rowcount),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
file_write(args.mutex_path, "", {"append": True})
|
||||
|
||||
### iterate through checks
|
||||
for check_data in order["checks"]:
|
||||
if (not check_data["active"]):
|
||||
pass
|
||||
else:
|
||||
### get old state and examine whether the check shall be executed
|
||||
rows1 = sqlite_query_get(
|
||||
database_path,
|
||||
"SELECT MAX(timestamp) FROM results WHERE (check_name = :check_name) AND (notification_sent = TRUE);",
|
||||
{
|
||||
"check_name": check_data["name"],
|
||||
}
|
||||
)
|
||||
rows2 = sqlite_query_get(
|
||||
database_path,
|
||||
"SELECT timestamp, condition, notification_sent FROM results WHERE (check_name = :check_name) ORDER BY timestamp DESC LIMIT :limit;",
|
||||
{
|
||||
"check_name": check_data["name"],
|
||||
"limit": (check_data["threshold"] + 1),
|
||||
}
|
||||
)
|
||||
if (len(rows2) <= 0):
|
||||
old_item_state = None
|
||||
else:
|
||||
count = 1
|
||||
for row in rows2[1:]:
|
||||
if (row[1] == rows2[0][1]):
|
||||
count += 1
|
||||
else:
|
||||
break
|
||||
if (count > check_data["threshold"]):
|
||||
count = None
|
||||
else:
|
||||
pass
|
||||
old_item_state = {
|
||||
"timestamp": rows2[0][0],
|
||||
"condition": condition_decode(rows2[0][1]),
|
||||
"count": count,
|
||||
"last_notification_timestamp": rows1[0][0],
|
||||
}
|
||||
|
||||
timestamp = get_current_timestamp()
|
||||
due = (
|
||||
(old_item_state is None)
|
||||
or
|
||||
(old_item_state["condition"] != enum_condition.ok)
|
||||
or
|
||||
((timestamp - old_item_state["timestamp"]) >= check_data["schedule"]["regular_interval"])
|
||||
or
|
||||
(
|
||||
(old_item_state["count"] is not None)
|
||||
and
|
||||
((timestamp - old_item_state["timestamp"]) >= check_data["schedule"]["attentive_interval"])
|
||||
)
|
||||
)
|
||||
if (not due):
|
||||
pass
|
||||
else:
|
||||
_sys.stderr.write(
|
||||
string_coin(
|
||||
"-- {{check_name}}\n",
|
||||
{
|
||||
"check_name": check_data["name"],
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
### execute check and set new state
|
||||
try:
|
||||
result = check_kind_implementations[check_data["kind"]].run(check_data["parameters"])
|
||||
except Exception as error:
|
||||
result = {
|
||||
"condition": enum_condition.unknown,
|
||||
"info": {
|
||||
"cause": translation_get("misc.check_procedure_failed"),
|
||||
"error": str(error),
|
||||
},
|
||||
}
|
||||
count = (
|
||||
1
|
||||
if (
|
||||
(old_item_state is None)
|
||||
or
|
||||
(old_item_state["condition"] != result["condition"])
|
||||
) else
|
||||
(
|
||||
(old_item_state["count"] + 1)
|
||||
if (
|
||||
(old_item_state["count"] is not None)
|
||||
and
|
||||
((old_item_state["count"] + 1) <= check_data["threshold"])
|
||||
) else
|
||||
None
|
||||
)
|
||||
)
|
||||
shall_send_notification = (
|
||||
(
|
||||
(
|
||||
(count is not None)
|
||||
and
|
||||
(count == check_data["threshold"])
|
||||
)
|
||||
or
|
||||
(
|
||||
(count is None)
|
||||
and
|
||||
check_data["annoy"]
|
||||
)
|
||||
or
|
||||
(
|
||||
(count is None)
|
||||
and
|
||||
(
|
||||
(old_item_state is not None)
|
||||
and
|
||||
(old_item_state["last_notification_timestamp"] is not None)
|
||||
and
|
||||
(check_data["schedule"]["reminding_interval"] is not None)
|
||||
and
|
||||
(
|
||||
(timestamp - old_item_state["last_notification_timestamp"])
|
||||
>=
|
||||
check_data["schedule"]["reminding_interval"]
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
and
|
||||
(
|
||||
(result["condition"] != enum_condition.ok)
|
||||
or
|
||||
args.send_ok_notifications
|
||||
)
|
||||
)
|
||||
new_item_state = {
|
||||
"timestamp": timestamp,
|
||||
"condition": result["condition"],
|
||||
"count": count,
|
||||
"last_notification_timestamp": (
|
||||
timestamp
|
||||
if shall_send_notification else
|
||||
(
|
||||
None
|
||||
if (old_item_state is None) else
|
||||
old_item_state["last_notification_timestamp"]
|
||||
)
|
||||
),
|
||||
}
|
||||
sqlite_query_put(
|
||||
database_path,
|
||||
"INSERT INTO results(check_name, timestamp, condition, notification_sent, info) VALUES (:check_name, :timestamp, :condition, :notification_sent, :info);",
|
||||
{
|
||||
"check_name": check_data["name"],
|
||||
"timestamp": timestamp,
|
||||
"condition": condition_encode(result["condition"]),
|
||||
"notification_sent": shall_send_notification,
|
||||
"info": _json.dumps(result["info"]),
|
||||
}
|
||||
)
|
||||
|
||||
### send notifications
|
||||
if (not shall_send_notification):
|
||||
pass
|
||||
else:
|
||||
for notification in check_data["notifications"]:
|
||||
notification_channel_implementations[notification["kind"]].notify(
|
||||
notification["parameters"],
|
||||
check_data["name"],
|
||||
check_data,
|
||||
new_item_state,
|
||||
dict_merge(
|
||||
(
|
||||
{}
|
||||
if (check_data["custom"] is None) else
|
||||
{"custom": check_data["custom"]}
|
||||
),
|
||||
result["info"]
|
||||
)
|
||||
)
|
||||
|
||||
if (not _os.path.exists(args.mutex_path)):
|
||||
pass
|
||||
else:
|
||||
_os.remove(args.mutex_path)
|
||||
|
||||
|
||||
main()
|
||||
|
||||
|
|
@ -1,3 +1,9 @@
|
|||
/**
|
||||
* @todo outsource
|
||||
*/
|
||||
declare var __dirname;
|
||||
|
||||
|
||||
namespace _heimdall
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -111,11 +111,12 @@ namespace _heimdall.check_kinds.generic_remote
|
|||
true
|
||||
);
|
||||
return {
|
||||
"host": node_["host"],
|
||||
"ssh_port": node_["ssh_port"],
|
||||
"ssh_user": node_["ssh_user"],
|
||||
"ssh_key": node_["ssh_key"],
|
||||
"mount_point": node_["ssh_path"],
|
||||
"threshold": node_["ssh_threshold"],
|
||||
"mount_point": node_["mount_point"],
|
||||
"threshold": node_["threshold"],
|
||||
"critical": node_["strict"],
|
||||
};
|
||||
}
|
||||
|
|
@ -185,49 +186,21 @@ namespace _heimdall.check_kinds.generic_remote
|
|||
}
|
||||
const outer_command : string = outer_command_parts.join(" ");
|
||||
|
||||
type type_result = {
|
||||
return_code : int;
|
||||
stdout : string;
|
||||
stderr : string;
|
||||
};
|
||||
/**
|
||||
* @see https://nodejs.org/api/child_process.html#child_processspawncommand-args-options
|
||||
*/
|
||||
const result : type_result = await new Promise<type_result>(
|
||||
(resolve, reject) => {
|
||||
nm_child_process.spawnSync(
|
||||
outer_command,
|
||||
[],
|
||||
{
|
||||
},
|
||||
(result) => {
|
||||
if (result.error) {
|
||||
reject(result.error);
|
||||
}
|
||||
else {
|
||||
resolve(
|
||||
{
|
||||
"return_code": result.status,
|
||||
"stdout": result.stdout,
|
||||
"stderr": result.stdin,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
const shell_exec_result : _heimdall.helpers.misc.type_shell_exec_result = await _heimdall.helpers.misc.shell_exec(
|
||||
outer_command_parts[0],
|
||||
outer_command_parts.slice(1)
|
||||
);
|
||||
|
||||
if (result.return_code > 0) {
|
||||
if (shell_exec_result.return_code > 0) {
|
||||
return {
|
||||
"condition": _heimdall.enum_condition.unknown,
|
||||
"info": {
|
||||
"error": result.stderr,
|
||||
"error": shell_exec_result.stderr,
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
const stuff : Array<string> = result.stdout.split("\n").slice(-2)[0].split(" ");
|
||||
const stuff : Array<string> = shell_exec_result.stdout.split("\n").slice(-2)[0].split(" ");
|
||||
const data = {
|
||||
"device": stuff[0],
|
||||
"used": parseInt(stuff[2]),
|
||||
|
|
|
|||
|
|
@ -194,12 +194,21 @@ namespace _heimdall.check_kinds.http_request
|
|||
) : Promise<_heimdall.type_result>
|
||||
{
|
||||
let error : (null | Error);
|
||||
const url : URL = new URL(parameters.request.target);
|
||||
const http_request : lib_plankton.http.type_request = {
|
||||
"target": parameters.request.target,
|
||||
"version": "HTTP/1.1",
|
||||
"scheme": (
|
||||
(url.protocol.slice(0, -1) === "https")
|
||||
? "https"
|
||||
: "http"
|
||||
),
|
||||
"host": url.host,
|
||||
"method": {
|
||||
"GET": lib_plankton.http.enum_method.get,
|
||||
"POST": lib_plankton.http.enum_method.post,
|
||||
}[parameters.request.method],
|
||||
"path": url.pathname,
|
||||
"query": url.search,
|
||||
"headers": {},
|
||||
"body": "",
|
||||
};
|
||||
|
|
@ -208,9 +217,9 @@ namespace _heimdall.check_kinds.http_request
|
|||
http_response = await lib_plankton.http.call(
|
||||
http_request,
|
||||
{
|
||||
"timeout": /*parameters.timeout*/20.0,
|
||||
"timeout": parameters.timeout,
|
||||
"follow_redirects": parameters.follow_redirects,
|
||||
"implementation": "http_module",
|
||||
"implementation": "fetch",
|
||||
}
|
||||
);
|
||||
error = null;
|
||||
|
|
@ -247,12 +256,12 @@ namespace _heimdall.check_kinds.http_request
|
|||
}
|
||||
else {
|
||||
const status_code_expected : int = (parameters.response.status_code as int);
|
||||
if (! (http_response.statuscode === status_code_expected)) {
|
||||
if (! (http_response.status_code === status_code_expected)) {
|
||||
faults.push(
|
||||
lib_plankton.translate.get(
|
||||
"checks.http_request.status_code_mismatch",
|
||||
{
|
||||
"status_code_actual": http_response.statuscode.toFixed(0),
|
||||
"status_code_actual": http_response.status_code.toFixed(0),
|
||||
"status_code_expected": status_code_expected.toFixed(0),
|
||||
}
|
||||
)
|
||||
|
|
@ -327,7 +336,7 @@ namespace _heimdall.check_kinds.http_request
|
|||
}
|
||||
else {
|
||||
const body_part : string = (parameters.response.body_part as string);
|
||||
if (! http_response.body.includes(body_part)) {
|
||||
if (! http_response.body.toString().includes(body_part)) {
|
||||
faults.push(
|
||||
lib_plankton.translate.get(
|
||||
"checks.http_request.body_misses_part",
|
||||
|
|
@ -356,7 +365,7 @@ namespace _heimdall.check_kinds.http_request
|
|||
"info": {
|
||||
"request": parameters.request,
|
||||
"response": {
|
||||
"status_code": http_response.statuscode,
|
||||
"status_code": http_response.status_code,
|
||||
"headers": http_response.headers,
|
||||
// "body": http_response.body,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -54,47 +54,36 @@ namespace _heimdall.check_kinds.script
|
|||
parameters
|
||||
) : Promise<_heimdall.type_result>
|
||||
{
|
||||
const nm_child_process = require("child_process");
|
||||
|
||||
type type_result = {
|
||||
return_code : int;
|
||||
stdout : string;
|
||||
stderr : string;
|
||||
};
|
||||
/**
|
||||
* @see https://nodejs.org/api/child_process.html#child_processspawncommand-args-options
|
||||
*/
|
||||
const result : type_result = await new Promise<type_result>(
|
||||
(resolve, reject) => {
|
||||
nm_child_process.spawnSync(
|
||||
let shell_exec_result : (null | _heimdall.helpers.misc.type_shell_exec_result);
|
||||
let error : any;
|
||||
try {
|
||||
shell_exec_result = await _heimdall.helpers.misc.shell_exec(
|
||||
parameters["path"],
|
||||
parameters["arguments"],
|
||||
);
|
||||
error = null;
|
||||
}
|
||||
catch (error_) {
|
||||
shell_exec_result = null;
|
||||
error = error_;
|
||||
}
|
||||
let condition : _heimdall.enum_condition;
|
||||
if ((error !== null) || (shell_exec_result === null)) {
|
||||
lib_plankton.log.notice(
|
||||
lib_plankton.translate.get("checks.script.execution_failed"),
|
||||
{
|
||||
},
|
||||
(result) => {
|
||||
if (result.error) {
|
||||
reject(result.error);
|
||||
"error": String(error),
|
||||
}
|
||||
);
|
||||
condition = _heimdall.enum_condition.unknown;
|
||||
}
|
||||
else {
|
||||
resolve(
|
||||
{
|
||||
"return_code": result.status,
|
||||
"stdout": result.stdout,
|
||||
"stderr": result.stdin,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
let condition : _heimdall.enum_condition;
|
||||
switch (result.return_code) {
|
||||
switch (shell_exec_result.return_code) {
|
||||
default: {
|
||||
lib_plankton.log.notice(
|
||||
lib_plankton.translate.get("check_kind_script_invalid_return_code"),
|
||||
lib_plankton.translate.get("checks.script.invalid_return_code"),
|
||||
{
|
||||
"return_code": result.return_code,
|
||||
"return_code": shell_exec_result.return_code,
|
||||
}
|
||||
);
|
||||
condition = _heimdall.enum_condition.unknown;
|
||||
|
|
@ -117,10 +106,11 @@ namespace _heimdall.check_kinds.script
|
|||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
"condition": condition,
|
||||
"info": {
|
||||
"result": result,
|
||||
"result": shell_exec_result,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,8 +33,14 @@ namespace _heimdall.helpers.json_schema
|
|||
}
|
||||
&
|
||||
(
|
||||
{
|
||||
type : "any";
|
||||
default ?: any;
|
||||
}
|
||||
|
|
||||
{
|
||||
type : "null";
|
||||
default ?: null;
|
||||
}
|
||||
|
|
||||
{
|
||||
|
|
@ -97,11 +103,13 @@ namespace _heimdall.helpers.json_schema
|
|||
}
|
||||
|
|
||||
{
|
||||
anyOf ?: Array<type_schema>;
|
||||
anyOf : Array<type_schema>;
|
||||
default ?: any;
|
||||
}
|
||||
|
|
||||
{
|
||||
allOf ?: Array<type_schema>;
|
||||
allOf : Array<type_schema>;
|
||||
default ?: any;
|
||||
}
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -29,4 +29,59 @@ namespace _heimdall.helpers.misc
|
|||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_shell_exec_result = {return_code : int; stdout : string; stderr : string;};
|
||||
|
||||
|
||||
/**
|
||||
* @see https://nodejs.org/api/child_process.html#child_processspawncommand-args-options
|
||||
*/
|
||||
export function shell_exec(
|
||||
head : string,
|
||||
args : Array<string>
|
||||
) : Promise<type_shell_exec_result>
|
||||
{
|
||||
const nm_child_process = require("child_process");
|
||||
|
||||
return new Promise<type_shell_exec_result>(
|
||||
(resolve, reject) => {
|
||||
let stdout : string = "";
|
||||
let stderr : string = "";
|
||||
const proc = nm_child_process.spawn(
|
||||
head,
|
||||
args,
|
||||
{
|
||||
},
|
||||
);
|
||||
proc.stdout.on(
|
||||
"data",
|
||||
(data) => {
|
||||
stdout += data;
|
||||
}
|
||||
);
|
||||
proc.stderr.on(
|
||||
"data",
|
||||
(data) => {
|
||||
stderr += data;
|
||||
}
|
||||
);
|
||||
proc.on(
|
||||
"close",
|
||||
(return_code) => {
|
||||
resolve(
|
||||
{
|
||||
"return_code": return_code,
|
||||
"stdout": stdout,
|
||||
"stderr": stderr,
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ async function main(
|
|||
{
|
||||
// consts
|
||||
const version : string = "0.8";
|
||||
const workdir : string = __dirname;
|
||||
|
||||
// init translations
|
||||
// TODO: use env language
|
||||
|
|
@ -12,8 +13,8 @@ async function main(
|
|||
"order": ["de", "en"],
|
||||
"packages": await Promise.all(
|
||||
[
|
||||
{"identifier": "de", "path": "localization/de.json"},
|
||||
{"identifier": "en", "path": "localization/en.json"},
|
||||
{"identifier": "de", "path": (workdir + "/localization/de.json")},
|
||||
{"identifier": "en", "path": (workdir + "/localization/en.json")},
|
||||
]
|
||||
.map(
|
||||
(entry) => (
|
||||
|
|
@ -65,7 +66,7 @@ async function main(
|
|||
"default": false,
|
||||
"parameters": {
|
||||
"indicators_long": ["version"],
|
||||
"indicators_short": ["v"],
|
||||
"indicators_short": ["r"],
|
||||
},
|
||||
"info": lib_plankton.translate.get("help.args.show_version"),
|
||||
}
|
||||
|
|
@ -243,6 +244,8 @@ async function main(
|
|||
else {
|
||||
const notification_kind_implementations : Record<string, _heimdall.notification_kinds.type_notification_kind> = {
|
||||
"console": _heimdall.notification_kinds.console.notification_kind_implementation(),
|
||||
"email": _heimdall.notification_kinds.email.notification_kind_implementation(),
|
||||
"libnotify": _heimdall.notification_kinds.libnotify.notification_kind_implementation(),
|
||||
};
|
||||
const check_kind_implementations : Record<string, _heimdall.check_kinds.type_check_kind> = {
|
||||
"script": _heimdall.check_kinds.script.check_kind_implementation(),
|
||||
|
|
@ -341,8 +344,7 @@ async function main(
|
|||
// create mutex file
|
||||
await lib_plankton.file.write(args["mutex_path"], "");
|
||||
|
||||
order.checks.forEach(
|
||||
async (check) => {
|
||||
for await (const check of order.checks) {
|
||||
if (! check.active) {
|
||||
// do nothing
|
||||
}
|
||||
|
|
@ -542,7 +544,6 @@ async function main(
|
|||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// drop mutex file
|
||||
await (
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ namespace _heimdall.notification_kinds.console
|
|||
lib_plankton.string.coin(
|
||||
"[{{title}}] <{{condition}}> {{info}}\n",
|
||||
{
|
||||
"title": check.title,
|
||||
"title": check.name,
|
||||
"condition": _heimdall.condition_show(state.condition),
|
||||
"info": lib_plankton.json.encode(info, true),
|
||||
}
|
||||
|
|
|
|||
155
source/logic/notification_kinds/email.ts
Normal file
155
source/logic/notification_kinds/email.ts
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
namespace _heimdall.notification_kinds.email
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
function parameters_schema(
|
||||
) : _heimdall.helpers.json_schema.type_schema
|
||||
{
|
||||
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"
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function normalize_order_node(
|
||||
node : any
|
||||
) : any
|
||||
{
|
||||
return Object.assign(
|
||||
{
|
||||
},
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
async function notify(
|
||||
parameters : any,
|
||||
name : string,
|
||||
check : type_check,
|
||||
state : type_item_state,
|
||||
info : any
|
||||
) : Promise<void>
|
||||
{
|
||||
const nm_nodemailer = require("nodemailer");
|
||||
|
||||
// datetime = _datetime.datetime.utcnow()
|
||||
const now : Date = new Date(Date.now());
|
||||
|
||||
const transporter = nm_nodemailer.createTransport(
|
||||
{
|
||||
"host": parameters.access.host,
|
||||
"port": parameters.access.port,
|
||||
"secure": true,
|
||||
"auth": {
|
||||
"user": parameters.access.username,
|
||||
"pass": parameters.access.password,
|
||||
}
|
||||
}
|
||||
);
|
||||
const send_result = await transporter.sendMail(
|
||||
{
|
||||
"from": parameters.sender,
|
||||
"to": parameters.receivers.join(","),
|
||||
"subject": lib_plankton.string.coin(
|
||||
"{{tags}} {{title}}",
|
||||
{
|
||||
"tags": (
|
||||
parameters.tags
|
||||
.concat([_heimdall.condition_show(state.condition)])
|
||||
.map(tag => lib_plankton.string.coin("[{{tag}}]", tag.toUpperCase()))
|
||||
.join(" ")
|
||||
),
|
||||
"title": check.name,
|
||||
}
|
||||
),
|
||||
"text": JSON.stringify(info, undefined, "\t"),
|
||||
"headers": {
|
||||
"Date": lib_plankton.string.coin(
|
||||
"{{day_of_week}}, {{day_of_month}} {{month}} {{year}} {{hour}}:{{minute}}:{{second}} {{time_offset}}",
|
||||
{
|
||||
"day_of_week": now.getDay().toFixed(0),
|
||||
"day_of_month": now.getDate().toFixed(0),
|
||||
"month": (now.getMonth() + 1).toFixed(0),
|
||||
"year": now.getFullYear().toFixed(0),
|
||||
"hour": now.getHours().toFixed(0),
|
||||
"minute": now.getMinutes().toFixed(0),
|
||||
"second": now.getSeconds().toFixed(0),
|
||||
// "time_offset": (now.getTimezoneOffset() / 60).toFixed(0),
|
||||
"time_offset": "+0000",
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
);
|
||||
return Promise.resolve<void>(undefined);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function notification_kind_implementation(
|
||||
) : type_notification_kind
|
||||
{
|
||||
return {
|
||||
"parameters_schema": parameters_schema,
|
||||
"normalize_order_node": normalize_order_node,
|
||||
"notify": notify,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
127
source/logic/notification_kinds/libnotify.ts
Normal file
127
source/logic/notification_kinds/libnotify.ts
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
namespace _heimdall.notification_kinds.libnotify
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
function parameters_schema(
|
||||
) : _heimdall.helpers.json_schema.type_schema
|
||||
{
|
||||
return {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"icon": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function normalize_order_node(
|
||||
node : any
|
||||
) : any
|
||||
{
|
||||
return Object.assign(
|
||||
{
|
||||
"icon": "/usr/local/share/icons/heimdall.png",
|
||||
},
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
async function notify(
|
||||
parameters : any,
|
||||
name : string,
|
||||
check : type_check,
|
||||
state : type_item_state,
|
||||
info : any
|
||||
) : Promise<void>
|
||||
{
|
||||
const condition_translate = function (condition : _heimdall.enum_condition): string
|
||||
{
|
||||
switch (condition) {
|
||||
case _heimdall.enum_condition.unknown: return "normal";
|
||||
case _heimdall.enum_condition.ok: return "low";
|
||||
case _heimdall.enum_condition.concerning: return "normal";
|
||||
case _heimdall.enum_condition.critical: return "critical";
|
||||
}
|
||||
};
|
||||
|
||||
let parts : Array<string> = [];
|
||||
parts.push(
|
||||
"notify-send"
|
||||
);
|
||||
// app name
|
||||
parts.push(
|
||||
lib_plankton.string.coin(
|
||||
"--app-name={{app_name}}",
|
||||
{
|
||||
"app_name": "heimdall",
|
||||
}
|
||||
)
|
||||
)
|
||||
// urgency
|
||||
parts.push(
|
||||
lib_plankton.string.coin(
|
||||
"--urgency={{urgency}}",
|
||||
{
|
||||
"urgency": condition_translate(state.condition),
|
||||
}
|
||||
)
|
||||
)
|
||||
// icon
|
||||
if (! ("icon" in parameters)) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
parts.push(
|
||||
lib_plankton.string.coin(
|
||||
"--icon={{icon}}",
|
||||
{
|
||||
"icon": parameters.icon,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
// summary
|
||||
parts.push(
|
||||
lib_plankton.string.coin(
|
||||
"{{condition}} | {{title}}",
|
||||
{
|
||||
"title": check.name,
|
||||
"condition": _heimdall.condition_show(state.condition).toUpperCase(),
|
||||
}
|
||||
)
|
||||
);
|
||||
// body
|
||||
parts.push(
|
||||
JSON.stringify(info)
|
||||
);
|
||||
|
||||
await _heimdall.helpers.misc.shell_exec(parts[0], parts.slice(1));
|
||||
|
||||
return Promise.resolve<void>(undefined);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function notification_kind_implementation(
|
||||
) : type_notification_kind
|
||||
{
|
||||
return {
|
||||
"parameters_schema": parameters_schema,
|
||||
"normalize_order_node": normalize_order_node,
|
||||
"notify": notify,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -219,6 +219,7 @@ namespace _heimdall.order
|
|||
},
|
||||
"parameters": value.parameters_schema(),
|
||||
"custom": {
|
||||
"type": "any",
|
||||
"description": "custom data, which shall be attached to notifications",
|
||||
"default": null,
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue