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.
This commit is contained in:
Graham McIntire 2026-01-17 11:36:37 -06:00
parent 79dabd840b
commit de002f9903
No known key found for this signature in database
3 changed files with 50 additions and 4 deletions

View file

@ -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}

View file

@ -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"

View file

@ -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
[