324 lines
9.1 KiB
Elixir
324 lines
9.1 KiB
Elixir
defmodule SnmpKit.SnmpMgr do
|
|
@moduledoc """
|
|
Lightweight SNMP client library for Elixir.
|
|
|
|
This library provides a simple, stateless interface for SNMP operations
|
|
without requiring heavyweight management processes or configurations.
|
|
|
|
## Core Operations
|
|
|
|
- `get/3` - Single OID retrieval
|
|
- `get_next/3` - Get next OID in tree
|
|
- `get_bulk/3` - Bulk retrieval (SNMPv2c)
|
|
- `walk/3` - Walk subtree
|
|
- `set/4` - Set OID value
|
|
"""
|
|
|
|
alias SnmpKit.SnmpLib.OID
|
|
alias SnmpKit.SnmpMgr.Bulk
|
|
alias SnmpKit.SnmpMgr.Config
|
|
alias SnmpKit.SnmpMgr.Core
|
|
alias SnmpKit.SnmpMgr.Format
|
|
alias SnmpKit.SnmpMgr.MIB
|
|
alias SnmpKit.SnmpMgr.Walk
|
|
|
|
@type target :: binary() | tuple() | map()
|
|
@type oid :: binary() | list()
|
|
@type opts :: keyword()
|
|
|
|
@doc """
|
|
Performs an SNMP GET request.
|
|
|
|
## Parameters
|
|
- `target` - The target device (e.g., "192.168.1.1:161" or "device.local")
|
|
- `oid` - The OID to retrieve (string or list format)
|
|
- `opts` - Options including :community, :timeout, :retries
|
|
|
|
## Examples
|
|
|
|
{:ok, value} = SnmpMgr.get("device.local:161", "sysDescr.0", community: "public")
|
|
"""
|
|
def get(target, oid, opts \\ []) do
|
|
merged_opts = Config.merge_opts(opts)
|
|
|
|
case Core.send_get_request_with_type(target, oid, merged_opts) do
|
|
{:ok, {oid_str, type, value}} ->
|
|
{:ok, Format.enrich_varbind({oid_str, type, value}, merged_opts)}
|
|
|
|
{:error, reason} ->
|
|
{:error, reason}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Performs an SNMP GETNEXT request.
|
|
|
|
## Parameters
|
|
- `target` - The target device
|
|
- `oid` - The starting OID
|
|
- `opts` - Options including :community, :timeout, :retries
|
|
"""
|
|
def get_next(target, oid, opts \\ []) do
|
|
merged_opts = Config.merge_opts(opts)
|
|
|
|
case Core.send_get_next_request(target, oid, merged_opts) do
|
|
{:ok, {oid_string, type, value}} ->
|
|
{:ok, Format.enrich_varbind({oid_string, type, value}, merged_opts)}
|
|
|
|
{:error, reason} ->
|
|
{:error, reason}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Performs an SNMP SET request.
|
|
|
|
## Parameters
|
|
- `target` - The target device
|
|
- `oid` - The OID to set
|
|
- `value` - The value to set
|
|
- `opts` - Options including :community, :timeout, :retries
|
|
"""
|
|
def set(target, oid, value, opts \\ []) do
|
|
merged_opts = Config.merge_opts(opts)
|
|
Core.send_set_request(target, oid, value, merged_opts)
|
|
end
|
|
|
|
@doc """
|
|
Performs an asynchronous SNMP GET request.
|
|
|
|
Returns immediately with a reference. The caller will receive a message
|
|
with the result.
|
|
"""
|
|
def get_async(target, oid, opts \\ []) do
|
|
merged_opts = Config.merge_opts(opts)
|
|
Core.send_get_request_async(target, oid, merged_opts)
|
|
end
|
|
|
|
@doc """
|
|
Performs an SNMP GETBULK request (SNMPv2c only).
|
|
|
|
GETBULK is more efficient than multiple GETNEXT requests for retrieving
|
|
large amounts of data.
|
|
|
|
## Parameters
|
|
- `target` - The target device
|
|
- `oid` - The starting OID
|
|
- `opts` - Options including :non_repeaters, :max_repetitions, :community, :timeout
|
|
"""
|
|
@spec get_bulk(target(), oid(), opts()) :: {:ok, [{list(), atom(), any()}]} | {:error, any()}
|
|
def get_bulk(target, oid, opts \\ []) do
|
|
case Keyword.get(opts, :version) do
|
|
:v1 ->
|
|
{:error, {:unsupported_operation, :get_bulk_requires_v2c}}
|
|
|
|
:v3 ->
|
|
{:error, {:unsupported_operation, :get_bulk_requires_v2c}}
|
|
|
|
_ ->
|
|
merged_opts =
|
|
opts
|
|
|> Keyword.put(:version, :v2c)
|
|
|> then(&Config.merge_opts/1)
|
|
|
|
case Core.send_get_bulk_request(target, oid, merged_opts) do
|
|
{:ok, results} -> {:ok, Format.enrich_varbinds(results, merged_opts)}
|
|
{:error, reason} -> {:error, reason}
|
|
end
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Performs an asynchronous SNMP GETBULK request.
|
|
|
|
Returns immediately with a reference. The caller will receive a message
|
|
with the result.
|
|
"""
|
|
def get_bulk_async(target, oid, opts \\ []) do
|
|
case Keyword.get(opts, :version) do
|
|
:v1 ->
|
|
{:error, {:unsupported_operation, :get_bulk_requires_v2c}}
|
|
|
|
:v3 ->
|
|
{:error, {:unsupported_operation, :get_bulk_requires_v2c}}
|
|
|
|
_ ->
|
|
merged_opts =
|
|
opts
|
|
|> Keyword.put(:version, :v2c)
|
|
|> then(&Config.merge_opts/1)
|
|
|
|
Core.send_get_bulk_request_async(target, oid, merged_opts)
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Performs an SNMP walk operation using iterative GETNEXT requests.
|
|
|
|
Walks the SNMP tree starting from the given OID and returns all OID/value
|
|
pairs found under that subtree.
|
|
|
|
## Parameters
|
|
- `target` - The target device
|
|
- `root_oid` - The starting OID for the walk
|
|
- `opts` - Options including :community, :timeout, :max_repetitions
|
|
"""
|
|
@spec walk(target(), oid(), opts()) :: {:ok, [{list(), atom(), any()}]} | {:error, any()}
|
|
def walk(target, root_oid, opts \\ []) do
|
|
merged_opts = Config.merge_opts(opts)
|
|
|
|
case Walk.walk(target, root_oid, merged_opts) do
|
|
{:ok, results} -> {:ok, Format.enrich_varbinds(results, merged_opts)}
|
|
{:error, reason} -> {:error, reason}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Walks an SNMP table and returns all entries.
|
|
|
|
## Parameters
|
|
- `target` - The target device
|
|
- `table_oid` - The table OID to walk
|
|
- `opts` - Options including :community, :timeout
|
|
"""
|
|
@spec walk_table(target(), oid(), opts()) :: {:ok, [{list(), atom(), any()}]} | {:error, any()}
|
|
def walk_table(target, table_oid, opts \\ []) do
|
|
merged_opts = Config.merge_opts(opts)
|
|
|
|
case Walk.walk_table(target, table_oid, merged_opts) do
|
|
{:ok, results} -> {:ok, Format.enrich_varbinds(results, merged_opts)}
|
|
{:error, reason} -> {:error, reason}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Gets a specific column from an SNMP table.
|
|
|
|
## Parameters
|
|
- `target` - The target device
|
|
- `table_oid` - The table OID
|
|
- `column` - The column number or name
|
|
- `opts` - Options including :community, :timeout
|
|
"""
|
|
def get_column(target, table_oid, column, opts \\ []) do
|
|
with {:ok, resolved_table_oid} <- resolve_oid_if_needed(table_oid),
|
|
{:ok, column_oid} <- build_column_oid(resolved_table_oid, column) do
|
|
walk(target, column_oid, opts)
|
|
end
|
|
end
|
|
|
|
defp build_column_oid(table_oid, column) when is_integer(column) do
|
|
{:ok, table_oid ++ [1, column]}
|
|
end
|
|
|
|
defp build_column_oid(_table_oid, column) do
|
|
case MIB.resolve(column) do
|
|
{:ok, oid} -> {:ok, oid}
|
|
error -> error
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Performs an SNMP GET operation and returns a formatted value.
|
|
"""
|
|
@spec get_pretty(target(), oid(), opts()) :: {:ok, String.t()} | {:error, any()}
|
|
def get_pretty(target, oid, opts \\ []) do
|
|
merged_opts =
|
|
opts
|
|
|> Keyword.put(:include_formatted, true)
|
|
|> Config.merge_opts()
|
|
|
|
case get(target, oid, merged_opts) do
|
|
{:ok, enriched} -> {:ok, enriched}
|
|
{:error, reason} -> {:error, reason}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Performs an SNMP WALK operation and returns formatted results.
|
|
"""
|
|
@spec walk_pretty(target(), oid(), opts()) ::
|
|
{:ok, [{String.t(), String.t()}]} | {:error, any()}
|
|
def walk_pretty(target, oid, opts \\ []) do
|
|
merged_opts =
|
|
opts
|
|
|> Keyword.put(:include_formatted, true)
|
|
|> Config.merge_opts()
|
|
|
|
case Walk.walk(target, oid, merged_opts) do
|
|
{:ok, results} -> {:ok, Format.enrich_varbinds(results, merged_opts)}
|
|
{:error, reason} -> {:error, reason}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Performs an SNMP BULK operation and returns formatted results.
|
|
"""
|
|
@spec bulk_pretty(target(), oid(), opts()) ::
|
|
{:ok, [{String.t(), String.t()}]} | {:error, any()}
|
|
def bulk_pretty(target, oid, opts \\ []) do
|
|
merged_opts =
|
|
opts
|
|
|> Keyword.put(:include_formatted, true)
|
|
|> Config.merge_opts()
|
|
|
|
case Bulk.get_bulk(target, oid, merged_opts) do
|
|
{:ok, results} -> {:ok, Format.enrich_varbinds(results, merged_opts)}
|
|
{:error, reason} -> {:error, reason}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Performs an SNMP BULK WALK operation.
|
|
|
|
Uses iterative GETBULK requests to efficiently walk the subtree.
|
|
"""
|
|
@spec bulk_walk(target(), oid(), opts()) ::
|
|
{:ok, [{String.t(), atom(), any()}]} | {:error, any()}
|
|
def bulk_walk(target, oid, opts \\ []) do
|
|
merged_opts =
|
|
opts
|
|
|> Keyword.put(:version, :v2c)
|
|
|> Config.merge_opts()
|
|
|
|
case Bulk.walk_bulk(target, oid, merged_opts) do
|
|
{:ok, results} ->
|
|
{:ok, results}
|
|
|
|
{:error, reason} ->
|
|
{:error, reason}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Performs an SNMP BULK WALK operation and returns formatted results.
|
|
"""
|
|
@spec bulk_walk_pretty(target(), oid(), opts()) ::
|
|
{:ok, [{String.t(), atom(), String.t()}]} | {:error, any()}
|
|
def bulk_walk_pretty(target, oid, opts \\ []) do
|
|
merged_opts =
|
|
opts
|
|
|> Keyword.put(:include_formatted, true)
|
|
|> Config.merge_opts()
|
|
|
|
case bulk_walk(target, oid, merged_opts) do
|
|
{:ok, results} -> {:ok, results}
|
|
{:error, reason} -> {:error, reason}
|
|
end
|
|
end
|
|
|
|
# Private helper function
|
|
defp resolve_oid_if_needed(oid) when is_binary(oid) do
|
|
case OID.string_to_list(oid) do
|
|
{:ok, oid_list} ->
|
|
{:ok, oid_list}
|
|
|
|
{:error, _} ->
|
|
# Try resolving as symbolic name
|
|
MIB.resolve(oid)
|
|
end
|
|
end
|
|
|
|
defp resolve_oid_if_needed(oid) when is_list(oid), do: {:ok, oid}
|
|
defp resolve_oid_if_needed(_), do: {:error, :invalid_oid_format}
|
|
end
|