diff --git a/cmk/base/legacy_checks/mounts.py b/cmk/base/legacy_checks/mounts.py index b429cca229f..6193b807781 100644 --- a/cmk/base/legacy_checks/mounts.py +++ b/cmk/base/legacy_checks/mounts.py @@ -9,30 +9,50 @@ # to # knvmsapprd:/transreorg/sap/trans /transreorg/sap/trans\040(deleted) nfs4 rw,relatime,vers=4.0,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=172.24.98.63,local_lock=none,addr=172.24.98.57 0 0 -# -# (mo): This plugin is a good example for plugins that would massively benefit from a -# section definition (parse_function). -# However, I need an exapmle for a plugin without one, so for now I keep it this way. -# - +from collections.abc import Iterable, Mapping, Sequence +from dataclasses import dataclass +from typing import Any from cmk.base.check_api import LegacyCheckDefinition from cmk.base.config import check_info +from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import StringTable -def discovery_mounts(info): - devices = [] - for dev, mountpoint, fstype, options, _dump, _fsck in info: - if fstype == "tmpfs": - continue +@dataclass(frozen=True, kw_only=True) +class Mount: + mountpoint: str + options: Sequence[str] # this could be improved... + fs_type: str + is_stale: bool - if mountpoint in ["/etc/resolv.conf", "/etc/hostname", "/etc/hosts"]: + +def parse_mounts(string_table: StringTable) -> Mapping[str, Mount]: + devices = set() + section = {} + for dev, mountpoint, fs_type, options, _dump, _fsck in string_table: + if dev in devices: continue - if dev not in devices: - devices.append(dev) - opts = sorted(options.split(",")) - yield mountpoint.replace("\\040(deleted)", ""), opts + devices.add(dev) + + mountname = mountpoint.replace("\\040(deleted)", "") + section[mountname] = Mount( + mountpoint=mountname, + options=sorted(options.split(",")), + fs_type=fs_type, + is_stale=mountpoint.endswith("\\040(deleted)"), + ) + + return section + + +def discovery_mounts(section: Mapping[str, Mount]) -> Iterable[tuple[str, Mapping]]: + yield from ( + (m.mountpoint, {"expected_mount_options": m.options}) + for m in section.values() + if m.fs_type != "tmpfs" + and m.mountpoint not in ["/etc/resolv.conf", "/etc/hostname", "/etc/hosts"] + ) def _should_ignore_option(option): @@ -42,45 +62,45 @@ def _should_ignore_option(option): return False -def check_mounts(item, targetopts, info): - # Ignore options that are allowed to change - for _dev, mountpoint, _fstype, options, _dump, _fsck in info: - if item == mountpoint.replace("\\040(deleted)", ""): - if mountpoint.endswith("\\040(deleted)"): - yield 1, "Mount point detected as stale" - return +def check_mounts( + item: str, params: Mapping[str, Any], section: Mapping[str, Mount] +) -> Iterable[tuple[int, str]]: + if (mount := section.get(item)) is None: + return + + if mount.is_stale: + yield 1, "Mount point detected as stale" + return - opts = options.split(",") - # Now compute the exact difference. + targetopts = params["expected_mount_options"] - exceeding = [ - opt for opt in opts if opt not in targetopts and not _should_ignore_option(opt) - ] + # Now compute the exact difference. + exceeding = [ + opt for opt in mount.options if opt not in targetopts and not _should_ignore_option(opt) + ] - missing = [ - opt for opt in targetopts if opt not in opts and not _should_ignore_option(opt) - ] + missing = [ + opt for opt in targetopts if opt not in mount.options and not _should_ignore_option(opt) + ] - if not missing and not exceeding: - yield 0, "Mount options exactly as expected" - return + if not missing and not exceeding: + yield 0, "Mount options exactly as expected" + return - infos = [] - if missing: - infos.append("Missing: %s" % ",".join(missing)) - if exceeding: - infos.append("Exceeding: %s" % ",".join(exceeding)) - infotext = ", ".join(infos) + if missing: + yield 1, "Missing: %s" % ",".join(missing) + if exceeding: + yield 1, "Exceeding: %s" % ",".join(exceeding) - yield 1, infotext - if "ro" in exceeding: - yield 2, "Filesystem has switched to read-only and is probably corrupted" - return + if "ro" in exceeding: + yield 2, "Filesystem has switched to read-only and is probably corrupted" check_info["mounts"] = LegacyCheckDefinition( service_name="Mount options of %s", - discovery_function=discovery_mounts, + parse_function=parse_mounts, + discovery_function=discovery_mounts, # type: ignore[typeddict-item] # fix coming up check_function=check_mounts, check_ruleset_name="fs_mount_options", + check_default_parameters={"expected_mount_options": []}, # will be overwritten by dicsovery ) diff --git a/cmk/gui/plugins/wato/check_parameters/fs_mount_options.py b/cmk/gui/plugins/wato/check_parameters/fs_mount_options.py index 8ad22076948..948ae24331d 100644 --- a/cmk/gui/plugins/wato/check_parameters/fs_mount_options.py +++ b/cmk/gui/plugins/wato/check_parameters/fs_mount_options.py @@ -9,19 +9,30 @@ rulespec_registry, RulespecGroupCheckParametersStorage, ) -from cmk.gui.valuespec import ListOfStrings, TextInput +from cmk.gui.valuespec import Dictionary, ListOfStrings, Migrate, TextInput -def _parameter_valuespec_fs_mount_options(): - return ListOfStrings( - title=_("Expected mount options"), - help=_( - "Specify all expected mount options here. If the list of " - "actually found options differs from this list, the check will go " - "warning or critical. Just the option commit is being " - "ignored since it is modified by the power saving algorithms." +def _parameter_valuespec_fs_mount_options() -> Migrate: + return Migrate( + valuespec=Dictionary( + elements=[ + ( + "expected_mount_options", + ListOfStrings( + title=_("Expected mount options"), + help=_( + "Specify all expected mount options here. If the list of " + "actually found options differs from this list, the check will go " + "warning or critical. Just the option commit is being " + "ignored since it is modified by the power saving algorithms." + ), + valuespec=TextInput(), + ), + ), + ], + optional_keys=[], ), - valuespec=TextInput(), + migrate=lambda p: p if isinstance(p, dict) else {"expected_mount_options": p}, ) diff --git a/tests/unit/cmk/base/plugins/agent_based/test_section_properties.py b/tests/unit/cmk/base/plugins/agent_based/test_section_properties.py index ffc798066b2..8b8b74d5c26 100644 --- a/tests/unit/cmk/base/plugins/agent_based/test_section_properties.py +++ b/tests/unit/cmk/base/plugins/agent_based/test_section_properties.py @@ -281,7 +281,6 @@ def test_section_parse_function_does_something(fix_register: FixRegister) -> Non "mongodb_flushing", "mongodb_instance", "mongodb_locks", - "mounts", "mq_queues", "msexch_replhealth", "msoffice_serviceplans", diff --git a/tests/unit/cmk/gui/plugins/wato/check_parameters/test_plugin_vs_wato.py b/tests/unit/cmk/gui/plugins/wato/check_parameters/test_plugin_vs_wato.py index 31e056d45d1..6203350cdd2 100644 --- a/tests/unit/cmk/gui/plugins/wato/check_parameters/test_plugin_vs_wato.py +++ b/tests/unit/cmk/gui/plugins/wato/check_parameters/test_plugin_vs_wato.py @@ -607,11 +607,6 @@ class ErrorReporter: "mongodb_collections", RuleGroup.CheckgroupParameters("mongodb_collections"), ), - ( - "check", - "mounts", - RuleGroup.CheckgroupParameters("fs_mount_options"), - ), ("check", "mq_queues", RuleGroup.CheckgroupParameters("mq_queues")), ( "check",