diff --git a/lib/towerops/agents.ex b/lib/towerops/agents.ex index 8e2828e8..1c084010 100644 --- a/lib/towerops/agents.ex +++ b/lib/towerops/agents.ex @@ -710,11 +710,11 @@ defmodule Towerops.Agents do Returns the agent_token_id if found, nil otherwise. - The device must be preloaded with site: [organization: :default_agent_token]. + The device must be preloaded with `[:organization, site: [organization: :default_agent_token]]`. ## Examples - iex> device = Repo.preload(device, site: [organization: :default_agent_token]) + iex> device = Repo.preload(device, [:organization, site: [organization: :default_agent_token]]) iex> get_effective_agent_token(device) "agent-token-uuid" @@ -730,21 +730,27 @@ defmodule Towerops.Agents do agent_token_id _ -> - get_fallback_agent_token(device.site) + get_fallback_agent_token(device) end end # Checks site, organization, and global default in sequence - defp get_fallback_agent_token(site) do - case site do - %{agent_token_id: agent_token_id} when not is_nil(agent_token_id) -> - agent_token_id + defp get_fallback_agent_token(device) do + cond do + # 2. Site-level agent + match?(%{agent_token_id: id} when not is_nil(id), device.site) -> + device.site.agent_token_id - %{organization: %{default_agent_token_id: agent_token_id}} - when not is_nil(agent_token_id) -> - agent_token_id + # 3. Organization default via site + match?(%{organization: %{default_agent_token_id: id}} when not is_nil(id), device.site) -> + device.site.organization.default_agent_token_id - _ -> + # 3b. Organization default directly (device has no site) + match?(%{default_agent_token_id: id} when not is_nil(id), device.organization) -> + device.organization.default_agent_token_id + + # 4. Global default + true -> Towerops.Settings.get_global_default_cloud_poller() end end @@ -777,21 +783,27 @@ defmodule Towerops.Agents do {agent_token_id, :device} _ -> - get_fallback_agent_token_with_source(device.site) + get_fallback_agent_token_with_source(device) end end # Checks site, organization, and global default in sequence with source tracking - defp get_fallback_agent_token_with_source(site) do - case site do - %{agent_token_id: agent_token_id} when not is_nil(agent_token_id) -> - {agent_token_id, :site} + defp get_fallback_agent_token_with_source(device) do + cond do + # 2. Site-level agent + match?(%{agent_token_id: id} when not is_nil(id), device.site) -> + {device.site.agent_token_id, :site} - %{organization: %{default_agent_token_id: agent_token_id}} - when not is_nil(agent_token_id) -> - {agent_token_id, :organization} + # 3. Organization default via site + match?(%{organization: %{default_agent_token_id: id}} when not is_nil(id), device.site) -> + {device.site.organization.default_agent_token_id, :organization} - _ -> + # 3b. Organization default directly (device has no site) + match?(%{default_agent_token_id: id} when not is_nil(id), device.organization) -> + {device.organization.default_agent_token_id, :organization} + + # 4. Global default + true -> case Towerops.Settings.get_global_default_cloud_poller() do agent_token_id when not is_nil(agent_token_id) -> {agent_token_id, :global} diff --git a/lib/towerops/agents/stats.ex b/lib/towerops/agents/stats.ex index 1a4f9874..2d423173 100644 --- a/lib/towerops/agents/stats.ex +++ b/lib/towerops/agents/stats.ex @@ -378,7 +378,7 @@ defmodule Towerops.Agents.Stats do device = Device |> Repo.get!(device_id) - |> Repo.preload(site: [organization: :default_agent_token]) + |> Repo.preload([:organization, site: [organization: :default_agent_token]]) {current_agent_id, source} = Towerops.Agents.get_effective_agent_token_with_source(device) @@ -453,7 +453,7 @@ defmodule Towerops.Agents.Stats do device = Device |> Repo.get!(device_id) - |> Repo.preload(site: [organization: :default_agent_token]) + |> Repo.preload([:organization, site: [organization: :default_agent_token]]) {current_agent_id, source} = Towerops.Agents.get_effective_agent_token_with_source(device) diff --git a/lib/towerops/devices.ex b/lib/towerops/devices.ex index d4fc9cf1..dc6cd88d 100644 --- a/lib/towerops/devices.ex +++ b/lib/towerops/devices.ex @@ -241,7 +241,7 @@ defmodule Towerops.Devices do nil device -> - Repo.preload(device, [:site, snmp_device: [:interfaces, :sensors]]) + Repo.preload(device, [:site, :organization, snmp_device: [:interfaces, :sensors]]) end end @@ -653,7 +653,7 @@ defmodule Towerops.Devices do defp validate_cloud_poller_requires_ssl(changeset, device) do # Get effective agent to check if it's a cloud poller - device_with_site = Repo.preload(device, site: :organization) + device_with_site = Repo.preload(device, [:organization, site: :organization]) {effective_agent_id, _source} = Agents.get_effective_agent_token_with_source(device_with_site) using_cloud_poller = diff --git a/lib/towerops/organizations.ex b/lib/towerops/organizations.ex index 22df4477..af68e21c 100644 --- a/lib/towerops/organizations.ex +++ b/lib/towerops/organizations.ex @@ -415,11 +415,9 @@ defmodule Towerops.Organizations do organization = Repo.get!(Organization, organization_id) if organization.default_agent_token_id do - # device IDs for this organization + # device IDs for this organization (includes devices with and without sites) device_ids = - Repo.all( - from(e in Device, join: s in assoc(e, :site), where: s.organization_id == ^organization_id, select: e.id) - ) + Repo.all(from(e in Device, where: e.organization_id == ^organization_id, select: e.id)) # Delete existing assignments Repo.delete_all(from(a in AgentAssignment, where: a.device_id in ^device_ids)) @@ -442,9 +440,7 @@ defmodule Towerops.Organizations do else # No default agent set, delete all assignments device_ids = - Repo.all( - from(e in Device, join: s in assoc(e, :site), where: s.organization_id == ^organization_id, select: e.id) - ) + Repo.all(from(e in Device, where: e.organization_id == ^organization_id, select: e.id)) Repo.delete_all(from(a in AgentAssignment, where: a.device_id in ^device_ids)) end diff --git a/lib/towerops_web/live/device_live/form.ex b/lib/towerops_web/live/device_live/form.ex index 9be7ad88..060833d8 100644 --- a/lib/towerops_web/live/device_live/form.ex +++ b/lib/towerops_web/live/device_live/form.ex @@ -101,7 +101,7 @@ defmodule ToweropsWeb.DeviceLive.Form do case AccessControl.verify_device_access(id, organization.id) do {:ok, device} -> # Preload necessary associations for agent resolution - device = Repo.preload(device, site: [organization: :default_agent_token]) + device = Repo.preload(device, [:organization, site: [organization: :default_agent_token]]) apply_edit_action(socket, device) {:error, :not_found} -> diff --git a/lib/towerops_web/live/device_live/show.ex b/lib/towerops_web/live/device_live/show.ex index 3ed9c5a8..225e21e5 100644 --- a/lib/towerops_web/live/device_live/show.ex +++ b/lib/towerops_web/live/device_live/show.ex @@ -222,7 +222,7 @@ defmodule ToweropsWeb.DeviceLive.Show do defp load_equipment_data(socket, device_id) do device = Devices.get_device!(device_id) - device_with_associations = Repo.preload(device, site: [organization: :default_agent_token]) + device_with_associations = Repo.preload(device, [:organization, site: [organization: :default_agent_token]]) {agent_token_id, agent_source} = Agents.get_effective_agent_token_with_source(device_with_associations) agent_info = load_agent_info(agent_token_id, agent_source) recent_checks = Monitoring.list_devices_checks(device_id, 50)