288 lines
6.2 KiB
Elixir
288 lines
6.2 KiB
Elixir
defmodule Towerops.Snmp do
|
|
@moduledoc """
|
|
The SNMP context.
|
|
|
|
Provides the public API for SNMP discovery and monitoring functionality.
|
|
"""
|
|
|
|
import Ecto.Query
|
|
|
|
alias Towerops.Equipment.Equipment
|
|
alias Towerops.Repo
|
|
alias Towerops.Snmp.Client
|
|
alias Towerops.Snmp.Device
|
|
alias Towerops.Snmp.Discovery
|
|
alias Towerops.Snmp.Interface
|
|
alias Towerops.Snmp.InterfaceStat
|
|
alias Towerops.Snmp.Sensor
|
|
alias Towerops.Snmp.SensorReading
|
|
|
|
@doc """
|
|
Tests SNMP connectivity to a device.
|
|
|
|
## Examples
|
|
|
|
iex> test_connection("192.168.1.1", "public", "2c")
|
|
{:ok, "Connection successful"}
|
|
|
|
iex> test_connection("192.168.1.99", "wrong", "2c")
|
|
{:error, :timeout}
|
|
"""
|
|
def test_connection(ip, community, version, port \\ 161) do
|
|
Client.test_connection(
|
|
ip: ip,
|
|
community: community,
|
|
version: version,
|
|
port: port
|
|
)
|
|
end
|
|
|
|
@doc """
|
|
Runs SNMP discovery for a piece of equipment.
|
|
|
|
Discovers:
|
|
- Device information (manufacturer, model, firmware)
|
|
- Network interfaces
|
|
- Sensors (temperature, power, fans, etc.)
|
|
|
|
Updates the database with discovered data.
|
|
|
|
## Examples
|
|
|
|
iex> discover_equipment(equipment)
|
|
{:ok, %Device{}}
|
|
"""
|
|
def discover_equipment(%Equipment{} = equipment) do
|
|
Discovery.discover_equipment(equipment)
|
|
end
|
|
|
|
@doc """
|
|
Runs SNMP discovery for all SNMP-enabled equipment in an organization.
|
|
|
|
Returns a summary of successful and failed discoveries.
|
|
|
|
## Examples
|
|
|
|
iex> discover_all_for_org(org_id)
|
|
{:ok, %{success: 10, failed: 2, errors: [:timeout, :no_response]}}
|
|
"""
|
|
def discover_all_for_org(org_id) do
|
|
Discovery.discover_all(org_id)
|
|
end
|
|
|
|
# Device queries
|
|
|
|
@doc """
|
|
Gets the SNMP device for a piece of equipment.
|
|
|
|
## Examples
|
|
|
|
iex> get_device(equipment_id)
|
|
%Device{}
|
|
|
|
iex> get_device(nonexistent_id)
|
|
nil
|
|
"""
|
|
def get_device(equipment_id) do
|
|
Repo.get_by(Device, equipment_id: equipment_id)
|
|
end
|
|
|
|
@doc """
|
|
Gets the SNMP device for a piece of equipment with preloaded associations.
|
|
"""
|
|
def get_device_with_associations(equipment_id) do
|
|
Device
|
|
|> where([d], d.equipment_id == ^equipment_id)
|
|
|> preload([:interfaces, :sensors])
|
|
|> Repo.one()
|
|
end
|
|
|
|
# Interface queries
|
|
|
|
@doc """
|
|
Lists all interfaces for a device.
|
|
"""
|
|
def list_interfaces(device_id) do
|
|
Interface
|
|
|> where([i], i.snmp_device_id == ^device_id)
|
|
|> order_by([i], i.if_index)
|
|
|> Repo.all()
|
|
end
|
|
|
|
@doc """
|
|
Lists only monitored interfaces for a device.
|
|
"""
|
|
def list_monitored_interfaces(device_id) do
|
|
Interface
|
|
|> where([i], i.snmp_device_id == ^device_id and i.monitored == true)
|
|
|> order_by([i], i.if_index)
|
|
|> Repo.all()
|
|
end
|
|
|
|
@doc """
|
|
Gets a specific interface.
|
|
"""
|
|
def get_interface(interface_id) do
|
|
Repo.get(Interface, interface_id)
|
|
end
|
|
|
|
@doc """
|
|
Updates an interface.
|
|
"""
|
|
def update_interface(%Interface{} = interface, attrs) do
|
|
interface
|
|
|> Interface.changeset(attrs)
|
|
|> Repo.update()
|
|
end
|
|
|
|
# Sensor queries
|
|
|
|
@doc """
|
|
Lists all sensors for a device.
|
|
"""
|
|
def list_sensors(device_id) do
|
|
Sensor
|
|
|> where([s], s.snmp_device_id == ^device_id)
|
|
|> order_by([s], [s.sensor_type, s.sensor_index])
|
|
|> Repo.all()
|
|
end
|
|
|
|
@doc """
|
|
Lists only monitored sensors for a device.
|
|
"""
|
|
def list_monitored_sensors(device_id) do
|
|
Sensor
|
|
|> where([s], s.snmp_device_id == ^device_id and s.monitored == true)
|
|
|> order_by([s], [s.sensor_type, s.sensor_index])
|
|
|> Repo.all()
|
|
end
|
|
|
|
@doc """
|
|
Lists sensors grouped by type.
|
|
"""
|
|
def list_sensors_by_type(device_id) do
|
|
sensors =
|
|
Sensor
|
|
|> where([s], s.snmp_device_id == ^device_id)
|
|
|> order_by([s], [s.sensor_type, s.sensor_index])
|
|
|> Repo.all()
|
|
|
|
Enum.group_by(sensors, & &1.sensor_type)
|
|
end
|
|
|
|
@doc """
|
|
Gets a specific sensor.
|
|
"""
|
|
def get_sensor(sensor_id) do
|
|
Repo.get(Sensor, sensor_id)
|
|
end
|
|
|
|
@doc """
|
|
Updates a sensor.
|
|
"""
|
|
def update_sensor(%Sensor{} = sensor, attrs) do
|
|
sensor
|
|
|> Sensor.changeset(attrs)
|
|
|> Repo.update()
|
|
end
|
|
|
|
# Sensor readings queries
|
|
|
|
@doc """
|
|
Gets recent sensor readings for a sensor.
|
|
|
|
## Options
|
|
|
|
- `:limit` - Maximum number of readings to return (default: 100)
|
|
- `:since` - Only return readings after this datetime
|
|
"""
|
|
def get_sensor_readings(sensor_id, opts \\ []) do
|
|
limit = Keyword.get(opts, :limit, 100)
|
|
since = Keyword.get(opts, :since)
|
|
|
|
query =
|
|
SensorReading
|
|
|> where([r], r.sensor_id == ^sensor_id)
|
|
|> order_by([r], desc: r.checked_at)
|
|
|> limit(^limit)
|
|
|
|
query =
|
|
if since do
|
|
where(query, [r], r.checked_at >= ^since)
|
|
else
|
|
query
|
|
end
|
|
|
|
Repo.all(query)
|
|
end
|
|
|
|
@doc """
|
|
Gets the latest sensor reading for a sensor.
|
|
"""
|
|
def get_latest_sensor_reading(sensor_id) do
|
|
SensorReading
|
|
|> where([r], r.sensor_id == ^sensor_id)
|
|
|> order_by([r], desc: r.checked_at)
|
|
|> limit(1)
|
|
|> Repo.one()
|
|
end
|
|
|
|
# Interface stats queries
|
|
|
|
@doc """
|
|
Gets recent interface statistics for an interface.
|
|
|
|
## Options
|
|
|
|
- `:limit` - Maximum number of stats to return (default: 100)
|
|
- `:since` - Only return stats after this datetime
|
|
"""
|
|
def get_interface_stats(interface_id, opts \\ []) do
|
|
limit = Keyword.get(opts, :limit, 100)
|
|
since = Keyword.get(opts, :since)
|
|
|
|
query =
|
|
InterfaceStat
|
|
|> where([s], s.interface_id == ^interface_id)
|
|
|> order_by([s], desc: s.checked_at)
|
|
|> limit(^limit)
|
|
|
|
query =
|
|
if since do
|
|
where(query, [s], s.checked_at >= ^since)
|
|
else
|
|
query
|
|
end
|
|
|
|
Repo.all(query)
|
|
end
|
|
|
|
@doc """
|
|
Gets the latest interface stat for an interface.
|
|
"""
|
|
def get_latest_interface_stat(interface_id) do
|
|
InterfaceStat
|
|
|> where([s], s.interface_id == ^interface_id)
|
|
|> order_by([s], desc: s.checked_at)
|
|
|> limit(1)
|
|
|> Repo.one()
|
|
end
|
|
|
|
@doc """
|
|
Records a new sensor reading.
|
|
"""
|
|
def create_sensor_reading(attrs) do
|
|
%SensorReading{}
|
|
|> SensorReading.changeset(attrs)
|
|
|> Repo.insert()
|
|
end
|
|
|
|
@doc """
|
|
Records a new interface stat.
|
|
"""
|
|
def create_interface_stat(attrs) do
|
|
%InterfaceStat{}
|
|
|> InterfaceStat.changeset(attrs)
|
|
|> Repo.insert()
|
|
end
|
|
end
|