towerops/lib/towerops/proto/field_specs.ex
Graham McInitre 9d4a5f6d81 refactor: comprehensive simplification and functional programming improvements
- help_live/index.ex: 2,642→173 lines, 15 section modules + sidebar extracted
  MikroTik now real section, dead if false removed
- agent_channel.ex: 2,506→1,751 lines, 3 helper modules extracted
  (heartbeat/subscriptions/job_builder), decode_and_process/4 eliminates
  8 repeated handle_in patterns, guard-based size checks
- antenna_catalog.ex: 1,174→47 lines, 107 specs → priv/antennas/catalog.json
- topology.ex: unbounded query → batched loading (100/batch),
  all guard errors fixed, zero if/case/cond conditionals
- proto: decoder_macros.ex + field_specs.ex infrastructure for
  macro-generated protobuf decoders
2026-07-21 17:03:47 -05:00

226 lines
9.5 KiB
Elixir

defmodule Towerops.Proto.FieldSpecs do
@moduledoc """
Declarative field specifications for all protobuf message decoders.
This module documents every message type and its wire format fields.
Each spec tuple: `{struct_module, decode_function_name, fields_list, opts_keyword}`
Field types: `:string`, `:uint`, `:int64`, `:double`, `:bool`,
`{:enum, conv_fn}`, `{:message, decode_fn}`, `{:oneof, decode_fn, tag}`,
`{:repeated, :string}`, `{:map, :string, :string}`,
`{:repeated_message, decode_fn}`, `{:repeated_message, decode_fn, validate_fn}`.
Options: `validate`, `finalize`, `defaults`.
"""
alias Towerops.Proto.Types
@doc "Return all decoder specifications."
def all do
[
# ── Agent → Server messages ──────────────────────
{Types.AgentHeartbeat, :decode_agent_heartbeat,
[
{1, :version, :string},
{2, :hostname, :string},
{3, :uptime_seconds, :uint},
{4, :ip_address, :string},
{5, :arch, :string}
], validate: :validate_heartbeat},
{Types.HeartbeatMetadata, :decode_heartbeat_metadata,
[{1, :version, :string}, {2, :hostname, :string}, {3, :uptime_seconds, :uint}]},
{Types.HeartbeatResponse, :decode_heartbeat_response, [{1, :status, :string}]},
{Types.SnmpResult, :decode_snmp_result,
[
{1, :device_id, :string},
{2, :job_type, {:enum, &Types.job_type_from_int/1}},
{3, :oid_values, {:map, :string, :string}},
{4, :timestamp, :int64},
{5, :job_id, :string}
], validate: :validate_snmp_result, defaults: [job_type: :discover]},
{Types.AgentError, :decode_agent_error,
[{1, :device_id, :string}, {2, :job_id, :string}, {3, :message, :string}, {4, :timestamp, :int64}],
validate: :validate_agent_error},
{Types.CredentialTestResult, :decode_credential_test_result,
[
{1, :test_id, :string},
{2, :success, :bool},
{3, :error_message, :string},
{4, :system_description, :string},
{5, :timestamp, :int64}
], validate: :validate_credential_test_result},
{Types.MikrotikResult, :decode_mikrotik_result,
[
{1, :device_id, :string},
{2, :job_id, :string},
{3, :sentences, {:repeated_message, :decode_mikrotik_sentence}},
{4, :error, :string},
{5, :timestamp, :int64}
], validate: :validate_mikrotik_result, finalize: {:reverse, [:sentences]}},
{Types.MonitoringCheck, :decode_monitoring_check,
[{1, :device_id, :string}, {2, :status, :string}, {3, :response_time_ms, :double}, {4, :timestamp, :int64}],
validate: :validate_monitoring_check},
{Types.LldpTopologyResult, :decode_lldp_topology_result,
[
{1, :device_id, :string},
{2, :job_id, :string},
{3, :local_system_name, :string},
{4, :neighbors, {:repeated_message, :decode_lldp_neighbor}},
{5, :timestamp, :int64}
], validate: :validate_lldp_topology_result, finalize: {:reverse, [:neighbors]}},
{Types.CheckResult, :decode_check_result,
[
{1, :check_id, :string},
{2, :status, :uint},
{3, :output, :string},
{4, :response_time_ms, :double},
{5, :timestamp, :int64}
], validate: :validate_check_result},
{Types.Sensor, :decode_sensor,
[
{1, :id, :string},
{2, :sensor_type, :string},
{3, :oid, :string},
{4, :divisor, :double},
{5, :unit, :string},
{6, :metadata, {:map, :string, :string}}
]},
# ── Server → Agent messages ──────────────────────
{Types.AgentJob, :decode_agent_job,
[
{1, :job_id, :string},
{2, :job_type, {:enum, &Types.job_type_from_int/1}},
{3, :device_id, :string},
{4, :snmp_device, {:message, :decode_snmp_device, :validate_snmp_device}},
{5, :queries, {:repeated_message, :decode_snmp_query, :validate_snmp_query}},
{6, :mikrotik_device, {:message, :decode_mikrotik_device, :validate_mikrotik_device}},
{7, :mikrotik_commands, {:repeated_message, :decode_mikrotik_command, :validate_mikrotik_command}}
], defaults: [job_type: :discover], finalize: {:reverse, [:queries, :mikrotik_commands]}},
{Types.AgentConfig, :decode_agent_config,
[
{1, :version, :string},
{2, :poll_interval_seconds, :uint},
{3, :devices, {:repeated_message, :decode_device}},
{4, :checks, {:repeated_message, :decode_check, :validate_check}}
], finalize: {:reverse, [:devices, :checks]}},
# ── Nested sub-messages ──────────────────────────
{Types.Device, :decode_device,
[
{1, :id, :string},
{2, :name, :string},
{3, :ip_address, :string},
{4, :snmp, {:message, :decode_snmp_config}},
{5, :poll_interval_seconds, :uint},
{6, :sensors, {:repeated_message, :decode_sensor, :validate_sensor}},
{7, :interfaces, {:repeated_message, :decode_interface, :validate_interface}},
{8, :monitoring_enabled, :bool},
{9, :check_interval_seconds, :uint}
], finalize: {:reverse, [:sensors, :interfaces]}},
{Types.SnmpConfig, :decode_snmp_config,
[
{1, :enabled, :bool},
{2, :version, :string},
{3, :community, :string},
{4, :port, :uint},
{5, :transport, :string}
]},
{Types.SensorReading, :decode_sensor_reading,
[{1, :sensor_id, :string}, {2, :value, :double}, {3, :status, :string}, {4, :timestamp, :int64}]},
{Types.InterfaceStat, :decode_interface_stat,
[
{1, :interface_id, :string},
{2, :if_in_octets, :int64},
{3, :if_out_octets, :int64},
{4, :if_in_errors, :int64},
{5, :if_out_errors, :int64},
{6, :if_in_discards, :int64},
{7, :if_out_discards, :int64},
{8, :timestamp, :int64}
]},
{Types.NeighborDiscovery, :decode_neighbor_discovery,
[
{1, :interface_id, :string},
{2, :protocol, :string},
{3, :remote_chassis_id, :string},
{4, :remote_system_name, :string},
{5, :remote_system_description, :string},
{6, :remote_platform, :string},
{7, :remote_port_id, :string},
{8, :remote_port_description, :string},
{9, :remote_address, :string},
{10, :remote_capabilities, {:repeated, :string}},
{11, :timestamp, :int64}
]},
{Types.Interface, :decode_interface, [{1, :id, :string}, {2, :if_index, :uint}, {3, :if_name, :string}]},
{Types.Check, :decode_check,
[
{1, :id, :string},
{2, :check_type, :string},
{3, :interval_seconds, :uint},
{4, :timeout_ms, :uint},
{5, :config, {:oneof, :decode_http_check_config, :http}},
{6, :config, {:oneof, :decode_tcp_check_config, :tcp}},
{7, :config, {:oneof, :decode_dns_check_config, :dns}},
{8, :config, {:oneof, :decode_ssl_check_config, :ssl}}
], defaults: [config: :no_config]},
{Types.HttpCheckConfig, :decode_http_check_config,
[
{1, :url, :string},
{2, :method, :string},
{3, :expected_status, :uint},
{4, :verify_ssl, :bool},
{5, :headers, {:map, :string, :string}},
{6, :body, :string},
{7, :regex, :string},
{8, :follow_redirects, :bool}
]},
{Types.TcpCheckConfig, :decode_tcp_check_config,
[{1, :host, :string}, {2, :port, :uint}, {3, :send, :string}, {4, :expect, :string}]},
{Types.DnsCheckConfig, :decode_dns_check_config,
[{1, :hostname, :string}, {2, :server, :string}, {3, :record_type, :string}, {4, :expected, :string}]},
{Types.SslCheckConfig, :decode_ssl_check_config,
[{1, :host, :string}, {2, :port, :uint}, {3, :warning_days, :uint}]},
{Types.SnmpDevice, :decode_snmp_device,
[
{1, :ip, :string},
{2, :community, :string},
{3, :version, :string},
{4, :port, :uint},
{5, :v3_security_level, :string},
{6, :v3_username, :string},
{7, :v3_auth_protocol, :string},
{8, :v3_auth_password, :string},
{9, :v3_priv_protocol, :string},
{10, :v3_priv_password, :string},
{11, :transport, :string}
], validate: :validate_snmp_device},
{Types.SnmpQuery, :decode_snmp_query,
[{1, :query_type, {:enum, &Types.query_type_from_int/1}}, {2, :oids, {:repeated, :string}}],
validate: :validate_snmp_query, defaults: [query_type: :get], finalize: {:reverse, [:oids]}},
{Types.MikrotikDevice, :decode_mikrotik_device,
[
{1, :ip, :string},
{2, :port, :uint},
{3, :username, :string},
{4, :password, :string},
{5, :use_ssl, :bool},
{6, :ssh_port, :uint}
], validate: :validate_mikrotik_device},
{Types.MikrotikCommand, :decode_mikrotik_command, [{1, :command, :string}, {2, :args, {:map, :string, :string}}],
validate: :validate_mikrotik_command},
{Types.LldpNeighbor, :decode_lldp_neighbor,
[
{1, :neighbor_name, :string},
{2, :local_port, :string},
{3, :remote_port, :string},
{4, :remote_port_id, :string},
{5, :management_addresses, {:repeated, :string}}
], finalize: {:reverse, [:management_addresses]}}
]
end
end