feat: use Exq for SNMP discovery background jobs

- Create DiscoveryWorker for handling SNMP device discovery
- Replace Task.start with Exq.enqueue for discovery operations
- Enqueue jobs to 'discovery' queue with device_id parameter
- Add enqueue_discovery/1 helper that falls back to Task.start in test
- Discovery jobs now persistent and retryable via Exq
- Triggered on: new device creation, device update, manual rediscover button
This commit is contained in:
Graham McIntire 2026-01-17 16:39:40 -06:00
parent 13f447f1b0
commit 073f9bf27d
No known key found for this signature in database
2 changed files with 63 additions and 7 deletions

View file

@ -0,0 +1,47 @@
defmodule Towerops.Workers.DiscoveryWorker do
@moduledoc """
Background worker for SNMP device discovery.
Enqueued when:
- A new device is created with SNMP enabled
- An existing device's SNMP configuration is changed
- User manually triggers discovery via "Rediscover Device" button
Queue: discovery
"""
alias Towerops.Devices
alias Towerops.Snmp
require Logger
@doc """
Performs SNMP discovery for a device.
## Parameters
- device_id: The UUID of the device to discover
## Returns
- :ok on success
- {:error, reason} on failure
"""
def perform(device_id) do
Logger.info("Starting SNMP discovery for device #{device_id}")
device = Devices.get_device!(device_id)
case Snmp.discover_device(device) do
{:ok, _snmp_device} ->
Logger.info("SNMP discovery completed successfully for device #{device_id}")
:ok
{:error, reason} ->
Logger.error("SNMP discovery failed for device #{device_id}: #{inspect(reason)}")
{:error, reason}
end
rescue
Ecto.NoResultsError ->
Logger.error("Device #{device_id} not found")
{:error, :device_not_found}
end
end

View file

@ -7,6 +7,7 @@ defmodule ToweropsWeb.DeviceLive.Form do
alias Towerops.Devices.Device, as: DeviceSchema
alias Towerops.Sites
alias Towerops.Snmp
alias Towerops.Workers.DiscoveryWorker
@impl true
def mount(params, _session, socket) do
@ -195,11 +196,8 @@ defmodule ToweropsWeb.DeviceLive.Form do
device = socket.assigns.device
if device.snmp_enabled do
# Run discovery in background
_ =
Task.start(fn ->
Snmp.discover_device(device)
end)
# Enqueue discovery job
enqueue_discovery(device.id)
{:noreply, put_flash(socket, :info, "Discovery started...")}
else
@ -277,7 +275,7 @@ defmodule ToweropsWeb.DeviceLive.Form do
defp handle_device_creation(device) do
if device.snmp_enabled do
_ = Task.start(fn -> Snmp.discover_device(device) end)
enqueue_discovery(device.id)
"Device created successfully. SNMP discovery started in background."
else
"Device created successfully"
@ -286,13 +284,24 @@ defmodule ToweropsWeb.DeviceLive.Form do
defp handle_device_update(old_device, device) do
if should_trigger_snmp_discovery?(old_device, device) do
_ = Task.start(fn -> Snmp.discover_device(device) end)
enqueue_discovery(device.id)
"Device updated successfully. SNMP discovery started in background."
else
"Device updated successfully"
end
end
# Enqueue discovery job - safe to call in test environment
defp enqueue_discovery(device_id) do
if Application.get_env(:towerops, :env) == :test do
# In test, run synchronously
Task.start(fn -> Snmp.discover_device(Devices.get_device!(device_id)) end)
else
# In dev/prod, enqueue to Exq
{:ok, _job} = Exq.enqueue(Exq, "discovery", DiscoveryWorker, [device_id])
end
end
defp should_trigger_snmp_discovery?(old_device, device) do
device.snmp_enabled and
(!old_device.snmp_enabled or