Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions agents/sap/sap.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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*',
]
16 changes: 16 additions & 0 deletions cmk/plugins/sap/agents/mk_sap.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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])
Expand Down
53 changes: 53 additions & 0 deletions tests/unit/cmk/plugins/sap/agents/test_mk_sap.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Loading