Add per-interface capacity tracking with manual override and automatic detection from SNMP sensors. Includes capacity resolver with priority cascade (manual > sensor > if_speed), throughput calculation from interface stats, and site/organization-level capacity summaries. - Migration: add configured_capacity_bps and capacity_source to snmp_interfaces - Capacity.Resolver: multi-source capacity detection with vendor unit normalization - Capacity context: throughput calculation, utilization percentages, site/org summaries - Snmp context: set_manual_capacity/2 and clear_manual_capacity/1 functions
88 lines
2.6 KiB
Elixir
88 lines
2.6 KiB
Elixir
defmodule Towerops.Snmp.Interface do
|
|
@moduledoc """
|
|
SNMP interface schema for network interfaces discovered via IF-MIB.
|
|
|
|
Stores interface details (name, description, type, speed, MAC address)
|
|
and has many interface statistics.
|
|
"""
|
|
use Ecto.Schema
|
|
|
|
import Ecto.Changeset
|
|
|
|
alias Ecto.Association.NotLoaded
|
|
alias Towerops.Snmp.Device
|
|
alias Towerops.Snmp.InterfaceStat
|
|
alias Towerops.Snmp.IpAddress
|
|
alias Towerops.Snmp.Neighbor
|
|
|
|
@primary_key {:id, :binary_id, autogenerate: true}
|
|
@foreign_key_type :binary_id
|
|
schema "snmp_interfaces" do
|
|
field :if_index, :integer
|
|
field :if_name, :string
|
|
field :if_descr, :string
|
|
field :if_alias, :string
|
|
field :if_type, :integer
|
|
field :if_speed, :integer
|
|
field :if_phys_address, :string
|
|
field :if_admin_status, :string
|
|
field :if_oper_status, :string
|
|
field :monitored, :boolean, default: true
|
|
field :configured_capacity_bps, :integer
|
|
field :capacity_source, :string
|
|
|
|
belongs_to :snmp_device, Device
|
|
|
|
has_many :stats, InterfaceStat, foreign_key: :interface_id
|
|
has_many :neighbors, Neighbor, foreign_key: :interface_id
|
|
has_many :ip_addresses, IpAddress, foreign_key: :snmp_interface_id
|
|
|
|
timestamps(type: :utc_datetime)
|
|
end
|
|
|
|
@type t :: %__MODULE__{
|
|
id: Ecto.UUID.t(),
|
|
if_index: integer(),
|
|
if_name: String.t() | nil,
|
|
if_descr: String.t() | nil,
|
|
if_alias: String.t() | nil,
|
|
if_type: integer() | nil,
|
|
if_speed: integer() | nil,
|
|
if_phys_address: String.t() | nil,
|
|
if_admin_status: String.t() | nil,
|
|
if_oper_status: String.t() | nil,
|
|
monitored: boolean(),
|
|
configured_capacity_bps: integer() | nil,
|
|
capacity_source: String.t() | nil,
|
|
snmp_device_id: Ecto.UUID.t(),
|
|
snmp_device: NotLoaded.t() | Device.t(),
|
|
stats: NotLoaded.t() | [InterfaceStat.t()],
|
|
inserted_at: DateTime.t(),
|
|
updated_at: DateTime.t()
|
|
}
|
|
|
|
@doc false
|
|
def changeset(interface, attrs) do
|
|
interface
|
|
|> cast(attrs, [
|
|
:snmp_device_id,
|
|
:if_index,
|
|
:if_name,
|
|
:if_descr,
|
|
:if_alias,
|
|
:if_type,
|
|
:if_speed,
|
|
:if_phys_address,
|
|
:if_admin_status,
|
|
:if_oper_status,
|
|
:monitored,
|
|
:configured_capacity_bps,
|
|
:capacity_source
|
|
])
|
|
|> validate_required([:snmp_device_id, :if_index])
|
|
|> validate_inclusion(:capacity_source, ~w(manual sensor if_speed peak))
|
|
|> validate_number(:configured_capacity_bps, greater_than: 0)
|
|
|> unique_constraint([:snmp_device_id, :if_index])
|
|
|> foreign_key_constraint(:snmp_device_id)
|
|
end
|
|
end
|