From 592b99d739f05c66c99e305b4607a68e7159ed18 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Sun, 18 Jan 2026 13:00:31 -0600 Subject: [PATCH] fix: use correct device_id for neighbor discovery The neighbor discovery was using snmp_device.id instead of the actual Equipment device.id when creating neighbor records. This caused all neighbor inserts to fail because the foreign key constraint expected the Equipment ID, not the SNMP Device ID. Fixed in two places: - Discovery: Use snmp_device.device_id (FK to Equipment) - PollerWorker: Use device.id (Equipment ID) instead of snmp_device.id Neighbors should now be properly discovered and saved during both initial discovery and ongoing polling. --- .elixir-tools/next-ls.log | 2 + .expert/expert.log | 286 +++++++++++++++++++++++++++++ lib/towerops/snmp/discovery.ex | 11 +- lib/towerops/snmp/poller_worker.ex | 2 +- 4 files changed, 295 insertions(+), 6 deletions(-) create mode 100644 .elixir-tools/next-ls.log create mode 100644 .expert/expert.log diff --git a/.elixir-tools/next-ls.log b/.elixir-tools/next-ls.log new file mode 100644 index 00000000..4fa691d5 --- /dev/null +++ b/.elixir-tools/next-ls.log @@ -0,0 +1,2 @@ + +13:00:24.245 [info] Loading 161 CA(s) from :otp store diff --git a/.expert/expert.log b/.expert/expert.log new file mode 100644 index 00000000..ca1034c4 --- /dev/null +++ b/.expert/expert.log @@ -0,0 +1,286 @@ +13:00:30.826 [debug] Child {XPLoggerFileBackend, :general_log} of Supervisor Logger.Backends.Supervisor started +Pid: #PID<0.102.0> +Start Call: Logger.Backends.Watcher.start_link({{XPLoggerFileBackend, :general_log}, {XPLoggerFileBackend, :general_log}}) +Restart: :transient +Shutdown: 5000 +Type: :worker +13:00:30.826 [info] Child Logger.Backends.Supervisor of Supervisor Logger.Backends.Internal started +Pid: #PID<0.101.0> +Start Call: Logger.Backends.Supervisor.start_link([{XPLoggerFileBackend, :general_log}]) +Restart: :permanent +Shutdown: :infinity +Type: :supervisor +13:00:30.826 [info] Child Logger.Backends.Internal of Supervisor Logger.Supervisor started +Pid: #PID<0.97.0> +Start Call: Logger.Backends.Internal.start_link([]) +Restart: :permanent +Shutdown: :infinity +Type: :supervisor +13:00:30.826 [info] Application logger started at :nonode@nohost +13:00:30.826 [info] Application xp_mime started at :nonode@nohost +13:00:30.826 [info] Application xp_hpax started at :nonode@nohost +13:00:30.826 [info] Application crypto started at :nonode@nohost +13:00:30.826 [info] Application asn1 started at :nonode@nohost +13:00:30.826 [info] Application public_key started at :nonode@nohost +13:00:30.827 [debug] Child :ssl_handler of Supervisor :logger_sup started +Pid: #PID<0.107.0> +Start Call: :logger_olp.start_link(:logger_std_h_ssl_handler, :logger_h_common, %{id: :ssl_handler, module: :logger_std_h, config: %{type: :standard_io, filesync_repeat_interval: :no_repeat}, level: :debug, filter_default: :stop, filters: [], formatter: {:ssl_logger, %{}}}, %{}) +Restart: :temporary +Shutdown: 2000 +Type: :worker +13:00:30.829 [info] Child :ssl_pem_cache of Supervisor :ssl_admin_sup started +Pid: #PID<0.111.0> +Start Call: :ssl_pem_cache.start_link([]) +Restart: :permanent +Shutdown: 4000 +Type: :worker +13:00:30.831 [info] Child :ssl_manager of Supervisor :ssl_admin_sup started +Pid: #PID<0.112.0> +Start Call: :ssl_manager.start_link([]) +Restart: :permanent +Shutdown: 4000 +Type: :worker +13:00:30.832 [info] Child :tls_client_ticket_store of Supervisor :ssl_admin_sup started +Pid: #PID<0.113.0> +Start Call: :tls_client_ticket_store.start_link(1000, 7200) +Restart: :permanent +Shutdown: 4000 +Type: :worker +13:00:30.832 [info] Child :ssl_admin_sup of Supervisor :ssl_sup started +Pid: #PID<0.110.0> +Start Call: :ssl_admin_sup.start_link() +Restart: :permanent +Shutdown: 4000 +Type: :supervisor +13:00:30.833 [info] Child :tls_connection_sup of Supervisor :tls_sup started +Pid: #PID<0.116.0> +Start Call: :tls_connection_sup.start_link() +Restart: :permanent +Shutdown: 4000 +Type: :supervisor +13:00:30.834 [info] Child :ssl_listen_tracker_sup of Supervisor :tls_server_sup started +Pid: #PID<0.118.0> +Start Call: :ssl_listen_tracker_sup.start_link() +Restart: :permanent +Shutdown: 4000 +Type: :supervisor +13:00:30.835 [info] Child :tls_server_session_ticket of Supervisor :tls_server_sup started +Pid: #PID<0.119.0> +Start Call: :tls_server_session_ticket_sup.start_link() +Restart: :permanent +Shutdown: 4000 +Type: :supervisor +13:00:30.835 [info] Child :ssl_server_session_cache_sup of Supervisor :tls_server_sup started +Pid: #PID<0.120.0> +Start Call: :ssl_server_session_cache_sup.start_link() +Restart: :permanent +Shutdown: 4000 +Type: :supervisor +13:00:30.836 [info] Child :ssl_upgrade_server_session_cache_sup of Supervisor :tls_server_sup started +Pid: #PID<0.121.0> +Start Call: :ssl_upgrade_server_session_cache_sup.start_link() +Restart: :permanent +Shutdown: 4000 +Type: :supervisor +13:00:30.836 [info] Child :tls_server_sup of Supervisor :tls_sup started +Pid: #PID<0.117.0> +Start Call: :tls_server_sup.start_link() +Restart: :permanent +Shutdown: 4000 +Type: :supervisor +13:00:30.836 [info] Child :tls_sup of Supervisor :ssl_connection_sup started +Pid: #PID<0.115.0> +Start Call: :tls_sup.start_link() +Restart: :permanent +Shutdown: 4000 +Type: :supervisor +13:00:30.837 [info] Child :dtls_connection_sup of Supervisor :dtls_sup started +Pid: #PID<0.123.0> +Start Call: :dtls_connection_sup.start_link() +Restart: :permanent +Shutdown: 4000 +Type: :supervisor +13:00:30.837 [info] Child :dtls_listener_sup of Supervisor :dtls_server_sup started +Pid: #PID<0.125.0> +Start Call: :dtls_listener_sup.start_link() +Restart: :permanent +Shutdown: 4000 +Type: :supervisor +13:00:30.838 [info] Child :dtls_server_session_cache_sup of Supervisor :dtls_server_sup started +Pid: #PID<0.126.0> +Start Call: :dtls_server_session_cache_sup.start_link() +Restart: :permanent +Shutdown: 4000 +Type: :supervisor +13:00:30.838 [info] Child :dtls_server_sup of Supervisor :dtls_sup started +Pid: #PID<0.124.0> +Start Call: :dtls_server_sup.start_link() +Restart: :permanent +Shutdown: 4000 +Type: :supervisor +13:00:30.838 [info] Child :dtls_sup of Supervisor :ssl_connection_sup started +Pid: #PID<0.122.0> +Start Call: :dtls_sup.start_link() +Restart: :permanent +Shutdown: 4000 +Type: :supervisor +13:00:30.838 [info] Child :ssl_connection_sup of Supervisor :ssl_sup started +Pid: #PID<0.114.0> +Start Call: :ssl_connection_sup.start_link() +Restart: :permanent +Shutdown: 4000 +Type: :supervisor +13:00:30.838 [info] Application ssl started at :nonode@nohost +13:00:30.839 [info] Application xp_mint started at :nonode@nohost +13:00:30.839 [info] Application xp_nimble_options started at :nonode@nohost +13:00:30.841 [info] Child XPNimblePool.TaskSupervisor of Supervisor #PID<0.132.0> (Supervisor.Default) started +Pid: #PID<0.133.0> +Start Call: Task.Supervisor.start_link([name: XPNimblePool.TaskSupervisor]) +Restart: :permanent +Shutdown: :infinity +Type: :supervisor +13:00:30.841 [info] Application xp_nimble_pool started at :nonode@nohost +13:00:30.843 [info] Child :telemetry_handler_table of Supervisor :telemetry_sup started +Pid: #PID<0.137.0> +Start Call: :telemetry_handler_table.start_link() +Restart: :permanent +Shutdown: 5000 +Type: :worker +13:00:30.843 [info] Application xp_telemetry started at :nonode@nohost +13:00:30.843 [info] Application xp_finch started at :nonode@nohost +13:00:30.858 [info] Child XPReq.Finch.PIDPartition0 of Supervisor XPReq.Finch started +Pid: #PID<0.143.0> +Start Call: Registry.Partition.start_link(XPReq.Finch.PIDPartition0, {:duplicate, XPReq.Finch, 0, 1, XPReq.Finch.KeyPartition0, XPReq.Finch.PIDPartition0, [], false}) +Restart: :permanent +Shutdown: 5000 +Type: :worker +13:00:30.858 [info] Child XPReq.Finch of Supervisor XPReq.Finch.Supervisor started +Pid: #PID<0.142.0> +Start Call: Registry.start_link([keys: :duplicate, name: XPReq.Finch, meta: [config: %{pools: %{}, registry_name: XPReq.Finch, supervisor_name: XPReq.Finch.PoolSupervisor, manager_name: XPReq.Finch.PoolManager, default_pool_config: %{count: 1, size: 50, mod: XPFinch.HTTP1.Pool, conn_opts: [protocols: [:http1], transport_opts: [keepalive: true, nodelay: true, timeout: 5000], ssl_key_log_file_device: nil], pool_max_idle_time: :infinity, conn_max_idle_time: :infinity, start_pool_metrics?: false}}]]) +Restart: :permanent +Shutdown: :infinity +Type: :supervisor +13:00:30.858 [info] Child XPReq.Finch.PoolSupervisor of Supervisor XPReq.Finch.Supervisor started +Pid: #PID<0.144.0> +Start Call: DynamicSupervisor.start_link([name: XPReq.Finch.PoolSupervisor, strategy: :one_for_one]) +Restart: :permanent +Shutdown: :infinity +Type: :supervisor +13:00:30.859 [info] Child XPFinch.PoolManager of Supervisor XPReq.Finch.Supervisor started +Pid: #PID<0.145.0> +Start Call: XPFinch.PoolManager.start_link(%{pools: %{}, registry_name: XPReq.Finch, supervisor_name: XPReq.Finch.PoolSupervisor, manager_name: XPReq.Finch.PoolManager, default_pool_config: %{count: 1, size: 50, mod: XPFinch.HTTP1.Pool, conn_opts: [protocols: [:http1], transport_opts: [keepalive: true, nodelay: true, timeout: 5000], ssl_key_log_file_device: nil], pool_max_idle_time: :infinity, conn_max_idle_time: :infinity, start_pool_metrics?: false}}) +Restart: :permanent +Shutdown: 5000 +Type: :worker +13:00:30.859 [info] Child XPReq.Finch of Supervisor #PID<0.140.0> (Supervisor.Default) started +Pid: #PID<0.141.0> +Start Call: XPFinch.start_link([name: XPReq.Finch, pools: %{default: [protocols: [:http1]]}]) +Restart: :permanent +Shutdown: 5000 +Type: :worker +13:00:30.859 [info] Child XPReq.FinchSupervisor of Supervisor #PID<0.140.0> (Supervisor.Default) started +Pid: #PID<0.146.0> +Start Call: DynamicSupervisor.start_link([strategy: :one_for_one, name: XPReq.FinchSupervisor]) +Restart: :permanent +Shutdown: :infinity +Type: :supervisor +13:00:30.859 [info] Child XPReq.Test.Ownership of Supervisor #PID<0.140.0> (Supervisor.Default) started +Pid: #PID<0.147.0> +Start Call: XPReq.Test.Ownership.start_link([name: XPReq.Test.Ownership]) +Restart: :permanent +Shutdown: 5000 +Type: :worker +13:00:30.859 [info] Application xp_req started at :nonode@nohost +13:00:30.859 [info] Application burrito started at :nonode@nohost +13:00:30.861 [info] Child XPBriefly.Entry of Supervisor XPBriefly.Supervisor started +Pid: #PID<0.151.0> +Start Call: XPBriefly.Entry.start_link([]) +Restart: :permanent +Shutdown: 5000 +Type: :worker +13:00:30.861 [info] Application xp_briefly started at :nonode@nohost +13:00:30.861 [info] Application xp_schematic started at :nonode@nohost +13:00:30.861 [info] Application xp_typed_struct started at :nonode@nohost +13:00:30.861 [info] Application xp_gen_lsp started at :nonode@nohost +13:00:30.866 [info] Child XPSnowflake.Generator of Supervisor #PID<0.154.0> (Supervisor.Default) started +Pid: #PID<0.155.0> +Start Call: XPSnowflake.Generator.start_link(0, 1023) +Restart: :permanent +Shutdown: 5000 +Type: :worker +13:00:30.866 [info] Application xp_snowflake started at :nonode@nohost +13:00:30.866 [info] Application xp_sourceror started at :nonode@nohost +13:00:30.866 [info] Application xp_forge started at :nonode@nohost +13:00:30.866 [info] Application xp_logger_file_backend started at :nonode@nohost +13:00:30.870 [info] Child :ttb_autostart of Supervisor :runtime_tools_sup started +Pid: #PID<0.159.0> +Start Call: :ttb_autostart.start_link() +Restart: :temporary +Shutdown: 3000 +Type: :worker +13:00:30.870 [info] Application runtime_tools started at :nonode@nohost +13:00:30.870 [info] Application et started at :nonode@nohost +13:00:30.870 [info] Application observer started at :nonode@nohost +13:00:30.870 [info] Application xp_path_glob started at :nonode@nohost +13:00:30.883 [info] Starting NodePortMapper on nonode@nohost +13:00:30.883 [info] Child XPForge.NodePortMapper of Supervisor XPExpert.Supervisor started +Pid: #PID<0.163.0> +Start Call: XPForge.NodePortMapper.start_link([]) +Restart: :permanent +Shutdown: 5000 +Type: :worker +13:00:30.884 [info] Child XPForge.Document.Store of Supervisor XPExpert.Supervisor started +Pid: #PID<0.164.0> +Start Call: XPForge.Document.Store.start_link([derive: [analysis: &XPForge.Ast.analyze/1]]) +Restart: :permanent +Shutdown: 5000 +Type: :worker +13:00:30.885 [info] Child XPExpert.ProjectSupervisor of Supervisor XPExpert.Supervisor started +Pid: #PID<0.165.0> +Start Call: DynamicSupervisor.start_link([name: XPExpert.ProjectSupervisor, strategy: :one_for_one]) +Restart: :permanent +Shutdown: :infinity +Type: :supervisor +13:00:30.885 [info] Child XPExpert.DynamicSupervisor of Supervisor XPExpert.Supervisor started +Pid: #PID<0.166.0> +Start Call: DynamicSupervisor.start_link([name: XPExpert.DynamicSupervisor]) +Restart: :permanent +Shutdown: :infinity +Type: :supervisor +13:00:30.886 [info] Child XPGenLSP.Assigns of Supervisor XPExpert.Supervisor started +Pid: #PID<0.167.0> +Start Call: XPGenLSP.Assigns.start_link([name: XPExpert.Assigns]) +Restart: :permanent +Shutdown: 5000 +Type: :worker +13:00:30.886 [info] Child :expert_task_queue of Supervisor XPExpert.Supervisor started +Pid: #PID<0.168.0> +Start Call: Task.Supervisor.start_link([name: :expert_task_queue]) +Restart: :permanent +Shutdown: :infinity +Type: :supervisor +13:00:30.889 [info] Child XPGenLSP.Buffer of Supervisor XPExpert.Supervisor started +Pid: #PID<0.169.0> +Start Call: XPGenLSP.Buffer.start_link([name: XPExpert.Buffer]) +Restart: :permanent +Shutdown: 5000 +Type: :worker +13:00:30.889 [info] Child XPExpert.ActiveProjects of Supervisor XPExpert.Supervisor started +Pid: #PID<0.170.0> +Start Call: XPExpert.ActiveProjects.start_link() +Restart: :permanent +Shutdown: 5000 +Type: :worker +13:00:30.890 [debug] [name: XPExpert, buffer: XPExpert.Buffer, task_supervisor: :expert_task_queue, dynamic_supervisor: XPExpert.DynamicSupervisor, assigns: XPExpert.Assigns] +13:00:30.894 [info] Child XPExpert of Supervisor XPExpert.Supervisor started +Pid: #PID<0.171.0> +Start Call: XPExpert.start_link([name: XPExpert, buffer: XPExpert.Buffer, task_supervisor: :expert_task_queue, dynamic_supervisor: XPExpert.DynamicSupervisor, assigns: XPExpert.Assigns]) +Restart: :permanent +Shutdown: 500 +Type: :worker +13:00:30.895 [info] Application xp_expert started at :nonode@nohost +13:00:30.921 [debug] handled notification client -> server $/cancelRequest in 18ms +13:00:30.942 [debug] sent notification server -> client window/logMessage +13:00:30.945 [error] Received shutdown before server was initialized +13:00:30.946 [error] Failed to handle Elixir.XPGenLSP.Requests.Shutdown, {{:error, :not_initialized}, %XPExpert.State{initialized?: false, shutdown_received?: false, in_flight_requests: %{}}} +13:00:30.947 [debug] handled request client -> server shutdown in 44ms diff --git a/lib/towerops/snmp/discovery.ex b/lib/towerops/snmp/discovery.ex index 652554a1..ed88d673 100644 --- a/lib/towerops/snmp/discovery.ex +++ b/lib/towerops/snmp/discovery.ex @@ -327,15 +327,16 @@ defmodule Towerops.Snmp.Discovery do defp save_discovery_results(device, device_info, interfaces, sensors) do Repo.transaction(fn -> # Upsert Device - device = upsert_device(device, device_info) + snmp_device = upsert_device(device, device_info) # Delete old interfaces/sensors and insert new ones - _ = delete_old_data(device) - new_interfaces = insert_interfaces(device, interfaces) - _ = insert_sensors(device, sensors) + _ = delete_old_data(snmp_device) + new_interfaces = insert_interfaces(snmp_device, interfaces) + _ = insert_sensors(snmp_device, sensors) # Return device with interfaces loaded and device_id added - %{device | interfaces: Enum.map(new_interfaces, &Map.put(&1, :device_id, device.id))} + # Use snmp_device.device_id which references the Equipment table + %{snmp_device | interfaces: Enum.map(new_interfaces, &Map.put(&1, :device_id, snmp_device.device_id))} end) end diff --git a/lib/towerops/snmp/poller_worker.ex b/lib/towerops/snmp/poller_worker.ex index af01b6c0..128ad320 100644 --- a/lib/towerops/snmp/poller_worker.ex +++ b/lib/towerops/snmp/poller_worker.ex @@ -198,7 +198,7 @@ defmodule Towerops.Snmp.PollerWorker do defp poll_device_neighbors(device, snmp_device, client_opts) do # Add device_id to interfaces for neighbor discovery - interfaces_with_device = Enum.map(snmp_device.interfaces, &Map.put(&1, :device_id, snmp_device.id)) + interfaces_with_device = Enum.map(snmp_device.interfaces, &Map.put(&1, :device_id, device.id)) {:ok, neighbors} = NeighborDiscovery.discover_neighbors(client_opts, interfaces_with_device)