import yamls
This commit is contained in:
parent
d68991c484
commit
7656ac7212
1474 changed files with 77977 additions and 3744 deletions
|
|
@ -82,7 +82,7 @@ FROM ${RUNNER_IMAGE} AS final
|
|||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get upgrade -y \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends libstdc++6 openssl libncurses6 locales ca-certificates iputils-ping \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends libstdc++6 openssl libncurses6 locales ca-certificates iputils-ping snmp \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set the locale
|
||||
|
|
|
|||
|
|
@ -2,27 +2,30 @@ defmodule Mix.Tasks.ImportProfiles do
|
|||
@shortdoc "Import device profiles from LibreNMS YAML definitions"
|
||||
|
||||
@moduledoc """
|
||||
Imports device profiles from LibreNMS YAML definition files.
|
||||
Imports device profiles from YAML definition files.
|
||||
|
||||
## Usage
|
||||
|
||||
# Import all profile definitions
|
||||
mix import_profiles --source-path ~/librenms/includes/definitions
|
||||
# Import all profiles from priv/profiles (default)
|
||||
mix import_profiles
|
||||
|
||||
# Import from a custom path
|
||||
mix import_profiles --source-path ~/librenms/resources/definitions
|
||||
|
||||
# Import specific profiles
|
||||
mix import_profiles --source-path ~/librenms/includes/definitions --profiles mikrotik,cisco
|
||||
mix import_profiles --profiles mikrotik,cisco,epmp
|
||||
|
||||
## Options
|
||||
|
||||
* `--source-path` - Path to LibreNMS includes/definitions directory (required)
|
||||
* `--source-path` - Path to definitions directory (default: priv/profiles)
|
||||
* `--profiles` - Comma-separated list of profiles to import (default: all)
|
||||
|
||||
## Notes
|
||||
|
||||
* Reads YAML files from the definitions directory
|
||||
* Reads YAML files from os_detection/ and os_discovery/ subdirectories
|
||||
* Creates database profiles with MIB symbolic names
|
||||
* Updates existing profiles if they already exist
|
||||
* Requires the application to be started to access the database
|
||||
* Run ./scripts/sync-librenms.sh to sync profiles from LibreNMS
|
||||
"""
|
||||
|
||||
use Mix.Task
|
||||
|
|
@ -47,7 +50,7 @@ defmodule Mix.Tasks.ImportProfiles do
|
|||
]
|
||||
)
|
||||
|
||||
source_path = opts[:source_path] || raise "Missing --source-path argument"
|
||||
source_path = opts[:source_path] || default_source_path()
|
||||
profiles_filter = parse_profiles(opts[:profiles])
|
||||
|
||||
if !File.exists?(source_path) do
|
||||
|
|
@ -63,6 +66,10 @@ defmodule Mix.Tasks.ImportProfiles do
|
|||
end
|
||||
end
|
||||
|
||||
defp default_source_path do
|
||||
Path.join(:code.priv_dir(:towerops), "profiles")
|
||||
end
|
||||
|
||||
defp parse_profiles(nil), do: :all
|
||||
defp parse_profiles(""), do: :all
|
||||
|
||||
|
|
@ -122,7 +129,8 @@ defmodule Mix.Tasks.ImportProfiles do
|
|||
discovery_file = Path.join([source_path, "os_discovery", "#{profile_name}.yaml"])
|
||||
|
||||
with {:ok, detection_data} <- read_yaml_file(detection_file),
|
||||
{:ok, discovery_data} <- read_yaml_file(discovery_file),
|
||||
# Discovery file is optional - use empty map if not found
|
||||
discovery_data = read_yaml_file_optional(discovery_file),
|
||||
{:ok, _profile} <- create_or_update_profile(detection_data, discovery_data, profile_name),
|
||||
# Reload profile to get correct binary ID
|
||||
profile when not is_nil(profile) <- Profiles.get_profile(profile_name),
|
||||
|
|
@ -132,6 +140,13 @@ defmodule Mix.Tasks.ImportProfiles do
|
|||
end
|
||||
end
|
||||
|
||||
defp read_yaml_file_optional(file_path) do
|
||||
case read_yaml_file(file_path) do
|
||||
{:ok, data} -> data
|
||||
{:error, _} -> %{}
|
||||
end
|
||||
end
|
||||
|
||||
defp read_yaml_file(file_path) do
|
||||
with {:ok, yaml_content} <- File.read(file_path),
|
||||
{:ok, yaml_data} <- YamlElixir.read_from_string(yaml_content) do
|
||||
|
|
@ -231,33 +246,29 @@ defmodule Mix.Tasks.ImportProfiles do
|
|||
|
||||
defp extract_device_oids(yaml_data) do
|
||||
# Extract from modules.os section in os_discovery YAML
|
||||
device_oids = []
|
||||
|
||||
# Get the modules.os section
|
||||
os_module = get_in(yaml_data, ["modules", "os"]) || %{}
|
||||
|
||||
# Serial number
|
||||
device_oids =
|
||||
case Map.get(os_module, "serial") do
|
||||
nil -> device_oids
|
||||
mib_name -> [{"serial_number", mib_name} | device_oids]
|
||||
end
|
||||
# Map of YAML field names to our field names
|
||||
field_mappings = [
|
||||
{"serial", "serial_number"},
|
||||
{"version", "firmware_version"},
|
||||
{"hardware", "hardware"},
|
||||
{"lat", "latitude"},
|
||||
{"long", "longitude"},
|
||||
{"features", "features"},
|
||||
{"sysName", "sys_name_oid"}
|
||||
]
|
||||
|
||||
# Firmware version
|
||||
device_oids =
|
||||
case Map.get(os_module, "version") do
|
||||
nil -> device_oids
|
||||
mib_name -> [{"firmware_version", mib_name} | device_oids]
|
||||
Enum.flat_map(field_mappings, fn {yaml_field, db_field} ->
|
||||
case Map.get(os_module, yaml_field) do
|
||||
nil -> []
|
||||
# Handle list of OIDs (fallback order)
|
||||
mib_names when is_list(mib_names) -> [{db_field, List.first(mib_names)}]
|
||||
mib_name when is_binary(mib_name) -> [{db_field, mib_name}]
|
||||
_ -> []
|
||||
end
|
||||
|
||||
# Hardware/model
|
||||
device_oids =
|
||||
case Map.get(os_module, "hardware") do
|
||||
nil -> device_oids
|
||||
mib_name -> [{"hardware", mib_name} | device_oids]
|
||||
end
|
||||
|
||||
device_oids
|
||||
end)
|
||||
end
|
||||
|
||||
defp import_sensor_oids(profile, yaml_data) do
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ defmodule Towerops.Application do
|
|||
ToweropsWeb.Telemetry,
|
||||
Towerops.Repo,
|
||||
{DNSCluster, query: Application.get_env(:towerops, :dns_cluster_query) || :ignore},
|
||||
pubsub_spec()
|
||||
# Start a worker by calling: Towerops.Worker.start_link(arg)
|
||||
# {Towerops.Worker, arg},
|
||||
pubsub_spec(),
|
||||
# Load YAML profiles into ETS cache
|
||||
Towerops.Profiles.YamlProfiles
|
||||
] ++
|
||||
background_workers() ++
|
||||
exq_workers() ++
|
||||
|
|
|
|||
286
lib/towerops/profiles/yaml_profiles.ex
Normal file
286
lib/towerops/profiles/yaml_profiles.ex
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
defmodule Towerops.Profiles.YamlProfiles do
|
||||
@moduledoc """
|
||||
Loads and caches device profiles directly from YAML files.
|
||||
|
||||
Profiles are stored in priv/profiles/ and loaded into an ETS table on startup.
|
||||
This avoids database storage and keeps YAML files as the source of truth.
|
||||
"""
|
||||
|
||||
use GenServer
|
||||
|
||||
require Logger
|
||||
|
||||
@table :yaml_profiles
|
||||
@profiles_dir "profiles"
|
||||
|
||||
def start_link(opts \\ []) do
|
||||
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a profile by name from the cache.
|
||||
"""
|
||||
def get_profile(name) when is_binary(name) do
|
||||
case :ets.lookup(@table, {:profile, name}) do
|
||||
[{_, profile}] -> profile
|
||||
[] -> nil
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Lists all cached profiles.
|
||||
"""
|
||||
def list_profiles do
|
||||
@table
|
||||
|> :ets.match_object({{:profile, :_}, :_})
|
||||
|> Enum.map(fn {_, profile} -> profile end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Matches a profile based on system info (sysObjectID or sysDescr).
|
||||
Returns the best matching profile or nil.
|
||||
"""
|
||||
def match_profile(system_info) do
|
||||
sys_object_id = Map.get(system_info, :sys_object_id, "")
|
||||
sys_descr = Map.get(system_info, :sys_descr, "")
|
||||
|
||||
# First try to match by sysObjectID (more specific)
|
||||
case match_by_oid(sys_object_id) do
|
||||
nil -> match_by_descr(sys_descr)
|
||||
profile -> profile
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Reloads all profiles from YAML files.
|
||||
"""
|
||||
def reload do
|
||||
GenServer.call(__MODULE__, :reload)
|
||||
end
|
||||
|
||||
# GenServer callbacks
|
||||
|
||||
@impl true
|
||||
def init(_opts) do
|
||||
# Create ETS table
|
||||
:ets.new(@table, [:named_table, :set, :public, read_concurrency: true])
|
||||
|
||||
# Load profiles
|
||||
load_all_profiles()
|
||||
|
||||
{:ok, %{}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call(:reload, _from, state) do
|
||||
:ets.delete_all_objects(@table)
|
||||
count = load_all_profiles()
|
||||
{:reply, {:ok, count}, state}
|
||||
end
|
||||
|
||||
# Private functions
|
||||
|
||||
defp load_all_profiles do
|
||||
profiles_path = Path.join(:code.priv_dir(:towerops), @profiles_dir)
|
||||
detection_path = Path.join(profiles_path, "os_detection")
|
||||
|
||||
if File.exists?(detection_path) do
|
||||
yaml_files =
|
||||
detection_path
|
||||
|> Path.join("*.yaml")
|
||||
|> Path.wildcard()
|
||||
|
||||
Enum.each(yaml_files, fn file ->
|
||||
name = Path.basename(file, ".yaml")
|
||||
load_profile(profiles_path, name)
|
||||
end)
|
||||
|
||||
count = length(yaml_files)
|
||||
Logger.info("Loaded #{count} device profiles from YAML files")
|
||||
count
|
||||
else
|
||||
Logger.debug("No profiles directory found at #{detection_path}")
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
defp load_profile(profiles_path, name) do
|
||||
detection_file = Path.join([profiles_path, "os_detection", "#{name}.yaml"])
|
||||
discovery_file = Path.join([profiles_path, "os_discovery", "#{name}.yaml"])
|
||||
|
||||
with {:ok, detection} <- read_yaml(detection_file) do
|
||||
discovery = read_yaml_optional(discovery_file)
|
||||
|
||||
profile = build_profile(name, detection, discovery)
|
||||
:ets.insert(@table, {{:profile, name}, profile})
|
||||
|
||||
# Index by detection OID for fast lookups
|
||||
if profile.detection_oid do
|
||||
:ets.insert(@table, {{:oid, profile.detection_oid}, name})
|
||||
end
|
||||
|
||||
# Index by detection patterns
|
||||
Enum.each(profile.detection_patterns, fn pattern ->
|
||||
:ets.insert(@table, {{:pattern, pattern}, name})
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
defp build_profile(name, detection, discovery) do
|
||||
%{
|
||||
name: name,
|
||||
vendor: Map.get(detection, "text") || String.capitalize(name),
|
||||
detection_oid: extract_detection_oid(detection),
|
||||
detection_patterns: extract_detection_patterns(detection),
|
||||
device_oids: extract_device_oids(discovery),
|
||||
sensor_oids: extract_sensor_oids(discovery)
|
||||
}
|
||||
end
|
||||
|
||||
defp extract_detection_oid(yaml) do
|
||||
case get_in(yaml, ["discovery"]) do
|
||||
discovery when is_list(discovery) ->
|
||||
discovery
|
||||
|> Enum.flat_map(fn
|
||||
%{"sysObjectID" => patterns} when is_list(patterns) -> patterns
|
||||
_ -> []
|
||||
end)
|
||||
|> List.first()
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_detection_patterns(yaml) do
|
||||
case get_in(yaml, ["discovery"]) do
|
||||
discovery when is_list(discovery) ->
|
||||
Enum.flat_map(discovery, fn
|
||||
%{"sysDescr" => patterns} when is_list(patterns) -> patterns
|
||||
_ -> []
|
||||
end)
|
||||
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_device_oids(yaml) do
|
||||
os_module = get_in(yaml, ["modules", "os"]) || %{}
|
||||
|
||||
field_mappings = [
|
||||
{"serial", :serial_number},
|
||||
{"version", :firmware_version},
|
||||
{"hardware", :hardware},
|
||||
{"lat", :latitude},
|
||||
{"long", :longitude}
|
||||
]
|
||||
|
||||
field_mappings
|
||||
|> Enum.flat_map(fn {yaml_field, field} ->
|
||||
case Map.get(os_module, yaml_field) do
|
||||
nil -> []
|
||||
mib_names when is_list(mib_names) -> [{field, List.first(mib_names)}]
|
||||
mib_name when is_binary(mib_name) -> [{field, mib_name}]
|
||||
_ -> []
|
||||
end
|
||||
end)
|
||||
|> Map.new()
|
||||
end
|
||||
|
||||
defp extract_sensor_oids(yaml) do
|
||||
sensors_module = get_in(yaml, ["modules", "sensors"]) || %{}
|
||||
sensor_types = ["temperature", "voltage", "current", "power", "fanspeed"]
|
||||
|
||||
Enum.flat_map(sensor_types, fn sensor_type ->
|
||||
case get_in(sensors_module, [sensor_type, "data"]) do
|
||||
data when is_list(data) ->
|
||||
data
|
||||
|> Enum.map(&parse_sensor(&1, sensor_type))
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp parse_sensor(sensor_def, sensor_type) do
|
||||
mib_name = Map.get(sensor_def, "oid") || Map.get(sensor_def, "value")
|
||||
num_oid = Map.get(sensor_def, "num_oid")
|
||||
|
||||
# Skip table-based sensors (with {{ $index }})
|
||||
cond do
|
||||
is_binary(num_oid) and String.contains?(num_oid, "{{ $index }}") ->
|
||||
nil
|
||||
|
||||
is_binary(mib_name) and String.ends_with?(mib_name, ".0") ->
|
||||
%{
|
||||
sensor_type: sensor_type,
|
||||
mib_name: mib_name,
|
||||
sensor_descr: extract_descr(sensor_def, sensor_type),
|
||||
sensor_unit: Map.get(sensor_def, "unit"),
|
||||
sensor_divisor: Map.get(sensor_def, "divisor", 1)
|
||||
}
|
||||
|
||||
true ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_descr(sensor_def, default) do
|
||||
case Map.get(sensor_def, "descr") do
|
||||
nil ->
|
||||
default
|
||||
|
||||
descr when is_binary(descr) ->
|
||||
cleaned = descr |> String.replace(~r/\{\{ [^}]+ \}\}/, "") |> String.trim()
|
||||
if cleaned == "", do: default, else: cleaned
|
||||
|
||||
_ ->
|
||||
default
|
||||
end
|
||||
end
|
||||
|
||||
defp match_by_oid(sys_object_id) when is_binary(sys_object_id) and sys_object_id != "" do
|
||||
# Try exact match first, then prefix matches
|
||||
list_profiles()
|
||||
|> Enum.filter(fn profile ->
|
||||
profile.detection_oid && String.contains?(sys_object_id, profile.detection_oid)
|
||||
end)
|
||||
|> Enum.max_by(fn profile -> String.length(profile.detection_oid || "") end, fn -> nil end)
|
||||
end
|
||||
|
||||
defp match_by_oid(_), do: nil
|
||||
|
||||
defp match_by_descr(sys_descr) when is_binary(sys_descr) and sys_descr != "" do
|
||||
Enum.find(list_profiles(), fn profile ->
|
||||
Enum.any?(profile.detection_patterns, fn pattern ->
|
||||
Regex.match?(~r/#{Regex.escape(pattern)}/i, sys_descr)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
defp match_by_descr(_), do: nil
|
||||
|
||||
defp read_yaml(file_path) do
|
||||
with {:ok, content} <- File.read(file_path),
|
||||
{:ok, data} <- YamlElixir.read_from_string(content) do
|
||||
data =
|
||||
case data do
|
||||
[d] -> d
|
||||
d when is_map(d) -> d
|
||||
_ -> data
|
||||
end
|
||||
|
||||
{:ok, data}
|
||||
end
|
||||
end
|
||||
|
||||
defp read_yaml_optional(file_path) do
|
||||
case read_yaml(file_path) do
|
||||
{:ok, data} -> data
|
||||
_ -> %{}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -16,8 +16,7 @@ defmodule Towerops.Snmp.Discovery do
|
|||
|
||||
alias Towerops.Devices
|
||||
alias Towerops.Devices.Device, as: DeviceSchema
|
||||
alias Towerops.Profiles
|
||||
alias Towerops.Profiles.DeviceProfile
|
||||
alias Towerops.Profiles.YamlProfiles
|
||||
alias Towerops.Repo
|
||||
alias Towerops.Snmp.Client
|
||||
alias Towerops.Snmp.DeferredDiscovery
|
||||
|
|
@ -25,11 +24,7 @@ defmodule Towerops.Snmp.Discovery do
|
|||
alias Towerops.Snmp.Interface
|
||||
alias Towerops.Snmp.NeighborDiscovery
|
||||
alias Towerops.Snmp.Profiles.Base
|
||||
alias Towerops.Snmp.Profiles.Cisco
|
||||
alias Towerops.Snmp.Profiles.Dynamic
|
||||
alias Towerops.Snmp.Profiles.Mikrotik
|
||||
alias Towerops.Snmp.Profiles.NetSnmp
|
||||
alias Towerops.Snmp.Profiles.Ubiquiti
|
||||
alias Towerops.Snmp.Sensor
|
||||
alias Towerops.Snmp.Vlan
|
||||
|
||||
|
|
@ -79,7 +74,7 @@ defmodule Towerops.Snmp.Discovery do
|
|||
optional(atom()) => term()
|
||||
}
|
||||
|
||||
@type profile :: module() | {:dynamic, DeviceProfile.t()}
|
||||
@type profile :: module() | {:yaml, map()}
|
||||
|
||||
@type discovery_summary :: %{
|
||||
success: non_neg_integer(),
|
||||
|
|
@ -262,70 +257,39 @@ defmodule Towerops.Snmp.Discovery do
|
|||
@doc """
|
||||
Selects the appropriate SNMP profile based on system information.
|
||||
|
||||
First tries to match against database-stored device profiles.
|
||||
If no database match is found, falls back to hard-coded profile modules.
|
||||
First tries to match against database-stored device profiles (imported from LibreNMS).
|
||||
If no database match is found, falls back to the generic Base profile.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> select_profile(%{sys_descr: "Cisco IOS"})
|
||||
Towerops.Snmp.Profiles.Cisco
|
||||
{:dynamic, %DeviceProfile{name: "ios"}}
|
||||
|
||||
iex> select_profile(%{sys_object_id: "1.3.6.1.4.1.41112"})
|
||||
Towerops.Snmp.Profiles.Ubiquiti
|
||||
{:dynamic, %DeviceProfile{name: "airos"}}
|
||||
"""
|
||||
@spec select_profile(system_info()) :: profile()
|
||||
def select_profile(system_info) do
|
||||
# First try database profiles
|
||||
case Profiles.match_profile(system_info) do
|
||||
%DeviceProfile{} = profile ->
|
||||
Logger.debug("Selected database profile: #{profile.name}")
|
||||
{:dynamic, profile}
|
||||
case YamlProfiles.match_profile(system_info) do
|
||||
%{name: name} = profile ->
|
||||
Logger.debug("Selected YAML profile: #{name}")
|
||||
{:yaml, profile}
|
||||
|
||||
nil ->
|
||||
# Fall back to hardcoded profiles
|
||||
select_hardcoded_profile(system_info)
|
||||
end
|
||||
end
|
||||
|
||||
defp select_hardcoded_profile(system_info) do
|
||||
sys_descr = Map.get(system_info, :sys_descr, "")
|
||||
sys_object_id = Map.get(system_info, :sys_object_id, "")
|
||||
|
||||
cond do
|
||||
String.contains?(sys_descr, "RouterOS") ->
|
||||
Logger.debug("Selected MikroTik profile")
|
||||
Mikrotik
|
||||
|
||||
String.contains?(sys_descr, ["Cisco", "IOS"]) ->
|
||||
Logger.debug("Selected Cisco profile")
|
||||
Cisco
|
||||
|
||||
# Ubiquiti devices (check before generic Linux since they run Linux)
|
||||
# OID 10002 = old Ubiquiti/UniFi devices, 41112 = newer airOS devices
|
||||
String.contains?(sys_object_id, ["10002", "41112"]) or
|
||||
String.contains?(sys_descr, ["airOS", "AirFiber", "airMAX", "Ubiquiti"]) ->
|
||||
Logger.debug("Selected Ubiquiti profile")
|
||||
Ubiquiti
|
||||
|
||||
String.contains?(sys_descr, "Linux") ->
|
||||
Logger.debug("Selected NetSNMP profile")
|
||||
NetSnmp
|
||||
|
||||
true ->
|
||||
Logger.debug("Selected Base profile")
|
||||
Logger.debug("No YAML profile matched, using Base profile")
|
||||
Base
|
||||
end
|
||||
end
|
||||
|
||||
@spec build_device_info(Client.connection_opts(), system_info(), profile()) ::
|
||||
{:ok, device_info()}
|
||||
defp build_device_info(client_opts, system_info, {:dynamic, profile}) do
|
||||
# Use dynamic profile to identify device
|
||||
defp build_device_info(client_opts, system_info, {:yaml, profile}) do
|
||||
# Use YAML profile to identify device
|
||||
identified_info = Dynamic.identify_device(profile, client_opts, system_info)
|
||||
{:ok, identified_info}
|
||||
rescue
|
||||
error ->
|
||||
Logger.error("Failed to identify device with dynamic profile: #{inspect(error)}")
|
||||
Logger.error("Failed to identify device with YAML profile: #{inspect(error)}")
|
||||
{:ok, system_info}
|
||||
end
|
||||
|
||||
|
|
@ -380,7 +344,7 @@ defmodule Towerops.Snmp.Discovery do
|
|||
|
||||
@spec discover_interfaces(Client.connection_opts(), profile()) ::
|
||||
{:ok, [interface_data()]}
|
||||
defp discover_interfaces(client_opts, {:dynamic, profile}) do
|
||||
defp discover_interfaces(client_opts, {:yaml, profile}) do
|
||||
case Dynamic.discover_interfaces(profile, client_opts) do
|
||||
{:ok, interfaces} ->
|
||||
Logger.debug("Discovered #{length(interfaces)} interfaces")
|
||||
|
|
@ -405,7 +369,7 @@ defmodule Towerops.Snmp.Discovery do
|
|||
end
|
||||
|
||||
@spec discover_sensors(Client.connection_opts(), profile()) :: {:ok, [sensor_data()]}
|
||||
defp discover_sensors(client_opts, {:dynamic, profile}) do
|
||||
defp discover_sensors(client_opts, {:yaml, profile}) do
|
||||
{:ok, sensors} = Dynamic.discover_sensors(profile, client_opts)
|
||||
Logger.debug("Discovered #{length(sensors)} sensors")
|
||||
{:ok, sensors}
|
||||
|
|
@ -424,7 +388,7 @@ defmodule Towerops.Snmp.Discovery do
|
|||
end
|
||||
|
||||
@spec discover_vlans(Client.connection_opts(), profile()) :: {:ok, [map()]}
|
||||
defp discover_vlans(client_opts, {:dynamic, _profile}) do
|
||||
defp discover_vlans(client_opts, {:yaml, _profile}) do
|
||||
# Dynamic profiles use Base VLAN discovery
|
||||
{:ok, vlans} = Base.discover_vlans(client_opts)
|
||||
Logger.debug("Discovered #{length(vlans)} VLANs")
|
||||
|
|
|
|||
|
|
@ -84,7 +84,12 @@ defmodule Towerops.Snmp.MibTranslator do
|
|||
"UCD-SNMP-MIB::memAvailSwap" => "1.3.6.1.4.1.2021.4.4.0",
|
||||
# Ubiquiti (enterprise 41112)
|
||||
"UBNT-UniFi-MIB::unifiApSystemModel" => "1.3.6.1.4.1.41112.1.6.3.3.0",
|
||||
"UBNT-UniFi-MIB::unifiApSystemVersion" => "1.3.6.1.4.1.41112.1.6.3.6.0"
|
||||
"UBNT-UniFi-MIB::unifiApSystemVersion" => "1.3.6.1.4.1.41112.1.6.3.6.0",
|
||||
# Cambium ePMP (enterprise 17713.21)
|
||||
"CAMBIUM-PMP80211-MIB::cambiumCurrentuImageVersion.0" => "1.3.6.1.4.1.17713.21.1.1.17.0",
|
||||
"CAMBIUM-PMP80211-MIB::cambiumEPMPMSN.0" => "1.3.6.1.4.1.17713.21.1.1.31.0",
|
||||
"CAMBIUM-PMP80211-MIB::cambiumDeviceLatitude.0" => "1.3.6.1.4.1.17713.21.1.1.18.0",
|
||||
"CAMBIUM-PMP80211-MIB::cambiumDeviceLongitude.0" => "1.3.6.1.4.1.17713.21.1.1.19.0"
|
||||
}
|
||||
|
||||
@doc """
|
||||
|
|
@ -146,7 +151,9 @@ defmodule Towerops.Snmp.MibTranslator do
|
|||
|
||||
defp try_snmptranslate(mib_name) do
|
||||
# Get MIB directories from config or use default
|
||||
mib_dirs = Application.get_env(:towerops, :mib_dirs, ["/app/priv/mibs", "/usr/share/snmp/mibs"])
|
||||
# Use :code.priv_dir to find the actual priv directory location in releases
|
||||
default_priv_mibs = Path.join(:code.priv_dir(:towerops), "mibs")
|
||||
mib_dirs = Application.get_env(:towerops, :mib_dirs, [default_priv_mibs, "/usr/share/snmp/mibs"])
|
||||
|
||||
# Expand directories to include subdirectories (snmptranslate doesn't search recursively)
|
||||
expanded_dirs = Enum.flat_map(mib_dirs, &expand_mib_directory/1)
|
||||
|
|
|
|||
|
|
@ -831,10 +831,11 @@ defmodule Towerops.Snmp.Profiles.Base do
|
|||
|
||||
# Calculate divisor from scale and precision (like LibreNMS)
|
||||
# Scale: 10^scale exponent, Precision: decimal places
|
||||
# Always returns an integer for compatibility with Ecto schema
|
||||
defp calculate_divisor(scale, precision) when is_integer(scale) and is_integer(precision) do
|
||||
scale_factor = Map.get(@scale_factors, scale, 1)
|
||||
precision_factor = :math.pow(10, precision)
|
||||
scale_factor * precision_factor
|
||||
precision_factor = Integer.pow(10, precision)
|
||||
round(scale_factor * precision_factor)
|
||||
end
|
||||
|
||||
defp calculate_divisor(scale, _) when is_integer(scale), do: scale_to_divisor(scale)
|
||||
|
|
@ -852,19 +853,95 @@ defmodule Towerops.Snmp.Profiles.Base do
|
|||
end)
|
||||
end
|
||||
|
||||
defp parse_sys_descr(sys_descr, _sys_object_id) do
|
||||
# Vendor detection patterns: {descr_patterns, oid_patterns, manufacturer, model_extractor}
|
||||
@vendor_patterns [
|
||||
{["Cisco", "IOS"], [], "Cisco", :cisco},
|
||||
{["Cambium", "ePMP", "PMP", "PTP", "cnPilot"], ["17713"], "Cambium Networks", :cambium},
|
||||
{["Ubiquiti", "airOS", "AirFiber", "airMAX", "EdgeOS"], ["10002", "41112"], "Ubiquiti", :ubiquiti},
|
||||
{["MikroTik", "RouterOS"], ["14988"], "MikroTik", :mikrotik},
|
||||
{["Linux"], [], "Linux", :linux},
|
||||
{["Windows"], [], "Microsoft", :windows}
|
||||
]
|
||||
|
||||
defp parse_sys_descr(sys_descr, sys_object_id) do
|
||||
case find_vendor_match(sys_descr, sys_object_id) do
|
||||
{manufacturer, extractor} -> {manufacturer, extract_model(extractor, sys_descr, sys_object_id)}
|
||||
nil -> {"Unknown", "Generic Device"}
|
||||
end
|
||||
end
|
||||
|
||||
defp find_vendor_match(sys_descr, sys_object_id) do
|
||||
Enum.find_value(@vendor_patterns, fn {descr_patterns, oid_patterns, manufacturer, extractor} ->
|
||||
descr_match = descr_patterns != [] and String.contains?(sys_descr, descr_patterns)
|
||||
oid_match = oid_patterns != [] and String.contains?(sys_object_id, oid_patterns)
|
||||
|
||||
if descr_match or oid_match do
|
||||
{manufacturer, extractor}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp extract_model(:cisco, sys_descr, _sys_object_id), do: extract_cisco_model(sys_descr)
|
||||
defp extract_model(:cambium, sys_descr, sys_object_id), do: extract_cambium_model(sys_descr, sys_object_id)
|
||||
defp extract_model(:ubiquiti, sys_descr, _sys_object_id), do: extract_ubiquiti_model(sys_descr)
|
||||
defp extract_model(:mikrotik, sys_descr, _sys_object_id), do: extract_mikrotik_model(sys_descr)
|
||||
defp extract_model(:linux, sys_descr, _sys_object_id), do: sys_descr
|
||||
defp extract_model(:windows, sys_descr, _sys_object_id), do: sys_descr
|
||||
|
||||
# Cambium sysObjectID product line mapping
|
||||
# Format: .1.3.6.1.4.1.17713.X.Y.Z where X indicates product line
|
||||
# Order matters - more specific patterns first (21 before 2)
|
||||
@cambium_product_lines [
|
||||
{~r/\.17713\.21\./, "ePMP"},
|
||||
{~r/\.17713\.21$/, "ePMP"},
|
||||
{~r/\.17713\.[12]\./, "PMP"},
|
||||
{~r/\.17713\.[12]$/, "PMP"},
|
||||
{~r/\.17713\.[678]\./, "PTP"},
|
||||
{~r/\.17713\.[678]$/, "PTP"},
|
||||
{~r/\.17713\.9\./, "cnPilot"},
|
||||
{~r/\.17713\.9$/, "cnPilot"}
|
||||
]
|
||||
|
||||
defp extract_cambium_model(sys_descr, sys_object_id) do
|
||||
# First try to extract from sysDescr, then fall back to sysObjectID product line
|
||||
extract_cambium_model_from_descr(sys_descr) ||
|
||||
extract_cambium_product_line(sys_object_id) ||
|
||||
"Wireless"
|
||||
end
|
||||
|
||||
defp extract_cambium_model_from_descr(sys_descr) do
|
||||
cond do
|
||||
String.contains?(sys_descr, ["Cisco", "IOS"]) ->
|
||||
{"Cisco", extract_cisco_model(sys_descr)}
|
||||
match = Regex.run(~r/ePMP\s*(\d+)/i, sys_descr) -> "ePMP #{Enum.at(match, 1)}"
|
||||
Regex.match?(~r/ePMP/i, sys_descr) -> "ePMP"
|
||||
match = Regex.run(~r/PMP\s*(\d+)/i, sys_descr) -> "PMP #{Enum.at(match, 1)}"
|
||||
match = Regex.run(~r/PTP\s*(\d+)/i, sys_descr) -> "PTP #{Enum.at(match, 1)}"
|
||||
match = Regex.run(~r/cnPilot\s*(\w+)/i, sys_descr) -> "cnPilot #{Enum.at(match, 1)}"
|
||||
Regex.match?(~r/cnPilot/i, sys_descr) -> "cnPilot"
|
||||
true -> nil
|
||||
end
|
||||
end
|
||||
|
||||
String.contains?(sys_descr, "Linux") ->
|
||||
{"Linux", "Server"}
|
||||
defp extract_cambium_product_line(sys_object_id) when is_binary(sys_object_id) do
|
||||
Enum.find_value(@cambium_product_lines, fn {regex, product} ->
|
||||
if Regex.match?(regex, sys_object_id), do: product
|
||||
end)
|
||||
end
|
||||
|
||||
String.contains?(sys_descr, "Windows") ->
|
||||
{"Microsoft", "Windows Server"}
|
||||
defp extract_cambium_product_line(_), do: nil
|
||||
|
||||
true ->
|
||||
{"Unknown", "Generic Device"}
|
||||
defp extract_ubiquiti_model(sys_descr) do
|
||||
cond do
|
||||
match = Regex.run(~r/(AF-?\w+)/i, sys_descr) -> hd(match)
|
||||
match = Regex.run(~r/(LBE-?\w+|NBE-?\w+|NSM\d+|PBE-?\w+)/i, sys_descr) -> hd(match)
|
||||
Regex.match?(~r/EdgeOS/i, sys_descr) -> "EdgeRouter"
|
||||
true -> "Wireless"
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_mikrotik_model(sys_descr) do
|
||||
case Regex.run(~r/(RB\w+|CCR\w+|CRS\w+|hAP\w*|hEX\w*)/i, sys_descr) do
|
||||
[model | _] -> model
|
||||
_ -> "RouterOS"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,379 +0,0 @@
|
|||
defmodule Towerops.Snmp.Profiles.Cisco do
|
||||
@moduledoc """
|
||||
Cisco-specific SNMP device profile.
|
||||
Extends Base profile with Cisco-specific sensor discovery from CISCO-ENTITY-SENSOR-MIB.
|
||||
|
||||
Discovers additional sensors:
|
||||
- Temperature sensors (chassis, modules, power supplies)
|
||||
- Power supply sensors
|
||||
- Fan speed sensors
|
||||
- Voltage sensors
|
||||
"""
|
||||
|
||||
alias Towerops.Snmp.Client
|
||||
alias Towerops.Snmp.Discovery
|
||||
alias Towerops.Snmp.Profiles.Base
|
||||
|
||||
require Logger
|
||||
|
||||
@cisco_sensor_oids %{
|
||||
# CISCO-ENTITY-SENSOR-MIB
|
||||
ent_sensor_type: "1.3.6.1.4.1.9.9.91.1.1.1.1.1",
|
||||
ent_sensor_scale: "1.3.6.1.4.1.9.9.91.1.1.1.1.2",
|
||||
ent_sensor_precision: "1.3.6.1.4.1.9.9.91.1.1.1.1.3",
|
||||
ent_sensor_value: "1.3.6.1.4.1.9.9.91.1.1.1.1.4",
|
||||
ent_sensor_status: "1.3.6.1.4.1.9.9.91.1.1.1.1.5",
|
||||
|
||||
# ENTITY-MIB for physical descriptions
|
||||
ent_phys_descr: "1.3.6.1.2.1.47.1.1.1.1.2",
|
||||
ent_phys_name: "1.3.6.1.2.1.47.1.1.1.1.7"
|
||||
}
|
||||
|
||||
@cisco_vtp_oids %{
|
||||
# CISCO-VTP-MIB - VTP VLAN discovery
|
||||
vtp_vlan_state: "1.3.6.1.4.1.9.9.46.1.3.1.1.2",
|
||||
vtp_vlan_type: "1.3.6.1.4.1.9.9.46.1.3.1.1.3",
|
||||
vtp_vlan_name: "1.3.6.1.4.1.9.9.46.1.3.1.1.4"
|
||||
}
|
||||
|
||||
@doc """
|
||||
Discovers system information.
|
||||
Cisco profile doesn't query additional system OIDs beyond Base profile.
|
||||
"""
|
||||
@spec discover_system_info(Client.connection_opts()) ::
|
||||
{:ok, Discovery.system_info()} | {:error, term()}
|
||||
def discover_system_info(_client_opts) do
|
||||
# No additional system info beyond Base profile
|
||||
{:ok, %{}}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Discovers network interfaces using Base profile.
|
||||
"""
|
||||
@spec discover_interfaces(Client.connection_opts()) ::
|
||||
{:ok, [Discovery.interface_data()]} | {:error, term()}
|
||||
def discover_interfaces(client_opts) do
|
||||
Base.discover_interfaces(client_opts)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Discovers sensors from both standard ENTITY-SENSOR-MIB and CISCO-ENTITY-SENSOR-MIB.
|
||||
Cisco-specific sensors provide more detailed temperature, power, and fan monitoring.
|
||||
"""
|
||||
@spec discover_sensors(Client.connection_opts()) ::
|
||||
{:ok, [Discovery.sensor_data()]} | {:error, term()}
|
||||
def discover_sensors(client_opts) do
|
||||
# Try Cisco-specific sensors first
|
||||
case discover_cisco_sensors(client_opts) do
|
||||
{:ok, [_ | _] = sensors} ->
|
||||
{:ok, sensors}
|
||||
|
||||
_ ->
|
||||
# Fall back to standard ENTITY-SENSOR-MIB
|
||||
Logger.debug("Cisco-specific sensors not available, using standard discovery")
|
||||
Base.discover_sensors(client_opts)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Discovers VLANs from Cisco VTP MIB, falling back to Q-BRIDGE-MIB if VTP not available.
|
||||
"""
|
||||
@spec discover_vlans(Client.connection_opts()) :: {:ok, [map()]}
|
||||
def discover_vlans(client_opts) do
|
||||
# Try Cisco VTP MIB first
|
||||
case Client.walk(client_opts, @cisco_vtp_oids.vtp_vlan_name) do
|
||||
{:ok, name_results} when is_map(name_results) and map_size(name_results) > 0 ->
|
||||
do_discover_vtp_vlans(client_opts, name_results)
|
||||
|
||||
_ ->
|
||||
# Fall back to Q-BRIDGE-MIB
|
||||
Logger.debug("Cisco VTP MIB not available, falling back to Q-BRIDGE-MIB")
|
||||
Base.discover_vlans(client_opts)
|
||||
end
|
||||
end
|
||||
|
||||
defp do_discover_vtp_vlans(client_opts, name_results) do
|
||||
# Fetch VLAN states
|
||||
state_map =
|
||||
case Client.walk(client_opts, @cisco_vtp_oids.vtp_vlan_state) do
|
||||
{:ok, results} when is_map(results) -> build_vtp_vlan_index_map(results)
|
||||
_ -> %{}
|
||||
end
|
||||
|
||||
# Fetch VLAN types
|
||||
type_map =
|
||||
case Client.walk(client_opts, @cisco_vtp_oids.vtp_vlan_type) do
|
||||
{:ok, results} when is_map(results) -> build_vtp_vlan_index_map(results)
|
||||
_ -> %{}
|
||||
end
|
||||
|
||||
# Build VLAN list from names (name_results is a map of OID -> name)
|
||||
vlans =
|
||||
name_results
|
||||
|> Enum.map(fn {oid, name} ->
|
||||
vlan_id = extract_vtp_vlan_id_from_oid(oid)
|
||||
state = Map.get(state_map, vlan_id, 1)
|
||||
type = Map.get(type_map, vlan_id, 1)
|
||||
|
||||
if valid_vlan_id?(vlan_id) do
|
||||
%{
|
||||
vlan_id: vlan_id,
|
||||
vlan_name: name,
|
||||
vlan_type: vtp_vlan_type_to_string(type),
|
||||
status: vtp_vlan_state_to_status(state),
|
||||
last_checked_at: DateTime.utc_now()
|
||||
}
|
||||
end
|
||||
end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|
||||
Logger.debug("Discovered #{length(vlans)} VLANs from Cisco VTP MIB")
|
||||
{:ok, vlans}
|
||||
end
|
||||
|
||||
defp build_vtp_vlan_index_map(results) when is_map(results) do
|
||||
Map.new(results, fn {oid, value} ->
|
||||
vlan_id = extract_vtp_vlan_id_from_oid(oid)
|
||||
{vlan_id, value}
|
||||
end)
|
||||
end
|
||||
|
||||
# VTP VLAN OIDs are indexed by managementDomainIndex.vlanIndex
|
||||
# e.g., 1.3.6.1.4.1.9.9.46.1.3.1.1.4.1.100 = domain 1, VLAN 100
|
||||
defp extract_vtp_vlan_id_from_oid(oid) when is_binary(oid) do
|
||||
oid
|
||||
|> String.split(".")
|
||||
|> List.last()
|
||||
|> String.to_integer()
|
||||
rescue
|
||||
_ -> 0
|
||||
end
|
||||
|
||||
defp extract_vtp_vlan_id_from_oid(_), do: 0
|
||||
|
||||
defp valid_vlan_id?(vlan_id) when is_integer(vlan_id) do
|
||||
vlan_id >= 1 and vlan_id <= 4094
|
||||
end
|
||||
|
||||
defp valid_vlan_id?(_), do: false
|
||||
|
||||
# CISCO-VTP-MIB vtpVlanState values:
|
||||
# 1=operational, 2=suspended, 3=mtuTooBigForDevice, 4=mtuTooBigForTrunk
|
||||
defp vtp_vlan_state_to_status(1), do: "active"
|
||||
defp vtp_vlan_state_to_status(_), do: "suspended"
|
||||
|
||||
# CISCO-VTP-MIB vtpVlanType values:
|
||||
# 1=ethernet, 2=fddi, 3=tokenRing, 4=fddiNet, 5=trNet, 6=deprecated
|
||||
defp vtp_vlan_type_to_string(1), do: "ethernet"
|
||||
defp vtp_vlan_type_to_string(2), do: "fddi"
|
||||
defp vtp_vlan_type_to_string(3), do: "tokenRing"
|
||||
defp vtp_vlan_type_to_string(4), do: "fddiNet"
|
||||
defp vtp_vlan_type_to_string(5), do: "trNet"
|
||||
defp vtp_vlan_type_to_string(_), do: "ethernet"
|
||||
|
||||
@doc """
|
||||
Identifies Cisco device model more accurately from sysDescr.
|
||||
"""
|
||||
@spec identify_device(Discovery.system_info()) :: Discovery.device_info()
|
||||
def identify_device(system_info) do
|
||||
sys_descr = Map.get(system_info, :sys_descr, "")
|
||||
|
||||
model = extract_cisco_model_detailed(sys_descr)
|
||||
|
||||
Map.merge(system_info, %{
|
||||
manufacturer: "Cisco",
|
||||
model: model,
|
||||
firmware_version: extract_firmware_version(sys_descr)
|
||||
})
|
||||
end
|
||||
|
||||
# Private functions
|
||||
|
||||
defp discover_cisco_sensors(client_opts) do
|
||||
case Client.walk(client_opts, @cisco_sensor_oids.ent_sensor_type) do
|
||||
{:ok, sensor_types} when map_size(sensor_types) > 0 ->
|
||||
sensor_indices = extract_indices_from_oids(sensor_types)
|
||||
|
||||
sensors =
|
||||
sensor_indices
|
||||
|> Enum.map(fn index ->
|
||||
build_cisco_sensor(client_opts, index)
|
||||
end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|
||||
{:ok, sensors}
|
||||
|
||||
_ ->
|
||||
{:error, :cisco_sensors_not_available}
|
||||
end
|
||||
end
|
||||
|
||||
defp build_cisco_sensor(client_opts, index) do
|
||||
oids = [
|
||||
@cisco_sensor_oids.ent_sensor_type <> ".#{index}",
|
||||
@cisco_sensor_oids.ent_sensor_scale <> ".#{index}",
|
||||
@cisco_sensor_oids.ent_sensor_precision <> ".#{index}",
|
||||
@cisco_sensor_oids.ent_sensor_value <> ".#{index}",
|
||||
@cisco_sensor_oids.ent_sensor_status <> ".#{index}",
|
||||
@cisco_sensor_oids.ent_phys_descr <> ".#{index}",
|
||||
@cisco_sensor_oids.ent_phys_name <> ".#{index}"
|
||||
]
|
||||
|
||||
case Client.get_multiple(client_opts, oids) do
|
||||
{:ok, [type, scale, precision, value, status, descr, name]} ->
|
||||
sensor_type = parse_cisco_sensor_type(type)
|
||||
divisor = calculate_divisor(scale, precision)
|
||||
|
||||
%{
|
||||
sensor_type: sensor_type,
|
||||
sensor_index: "#{index}",
|
||||
sensor_oid: @cisco_sensor_oids.ent_sensor_value <> ".#{index}",
|
||||
sensor_descr: parse_sensor_description(descr, name),
|
||||
sensor_unit: cisco_sensor_type_to_unit(sensor_type),
|
||||
sensor_divisor: divisor,
|
||||
last_value: parse_sensor_value(value, divisor),
|
||||
status: parse_cisco_sensor_status(status)
|
||||
}
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.debug("Failed to fetch Cisco sensor #{index}: #{inspect(reason)}")
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_indices_from_oids(oid_map) do
|
||||
oid_map
|
||||
|> Map.keys()
|
||||
|> Enum.map(fn oid_string ->
|
||||
oid_string
|
||||
|> String.split(".")
|
||||
|> List.last()
|
||||
|> String.to_integer()
|
||||
end)
|
||||
end
|
||||
|
||||
defp extract_cisco_model_detailed(sys_descr) do
|
||||
cond do
|
||||
# Catalyst switches: "Cisco IOS Software, C2960 Software..."
|
||||
match = Regex.run(~r/\b(C\d+\w*|WS-C\d+\w*)\b/, sys_descr) ->
|
||||
[_, model] = match
|
||||
model
|
||||
|
||||
# ISR routers: "Cisco IOS Software, 1900 Software..."
|
||||
match = Regex.run(~r/\b(ISR\d+\w*|[0-9]{4})\b/, sys_descr) ->
|
||||
[_, model] = match
|
||||
model
|
||||
|
||||
# ASR routers
|
||||
match = Regex.run(~r/\b(ASR\d+\w*)\b/, sys_descr) ->
|
||||
[_, model] = match
|
||||
model
|
||||
|
||||
true ->
|
||||
"Unknown Cisco Model"
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_firmware_version(sys_descr) do
|
||||
# Try to extract version like "Version 15.2(4)E7"
|
||||
case Regex.run(~r/Version\s+([\d.()A-Za-z]+)/, sys_descr) do
|
||||
[_, version] -> version
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
# CISCO-ENTITY-SENSOR-MIB sensor types
|
||||
defp parse_cisco_sensor_type(1), do: "other"
|
||||
defp parse_cisco_sensor_type(2), do: "unknown"
|
||||
defp parse_cisco_sensor_type(3), do: "volts_ac"
|
||||
defp parse_cisco_sensor_type(4), do: "volts_dc"
|
||||
defp parse_cisco_sensor_type(5), do: "amperes"
|
||||
defp parse_cisco_sensor_type(6), do: "watts"
|
||||
defp parse_cisco_sensor_type(7), do: "hertz"
|
||||
defp parse_cisco_sensor_type(8), do: "celsius"
|
||||
defp parse_cisco_sensor_type(9), do: "percent_rh"
|
||||
defp parse_cisco_sensor_type(10), do: "rpm"
|
||||
defp parse_cisco_sensor_type(11), do: "cmm"
|
||||
defp parse_cisco_sensor_type(12), do: "truthvalue"
|
||||
defp parse_cisco_sensor_type(13), do: "specialenum"
|
||||
defp parse_cisco_sensor_type(14), do: "dBm"
|
||||
defp parse_cisco_sensor_type(_), do: "unknown"
|
||||
|
||||
defp cisco_sensor_type_to_unit("celsius"), do: "°C"
|
||||
defp cisco_sensor_type_to_unit("volts_ac"), do: "VAC"
|
||||
defp cisco_sensor_type_to_unit("volts_dc"), do: "VDC"
|
||||
defp cisco_sensor_type_to_unit("amperes"), do: "A"
|
||||
defp cisco_sensor_type_to_unit("watts"), do: "W"
|
||||
defp cisco_sensor_type_to_unit("hertz"), do: "Hz"
|
||||
defp cisco_sensor_type_to_unit("percent_rh"), do: "%RH"
|
||||
defp cisco_sensor_type_to_unit("rpm"), do: "RPM"
|
||||
defp cisco_sensor_type_to_unit("dBm"), do: "dBm"
|
||||
defp cisco_sensor_type_to_unit(_), do: ""
|
||||
|
||||
# Calculate divisor from scale and precision
|
||||
# Scale: 10^scale (e.g., -3 = milli, 0 = units)
|
||||
# Precision: number of decimal places
|
||||
defp calculate_divisor(scale, _precision) when is_integer(scale) do
|
||||
case scale do
|
||||
-24 -> 1_000_000_000_000_000_000_000_000
|
||||
-12 -> 1_000_000_000_000
|
||||
-9 -> 1_000_000_000
|
||||
-6 -> 1_000_000
|
||||
-3 -> 1_000
|
||||
0 -> 1
|
||||
3 -> 0.001
|
||||
_ -> 1
|
||||
end
|
||||
end
|
||||
|
||||
defp calculate_divisor(_, _), do: 1
|
||||
|
||||
defp parse_sensor_value(value, divisor) when is_integer(value) and is_number(divisor) do
|
||||
value / divisor
|
||||
end
|
||||
|
||||
defp parse_sensor_value(_, _), do: nil
|
||||
|
||||
defp parse_sensor_description(descr, _name) when is_binary(descr) and byte_size(descr) > 0 do
|
||||
descr
|
||||
end
|
||||
|
||||
defp parse_sensor_description(_descr, name) when is_binary(name) and byte_size(name) > 0 do
|
||||
name
|
||||
end
|
||||
|
||||
defp parse_sensor_description(_, _), do: "Unknown Sensor"
|
||||
|
||||
# CISCO-ENTITY-SENSOR-MIB sensor status
|
||||
defp parse_cisco_sensor_status(1), do: "ok"
|
||||
defp parse_cisco_sensor_status(2), do: "unavailable"
|
||||
defp parse_cisco_sensor_status(3), do: "nonoperational"
|
||||
defp parse_cisco_sensor_status(_), do: "unknown"
|
||||
|
||||
@doc """
|
||||
Collects raw debug data for troubleshooting.
|
||||
Includes Cisco-specific sensor OIDs and entity tables.
|
||||
"""
|
||||
def collect_raw_debug_data(client_opts) do
|
||||
# Walk Cisco entity sensor table
|
||||
cisco_sensor_table =
|
||||
case Client.walk(client_opts, "1.3.6.1.4.1.9.9.91.1.1.1") do
|
||||
{:ok, results} -> results
|
||||
{:error, _} -> %{}
|
||||
end
|
||||
|
||||
# Walk entity physical table for descriptions
|
||||
entity_phys_table =
|
||||
case Client.walk(client_opts, "1.3.6.1.2.1.47.1.1.1") do
|
||||
{:ok, results} -> results
|
||||
{:error, _} -> %{}
|
||||
end
|
||||
|
||||
%{
|
||||
timestamp: DateTime.utc_now(),
|
||||
cisco_sensor_oids: @cisco_sensor_oids,
|
||||
cisco_sensor_table: cisco_sensor_table,
|
||||
entity_phys_table: entity_phys_table
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
defmodule Towerops.Snmp.Profiles.Dynamic do
|
||||
@moduledoc """
|
||||
Dynamic profile that uses database-stored profiles with MIB name translation.
|
||||
Allows profiles to be updated without redeployment.
|
||||
Dynamic profile that uses YAML-loaded profiles with MIB name translation.
|
||||
Profiles are loaded from priv/profiles/ and cached in ETS.
|
||||
"""
|
||||
|
||||
alias Towerops.Profiles
|
||||
alias Towerops.Profiles.DeviceProfile
|
||||
alias Towerops.Snmp.Client
|
||||
alias Towerops.Snmp.Discovery
|
||||
alias Towerops.Snmp.MibTranslator
|
||||
|
|
@ -16,38 +14,29 @@ defmodule Towerops.Snmp.Profiles.Dynamic do
|
|||
Identifies device using profile's device_oids.
|
||||
Translates MIB names to numeric OIDs and polls the device.
|
||||
"""
|
||||
@spec identify_device(DeviceProfile.t(), Client.connection_opts(), Discovery.system_info()) ::
|
||||
@spec identify_device(map(), Client.connection_opts(), Discovery.system_info()) ::
|
||||
Discovery.device_info()
|
||||
def identify_device(profile, client_opts, system_info) do
|
||||
# Get device OIDs from profile
|
||||
profile = Profiles.get_profile(profile.name)
|
||||
device_oids = Map.get(profile, :device_oids, %{})
|
||||
|
||||
device_data =
|
||||
profile.device_oids
|
||||
|> Enum.map(&fetch_device_oid_value(&1, client_opts))
|
||||
device_oids
|
||||
|> Enum.map(fn {field, mib_name} -> fetch_device_oid_value(field, mib_name, client_opts) end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|> Map.new()
|
||||
|
||||
# Format firmware version for MikroTik devices
|
||||
firmware_version =
|
||||
case {Map.get(device_data, :firmware_version), Map.get(device_data, :license_version)} do
|
||||
{fw, license} when not is_nil(fw) and not is_nil(license) ->
|
||||
"#{profile.vendor} RouterOS #{fw} (Level #{license})"
|
||||
|
||||
{fw, _} when not is_nil(fw) ->
|
||||
"#{profile.vendor} RouterOS #{fw}"
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
# Format firmware version (MikroTik has special formatting with license level)
|
||||
firmware_version = format_firmware_version(profile, device_data)
|
||||
|
||||
# Merge with system_info
|
||||
system_info
|
||||
|> Map.merge(%{
|
||||
manufacturer: profile.vendor,
|
||||
model: system_info.sys_descr,
|
||||
model: system_info[:sys_descr],
|
||||
firmware_version: firmware_version,
|
||||
serial_number: Map.get(device_data, :serial_number)
|
||||
serial_number: Map.get(device_data, :serial_number),
|
||||
latitude: Map.get(device_data, :latitude),
|
||||
longitude: Map.get(device_data, :longitude)
|
||||
})
|
||||
|> Map.merge(device_data)
|
||||
end
|
||||
|
|
@ -55,24 +44,32 @@ defmodule Towerops.Snmp.Profiles.Dynamic do
|
|||
@doc """
|
||||
Discovers sensors using profile's sensor_oids.
|
||||
Translates MIB names and polls sensors.
|
||||
Falls back to Base ENTITY-SENSOR-MIB discovery if profile has no sensor_oids.
|
||||
"""
|
||||
@spec discover_sensors(DeviceProfile.t(), Client.connection_opts()) ::
|
||||
@spec discover_sensors(map(), Client.connection_opts()) ::
|
||||
{:ok, [Discovery.sensor_data()]}
|
||||
def discover_sensors(profile, client_opts) do
|
||||
profile = Profiles.get_profile(profile.name)
|
||||
alias Towerops.Snmp.Profiles.Base
|
||||
|
||||
sensors =
|
||||
profile.sensor_oids
|
||||
|> Enum.map(&fetch_sensor_value(&1, client_opts))
|
||||
|> Enum.reject(&is_nil/1)
|
||||
sensor_oids = Map.get(profile, :sensor_oids, [])
|
||||
|
||||
{:ok, sensors}
|
||||
if Enum.empty?(sensor_oids) do
|
||||
# Fall back to ENTITY-SENSOR-MIB discovery from Base profile
|
||||
Base.discover_sensors(client_opts)
|
||||
else
|
||||
sensors =
|
||||
sensor_oids
|
||||
|> Enum.map(&fetch_sensor_value(&1, client_opts))
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|
||||
{:ok, sensors}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Uses Base profile for interface discovery.
|
||||
"""
|
||||
@spec discover_interfaces(DeviceProfile.t(), Client.connection_opts()) ::
|
||||
@spec discover_interfaces(map(), Client.connection_opts()) ::
|
||||
{:ok, [Discovery.interface_data()]}
|
||||
def discover_interfaces(_profile, client_opts) do
|
||||
alias Towerops.Snmp.Profiles.Base
|
||||
|
|
@ -82,13 +79,31 @@ defmodule Towerops.Snmp.Profiles.Dynamic do
|
|||
|
||||
# Private helper functions
|
||||
|
||||
defp fetch_device_oid_value(device_oid, client_opts) do
|
||||
with {:ok, numeric_oid} <- MibTranslator.translate(device_oid.mib_name),
|
||||
defp format_firmware_version(%{vendor: vendor}, device_data) when vendor in ["MikroTik", "Mikrotik RouterOS"] do
|
||||
case {Map.get(device_data, :firmware_version), Map.get(device_data, :license_version)} do
|
||||
{fw, license} when not is_nil(fw) and not is_nil(license) ->
|
||||
"MikroTik RouterOS #{fw} (Level #{license})"
|
||||
|
||||
{fw, _} when not is_nil(fw) ->
|
||||
"MikroTik RouterOS #{fw}"
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp format_firmware_version(_profile, device_data) do
|
||||
# For non-MikroTik devices, use raw firmware version
|
||||
Map.get(device_data, :firmware_version)
|
||||
end
|
||||
|
||||
defp fetch_device_oid_value(field, mib_name, client_opts) do
|
||||
with {:ok, numeric_oid} <- MibTranslator.translate(mib_name),
|
||||
{:ok, value} <- Client.get(client_opts, numeric_oid) do
|
||||
{String.to_atom(device_oid.field), value}
|
||||
{field, value}
|
||||
else
|
||||
{:error, :translation_failed} ->
|
||||
Logger.warning("Failed to translate MIB name: #{device_oid.mib_name}")
|
||||
Logger.warning("Failed to translate MIB name: #{mib_name}")
|
||||
nil
|
||||
|
||||
{:error, _} ->
|
||||
|
|
@ -97,21 +112,23 @@ defmodule Towerops.Snmp.Profiles.Dynamic do
|
|||
end
|
||||
|
||||
defp fetch_sensor_value(sensor_oid, client_opts) do
|
||||
with {:ok, numeric_oid} <- MibTranslator.translate(sensor_oid.mib_name),
|
||||
mib_name = Map.get(sensor_oid, :mib_name)
|
||||
|
||||
with {:ok, numeric_oid} <- MibTranslator.translate(mib_name),
|
||||
{:ok, value} when is_integer(value) and value > 0 <-
|
||||
Client.get(client_opts, numeric_oid) do
|
||||
%{
|
||||
sensor_type: sensor_oid.sensor_type,
|
||||
sensor_index: sensor_oid.sensor_type,
|
||||
sensor_type: sensor_oid[:sensor_type],
|
||||
sensor_index: sensor_oid[:sensor_type],
|
||||
sensor_oid: numeric_oid,
|
||||
sensor_descr: sensor_oid.sensor_descr || sensor_oid.sensor_type,
|
||||
sensor_unit: sensor_oid.sensor_unit || "",
|
||||
sensor_divisor: sensor_oid.sensor_divisor || 1,
|
||||
sensor_descr: sensor_oid[:sensor_descr] || sensor_oid[:sensor_type],
|
||||
sensor_unit: sensor_oid[:sensor_unit] || "",
|
||||
sensor_divisor: sensor_oid[:sensor_divisor] || 1,
|
||||
sensor_value: value
|
||||
}
|
||||
else
|
||||
{:error, :translation_failed} ->
|
||||
Logger.warning("Failed to translate sensor MIB name: #{sensor_oid.mib_name}")
|
||||
Logger.warning("Failed to translate sensor MIB name: #{mib_name}")
|
||||
nil
|
||||
|
||||
_ ->
|
||||
|
|
|
|||
|
|
@ -1,725 +0,0 @@
|
|||
defmodule Towerops.Snmp.Profiles.Mikrotik do
|
||||
@moduledoc """
|
||||
MikroTik RouterOS device profile.
|
||||
Extends Base profile with MikroTik-specific monitoring via MIKROTIK-MIB.
|
||||
|
||||
Discovers and monitors:
|
||||
- RouterBoard hardware information
|
||||
- Health sensors (temperature, voltage, current, fan speed)
|
||||
- CPU and memory usage
|
||||
- Power supply status
|
||||
- Wireless interfaces (if present)
|
||||
"""
|
||||
|
||||
alias Towerops.Snmp.Client
|
||||
alias Towerops.Snmp.Discovery
|
||||
alias Towerops.Snmp.Profiles.Base
|
||||
|
||||
require Logger
|
||||
|
||||
# MikroTik enterprise OID: 1.3.6.1.4.1.14988
|
||||
@mikrotik_oids %{
|
||||
# System Information (mtxrSystem)
|
||||
serial_number: "1.3.6.1.4.1.14988.1.1.7.3.0",
|
||||
firmware_version: "1.3.6.1.4.1.14988.1.1.7.4.0",
|
||||
board_name: "1.3.6.1.4.1.14988.1.1.7.9.0",
|
||||
display_name: "1.3.6.1.4.1.14988.1.1.7.8.0",
|
||||
build_time: "1.3.6.1.4.1.14988.1.1.7.6.0",
|
||||
license_level: "1.3.6.1.4.1.14988.1.1.7.5.0",
|
||||
|
||||
# Health - Voltages (mtxrHealth)
|
||||
core_voltage: "1.3.6.1.4.1.14988.1.1.3.1.0",
|
||||
three_volt: "1.3.6.1.4.1.14988.1.1.3.2.0",
|
||||
five_volt: "1.3.6.1.4.1.14988.1.1.3.3.0",
|
||||
twelve_volt: "1.3.6.1.4.1.14988.1.1.3.4.0",
|
||||
voltage: "1.3.6.1.4.1.14988.1.1.3.8.0",
|
||||
|
||||
# Health - Temperatures
|
||||
sensor_temperature: "1.3.6.1.4.1.14988.1.1.3.5.0",
|
||||
cpu_temperature: "1.3.6.1.4.1.14988.1.1.3.6.0",
|
||||
board_temperature: "1.3.6.1.4.1.14988.1.1.3.7.0",
|
||||
temperature: "1.3.6.1.4.1.14988.1.1.3.10.0",
|
||||
processor_temperature: "1.3.6.1.4.1.14988.1.1.3.11.0",
|
||||
|
||||
# Health - Power & Current
|
||||
current: "1.3.6.1.4.1.14988.1.1.3.13.0",
|
||||
power: "1.3.6.1.4.1.14988.1.1.3.12.0",
|
||||
|
||||
# Health - Fans & Cooling
|
||||
active_fan: "1.3.6.1.4.1.14988.1.1.3.9.0",
|
||||
fan_speed_1: "1.3.6.1.4.1.14988.1.1.3.17.0",
|
||||
fan_speed_2: "1.3.6.1.4.1.14988.1.1.3.18.0",
|
||||
|
||||
# Health - Power Supply
|
||||
processor_frequency: "1.3.6.1.4.1.14988.1.1.3.14.0",
|
||||
power_supply_state: "1.3.6.1.4.1.14988.1.1.3.15.0",
|
||||
backup_psu_state: "1.3.6.1.4.1.14988.1.1.3.16.0",
|
||||
|
||||
# System Resources
|
||||
cpu_load: "1.3.6.1.2.1.25.3.3.1.2.1",
|
||||
total_memory: "1.3.6.1.2.1.25.2.2.0",
|
||||
used_memory: "1.3.6.1.4.1.14988.1.1.1.2.0",
|
||||
total_hdd: "1.3.6.1.4.1.14988.1.1.1.6.0",
|
||||
used_hdd: "1.3.6.1.4.1.14988.1.1.1.7.0",
|
||||
|
||||
# Network Services
|
||||
dhcp_lease_count: "1.3.6.1.4.1.14988.1.1.1.3.0",
|
||||
|
||||
# Connection Tracking (mtxrFWConnection)
|
||||
fw_connection_count: "1.3.6.1.4.1.14988.1.1.13.1.1.1.0",
|
||||
fw_connection_max: "1.3.6.1.4.1.14988.1.1.13.1.1.2.0"
|
||||
}
|
||||
|
||||
# Table OID bases for walking
|
||||
@health_sensor_table "1.3.6.1.4.1.14988.1.1.3.100.1"
|
||||
@poe_table "1.3.6.1.4.1.14988.1.1.15.1"
|
||||
@optical_table "1.3.6.1.4.1.14988.1.1.16.1"
|
||||
|
||||
@doc """
|
||||
Discovers system information using Base profile and adds MikroTik-specific details.
|
||||
"""
|
||||
@spec discover_system_info(Client.connection_opts()) ::
|
||||
{:ok, Discovery.system_info()} | {:error, term()}
|
||||
def discover_system_info(client_opts) do
|
||||
with {:ok, base_info} <- Base.discover_system_info(client_opts),
|
||||
{:ok, mikrotik_info} <- discover_mikrotik_system_info(client_opts) do
|
||||
{:ok, Map.merge(base_info, mikrotik_info)}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Discovers network interfaces using Base profile.
|
||||
"""
|
||||
@spec discover_interfaces(Client.connection_opts()) ::
|
||||
{:ok, [Discovery.interface_data()]} | {:error, term()}
|
||||
def discover_interfaces(client_opts) do
|
||||
Base.discover_interfaces(client_opts)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Discovers MikroTik health sensors and system resources.
|
||||
Returns comprehensive sensor list including temperature, voltage, current, fans, CPU, memory, disk.
|
||||
Also discovers entity sensors from ENTITY-SENSOR-MIB (for additional temperature, voltage, etc.).
|
||||
"""
|
||||
@spec discover_sensors(Client.connection_opts()) ::
|
||||
{:ok, [Discovery.sensor_data()]} | {:error, term()}
|
||||
def discover_sensors(client_opts) do
|
||||
Logger.debug("Discovering MikroTik sensors...")
|
||||
|
||||
health_sensors = discover_health_sensors(client_opts)
|
||||
resource_sensors = discover_resource_sensors(client_opts)
|
||||
|
||||
# Also try to discover entity sensors (for additional temperature, voltage sensors)
|
||||
{:ok, entity_sensors} = Base.discover_sensors(client_opts)
|
||||
|
||||
all_sensors = health_sensors ++ resource_sensors ++ entity_sensors
|
||||
|
||||
if all_sensors == [] do
|
||||
Logger.warning("No MikroTik sensors found")
|
||||
{:ok, []}
|
||||
else
|
||||
Logger.debug("Discovered #{length(all_sensors)} MikroTik sensors")
|
||||
{:ok, all_sensors}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Identifies MikroTik device from sysDescr.
|
||||
"""
|
||||
@spec identify_device(Discovery.system_info()) :: Discovery.device_info()
|
||||
def identify_device(system_info) do
|
||||
sys_descr = Map.get(system_info, :sys_descr, "")
|
||||
|
||||
# Extract model from sysDescr (e.g., "RouterOS RB5009UG+S+")
|
||||
model =
|
||||
case Regex.run(~r/RouterOS\s+([A-Z0-9\+\-]+)/, sys_descr) do
|
||||
[_, model] -> model
|
||||
_ -> "MikroTik RouterOS"
|
||||
end
|
||||
|
||||
# Build formatted firmware version
|
||||
firmware_version = Map.get(system_info, :firmware_version)
|
||||
license_level = Map.get(system_info, :license_version)
|
||||
|
||||
# Treat empty string as nil for license level
|
||||
license_level = if license_level == "", do: nil, else: license_level
|
||||
|
||||
formatted_firmware =
|
||||
cond do
|
||||
firmware_version && license_level ->
|
||||
"RouterOS #{firmware_version} (Level #{license_level})"
|
||||
|
||||
firmware_version ->
|
||||
"RouterOS #{firmware_version}"
|
||||
|
||||
true ->
|
||||
# Fallback to extracting from sysDescr
|
||||
extract_firmware_from_sysdescr(sys_descr)
|
||||
end
|
||||
|
||||
Map.merge(system_info, %{
|
||||
manufacturer: "MikroTik",
|
||||
model: model,
|
||||
firmware_version: formatted_firmware,
|
||||
serial_number: Map.get(system_info, :serial_number)
|
||||
})
|
||||
end
|
||||
|
||||
# Private functions
|
||||
|
||||
defp discover_mikrotik_system_info(client_opts) do
|
||||
alias Towerops.Snmp.MibTranslator
|
||||
|
||||
# Use MIB symbolic names instead of hardcoded OIDs
|
||||
mib_names = [
|
||||
"MIKROTIK-MIB::mtxrSerialNumber.0",
|
||||
"MIKROTIK-MIB::mtxrFirmwareVersion.0",
|
||||
"MIKROTIK-MIB::mtxrBoardName.0",
|
||||
"MIKROTIK-MIB::mtxrSystemDisplayName.0",
|
||||
"MIKROTIK-MIB::mtxrLicenseVersion.0"
|
||||
]
|
||||
|
||||
# Translate MIB names to numeric OIDs
|
||||
translated = MibTranslator.translate_batch(mib_names)
|
||||
|
||||
oids =
|
||||
Enum.map(mib_names, fn name ->
|
||||
case Map.get(translated, name) do
|
||||
{:ok, oid} -> oid
|
||||
_ -> nil
|
||||
end
|
||||
end)
|
||||
|
||||
# If any translation failed, fall back to hardcoded OIDs
|
||||
oids =
|
||||
if Enum.any?(oids, &is_nil/1) do
|
||||
Logger.warning("MIB translation failed for some MikroTik OIDs, using hardcoded values")
|
||||
|
||||
[
|
||||
@mikrotik_oids.serial_number,
|
||||
@mikrotik_oids.firmware_version,
|
||||
@mikrotik_oids.board_name,
|
||||
@mikrotik_oids.display_name,
|
||||
@mikrotik_oids.license_level
|
||||
]
|
||||
else
|
||||
oids
|
||||
end
|
||||
|
||||
case Client.get_multiple(client_opts, oids) do
|
||||
{:ok, [serial, firmware, board, display, license_level]} ->
|
||||
{:ok,
|
||||
%{
|
||||
serial_number: serial,
|
||||
firmware_version: firmware,
|
||||
board_name: board,
|
||||
display_name: display,
|
||||
license_version: license_level
|
||||
}}
|
||||
|
||||
{:error, reason} ->
|
||||
# MikroTik-specific OIDs not available or partially available
|
||||
Logger.warning("Failed to get MikroTik device info: #{inspect(reason)}")
|
||||
{:ok, %{}}
|
||||
end
|
||||
end
|
||||
|
||||
defp discover_health_sensors(client_opts) do
|
||||
# First try the dynamic health sensor table (available on newer devices like CCR2004)
|
||||
table_sensors = discover_health_table_sensors(client_opts)
|
||||
|
||||
# Also try individual health OIDs for older devices
|
||||
oid_sensors = discover_health_oid_sensors(client_opts)
|
||||
|
||||
# Combine both, preferring table sensors (more accurate)
|
||||
table_sensors ++ oid_sensors
|
||||
end
|
||||
|
||||
defp discover_health_table_sensors(client_opts) do
|
||||
# Walk health sensor table: mtxrHealthTable (1.3.6.1.4.1.14988.1.1.3.100.1)
|
||||
# Column 2: Name, Column 3: Value, Column 4: Type
|
||||
with {:ok, name_results} <- Client.walk(client_opts, "#{@health_sensor_table}.2"),
|
||||
{:ok, value_results} <- Client.walk(client_opts, "#{@health_sensor_table}.3"),
|
||||
{:ok, type_results} <- Client.walk(client_opts, "#{@health_sensor_table}.4") do
|
||||
indices =
|
||||
name_results
|
||||
|> Map.keys()
|
||||
|> Enum.map(fn oid -> oid |> String.split(".") |> List.last() end)
|
||||
|
||||
indices
|
||||
|> Enum.map(fn index ->
|
||||
name_oid = "#{@health_sensor_table}.2.#{index}"
|
||||
value_oid = "#{@health_sensor_table}.3.#{index}"
|
||||
type_oid = "#{@health_sensor_table}.4.#{index}"
|
||||
|
||||
name = Map.get(name_results, name_oid, "unknown")
|
||||
value = Map.get(value_results, value_oid, 0)
|
||||
type_code = Map.get(type_results, type_oid, 0)
|
||||
|
||||
{sensor_type, unit, divisor} = decode_health_sensor_type(type_code, name)
|
||||
|
||||
# Skip sensors with value 0 (disabled PSUs, etc.)
|
||||
if value > 0 do
|
||||
%{
|
||||
sensor_type: sensor_type,
|
||||
sensor_index: "health_#{index}",
|
||||
sensor_oid: value_oid,
|
||||
sensor_descr: format_health_sensor_name(name),
|
||||
sensor_unit: unit,
|
||||
sensor_divisor: divisor,
|
||||
last_value: value / divisor,
|
||||
status: "ok"
|
||||
}
|
||||
end
|
||||
end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
else
|
||||
_ -> []
|
||||
end
|
||||
end
|
||||
|
||||
defp decode_health_sensor_type(type_code, name) do
|
||||
cond do
|
||||
# Type 1 = Temperature (Celsius)
|
||||
type_code == 1 -> {"temperature", "°C", 1}
|
||||
# Type 3 = Voltage (V, value in decivolts)
|
||||
type_code == 3 -> {"voltage", "V", 10}
|
||||
# Type 2 = Current (mA)
|
||||
type_code == 2 -> {"current", "mA", 1}
|
||||
# Fallback: infer from name
|
||||
String.contains?(name, "temperature") -> {"temperature", "°C", 1}
|
||||
String.contains?(name, "voltage") -> {"voltage", "V", 10}
|
||||
String.contains?(name, "current") -> {"current", "mA", 1}
|
||||
String.contains?(name, "power") -> {"power", "W", 10}
|
||||
true -> {"unknown", "", 1}
|
||||
end
|
||||
end
|
||||
|
||||
defp format_health_sensor_name(name) do
|
||||
name
|
||||
|> String.replace("-", " ")
|
||||
|> String.split()
|
||||
|> Enum.map_join(" ", &String.capitalize/1)
|
||||
end
|
||||
|
||||
defp discover_health_oid_sensors(client_opts) do
|
||||
# Try to get all health OIDs - voltages, temperatures, power, fans
|
||||
health_oids = [
|
||||
# Voltages
|
||||
{@mikrotik_oids.core_voltage, "core_voltage", "V", 10},
|
||||
{@mikrotik_oids.three_volt, "3v3_voltage", "V", 10},
|
||||
{@mikrotik_oids.five_volt, "5v_voltage", "V", 10},
|
||||
{@mikrotik_oids.twelve_volt, "12v_voltage", "V", 10},
|
||||
{@mikrotik_oids.voltage, "input_voltage", "V", 10},
|
||||
# Temperatures
|
||||
{@mikrotik_oids.sensor_temperature, "sensor_temperature", "°C", 10},
|
||||
{@mikrotik_oids.cpu_temperature, "cpu_temperature", "°C", 10},
|
||||
{@mikrotik_oids.board_temperature, "board_temperature", "°C", 10},
|
||||
{@mikrotik_oids.temperature, "temperature", "°C", 10},
|
||||
{@mikrotik_oids.processor_temperature, "processor_temperature", "°C", 10},
|
||||
# Power & Current
|
||||
{@mikrotik_oids.current, "current", "mA", 1},
|
||||
{@mikrotik_oids.power, "power", "W", 10},
|
||||
# Fans
|
||||
{@mikrotik_oids.fan_speed_1, "fan1", "RPM", 1},
|
||||
{@mikrotik_oids.fan_speed_2, "fan2", "RPM", 1},
|
||||
# Processor
|
||||
{@mikrotik_oids.processor_frequency, "processor_frequency", "MHz", 1}
|
||||
]
|
||||
|
||||
health_oids
|
||||
|> Enum.map(fn {oid, type, unit, divisor} ->
|
||||
case Client.get(client_opts, oid) do
|
||||
{:ok, value} when is_integer(value) and value > 0 ->
|
||||
%{
|
||||
sensor_type: type,
|
||||
sensor_index: type,
|
||||
sensor_oid: oid,
|
||||
sensor_descr: format_sensor_name(type),
|
||||
sensor_unit: unit,
|
||||
sensor_divisor: divisor,
|
||||
last_value: value / divisor,
|
||||
status: "ok"
|
||||
}
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
end
|
||||
|
||||
defp discover_resource_sensors(client_opts) do
|
||||
cpu_sensors = discover_cpu_sensors(client_opts)
|
||||
storage_sensors = discover_storage_sensors(client_opts)
|
||||
network_sensors = discover_network_sensors(client_opts)
|
||||
poe_sensors = discover_poe_sensors(client_opts)
|
||||
optical_sensors = discover_optical_sensors(client_opts)
|
||||
|
||||
cpu_sensors ++ storage_sensors ++ network_sensors ++ poe_sensors ++ optical_sensors
|
||||
end
|
||||
|
||||
defp discover_cpu_sensors(client_opts) do
|
||||
# Walk hrProcessorLoad (1.3.6.1.2.1.25.3.3.1.2) to get CPU load per core
|
||||
case Client.walk(client_opts, "1.3.6.1.2.1.25.3.3.1.2") do
|
||||
{:ok, results} ->
|
||||
Enum.map(results, fn {oid, load} ->
|
||||
index = oid |> String.split(".") |> List.last()
|
||||
|
||||
%{
|
||||
sensor_type: "cpu_load",
|
||||
sensor_index: "cpu#{index}",
|
||||
sensor_oid: oid,
|
||||
sensor_descr: "CPU #{index} Load",
|
||||
sensor_unit: "%",
|
||||
sensor_divisor: 1,
|
||||
last_value: load / 1.0,
|
||||
status: cpu_status(load)
|
||||
}
|
||||
end)
|
||||
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
defp discover_network_sensors(client_opts) do
|
||||
# Get DHCP lease count and connection tracking stats
|
||||
network_oids = [
|
||||
{@mikrotik_oids.dhcp_lease_count, "dhcp_leases", "count", 1},
|
||||
{@mikrotik_oids.fw_connection_count, "connection_count", "count", 1}
|
||||
]
|
||||
|
||||
network_oids
|
||||
|> Enum.map(fn {oid, type, unit, divisor} ->
|
||||
case Client.get(client_opts, oid) do
|
||||
{:ok, value} when is_integer(value) and value >= 0 ->
|
||||
%{
|
||||
sensor_type: type,
|
||||
sensor_index: type,
|
||||
sensor_oid: oid,
|
||||
sensor_descr: format_network_sensor_name(type),
|
||||
sensor_unit: unit,
|
||||
sensor_divisor: divisor,
|
||||
last_value: value / divisor,
|
||||
status: "ok"
|
||||
}
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
end
|
||||
|
||||
defp discover_storage_sensors(client_opts) do
|
||||
with {:ok, descr_results} <- Client.walk(client_opts, "1.3.6.1.2.1.25.2.3.1.3"),
|
||||
{:ok, size_results} <- Client.walk(client_opts, "1.3.6.1.2.1.25.2.3.1.5"),
|
||||
{:ok, used_results} <- Client.walk(client_opts, "1.3.6.1.2.1.25.2.3.1.6") do
|
||||
indices = extract_storage_indices(descr_results)
|
||||
build_storage_sensors(indices, descr_results, size_results, used_results)
|
||||
else
|
||||
_ -> []
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_storage_indices(descr_results) do
|
||||
descr_results
|
||||
|> Map.keys()
|
||||
|> Enum.map(fn oid -> oid |> String.split(".") |> List.last() end)
|
||||
end
|
||||
|
||||
defp build_storage_sensors(indices, descr_results, size_results, used_results) do
|
||||
indices
|
||||
|> Enum.map(&build_storage_sensor(&1, descr_results, size_results, used_results))
|
||||
|> Enum.reject(&is_nil/1)
|
||||
end
|
||||
|
||||
defp build_storage_sensor(index, descr_results, size_results, used_results) do
|
||||
descr_oid = "1.3.6.1.2.1.25.2.3.1.3.#{index}"
|
||||
size_oid = "1.3.6.1.2.1.25.2.3.1.5.#{index}"
|
||||
used_oid = "1.3.6.1.2.1.25.2.3.1.6.#{index}"
|
||||
|
||||
descr = Map.get(descr_results, descr_oid, "")
|
||||
size = Map.get(size_results, size_oid, 0)
|
||||
used = Map.get(used_results, used_oid, 0)
|
||||
|
||||
if size > 0 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,
|
||||
sensor_oid: used_oid,
|
||||
sensor_descr: descr,
|
||||
sensor_unit: "%",
|
||||
sensor_divisor: 1,
|
||||
last_value: capped_percent,
|
||||
status: storage_status(type, capped_percent),
|
||||
metadata: %{
|
||||
size_oid: size_oid,
|
||||
calculation: "percentage"
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
defp determine_storage_type(descr) do
|
||||
cond do
|
||||
String.contains?(descr, "memory") -> "memory"
|
||||
String.contains?(descr, "disk") -> "disk"
|
||||
true -> "storage"
|
||||
end
|
||||
end
|
||||
|
||||
defp discover_poe_sensors(client_opts) do
|
||||
# Walk POE table to get per-port POE statistics
|
||||
# Table columns: Name(3), Status(4), Voltage(5), Current(6), Power(7)
|
||||
with {:ok, name_results} <- Client.walk(client_opts, "#{@poe_table}.1.3"),
|
||||
{:ok, voltage_results} <- Client.walk(client_opts, "#{@poe_table}.1.5"),
|
||||
{:ok, current_results} <- Client.walk(client_opts, "#{@poe_table}.1.6"),
|
||||
{:ok, power_results} <- Client.walk(client_opts, "#{@poe_table}.1.7") do
|
||||
# Get indices from the name results
|
||||
indices =
|
||||
name_results
|
||||
|> Map.keys()
|
||||
|> Enum.map(fn oid -> oid |> String.split(".") |> List.last() end)
|
||||
|
||||
# Create sensors for voltage, current, and power per port
|
||||
Enum.flat_map(indices, fn index ->
|
||||
name_oid = "#{@poe_table}.1.3.#{index}"
|
||||
voltage_oid = "#{@poe_table}.1.5.#{index}"
|
||||
current_oid = "#{@poe_table}.1.6.#{index}"
|
||||
power_oid = "#{@poe_table}.1.7.#{index}"
|
||||
|
||||
port_name = Map.get(name_results, name_oid, "Port #{index}")
|
||||
voltage = Map.get(voltage_results, voltage_oid, 0)
|
||||
current = Map.get(current_results, current_oid, 0)
|
||||
power = Map.get(power_results, power_oid, 0)
|
||||
|
||||
Enum.reject(
|
||||
[
|
||||
if voltage > 0 do
|
||||
%{
|
||||
sensor_type: "poe_voltage",
|
||||
sensor_index: "poe_#{index}_voltage",
|
||||
sensor_oid: voltage_oid,
|
||||
sensor_descr: "#{port_name} POE Voltage",
|
||||
sensor_unit: "V",
|
||||
sensor_divisor: 10,
|
||||
last_value: voltage / 10.0,
|
||||
status: "ok"
|
||||
}
|
||||
end,
|
||||
if current > 0 do
|
||||
%{
|
||||
sensor_type: "poe_current",
|
||||
sensor_index: "poe_#{index}_current",
|
||||
sensor_oid: current_oid,
|
||||
sensor_descr: "#{port_name} POE Current",
|
||||
sensor_unit: "mA",
|
||||
sensor_divisor: 1,
|
||||
last_value: current / 1.0,
|
||||
status: "ok"
|
||||
}
|
||||
end,
|
||||
if power > 0 do
|
||||
%{
|
||||
sensor_type: "poe_power",
|
||||
sensor_index: "poe_#{index}_power",
|
||||
sensor_oid: power_oid,
|
||||
sensor_descr: "#{port_name} POE Power",
|
||||
sensor_unit: "W",
|
||||
sensor_divisor: 10,
|
||||
last_value: power / 10.0,
|
||||
status: "ok"
|
||||
}
|
||||
end
|
||||
],
|
||||
&is_nil/1
|
||||
)
|
||||
end)
|
||||
else
|
||||
_ -> []
|
||||
end
|
||||
end
|
||||
|
||||
defp discover_optical_sensors(client_opts) do
|
||||
# Walk optical table to get SFP/optical monitoring data
|
||||
# Table columns: Name(2), Temperature(3), Voltage(5), TxBiasCurrent(6), TxPower(7), RxPower(8)
|
||||
with {:ok, name_results} <- Client.walk(client_opts, "#{@optical_table}.1.2"),
|
||||
{:ok, temp_results} <- Client.walk(client_opts, "#{@optical_table}.1.3"),
|
||||
{:ok, voltage_results} <- Client.walk(client_opts, "#{@optical_table}.1.5"),
|
||||
{:ok, tx_power_results} <- Client.walk(client_opts, "#{@optical_table}.1.7"),
|
||||
{:ok, rx_power_results} <- Client.walk(client_opts, "#{@optical_table}.1.8") do
|
||||
# Get indices from the name results
|
||||
indices =
|
||||
name_results
|
||||
|> Map.keys()
|
||||
|> Enum.map(fn oid -> oid |> String.split(".") |> List.last() end)
|
||||
|
||||
# Create sensors for each optical metric
|
||||
Enum.flat_map(indices, fn index ->
|
||||
name_oid = "#{@optical_table}.1.2.#{index}"
|
||||
temp_oid = "#{@optical_table}.1.3.#{index}"
|
||||
voltage_oid = "#{@optical_table}.1.5.#{index}"
|
||||
tx_power_oid = "#{@optical_table}.1.7.#{index}"
|
||||
rx_power_oid = "#{@optical_table}.1.8.#{index}"
|
||||
|
||||
sfp_name = Map.get(name_results, name_oid, "SFP #{index}")
|
||||
temperature = Map.get(temp_results, temp_oid)
|
||||
voltage = Map.get(voltage_results, voltage_oid)
|
||||
tx_power = Map.get(tx_power_results, tx_power_oid)
|
||||
rx_power = Map.get(rx_power_results, rx_power_oid)
|
||||
|
||||
Enum.reject(
|
||||
[
|
||||
if temperature do
|
||||
%{
|
||||
sensor_type: "sfp_temperature",
|
||||
sensor_index: "sfp_#{index}_temp",
|
||||
sensor_oid: temp_oid,
|
||||
sensor_descr: "#{sfp_name} Temperature",
|
||||
sensor_unit: "°C",
|
||||
sensor_divisor: 1000,
|
||||
last_value: temperature / 1000.0,
|
||||
status: "ok"
|
||||
}
|
||||
end,
|
||||
if voltage do
|
||||
%{
|
||||
sensor_type: "sfp_voltage",
|
||||
sensor_index: "sfp_#{index}_voltage",
|
||||
sensor_oid: voltage_oid,
|
||||
sensor_descr: "#{sfp_name} Voltage",
|
||||
sensor_unit: "V",
|
||||
sensor_divisor: 1000,
|
||||
last_value: voltage / 1000.0,
|
||||
status: "ok"
|
||||
}
|
||||
end,
|
||||
if tx_power do
|
||||
%{
|
||||
sensor_type: "sfp_tx_power",
|
||||
sensor_index: "sfp_#{index}_tx",
|
||||
sensor_oid: tx_power_oid,
|
||||
sensor_descr: "#{sfp_name} TX Power",
|
||||
sensor_unit: "dBm",
|
||||
sensor_divisor: 1000,
|
||||
last_value: tx_power / 1000.0,
|
||||
status: "ok"
|
||||
}
|
||||
end,
|
||||
if rx_power do
|
||||
%{
|
||||
sensor_type: "sfp_rx_power",
|
||||
sensor_index: "sfp_#{index}_rx",
|
||||
sensor_oid: rx_power_oid,
|
||||
sensor_descr: "#{sfp_name} RX Power",
|
||||
sensor_unit: "dBm",
|
||||
sensor_divisor: 1000,
|
||||
last_value: rx_power / 1000.0,
|
||||
status: "ok"
|
||||
}
|
||||
end
|
||||
],
|
||||
&is_nil/1
|
||||
)
|
||||
end)
|
||||
else
|
||||
_ -> []
|
||||
end
|
||||
end
|
||||
|
||||
defp format_sensor_name("core_voltage"), do: "Core Voltage"
|
||||
defp format_sensor_name("3v3_voltage"), do: "3.3V Supply"
|
||||
defp format_sensor_name("5v_voltage"), do: "5V Supply"
|
||||
defp format_sensor_name("12v_voltage"), do: "12V Supply"
|
||||
defp format_sensor_name("input_voltage"), do: "Input Voltage"
|
||||
defp format_sensor_name("sensor_temperature"), do: "Sensor Chip Temperature"
|
||||
defp format_sensor_name("cpu_temperature"), do: "CPU Temperature"
|
||||
defp format_sensor_name("board_temperature"), do: "Board Temperature"
|
||||
defp format_sensor_name("temperature"), do: "System Temperature"
|
||||
defp format_sensor_name("processor_temperature"), do: "Processor Temperature"
|
||||
defp format_sensor_name("current"), do: "Current Draw"
|
||||
defp format_sensor_name("power"), do: "Power Consumption"
|
||||
defp format_sensor_name("fan1"), do: "Fan 1 Speed"
|
||||
defp format_sensor_name("fan2"), do: "Fan 2 Speed"
|
||||
defp format_sensor_name("processor_frequency"), do: "Processor Frequency"
|
||||
defp format_sensor_name(other), do: String.capitalize(other)
|
||||
|
||||
defp format_network_sensor_name("dhcp_leases"), do: "DHCP Leases"
|
||||
defp format_network_sensor_name("connection_count"), do: "Active Connections"
|
||||
defp format_network_sensor_name(other), do: String.capitalize(other)
|
||||
|
||||
defp cpu_status(load) when load < 70, do: "ok"
|
||||
defp cpu_status(load) when load < 90, do: "warning"
|
||||
defp cpu_status(_), do: "critical"
|
||||
|
||||
defp storage_status("memory", percent) when percent < 80, do: "ok"
|
||||
defp storage_status("memory", percent) when percent < 95, do: "warning"
|
||||
defp storage_status("memory", _), do: "critical"
|
||||
|
||||
defp storage_status("disk", percent) when percent < 85, do: "ok"
|
||||
defp storage_status("disk", percent) when percent < 95, do: "warning"
|
||||
defp storage_status("disk", _), do: "critical"
|
||||
|
||||
defp storage_status(_, percent) when percent < 85, do: "ok"
|
||||
defp storage_status(_, percent) when percent < 95, do: "warning"
|
||||
defp storage_status(_, _), do: "critical"
|
||||
|
||||
defp extract_firmware_from_sysdescr(sys_descr) do
|
||||
# Try to extract version from sysDescr
|
||||
# Example: "RouterOS RB5009UG+S+ (stable) 7.13.2"
|
||||
case Regex.run(~r/RouterOS.*?(\d+\.\d+(?:\.\d+)?)/, sys_descr) do
|
||||
[_, version] -> version
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Collects raw SNMP OID data for debugging purposes.
|
||||
"""
|
||||
def collect_raw_debug_data(client_opts) do
|
||||
# Collect raw SNMP data for debugging
|
||||
system_oids =
|
||||
Map.new(@mikrotik_oids, fn {key, oid} ->
|
||||
value =
|
||||
case Client.get(client_opts, oid) do
|
||||
{:ok, val} -> val
|
||||
{:error, reason} -> "ERROR: #{inspect(reason)}"
|
||||
end
|
||||
|
||||
{key, %{oid: oid, value: value}}
|
||||
end)
|
||||
|
||||
# Walk health sensor table
|
||||
health_table =
|
||||
case Client.walk(client_opts, @health_sensor_table) do
|
||||
{:ok, results} -> results
|
||||
{:error, _} -> %{}
|
||||
end
|
||||
|
||||
%{
|
||||
timestamp: DateTime.utc_now(),
|
||||
system_oids: system_oids,
|
||||
health_sensor_table: health_table
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -1,342 +0,0 @@
|
|||
defmodule Towerops.Snmp.Profiles.NetSnmp do
|
||||
@moduledoc """
|
||||
Net-SNMP device profile for Linux/Unix servers.
|
||||
Extends Base profile with Net-SNMP specific sensor discovery from:
|
||||
- LM-SENSORS-MIB (lm-sensors hardware monitoring)
|
||||
- UCD-SNMP-MIB (system statistics)
|
||||
|
||||
Discovers:
|
||||
- CPU temperature, fan speed, voltage from lm-sensors
|
||||
- System load, memory, disk usage from UCD-SNMP-MIB
|
||||
"""
|
||||
|
||||
alias Towerops.Snmp.Client
|
||||
alias Towerops.Snmp.Discovery
|
||||
alias Towerops.Snmp.Profiles.Base
|
||||
|
||||
require Logger
|
||||
|
||||
@lm_sensors_oids %{
|
||||
# LM-SENSORS-MIB
|
||||
lm_temp_sensors_device: "1.3.6.1.4.1.2021.13.16.2.1.2",
|
||||
lm_temp_sensors_value: "1.3.6.1.4.1.2021.13.16.2.1.3",
|
||||
lm_fans_device: "1.3.6.1.4.1.2021.13.16.3.1.2",
|
||||
lm_fans_value: "1.3.6.1.4.1.2021.13.16.3.1.3",
|
||||
lm_voltage_device: "1.3.6.1.4.1.2021.13.16.4.1.2",
|
||||
lm_voltage_value: "1.3.6.1.4.1.2021.13.16.4.1.3"
|
||||
}
|
||||
|
||||
@ucd_snmp_oids %{
|
||||
# UCD-SNMP-MIB system statistics
|
||||
load_average_1: "1.3.6.1.4.1.2021.10.1.3.1",
|
||||
load_average_5: "1.3.6.1.4.1.2021.10.1.3.2",
|
||||
load_average_15: "1.3.6.1.4.1.2021.10.1.3.3",
|
||||
mem_total: "1.3.6.1.4.1.2021.4.5.0",
|
||||
mem_available: "1.3.6.1.4.1.2021.4.6.0",
|
||||
mem_buffer: "1.3.6.1.4.1.2021.4.14.0",
|
||||
mem_cached: "1.3.6.1.4.1.2021.4.15.0"
|
||||
}
|
||||
|
||||
@doc """
|
||||
Discovers system information.
|
||||
NetSnmp profile doesn't query additional system OIDs beyond Base profile.
|
||||
"""
|
||||
@spec discover_system_info(Client.connection_opts()) ::
|
||||
{:ok, Discovery.system_info()} | {:error, term()}
|
||||
def discover_system_info(_client_opts) do
|
||||
# No additional system info beyond Base profile
|
||||
{:ok, %{}}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Discovers network interfaces using Base profile.
|
||||
"""
|
||||
@spec discover_interfaces(Client.connection_opts()) ::
|
||||
{:ok, [Discovery.interface_data()]} | {:error, term()}
|
||||
def discover_interfaces(client_opts) do
|
||||
Base.discover_interfaces(client_opts)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Discovers sensors from LM-SENSORS-MIB and UCD-SNMP-MIB.
|
||||
Combines hardware sensors (temperature, fans, voltage) with system stats (load, memory).
|
||||
"""
|
||||
def discover_sensors(client_opts) do
|
||||
lm_sensors = discover_lm_sensors(client_opts)
|
||||
ucd_sensors = discover_ucd_sensors(client_opts)
|
||||
|
||||
all_sensors = lm_sensors ++ ucd_sensors
|
||||
|
||||
if all_sensors == [] do
|
||||
# Fall back to standard ENTITY-SENSOR-MIB
|
||||
Logger.debug("Net-SNMP specific sensors not available, using standard discovery")
|
||||
Base.discover_sensors(client_opts)
|
||||
else
|
||||
{:ok, all_sensors}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Identifies Linux/Unix device from sysDescr.
|
||||
"""
|
||||
def identify_device(system_info) do
|
||||
sys_descr = Map.get(system_info, :sys_descr, "")
|
||||
|
||||
{os, version} = extract_linux_info(sys_descr)
|
||||
|
||||
Map.merge(system_info, %{
|
||||
manufacturer: "Linux",
|
||||
model: os,
|
||||
firmware_version: version
|
||||
})
|
||||
end
|
||||
|
||||
# Private functions
|
||||
|
||||
defp discover_lm_sensors(client_opts) do
|
||||
temp_sensors = discover_temperature_sensors(client_opts)
|
||||
fan_sensors = discover_fan_sensors(client_opts)
|
||||
voltage_sensors = discover_voltage_sensors(client_opts)
|
||||
|
||||
temp_sensors ++ fan_sensors ++ voltage_sensors
|
||||
end
|
||||
|
||||
defp discover_temperature_sensors(client_opts) do
|
||||
with {:ok, devices} <- Client.walk(client_opts, @lm_sensors_oids.lm_temp_sensors_device),
|
||||
{:ok, values} <- Client.walk(client_opts, @lm_sensors_oids.lm_temp_sensors_value) do
|
||||
build_lm_sensors(devices, values, "celsius", "°C")
|
||||
else
|
||||
_ -> []
|
||||
end
|
||||
end
|
||||
|
||||
defp discover_fan_sensors(client_opts) do
|
||||
with {:ok, devices} <- Client.walk(client_opts, @lm_sensors_oids.lm_fans_device),
|
||||
{:ok, values} <- Client.walk(client_opts, @lm_sensors_oids.lm_fans_value) do
|
||||
build_lm_sensors(devices, values, "rpm", "RPM")
|
||||
else
|
||||
_ -> []
|
||||
end
|
||||
end
|
||||
|
||||
defp discover_voltage_sensors(client_opts) do
|
||||
with {:ok, devices} <- Client.walk(client_opts, @lm_sensors_oids.lm_voltage_device),
|
||||
{:ok, values} <- Client.walk(client_opts, @lm_sensors_oids.lm_voltage_value) do
|
||||
build_lm_sensors(devices, values, "volts", "V")
|
||||
else
|
||||
_ -> []
|
||||
end
|
||||
end
|
||||
|
||||
defp build_lm_sensors(devices, values, sensor_type, unit) do
|
||||
Enum.map(devices, fn {oid, device_name} ->
|
||||
index = extract_last_oid_part(oid)
|
||||
value_oid = find_matching_oid(values, index)
|
||||
|
||||
%{
|
||||
sensor_type: sensor_type,
|
||||
sensor_index: index,
|
||||
sensor_oid: value_oid,
|
||||
sensor_descr: device_name,
|
||||
sensor_unit: unit,
|
||||
sensor_divisor: divisor_for_type(sensor_type),
|
||||
last_value: get_sensor_value(values, index),
|
||||
status: "ok"
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
defp discover_ucd_sensors(client_opts) do
|
||||
load_sensors = discover_load_sensors(client_opts)
|
||||
memory_sensors = discover_memory_sensors(client_opts)
|
||||
|
||||
load_sensors ++ memory_sensors
|
||||
end
|
||||
|
||||
defp discover_load_sensors(client_opts) do
|
||||
case Client.get_multiple(client_opts, [
|
||||
@ucd_snmp_oids.load_average_1,
|
||||
@ucd_snmp_oids.load_average_5,
|
||||
@ucd_snmp_oids.load_average_15
|
||||
]) do
|
||||
{:ok, [load1, load5, load15]} ->
|
||||
[
|
||||
%{
|
||||
sensor_type: "load",
|
||||
sensor_index: "1min",
|
||||
sensor_oid: @ucd_snmp_oids.load_average_1,
|
||||
sensor_descr: "System Load (1 min)",
|
||||
sensor_unit: "",
|
||||
sensor_divisor: 100,
|
||||
last_value: parse_float(load1),
|
||||
status: "ok"
|
||||
},
|
||||
%{
|
||||
sensor_type: "load",
|
||||
sensor_index: "5min",
|
||||
sensor_oid: @ucd_snmp_oids.load_average_5,
|
||||
sensor_descr: "System Load (5 min)",
|
||||
sensor_unit: "",
|
||||
sensor_divisor: 100,
|
||||
last_value: parse_float(load5),
|
||||
status: "ok"
|
||||
},
|
||||
%{
|
||||
sensor_type: "load",
|
||||
sensor_index: "15min",
|
||||
sensor_oid: @ucd_snmp_oids.load_average_15,
|
||||
sensor_descr: "System Load (15 min)",
|
||||
sensor_unit: "",
|
||||
sensor_divisor: 100,
|
||||
last_value: parse_float(load15),
|
||||
status: "ok"
|
||||
}
|
||||
]
|
||||
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
defp discover_memory_sensors(client_opts) do
|
||||
case Client.get_multiple(client_opts, [
|
||||
@ucd_snmp_oids.mem_total,
|
||||
@ucd_snmp_oids.mem_available
|
||||
]) do
|
||||
{:ok, [total, available]} ->
|
||||
total_kb = parse_integer(total)
|
||||
available_kb = parse_integer(available)
|
||||
used_kb = calculate_used_memory(total_kb, available_kb)
|
||||
used_percent = calculate_memory_percentage(total_kb, used_kb)
|
||||
|
||||
[
|
||||
%{
|
||||
sensor_type: "memory",
|
||||
sensor_index: "usage",
|
||||
sensor_oid: @ucd_snmp_oids.mem_available,
|
||||
sensor_descr: "Memory Usage",
|
||||
sensor_unit: "%",
|
||||
sensor_divisor: 1,
|
||||
last_value: used_percent,
|
||||
status: "ok"
|
||||
}
|
||||
]
|
||||
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
defp calculate_used_memory(nil, _available_kb), do: nil
|
||||
defp calculate_used_memory(_total_kb, nil), do: nil
|
||||
defp calculate_used_memory(total_kb, available_kb), do: total_kb - available_kb
|
||||
|
||||
defp calculate_memory_percentage(total_kb, used_kb) when is_number(total_kb) and total_kb > 0 and is_number(used_kb) do
|
||||
percent = used_kb / total_kb * 100
|
||||
cap_memory_percentage(percent, used_kb, total_kb)
|
||||
end
|
||||
|
||||
defp calculate_memory_percentage(_total_kb, _used_kb), do: nil
|
||||
|
||||
defp cap_memory_percentage(percent, _used_kb, _total_kb) when percent <= 100, do: percent
|
||||
|
||||
defp cap_memory_percentage(percent, used_kb, total_kb) 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
|
||||
end
|
||||
|
||||
defp extract_linux_info(sys_descr) do
|
||||
cond do
|
||||
String.contains?(sys_descr, "Ubuntu") ->
|
||||
version = extract_version(sys_descr, ~r/Ubuntu[\/\s]+([\d.]+)/)
|
||||
{"Ubuntu", version}
|
||||
|
||||
String.contains?(sys_descr, "Debian") ->
|
||||
version = extract_version(sys_descr, ~r/Debian[\/\s]+([\d.]+)/)
|
||||
{"Debian", version}
|
||||
|
||||
String.contains?(sys_descr, "CentOS") ->
|
||||
version = extract_version(sys_descr, ~r/CentOS[\/\s]+([\d.]+)/)
|
||||
{"CentOS", version}
|
||||
|
||||
String.contains?(sys_descr, "Red Hat") ->
|
||||
version = extract_version(sys_descr, ~r/Red Hat[\/\s]+([\d.]+)/)
|
||||
{"Red Hat Enterprise Linux", version}
|
||||
|
||||
String.contains?(sys_descr, "Linux") ->
|
||||
version = extract_version(sys_descr, ~r/Linux[\/\s]+([\d.]+)/)
|
||||
{"Linux", version}
|
||||
|
||||
true ->
|
||||
{"Unix", nil}
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_version(sys_descr, regex) do
|
||||
case Regex.run(regex, sys_descr) do
|
||||
[_, version] -> version
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_last_oid_part(oid_string) do
|
||||
oid_string
|
||||
|> String.split(".")
|
||||
|> List.last()
|
||||
end
|
||||
|
||||
defp find_matching_oid(values, index) do
|
||||
values
|
||||
|> Map.keys()
|
||||
|> Enum.find(fn oid -> String.ends_with?(oid, ".#{index}") end)
|
||||
end
|
||||
|
||||
defp get_sensor_value(values, index) do
|
||||
oid = find_matching_oid(values, index)
|
||||
value = Map.get(values, oid)
|
||||
parse_float(value)
|
||||
end
|
||||
|
||||
defp divisor_for_type("celsius"), do: 1000
|
||||
defp divisor_for_type("volts"), do: 1000
|
||||
defp divisor_for_type("rpm"), do: 1
|
||||
defp divisor_for_type(_), do: 1
|
||||
|
||||
defp parse_integer(value) when is_integer(value), do: value
|
||||
defp parse_integer(value) when is_binary(value), do: String.to_integer(value)
|
||||
defp parse_integer(_), do: nil
|
||||
|
||||
defp parse_float(value) when is_float(value), do: value
|
||||
defp parse_float(value) when is_integer(value), do: value / 1.0
|
||||
defp parse_float(value) when is_binary(value), do: String.to_float(value)
|
||||
defp parse_float(_), do: nil
|
||||
|
||||
@doc """
|
||||
Collects raw debug data for troubleshooting.
|
||||
Includes LM-SENSORS and UCD-SNMP tables.
|
||||
"""
|
||||
def collect_raw_debug_data(client_opts) do
|
||||
%{
|
||||
timestamp: DateTime.utc_now(),
|
||||
lm_sensors_oids: @lm_sensors_oids,
|
||||
ucd_snmp_oids: @ucd_snmp_oids,
|
||||
lm_temp_table: safe_walk(client_opts, "1.3.6.1.4.1.2021.13.16.2"),
|
||||
lm_fan_table: safe_walk(client_opts, "1.3.6.1.4.1.2021.13.16.3"),
|
||||
lm_voltage_table: safe_walk(client_opts, "1.3.6.1.4.1.2021.13.16.4"),
|
||||
ucd_load_table: safe_walk(client_opts, "1.3.6.1.4.1.2021.10"),
|
||||
ucd_memory_table: safe_walk(client_opts, "1.3.6.1.4.1.2021.4")
|
||||
}
|
||||
end
|
||||
|
||||
defp safe_walk(client_opts, oid) do
|
||||
case Client.walk(client_opts, oid) do
|
||||
{:ok, results} -> results
|
||||
{:error, _} -> %{}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,262 +0,0 @@
|
|||
defmodule Towerops.Snmp.Profiles.Ubiquiti do
|
||||
@moduledoc """
|
||||
SNMP profile for Ubiquiti devices (AirFiber, AirMax, etc.) using Ubiquiti airOS MIB.
|
||||
|
||||
Discovers wireless-specific sensors:
|
||||
- TX/RX Frequency
|
||||
- TX/RX Throughput
|
||||
- TX/RX Capacity
|
||||
- Signal Strength
|
||||
- Remote device information
|
||||
"""
|
||||
|
||||
alias Towerops.Snmp.Client
|
||||
alias Towerops.Snmp.Profiles.Base
|
||||
|
||||
require Logger
|
||||
|
||||
# Ubiquiti enterprise MIB base: 1.3.6.1.4.1.41112
|
||||
@ubnt_base "1.3.6.1.4.1.41112"
|
||||
|
||||
# UniFi specific OIDs (.1.6.3)
|
||||
@unifi_system_model "#{@ubnt_base}.1.6.3.3.0"
|
||||
@unifi_system_version "#{@ubnt_base}.1.6.3.6.0"
|
||||
|
||||
# Radio configuration OIDs (.1.3.1.1)
|
||||
@radio_config_base "#{@ubnt_base}.1.3.1.1"
|
||||
@radio_tx_freq "#{@radio_config_base}.5"
|
||||
@radio_rx_freq "#{@radio_config_base}.6"
|
||||
@radio_tx_power "#{@radio_config_base}.9"
|
||||
|
||||
# Radio statistics OIDs (.1.3.2.1)
|
||||
@radio_stats_base "#{@ubnt_base}.1.3.2.1"
|
||||
@radio_tx_throughput "#{@radio_stats_base}.3"
|
||||
@radio_rx_throughput "#{@radio_stats_base}.4"
|
||||
@radio_tx_capacity_bps "#{@radio_stats_base}.5"
|
||||
@radio_rx_capacity_bps "#{@radio_stats_base}.6"
|
||||
@radio_tx_signal "#{@radio_stats_base}.7"
|
||||
@radio_rx_signal "#{@radio_stats_base}.8"
|
||||
@radio_remote_tx_power "#{@radio_stats_base}.11"
|
||||
@radio_remote_rx_power "#{@radio_stats_base}.14"
|
||||
|
||||
@doc """
|
||||
Discovers system information, extending Base with UniFi-specific OIDs.
|
||||
"""
|
||||
def discover_system_info(client_opts) do
|
||||
with {:ok, base_info} <- Base.discover_system_info(client_opts) do
|
||||
# Try to get UniFi-specific model and version
|
||||
unifi_info = discover_unifi_system_info(client_opts)
|
||||
{:ok, Map.merge(base_info, unifi_info)}
|
||||
end
|
||||
end
|
||||
|
||||
defp discover_unifi_system_info(client_opts) do
|
||||
case Client.get_multiple(client_opts, [@unifi_system_model, @unifi_system_version]) do
|
||||
{:ok, [model, version]} when is_binary(model) or is_binary(version) ->
|
||||
%{
|
||||
unifi_model: model,
|
||||
unifi_version: version
|
||||
}
|
||||
|
||||
_ ->
|
||||
%{}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Discovers interfaces using base profile.
|
||||
"""
|
||||
def discover_interfaces(client_opts) do
|
||||
Base.discover_interfaces(client_opts)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Discovers Ubiquiti wireless sensors.
|
||||
Includes frequency, throughput, capacity, and signal strength metrics.
|
||||
"""
|
||||
def discover_sensors(client_opts) do
|
||||
# Walk the radio config base to see if this is a radio device
|
||||
case Client.walk(client_opts, @radio_config_base) do
|
||||
{:ok, config} when map_size(config) > 0 ->
|
||||
# This is a Ubiquiti radio device, discover wireless sensors
|
||||
Logger.debug("Discovered Ubiquiti radio device")
|
||||
sensors = discover_radio_sensors(client_opts)
|
||||
{:ok, sensors}
|
||||
|
||||
_ ->
|
||||
# Not a radio device or no radio data, fall back to base
|
||||
Logger.debug("No Ubiquiti radio sensors found, using base profile")
|
||||
Base.discover_sensors(client_opts)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Identifies Ubiquiti device from sysDescr and UniFi-specific OIDs.
|
||||
"""
|
||||
def identify_device(system_info) do
|
||||
sys_descr = Map.get(system_info, :sys_descr, "")
|
||||
unifi_model = Map.get(system_info, :unifi_model)
|
||||
unifi_version = Map.get(system_info, :unifi_version)
|
||||
|
||||
# Prefer UniFi-specific model/version if available, otherwise detect from sysDescr
|
||||
{model, firmware} =
|
||||
if unifi_model || unifi_version do
|
||||
{unifi_model || "UniFi Device", unifi_version}
|
||||
else
|
||||
{detect_model(sys_descr), extract_airos_version(sys_descr)}
|
||||
end
|
||||
|
||||
Map.merge(system_info, %{
|
||||
manufacturer: "Ubiquiti",
|
||||
model: model,
|
||||
firmware_version: firmware
|
||||
})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Collects raw debug data for troubleshooting.
|
||||
Includes UniFi system OIDs, radio configuration and statistics.
|
||||
"""
|
||||
def collect_raw_debug_data(client_opts) do
|
||||
# UniFi system OIDs
|
||||
unifi_system_oids = %{
|
||||
unifi_model: @unifi_system_model,
|
||||
unifi_version: @unifi_system_version
|
||||
}
|
||||
|
||||
# Radio configuration OIDs
|
||||
radio_config_oids = %{
|
||||
radio_tx_freq: @radio_tx_freq,
|
||||
radio_rx_freq: @radio_rx_freq,
|
||||
radio_tx_power: @radio_tx_power
|
||||
}
|
||||
|
||||
# Radio statistics OIDs
|
||||
radio_stats_oids = %{
|
||||
radio_tx_throughput: @radio_tx_throughput,
|
||||
radio_rx_throughput: @radio_rx_throughput,
|
||||
radio_tx_capacity: @radio_tx_capacity_bps,
|
||||
radio_rx_capacity: @radio_rx_capacity_bps,
|
||||
radio_tx_signal: @radio_tx_signal,
|
||||
radio_rx_signal: @radio_rx_signal,
|
||||
radio_remote_tx_power: @radio_remote_tx_power,
|
||||
radio_remote_rx_power: @radio_remote_rx_power
|
||||
}
|
||||
|
||||
# Get UniFi system values
|
||||
unifi_system_values =
|
||||
Map.new(unifi_system_oids, fn {key, oid} ->
|
||||
value =
|
||||
case Client.get(client_opts, oid) do
|
||||
{:ok, val} -> val
|
||||
{:error, reason} -> "ERROR: #{inspect(reason)}"
|
||||
end
|
||||
|
||||
{key, %{oid: oid, value: value}}
|
||||
end)
|
||||
|
||||
# Walk radio config and stats tables
|
||||
radio_config_table =
|
||||
case Client.walk(client_opts, @radio_config_base) do
|
||||
{:ok, results} -> results
|
||||
{:error, _} -> %{}
|
||||
end
|
||||
|
||||
radio_stats_table =
|
||||
case Client.walk(client_opts, @radio_stats_base) do
|
||||
{:ok, results} -> results
|
||||
{:error, _} -> %{}
|
||||
end
|
||||
|
||||
%{
|
||||
timestamp: DateTime.utc_now(),
|
||||
unifi_system_oids: unifi_system_values,
|
||||
radio_config_oids: radio_config_oids,
|
||||
radio_stats_oids: radio_stats_oids,
|
||||
radio_config_table: radio_config_table,
|
||||
radio_stats_table: radio_stats_table
|
||||
}
|
||||
end
|
||||
|
||||
# Private functions
|
||||
|
||||
defp discover_radio_sensors(client_opts) do
|
||||
# Discover radio index (usually 1 for most devices)
|
||||
case Client.walk(client_opts, @radio_config_base) do
|
||||
{:ok, entries} when map_size(entries) > 0 ->
|
||||
# Extract radio index from first entry
|
||||
{first_oid, _} = Enum.at(entries, 0)
|
||||
index = extract_radio_index(first_oid)
|
||||
|
||||
build_radio_sensors(client_opts, index)
|
||||
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
defp build_radio_sensors(client_opts, index) do
|
||||
Enum.reject(
|
||||
[
|
||||
build_sensor(client_opts, "#{@radio_tx_freq}.#{index}", "tx_frequency", "TX Frequency", "MHz", 1),
|
||||
build_sensor(client_opts, "#{@radio_rx_freq}.#{index}", "rx_frequency", "RX Frequency", "MHz", 1),
|
||||
build_sensor(client_opts, "#{@radio_tx_throughput}.#{index}", "tx_throughput", "TX Throughput", "kbps", 1),
|
||||
build_sensor(client_opts, "#{@radio_rx_throughput}.#{index}", "rx_throughput", "RX Throughput", "kbps", 1),
|
||||
build_sensor(client_opts, "#{@radio_tx_capacity_bps}.#{index}", "tx_capacity", "TX Capacity", "bps", 1),
|
||||
build_sensor(client_opts, "#{@radio_rx_capacity_bps}.#{index}", "rx_capacity", "RX Capacity", "bps", 1),
|
||||
build_sensor(client_opts, "#{@radio_tx_signal}.#{index}", "tx_signal", "TX Signal", "dBm", 1),
|
||||
build_sensor(client_opts, "#{@radio_rx_signal}.#{index}", "rx_signal", "RX Signal", "dBm", 1),
|
||||
build_sensor(client_opts, "#{@radio_remote_tx_power}.#{index}", "remote_tx_power", "Remote TX Power", "dBm", 1),
|
||||
build_sensor(client_opts, "#{@radio_remote_rx_power}.#{index}", "remote_rx_power", "Remote RX Power", "dBm", 1),
|
||||
build_sensor(client_opts, "#{@radio_tx_power}.#{index}", "tx_power", "TX Power", "dBm", 1)
|
||||
],
|
||||
&is_nil/1
|
||||
)
|
||||
|
||||
# Frequency sensors
|
||||
|
||||
# Throughput sensors (kbps)
|
||||
|
||||
# Capacity sensors (bps)
|
||||
|
||||
# Signal strength sensors
|
||||
|
||||
# Remote power sensors
|
||||
|
||||
# TX Power from config
|
||||
end
|
||||
|
||||
defp build_sensor(_client_opts, oid, type, descr, unit, divisor) do
|
||||
%{
|
||||
sensor_type: type,
|
||||
sensor_index: oid,
|
||||
sensor_oid: oid,
|
||||
sensor_descr: descr,
|
||||
sensor_unit: unit,
|
||||
sensor_divisor: divisor
|
||||
}
|
||||
end
|
||||
|
||||
defp extract_radio_index(oid) do
|
||||
# OID format: 1.3.6.1.4.1.41112.1.3.1.1.X.INDEX
|
||||
parts = String.split(oid, ".")
|
||||
List.last(parts)
|
||||
end
|
||||
|
||||
defp extract_airos_version(sys_descr) do
|
||||
# Extract version from something like "Linux 2.6.33 #1 Wed Mar 18 11:38:22 UTC 2020 armv5tejl"
|
||||
case Regex.run(~r/Linux (\d+\.\d+\.\d+)/, sys_descr) do
|
||||
[_, version] -> version
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp detect_model(sys_descr) do
|
||||
cond do
|
||||
String.contains?(sys_descr, "AirFiber") -> "AirFiber"
|
||||
String.contains?(sys_descr, "airMAX") -> "airMAX"
|
||||
String.contains?(sys_descr, "airOS") -> "airOS Device"
|
||||
true -> "Ubiquiti Device"
|
||||
end
|
||||
end
|
||||
end
|
||||
10
priv/profiles/os_detection/3com.yaml
Normal file
10
priv/profiles/os_detection/3com.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
os: 3com
|
||||
text: 3Com
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
type: network
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.43.1.
|
||||
- .1.3.6.1.4.1.43.10.
|
||||
11
priv/profiles/os_detection/aaron.yaml
Normal file
11
priv/profiles/os_detection/aaron.yaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
os: aaron
|
||||
text: 'Aaron FM Rebroadcaster'
|
||||
type: appliance
|
||||
icon: inovonics
|
||||
group: inovonics
|
||||
over:
|
||||
- { graph: device_dbm, text: RSSI }
|
||||
discovery:
|
||||
- sysObjectID:
|
||||
- .1.3.6.1.4.1.42111.640
|
||||
- .1.3.6.1.4.1.42111.650
|
||||
19
priv/profiles/os_detection/abbups.yaml
Normal file
19
priv/profiles/os_detection/abbups.yaml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
os: abbups
|
||||
text: ABB UPS
|
||||
group: ups
|
||||
type: power
|
||||
icon: abb
|
||||
mib_dir: abb
|
||||
rfc1628_compat: true
|
||||
over:
|
||||
- { graph: device_voltage, text: Voltage }
|
||||
- { graph: device_current, text: Current }
|
||||
- { graph: device_frequency, text: Load }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.8072.3.2.10
|
||||
snmpget:
|
||||
oid: .1.3.6.1.4.1.53973.1.1.1.1.1.1.0
|
||||
op: contains
|
||||
value: ABB
|
||||
26
priv/profiles/os_detection/acano.yaml
Normal file
26
priv/profiles/os_detection/acano.yaml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
os: acano
|
||||
group: cisco
|
||||
text: 'Acano OS'
|
||||
type: collaboration
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
icon: cisco
|
||||
discovery:
|
||||
- { sysObjectID: .1.3.6.1.4.1.8072.3.2.10, sysDescr: Acano }
|
||||
poller_modules:
|
||||
cisco-cef: true
|
||||
cisco-mac-accounting: true
|
||||
cisco-remote-access-monitor: true
|
||||
slas: true
|
||||
cisco-ipsec-flow-monitor: true
|
||||
cipsec-tunnels: true
|
||||
cisco-otv: true
|
||||
discovery_modules:
|
||||
cisco-cef: true
|
||||
slas: true
|
||||
cisco-mac-accounting: true
|
||||
cisco-otv: true
|
||||
cisco-pw: true
|
||||
vrf: true
|
||||
cisco-vrf-lite: true
|
||||
13
priv/profiles/os_detection/acos.yaml
Normal file
13
priv/profiles/os_detection/acos.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
os: acos
|
||||
text: 'A10 Networks'
|
||||
type: network
|
||||
icon: a10
|
||||
group: a10
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.22610.1.3
|
||||
35
priv/profiles/os_detection/acs.yaml
Normal file
35
priv/profiles/os_detection/acs.yaml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
os: acs
|
||||
group: cisco
|
||||
text: 'Cisco ACS'
|
||||
ifname: true
|
||||
type: server
|
||||
icon: cisco
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
poller_modules:
|
||||
cisco-ace-serverfarms: true
|
||||
cisco-ace-loadbalancer: true
|
||||
cisco-cef: true
|
||||
cisco-mac-accounting: true
|
||||
cisco-remote-access-monitor: true
|
||||
slas: true
|
||||
cisco-ipsec-flow-monitor: true
|
||||
cipsec-tunnels: true
|
||||
cisco-otv: true
|
||||
discovery_modules:
|
||||
cisco-cef: true
|
||||
slas: true
|
||||
cisco-mac-accounting: true
|
||||
cisco-otv: true
|
||||
cisco-pw: true
|
||||
vrf: true
|
||||
cisco-vrf-lite: true
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.9.1.1117
|
||||
-
|
||||
sysDescr:
|
||||
- 'Cisco Secure Access Control System'
|
||||
35
priv/profiles/os_detection/acsw.yaml
Normal file
35
priv/profiles/os_detection/acsw.yaml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
os: acsw
|
||||
group: cisco
|
||||
text: 'Cisco ACE'
|
||||
ifname: true
|
||||
type: loadbalancer
|
||||
icon: cisco
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
poller_modules:
|
||||
cisco-ace-serverfarms: true
|
||||
cisco-ace-loadbalancer: true
|
||||
cisco-cef: true
|
||||
cisco-mac-accounting: true
|
||||
cisco-remote-access-monitor: true
|
||||
slas: true
|
||||
cisco-ipsec-flow-monitor: true
|
||||
cipsec-tunnels: true
|
||||
cisco-otv: true
|
||||
discovery_modules:
|
||||
cisco-cef: true
|
||||
slas: true
|
||||
cisco-mac-accounting: true
|
||||
cisco-otv: true
|
||||
cisco-pw: true
|
||||
vrf: true
|
||||
cisco-vrf-lite: true
|
||||
discovery:
|
||||
-
|
||||
sysObjectID: .1.3.6.1.4.1.9.1.1291
|
||||
-
|
||||
sysDescr:
|
||||
- 'Application Control Engine'
|
||||
- 'Cisco Application Control Software'
|
||||
16
priv/profiles/os_detection/adtran-aos.yaml
Normal file
16
priv/profiles/os_detection/adtran-aos.yaml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
os: adtran-aos
|
||||
text: 'Adtran AOS'
|
||||
type: network
|
||||
icon: adtran
|
||||
mib_dir: adtran
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID: .1.3.6.1.4.1.664
|
||||
-
|
||||
sysDescr: 'NetVanta'
|
||||
good_if:
|
||||
- EtsecBasedVirtualEthernet1
|
||||
11
priv/profiles/os_detection/adva-alm.yaml
Normal file
11
priv/profiles/os_detection/adva-alm.yaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
os: adva-alm
|
||||
text: 'Adtran ALM'
|
||||
type: network
|
||||
icon: adtran
|
||||
mib_dir: adva
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.2544.1.14
|
||||
12
priv/profiles/os_detection/adva-aos.yaml
Normal file
12
priv/profiles/os_detection/adva-aos.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
os: adva-aos
|
||||
text: 'ADVA AOS'
|
||||
type: network
|
||||
icon: adva
|
||||
group: ADVA
|
||||
over:
|
||||
- { graph: device_dbm, text: dbm }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.2544.1.20.2.
|
||||
mib_dir: adva
|
||||
19
priv/profiles/os_detection/adva-fsp150cp.yaml
Normal file
19
priv/profiles/os_detection/adva-fsp150cp.yaml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
os: adva-fsp150cp
|
||||
text: 'ADVA FSP150CP'
|
||||
type: network
|
||||
icon: adva
|
||||
group: ADVA
|
||||
ifname: true
|
||||
mib_dir: adva
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.2544.1.10.1.1
|
||||
discovery_modules:
|
||||
cisco-vrf-lite: false
|
||||
storage: false
|
||||
hr-device: false
|
||||
arp-table: false
|
||||
ucd-diskio: false
|
||||
14
priv/profiles/os_detection/adva-osa.yaml
Normal file
14
priv/profiles/os_detection/adva-osa.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
os: adva-osa
|
||||
text: 'ADVA OSA'
|
||||
type: timing
|
||||
icon: adva
|
||||
group: ADVA
|
||||
ifname: true
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
- { graph: device_temperature, text: Temperature }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.5551.3.
|
||||
mib_dir: adva
|
||||
15
priv/profiles/os_detection/adva_fsp150.yaml
Normal file
15
priv/profiles/os_detection/adva_fsp150.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
os: adva_fsp150
|
||||
text: 'ADVA FSP150CC'
|
||||
type: network
|
||||
icon: adva
|
||||
group: ADVA
|
||||
ifname: true
|
||||
mib_dir: adva
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
- { graph: device_temperature, text: Temperature }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.2544.1.12.1.1.
|
||||
sysDescr_except: ['FSP150CM-HUB'] #Exclude 150CM nemihubshelf (different OS, and failing to respond to most OID/crashing)
|
||||
13
priv/profiles/os_detection/adva_fsp3kr7.yaml
Normal file
13
priv/profiles/os_detection/adva_fsp3kr7.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
os: adva_fsp3kr7
|
||||
text: 'ADVA FSP3000 R7'
|
||||
type: network
|
||||
icon: adva
|
||||
group: ADVA
|
||||
over:
|
||||
- { graph: device_dbm, text: dbm }
|
||||
- { graph: device_temperature, text: Temperature }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.2544.1.11.1.1
|
||||
mib_dir: adva
|
||||
15
priv/profiles/os_detection/adva_xg300.yaml
Normal file
15
priv/profiles/os_detection/adva_xg300.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
os: adva_xg300
|
||||
text: 'ADVA OptiSwitch'
|
||||
type: network
|
||||
icon: adva
|
||||
group: ADVA
|
||||
ifname: true
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.629.22
|
||||
mib_dir: adva
|
||||
12
priv/profiles/os_detection/advantech.yaml
Normal file
12
priv/profiles/os_detection/advantech.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
os: advantech
|
||||
text: 'Advantech'
|
||||
type: network
|
||||
icon: advantech
|
||||
group: advantech
|
||||
mib_dir: advantech
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.10297.202
|
||||
16
priv/profiles/os_detection/aen.yaml
Normal file
16
priv/profiles/os_detection/aen.yaml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
os: aen
|
||||
text: 'Accedian AEN'
|
||||
type: network
|
||||
icon: accedian
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
mib_dir: accedian
|
||||
discovery:
|
||||
-
|
||||
sysDescr_regex:
|
||||
- '/^AMN-/'
|
||||
- '/^AEN-/'
|
||||
- '/^AMO-/'
|
||||
|
||||
9
priv/profiles/os_detection/airconsole.yaml
Normal file
9
priv/profiles/os_detection/airconsole.yaml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
os: airconsole
|
||||
text: 'Airconsole Server'
|
||||
type: network
|
||||
icon: airconsole
|
||||
group: airconsole
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.14358.1
|
||||
18
priv/profiles/os_detection/airos-af-ltu.yaml
Normal file
18
priv/profiles/os_detection/airos-af-ltu.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
os: airos-af-ltu
|
||||
text: 'Ubiquiti AirFiber LTU'
|
||||
type: wireless
|
||||
icon: ubiquiti
|
||||
snmp_bulk: false
|
||||
mib_dir: ubnt
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_wireless_rate, text: 'Wireless Rate' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID: .1.3.6.1.4.1.8072.3.2.10
|
||||
sysDescr: Linux
|
||||
snmpget:
|
||||
oid: UBNT-AFLTU-MIB::afLTUFirmwareVersion.0
|
||||
op: '!='
|
||||
value: false
|
||||
18
priv/profiles/os_detection/airos-af.yaml
Normal file
18
priv/profiles/os_detection/airos-af.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
os: airos-af
|
||||
text: 'Ubiquiti AirFiber'
|
||||
type: wireless
|
||||
icon: ubiquiti
|
||||
snmp_bulk: false
|
||||
mib_dir: ubnt
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_wireless_rate, text: 'Wireless Rate' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID: .1.3.6.1.4.1.10002.1
|
||||
sysDescr: Linux
|
||||
snmpget:
|
||||
oid: UBNT-AirFIBER-MIB::fwVersion.1
|
||||
op: '!='
|
||||
value: false
|
||||
20
priv/profiles/os_detection/airos-af60.yaml
Normal file
20
priv/profiles/os_detection/airos-af60.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
os: airos-af60
|
||||
text: 'Ubiquiti AirFiber 60'
|
||||
type: wireless
|
||||
icon: ubiquiti
|
||||
snmp_bulk: false
|
||||
mib_dir: ubnt
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_wireless_rate, text: 'Wireless Rate' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.10002.1
|
||||
- .1.3.6.1.4.1.41112
|
||||
sysDescr: Linux
|
||||
snmpget:
|
||||
oid: UI-AF60-MIB::af60FirmwareVersion.1
|
||||
op: '!='
|
||||
value: false
|
||||
20
priv/profiles/os_detection/airos.yaml
Normal file
20
priv/profiles/os_detection/airos.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
os: airos
|
||||
text: 'Ubiquiti AirOS'
|
||||
type: wireless
|
||||
icon: ubiquiti
|
||||
snmp_bulk: false
|
||||
mib_dir: ubnt
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_wireless_clients, text: 'Connected Clients' }
|
||||
- { graph: device_wireless_ccq, text: 'Connection Quality' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.10002.1
|
||||
- .1.3.6.1.4.1.41112.1.4
|
||||
sysDescr: Linux
|
||||
snmpget:
|
||||
oid: UI-AF60-MIB::af60Role.1
|
||||
op: '='
|
||||
value: false
|
||||
13
priv/profiles/os_detection/airport.yaml
Normal file
13
priv/profiles/os_detection/airport.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
os: airport
|
||||
type: wireless
|
||||
text: 'Apple AirPort'
|
||||
icon: apple
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
- { graph: device_wireless_clients, text: Clients }
|
||||
discovery:
|
||||
-
|
||||
sysDescr:
|
||||
- Apple AirPort
|
||||
- Apple Base Station
|
||||
- Base Station V3.84
|
||||
27
priv/profiles/os_detection/aix.yaml
Normal file
27
priv/profiles/os_detection/aix.yaml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
os: aix
|
||||
text: 'AIX'
|
||||
type: server
|
||||
icon: aix
|
||||
group: unix
|
||||
mib_dir: ibm
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
poller_modules:
|
||||
bgp-peers: false
|
||||
ospf: false
|
||||
stp: false
|
||||
discovery_modules:
|
||||
applications: false
|
||||
bgp-peers: false
|
||||
stp: false
|
||||
wireless: false
|
||||
processor_stacked: true
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.8072.3.2.15
|
||||
- .1.3.6.1.4.1.2.3.1.
|
||||
ignore_mount_type:
|
||||
- procfs
|
||||
10
priv/profiles/os_detection/akcp.yaml
Normal file
10
priv/profiles/os_detection/akcp.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
os: akcp
|
||||
text: 'AKCP SensorProbe'
|
||||
type: environment
|
||||
mib_dir: akcp
|
||||
over:
|
||||
- { graph: device_temperature, text: temperature }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.3854
|
||||
11
priv/profiles/os_detection/alcoma-almp.yaml
Normal file
11
priv/profiles/os_detection/alcoma-almp.yaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
os: alcoma-almp
|
||||
text: Alcoma
|
||||
type: wireless
|
||||
over:
|
||||
- { graph: device_wireless_rssi, text: 'Wireless rssi' }
|
||||
- { graph: device_wireless_snr, text: 'Wireles snr' }
|
||||
- { graph: device_wireless_power, text: 'Wireless power' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID: .1.3.6.1.4.1.12140.2
|
||||
mib_dir: alcoma
|
||||
10
priv/profiles/os_detection/alfo80hd.yaml
Normal file
10
priv/profiles/os_detection/alfo80hd.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
os: alfo80hd
|
||||
text: 'SIAE Alfoplus 80HD'
|
||||
type: wireless
|
||||
icon: siae
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
- { graph: device_wireless_rssi, text: RSSI }
|
||||
- { graph: device_wireless_power, text: POWER }
|
||||
discovery:
|
||||
- { sysObjectID: .1.3.6.1.4.1.3373.1103, sysDescr: 'ALFOplus80HD - SIAE Microelettronica' }
|
||||
17
priv/profiles/os_detection/algcom-dc-ups.yaml
Normal file
17
priv/profiles/os_detection/algcom-dc-ups.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
os: algcom-dc-ups
|
||||
text: 'ALGCOM DC UPS'
|
||||
type: power
|
||||
icon: algcom
|
||||
|
||||
over:
|
||||
- { graph: device_temperature, text: Temperature }
|
||||
- { graph: device_humidity, text: Humidity }
|
||||
- { graph: device_voltage, text: Voltage }
|
||||
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.49136
|
||||
sysObjectID_except:
|
||||
- .1.3.6.1.4.1.49136.100
|
||||
mib_dir: algcom
|
||||
13
priv/profiles/os_detection/algcom-sm.yaml
Normal file
13
priv/profiles/os_detection/algcom-sm.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
os: algcom-sm
|
||||
text: 'ALGCOM SM'
|
||||
type: environment
|
||||
icon: algcom
|
||||
mib_dir: algcom
|
||||
over:
|
||||
- { graph: device_temperature, text: Temperature }
|
||||
- { graph: device_humidity, text: Humidity }
|
||||
- { graph: device_voltage, text: Voltage }
|
||||
|
||||
discovery:
|
||||
- sysObjectID:
|
||||
- .1.3.6.1.4.1.49136.100
|
||||
13
priv/profiles/os_detection/allied-tq.yaml
Executable file
13
priv/profiles/os_detection/allied-tq.yaml
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
os: allied-tq
|
||||
text: 'Allied Telesis Wireless (TQ)'
|
||||
type: wireless
|
||||
icon: alliedtelesis
|
||||
group: allied
|
||||
ifname: true
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID: .1.3.6.1.4.1.207.1.13.
|
||||
sysDescr_except: 'AW+'
|
||||
mib_dir: awplus
|
||||
21
priv/profiles/os_detection/allied.yaml
Normal file
21
priv/profiles/os_detection/allied.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
os: allied
|
||||
text: Alliedware
|
||||
type: network
|
||||
icon: alliedtelesis
|
||||
group: allied
|
||||
ifname: true
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID: .1.3.6.1.4.1.207.
|
||||
sysObjectID_except:
|
||||
- .1.3.6.1.4.1.207.1.4.125 #exclude the following ATI 8000S/8000GS which identify as "radlan" OS.
|
||||
- .1.3.6.1.4.1.207.1.4.126
|
||||
- .1.3.6.1.4.1.207.1.4.127
|
||||
- .1.3.6.1.4.1.207.1.4.128
|
||||
- .1.3.6.1.4.1.207.1.4.143
|
||||
- .1.3.6.1.4.1.207.1.4.144
|
||||
- .1.3.6.1.4.1.207.1.4.145
|
||||
sysDescr_except: ['AW+', 'Software (AlliedWare Plus)']
|
||||
mib_dir: allied
|
||||
11
priv/profiles/os_detection/allworx_voip.yaml
Normal file
11
priv/profiles/os_detection/allworx_voip.yaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
os: allworx_voip
|
||||
text: 'Allworx VoIP'
|
||||
type: appliance
|
||||
icon: allworx
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.38516.2.
|
||||
15
priv/profiles/os_detection/aloha.yaml
Normal file
15
priv/profiles/os_detection/aloha.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
os: aloha
|
||||
text: HAProxy ALOHA
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
type: loadbalancer
|
||||
group: linux
|
||||
mib_dir: haproxy
|
||||
icon: haproxy
|
||||
discovery:
|
||||
-
|
||||
sysObjectID: .1.3.6.1.4.1.8072.3.2.10
|
||||
snmpget:
|
||||
oid: EXCELIANCE-MIB::alProductName
|
||||
op: '='
|
||||
value: 'aloha'
|
||||
13
priv/profiles/os_detection/alpineoe-tdcmedfa.yaml
Normal file
13
priv/profiles/os_detection/alpineoe-tdcmedfa.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
os: alpineoe-tdcmedfa
|
||||
text: 'Alpine OptoElectronics TDCM-EDFA platform'
|
||||
type: appliance
|
||||
icon: alpineoe
|
||||
mib_dir: alpineoe
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.52326
|
||||
12
priv/profiles/os_detection/altalabs-ap.yaml
Normal file
12
priv/profiles/os_detection/altalabs-ap.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
os: altalabs-ap
|
||||
text: Alta Labs AP
|
||||
icon: altalabs
|
||||
type: wireless
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.8072.3.2.10
|
||||
sysDescr_regex:
|
||||
- '/^Alta AP/'
|
||||
12
priv/profiles/os_detection/altalabs.yaml
Normal file
12
priv/profiles/os_detection/altalabs.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
os: altalabs
|
||||
text: Alta Labs
|
||||
type: network
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.8072.3.2.10
|
||||
sysDescr_regex:
|
||||
- '/^Alta Route/'
|
||||
- '/^Alta S/'
|
||||
14
priv/profiles/os_detection/alteonos.yaml
Normal file
14
priv/profiles/os_detection/alteonos.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
os: alteonos
|
||||
group: radware
|
||||
text: AlteonOS
|
||||
type: network
|
||||
icon: radware
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.1872.1.13.3.5
|
||||
- .1.3.6.1.4.1.1872.2.
|
||||
13
priv/profiles/os_detection/anue.yaml
Normal file
13
priv/profiles/os_detection/anue.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
os: anue
|
||||
text: 'Anue'
|
||||
type: network
|
||||
icon: ixia
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'Processor Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
mib_dir: ixia
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.32620.
|
||||
18
priv/profiles/os_detection/anyos.yaml
Normal file
18
priv/profiles/os_detection/anyos.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
os: anyos
|
||||
text: AnyOS
|
||||
type: network
|
||||
group: zyxel
|
||||
icon: zyxel
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.4413
|
||||
sysDescr:
|
||||
- 'AATO'
|
||||
- 'AAJZ'
|
||||
poller_modules:
|
||||
xdsl: true
|
||||
discovery_modules:
|
||||
xdsl: true
|
||||
51
priv/profiles/os_detection/aos-emu2.yaml
Normal file
51
priv/profiles/os_detection/aos-emu2.yaml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
os: aos-emu2
|
||||
text: 'APC Environmental Monitoring Unit'
|
||||
type: environment
|
||||
icon: apc
|
||||
over:
|
||||
- { graph: device_temperature, text: Temperature }
|
||||
- { graph: device_humidity, text: Humidity }
|
||||
mib_dir: apc
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.318.1.3.8.2
|
||||
- .1.3.6.1.4.1.318.1.3.8.3
|
||||
discovery_modules:
|
||||
ports: false
|
||||
ports-stack: false
|
||||
entity-physical: false
|
||||
processors: false
|
||||
mempools: false
|
||||
cisco-vrf-lite: false
|
||||
storage: false
|
||||
hr-device: false
|
||||
discovery-protocols: false
|
||||
bgp-peers: false
|
||||
vlans: false
|
||||
ucd-diskio: false
|
||||
services: false
|
||||
stp: false
|
||||
ntp: false
|
||||
wireless: false
|
||||
fdb-table: false
|
||||
poller_modules:
|
||||
ipmi: false
|
||||
entity-physical: false
|
||||
processors: false
|
||||
mempools: false
|
||||
storage: false
|
||||
netstats: false
|
||||
hr-mib: false
|
||||
ucd-mib: false
|
||||
ipSystemStats: false
|
||||
ports: false
|
||||
bgp-peers: false
|
||||
ospf: false
|
||||
ucd-diskio: false
|
||||
services: false
|
||||
stp: false
|
||||
ntp: false
|
||||
wireless: false
|
||||
fdb-table: false
|
||||
applications: false
|
||||
16
priv/profiles/os_detection/aos.yaml
Normal file
16
priv/profiles/os_detection/aos.yaml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
os: aos
|
||||
group: nokia
|
||||
text: 'Alcatel-Lucent OS'
|
||||
type: network
|
||||
ifXmcbc: true
|
||||
ifname: true
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_temperature, text: 'Temperature' }
|
||||
icon: alcatellucent
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.6486.800.1.1.2.2.4. #OmniStack LS
|
||||
mib_dir: radlan
|
||||
16
priv/profiles/os_detection/aos6.yaml
Executable file
16
priv/profiles/os_detection/aos6.yaml
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
os: aos6
|
||||
group: nokia
|
||||
text: 'Alcatel-Lucent OS'
|
||||
type: network
|
||||
ifXmcbc: true
|
||||
ifname: true
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_temperature, text: 'Temperature' }
|
||||
icon: alcatellucent
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.6486.800.1.1.2.1. #aos6
|
||||
mib_dir: nokia
|
||||
20
priv/profiles/os_detection/aos7.yaml
Normal file
20
priv/profiles/os_detection/aos7.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
os: aos7
|
||||
group: nokia
|
||||
text: 'Alcatel-Lucent OS'
|
||||
type: network
|
||||
ifXmcbc: true
|
||||
ifname: true
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_temperature, text: 'Temperature' }
|
||||
icon: alcatellucent
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.6486.801. #aos7
|
||||
mib_dir: nokia/aos7
|
||||
discovery_modules:
|
||||
bgp-peers: true
|
||||
poller_modules:
|
||||
bgp-peers: true
|
||||
12
priv/profiles/os_detection/apc-cpdu.yaml
Normal file
12
priv/profiles/os_detection/apc-cpdu.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
os: apc-cpdu
|
||||
text: 'APC cPDU'
|
||||
type: power
|
||||
icon: apc
|
||||
mib_dir: apc-cpdu
|
||||
over:
|
||||
- { graph: device_current, text: Current }
|
||||
- { graph: device_power, text: Power }
|
||||
discovery:
|
||||
- sysObjectID:
|
||||
- .1.3.6.1.4.1.318.1.1.32.1
|
||||
|
||||
18
priv/profiles/os_detection/apc-epdu.yaml
Normal file
18
priv/profiles/os_detection/apc-epdu.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
os: apc-epdu
|
||||
text: 'APC ePDU'
|
||||
type: power
|
||||
icon: apc
|
||||
mib_dir: apc
|
||||
over:
|
||||
- { graph: device_current, text: Current }
|
||||
- { graph: device_voltage, text: Voltage }
|
||||
|
||||
discovery:
|
||||
- sysObjectID:
|
||||
- .1.3.6.1.4.1.318.1.3.4.9
|
||||
- sysObjectID:
|
||||
- .1.3.6.1.4.1.0
|
||||
snmpget:
|
||||
oid: .1.3.6.1.6.3.10.2.1.1.0
|
||||
op: '='
|
||||
value: APC
|
||||
20
priv/profiles/os_detection/apc-mgeups.yaml
Normal file
20
priv/profiles/os_detection/apc-mgeups.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
os: apc-mgeups
|
||||
text: 'APC MGE UPS'
|
||||
group: ups
|
||||
type: power
|
||||
icon: apc
|
||||
rfc1628_compat: true
|
||||
mib_dir: eaton
|
||||
over:
|
||||
- { graph: device_current, text: Current }
|
||||
- { graph: device_voltage, text: Voltage }
|
||||
- { graph: device_load, text: Load }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.705.1
|
||||
sysDescr_regex:
|
||||
- '/^Galaxy/'
|
||||
- '/^GALAXY/'
|
||||
- '/^MGE Galaxy/'
|
||||
# Upsilon too (no data)
|
||||
12
priv/profiles/os_detection/apc-netbotz.yaml
Normal file
12
priv/profiles/os_detection/apc-netbotz.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
os: apc-netbotz
|
||||
text: 'NetBotz Environment Sensor'
|
||||
type: environment
|
||||
icon: apc
|
||||
over:
|
||||
- { graph: device_temperature, text: Temperature }
|
||||
- { graph: device_humidity, text: Humidity }
|
||||
mib_dir: netbotz
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.52674.500
|
||||
25
priv/profiles/os_detection/apc.yaml
Normal file
25
priv/profiles/os_detection/apc.yaml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
os: apc
|
||||
text: 'APC Management Module'
|
||||
type: power
|
||||
over:
|
||||
- { graph: device_current, text: Current }
|
||||
- { graph: device_voltage, text: Voltage }
|
||||
- { graph: device_runtime, text: Runtime }
|
||||
discovery:
|
||||
-
|
||||
sysDescr:
|
||||
- APC Web/SNMP Management Card
|
||||
- APC Switched Rack PDU
|
||||
- APC MasterSwitch PDU
|
||||
- APC Metered Rack PDU
|
||||
- APC Embedded PowerNet
|
||||
- APC Environmental Manager
|
||||
- APC InRow DX
|
||||
- APC Uniflair SP
|
||||
- APC InfraStruXure ATS
|
||||
- APC SNMP Agent
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.318.1
|
||||
sysObjectID_except:
|
||||
- .1.3.6.1.4.1.318.1.3.4.9
|
||||
10
priv/profiles/os_detection/apex-lynx.yaml
Normal file
10
priv/profiles/os_detection/apex-lynx.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
os: apex-lynx
|
||||
text: 'Apex Lynx'
|
||||
type: wireless
|
||||
icon: trango
|
||||
mib_dir: trango
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
discovery:
|
||||
-
|
||||
sysDescr_regex: '/^Apex Lynx/'
|
||||
10
priv/profiles/os_detection/apex-plus.yaml
Normal file
10
priv/profiles/os_detection/apex-plus.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
os: apex-plus
|
||||
text: 'Apex Plus'
|
||||
type: wireless
|
||||
icon: trango
|
||||
mib_dir: trango
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
discovery:
|
||||
-
|
||||
sysDescr_regex: '/^ApexPlus.*/'
|
||||
29
priv/profiles/os_detection/apic.yaml
Normal file
29
priv/profiles/os_detection/apic.yaml
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
os: apic
|
||||
group: cisco
|
||||
text: 'Cisco APIC'
|
||||
type: network
|
||||
icon: cisco
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_temperature, text: Temperature }
|
||||
poller_modules:
|
||||
cisco-cef: true
|
||||
cisco-mac-accounting: true
|
||||
cisco-remote-access-monitor: true
|
||||
slas: true
|
||||
cisco-ipsec-flow-monitor: true
|
||||
cipsec-tunnels: true
|
||||
cisco-otv: true
|
||||
discovery_modules:
|
||||
cisco-cef: true
|
||||
slas: true
|
||||
cisco-mac-accounting: true
|
||||
cisco-otv: true
|
||||
cisco-pw: true
|
||||
vrf: true
|
||||
cisco-vrf-lite: true
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.9.1.2238
|
||||
10
priv/profiles/os_detection/applicationsware.yaml
Normal file
10
priv/profiles/os_detection/applicationsware.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
os: applicationsware
|
||||
text: 'Vanguard ApplicationsWare'
|
||||
type: network
|
||||
icon: vanguard
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.449.2.1.
|
||||
14
priv/profiles/os_detection/aprisa.yaml
Normal file
14
priv/profiles/os_detection/aprisa.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
os: aprisa
|
||||
text: 'Aprisa'
|
||||
type: wireless
|
||||
icon: 4rf
|
||||
group: 4rf
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.14817.7.3
|
||||
mib_dir: 4rf
|
||||
|
||||
14
priv/profiles/os_detection/apsoluteos.yaml
Normal file
14
priv/profiles/os_detection/apsoluteos.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
os: apsoluteos
|
||||
text: ApsoluteOS
|
||||
type: network
|
||||
icon: radware
|
||||
group: radware
|
||||
mib_dir: radware
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.89.1.1.62
|
||||
21
priv/profiles/os_detection/arbos.yaml
Normal file
21
priv/profiles/os_detection/arbos.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
os: arbos
|
||||
text: ArbOS
|
||||
type: network
|
||||
icon: arbor
|
||||
group: arbor
|
||||
ifXmcbc: true
|
||||
ifname: true
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.9694.1
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.8072.3.2.10
|
||||
sysDescr:
|
||||
- arbux
|
||||
mib_dir: arbornet
|
||||
10
priv/profiles/os_detection/ardmore-encoder.yaml
Normal file
10
priv/profiles/os_detection/ardmore-encoder.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
os: ardmore-encoder
|
||||
text: 'AvediaStream Encoder'
|
||||
type: appliance
|
||||
icon: exterity
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
discovery:
|
||||
-
|
||||
sysDescr_regex:
|
||||
- '/Ardmore-Encoder_VX/'
|
||||
11
priv/profiles/os_detection/areca.yaml
Normal file
11
priv/profiles/os_detection/areca.yaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
os: areca
|
||||
text: 'Areca RAID Subsystem'
|
||||
type: appliance
|
||||
over:
|
||||
- { graph: '', text: '' }
|
||||
discovery:
|
||||
-
|
||||
sysDescr:
|
||||
- Raid Subsystem V
|
||||
sysObjectID_except:
|
||||
- .1.3.6.1.4.1.14752.1.3 #Proware/EuroStor
|
||||
22
priv/profiles/os_detection/arista-mos.yaml
Normal file
22
priv/profiles/os_detection/arista-mos.yaml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
os: arista-mos
|
||||
text: 'Arista MOS'
|
||||
type: network
|
||||
icon: arista
|
||||
group: arista
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.43191.1.2.5
|
||||
- .1.3.6.1.4.1.43191.1.2.7
|
||||
- .1.3.6.1.4.1.43191.1.2.8
|
||||
- .1.3.6.1.4.1.43191.1.6.6
|
||||
- .1.3.6.1.4.1.43191.1.6.7
|
||||
- .1.3.6.1.4.1.43191.1.6.9
|
||||
- .1.3.6.1.4.1.43191.1.6.10
|
||||
- .1.3.6.1.4.1.43191.1.7.5
|
||||
- .1.3.6.1.4.1.43191.1.7.6
|
||||
- .1.3.6.1.4.1.43191.1.7.7
|
||||
23
priv/profiles/os_detection/arista_eos.yaml
Normal file
23
priv/profiles/os_detection/arista_eos.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
os: arista_eos
|
||||
text: 'Arista EOS'
|
||||
type: network
|
||||
icon: arista
|
||||
group: arista
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID: .1.3.6.1.4.1.30065.1
|
||||
poller_modules:
|
||||
wireless: false
|
||||
ipmi: false
|
||||
applications: false
|
||||
services: false
|
||||
ntp: false
|
||||
discovery_modules:
|
||||
cisco-vrf-lite: false
|
||||
services: false
|
||||
ntp: false
|
||||
wireless: false
|
||||
9
priv/profiles/os_detection/arrayos.yaml
Normal file
9
priv/profiles/os_detection/arrayos.yaml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
os: arrayos
|
||||
text: 'Array Networks'
|
||||
type: loadbalancer
|
||||
icon: arraynetworks
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.7564
|
||||
mib_dir: arraynetworks
|
||||
11
priv/profiles/os_detection/arris-apex.yaml
Normal file
11
priv/profiles/os_detection/arris-apex.yaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
os: arris-apex
|
||||
text: 'Arris Apex'
|
||||
type: network
|
||||
icon: arris
|
||||
mib_dir: arris
|
||||
over:
|
||||
- { graph: '', text: '' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.1166.1.31
|
||||
11
priv/profiles/os_detection/arris-c3.yaml
Normal file
11
priv/profiles/os_detection/arris-c3.yaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
os: arris-c3
|
||||
text: 'Arris CMTS'
|
||||
type: network
|
||||
icon: arris
|
||||
mib_dir: arris
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.4115.1.4.3
|
||||
13
priv/profiles/os_detection/arris-c4.yaml
Normal file
13
priv/profiles/os_detection/arris-c4.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
os: arris-c4
|
||||
text: 'Arris CMTS'
|
||||
type: network
|
||||
icon: arris
|
||||
mib_dir: arris
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.4998.2.1
|
||||
- .1.3.6.1.4.1.4998.2.2
|
||||
- .1.3.6.1.4.1.4115.1.9.1
|
||||
11
priv/profiles/os_detection/arris-cm.yaml
Normal file
11
priv/profiles/os_detection/arris-cm.yaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
os: arris-cm
|
||||
text: 'ARRIS DOCSIS'
|
||||
type: network
|
||||
icon: arris
|
||||
mib_dir: arris
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.4115
|
||||
sysDescr:
|
||||
- ARRIS DOCSIS
|
||||
12
priv/profiles/os_detection/arris-d5.yaml
Normal file
12
priv/profiles/os_detection/arris-d5.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
os: arris-d5
|
||||
text: 'Arris D5 Universal EdgeQAM'
|
||||
type: network
|
||||
icon: arris
|
||||
group: arris
|
||||
mib_dir: arris/d5
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.4115.1.8
|
||||
30
priv/profiles/os_detection/arris-dsr4410md.yaml
Normal file
30
priv/profiles/os_detection/arris-dsr4410md.yaml
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
os: arris-dsr4410md
|
||||
text: 'Arris Satellite Receiver'
|
||||
type: network
|
||||
icon: arris
|
||||
mib_dir: arris
|
||||
over:
|
||||
- { graph: device_wireless_rssi, text: Sat Signal Level }
|
||||
- { graph: device_wireless_quality, text: Sat Signal Quality }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID: .1.3.6.1.4.1.1166.1.621
|
||||
discovery_modules:
|
||||
ports: false
|
||||
mempools: false
|
||||
processors: false
|
||||
fdb-table: false
|
||||
stp: false
|
||||
ntp: false
|
||||
bgp-peers: false
|
||||
arp-table: false
|
||||
vlans: false
|
||||
ucd-diskio: false
|
||||
cisco-vrf-lite: false
|
||||
entity-physical: false
|
||||
ports-stack: false
|
||||
ipv6-addresses: false
|
||||
services: false
|
||||
discovery-protocols: false
|
||||
hr-device: false
|
||||
storage: false
|
||||
15
priv/profiles/os_detection/aruba-instant.yaml
Normal file
15
priv/profiles/os_detection/aruba-instant.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
os: aruba-instant
|
||||
text: 'Aruba Instant'
|
||||
type: wireless
|
||||
icon: aruba
|
||||
mib_dir: arubaos
|
||||
over:
|
||||
- { graph: device_wireless_ap-count, text: 'AP Count' }
|
||||
- { graph: device_wireless_clients, text: 'Client Count' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID: .1.3.6.1.4.1.14823.1.2. #Aruba apProducts (Aruba Instant)
|
||||
sysDescr:
|
||||
- 'ArubaOS'
|
||||
- 'AOS-10'
|
||||
- 'AOS-8'
|
||||
13
priv/profiles/os_detection/arubaos-cx.yaml
Normal file
13
priv/profiles/os_detection/arubaos-cx.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
os: arubaos-cx
|
||||
text: 'ArubaOS-CX'
|
||||
type: network
|
||||
mib_dir: arubaos-cx
|
||||
icon: aruba
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.47196.4.1.1.1 #ArubaOS-CX
|
||||
24
priv/profiles/os_detection/arubaos.yaml
Normal file
24
priv/profiles/os_detection/arubaos.yaml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
os: arubaos
|
||||
text: ArubaOS
|
||||
type: wireless
|
||||
icon: aruba
|
||||
over:
|
||||
- { graph: device_wireless_ap-count, text: 'AP Count' }
|
||||
- { graph: device_wireless_clients, text: 'Client Count' }
|
||||
- { graph: device_arubacontroller_numaps, text: 'Number of APs (Legacy)' }
|
||||
- { graph: device_arubacontroller_numclients, text: 'Number of Clients (Legacy)' }
|
||||
poller_modules:
|
||||
aruba-controller: true
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.14823. #ArubaOS
|
||||
- .1.3.6.1.4.1.6486.800.1.1.2.2.2. #AOS-W
|
||||
sysObjectID_except:
|
||||
- .1.3.6.1.4.1.14823.1.2 #Aruba apProducts (Aruba Instant)
|
||||
- .1.3.6.1.4.1.14823.1.6 #ClearPass
|
||||
-
|
||||
sysDescr:
|
||||
- ArubaOS
|
||||
sysObjectID_except:
|
||||
- .1.3.6.1.4.1.14823.1.2 #Aruba apProducts (Aruba Instant)
|
||||
35
priv/profiles/os_detection/asa.yaml
Normal file
35
priv/profiles/os_detection/asa.yaml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
os: asa
|
||||
group: cisco
|
||||
text: 'Cisco ASA'
|
||||
ifname: true
|
||||
type: firewall
|
||||
icon: cisco
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
poller_modules:
|
||||
applications: false
|
||||
bgp-peers: false
|
||||
cipsec-tunnels: true
|
||||
cisco-ipsec-flow-monitor: true
|
||||
cisco-remote-access-monitor: true
|
||||
hr-mib: false
|
||||
ipSystemStats: false
|
||||
ipmi: false
|
||||
ntp: false
|
||||
ospf: false
|
||||
stp: false
|
||||
ucd-diskio: false
|
||||
ucd-mib: false
|
||||
discovery_modules:
|
||||
arp-table: false
|
||||
bgp-peers: false
|
||||
cisco-vrf-lite: false
|
||||
ntp: false
|
||||
stp: false
|
||||
ucd-diskio: false
|
||||
vlans: false
|
||||
discovery:
|
||||
-
|
||||
sysDescr_regex:
|
||||
- '/^Cisco (Adaptive|Industrial) Security Appliance/'
|
||||
29
priv/profiles/os_detection/ascom.yaml
Normal file
29
priv/profiles/os_detection/ascom.yaml
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
os: ascom
|
||||
text: 'Ascom'
|
||||
type: network
|
||||
icon: ascom
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'Processor Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
mib_dir: ascom
|
||||
poller_modules:
|
||||
storage: false
|
||||
applications: false
|
||||
bgp-peers: false
|
||||
ipmi: false
|
||||
ospf: false
|
||||
ucd-diskio: false
|
||||
ucd-mib: false
|
||||
discovery_modules:
|
||||
fdb-table: false
|
||||
storage: false
|
||||
bgp-peers: false
|
||||
ntp: false
|
||||
stp: false
|
||||
ucd-diskio: false
|
||||
vlans: false
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.27614
|
||||
15
priv/profiles/os_detection/asuswrt-merlin.yaml
Normal file
15
priv/profiles/os_detection/asuswrt-merlin.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
os: asuswrt-merlin
|
||||
text: 'AsusWRT Merlin'
|
||||
type: network
|
||||
icon: asuswrt-merlin
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID: .1.3.6.1.4.1.8072.3.2.10
|
||||
snmpget:
|
||||
oid: .1.3.6.1.4.1.2021.7890.1.101.1
|
||||
op: starts
|
||||
value: ASUSWRT-Merlin
|
||||
26
priv/profiles/os_detection/asyncos.yaml
Normal file
26
priv/profiles/os_detection/asyncos.yaml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
os: asyncos
|
||||
text: 'Cisco AsyncOS'
|
||||
type: proxy
|
||||
icon: cisco
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
- { graph: device_asyncos_conns, text: 'Current Connections' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.15497.1
|
||||
mib_dir: cisco
|
||||
poller_modules:
|
||||
ipmi: false
|
||||
bgp-peers: false
|
||||
ospf: false
|
||||
stp: false
|
||||
ntp: false
|
||||
discovery_modules:
|
||||
ports-stack: false
|
||||
cisco-vrf-lite: false
|
||||
bgp-peers: false
|
||||
stp: false
|
||||
ntp: false
|
||||
15
priv/profiles/os_detection/atenpdu.yaml
Executable file
15
priv/profiles/os_detection/atenpdu.yaml
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
os: atenpdu
|
||||
text: Aten PDU
|
||||
type: power
|
||||
icon: aten
|
||||
mib_dir: aten
|
||||
snmp_bulk: false
|
||||
over:
|
||||
- { graph: device_current, text: Current }
|
||||
- { graph: device_voltage, text: Voltage }
|
||||
- { graph: device_power, text: Power }
|
||||
discovery:
|
||||
- sysObjectID:
|
||||
- .1.3.6.1.4.1.21317.
|
||||
- sysDescr:
|
||||
- 'Aten'
|
||||
14
priv/profiles/os_detection/ats.yaml
Executable file
14
priv/profiles/os_detection/ats.yaml
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
os: ats
|
||||
text: Automatic Transfer Switch
|
||||
type: power
|
||||
icon: ats
|
||||
mib_dir: ats
|
||||
snmp_bulk: false
|
||||
over:
|
||||
- { graph: device_current, text: Current }
|
||||
- { graph: device_voltage, text: Voltage }
|
||||
- { graph: device_load, text: Load }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.37662.1.2.2.1
|
||||
12
priv/profiles/os_detection/audiocodes.yaml
Normal file
12
priv/profiles/os_detection/audiocodes.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
os: audiocodes
|
||||
group: audiocodes
|
||||
text: 'Audiocodes'
|
||||
type: network
|
||||
icon: audiocodes
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
mib_dir: audiocodes
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.5003.8.1.1
|
||||
9
priv/profiles/os_detection/avaya-ipo.yaml
Normal file
9
priv/profiles/os_detection/avaya-ipo.yaml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
os: avaya-ipo
|
||||
text: 'IP Office Firmware'
|
||||
type: network
|
||||
icon: avaya
|
||||
mib_dir: nortel
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.6889.
|
||||
41
priv/profiles/os_detection/aviat-wtm.yaml
Normal file
41
priv/profiles/os_detection/aviat-wtm.yaml
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
os: aviat-wtm
|
||||
text: 'Aviat WTM'
|
||||
type: wireless
|
||||
icon: aviat
|
||||
over:
|
||||
- { graph: device_bits, text: 'Traffic' }
|
||||
- { graph: device_wireless_rssi, text: 'RSSI' }
|
||||
- { graph: device_wireless_power, text: 'Power' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.2509.11.1.1.7
|
||||
- .1.3.6.1.4.1.2509.11.1.1.8
|
||||
- .1.3.6.1.4.1.2509.11.1.1.9
|
||||
- .1.3.6.1.4.1.2509.11.1.1.10
|
||||
- .1.3.6.1.4.1.2509.11.1.1.12
|
||||
- .1.3.6.1.4.1.2509.11.1.1.13
|
||||
- .1.3.6.1.4.1.2509.11.1.1.14
|
||||
- .1.3.6.1.4.1.2509.11.1.1.15
|
||||
discovery_modules:
|
||||
ucd-dsktable: false
|
||||
ucd-diskio: false
|
||||
arp-table: false
|
||||
fdb-table: false
|
||||
vlans: false
|
||||
stp: false
|
||||
processors: false
|
||||
mempools: false
|
||||
storage: false
|
||||
ipv4-addresses: false
|
||||
ipv6-addresses: false
|
||||
ports-stack: false
|
||||
hr-device: false
|
||||
bgp-peers: false
|
||||
poller_modules:
|
||||
netstats: false
|
||||
route: false
|
||||
hr-mib: false
|
||||
ucd-mib: false
|
||||
ospf: false
|
||||
ipSystemStats: false
|
||||
10
priv/profiles/os_detection/avocent-mp.yaml
Normal file
10
priv/profiles/os_detection/avocent-mp.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
os: avocent-mp
|
||||
text: Vertiv Avocent MergePoint Unity
|
||||
type: appliance
|
||||
icon: vertiv
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.10418.18.1
|
||||
13
priv/profiles/os_detection/avocent.yaml
Normal file
13
priv/profiles/os_detection/avocent.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
os: avocent
|
||||
text: Avocent
|
||||
type: management
|
||||
icon: avocent
|
||||
rfc1628_compat: true
|
||||
discovery:
|
||||
-
|
||||
sysDescr_regex:
|
||||
- '/^Avocent/'
|
||||
- '/^AlterPath/'
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.10418.16
|
||||
10
priv/profiles/os_detection/avr-hd.yaml
Normal file
10
priv/profiles/os_detection/avr-hd.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
os: avr-hd
|
||||
text: 'AvediaPlayer Receivers'
|
||||
type: appliance
|
||||
icon: exterity
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.28194.
|
||||
11
priv/profiles/os_detection/avtech.yaml
Normal file
11
priv/profiles/os_detection/avtech.yaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
os: avtech
|
||||
text: 'Avtech Environment Sensor'
|
||||
type: environment
|
||||
icon: avtech
|
||||
over:
|
||||
- { graph: device_temperature, text: Temperature }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.20916.1.
|
||||
- .1.3.6.1.4.1.20916 # RoomAlert 32S
|
||||
13
priv/profiles/os_detection/awplus.yaml
Normal file
13
priv/profiles/os_detection/awplus.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
os: awplus
|
||||
text: 'Alliedware Plus'
|
||||
type: network
|
||||
icon: alliedtelesis
|
||||
group: alliedtelesis
|
||||
ifname: true
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID: .1.3.6.1.4.1.207.
|
||||
sysDescr: ['AW+', 'Software (AlliedWare Plus)']
|
||||
mib_dir: awplus
|
||||
7
priv/profiles/os_detection/axisaudio.yaml
Normal file
7
priv/profiles/os_detection/axisaudio.yaml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
os: axisaudio
|
||||
text: 'AXIS Audio Appliances'
|
||||
type: appliance
|
||||
icon: axis
|
||||
discovery:
|
||||
-
|
||||
sysDescr_regex: '/AXIS .* (Network IO Audio Module|Network Speaker)/'
|
||||
12
priv/profiles/os_detection/axiscam.yaml
Normal file
12
priv/profiles/os_detection/axiscam.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
os: axiscam
|
||||
text: 'AXIS Network Camera'
|
||||
type: appliance
|
||||
icon: axis
|
||||
mib_dir: axis
|
||||
discovery:
|
||||
-
|
||||
sysDescr_regex: '/AXIS .* ((Network|Bullet|PTZ|Dome|Panoramic|Box) Camera|Security Radar|Video (Server|Decoder)|Network Video Encoder)/'
|
||||
over:
|
||||
- { graph: device_bits, text: Traffic }
|
||||
- { graph: device_temperature, text: 'Device Temperature' }
|
||||
- { graph: device_state, text: 'Device State' }
|
||||
7
priv/profiles/os_detection/axisdocserver.yaml
Normal file
7
priv/profiles/os_detection/axisdocserver.yaml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
os: axisdocserver
|
||||
text: 'AXIS Network Document Server'
|
||||
type: network
|
||||
icon: axis
|
||||
discovery:
|
||||
-
|
||||
sysDescr_regex: '/^AXIS .* Network Document Server/'
|
||||
13
priv/profiles/os_detection/axos.yaml
Normal file
13
priv/profiles/os_detection/axos.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
os: axos
|
||||
text: Calix AXOS
|
||||
type: network
|
||||
ifname: true
|
||||
empty_ifdescr: true
|
||||
mib_dir: calix
|
||||
icon: calix
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
discovery:
|
||||
-
|
||||
sysObjectID:
|
||||
- .1.3.6.1.4.1.6321.1.2.4
|
||||
12
priv/profiles/os_detection/baicells-od04.yaml
Normal file
12
priv/profiles/os_detection/baicells-od04.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
os: baicells-od04
|
||||
text: 'Baicells CPE'
|
||||
type: wireless
|
||||
icon: baicells
|
||||
mib_dir: baicells/cpe
|
||||
over:
|
||||
- { graph: device_bits, text: 'Device Traffic' }
|
||||
- { graph: device_processor, text: 'CPU Usage' }
|
||||
- { graph: device_mempool, text: 'Memory Usage' }
|
||||
discovery:
|
||||
- sysDescr:
|
||||
- 'BaiCPE'
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue