From dbb2412e8959787269db64761dc593c307812b6d Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Wed, 4 Feb 2026 17:28:14 -0600 Subject: [PATCH] tcp snmp test --- lib/towerops/devices.ex | 1 + lib/towerops/devices/device.ex | 27 +++++++++++++++++++ lib/towerops/organizations/organization.ex | 3 +++ lib/towerops/proto/agent.pb.ex | 2 ++ lib/towerops/sites/site.ex | 3 +++ lib/towerops/snmp/client.ex | 1 + lib/towerops_web/channels/agent_channel.ex | 2 ++ ...ort_to_organizations_sites_and_devices.exs | 21 +++++++++++++++ 8 files changed, 60 insertions(+) create mode 100644 priv/repo/migrations/20260204230342_add_snmp_transport_to_organizations_sites_and_devices.exs diff --git a/lib/towerops/devices.ex b/lib/towerops/devices.ex index a4654a35..cc06eaee 100644 --- a/lib/towerops/devices.ex +++ b/lib/towerops/devices.ex @@ -295,6 +295,7 @@ defmodule Towerops.Devices do version: device.snmp_version || "2c", community: device.snmp_community, port: device.snmp_port || 161, + transport: device.snmp_transport || "udp", source: String.to_atom(device.snmp_community_source || "organization") } end diff --git a/lib/towerops/devices/device.ex b/lib/towerops/devices/device.ex index 2b14542d..64daad97 100644 --- a/lib/towerops/devices/device.ex +++ b/lib/towerops/devices/device.ex @@ -38,6 +38,8 @@ defmodule Towerops.Devices.Device do field :snmp_community, :string field :snmp_community_source, :string, default: "organization" field :snmp_port, :integer, default: 161 + field :snmp_transport, :string, default: "udp" + field :snmp_transport_source, :string, default: "organization" field :last_discovery_at, :utc_datetime field :last_snmp_poll_at, :utc_datetime @@ -82,7 +84,10 @@ defmodule Towerops.Devices.Device do snmp_enabled: boolean(), snmp_version: String.t(), snmp_community: String.t() | nil, + snmp_community_source: String.t(), snmp_port: integer(), + snmp_transport: String.t(), + snmp_transport_source: String.t(), last_discovery_at: DateTime.t() | nil, last_snmp_poll_at: DateTime.t() | nil, snmpv3_security_level: String.t() | nil, @@ -126,6 +131,8 @@ defmodule Towerops.Devices.Device do :snmp_community, :snmp_community_source, :snmp_port, + :snmp_transport, + :snmp_transport_source, :status, :last_checked_at, :last_status_change_at, @@ -157,6 +164,7 @@ defmodule Towerops.Devices.Device do |> validate_mikrotik() |> validate_site_belongs_to_organization() |> update_community_source() + |> update_transport_source() |> update_snmpv3_credential_source() |> update_mikrotik_credential_source() |> foreign_key_constraint(:site_id) @@ -223,6 +231,7 @@ defmodule Towerops.Devices.Device do |> validate_required([:snmp_version], message: "required when SNMP is enabled") |> validate_inclusion(:snmp_version, ["1", "2c", "3"], message: "must be 1, 2c, or 3") |> validate_number(:snmp_port, greater_than: 0, less_than: 65_536) + |> validate_inclusion(:snmp_transport, ["udp", "tcp"], message: "must be udp or tcp") else changeset end @@ -246,6 +255,24 @@ defmodule Towerops.Devices.Device do end end + defp update_transport_source(changeset) do + # If user explicitly set a transport, mark source as "device" + # If it's blank/nil, mark source as "organization" (will inherit from site or org) + case get_change(changeset, :snmp_transport) do + nil -> + # No change to transport, keep existing source + changeset + + "" -> + # Explicitly cleared, set to inherit from organization + put_change(changeset, :snmp_transport_source, "organization") + + _value -> + # Explicitly set a value, mark as device-specific + put_change(changeset, :snmp_transport_source, "device") + end + end + defp validate_mikrotik(changeset) do mikrotik_enabled = get_field(changeset, :mikrotik_enabled) diff --git a/lib/towerops/organizations/organization.ex b/lib/towerops/organizations/organization.ex index 3dd25342..7b50517a 100644 --- a/lib/towerops/organizations/organization.ex +++ b/lib/towerops/organizations/organization.ex @@ -31,6 +31,7 @@ defmodule Towerops.Organizations.Organization do field :snmp_version, :string, default: "2c" field :snmp_community, :string field :snmp_port, :integer, default: 161 + field :snmp_transport, :string, default: "udp" # SNMPv3 credentials (organization-level defaults cascade to site → device) field :snmpv3_security_level, :string @@ -66,6 +67,7 @@ defmodule Towerops.Organizations.Organization do snmp_version: String.t() | nil, snmp_community: String.t() | nil, snmp_port: integer(), + snmp_transport: String.t(), snmpv3_security_level: String.t() | nil, snmpv3_username: String.t() | nil, snmpv3_auth_protocol: String.t() | nil, @@ -98,6 +100,7 @@ defmodule Towerops.Organizations.Organization do :snmp_version, :snmp_community, :snmp_port, + :snmp_transport, :snmpv3_security_level, :snmpv3_username, :snmpv3_auth_protocol, diff --git a/lib/towerops/proto/agent.pb.ex b/lib/towerops/proto/agent.pb.ex index cedd6cd0..cf26e436 100644 --- a/lib/towerops/proto/agent.pb.ex +++ b/lib/towerops/proto/agent.pb.ex @@ -42,6 +42,7 @@ defmodule Towerops.Agent.SnmpConfig do field :version, 2, type: :string field :community, 3, type: :string field :port, 4, type: :uint32 + field :transport, 5, type: :string end defmodule Towerops.Agent.Sensor.MetadataEntry do @@ -293,6 +294,7 @@ defmodule Towerops.Agent.SnmpDevice do field :v3_auth_password, 8, type: :string, json_name: "v3AuthPassword" field :v3_priv_protocol, 9, type: :string, json_name: "v3PrivProtocol" field :v3_priv_password, 10, type: :string, json_name: "v3PrivPassword" + field :transport, 11, type: :string end defmodule Towerops.Agent.SnmpQuery do diff --git a/lib/towerops/sites/site.ex b/lib/towerops/sites/site.ex index e97c71c2..cdd63132 100644 --- a/lib/towerops/sites/site.ex +++ b/lib/towerops/sites/site.ex @@ -27,6 +27,7 @@ defmodule Towerops.Sites.Site do field :snmp_version, :string field :snmp_community, :string field :snmp_port, :integer + field :snmp_transport, :string # SNMPv3 credentials (overrides organization default, all nullable) field :snmpv3_security_level, :string @@ -62,6 +63,7 @@ defmodule Towerops.Sites.Site do snmp_version: String.t() | nil, snmp_community: String.t() | nil, snmp_port: integer() | nil, + snmp_transport: String.t() | nil, snmpv3_security_level: String.t() | nil, snmpv3_username: String.t() | nil, snmpv3_auth_protocol: String.t() | nil, @@ -100,6 +102,7 @@ defmodule Towerops.Sites.Site do :snmp_version, :snmp_community, :snmp_port, + :snmp_transport, :snmpv3_security_level, :snmpv3_username, :snmpv3_auth_protocol, diff --git a/lib/towerops/snmp/client.ex b/lib/towerops/snmp/client.ex index b0950668..1a959faa 100644 --- a/lib/towerops/snmp/client.ex +++ b/lib/towerops/snmp/client.ex @@ -13,6 +13,7 @@ defmodule Towerops.Snmp.Client do community: String.t(), version: String.t(), port: non_neg_integer(), + transport: String.t(), timeout: non_neg_integer() ] diff --git a/lib/towerops_web/channels/agent_channel.ex b/lib/towerops_web/channels/agent_channel.ex index da38f203..7cea1859 100644 --- a/lib/towerops_web/channels/agent_channel.ex +++ b/lib/towerops_web/channels/agent_channel.ex @@ -385,6 +385,7 @@ defmodule ToweropsWeb.AgentChannel do ip: device.ip_address, version: device.snmp_version, port: device.snmp_port || 161, + transport: device.snmp_transport || "udp", community: "", v3_security_level: snmp_config.security_level || "", v3_username: snmp_config.username || "", @@ -402,6 +403,7 @@ defmodule ToweropsWeb.AgentChannel do ip: device.ip_address, version: device.snmp_version, port: device.snmp_port || 161, + transport: device.snmp_transport || "udp", community: community } end diff --git a/priv/repo/migrations/20260204230342_add_snmp_transport_to_organizations_sites_and_devices.exs b/priv/repo/migrations/20260204230342_add_snmp_transport_to_organizations_sites_and_devices.exs new file mode 100644 index 00000000..e6732d3d --- /dev/null +++ b/priv/repo/migrations/20260204230342_add_snmp_transport_to_organizations_sites_and_devices.exs @@ -0,0 +1,21 @@ +defmodule Towerops.Repo.Migrations.AddSnmpTransportToOrganizationsSitesAndDevices do + use Ecto.Migration + + def change do + # Add snmp_transport to organizations (default UDP) + alter table(:organizations) do + add :snmp_transport, :string, default: "udp", null: false + end + + # Add snmp_transport to sites (nullable - inherits from organization) + alter table(:sites) do + add :snmp_transport, :string + end + + # Add snmp_transport and source tracking to devices + alter table(:devices) do + add :snmp_transport, :string, default: "udp", null: false + add :snmp_transport_source, :string, default: "organization", null: false + end + end +end