SnmpKit.SnmpMgr.Engine (snmpkit v2.0.1)

The manager's shared UDP socket and response correlator.

The engine owns one UDP socket. Callers (SnmpKit.SnmpMgr.Multi, SnmpKit.SnmpMgr.V2Walk) register a request id, send their PDU themselves on the socket from get_socket/1, and receive {:snmp_response, id, data} or {:snmp_timeout, id} in their mailbox. Responses arrive straight in the engine's mailbox; there is no forwarding hop.

Datagrams from {:unspec, _} sources (ICMP errors surfaced by the socket) and empty datagrams are dropped and counted.

Options

  • :name - process name (default: SnmpKit.SnmpMgr.Engine)
  • :port - local UDP port (default 0, ephemeral)
  • :buffer_size - socket receive buffer in bytes (default 4 MiB)
  • :max_queue_depth - mailbox depth treated as 100% for health_check/1
  • :inet6_opts - extra :gen_udp.open/2 options for the IPv6 socket, which is opened on first use (default [])

Every call returns {:error, :services_not_started} when the engine process is not running, instead of exiting the caller with :noproc.

Summary

Types

Returned by every call when the engine process is not running.

Functions

Returns a specification to start this module under a supervisor.

Socket buffer statistics: largest datagram seen relative to the receive buffer, mailbox depth.

The local UDP port the shared socket is bound to.

The shared UDP socket. Send request PDUs on it after register_request/4.

Gets engine statistics: pending request count, correlation metrics, socket counters (:socket_stats), the local :port and :recv_queue_length.

Health based on how many datagrams are waiting in the engine's mailbox relative to :max_queue_depth. The :status is :healthy (< 50%), :warning (< 80%), :critical, or :error if the socket is gone; the rest of the map is the port, the queue depth and limit, and the uptime.

Gets the number of pending requests.

Draws the next id from SnmpKit.SnmpMgr.RequestIdGenerator and registers it, retrying with a fresh id while the drawn one is still pending.

Starts the Engine response correlator.

Gracefully shuts down the engine.

Unregisters a request (used when caller times out locally).

Types

health()

@type health() :: %{
  status: :healthy | :warning | :critical | :error,
  port: :inet.port_number(),
  queue_depth: non_neg_integer(),
  max_queue_depth: non_neg_integer(),
  uptime_ms: non_neg_integer()
}

Reply of health_check/1.

not_started()

@type not_started() :: {:error, :services_not_started}

Returned by every call when the engine process is not running.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

get_buffer_stats(engine \\ __MODULE__)

@spec get_buffer_stats(GenServer.server()) :: map() | not_started()

Socket buffer statistics: largest datagram seen relative to the receive buffer, mailbox depth.

get_port(engine \\ __MODULE__)

@spec get_port(GenServer.server()) :: :inet.port_number() | not_started()

The local UDP port the shared socket is bound to.

get_socket(engine \\ __MODULE__, family \\ :inet)

@spec get_socket(GenServer.server(), :inet | :inet6) ::
  :gen_udp.socket() | {:error, term()} | not_started()

The shared UDP socket. Send request PDUs on it after register_request/4.

The IPv6 socket is opened on first use; when :gen_udp.open/2 fails the reply is that {:error, reason}.

get_stats(engine \\ __MODULE__)

@spec get_stats(GenServer.server()) :: map() | not_started()

Gets engine statistics: pending request count, correlation metrics, socket counters (:socket_stats), the local :port and :recv_queue_length.

health_check(engine \\ __MODULE__)

@spec health_check(GenServer.server()) :: health() | not_started()

Health based on how many datagrams are waiting in the engine's mailbox relative to :max_queue_depth. The :status is :healthy (< 50%), :warning (< 80%), :critical, or :error if the socket is gone; the rest of the map is the port, the queue depth and limit, and the uptime.

pending_count(engine)

@spec pending_count(GenServer.server()) :: non_neg_integer() | not_started()

Gets the number of pending requests.

register_next_request(engine, caller_pid, timeout_ms, opts \\ [])

@spec register_next_request(GenServer.server(), pid(), non_neg_integer(), keyword()) ::
  {:ok, pos_integer()} | {:error, :request_id_space_exhausted} | not_started()

Draws the next id from SnmpKit.SnmpMgr.RequestIdGenerator and registers it, retrying with a fresh id while the drawn one is still pending.

Returns {:error, :services_not_started} when the generator or the engine is not running, and {:error, :request_id_space_exhausted} after 16 consecutive collisions.

register_request(engine, request_id, caller_pid, timeout_ms \\ SnmpKit.SnmpMgr.Config.get(:timeout), opts \\ [])

Registers a request for response correlation.

Refused with {:error, :request_id_in_use} while an earlier registration of the same id is still pending, so a wrapped-around id can never take another caller's response or its timeout. register_next_request/3 draws a fresh id and retries on a collision.

Parameters

  • engine - Engine PID or name
  • request_id - Unique request identifier
  • caller_pid - Process to send response to
  • timeout_ms - Timeout in milliseconds (default: the :timeout in SnmpKit.SnmpMgr.Config)

Examples

SnmpKit.SnmpMgr.Engine.register_request(engine, 12345, self(), 5000)

start_link(opts \\ [])

@spec start_link(keyword()) :: GenServer.on_start()

Starts the Engine response correlator.

Options

  • :name - Process name (default: MODULE)

stop(engine)

@spec stop(GenServer.server()) :: :ok

Gracefully shuts down the engine.

unregister_request(engine, request_id)

@spec unregister_request(GenServer.server(), integer()) :: :ok

Unregisters a request (used when caller times out locally).

Parameters

  • engine - Engine PID or name
  • request_id - Request identifier to unregister