@@ -95,7 +95,37 @@ def to_bytes(units: str) -> int:
9595 return parsed
9696
9797
98- def aggregate_meminfo (parsed : PreParsed ) -> memory .SectionMemUsed :
98+ # The "Physical memory" hrStorageUsed value is normally interpreted as
99+ # INCLUDING the reclaimable page cache (classic net-snmp / UCD, which reports
100+ # "MemTotal - MemFree"). Checkmk therefore subtracts the "Cached memory" entry
101+ # to obtain the real usage.
102+ #
103+ # ArubaOS-CX switches (HPE Aruba Networking) instead report the physical "used"
104+ # value with the cache already excluded (modern net-snmp "MemAvailable"
105+ # semantics), while still exposing a large "Cached memory" entry. There the
106+ # cached memory can exceed the reported "used" value, and subtracting it would
107+ # understate the usage or even push it below zero. The two cases cannot be told
108+ # apart from the HOST-RESOURCES-MIB values alone (RFC 2790 defines no
109+ # relationship between storage entries), so these devices are recognized by
110+ # their sysObjectID, which lives under the Aruba (enterprise 47196) wired switch
111+ # product arc, e.g. ...47196.4.1.1.1.100 (6300M) or ...4.1.1.1.309 (6200F).
112+ _CACHE_EXCLUDED_FROM_USED_SYS_OBJECT_IDS : tuple [str , ...] = (
113+ ".1.3.6.1.4.1.47196.4.1.1.1." , # ArubaOS-CX switches (SUP-29474)
114+ )
115+
116+
117+ def _reports_cache_excluded_from_used (system_info : StringTable ) -> bool :
118+ """Whether the device's SNMP agent reports the physical memory usage with the
119+ page cache already excluded (so the cache must not be subtracted again)."""
120+ if not system_info or not system_info [0 ]:
121+ return False
122+ sys_object_id = system_info [0 ][0 ]
123+ return any (
124+ sys_object_id .startswith (prefix ) for prefix in _CACHE_EXCLUDED_FROM_USED_SYS_OBJECT_IDS
125+ )
126+
127+
128+ def aggregate_meminfo (parsed : PreParsed , * , subtract_cache : bool = True ) -> memory .SectionMemUsed :
99129 """return a meminfo dict as expected by check_memory from mem.include"""
100130 meminfo : memory .SectionMemUsed = {"Cached" : 0 }
101131
@@ -115,7 +145,7 @@ def aggregate_meminfo(parsed: PreParsed) -> memory.SectionMemUsed:
115145 meminfo .setdefault ("SwapTotal" , size )
116146 meminfo .setdefault ("SwapFree" , (size - used ))
117147
118- if descr == "cached memory" and used > 0 :
148+ if subtract_cache and descr == "cached memory" and used > 0 :
119149 # Account for cached memory (this works at least for systems using
120150 # the UCD snmpd (such as Linux based applicances)
121151 # some devices report negative used cache values...
@@ -132,7 +162,10 @@ def parse_hr_mem(string_table: Sequence[StringTable]) -> memory.SectionMemUsed |
132162 if not any (size > 0 for _ , size , __ in pre_parsed .get ("RAM" , [])):
133163 return None
134164
135- section = aggregate_meminfo (pre_parsed )
165+ system_info = string_table [1 ] if len (string_table ) > 1 else []
166+ subtract_cache = not _reports_cache_excluded_from_used (system_info )
167+
168+ section = aggregate_meminfo (pre_parsed , subtract_cache = subtract_cache )
136169 return section if section .get ("MemTotal" ) else None
137170
138171
@@ -151,6 +184,12 @@ def parse_hr_mem(string_table: Sequence[StringTable]) -> memory.SectionMemUsed |
151184 "6" , # hrStorageUsed
152185 ],
153186 ),
187+ SNMPTree (
188+ base = ".1.3.6.1.2.1.1" ,
189+ oids = [
190+ "2.0" , # sysObjectID
191+ ],
192+ ),
154193 ],
155194 detect = ucd_hr_detection .USE_HR_MEM ,
156195)
0 commit comments