Add an informix_transactions plugin version that works for CheckMK v2.4.

This commit is contained in:
Marsell Kukuljevic 2026-03-08 19:37:51 +01:00
parent 0c882e643c
commit fed4e940c0
4 changed files with 450 additions and 0 deletions

Binary file not shown.

View File

@ -0,0 +1,220 @@
# Copyright 2026 Spearhead Systems SRL
# Copyright (C) 2019 tribe29 GmbH
#
# Expected example input (and expect arbitrary blank lines):
#
# <<<informix_transactions>>>
# [[[1/2]]]
# a40dbb0 6 U---C-L 6 1:3263 500 372 74.40
#
# a6d8028 A---- a695028 0 - - COMMIT - 0
# a6d8348 A---- a695878 0 - - COMMIT - 0
# a6d8668 A---- a6960c8 0 - - COMMIT - 0
# a6d8988 A---- a696918 0 - - COMMIT - 0
# a6d8fc8 A---- a698208 0 - - COMMIT - 0
# a6d92e8 A---- a6979b8 0 - - COMMIT - 0
# a6d9608 A---- a698a58 0 - - COMMIT - 0
# a6d9928 A---- a6992a8 1 - - DIRTY - 0
# a6d9c48 A---- a6992a8 0 - - NOTRANS - 0
# a6d9f68 A---- a69a348 0 - - COMMIT - 0
# a6da288 A---- a69ab98 0 - - COMMIT - 0
# a6da5a8 A---- a69b3e8 0 - - COMMIT - 0
# a6da8c8 A---- a69bc38 0 - - COMMIT - 0
# a6dabe8 A---- a69c488 0 - - COMMIT - 0
# a6daf08 A---- a699af8 0 - - COMMIT - 0
# a6db228 A---- a6992a8 0 - - COMMIT - 0
# a6db548 A---- a69ccd8 1 - - DIRTY - 0
# a6db868 A---- a69d528 1 - - DIRTY - 0
# a6dbb88 A---- a69ccd8 0 - - COMMIT - 0
# a6dbea8 A---- a69dd78 0 - - COMMIT - 0
# a6dc1c8 A---- a69e5c8 0 - - COMMIT - 0
# a6dc4e8 A-B-- a69ee18 502 33:0x25018 34:0x486fc COMMIT 0
import json
from cmk.agent_based.v2 import (
Result,
Service,
Metric,
State,
CheckPlugin,
AgentSection,
)
# This is what the columns from the onstat -x output represent:
#
# line[0]= first line represents current log ID
# line[1]= <ignore>
# line[2]= sessionID
# line[3]= number of locks kept by sessionID
# line[4]= begin log position
# line[5]= current log position
def parse_informix_transactions(string_table):
instance = string_table[0][0][3:-3]
curr_log = ""
sessions = []
for line in string_table[1:]:
if len(line) <= 5:
continue
if "C" in line[2]:
curr_log = int(line[3])
continue
if ":" in line[4]:
line[4] = int(line[4].split(":")[0])
else:
line[4] = None
if ":" in line[5]:
line[5] = int(line[5].split(":")[0])
else:
line[5] = None
sessions.append({
"id": line[2],
"locks": int(line[3]),
"log_start": line[4],
"log_end": line[5],
})
return {
instance: {
"curr_log": curr_log,
"sessions": sessions,
}
}
def discover_informix_transactions(section):
for instance in section:
yield Service(item=instance)
def check_informix_locks(item, params, section):
data = section.get(item)
if not data:
return
levels = params.get("levels")
infotext = ""
state = State.OK
max_locks = 0
if levels and levels[0] == "fixed":
warn, crit = levels[1]
else:
warn, crit = None, None
for session in data["sessions"]:
id = session["id"]
locks = session["locks"]
log_start = session["log_start"]
max_locks = max(max_locks, locks)
if log_start == "-":
continue
if crit and locks >= crit:
state = State.CRIT
infotext += f"Session with ID {id} has {locks} locks; "
elif warn and locks >= warn:
state = State.WARN
infotext += f"Session with ID {id} has {locks} locks; "
if state != State.OK:
infotext += f"(warn/crit at {warn}/{crit})"
else:
infotext = "There are no sessions with a high number of locks"
yield Metric(name="MaximumLocksInSession", value=max_locks)
yield Result(state=state, summary=infotext)
def check_informix_activity(item, params, section):
data = section.get(item)
if not data:
return
state = State.OK
infotext = ""
curr_log = data["curr_log"]
for session in data["sessions"]:
id = session["id"]
log_start = session["log_start"]
if log_start is None:
continue
if log_start < curr_log:
state = State.CRIT
infotext += f"Session {id} doesn't have activity in current log; "
if state == State.OK:
infotext = "There are no inactive sessions in current log"
yield Result(state=state, summary=infotext)
def check_informix_long_transactions(item, params, section):
data = section.get(item)
if not data:
return
state = State.OK
infotext = ""
for session in data["sessions"]:
id = session["id"]
log_start = session["log_start"]
log_end = session["log_end"]
if log_start is None or log_end is None:
continue
if log_end - log_start > 2:
state = State.CRIT
infotext += f"Session {id} is using more than 3 logical logs; "
if state == State.OK:
infotext = "There are no long-running transactions"
yield Result(state=state, summary=infotext)
agent_section_informix_transactions = AgentSection(
name = "informix_transactions",
parse_function = parse_informix_transactions,
)
check_plugin_informix_session_locks = CheckPlugin(
name = "informix_session_locks",
check_ruleset_name = "informix_session_locks",
service_name = "Informix session locks %s",
sections = ["informix_transactions"],
discovery_function = discover_informix_transactions,
check_function = check_informix_locks,
check_default_parameters = {},
)
check_plugin_informix_session_activity = CheckPlugin(
name = "informix_session_activity",
service_name = "Informix session activity %s",
sections = ["informix_transactions"],
discovery_function = discover_informix_transactions,
check_function = check_informix_activity,
check_default_parameters = {},
)
check_plugin_informix_session_long_transactions = CheckPlugin(
name = "informix_session_long_transactions",
service_name = "Informix session long transactions %s",
sections = ["informix_transactions"],
discovery_function = discover_informix_transactions,
check_function = check_informix_long_transactions,
check_default_parameters = {},
)

View File

@ -0,0 +1,46 @@
# Copyright 2026 Spearhead Systems SRL
from cmk.rulesets.v1.form_specs import (
Dictionary,
DictElement,
Integer,
DefaultValue,
LevelDirection,
SimpleLevels,
)
from cmk.rulesets.v1.rule_specs import (
CheckParameters,
HostAndItemCondition,
Topic,
Title,
Help,
)
def _valuespec_informix_session_locks():
return Dictionary(
elements = {
"levels": DictElement(
parameter_form = SimpleLevels(
title = Title("Session locks"),
help_text = Help(
"You can set a limit to the number of locks for a "
"session in Informix Database application"
),
level_direction = LevelDirection.UPPER,
form_spec_template = Integer(title = Title("locks")),
prefill_fixed_levels = DefaultValue(value=(40, 70))
)
),
},
)
rule_spec_informix_session_locks = CheckParameters(
title = Title("Informix Session Locks"),
name = "informix_session_locks",
topic = Topic.APPLICATIONS,
parameter_form = _valuespec_informix_session_locks,
condition = HostAndItemCondition(
item_title = Title("DBMS"),
),
)

View File

@ -0,0 +1,184 @@
#!/bin/bash
# Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.
# Reason for this no-op: shellcheck disable=... before the first command disables the error for the
# entire script.
:
# Disable unused variable error (needed to keep track of version)
# shellcheck disable=SC2034
CMK_VERSION="2.4.0"
# Informix
# Make ENV-VARs avail for subshells
set -a
# .--helper--------------------------------------------------------------.
# | _ _ |
# | | |__ ___| |_ __ ___ _ __ |
# | | '_ \ / _ \ | '_ \ / _ \ '__| |
# | | | | | __/ | |_) | __/ | |
# | |_| |_|\___|_| .__/ \___|_| |
# | |_| |
# '----------------------------------------------------------------------'
# BEGIN COMMON PLUGIN CODE
# check that no users other than root can change the file
only_root_can_modify() {
permissions=$1
owner=$2
group=$3
group_write_perm=$(echo "$permissions" | cut -c 6)
other_write_perm=$(echo "$permissions" | cut -c 9)
if [ "$owner" != "root" ] || [ "$other_write_perm" != "-" ]; then
return 1
fi
[ "$group" = "root" ] || [ "$group_write_perm" = "-" ]
}
get_binary_owner() {
BINARY_PATH=$1
stat -c '%U' "${BINARY_PATH}"
}
get_binary_execution_mode() {
BINARY_PATH=$1
BINARY_USER=$2
# if the executable belongs to someone besides root, do not execute it as root
if needs_user_switch_before_executing "$BINARY_PATH"; then
echo "su ${BINARY_USER} -c"
return
fi
echo "bash -c"
}
needs_user_switch_before_executing() {
BINARY_PATH=$1
[ "$(whoami)" = "root" ] && ! only_root_can_modify "$(stat -c '%A' "$BINARY_PATH")" "$(stat -c '%U' "$BINARY_PATH")" "$(stat -c '%G' "$BINARY_PATH")"
}
# END COMMON PLUGIN CODE
set_env() {
# set environment variables given in the form VARNAME1=value1;VARNAME2=value2;...
while IFS=';' read -ra parts; do
for part in "${parts[@]}"; do
var_name="${part%%=*}"
var_value="${part#*=}"
export "$var_name"="$var_value"
done
done <<<"$1"
}
#.
# .--sqls----------------------------------------------------------------.
# | _ |
# | ___ __ _| |___ |
# | / __|/ _` | / __| |
# | \__ \ (_| | \__ \ |
# | |___/\__, |_|___/ |
# | |_| |
# '----------------------------------------------------------------------'
informix_transactions() {
echo "<<<informix_transactions>>>"
echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
$EXECUTION_MODE "\"$INFORMIXDIR\"/bin/onstat -l" | grep C | tail -n 1
$EXECUTION_MODE "\"$INFORMIXDIR\"/bin/onstat -x" | egrep -v 'IBM|maximum|Transactions|est.|userthread|logpos' | sed 's/:[^:]*$//'
}
#.
# .--config--------------------------------------------------------------.
# | __ _ |
# | ___ ___ _ __ / _(_) __ _ |
# | / __/ _ \| '_ \| |_| |/ _` | |
# | | (_| (_) | | | | _| | (_| | |
# | \___\___/|_| |_|_| |_|\__, | |
# | |___/ |
# '----------------------------------------------------------------------'
# Config opts:
# - oninit-path; Default is empty, which means autodetection:
# ONINIT_PATH=<path to oninit-binary>
# - Excluding sections ("status sessions locks tabextents dbspaces logusage"):
# EXCLUDES="SECTION SECTION ..."
# EXCLUDES=ALL
# shellcheck source=agents/cfg_examples/informix.cfg
. "$MK_CONFDIR/informix.cfg" 2>/dev/null
if [ -z "$ONINIT_PATH" ] || [ ! -x "$ONINIT_PATH" ]; then
ONINIT=$(UNIX95=true ps ax | grep oninit | grep -v grep | head -1 | awk '{print $1 " " $5}')
if [ -z "$ONINIT" ]; then
exit 0
fi
ONINIT_PATH=${ONINIT#* }
ONINIT_PID=${ONINIT% *}
case "$ONINIT_PATH" in
/*) ;;
*) # BUG not platform independent!
ONINIT_PATH=$(readlink "/proc/$ONINIT_PID/exe")
;;
esac
# If not set in config or not found we end up here
if [ -z "$ONINIT_PATH" ] || [ ! -f "$ONINIT_PATH" ]; then
exit 1
fi
fi
#.
# .--main----------------------------------------------------------------.
# | _ |
# | _ __ ___ __ _(_)_ __ |
# | | '_ ` _ \ / _` | | '_ \ |
# | | | | | | | (_| | | | | | |
# | |_| |_| |_|\__,_|_|_| |_| |
# | |
# '----------------------------------------------------------------------'
INFORMIXDIR=${ONINIT_PATH%/bin*}
if [ ! -f "$INFORMIXDIR/bin/onstat" ]; then
exit 1
fi
EXECUTION_MODE="$(get_binary_execution_mode "$INFORMIXDIR/bin/onstat" "$(get_binary_owner "$INFORMIXDIR/bin/onstat")")"
for IDSENV in $(
$EXECUTION_MODE "$INFORMIXDIR/bin/onstat -g dis" |
grep -E '^Server[ ]*:|^Server Number[ ]*:|^INFORMIX|^SQLHOSTS|^ONCONFIG' |
sed -e 's/Server Number/SERVERNUM/' \
-e 's/Server/INFORMIXSERVER/' \
-e 's/SQLHOSTS/INFORMIXSQLHOSTS/' \
-e 's/[ ]*:[ ]*/=/' |
tr '\n' ';' |
sed -e 's/;$/\n/' -e 's/;\(INFORMIXSERVER=[^;]*;\)/\n\1/g'
); do
(
set_env "$IDSENV"
# try to set them via 'onstat -g env' otherwise
# DB HAS TO BE RUNNING
if [ -z "$INFORMIXSQLHOSTS" ] || [ -z "$ONCONFIG" ]; then
$EXECUTION_MODE "$INFORMIXDIR/bin/onstat -g env" | grep -E -e '^INFORMIXSQLHOSTS' \
-e '^ONCONFIG' |
sed -e 's/[ ][ ]*/=/'
fi
informix_transactions
)
done