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 [