SnmpKit.SnmpSim (snmpkit v2.0.1)

Top-level API for the SNMP device simulator.

The simulator can be driven two ways:

Devices started from a configuration run under the application's device supervisor (SnmpSim.DeviceSupervisor), so they survive the caller and can be inspected with list_devices/0 and torn down with stop/0.

Example

config = %{
  snmp_sim: %{
    device_groups: [
      %{
        name: "cable_modems",
        device_type: "cable_modem",
        count: 10,
        port_range: %{start: 30_000, end: 30_009},
        community: "public",
        walk_file: "priv/walks/cable_modem.walk"
      }
    ]
  }
}

{:ok, _supervisor} = SnmpKit.SnmpSim.start(config)
10 = SnmpKit.SnmpSim.device_count()
{:ok, %{value: descr}} = SnmpKit.SNMP.get("127.0.0.1", "sysDescr.0", port: 30_000)
:ok = SnmpKit.SnmpSim.stop()

Summary

Functions

Number of devices running under the device supervisor.

Lists the devices running under the device supervisor.

Loads a configuration file. .json files are decoded with Jason; .yaml / .yml files require the optional yaml_elixir dependency.

A complete example configuration, useful as a starting point.

Starts every device group in config under the device supervisor.

Starts a population of devices with mixed types through SnmpKit.SnmpSim.LazyDevicePool.

Stops every device running under the device supervisor.

Stops one device, given its pid or its UDP port.

The device supervisor pid, or {:error, :not_started} if the application is not running.

Validates a configuration map without starting anything.

Types

config()

@type config() :: map() | Path.t()

device_info()

@type device_info() :: %{
  pid: pid(),
  device_id: binary() | nil,
  device_type: atom() | binary() | nil,
  port: non_neg_integer() | nil
}

Functions

device_count()

@spec device_count() :: non_neg_integer()

Number of devices running under the device supervisor.

list_devices()

@spec list_devices() :: [device_info()]

Lists the devices running under the device supervisor.

Each entry has :pid, :device_id, :device_type and :port; the last three are nil if a device did not answer its info call in time.

load_config(path)

@spec load_config(Path.t()) :: {:ok, map()} | {:error, term()}

Loads a configuration file. .json files are decoded with Jason; .yaml / .yml files require the optional yaml_elixir dependency.

sample_config()

A complete example configuration, useful as a starting point.

start(config)

@spec start(config()) :: {:ok, pid()} | {:error, term()}

Starts every device group in config under the device supervisor.

config is a configuration map (with or without the top-level :snmp_sim key) or the path of a JSON / YAML file. Returns {:ok, supervisor_pid}; the started devices are available through list_devices/0.

start_device(profile, opts \\ [])

@spec start_device(
  map(),
  keyword()
) :: GenServer.on_start()

Starts one device from a SnmpKit.SnmpSim.ProfileLoader profile.

Options: :port (required), :device_id (default "<type>_<port>"), :community (default "public"), :bind_address (default all IPv4 interfaces; an IPv6 address such as "::1" binds an IPv6 socket), :v3_users (a list of %{name:, auth:, auth_password:, priv:, priv_password:} maps that enables SNMPv3, see SnmpKit.SnmpSim.Core.UsmAgent), :engine_id, :seed (an integer that makes the device's error injection reproducible, see SnmpKit.SnmpSim.Device) and :mac_address. The device is linked to the caller.

{:ok, profile} = SnmpKit.SnmpSim.ProfileLoader.load_profile(:cable_modem, {:walk_file, "priv/walks/cable_modem.walk"})
{:ok, device} = SnmpKit.SnmpSim.start_device(profile, port: 9001)

A bare %{objects: %{oid => value}} map (OID keys as strings or lists) is accepted in place of a profile.

start_device_population(device_configs, opts \\ [])

Starts a population of devices with mixed types through SnmpKit.SnmpSim.LazyDevicePool.

Devices are pre-warmed unless pre_warm: false is given and run under the device supervisor (stop them with stop_device/1 or stop/0). Returns {:ok, [%{type, port, pid, target}]}. A list of explicit %{type:, port:, community:} maps is also accepted, one device each.

{:ok, devices} = SnmpKit.SnmpSim.start_device_population(
  [
    {:cable_modem, {:walk_file, "priv/walks/cable_modem.walk"}, count: 1000},
    {:switch, {:walk_file, "priv/walks/switch.walk"}, count: 50}
  ],
  port_range: 30_000..39_999
)

start_link(config)

@spec start_link(config()) :: {:ok, pid()} | {:error, term()}

Same as start/1.

The name is kept for compatibility with the original draft of this module. Note that the devices are supervised by the application, not linked to the caller, so this is safe to call from any process.

stop()

@spec stop() :: :ok

Stops every device running under the device supervisor.

stop_device(pid)

@spec stop_device(pid() | non_neg_integer()) :: :ok | {:error, :not_found}

Stops one device, given its pid or its UDP port.

supervisor()

@spec supervisor() :: {:ok, pid()} | {:error, :not_started}

The device supervisor pid, or {:error, :not_started} if the application is not running.

validate_config(config)

@spec validate_config(map()) :: :ok | {:error, term()}

Validates a configuration map without starting anything.

write_sample_config(path, format \\ :json)

Writes sample_config/0 to path as :json or :yaml.