feat: poll actual firmware version and serial from SNMP
Enhance dynamic profile to poll OS definitions from SNMP: - Add serial_number field to snmp_devices table - Update Dynamic profile to query profile_os_definitions - Poll SNMP OIDs for version and serial fields - Display actual firmware version instead of profile name For ePMP devices, this will now query: - CAMBIUM-PMP80211-MIB::cambiumCurrentuImageVersion.0 for firmware - CAMBIUM-PMP80211-MIB::cambiumEPMPMSN.0 for serial number The Operating System field will now show actual version like 'Cambium 4.7.1' instead of 'Cambium epmp'.
This commit is contained in:
parent
5baad658a3
commit
b83484acf8
4 changed files with 67 additions and 11 deletions
|
|
@ -26,6 +26,7 @@ defmodule Towerops.Snmp.Device do
|
|||
field :manufacturer, :string
|
||||
field :model, :string
|
||||
field :firmware_version, :string
|
||||
field :serial_number, :string
|
||||
|
||||
belongs_to :device, DeviceSchema
|
||||
|
||||
|
|
@ -46,6 +47,7 @@ defmodule Towerops.Snmp.Device do
|
|||
manufacturer: String.t() | nil,
|
||||
model: String.t() | nil,
|
||||
firmware_version: String.t() | nil,
|
||||
serial_number: String.t() | nil,
|
||||
device_id: Ecto.UUID.t(),
|
||||
device: NotLoaded.t() | Devices.t(),
|
||||
interfaces: NotLoaded.t() | [Interface.t()],
|
||||
|
|
@ -67,7 +69,8 @@ defmodule Towerops.Snmp.Device do
|
|||
:sys_location,
|
||||
:manufacturer,
|
||||
:model,
|
||||
:firmware_version
|
||||
:firmware_version,
|
||||
:serial_number
|
||||
])
|
||||
|> validate_required([:device_id])
|
||||
|> unique_constraint(:device_id)
|
||||
|
|
|
|||
|
|
@ -253,13 +253,13 @@ defmodule Towerops.Snmp.Discovery do
|
|||
|
||||
@spec build_device_info(Client.connection_opts(), system_info(), profile()) ::
|
||||
{:ok, device_info()}
|
||||
defp build_device_info(_client_opts, system_info, {:dynamic, profile}) do
|
||||
defp build_device_info(client_opts, system_info, {:dynamic, profile}) do
|
||||
# Use dynamic profile to identify device
|
||||
identified_info = Dynamic.identify_device(profile, system_info)
|
||||
identified_info = Dynamic.identify_device(profile, client_opts, system_info)
|
||||
{:ok, identified_info}
|
||||
rescue
|
||||
_error ->
|
||||
Logger.error("Failed to identify device with dynamic profile")
|
||||
error ->
|
||||
Logger.error("Failed to identify device with dynamic profile: #{inspect(error)}")
|
||||
{:ok, system_info}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -17,17 +17,24 @@ defmodule Towerops.Snmp.Profiles.Dynamic do
|
|||
@doc """
|
||||
Identifies device using the database profile information.
|
||||
Returns manufacturer, model, and firmware info.
|
||||
|
||||
Polls SNMP OIDs from OS definitions to extract version and serial number.
|
||||
"""
|
||||
@spec identify_device(DeviceProfile.t(), Discovery.system_info()) :: Discovery.device_info()
|
||||
def identify_device(profile, _system_info) do
|
||||
@spec identify_device(DeviceProfile.t(), Client.connection_opts(), Discovery.system_info()) ::
|
||||
Discovery.device_info()
|
||||
def identify_device(profile, client_opts, _system_info) do
|
||||
# Load profile with OS definitions
|
||||
profile = DeviceProfiles.get_profile_with_associations(profile.os)
|
||||
|
||||
# Poll OS definitions for version, serial, etc.
|
||||
os_data = poll_os_definitions(profile, client_opts)
|
||||
|
||||
# Use profile metadata to build device info
|
||||
%{
|
||||
manufacturer: extract_manufacturer(profile),
|
||||
model: profile.text || profile.os,
|
||||
# Use profile OS name as firmware_version for now
|
||||
# TODO: Poll SNMP OIDs from profile_os_definitions to get actual version
|
||||
firmware_version: profile.os,
|
||||
serial_number: nil
|
||||
firmware_version: os_data[:version] || profile.os,
|
||||
serial_number: os_data[:serial]
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -69,6 +76,43 @@ defmodule Towerops.Snmp.Profiles.Dynamic do
|
|||
|
||||
# Private functions
|
||||
|
||||
defp poll_os_definitions(profile, client_opts) do
|
||||
if profile && profile.os_definitions && length(profile.os_definitions) > 0 do
|
||||
Logger.debug("Polling #{length(profile.os_definitions)} OS definitions for #{profile.os}")
|
||||
|
||||
# Poll each OS definition and build a map
|
||||
profile.os_definitions
|
||||
|> Enum.map(fn os_def ->
|
||||
field = String.to_atom(os_def.field)
|
||||
value = poll_os_definition_oid(client_opts, os_def)
|
||||
{field, value}
|
||||
end)
|
||||
|> Enum.reject(fn {_field, value} -> is_nil(value) end)
|
||||
|> Map.new()
|
||||
else
|
||||
%{}
|
||||
end
|
||||
end
|
||||
|
||||
defp poll_os_definition_oid(client_opts, os_def) do
|
||||
# Use the OID from the OS definition
|
||||
oid = os_def.oid
|
||||
|
||||
if oid do
|
||||
case Client.get(client_opts, oid) do
|
||||
{:ok, value} when is_binary(value) ->
|
||||
value
|
||||
|
||||
{:ok, value} ->
|
||||
to_string(value)
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.debug("Failed to poll OS definition OID #{oid}: #{inspect(reason)}")
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_manufacturer(%DeviceProfile{group: group}) when is_binary(group) do
|
||||
# Capitalize manufacturer from group (e.g., "cambium" -> "Cambium")
|
||||
String.capitalize(group)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
defmodule Towerops.Repo.Migrations.AddSerialNumberToSnmpDevices do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:snmp_devices) do
|
||||
add :serial_number, :string
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue