From de002f99037a5017c4d6d43dca289994dce94832 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Sat, 17 Jan 2026 11:36:37 -0600 Subject: [PATCH] Cap storage/memory usage sensors at 100% to handle device firmware bugs Fixes an issue where storage usage was reporting values over 200,000% due to incorrect SNMP values from the device. This can occur when: - Device firmware has bugs in hrStorageUsed/hrStorageSize reporting - SNMP allocation units differ between OIDs - Values are read during device reconfiguration Changes: - PollerWorker: Cap percentage sensors at 100%, log warning with raw values - Mikrotik profile: Cap storage usage at 100% during discovery - NetSnmp profile: Cap memory usage at 100% during discovery The raw percentage is logged before capping to help diagnose device issues. All percentage sensors now have consistent validation across polling and discovery. --- lib/towerops/snmp/poller_worker.ex | 20 +++++++++++++++++++- lib/towerops/snmp/profiles/mikrotik.ex | 19 +++++++++++++++++-- lib/towerops/snmp/profiles/net_snmp.ex | 15 ++++++++++++++- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/lib/towerops/snmp/poller_worker.ex b/lib/towerops/snmp/poller_worker.ex index 3438efc0..265b448b 100644 --- a/lib/towerops/snmp/poller_worker.ex +++ b/lib/towerops/snmp/poller_worker.ex @@ -278,7 +278,25 @@ defmodule Towerops.Snmp.PollerWorker do size when is_number(size) <- decode_snmp_value(size_raw), true <- size > 0 do percentage = used / size * 100 - {:ok, percentage} + + # Validate percentage and warn if it exceeds 100% + # This can happen if the device reports incorrect SNMP values or if + # hrStorageAllocationUnits differs between used/size OIDs + capped_percentage = + if percentage > 100 do + Logger.warning( + "Percentage sensor #{sensor.sensor_descr} (#{sensor.sensor_type}) " <> + "exceeded 100%: #{Float.round(percentage, 2)}% " <> + "(used=#{used} from #{used_oid}, size=#{size} from #{size_oid}). " <> + "Capping at 100%. This may indicate a device firmware bug." + ) + + 100.0 + else + percentage + end + + {:ok, capped_percentage} else {:error, reason} -> {:error, reason} false -> {:error, :division_by_zero} diff --git a/lib/towerops/snmp/profiles/mikrotik.ex b/lib/towerops/snmp/profiles/mikrotik.ex index 413b8e8b..7cb5f299 100644 --- a/lib/towerops/snmp/profiles/mikrotik.ex +++ b/lib/towerops/snmp/profiles/mikrotik.ex @@ -282,6 +282,21 @@ defmodule Towerops.Snmp.Profiles.Mikrotik do percent = used / size * 100 type = determine_storage_type(descr) + # Cap percentage at 100% and log warning if exceeded + # This can happen if the device reports incorrect SNMP values + capped_percent = + if percent > 100 do + Logger.warning( + "Storage sensor #{descr} exceeded 100%: #{Float.round(percent, 2)}% " <> + "(used=#{used} from #{used_oid}, size=#{size} from #{size_oid}). " <> + "Capping at 100%. This may indicate a device firmware bug." + ) + + 100.0 + else + percent + end + %{ sensor_type: "#{type}_usage", sensor_index: index, @@ -289,8 +304,8 @@ defmodule Towerops.Snmp.Profiles.Mikrotik do sensor_descr: descr, sensor_unit: "%", sensor_divisor: 1, - last_value: percent, - status: storage_status(type, percent), + last_value: capped_percent, + status: storage_status(type, capped_percent), metadata: %{ size_oid: size_oid, calculation: "percentage" diff --git a/lib/towerops/snmp/profiles/net_snmp.ex b/lib/towerops/snmp/profiles/net_snmp.ex index 1b1930ae..3babe497 100644 --- a/lib/towerops/snmp/profiles/net_snmp.ex +++ b/lib/towerops/snmp/profiles/net_snmp.ex @@ -208,7 +208,20 @@ defmodule Towerops.Snmp.Profiles.NetSnmp do used_percent = if total_kb && total_kb > 0 && used_kb do - used_kb / total_kb * 100 + percent = used_kb / total_kb * 100 + + # Cap percentage at 100% and log warning if exceeded + if percent > 100 do + Logger.warning( + "Memory usage exceeded 100%: #{Float.round(percent, 2)}% " <> + "(used=#{used_kb} KB, total=#{total_kb} KB). " <> + "Capping at 100%. This may indicate a device firmware bug." + ) + + 100.0 + else + percent + end end [