diff --git a/.gitignore b/.gitignore index 51250184..9229a61e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ .nix-mix/ .nix-hex/ .nix-postgres/ +.nix-postgres-version .nix-redis/ .nix-services-started diff --git a/lib/towerops/application.ex b/lib/towerops/application.ex index f4fd069a..11e4bd00 100644 --- a/lib/towerops/application.ex +++ b/lib/towerops/application.ex @@ -41,46 +41,8 @@ defmodule Towerops.Application do Towerops.Release.migrate() end - # Configure MIB directories for NIF resolution (before supervisor starts) - Logger.info("Configuring MIB directories for NIF resolution...") - mib_dir = Application.app_dir(:towerops, "priv/mibs") - - # Build list of all MIB directories (including subdirectories for vendor MIBs) - mib_dirs = - if File.dir?(mib_dir) do - # Get main directory plus all subdirectories - subdirs = - mib_dir - |> File.ls!() - |> Enum.map(&Path.join(mib_dir, &1)) - |> Enum.filter(&File.dir?/1) - - [mib_dir | subdirs] - else - [mib_dir] - end - - # Set MIBDIRS environment variable for net-snmp (must be set BEFORE init) - # Join with colon for Unix-style path list - mibdirs_env = Enum.join(mib_dirs, ":") - System.put_env("MIBDIRS", mibdirs_env) - System.put_env("MIBS", "ALL") - - Logger.info("MIB directories configured: #{length(mib_dirs)} directories") - - # Add each directory to net-snmp's search path - Enum.each(mib_dirs, fn dir -> - :ok = ToweropsNative.load_mib_directory(dir) - end) - - # Initialize the MIB library (loads all MIBs into memory) - case ToweropsNative.init_mib_library() do - result when result in ["initialized", "already_initialized"] -> - Logger.info("MIB library initialized: #{result}") - - other -> - Logger.warning("Unexpected MIB library init result: #{inspect(other)}") - end + # Initialize MIB resolution via C NIF + init_mib_resolution() # Load antenna pattern registry (priv/antennas/*.ant) into :persistent_term Antenna.load_registry() @@ -152,6 +114,48 @@ defmodule Towerops.Application do result end + # Configure MIB resolution via C NIF. Handles missing NIF gracefully. + defp init_mib_resolution do + Logger.info("Configuring MIB directories for NIF resolution...") + mib_dir = Application.app_dir(:towerops, "priv/mibs") + + mib_dirs = + if File.dir?(mib_dir) do + subdirs = + mib_dir + |> File.ls!() + |> Enum.map(&Path.join(mib_dir, &1)) + |> Enum.filter(&File.dir?/1) + + [mib_dir | subdirs] + else + [mib_dir] + end + + mibdirs_env = Enum.join(mib_dirs, ":") + System.put_env("MIBDIRS", mibdirs_env) + System.put_env("MIBS", "ALL") + + Logger.info("MIB directories configured: #{length(mib_dirs)} directories") + + try do + Enum.each(mib_dirs, fn dir -> + :ok = ToweropsNative.load_mib_directory(dir) + end) + + case ToweropsNative.init_mib_library() do + result when result in ["initialized", "already_initialized"] -> + Logger.info("MIB library initialized: #{result}") + + other -> + Logger.warning("Unexpected MIB library init result: #{inspect(other)}") + end + rescue + _e in ErlangError -> + Logger.warning("ToweropsNative NIF not loaded — MIB resolution unavailable") + end + end + # Post-startup work runs only when the supervisor came up cleanly. The Task # supervisor call is wrapped in try/catch so a transient noproc (the named # supervisor may not be ready or may already be dying) cannot exit the boot diff --git a/mix.exs b/mix.exs index 5ccef46e..04f82660 100644 --- a/mix.exs +++ b/mix.exs @@ -10,7 +10,7 @@ defmodule Towerops.MixProject do start_permanent: Mix.env() == :prod, aliases: aliases(), deps: deps(), - compilers: [:phoenix_live_view] ++ Mix.compilers(), + compilers: [:towerops_nif, :phoenix_live_view] ++ Mix.compilers(), prune_code_paths: false, listeners: [Phoenix.CodeReloader], dialyzer: dialyzer(), diff --git a/nix/shell.nix b/nix/shell.nix index e4f99313..239e7d55 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -326,6 +326,12 @@ mkShell { mix local.hex --force --if-missing > /dev/null 2>&1 mix local.rebar --force --if-missing > /dev/null 2>&1 + # Compile C NIF if not already built (uses Nix-provided gcc/make/net-snmp) + if [ ! -f "priv/towerops_nif.so" ]; then + echo "🔧 Building C NIF..." + make -C c_src + fi + # Auto-start services on first shell entry. # Run in a detached subshell to prevent nix develop from waiting # on daemonized PostgreSQL/Redis child processes. diff --git a/test/test_helper.exs b/test/test_helper.exs index 9d0245ec..d996f3c6 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -18,22 +18,27 @@ if File.dir?(mib_dir) do System.put_env("MIBDIRS", mibdirs_env) System.put_env("MIBS", "ALL") - # Add each directory to net-snmp's search path - Enum.each(mib_dirs, fn dir -> - case ToweropsNative.load_mib_directory(dir) do - :ok -> :ok - # Ignore errors in test environment - {:error, _reason} -> :ok + # Add each directory to net-snmp's search path (NIF may not be loaded) + try do + Enum.each(mib_dirs, fn dir -> + case ToweropsNative.load_mib_directory(dir) do + :ok -> :ok + # Ignore errors in test environment + {:error, _reason} -> :ok + end + end) + + # Explicitly initialize the MIB library with correct environment variables + case ToweropsNative.init_mib_library() do + result when result in ["initialized", "already_initialized"] -> + IO.puts("Test MIB library initialized: #{result}") + + other -> + IO.puts("Warning: Unexpected MIB library init result: #{inspect(other)}") end - end) - - # Explicitly initialize the MIB library with correct environment variables - case ToweropsNative.init_mib_library() do - result when result in ["initialized", "already_initialized"] -> - IO.puts("Test MIB library initialized: #{result}") - - other -> - IO.puts("Warning: Unexpected MIB library init result: #{inspect(other)}") + rescue + _e in ErlangError -> + IO.puts("ToweropsNative NIF not available — MIB resolution disabled in tests") end end