From 72dbad096c6e81e92158128b7ab7f4f885d7e463 Mon Sep 17 00:00:00 2001 From: OMD site s6 Date: Mon, 21 Feb 2022 21:14:19 +0100 Subject: [PATCH] Add initial checkmk plugin --- README.md | 32 +++++ check_mk_plugin/agent_based/jobwatch_check.py | 125 ++++++++++++++++++ check_mk_plugin/collect.sh | 22 +++ check_mk_plugin/jobwatch-1.0.mkp | Bin 0 -> 1853 bytes 4 files changed, 179 insertions(+) create mode 100644 README.md create mode 100755 check_mk_plugin/agent_based/jobwatch_check.py create mode 100644 check_mk_plugin/collect.sh create mode 100644 check_mk_plugin/jobwatch-1.0.mkp diff --git a/README.md b/README.md new file mode 100644 index 0000000..7f55c80 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# Jobwatch + +## (C) 2022, Michael Höß, MIT-Licensed + +### What is jobwatch + +If you just want to execute a simple shell-script via cron every couple of hour, +often only a few lines of code are required. + +But if you want to ensure the script really run, in the required interval, completed +without error, you have to add a lot of plumping code. + +jobwatch helps to migitate this problem. It wraps the execution of your script, and does +all the rest for you, by providing output, which can be fed to CheckMK then +Agent-Plugin-Mechanism. + +jobwatch +- checks for required exit-codes +- searches through the output of your script via regex to classify certain + keywords as WARN or CRIT +- let you define the required run-interval + +Simply add a .job-file into /etc/jobwatch.d where you defined all of this, +call you script via jobwatch -j job in the crontab and you are done. + +TBD: +- document deployment +- document job-file (see included sample) +- feed logoutput to CheckMK +- redirect script-output to a logfile via Config +- + diff --git a/check_mk_plugin/agent_based/jobwatch_check.py b/check_mk_plugin/agent_based/jobwatch_check.py new file mode 100755 index 0000000..123fff1 --- /dev/null +++ b/check_mk_plugin/agent_based/jobwatch_check.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + + +import json +from operator import concat +from typing import Any, Dict, Generator, Union +from cmk.base.api.agent_based.checking_classes import IgnoreResults, IgnoreResultsError +from cmk.base.api.agent_based.type_defs import Parameters + +from typing import Any, Dict, Generator, Iterable +from cmk.base.api.agent_based.type_defs import Parameters, StringTable +from cmk.base.plugins.agent_based.agent_based_api.v1 import ( + register, + HostLabel +) +from cmk.base.plugins.agent_based.agent_based_api.v1 import ( + register, + render, + Result, Metric, State, + Service, ServiceLabel, + check_levels, + get_value_store, +) + + +# +# Section Plugin +# + + +def parse_jobwatch(string_table: StringTable) -> Dict: + #print("Agent-PARSE", string_table) + js = "" + for ln in string_table: + js += "".join(ln) + + data = json.loads(js) + + res = {} + for user in data: + uid = user.get("user_id") + if not uid: + continue + + entries = {} + for e in user.get("entries", []): + state = e.get("state") + if not state: + continue + + job_full_id = state.get("job_id") + if not job_full_id: + continue + + ji = state.get("job_inst_id") + if ji: + job_full_id += "_" +ji + + entries[job_full_id] = { + "state": state, + "log_entries": state.get("log_entries", []) + } + res[uid] = entries + + #print(res) + return res # return a parsed section + + +register.agent_section( + name="jobwatch", + parse_function=parse_jobwatch, +) + + + +# +# Check Plugin +# + +def discover_jobwatch(section: Any) -> Generator[Service, None, None]: + #print("D", section) + for uid, usr_data in section.items(): + for job_full_id, job_data in usr_data.items(): + state = job_data.get("state") + if not state: + continue + + item = uid + "/" + job_full_id + yield Service(item=item) + + + +def check_jobwatch(item: str, section: Any) -> Generator[Union[Result,Metric], None, None]: + uid, job_full_id = item.split("/", 2) + + state = section \ + .get(uid, {}) \ + .get(job_full_id, {}) \ + .get("state") + + if state: + yield Metric( + name="last_run", + value=state.get("last_run_age_seconds"), + ) + + yield Result( + state=State(state.get('last_run_age_state', 3)), + summary=f"last run at { state.get('last_run')}, {render.timespan(state.get('last_run_age_seconds'))} ago" + ) + + yield Result( + state=State(state.get('last_state', 3)), + summary=f"last exitcode { state.get('last_exit_code')}" + ) + +register.check_plugin( + name="jobwatch", + service_name="Job %s", + check_function=check_jobwatch, + discovery_function=discover_jobwatch, + + +) diff --git a/check_mk_plugin/collect.sh b/check_mk_plugin/collect.sh new file mode 100644 index 0000000..4270225 --- /dev/null +++ b/check_mk_plugin/collect.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +cd $SCRIPT_DIR + +files=( + "local/lib/check_mk/base/plugins/agent_based/jobwatch_check.py:agent_based" + "local/share/check_mk/web/plugins/metrics/jobwatch_metrics.py:web/plugins/metrics" +) + + +for f in ${files[*]}; do + s="$(echo $f | cut -d ":" -f 1)" + d="$(echo $f | cut -d ":" -f 2)" + src=$OMD_ROOT/$s + dest=$d + echo "$src -> $dest" + mkdir -p $dest + cp -L $src $dest +done + +mkp pack jobwatch \ No newline at end of file diff --git a/check_mk_plugin/jobwatch-1.0.mkp b/check_mk_plugin/jobwatch-1.0.mkp new file mode 100644 index 0000000000000000000000000000000000000000..bc43332fc2fa3c1fd7a6a30ad0033c3b97d7cf5b GIT binary patch literal 1853 zcma*hc{~#e0KjpC5Um`~axKaoF*mtZ?#G;!$uLH4a!12cBXYeWN9Kc%-ke)R%^dTX zjpllC=gzq;cgwl=_xtbt_4|DP`+>6f_}=b_Ah_5;;fOe&Xy3ia1~=S7e2f&o<7^&4VZ5cSM7)+;^@17n?q6y~YUz>T7+L7o#OgAo%UNk4diT_lCsY zkPiQ}E2BA+*Bv{h3?*%12b(8-2r_Wt=c*+V35==xyCBbooZMi6gcwMrI{^!f3&4Z$yCkFK_;krz_xSp%jc;WqWNY!G7O~ z>fGU4i)rQjn)o)9q`l4Hnv$UlZkuzShU;=Fc|A2MO^M?^v8`ODJcrL`u1woASgqF^vb=N5%a1NvNm( zUBi9quCE_U>Ft{q13Q2iMW_M$l{B`0fqwS-^a6RwiqcY>69AEkO87FM6T-Xa7eUQ2 zF+jL$ia#+xw!o#Hp>@)_&FTj#{}Id;OtWySf9SNQGHyYZu(@x2-CK2xE-a|uuSK{# z^;T|-^CZ)5R>j-YX6bii-K4CuQ&FKD1N_?kowsuTo~v@>J92no6ZRM2(KzA0dDpM@ z+RQBw1Hz66uV+mxe)-6SnItu(rROe6j#wF9Rgu~9%;!RehU%E@Zg1qQ zb@8HtM3oT}JriJ6UV8MdmG`n;-c~+KuqWbRZyJEZ^z_ZPEJ}7rlGPWLd@z|uA$qO=sevJs2L;}xajI7Hu7Guk`N+l+g9Db1f3KwYmQOH7q?@p10zPE zjFdATUewqjTBFkpH9hiTDTCnX&bhvM*?tOcpT8!g^-(H$`ic0tTQ3T3pO~~O-}GE( zF+Aq|ppPctdVX#1O_Qb<;{cv^3^w1CsU{pU>MoV4{JC(*7PmPh5p}cyGYNxL2K#{? zF=$5UmU+7rCZB#i&+vK}09cWK>C`MtZy&+>Bi7jwUDp=WLkvgJWS5(!lNj?+TyQJDEM~aG$*q9^gIl9Hq+8g~Wyn_A5<|rCV4vcNp!{dNA5IjNiUU zPvf#p)Kz0UbE7>~o`feT8BkvLCnKrc8(A(EkN;O`Jp*(0)lmR*h1IuA3=D9`s zeVWHIcf2_GHvk*ZN^Y)Hv+Gy*{u_|?{LXYDk+r$=gJoLPEYik4yBSY9Tem7^cM;gv z$}k^Uro$9pZ0_)o!qqu;l0f_Z+3=vy-$*sc)ntXaL(=m@6dG+7gVL(D9H)xKRq=ka(T-8R!ADuJ6IdX03{Q<-eD e-T;cS>-^aE;(z!4r{Zl-`F(SpjX2^tIQ|7-ex^qN literal 0 HcmV?d00001