diff --git a/lib/towerops/snmp/poller_worker.ex b/lib/towerops/snmp/poller_worker.ex index 4a65e839..3207ad20 100644 --- a/lib/towerops/snmp/poller_worker.ex +++ b/lib/towerops/snmp/poller_worker.ex @@ -99,40 +99,36 @@ defmodule Towerops.Snmp.PollerWorker do defp poll_sensors(sensors, client_opts, timestamp) do Enum.each(sensors, fn sensor -> - case Client.get(client_opts, sensor.sensor_oid) do - {:ok, raw_value} -> - decoded_value = decode_snmp_value(raw_value) + # Check if sensor requires special calculation (e.g., storage percentage) + result = + if sensor.metadata["calculation"] == "percentage" do + poll_percentage_sensor(sensor, client_opts) + else + poll_simple_sensor(sensor, client_opts) + end - if is_number(decoded_value) do - # Calculate actual value using divisor - value = decoded_value / sensor.sensor_divisor + case result do + {:ok, value} -> + Snmp.create_sensor_reading(%{ + sensor_id: sensor.id, + value: value, + status: "ok", + checked_at: timestamp + }) - # Determine status based on value (could be enhanced with thresholds) - status = "ok" + {:error, :non_numeric} -> + Logger.debug("Non-numeric SNMP value for sensor #{sensor.sensor_descr}") - # Save the reading - Snmp.create_sensor_reading(%{ - sensor_id: sensor.id, - value: value, - status: status, - checked_at: timestamp - }) - else - Logger.debug("Non-numeric SNMP value for sensor #{sensor.sensor_descr}") - - # Save reading with nil value - Snmp.create_sensor_reading(%{ - sensor_id: sensor.id, - value: nil, - status: "unknown", - checked_at: timestamp - }) - end + Snmp.create_sensor_reading(%{ + sensor_id: sensor.id, + value: nil, + status: "unknown", + checked_at: timestamp + }) {:error, reason} -> Logger.warning("Failed to poll sensor #{sensor.sensor_descr}: #{inspect(reason)}") - # Save error status Snmp.create_sensor_reading(%{ sensor_id: sensor.id, value: nil, @@ -143,6 +139,43 @@ defmodule Towerops.Snmp.PollerWorker do end) end + defp poll_simple_sensor(sensor, client_opts) do + case Client.get(client_opts, sensor.sensor_oid) do + {:ok, raw_value} -> + decoded_value = decode_snmp_value(raw_value) + + if is_number(decoded_value) do + # Calculate actual value using divisor + value = decoded_value / sensor.sensor_divisor + {:ok, value} + else + {:error, :non_numeric} + end + + {:error, reason} -> + {:error, reason} + end + end + + defp poll_percentage_sensor(sensor, client_opts) do + # For percentage sensors (like storage), fetch both used and size + size_oid = sensor.metadata["size_oid"] + used_oid = sensor.sensor_oid + + with {:ok, used_raw} <- Client.get(client_opts, used_oid), + {:ok, size_raw} <- Client.get(client_opts, size_oid), + used when is_number(used) <- decode_snmp_value(used_raw), + size when is_number(size) <- decode_snmp_value(size_raw), + true <- size > 0 do + percentage = used / size * 100 + {:ok, percentage} + else + {:error, reason} -> {:error, reason} + false -> {:error, :division_by_zero} + _ -> {:error, :non_numeric} + end + end + defp poll_interfaces(interfaces, client_opts, timestamp) do Enum.each(interfaces, fn interface -> # Poll interface stats: ifInOctets, ifOutOctets, ifInErrors, ifOutErrors diff --git a/lib/towerops/snmp/profiles/mikrotik.ex b/lib/towerops/snmp/profiles/mikrotik.ex index 60a77936..45b17654 100644 --- a/lib/towerops/snmp/profiles/mikrotik.ex +++ b/lib/towerops/snmp/profiles/mikrotik.ex @@ -235,7 +235,11 @@ defmodule Towerops.Snmp.Profiles.Mikrotik do sensor_unit: "%", sensor_divisor: 1, last_value: percent, - status: storage_status(type, percent) + status: storage_status(type, percent), + metadata: %{ + size_oid: size_oid, + calculation: "percentage" + } } end end) diff --git a/lib/towerops/snmp/sensor.ex b/lib/towerops/snmp/sensor.ex index 61245051..4eceea53 100644 --- a/lib/towerops/snmp/sensor.ex +++ b/lib/towerops/snmp/sensor.ex @@ -16,6 +16,7 @@ defmodule Towerops.Snmp.Sensor do field :monitored, :boolean, default: true field :last_value, :float field :last_checked_at, :utc_datetime + field :metadata, :map, default: %{} belongs_to :snmp_device, Towerops.Snmp.Device @@ -37,7 +38,8 @@ defmodule Towerops.Snmp.Sensor do :sensor_divisor, :monitored, :last_value, - :last_checked_at + :last_checked_at, + :metadata ]) |> validate_required([:snmp_device_id, :sensor_type, :sensor_index, :sensor_oid]) |> unique_constraint([:snmp_device_id, :sensor_index]) diff --git a/priv/repo/migrations/20260104190824_add_sensor_metadata.exs b/priv/repo/migrations/20260104190824_add_sensor_metadata.exs new file mode 100644 index 00000000..ee1854e5 --- /dev/null +++ b/priv/repo/migrations/20260104190824_add_sensor_metadata.exs @@ -0,0 +1,9 @@ +defmodule Towerops.Repo.Migrations.AddSensorMetadata do + use Ecto.Migration + + def change do + alter table(:snmp_sensors) do + add :metadata, :map, default: %{} + end + end +end