diff --git a/lib/towerops/snmp.ex b/lib/towerops/snmp.ex index 89747471..ad7798d8 100644 --- a/lib/towerops/snmp.ex +++ b/lib/towerops/snmp.ex @@ -1858,6 +1858,26 @@ defmodule Towerops.Snmp do end) end + # Helper to format changeset errors for logging + defp format_changeset_errors(changeset) do + Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end) + end + + # Helper to collect error samples (first N errors for debugging) + defp maybe_add_error_sample(error_count, samples, attrs, changeset, max_samples \\ 3) do + if error_count < max_samples do + error_details = %{ + ip: Map.get(attrs, :ip_address), + mac: Map.get(attrs, :mac_address), + errors: format_changeset_errors(changeset) + } + + [error_details | samples] + else + samples + end + end + @doc """ Bulk upserts ARP entries for a device. Returns `{success_count, error_count}` tuple. @@ -1866,7 +1886,7 @@ defmodule Towerops.Snmp do interface_map = Map.new(interfaces, fn i -> {i.if_index, i.id} end) arp_entries - |> Enum.reduce({0, 0}, fn entry, {s, e} -> + |> Enum.reduce({0, 0, []}, fn entry, {s, e, error_samples} -> interface_id = Map.get(interface_map, entry.if_index) attrs = @@ -1875,18 +1895,25 @@ defmodule Towerops.Snmp do |> Map.put(:interface_id, interface_id) case upsert_arp_entry(attrs) do - {:ok, _} -> {s + 1, e} - {:error, _} -> {s, e + 1} + {:ok, _} -> + {s + 1, e, error_samples} + + {:error, changeset} -> + # Sample first 3 errors for debugging + sample = maybe_add_error_sample(e, error_samples, attrs, changeset) + {s, e + 1, sample} end end) - |> tap(fn {_s, errors} -> + |> tap(fn {_s, errors, error_samples} -> if errors > 0 do Logger.warning("#{errors} ARP upsert failures for device #{device_id}", device_id: device_id, - error_count: errors + error_count: errors, + sample_errors: Enum.reverse(error_samples) ) end end) + |> then(fn {s, e, _samples} -> {s, e} end) end # MAC Address queries