towerops/lib/towerops/profiles.ex

120 lines
2.8 KiB
Elixir

defmodule Towerops.Profiles do
@moduledoc """
Context for managing device profiles.
Profiles are stored in the database and can be updated without deployment.
"""
import Ecto.Query
alias Towerops.Profiles.DeviceOid
alias Towerops.Profiles.DeviceProfile
alias Towerops.Profiles.SensorOid
alias Towerops.Repo
require Logger
@doc """
Lists all enabled profiles ordered by priority.
"""
def list_profiles do
DeviceProfile
|> where([p], p.enabled == true)
|> order_by([p], asc: p.priority)
|> Repo.all()
end
@doc """
Gets a profile by name with associations loaded.
"""
def get_profile(name) do
DeviceProfile
|> where([p], p.name == ^name)
|> preload([:device_oids, :sensor_oids])
|> Repo.one()
end
@doc """
Matches a profile based on sysDescr and sysObjectID.
Returns the first matching profile based on priority.
"""
def match_profile(system_info) do
sys_descr = Map.get(system_info, :sys_descr, "")
sys_object_id = Map.get(system_info, :sys_object_id, "")
profiles = list_profiles()
Enum.find(profiles, fn profile ->
matches_detection_pattern?(profile, sys_descr) ||
matches_detection_oid?(profile, sys_object_id)
end)
end
@doc """
Creates a new profile.
"""
def create_profile(attrs) do
%DeviceProfile{}
|> DeviceProfile.changeset(attrs)
|> Repo.insert()
end
@doc """
Updates a profile.
"""
def update_profile(%DeviceProfile{} = profile, attrs) do
profile
|> DeviceProfile.changeset(attrs)
|> Repo.update()
end
@doc """
Deletes a profile.
"""
def delete_profile(%DeviceProfile{} = profile) do
Repo.delete(profile)
end
@doc """
Adds a device OID to a profile.
"""
def add_device_oid(profile_id, field, mib_name) do
%DeviceOid{}
|> DeviceOid.changeset(%{
profile_id: profile_id,
field: field,
mib_name: mib_name
})
|> Repo.insert()
end
@doc """
Adds a sensor OID to a profile.
"""
def add_sensor_oid(profile_id, attrs) do
%SensorOid{}
|> SensorOid.changeset(Map.put(attrs, :profile_id, profile_id))
|> Repo.insert()
end
# Private functions
defp matches_detection_pattern?(%{detection_pattern: nil}, _sys_descr), do: false
defp matches_detection_pattern?(%{detection_pattern: pattern} = profile, sys_descr) do
case Regex.compile(pattern) do
{:ok, regex} ->
Regex.match?(regex, sys_descr)
{:error, reason} ->
Logger.warning("Invalid regex pattern in profile '#{profile.name}': #{pattern} - #{inspect(reason)}")
false
end
end
defp matches_detection_oid?(%{detection_oid: nil}, _sys_object_id), do: false
defp matches_detection_oid?(%{detection_oid: pattern}, sys_object_id) do
String.starts_with?(sys_object_id, pattern)
end
end