- Create snmp_ip_addresses table with migration - Add IpAddress schema with ip_type validation (ipv4/ipv6) - Add prefix_length validation based on ip_type - Implement discover_ip_addresses/1 in Base profile - Extract IP addresses from IP-MIB ipAdEntTable - Support multiple IPs per interface with subnet masks - Add 12 schema tests and 4 discovery tests
87 lines
2.3 KiB
Elixir
87 lines
2.3 KiB
Elixir
defmodule Towerops.Snmp.IpAddress do
|
|
@moduledoc """
|
|
SNMP IP address schema for tracking IP addresses assigned to interfaces.
|
|
|
|
IP addresses are discovered via:
|
|
- IP-MIB ipAddrTable (IPv4)
|
|
- IPV6-MIB ipv6AddrTable (IPv6)
|
|
|
|
Each interface can have multiple IP addresses (primary and secondary/alias).
|
|
"""
|
|
use Ecto.Schema
|
|
|
|
import Ecto.Changeset
|
|
|
|
alias Ecto.Association.NotLoaded
|
|
alias Towerops.Snmp.Interface
|
|
|
|
@valid_ip_types ~w(ipv4 ipv6)
|
|
|
|
@primary_key {:id, :binary_id, autogenerate: true}
|
|
@foreign_key_type :binary_id
|
|
schema "snmp_ip_addresses" do
|
|
field :ip_address, :string
|
|
field :subnet_mask, :string
|
|
field :prefix_length, :integer
|
|
field :ip_type, :string
|
|
field :is_primary, :boolean, default: false
|
|
field :last_checked_at, :utc_datetime
|
|
field :metadata, :map, default: %{}
|
|
|
|
belongs_to :snmp_interface, Interface
|
|
|
|
timestamps(type: :utc_datetime)
|
|
end
|
|
|
|
@type t :: %__MODULE__{
|
|
id: Ecto.UUID.t(),
|
|
ip_address: String.t(),
|
|
subnet_mask: String.t() | nil,
|
|
prefix_length: integer() | nil,
|
|
ip_type: String.t(),
|
|
is_primary: boolean(),
|
|
last_checked_at: DateTime.t() | nil,
|
|
metadata: map(),
|
|
snmp_interface_id: Ecto.UUID.t(),
|
|
snmp_interface: NotLoaded.t() | Interface.t(),
|
|
inserted_at: DateTime.t(),
|
|
updated_at: DateTime.t()
|
|
}
|
|
|
|
@doc false
|
|
def changeset(ip_address, attrs) do
|
|
ip_address
|
|
|> cast(attrs, [
|
|
:snmp_interface_id,
|
|
:ip_address,
|
|
:subnet_mask,
|
|
:prefix_length,
|
|
:ip_type,
|
|
:is_primary,
|
|
:last_checked_at,
|
|
:metadata
|
|
])
|
|
|> validate_required([:snmp_interface_id, :ip_address, :ip_type])
|
|
|> validate_inclusion(:ip_type, @valid_ip_types)
|
|
|> validate_prefix_length()
|
|
|> unique_constraint([:snmp_interface_id, :ip_address])
|
|
|> foreign_key_constraint(:snmp_interface_id)
|
|
end
|
|
|
|
defp validate_prefix_length(changeset) do
|
|
case get_field(changeset, :prefix_length) do
|
|
nil ->
|
|
changeset
|
|
|
|
prefix ->
|
|
ip_type = get_field(changeset, :ip_type)
|
|
max_prefix = if ip_type == "ipv6", do: 128, else: 32
|
|
|
|
if prefix >= 0 and prefix <= max_prefix do
|
|
changeset
|
|
else
|
|
add_error(changeset, :prefix_length, "must be between 0 and #{max_prefix}")
|
|
end
|
|
end
|
|
end
|
|
end
|