Changes to eliminate @dialyzer suppressions by fixing underlying causes:
NIF stubs (towerops_native.ex, mib_translator.ex):
- Change stubs to :erlang.nif_error(:nif_not_loaded) (no_return type).
Real NIF replaces stubs at load time; calls to unloaded stubs now fail
loudly instead of returning fake data. Lets dialyzer trust @spec.
- Remove @dialyzer :nowarn_function on three NIFs and on translate/1.
Discovery sync_* functions (snmp/discovery.ex, channels/agent_channel.ex):
- agent_channel passes %{device_id: _, interfaces: _} and %{id: _} maps
into Discovery.sync_ip_addresses/sync_processors/sync_storage, which
@spec'd only %Device{}. Add narrow map-type unions (ip_sync_device,
snmp_device_ref) reflecting what the functions actually access.
- Remove @dialyzer :nowarn_function on three agent_channel helpers.
remote_ip.ex — real bug caught and fixed:
- `:ranch.get_addr(socket.transport_pid)` was always raising since
Bandit uses ThousandIsland, not Ranch; the rescue _ -> nil silently
returned nil every time. Switched to Phoenix's documented
:peer_data connect_info (already enabled in endpoint.ex) via
socket.assigns; remote IP now actually works.
- Remove remote_ip.ex entry from .dialyzer_ignore.exs.
Accounts / Organizations (Ecto.Multi opacity):
- Add @specs to Multi-building helpers, refactor into pipe chains.
- 6 @dialyzer :nowarn_function → 0, but 7 :no_opaque remain. Root
cause is upstream: Ecto.Multi.new/0 returns a struct with a literal
%MapSet{} whose @opaque internal representation trips dialyzer on
every subsequent Multi.* call. Unfixable without an Ecto patch or
bypassing Multi entirely. Comments document the specific upstream
issue rather than a vague "Ecto.Multi opacity" claim.
Devices.ex:
- Adding @specs made it worse (call_without_opaque → contract_with_
opaque); inlining the Multi didn't help either — same MapSet root
cause. Suppression kept with a sharper comment.
101 lines
2.7 KiB
Elixir
101 lines
2.7 KiB
Elixir
defmodule ToweropsNative do
|
|
@moduledoc """
|
|
NIF wrapper for SNMP MIB resolution using pure C + libnetsnmp.
|
|
|
|
This module provides fast MIB name resolution by directly calling
|
|
the industry-standard net-snmp library through a pure C NIF (no Rust layer).
|
|
|
|
## Examples
|
|
|
|
iex> ToweropsNative.resolve_oid("sysDescr")
|
|
"1.3.6.1.2.1.1.1"
|
|
|
|
iex> ToweropsNative.resolve_oid("IF-MIB::ifDescr")
|
|
"1.3.6.1.2.1.2.2.1.2"
|
|
|
|
## Performance
|
|
|
|
- MIB loading: ~1-2 seconds at startup (one-time cost)
|
|
- Resolution: ~1-20µs per OID (in-memory lookups)
|
|
- Throughput: ~50,000-100,000 resolutions/second per core
|
|
"""
|
|
|
|
@on_load :load_nif
|
|
|
|
def load_nif do
|
|
nif_path = :filename.join(:code.priv_dir(:towerops), ~c"towerops_nif")
|
|
|
|
case :erlang.load_nif(nif_path, 0) do
|
|
:ok ->
|
|
:ok
|
|
|
|
{:error, _reason} ->
|
|
require Logger
|
|
|
|
Logger.warning("ToweropsNative NIF not available, using stubs")
|
|
:ok
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Resolve MIB name to numeric OID string.
|
|
|
|
Accepts both simple names ("sysDescr") and module-qualified names
|
|
("SNMPv2-MIB::sysDescr").
|
|
|
|
Returns the numeric OID string on success or `{:error, reason}` if the
|
|
name cannot be resolved.
|
|
|
|
## Examples
|
|
|
|
iex> ToweropsNative.resolve_oid("sysDescr")
|
|
"1.3.6.1.2.1.1.1"
|
|
|
|
iex> ToweropsNative.resolve_oid("SNMPv2-MIB::sysDescr")
|
|
"1.3.6.1.2.1.1.1"
|
|
|
|
iex> ToweropsNative.resolve_oid("invalidMibName")
|
|
{:error, "Failed to resolve MIB name"}
|
|
"""
|
|
@spec resolve_oid(String.t()) :: String.t() | {:error, String.t()}
|
|
def resolve_oid(_mib_name), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
|
@doc """
|
|
Add a MIB directory to the search path.
|
|
|
|
This function should be called once at application startup to configure
|
|
where net-snmp should look for MIB files.
|
|
|
|
Returns `:ok` on success.
|
|
|
|
## Examples
|
|
|
|
iex> ToweropsNative.load_mib_directory("/path/to/mibs")
|
|
:ok
|
|
"""
|
|
@spec load_mib_directory(Path.t()) :: :ok
|
|
def load_mib_directory(_path), do: :erlang.nif_error(:nif_not_loaded)
|
|
|
|
@doc """
|
|
Initialize the net-snmp library and load all MIBs into memory.
|
|
|
|
This function is called automatically on the first call to `resolve_oid/1`,
|
|
so you typically don't need to call it explicitly. However, you can call it
|
|
during application startup to warm up the MIB cache.
|
|
|
|
The initialization is performed on a dirty CPU scheduler to avoid blocking
|
|
the main BEAM schedulers.
|
|
|
|
Returns `"initialized"` or `"already_initialized"`.
|
|
|
|
## Examples
|
|
|
|
iex> ToweropsNative.init_mib_library()
|
|
"initialized"
|
|
|
|
iex> ToweropsNative.init_mib_library()
|
|
"already_initialized"
|
|
"""
|
|
@spec init_mib_library() :: String.t()
|
|
def init_mib_library, do: :erlang.nif_error(:nif_not_loaded)
|
|
end
|