From 387b103dccf9fbbe425c0cd0b9e622db655dd023 Mon Sep 17 00:00:00 2001 From: Marsell Kukuljevic Date: Fri, 6 Feb 2026 12:45:40 +0100 Subject: [PATCH] Switch graylog input metrics plugin to support <= instead of < --- .../2.3/graylog_input_metrics-0.4.0.mkp | Bin 0 -> 6396 bytes .../check_parameters/graylog_input_metrics.py | 2 +- .../agent_graylog_input_metrics.py | 0 .../special/agent_graylog_input_metrics | 0 .../checks/agent_graylog_input_metrics | 0 .../check_mk/checks/graylog_input_metrics | 75 +++++++++++++++++- .../web/plugins/wato/graylog_input_metrics.py | 0 .../graylog_input_metrics-0.0.3.mkp | Bin 5739 -> 0 bytes 8 files changed, 75 insertions(+), 2 deletions(-) create mode 100755 graylog-metrics/2.3/graylog_input_metrics-0.4.0.mkp rename graylog-metrics/{ => 2.3}/local/lib/python3/cmk/gui/plugins/wato/check_parameters/graylog_input_metrics.py (98%) rename graylog-metrics/{ => 2.3}/local/lib/python3/cmk/special_agents/agent_graylog_input_metrics.py (100%) rename graylog-metrics/{ => 2.3}/local/share/check_mk/agents/special/agent_graylog_input_metrics (100%) rename graylog-metrics/{ => 2.3}/local/share/check_mk/checks/agent_graylog_input_metrics (100%) rename graylog-metrics/{ => 2.3}/local/share/check_mk/checks/graylog_input_metrics (56%) rename graylog-metrics/{ => 2.3}/local/share/check_mk/web/plugins/wato/graylog_input_metrics.py (100%) delete mode 100644 graylog-metrics/graylog_input_metrics-0.0.3.mkp diff --git a/graylog-metrics/2.3/graylog_input_metrics-0.4.0.mkp b/graylog-metrics/2.3/graylog_input_metrics-0.4.0.mkp new file mode 100755 index 0000000000000000000000000000000000000000..3a5b238d1fa3885c1bbbc9ac564f793048522c93 GIT binary patch literal 6396 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 Bpk)96 literal 0 HcmV?d00001 diff --git a/graylog-metrics/local/lib/python3/cmk/gui/plugins/wato/check_parameters/graylog_input_metrics.py b/graylog-metrics/2.3/local/lib/python3/cmk/gui/plugins/wato/check_parameters/graylog_input_metrics.py similarity index 98% rename from graylog-metrics/local/lib/python3/cmk/gui/plugins/wato/check_parameters/graylog_input_metrics.py rename to graylog-metrics/2.3/local/lib/python3/cmk/gui/plugins/wato/check_parameters/graylog_input_metrics.py index 89e9057..a92ebe5 100644 --- a/graylog-metrics/local/lib/python3/cmk/gui/plugins/wato/check_parameters/graylog_input_metrics.py +++ b/graylog-metrics/2.3/local/lib/python3/cmk/gui/plugins/wato/check_parameters/graylog_input_metrics.py @@ -21,7 +21,7 @@ mappings={ "im": "messages", "rs": "bytes", "upper": "above", - "lower": "below", + "lower": "below or equal", } diff --git a/graylog-metrics/local/lib/python3/cmk/special_agents/agent_graylog_input_metrics.py b/graylog-metrics/2.3/local/lib/python3/cmk/special_agents/agent_graylog_input_metrics.py similarity index 100% rename from graylog-metrics/local/lib/python3/cmk/special_agents/agent_graylog_input_metrics.py rename to graylog-metrics/2.3/local/lib/python3/cmk/special_agents/agent_graylog_input_metrics.py diff --git a/graylog-metrics/local/share/check_mk/agents/special/agent_graylog_input_metrics b/graylog-metrics/2.3/local/share/check_mk/agents/special/agent_graylog_input_metrics similarity index 100% rename from graylog-metrics/local/share/check_mk/agents/special/agent_graylog_input_metrics rename to graylog-metrics/2.3/local/share/check_mk/agents/special/agent_graylog_input_metrics diff --git a/graylog-metrics/local/share/check_mk/checks/agent_graylog_input_metrics b/graylog-metrics/2.3/local/share/check_mk/checks/agent_graylog_input_metrics similarity index 100% rename from graylog-metrics/local/share/check_mk/checks/agent_graylog_input_metrics rename to graylog-metrics/2.3/local/share/check_mk/checks/agent_graylog_input_metrics diff --git a/graylog-metrics/local/share/check_mk/checks/graylog_input_metrics b/graylog-metrics/2.3/local/share/check_mk/checks/graylog_input_metrics similarity index 56% rename from graylog-metrics/local/share/check_mk/checks/graylog_input_metrics rename to graylog-metrics/2.3/local/share/check_mk/checks/graylog_input_metrics index 9c3483a..21dafd1 100644 --- a/graylog-metrics/local/share/check_mk/checks/graylog_input_metrics +++ b/graylog-metrics/2.3/local/share/check_mk/checks/graylog_input_metrics @@ -7,7 +7,7 @@ # resolving legacy discovery results such as [("SUMMARY", "diskstat_default_levels")]. Furthermore, # it might also remove variables needed for accessing discovery rulesets. import json -from cmk.base.check_api import check_levels, LegacyCheckDefinition +from cmk.base.check_api import LegacyCheckDefinition from cmk.base.config import check_info from cmk.agent_based.v2 import Service @@ -74,6 +74,79 @@ def check_graylog_input_metrics(item, params, parsed): infoname=infotext ) + +# A customer wanted us to support <= for "below", not <. This meant copying +# check_levels() and child functions wholesale out of +# lib/python3/cmk/base/check_api.py just to change < to <= in this function. +# Sometimes a sledgehammer is what it takes... D: +# Start copy =================================================================== +from cmk.agent_based import v1 as _v1 + +def _do_check_levels(value, levels, human_readable_func): + warn_upper, crit_upper, warn_lower, crit_lower = levels + + # Critical cases + if crit_upper is not None and value >= crit_upper: + return 2, _levelsinfo_ty("at", warn_upper, crit_upper, human_readable_func) + if crit_lower is not None and value <= crit_lower: + return 2, _levelsinfo_ty("below", warn_lower, crit_lower, human_readable_func) + + # Warning cases + if warn_upper is not None and value >= warn_upper: + return 1, _levelsinfo_ty("at", warn_upper, crit_upper, human_readable_func) + if warn_lower is not None and value <= warn_lower: + return 1, _levelsinfo_ty("below", warn_lower, crit_lower, human_readable_func) + return 0, "" + +def _levelsinfo_ty(ty, warn, crit, human_readable_func): + warn_str = "never" if warn is None else f"{human_readable_func(warn)}" + crit_str = "never" if crit is None else f"{human_readable_func(crit)}" + return f" (warn/crit {ty} {warn_str}/{crit_str})" + +def _build_perfdata(dsname, value, levels, boundaries): + used_boundaries = boundaries if isinstance(boundaries, tuple) and len(boundaries) == 2 else () + return [(dsname, value, levels[0], levels[1], *used_boundaries)] + +def check_levels(value, dsname, params, unit, human_readable_func, infoname, boundaries=None): + def render_func(x): + return "%s%s" % (human_readable_func(x), unit) + + if params and isinstance(params, dict): + result, *metrics = _v1.check_levels_predictive( + value, + levels=params, + metric_name=dsname, + render_func=render_func, + label=infoname, + boundaries=boundaries, + ) + assert isinstance(result, _v1.Result) + return ( + int(result.state), + result.summary, + [ + (m.name, m.value, *m.levels, *m.boundaries) + for m in metrics + if isinstance(m, _v1.Metric) + ], + ) + + infotext = f"{render_func(value)}" + if infoname: + infotext = f"{infoname}: {infotext}" + + levels = params + + state, levelstext = _do_check_levels(value, levels, render_func) + state, levelstext = _do_check_levels(value, levels, render_func) + return ( + state, + infotext + levelstext, + _build_perfdata(dsname, value, levels, boundaries), + ) +# End copy ==================================================================== + + check_info["graylog_input_metrics"] = LegacyCheckDefinition( parse_function = parse_graylog_input_metrics, check_function = check_graylog_input_metrics, diff --git a/graylog-metrics/local/share/check_mk/web/plugins/wato/graylog_input_metrics.py b/graylog-metrics/2.3/local/share/check_mk/web/plugins/wato/graylog_input_metrics.py similarity index 100% rename from graylog-metrics/local/share/check_mk/web/plugins/wato/graylog_input_metrics.py rename to graylog-metrics/2.3/local/share/check_mk/web/plugins/wato/graylog_input_metrics.py diff --git a/graylog-metrics/graylog_input_metrics-0.0.3.mkp b/graylog-metrics/graylog_input_metrics-0.0.3.mkp deleted file mode 100644 index f38c5ba97847b9fbdf426fc846d6bc614f0b1122..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5739 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 Nd`wDtbN=Sd{{c-iMUDUf