From 7799ea221662207f19a706ed0ba9a5cb6f615d0c Mon Sep 17 00:00:00 2001 From: Simon Harms Date: Thu, 16 Jul 2026 08:16:38 +0000 Subject: [PATCH] mk_sap: Add exclude_paths option to skip monitoring objects The monitor_paths option of the mk_sap agent plugin is a pure allow list. When using wildcard patterns there is no way to skip single unwanted monitoring objects below a matched subtree, e.g. the alert container of a client which only holds purely technical number range objects (like ALAUTOUID) that roll over by design and produce permanent noise once they pass their warning threshold. The only workarounds are to enumerate all wanted sibling nodes explicitly (losing auto-discovery of new nodes) or to disable the alert in the SAP system itself. Introduce an exclude_paths option which uses the same unix shell pattern syntax as monitor_paths. A path matching any exclude pattern is skipped, even if it matches one of the monitor_paths patterns. Exclude rules are always matched against the full path and are intentionally not shortened to the first two segments during toplevel matching: shortening a rule which targets a single subtree node would exclude the whole monitor. A rule matching the toplevel path itself still skips the whole monitor and saves the RFC calls for its tree. Since path segments are built from the 40 character SAP field MTNAMESHRT, long node names arrive truncated and patterns have to match the truncated name. This is documented in the sample config. The option defaults to an empty list, existing configurations are not affected. --- agents/sap/sap.cfg | 19 +++++++ cmk/plugins/sap/agents/mk_sap.py | 16 ++++++ .../cmk/plugins/sap/agents/test_mk_sap.py | 53 +++++++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/agents/sap/sap.cfg b/agents/sap/sap.cfg index 74501737e4a..19cfa093d6e 100644 --- a/agents/sap/sap.cfg +++ b/agents/sap/sap.cfg @@ -49,3 +49,22 @@ monitor_paths += [ #'SAP CCMS Monitor Templates/Operating System/OperatingSystem/CPU/CPU_Utilization', #'*', ] + +# A list of strings using the same unix shell pattern syntax as +# monitor_paths (see above). All monitoring objects whose path matches at +# least one of these patterns are excluded from monitoring, even if the +# path is matched by one of the monitor_paths patterns. This can be used +# to skip single objects below a monitor_paths wildcard, e.g. the alert +# container of a client which only holds purely technical number range +# objects rolling over by design. +# Anchoring the pattern to the affected subtree is recommended to avoid +# accidentally excluding objects in other monitors. +# Note: The path segments are built from the SAP field MTNAMESHRT, which +# is limited to 40 characters. Long node names are therefore truncated +# (e.g. "Client 000 Alert Messages for Number Ran") and patterns have to +# match the truncated name. To verify the exact paths, temporarily enable +# the debug output line in the plugin (search for 'node["PATH"]'). +exclude_paths += [ + #'SAP CCMS Technical Expert Monitors/All Monitoring Contexts/Critical Number Ranges/Critical Number Ranges All Clients/Client 000 Alert Messages for Number Ran*', + #'SAP CCMS Technical Expert Monitors/All Monitoring Contexts/Critical Number Ranges/*/Client 066*', +] diff --git a/cmk/plugins/sap/agents/mk_sap.py b/cmk/plugins/sap/agents/mk_sap.py index 54976fdd344..2fd17247f6b 100755 --- a/cmk/plugins/sap/agents/mk_sap.py +++ b/cmk/plugins/sap/agents/mk_sap.py @@ -136,6 +136,13 @@ "SAP CCMS Monitor Templates/Dialog Overview/*", ] monitor_types = [] # type: list[str] + +# A list of strings using the same unix shell pattern syntax as +# monitor_paths (see above). All monitoring objects whose path matches at +# least one of these patterns are excluded from monitoring, even if the +# path is matched by one of the monitor_paths patterns. This can be used +# to skip single objects below a monitor_paths wildcard. +exclude_paths = [] # type: list[str] config_file = MK_CONFDIR + "/sap.cfg" cfg = {} # type: list[dict[Any, Any]] | dict[Any, Any] @@ -208,6 +215,15 @@ class SapError(Exception): def to_be_monitored(path, toplevel_match=False): + # Exclude rules are always matched against the full path. They are + # intentionally not shortened during toplevel matching: Shortening a + # rule which targets a single subtree node would exclude the whole + # monitor instead. A rule matching the toplevel path itself still + # skips the whole monitor and saves the RFC calls for its tree. + for rule in exclude_paths: + if fnmatch.fnmatch(path, rule): + return False + for rule in monitor_paths: if toplevel_match and rule.count("/") > 1: rule = "/".join(rule.split("/")[:2]) diff --git a/tests/unit/cmk/plugins/sap/agents/test_mk_sap.py b/tests/unit/cmk/plugins/sap/agents/test_mk_sap.py index c497f94f9e4..d7e4eac4a34 100644 --- a/tests/unit/cmk/plugins/sap/agents/test_mk_sap.py +++ b/tests/unit/cmk/plugins/sap/agents/test_mk_sap.py @@ -112,6 +112,59 @@ def test_recursion(monkeypatch): } +@pytest.mark.parametrize( + "monitor, exclude, path, toplevel_match, expected", + [ + # default behaviour without exclude_paths stays untouched + (["SAP CCMS Monitor Templates/Dialog Overview/*"], [], "SAP CCMS Monitor Templates/Dialog Overview/ResponseTime", False, True), + (["SAP CCMS Monitor Templates/Dialog Overview/*"], [], "SAP CCMS Monitor Templates/Other Monitor/Foo", False, False), + # toplevel matching shortens monitor rules to the first two segments + (["SAP CCMS Monitor Templates/Dialog Overview/ResponseTime"], [], "SAP CCMS Monitor Templates/Dialog Overview", True, True), + # a matching exclude rule wins over a matching monitor rule. + # Note the truncated last path segment: segments come from the + # 40 character SAP field MTNAMESHRT, patterns must match the + # truncated name (pattern anchored to the subtree as recommended + # in sap.cfg) + ( + ["SAP CCMS Technical Expert Monitors/All Monitoring Contexts/Critical Number Ranges/*"], + ["SAP CCMS Technical Expert Monitors/All Monitoring Contexts/Critical Number Ranges/Critical Number Ranges All Clients/Client 000 Alert Messages for Number Ran*"], + "SAP CCMS Technical Expert Monitors/All Monitoring Contexts/Critical Number Ranges/Critical Number Ranges All Clients/Client 000 Alert Messages for Number Ran", + False, + False, + ), + # sibling nodes not matching the exclude rule are still monitored + ( + ["SAP CCMS Technical Expert Monitors/All Monitoring Contexts/Critical Number Ranges/*"], + ["SAP CCMS Technical Expert Monitors/All Monitoring Contexts/Critical Number Ranges/Critical Number Ranges All Clients/Client 000 Alert Messages for Number Ran*"], + "SAP CCMS Technical Expert Monitors/All Monitoring Contexts/Critical Number Ranges/Critical Number Ranges All Clients/Client 100 Alert Messages for Number Ran", + False, + True, + ), + # an exclude rule targeting a subtree node must not skip the whole + # monitor during toplevel matching (exclude rules are not shortened) + ( + ["SAP CCMS Technical Expert Monitors/All Monitoring Contexts/Critical Number Ranges/*"], + ["SAP CCMS Technical Expert Monitors/All Monitoring Contexts/Critical Number Ranges/Critical Number Ranges All Clients/Client 000 Alert Messages for Number Ran*"], + "SAP CCMS Technical Expert Monitors/All Monitoring Contexts", + True, + True, + ), + # an exclude rule matching the toplevel path skips the whole monitor + ( + ["*"], + ["SAP CCMS Monitor Templates/Dialog Overview*"], + "SAP CCMS Monitor Templates/Dialog Overview", + True, + False, + ), + ], +) +def test_to_be_monitored(monkeypatch, monitor, exclude, path, toplevel_match, expected): + monkeypatch.setattr(mk_sap, "monitor_paths", monitor) + monkeypatch.setattr(mk_sap, "exclude_paths", exclude) + assert mk_sap.to_be_monitored(path, toplevel_match) is expected + + def test_state_file_round_trip(monkeypatch, tmp_path): state_file = tmp_path / "sap.state" state_file.write_text(mk_sap.serialize_states(STATES))