Group cloud pollers and org agents separately on admin agents page
Cloud pollers render in their own table at the top, org agents below with the organization name shown under each agent name.
This commit is contained in:
parent
6a66203e48
commit
c6caa81850
2 changed files with 152 additions and 70 deletions
|
|
@ -14,13 +14,17 @@ defmodule ToweropsWeb.Admin.AgentLive.Index do
|
|||
end
|
||||
|
||||
agent_tokens = Agents.list_all_agent_tokens()
|
||||
{cloud_pollers, org_agents} = Enum.split_with(agent_tokens, & &1.is_cloud_poller)
|
||||
device_counts = calculate_device_counts(agent_tokens)
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(:page_title, "All Agents")
|
||||
|> assign(:timezone, socket.assigns.current_scope.timezone)
|
||||
|> stream(:agent_tokens, agent_tokens)
|
||||
|> stream(:cloud_pollers, cloud_pollers)
|
||||
|> stream(:org_agents, org_agents)
|
||||
|> assign(:has_cloud_pollers, cloud_pollers != [])
|
||||
|> assign(:has_org_agents, org_agents != [])
|
||||
|> assign(:device_counts, device_counts)
|
||||
|> assign(:now, DateTime.utc_now())}
|
||||
end
|
||||
|
|
@ -56,8 +60,10 @@ defmodule ToweropsWeb.Admin.AgentLive.Index do
|
|||
direct = Agents.count_assigned_devices(agent_token_id)
|
||||
total = length(Agents.list_agent_polling_targets(agent_token_id))
|
||||
|
||||
stream_name = if agent_token.is_cloud_poller, do: :cloud_pollers, else: :org_agents
|
||||
|
||||
socket
|
||||
|> stream_insert(:agent_tokens, agent_token)
|
||||
|> stream_insert(stream_name, agent_token)
|
||||
|> assign(:device_counts, Map.put(socket.assigns.device_counts, agent_token_id, %{direct: direct, total: total}))
|
||||
rescue
|
||||
Ecto.NoResultsError -> socket
|
||||
|
|
@ -65,10 +71,14 @@ defmodule ToweropsWeb.Admin.AgentLive.Index do
|
|||
|
||||
defp reload_all(socket) do
|
||||
agent_tokens = Agents.list_all_agent_tokens()
|
||||
{cloud_pollers, org_agents} = Enum.split_with(agent_tokens, & &1.is_cloud_poller)
|
||||
device_counts = calculate_device_counts(agent_tokens)
|
||||
|
||||
socket
|
||||
|> stream(:agent_tokens, agent_tokens, reset: true)
|
||||
|> stream(:cloud_pollers, cloud_pollers, reset: true)
|
||||
|> stream(:org_agents, org_agents, reset: true)
|
||||
|> assign(:has_cloud_pollers, cloud_pollers != [])
|
||||
|> assign(:has_org_agents, org_agents != [])
|
||||
|> assign(:device_counts, device_counts)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -7,77 +7,149 @@
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<.table id="admin-agents-table" rows={@streams.agent_tokens}>
|
||||
<:col :let={{_id, agent}} label="Name">
|
||||
<div class="flex items-center gap-2">
|
||||
<.link
|
||||
navigate={~p"/agents/#{agent.id}"}
|
||||
class="font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
|
||||
>
|
||||
{agent.name}
|
||||
</.link>
|
||||
<%= if agent.is_cloud_poller do %>
|
||||
<span class="inline-flex items-center gap-1 rounded-full bg-purple-50 dark:bg-purple-900/30 px-2 py-0.5 text-xs font-medium text-purple-700 dark:text-purple-300 ring-1 ring-inset ring-purple-600/20 dark:ring-purple-400/30">
|
||||
<.icon name="hero-cloud" class="h-3 w-3" /> Cloud
|
||||
<%!-- Cloud Pollers --%>
|
||||
<%= if @has_cloud_pollers do %>
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white mb-3">
|
||||
<.icon name="hero-cloud" class="w-5 h-5 inline mr-1" /> Cloud Pollers
|
||||
</h2>
|
||||
<.table id="cloud-pollers-table" rows={@streams.cloud_pollers}>
|
||||
<:col :let={{_id, agent}} label="Name">
|
||||
<div class="flex items-center gap-2">
|
||||
<.link
|
||||
navigate={~p"/agents/#{agent.id}"}
|
||||
class="font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
|
||||
>
|
||||
{agent.name}
|
||||
</.link>
|
||||
<%= if agent.allow_remote_debug do %>
|
||||
<span class="inline-flex items-center gap-1 rounded-full bg-amber-50 dark:bg-amber-900/30 px-2 py-0.5 text-xs font-medium text-amber-700 dark:text-amber-300 ring-1 ring-inset ring-amber-600/20 dark:ring-amber-400/30">
|
||||
<.icon name="hero-bug-ant" class="h-3 w-3" /> Debug
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</:col>
|
||||
|
||||
<:col :let={{_id, agent}} label="Status">
|
||||
<% {status, label} = agent_status(agent) %>
|
||||
<span class={"inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium #{status_badge_class(status)}"}>
|
||||
<span class={"h-1.5 w-1.5 rounded-full #{status_dot_class(status)}"} />
|
||||
{label}
|
||||
</span>
|
||||
<% end %>
|
||||
<%= if agent.allow_remote_debug do %>
|
||||
<span class="inline-flex items-center gap-1 rounded-full bg-amber-50 dark:bg-amber-900/30 px-2 py-0.5 text-xs font-medium text-amber-700 dark:text-amber-300 ring-1 ring-inset ring-amber-600/20 dark:ring-amber-400/30">
|
||||
<.icon name="hero-bug-ant" class="h-3 w-3" /> Debug
|
||||
</:col>
|
||||
|
||||
<:col :let={{_id, agent}} label="Devices">
|
||||
<% counts = Map.get(@device_counts, agent.id, %{direct: 0, total: 0}) %>
|
||||
<div class="text-sm text-gray-900 dark:text-white">
|
||||
{counts.total} total
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
|
||||
{counts.direct} direct
|
||||
<%= if counts.total > counts.direct do %>
|
||||
· {counts.total - counts.direct} inherited
|
||||
<% end %>
|
||||
</div>
|
||||
</:col>
|
||||
|
||||
<:col :let={{_id, agent}} label="Last Seen">
|
||||
<div class="text-sm text-gray-900 dark:text-white">
|
||||
<.timestamp datetime={agent.last_seen_at} timezone={@timezone} now={@now} />
|
||||
</div>
|
||||
<%= if agent.last_seen_at do %>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
|
||||
<.timestamp
|
||||
datetime={agent.last_seen_at}
|
||||
timezone={@timezone}
|
||||
format="absolute"
|
||||
/>
|
||||
</div>
|
||||
<% end %>
|
||||
</:col>
|
||||
|
||||
<:col :let={{_id, agent}} label="Version">
|
||||
<%= if agent.metadata["version"] do %>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400 font-mono">
|
||||
{format_agent_version(agent.metadata["version"])}
|
||||
</div>
|
||||
<% end %>
|
||||
</:col>
|
||||
</.table>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%!-- Organization Agents --%>
|
||||
<%= if @has_org_agents do %>
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white mb-3">
|
||||
<.icon name="hero-server" class="w-5 h-5 inline mr-1" /> Organization Agents
|
||||
</h2>
|
||||
<.table id="org-agents-table" rows={@streams.org_agents}>
|
||||
<:col :let={{_id, agent}} label="Name">
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
<.link
|
||||
navigate={~p"/agents/#{agent.id}"}
|
||||
class="font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
|
||||
>
|
||||
{agent.name}
|
||||
</.link>
|
||||
<%= if agent.allow_remote_debug do %>
|
||||
<span class="inline-flex items-center gap-1 rounded-full bg-amber-50 dark:bg-amber-900/30 px-2 py-0.5 text-xs font-medium text-amber-700 dark:text-amber-300 ring-1 ring-inset ring-amber-600/20 dark:ring-amber-400/30">
|
||||
<.icon name="hero-bug-ant" class="h-3 w-3" /> Debug
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
|
||||
{agent.organization && agent.organization.name}
|
||||
</div>
|
||||
</div>
|
||||
</:col>
|
||||
|
||||
<:col :let={{_id, agent}} label="Status">
|
||||
<% {status, label} = agent_status(agent) %>
|
||||
<span class={"inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium #{status_badge_class(status)}"}>
|
||||
<span class={"h-1.5 w-1.5 rounded-full #{status_dot_class(status)}"} />
|
||||
{label}
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</:col>
|
||||
</:col>
|
||||
|
||||
<:col :let={{_id, agent}} label="Organization">
|
||||
<%= if agent.is_cloud_poller do %>
|
||||
<span class="text-sm text-purple-600 dark:text-purple-400">Cloud Poller</span>
|
||||
<% else %>
|
||||
<span class="text-sm text-gray-900 dark:text-white">
|
||||
{agent.organization && agent.organization.name}
|
||||
</span>
|
||||
<% end %>
|
||||
</:col>
|
||||
<:col :let={{_id, agent}} label="Devices">
|
||||
<% counts = Map.get(@device_counts, agent.id, %{direct: 0, total: 0}) %>
|
||||
<div class="text-sm text-gray-900 dark:text-white">
|
||||
{counts.total} total
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
|
||||
{counts.direct} direct
|
||||
<%= if counts.total > counts.direct do %>
|
||||
· {counts.total - counts.direct} inherited
|
||||
<% end %>
|
||||
</div>
|
||||
</:col>
|
||||
|
||||
<:col :let={{_id, agent}} label="Status">
|
||||
<% {status, label} = agent_status(agent) %>
|
||||
<span class={"inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium #{status_badge_class(status)}"}>
|
||||
<span class={"h-1.5 w-1.5 rounded-full #{status_dot_class(status)}"} />
|
||||
{label}
|
||||
</span>
|
||||
</:col>
|
||||
<:col :let={{_id, agent}} label="Last Seen">
|
||||
<div class="text-sm text-gray-900 dark:text-white">
|
||||
<.timestamp datetime={agent.last_seen_at} timezone={@timezone} now={@now} />
|
||||
</div>
|
||||
<%= if agent.last_seen_at do %>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
|
||||
<.timestamp
|
||||
datetime={agent.last_seen_at}
|
||||
timezone={@timezone}
|
||||
format="absolute"
|
||||
/>
|
||||
</div>
|
||||
<% end %>
|
||||
</:col>
|
||||
|
||||
<:col :let={{_id, agent}} label="Devices">
|
||||
<% counts = Map.get(@device_counts, agent.id, %{direct: 0, total: 0}) %>
|
||||
<div class="text-sm text-gray-900 dark:text-white">
|
||||
{counts.total} total
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
|
||||
{counts.direct} direct
|
||||
<%= if counts.total > counts.direct do %>
|
||||
· {counts.total - counts.direct} inherited
|
||||
<% end %>
|
||||
</div>
|
||||
</:col>
|
||||
|
||||
<:col :let={{_id, agent}} label="Last Seen">
|
||||
<div class="text-sm text-gray-900 dark:text-white">
|
||||
<.timestamp datetime={agent.last_seen_at} timezone={@timezone} now={@now} />
|
||||
</div>
|
||||
<%= if agent.last_seen_at do %>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
|
||||
<.timestamp datetime={agent.last_seen_at} timezone={@timezone} format="absolute" />
|
||||
</div>
|
||||
<% end %>
|
||||
</:col>
|
||||
|
||||
<:col :let={{_id, agent}} label="Version">
|
||||
<%= if agent.metadata["version"] do %>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400 font-mono">
|
||||
{format_agent_version(agent.metadata["version"])}
|
||||
</div>
|
||||
<% end %>
|
||||
</:col>
|
||||
</.table>
|
||||
<:col :let={{_id, agent}} label="Version">
|
||||
<%= if agent.metadata["version"] do %>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400 font-mono">
|
||||
{format_agent_version(agent.metadata["version"])}
|
||||
</div>
|
||||
<% end %>
|
||||
</:col>
|
||||
</.table>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</Layouts.admin>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue