From 3f3df5938d9f204c69c2c98abdd0d139271e4b09 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Tue, 27 Jan 2026 14:19:37 -0600 Subject: [PATCH] agent debug improvements --- lib/towerops/snmp/agent_discovery.ex | 65 +++++++++++++++++-------- lib/towerops/snmp/deferred_discovery.ex | 21 +++++++- lib/towerops/snmp/discovery.ex | 17 +++++-- 3 files changed, 79 insertions(+), 24 deletions(-) diff --git a/lib/towerops/snmp/agent_discovery.ex b/lib/towerops/snmp/agent_discovery.ex index 6f51da59..6879f669 100644 --- a/lib/towerops/snmp/agent_discovery.ex +++ b/lib/towerops/snmp/agent_discovery.ex @@ -59,33 +59,47 @@ defmodule Towerops.Snmp.AgentDiscovery do @spec process_agent_discovery(DeviceSchema.t(), map()) :: {:ok, Discovery.Device.t()} | {:error, term()} def process_agent_discovery(device, oid_values) when is_map(oid_values) do + oid_count = map_size(oid_values) + Logger.info("Processing agent discovery for #{device.name} (#{device.ip_address})", device_id: device.id, - oid_count: map_size(oid_values) + oid_count: oid_count ) - # Build connection opts with Replay adapter - client_opts = build_replay_opts(device, oid_values) + # Check for empty OID map - this indicates agent couldn't collect any SNMP data + if oid_count == 0 do + Logger.error( + "Agent collected 0 OIDs for #{device.name} (#{device.ip_address})", + device_id: device.id, + troubleshooting: + "Agent couldn't collect SNMP data. Check: 1) Device reachable from agent network (ping #{device.ip_address}), 2) SNMP enabled on device, 3) Firewall allows UDP port #{device.snmp_port || 161}, 4) Community string '#{obscure_community(device)}' correct, 5) SNMP version matches device configuration" + ) - # Run discovery using standard pipeline - # This will call all the same functions as direct discovery, - # but read from the OID map instead of making SNMP queries - case Discovery.discover_device_with_opts(device, client_opts) do - {:ok, discovered_device} -> - Logger.info("Agent discovery succeeded for #{device.name}", - device_id: device.id, - interfaces: length(discovered_device.interfaces || []), - sensors: count_sensors(discovered_device) - ) + {:error, :agent_collected_no_data} + else + # Build connection opts with Replay adapter + client_opts = build_replay_opts(device, oid_values) - {:ok, discovered_device} + # Run discovery using standard pipeline + # This will call all the same functions as direct discovery, + # but read from the OID map instead of making SNMP queries + case Discovery.discover_device_with_opts(device, client_opts) do + {:ok, discovered_device} -> + Logger.info("Agent discovery succeeded for #{device.name}", + device_id: device.id, + interfaces: length(discovered_device.interfaces || []), + sensors: count_sensors(discovered_device) + ) - {:error, reason} = error -> - Logger.error("Agent discovery failed for #{device.name}: #{inspect(reason)}", - device_id: device.id - ) + {:ok, discovered_device} - error + {:error, reason} = error -> + Logger.error("Agent discovery failed for #{device.name}: #{inspect(reason)}", + device_id: device.id + ) + + error + end end end @@ -116,4 +130,17 @@ defmodule Towerops.Snmp.AgentDiscovery do 0 end end + + # Obscure community string for logging (show first 3 chars + ***) + @spec obscure_community(DeviceSchema.t()) :: String.t() + defp obscure_community(device) do + snmp_config = Devices.get_snmp_config(device) + community = snmp_config.community || "unknown" + + if String.length(community) > 3 do + String.slice(community, 0..2) <> "***" + else + "***" + end + end end diff --git a/lib/towerops/snmp/deferred_discovery.ex b/lib/towerops/snmp/deferred_discovery.ex index 0b9c0506..fa8e2d78 100644 --- a/lib/towerops/snmp/deferred_discovery.ex +++ b/lib/towerops/snmp/deferred_discovery.ex @@ -173,7 +173,26 @@ defmodule Towerops.Snmp.DeferredDiscovery do if elapsed < fast_threshold, do: :fast, else: :slow - {:error, _} -> + {:error, reason} -> + # Log detailed diagnostics to help troubleshoot connectivity issues + ip = Keyword.get(client_opts, :ip, "unknown") + community = Keyword.get(client_opts, :community, "unknown") + version = Keyword.get(client_opts, :version, "unknown") + port = Keyword.get(client_opts, :port, 161) + adapter = Keyword.get(client_opts, :adapter, "Real SNMP") + + Logger.warning( + "Device #{ip} categorized as unresponsive - SNMP GET failed", + ip: ip, + port: port, + version: version, + community: String.slice(community, 0..3) <> "***", + adapter: inspect(adapter), + error: inspect(reason), + troubleshooting: + "Check: 1) Device reachable (ping #{ip}), 2) SNMP enabled on device, 3) Firewall allows UDP #{port}, 4) Community string correct, 5) SNMP version matches" + ) + :unresponsive end end diff --git a/lib/towerops/snmp/discovery.ex b/lib/towerops/snmp/discovery.ex index f03592f0..c1ae7f37 100644 --- a/lib/towerops/snmp/discovery.ex +++ b/lib/towerops/snmp/discovery.ex @@ -143,10 +143,19 @@ defmodule Towerops.Snmp.Discovery do if device.snmp_enabled do Logger.info("Starting discovery with custom opts for: #{device.name}") - # Categorize device speed for adaptive timeouts - # Note: For Replay adapter, this will be fast since no network calls - device_speed = DeferredDiscovery.categorize_device_speed(client_opts) - timeouts = DeferredDiscovery.timeouts_for_speed(device_speed) + # Skip speed categorization for Replay adapter (agent-based discovery) + # Agent data is pre-collected, so network checks are not needed + adapter = Keyword.get(client_opts, :adapter) + + {device_speed, timeouts} = + if adapter == Towerops.Snmp.Adapters.Replay do + Logger.debug("Using Replay adapter, skipping speed categorization") + {:fast, DeferredDiscovery.timeouts_for_speed(:fast)} + else + # Categorize device speed for adaptive timeouts (real SNMP) + speed = DeferredDiscovery.categorize_device_speed(client_opts) + {speed, DeferredDiscovery.timeouts_for_speed(speed)} + end if device_speed == :unresponsive do Logger.warning("Device #{device.name} is unresponsive, aborting discovery")