normalize OIDs to fix agent-polled sensor matching
strip leading dots from OID keys returned by agent so they match sensor OIDs stored without leading dots. fixes ubiquiti airos wireless sensors (clients, ccq, frequency, etc) not being polled. also fix flaky sensor_change_detector tests using global pubsub topic.
This commit is contained in:
parent
abbe117f5e
commit
edb330e4de
3 changed files with 32 additions and 9 deletions
|
|
@ -1,6 +1,18 @@
|
|||
CHANGELOG - towerops-web
|
||||
========================
|
||||
|
||||
2026-02-11 - fix: normalize OIDs so agent-polled sensors match regardless of leading dot
|
||||
- File: lib/towerops_web/channels/agent_channel.ex
|
||||
Agent (gosnmp) returns OIDs with leading dots (e.g. ".1.3.6.1.4.1.41112...")
|
||||
but sensor OIDs stored in DB may omit the dot. process_sensor_reading used
|
||||
direct Map.get which silently missed wireless/vendor sensors on Ubiquiti airOS.
|
||||
- Fix: Added normalize_oid_keys/1 to strip leading dots from all oid_values keys,
|
||||
and normalize sensor.sensor_oid too. Both sides now match without leading dot.
|
||||
- File: test/towerops/snmp/sensor_change_detector_test.exs
|
||||
Fixed flaky refute_receive tests that subscribed to global "device:events" topic
|
||||
and caught unrelated device_discovered events from concurrent async tests.
|
||||
Changed to device-specific topic "device:#{device.id}" instead.
|
||||
|
||||
2026-02-11 - feat: add sensor value change events for all sensor types
|
||||
- New file: lib/towerops/snmp/sensor_change_detector.ex
|
||||
Extracted shared sensor change detection logic from DevicePollerWorker.
|
||||
|
|
|
|||
|
|
@ -1377,7 +1377,7 @@ defmodule ToweropsWeb.AgentChannel do
|
|||
Devices.update_snmp_poll_time(device)
|
||||
|
||||
snmp_device = device.snmp_device
|
||||
oid_values = Map.new(result.oid_values)
|
||||
oid_values = normalize_oid_keys(result.oid_values)
|
||||
# Use server time to avoid clock drift issues between agents
|
||||
timestamp = DateTime.utc_now()
|
||||
|
||||
|
|
@ -1534,7 +1534,9 @@ defmodule ToweropsWeb.AgentChannel do
|
|||
end
|
||||
|
||||
defp process_sensor_reading(sensor, oid_values, timestamp) do
|
||||
case Map.get(oid_values, sensor.sensor_oid) do
|
||||
normalized_oid = String.trim_leading(sensor.sensor_oid, ".")
|
||||
|
||||
case Map.get(oid_values, normalized_oid) do
|
||||
nil ->
|
||||
:ok
|
||||
|
||||
|
|
@ -1586,6 +1588,16 @@ defmodule ToweropsWeb.AgentChannel do
|
|||
|
||||
defp parse_integer(_), do: nil
|
||||
|
||||
# Normalize OID keys to strip leading dots.
|
||||
# The agent (via gosnmp/net-snmp) returns OIDs with leading dots
|
||||
# (e.g., ".1.3.6.1.2.1.1.1.0") but sensor OIDs and hardcoded interface
|
||||
# OIDs use the format without (e.g., "1.3.6.1.2.1.1.1.0").
|
||||
defp normalize_oid_keys(oid_values) do
|
||||
Map.new(oid_values, fn {oid, value} ->
|
||||
{String.trim_leading(oid, "."), value}
|
||||
end)
|
||||
end
|
||||
|
||||
defp parse_float(value) when is_float(value), do: value
|
||||
defp parse_float(value) when is_integer(value), do: value / 1.0
|
||||
|
||||
|
|
|
|||
|
|
@ -106,23 +106,22 @@ defmodule Towerops.Snmp.SensorChangeDetectorTest do
|
|||
assert String.contains?(event.message, "46.0C")
|
||||
end
|
||||
|
||||
test "does not emit event when value is unchanged", %{snmp_device: snmp_device} do
|
||||
test "does not emit event when value is unchanged", %{device: device, snmp_device: snmp_device} do
|
||||
sensor = create_sensor(snmp_device, %{last_value: 50.0, sensor_unit: "Hz"})
|
||||
timestamp = DateTime.utc_now()
|
||||
|
||||
# Subscribe to both topics
|
||||
Phoenix.PubSub.subscribe(Towerops.PubSub, "device:events")
|
||||
Phoenix.PubSub.subscribe(Towerops.PubSub, "device:#{device.id}")
|
||||
|
||||
SensorChangeDetector.detect_and_broadcast(sensor, 50.0, timestamp)
|
||||
|
||||
refute_receive {:device_event, _}, 100
|
||||
end
|
||||
|
||||
test "does not emit sensor_value_changed when last_value is nil", %{snmp_device: snmp_device} do
|
||||
test "does not emit sensor_value_changed when last_value is nil", %{device: device, snmp_device: snmp_device} do
|
||||
sensor = create_sensor(snmp_device, %{last_value: nil, sensor_unit: "Hz"})
|
||||
timestamp = DateTime.utc_now()
|
||||
|
||||
Phoenix.PubSub.subscribe(Towerops.PubSub, "device:events")
|
||||
Phoenix.PubSub.subscribe(Towerops.PubSub, "device:#{device.id}")
|
||||
|
||||
SensorChangeDetector.detect_and_broadcast(sensor, 50.0, timestamp)
|
||||
|
||||
|
|
@ -177,7 +176,7 @@ defmodule Towerops.Snmp.SensorChangeDetectorTest do
|
|||
assert event.severity == "info"
|
||||
end
|
||||
|
||||
test "does not emit spike/drop for % sensor with <30% change", %{snmp_device: snmp_device} do
|
||||
test "does not emit spike/drop for % sensor with <30% change", %{device: device, snmp_device: snmp_device} do
|
||||
sensor =
|
||||
create_sensor(snmp_device, %{
|
||||
sensor_type: "percent",
|
||||
|
|
@ -189,7 +188,7 @@ defmodule Towerops.Snmp.SensorChangeDetectorTest do
|
|||
|
||||
timestamp = DateTime.utc_now()
|
||||
|
||||
Phoenix.PubSub.subscribe(Towerops.PubSub, "device:events")
|
||||
Phoenix.PubSub.subscribe(Towerops.PubSub, "device:#{device.id}")
|
||||
|
||||
SensorChangeDetector.detect_and_broadcast(sensor, 55.0, timestamp)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue