allow override of agent and snmp community
This commit is contained in:
parent
7975581da2
commit
dca90db137
6 changed files with 268 additions and 23 deletions
|
|
@ -5,6 +5,8 @@ defmodule Towerops.Organizations do
|
|||
|
||||
import Ecto.Query
|
||||
|
||||
alias Towerops.Agents.AgentAssignment
|
||||
alias Towerops.Equipment.Equipment
|
||||
alias Towerops.Organizations.Invitation
|
||||
alias Towerops.Organizations.Membership
|
||||
alias Towerops.Organizations.Organization
|
||||
|
|
@ -248,6 +250,75 @@ defmodule Towerops.Organizations do
|
|||
Repo.delete(invitation)
|
||||
end
|
||||
|
||||
## Bulk Configuration Updates
|
||||
|
||||
@doc """
|
||||
Applies organization's SNMP configuration to all equipment in the organization.
|
||||
|
||||
This updates all equipment records to use the organization's SNMP version and community string,
|
||||
overwriting any existing equipment-level or site-level configurations.
|
||||
|
||||
Returns the number of equipment records updated.
|
||||
"""
|
||||
def apply_snmp_config_to_all_equipment(organization_id) do
|
||||
organization = get_organization!(organization_id)
|
||||
|
||||
Repo.update_all(from(e in Equipment, join: s in assoc(e, :site), where: s.organization_id == ^organization_id),
|
||||
set: [
|
||||
snmp_version: organization.snmp_version,
|
||||
snmp_community: organization.snmp_community,
|
||||
updated_at: DateTime.truncate(DateTime.utc_now(), :second)
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Applies organization's default agent to all equipment in the organization.
|
||||
|
||||
This creates/updates agent assignments for all equipment to use the organization's
|
||||
default agent, overwriting any existing equipment-level or site-level assignments.
|
||||
|
||||
Returns the number of equipment records assigned.
|
||||
"""
|
||||
def apply_agent_to_all_equipment(organization_id) do
|
||||
organization = Repo.get!(Organization, organization_id)
|
||||
|
||||
if organization.default_agent_token_id do
|
||||
# Get all equipment IDs for this organization
|
||||
equipment_ids =
|
||||
Repo.all(
|
||||
from(e in Equipment, join: s in assoc(e, :site), where: s.organization_id == ^organization_id, select: e.id)
|
||||
)
|
||||
|
||||
# Delete existing assignments
|
||||
Repo.delete_all(from(a in AgentAssignment, where: a.equipment_id in ^equipment_ids))
|
||||
|
||||
# Create new assignments
|
||||
now = DateTime.truncate(DateTime.utc_now(), :second)
|
||||
|
||||
assignments =
|
||||
Enum.map(equipment_ids, fn equipment_id ->
|
||||
%{
|
||||
equipment_id: equipment_id,
|
||||
agent_token_id: organization.default_agent_token_id,
|
||||
inserted_at: now,
|
||||
updated_at: now
|
||||
}
|
||||
end)
|
||||
|
||||
{count, _} = Repo.insert_all(AgentAssignment, assignments)
|
||||
{count, nil}
|
||||
else
|
||||
# No default agent set, delete all assignments
|
||||
equipment_ids =
|
||||
Repo.all(
|
||||
from(e in Equipment, join: s in assoc(e, :site), where: s.organization_id == ^organization_id, select: e.id)
|
||||
)
|
||||
|
||||
Repo.delete_all(from(a in AgentAssignment, where: a.equipment_id in ^equipment_ids))
|
||||
end
|
||||
end
|
||||
|
||||
## Authorization
|
||||
|
||||
@doc """
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ defmodule Towerops.Sites do
|
|||
|
||||
import Ecto.Query
|
||||
|
||||
alias Towerops.Agents.AgentAssignment
|
||||
alias Towerops.Equipment.Equipment
|
||||
alias Towerops.Repo
|
||||
alias Towerops.Sites.Site
|
||||
|
||||
|
|
@ -121,4 +123,65 @@ defmodule Towerops.Sites do
|
|||
|
||||
%{site: site, children: children}
|
||||
end
|
||||
|
||||
## Bulk Configuration Updates
|
||||
|
||||
@doc """
|
||||
Applies site's SNMP configuration to all equipment at the site.
|
||||
|
||||
This updates all equipment records to use the site's SNMP version and community string,
|
||||
overwriting any existing equipment-level configurations.
|
||||
|
||||
Returns the number of equipment records updated.
|
||||
"""
|
||||
def apply_snmp_config_to_all_equipment(site_id) do
|
||||
site = get_site!(site_id)
|
||||
|
||||
Repo.update_all(from(e in Equipment, where: e.site_id == ^site_id),
|
||||
set: [
|
||||
snmp_version: site.snmp_version,
|
||||
snmp_community: site.snmp_community,
|
||||
updated_at: DateTime.truncate(DateTime.utc_now(), :second)
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Applies site's agent to all equipment at the site.
|
||||
|
||||
This creates/updates agent assignments for all equipment at the site to use the site's
|
||||
agent, overwriting any existing equipment-level assignments.
|
||||
|
||||
Returns the number of equipment records assigned.
|
||||
"""
|
||||
def apply_agent_to_all_equipment(site_id) do
|
||||
site = Repo.get!(Site, site_id)
|
||||
|
||||
# Get all equipment IDs for this site
|
||||
equipment_ids = Repo.all(from(e in Equipment, where: e.site_id == ^site_id, select: e.id))
|
||||
|
||||
if site.agent_token_id do
|
||||
# Delete existing assignments
|
||||
Repo.delete_all(from(a in AgentAssignment, where: a.equipment_id in ^equipment_ids))
|
||||
|
||||
# Create new assignments
|
||||
now = DateTime.truncate(DateTime.utc_now(), :second)
|
||||
|
||||
assignments =
|
||||
Enum.map(equipment_ids, fn equipment_id ->
|
||||
%{
|
||||
equipment_id: equipment_id,
|
||||
agent_token_id: site.agent_token_id,
|
||||
inserted_at: now,
|
||||
updated_at: now
|
||||
}
|
||||
end)
|
||||
|
||||
{count, _} = Repo.insert_all(AgentAssignment, assignments)
|
||||
{count, nil}
|
||||
else
|
||||
# No agent set, delete all assignments (they will inherit from org or use cloud)
|
||||
Repo.delete_all(from(a in AgentAssignment, where: a.equipment_id in ^equipment_ids))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -47,4 +47,18 @@ defmodule ToweropsWeb.Org.SettingsLive do
|
|||
{:noreply, assign(socket, :form, to_form(changeset))}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("apply_snmp_to_all", _params, socket) do
|
||||
{count, _} = Organizations.apply_snmp_config_to_all_equipment(socket.assigns.organization.id)
|
||||
|
||||
{:noreply, put_flash(socket, :info, "Applied SNMP configuration to #{count} equipment records across all sites")}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("apply_agent_to_all", _params, socket) do
|
||||
{count, _} = Organizations.apply_agent_to_all_equipment(socket.assigns.organization.id)
|
||||
|
||||
{:noreply, put_flash(socket, :info, "Applied default agent to #{count} equipment records across all sites")}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -48,6 +48,25 @@
|
|||
<.icon name="hero-information-circle" class="h-4 w-4 inline" />
|
||||
SNMP configuration hierarchy: Equipment > Site > Organization
|
||||
</p>
|
||||
|
||||
<%= if @organization.snmp_community do %>
|
||||
<div class="mt-4 p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg">
|
||||
<p class="text-sm font-medium text-amber-900 dark:text-amber-200 mb-2">
|
||||
Force Apply to All Equipment
|
||||
</p>
|
||||
<p class="text-sm text-amber-700 dark:text-amber-300 mb-3">
|
||||
This will override SNMP settings for ALL equipment across all sites in this organization.
|
||||
</p>
|
||||
<.button
|
||||
type="button"
|
||||
phx-click="apply_snmp_to_all"
|
||||
data-confirm="This will replace SNMP settings for ALL equipment in this organization. Are you sure?"
|
||||
variant="danger"
|
||||
>
|
||||
<.icon name="hero-arrow-path" class="h-4 w-4" /> Apply SNMP Config to All Equipment
|
||||
</.button>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= if @available_agents != [] do %>
|
||||
|
|
@ -98,6 +117,26 @@
|
|||
<.icon name="hero-information-circle" class="h-4 w-4 inline" />
|
||||
Agent assignment hierarchy: Equipment > Site > Organization
|
||||
</p>
|
||||
|
||||
<%= if @organization.default_agent_token_id do %>
|
||||
<div class="mt-4 p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg">
|
||||
<p class="text-sm font-medium text-amber-900 dark:text-amber-200 mb-2">
|
||||
Force Apply to All Equipment
|
||||
</p>
|
||||
<p class="text-sm text-amber-700 dark:text-amber-300 mb-3">
|
||||
This will assign the default agent to ALL equipment across all sites in this organization.
|
||||
</p>
|
||||
<.button
|
||||
type="button"
|
||||
phx-click="apply_agent_to_all"
|
||||
data-confirm="This will replace agent assignments for ALL equipment in this organization. Are you sure?"
|
||||
variant="danger"
|
||||
>
|
||||
<.icon name="hero-arrow-path" class="h-4 w-4" />
|
||||
Apply Default Agent to All Equipment
|
||||
</.button>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="mt-6 border-t pt-6">
|
||||
|
|
|
|||
|
|
@ -93,6 +93,20 @@ defmodule ToweropsWeb.SiteLive.Form do
|
|||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("apply_snmp_to_all", _params, socket) do
|
||||
{count, _} = Sites.apply_snmp_config_to_all_equipment(socket.assigns.site.id)
|
||||
|
||||
{:noreply, put_flash(socket, :info, "Applied SNMP configuration to #{count} equipment records at this site")}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("apply_agent_to_all", _params, socket) do
|
||||
{count, _} = Sites.apply_agent_to_all_equipment(socket.assigns.site.id)
|
||||
|
||||
{:noreply, put_flash(socket, :info, "Applied agent to #{count} equipment records at this site")}
|
||||
end
|
||||
|
||||
defp save_site(socket, :new, site_params) do
|
||||
site_params = Map.put(site_params, "organization_id", socket.assigns.organization.id)
|
||||
|
||||
|
|
|
|||
|
|
@ -40,32 +40,59 @@
|
|||
|
||||
<.input field={@form[:location]} type="text" label="Location" />
|
||||
|
||||
<.input field={@form[:description]} type="textarea" label="Description" />
|
||||
|
||||
<%= if @available_agents != [] do %>
|
||||
<.input
|
||||
field={@form[:agent_token_id]}
|
||||
type="select"
|
||||
label="Default Agent for Site"
|
||||
prompt="Inherit from organization"
|
||||
options={Enum.map(@available_agents, &{&1.name, &1.id})}
|
||||
/>
|
||||
<%= if @live_action == :edit and @site.agent_token_id do %>
|
||||
<p class="mt-1 text-sm text-amber-600 dark:text-amber-400 flex items-center gap-1">
|
||||
<.icon name="hero-exclamation-triangle" class="h-4 w-4" />
|
||||
<strong>Overriding</strong> organization default -
|
||||
all equipment at this site will use this agent
|
||||
<div class="mt-6 border-t pt-6">
|
||||
<h3 class="text-base font-medium mb-4">Agent Configuration (Optional)</h3>
|
||||
<p class="text-sm text-zinc-600 dark:text-zinc-400 mb-4">
|
||||
Set a default agent for SNMP polling at this site. This will override the organization default for all equipment at this site. Leave blank to inherit from organization.
|
||||
</p>
|
||||
<% else %>
|
||||
<%= if Map.get(assigns, :org_agent) do %>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400 flex items-center gap-1">
|
||||
<.icon name="hero-building-office-2" class="h-4 w-4" />
|
||||
Currently inheriting organization default: <strong>{@org_agent.name}</strong>
|
||||
|
||||
<.input
|
||||
field={@form[:agent_token_id]}
|
||||
type="select"
|
||||
label="Default Agent for Site"
|
||||
prompt="Inherit from organization"
|
||||
options={Enum.map(@available_agents, &{&1.name, &1.id})}
|
||||
/>
|
||||
|
||||
<%= if @live_action == :edit and @site.agent_token_id do %>
|
||||
<p class="mt-1 text-sm text-amber-600 dark:text-amber-400 flex items-center gap-1">
|
||||
<.icon name="hero-exclamation-triangle" class="h-4 w-4" />
|
||||
<strong>Overriding</strong> organization default -
|
||||
all equipment at this site will use this agent
|
||||
</p>
|
||||
|
||||
<div class="mt-4 p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg">
|
||||
<p class="text-sm font-medium text-amber-900 dark:text-amber-200 mb-2">
|
||||
Force Apply to Site Equipment
|
||||
</p>
|
||||
<p class="text-sm text-amber-700 dark:text-amber-300 mb-3">
|
||||
This will assign this agent to ALL equipment at this site.
|
||||
</p>
|
||||
<.button
|
||||
type="button"
|
||||
phx-click="apply_agent_to_all"
|
||||
data-confirm="This will replace agent assignments for ALL equipment at this site. Are you sure?"
|
||||
variant="danger"
|
||||
>
|
||||
<.icon name="hero-arrow-path" class="h-4 w-4" /> Apply Agent to All Equipment
|
||||
</.button>
|
||||
</div>
|
||||
<% else %>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
Set a default agent for all equipment at this site. Equipment can override this setting individually.
|
||||
</p>
|
||||
<%= if Map.get(assigns, :org_agent) do %>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400 flex items-center gap-1">
|
||||
<.icon name="hero-building-office-2" class="h-4 w-4" />
|
||||
Currently inheriting organization default: <strong>{@org_agent.name}</strong>
|
||||
</p>
|
||||
<% else %>
|
||||
<p class="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
Set a default agent for all equipment at this site. Equipment can override this setting individually.
|
||||
</p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="mt-6 border-t pt-6">
|
||||
|
|
@ -88,9 +115,26 @@
|
|||
label="SNMP Community String"
|
||||
placeholder="Leave blank to inherit from organization"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<.input field={@form[:description]} type="textarea" label="Description" />
|
||||
<%= if @live_action == :edit and @site.snmp_community do %>
|
||||
<div class="mt-4 p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg">
|
||||
<p class="text-sm font-medium text-amber-900 dark:text-amber-200 mb-2">
|
||||
Force Apply to Site Equipment
|
||||
</p>
|
||||
<p class="text-sm text-amber-700 dark:text-amber-300 mb-3">
|
||||
This will override SNMP settings for ALL equipment at this site.
|
||||
</p>
|
||||
<.button
|
||||
type="button"
|
||||
phx-click="apply_snmp_to_all"
|
||||
data-confirm="This will replace SNMP settings for ALL equipment at this site. Are you sure?"
|
||||
variant="danger"
|
||||
>
|
||||
<.icon name="hero-arrow-path" class="h-4 w-4" /> Apply SNMP Config to All Equipment
|
||||
</.button>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3 mt-6">
|
||||
<.button phx-disable-with="Saving..." variant="primary">Save Site</.button>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue