SnmpKit.SnmpLib.Config (snmpkit v0.6.3)

Configuration management system for production SNMP deployments.

This module provides a flexible, environment-aware configuration system designed for real-world SNMP management applications. Based on patterns proven in large-scale deployments like the DDumb project managing 1000+ devices.

Features

  • Environment-Aware: Automatic detection of dev/test/prod environments
  • Layered Configuration: Application, environment, and runtime overrides
  • Dynamic Updates: Hot-reload configuration without service restart
  • Validation: Schema validation for all configuration values
  • Secrets Management: Secure handling of sensitive configuration data
  • Multi-Tenant Support: Per-deployment configuration isolation

Configuration Sources (in order of precedence)

  1. Runtime environment variables
  2. Configuration files (config/*.exs)
  3. Application defaults
  4. Module defaults

Usage Patterns

# Get configuration for a specific component
pool_config = SnmpKit.SnmpLib.Config.get(:pool, :default_settings)

# Get configuration with fallback
timeout = SnmpKit.SnmpLib.Config.get(:snmp, :timeout, 5000)

# Update configuration at runtime
SnmpKit.SnmpLib.Config.put(:pool, :max_size, 50)

# Load configuration from file
SnmpKit.SnmpLib.Config.load_from_file("/etc/snmp_lib/production.exs")

# Validate current configuration
{:ok, _} = SnmpKit.SnmpLib.Config.validate()

Environment Detection

The configuration system automatically detects the current environment:

  • :dev - Development with verbose logging and relaxed timeouts
  • :test - Testing with simulated backends and fast timeouts
  • :prod - Production with optimized settings and monitoring
  • :staging - Pre-production environment for integration testing

Configuration Schema

All configuration follows a validated schema to prevent runtime errors:

%{
  snmp: %{
    default_version: :v2c,
    default_timeout: 5_000,
    default_retries: 3,
    default_community: "public"
  },
  pool: %{
    default_size: 10,
    max_overflow: 5,
    strategy: :fifo,
    health_check_interval: 30_000
  },
  monitoring: %{
    metrics_enabled: true,
    prometheus_port: 9090,
    dashboard_enabled: true,
    alert_thresholds: %{...}
  }
}

Summary

Functions

Gets all configuration as a nested map.

Returns a specification to start this module under a supervisor.

Gets the current environment.

Gets a configuration value by key path with optional default.

Loads configuration from a file and merges with current config.

Merges user-provided options with default SNMP configuration values.

Sets a configuration value at runtime.

Reloads configuration from environment and files.

Starts the configuration manager with initial configuration.

Validates the current configuration against the schema.

Registers a callback function to be called when configuration changes.

Types

config_key()

@type config_key() :: atom() | [atom()]

config_section()

@type config_section() :: atom()

config_value()

@type config_value() :: any()

environment()

@type environment() :: :dev | :test | :prod | :staging

Functions

all()

@spec all() :: map()

Gets all configuration as a nested map.

Examples

config = SnmpKit.SnmpLib.Config.all()
IO.inspect(config.snmp.default_timeout)

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

environment()

@spec environment() :: environment()

Gets the current environment.

Examples

env = SnmpKit.SnmpLib.Config.environment()  # :prod

get(section, key, default \\ nil)

Gets a configuration value by key path with optional default.

Parameters

  • section: Configuration section (:snmp, :pool, :monitoring, etc.)
  • key: Specific configuration key or nested key path
  • default: Value to return if key is not found

Examples

# Get a simple value
timeout = SnmpKit.SnmpLib.Config.get(:snmp, :default_timeout)

# Get nested value
threshold = SnmpKit.SnmpLib.Config.get(:monitoring, [:alert_thresholds, :error_rate])

# Get with default
community = SnmpKit.SnmpLib.Config.get(:snmp, :community, "public")

load_from_file(file_path)

@spec load_from_file(binary()) :: :ok | {:error, any()}

Loads configuration from a file and merges with current config.

Examples

:ok = SnmpKit.SnmpLib.Config.load_from_file("/etc/snmp_lib/production.exs")

merge_opts(opts)

@spec merge_opts(keyword()) :: keyword()

Merges user-provided options with default SNMP configuration values.

This function provides the default SNMP configuration options that are commonly used across SNMP operations, then merges them with user-provided options. User options take precedence over defaults.

Default Values

  • community: "public"
  • timeout: 5000 (milliseconds)
  • retries: 3
  • port: 161
  • version: :v2c
  • mib_paths: []

Parameters

  • opts: Keyword list of user-provided options that override defaults

Returns

Keyword list with defaults merged with user options, where user options take precedence.

Examples

iex> SnmpKit.SnmpLib.Config.merge_opts([])
[community: "public", timeout: 5000, retries: 3, port: 161, version: :v2c, mib_paths: []]

iex> result = SnmpKit.SnmpLib.Config.merge_opts([timeout: 10000])
iex> result[:community]
"public"
iex> result[:timeout]
10000
iex> result[:retries]
3

iex> result = SnmpKit.SnmpLib.Config.merge_opts([community: "private", port: 162])
iex> result[:community]
"private"
iex> result[:port]
162
iex> result[:timeout]
5000

put(section, key, value)

@spec put(config_section(), config_key(), config_value()) :: :ok | {:error, any()}

Sets a configuration value at runtime.

Changes are applied immediately and optionally persisted.

Examples

:ok = SnmpKit.SnmpLib.Config.put(:pool, :default_size, 20)
:ok = SnmpKit.SnmpLib.Config.put(:monitoring, [:alert_thresholds, :error_rate], 0.10)

reload()

@spec reload() :: :ok | {:error, any()}

Reloads configuration from environment and files.

Examples

:ok = SnmpKit.SnmpLib.Config.reload()

start_link(opts \\ [])

@spec start_link(keyword()) :: {:ok, pid()} | {:error, any()}

Starts the configuration manager with initial configuration.

Options

  • config_file: Path to configuration file to load on startup
  • environment: Override automatic environment detection
  • validate_on_start: Validate configuration on startup (default: true)

Examples

{:ok, _pid} = SnmpKit.SnmpLib.Config.start_link()
{:ok, _pid} = SnmpKit.SnmpLib.Config.start_link(config_file: "/etc/snmp_lib/prod.exs")

validate()

@spec validate() :: {:ok, map()} | {:error, [any()]}

Validates the current configuration against the schema.

Returns

  • {:ok, config}: Configuration is valid
  • {:error, errors}: List of validation errors

Examples

case SnmpKit.SnmpLib.Config.validate() do
  {:ok, _config} -> Logger.info("Configuration is valid")
  {:error, validation_errors} -> Logger.error("Configuration errors: " <> inspect(validation_errors))
end

watch(section, callback)

@spec watch(config_section(), function()) :: :ok

Registers a callback function to be called when configuration changes.

Examples

SnmpKit.SnmpLib.Config.watch(:pool, fn old_config, new_config ->
  Logger.info("Pool configuration changed")
  SnmpKit.SnmpLib.Pool.reload_config(new_config)
end)