discovery fix and favicon
This commit is contained in:
parent
2324b75e47
commit
94311a48aa
7 changed files with 102 additions and 52 deletions
|
|
@ -204,7 +204,20 @@ defmodule Towerops.DeviceProfiles do
|
|||
end
|
||||
|
||||
defp match_detection_rules?(rules, system_info) do
|
||||
Enum.any?(rules, &match_rule?(&1, system_info))
|
||||
# Group rules by rule_group (each group represents a detection block from YAML)
|
||||
# OR logic between groups, AND logic within groups
|
||||
rules
|
||||
|> Enum.group_by(& &1.rule_group)
|
||||
|> Enum.any?(fn {_group_id, group_rules} ->
|
||||
# Within a group, ALL field types must match (AND logic)
|
||||
# But we need to handle multiple values for the same field type (OR within field)
|
||||
group_rules
|
||||
|> Enum.group_by(& &1.rule_type)
|
||||
|> Enum.all?(fn {_rule_type, type_rules} ->
|
||||
# Within the same field type, ANY rule can match (OR logic)
|
||||
Enum.any?(type_rules, &match_rule?(&1, system_info))
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
defp match_rule?(%DetectionRule{rule_type: "sysObjectID"} = rule, system_info) do
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ defmodule Towerops.DeviceProfiles.DetectionRule do
|
|||
field :value, :string
|
||||
field :pattern, :string
|
||||
field :priority, :integer, default: 0
|
||||
field :rule_group, :integer, default: 0
|
||||
|
||||
belongs_to :device_profile, DeviceProfile
|
||||
|
||||
|
|
@ -30,7 +31,16 @@ defmodule Towerops.DeviceProfiles.DetectionRule do
|
|||
@doc false
|
||||
def changeset(rule, attrs) do
|
||||
rule
|
||||
|> cast(attrs, [:rule_type, :oid, :operator, :value, :pattern, :priority, :device_profile_id])
|
||||
|> cast(attrs, [
|
||||
:rule_type,
|
||||
:oid,
|
||||
:operator,
|
||||
:value,
|
||||
:pattern,
|
||||
:priority,
|
||||
:rule_group,
|
||||
:device_profile_id
|
||||
])
|
||||
|> validate_required([:rule_type, :device_profile_id])
|
||||
|> foreign_key_constraint(:device_profile_id)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -132,17 +132,21 @@ defmodule Towerops.DeviceProfiles.Importer do
|
|||
end
|
||||
|
||||
defp create_detection_rules(profile_id, rules) when is_list(rules) do
|
||||
Enum.each(rules, fn rule ->
|
||||
parse_detection_rule(profile_id, rule)
|
||||
# Each rule in the list is a separate detection block
|
||||
# Uses OR logic between blocks, AND logic within blocks
|
||||
rules
|
||||
|> Enum.with_index()
|
||||
|> Enum.each(fn {rule, group_index} ->
|
||||
parse_detection_rule(profile_id, rule, group_index)
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp parse_detection_rule(profile_id, rule) do
|
||||
defp parse_detection_rule(profile_id, rule, group_index) do
|
||||
# Handle sysObjectID rules
|
||||
if Map.has_key?(rule, "sysObjectID") do
|
||||
create_sys_object_id_rules(profile_id, rule["sysObjectID"])
|
||||
create_sys_object_id_rules(profile_id, rule["sysObjectID"], group_index)
|
||||
end
|
||||
|
||||
# Handle sysDescr rules
|
||||
|
|
@ -150,67 +154,42 @@ defmodule Towerops.DeviceProfiles.Importer do
|
|||
DeviceProfiles.create_detection_rule(%{
|
||||
device_profile_id: profile_id,
|
||||
rule_type: "sysDescr",
|
||||
value: rule["sysDescr"]
|
||||
value: rule["sysDescr"],
|
||||
rule_group: group_index
|
||||
})
|
||||
end
|
||||
|
||||
# Handle sysDescr_regex rules
|
||||
if Map.has_key?(rule, "sysDescr_regex") do
|
||||
create_sys_descr_regex_rules(profile_id, rule["sysDescr_regex"])
|
||||
create_sys_descr_regex_rules(profile_id, rule["sysDescr_regex"], group_index)
|
||||
end
|
||||
|
||||
{:ok, :processed}
|
||||
end
|
||||
|
||||
defp create_sys_object_id_rules(profile_id, oids) when is_list(oids) do
|
||||
defp create_sys_object_id_rules(profile_id, oids, group_index) when is_list(oids) do
|
||||
Enum.each(oids, fn oid ->
|
||||
# Skip overly broad patterns that would match any enterprise OID
|
||||
if is_overly_broad_oid?(oid) do
|
||||
Logger.warning("Skipping overly broad sysObjectID pattern: #{oid}")
|
||||
else
|
||||
DeviceProfiles.create_detection_rule(%{
|
||||
device_profile_id: profile_id,
|
||||
rule_type: "sysObjectID",
|
||||
pattern: oid
|
||||
})
|
||||
end
|
||||
DeviceProfiles.create_detection_rule(%{
|
||||
device_profile_id: profile_id,
|
||||
rule_type: "sysObjectID",
|
||||
pattern: oid,
|
||||
rule_group: group_index
|
||||
})
|
||||
end)
|
||||
|
||||
{:ok, :created}
|
||||
end
|
||||
|
||||
defp create_sys_object_id_rules(profile_id, oid) when is_binary(oid) do
|
||||
# Skip overly broad patterns that would match any enterprise OID
|
||||
if is_overly_broad_oid?(oid) do
|
||||
Logger.warning("Skipping overly broad sysObjectID pattern: #{oid}")
|
||||
else
|
||||
DeviceProfiles.create_detection_rule(%{
|
||||
device_profile_id: profile_id,
|
||||
rule_type: "sysObjectID",
|
||||
pattern: oid
|
||||
})
|
||||
end
|
||||
defp create_sys_object_id_rules(profile_id, oid, group_index) when is_binary(oid) do
|
||||
DeviceProfiles.create_detection_rule(%{
|
||||
device_profile_id: profile_id,
|
||||
rule_type: "sysObjectID",
|
||||
pattern: oid,
|
||||
rule_group: group_index
|
||||
})
|
||||
end
|
||||
|
||||
# Check if an OID pattern is too broad and would match too many devices
|
||||
# Patterns like ".1.3.6.1.4.1" would match ANY enterprise OID
|
||||
defp is_overly_broad_oid?(oid) do
|
||||
# Normalize to dotted format
|
||||
normalized = if String.starts_with?(oid, "."), do: oid, else: "." <> oid
|
||||
|
||||
# These are the only acceptable root OIDs that are specific enough
|
||||
# .1.3.6.1.4.1 is the enterprise prefix - anything shorter is too broad
|
||||
# Must have at least one more component after .1.3.6.1.4.1
|
||||
case normalized do
|
||||
".1.3.6.1.4.1" -> true
|
||||
# Also reject other common roots that are too broad
|
||||
".1.3.6.1.4" -> true
|
||||
".1.3.6.1" -> true
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
|
||||
defp create_sys_descr_regex_rules(profile_id, patterns) when is_list(patterns) do
|
||||
defp create_sys_descr_regex_rules(profile_id, patterns, group_index) when is_list(patterns) do
|
||||
Enum.each(patterns, fn pattern ->
|
||||
# Remove leading/trailing slashes from regex pattern
|
||||
clean_pattern = String.trim(pattern, "/")
|
||||
|
|
@ -218,20 +197,22 @@ defmodule Towerops.DeviceProfiles.Importer do
|
|||
DeviceProfiles.create_detection_rule(%{
|
||||
device_profile_id: profile_id,
|
||||
rule_type: "sysDescr_regex",
|
||||
pattern: clean_pattern
|
||||
pattern: clean_pattern,
|
||||
rule_group: group_index
|
||||
})
|
||||
end)
|
||||
|
||||
{:ok, :created}
|
||||
end
|
||||
|
||||
defp create_sys_descr_regex_rules(profile_id, pattern) when is_binary(pattern) do
|
||||
defp create_sys_descr_regex_rules(profile_id, pattern, group_index) when is_binary(pattern) do
|
||||
clean_pattern = String.trim(pattern, "/")
|
||||
|
||||
DeviceProfiles.create_detection_rule(%{
|
||||
device_profile_id: profile_id,
|
||||
rule_type: "sysDescr_regex",
|
||||
pattern: clean_pattern
|
||||
pattern: clean_pattern,
|
||||
rule_group: group_index
|
||||
})
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ defmodule ToweropsWeb.DeviceLive.Index do
|
|||
|
||||
alias Towerops.Devices
|
||||
alias Towerops.Sites
|
||||
alias Towerops.Snmp
|
||||
alias Towerops.Workers.DiscoveryWorker
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
|
|
@ -26,4 +28,30 @@ defmodule ToweropsWeb.DeviceLive.Index do
|
|||
defp apply_action(socket, :index, _params) do
|
||||
socket
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("force_rediscover_all", _params, socket) do
|
||||
devices = socket.assigns.device
|
||||
snmp_devices = Enum.filter(devices, & &1.snmp_enabled)
|
||||
|
||||
if Enum.empty?(snmp_devices) do
|
||||
{:noreply, put_flash(socket, :error, "No SNMP-enabled devices found")}
|
||||
else
|
||||
Enum.each(snmp_devices, fn device ->
|
||||
enqueue_discovery(device.id)
|
||||
end)
|
||||
|
||||
count = length(snmp_devices)
|
||||
|
||||
{:noreply, put_flash(socket, :info, "Discovery started for #{count} device#{if count == 1, do: "", else: "s"}")}
|
||||
end
|
||||
end
|
||||
|
||||
defp enqueue_discovery(device_id) do
|
||||
if Application.get_env(:towerops, :env) == :test do
|
||||
Task.start(fn -> Snmp.discover_device(Devices.get_device!(device_id)) end)
|
||||
else
|
||||
{:ok, _job} = Exq.enqueue(Exq, "discovery", DiscoveryWorker, [device_id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,6 +9,15 @@
|
|||
{@page_title}
|
||||
<:subtitle>Monitor and manage all your network devices</:subtitle>
|
||||
<:actions>
|
||||
<%= if @device != [] do %>
|
||||
<.button
|
||||
type="button"
|
||||
phx-click="force_rediscover_all"
|
||||
data-confirm="This will trigger SNMP discovery for all SNMP-enabled devices. Continue?"
|
||||
>
|
||||
<.icon name="hero-magnifying-glass" class="h-4 w-4" /> Force Rediscover All
|
||||
</.button>
|
||||
<% end %>
|
||||
<.button
|
||||
:if={@has_sites}
|
||||
navigate={~p"/devices/new"}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
defmodule Towerops.Repo.Migrations.AddRuleGroupToDetectionRules do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:profile_detection_rules) do
|
||||
add :rule_group, :integer, null: false, default: 0
|
||||
end
|
||||
end
|
||||
end
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 152 B After Width: | Height: | Size: 1.1 KiB |
Loading…
Add table
Reference in a new issue