towerops/lib/towerops/proto/tuple_encode.ex
2026-03-29 11:03:20 -05:00

507 lines
16 KiB
Elixir

defmodule Towerops.Proto.TupleEncode do
@moduledoc """
Compatibility wrapper for encoding protobuf messages.
Accepts tagged tuples and converts them to Elixir structs for encoding.
"""
alias Towerops.Proto.Encode
alias Towerops.Proto.Types
# HeartbeatMetadata
def encode_heartbeat_metadata({:heartbeat_metadata, version, hostname, uptime}) do
Encode.encode_heartbeat_metadata(%Types.HeartbeatMetadata{
version: version,
hostname: hostname,
uptime_seconds: uptime
})
end
# HeartbeatResponse
def encode_heartbeat_response({:heartbeat_response, status}) do
Encode.encode_heartbeat_response(%Types.HeartbeatResponse{status: status})
end
# SnmpConfig
def encode_snmp_config({:snmp_config, enabled, version, community, port, transport}) do
Encode.encode_snmp_config(%Types.SnmpConfig{
enabled: enabled,
version: version,
community: community,
port: port,
transport: transport
})
end
# Sensor
def encode_sensor({:sensor, id, type, oid, divisor, unit, metadata}) do
Encode.encode_sensor(%Types.Sensor{
id: id,
sensor_type: type,
oid: oid,
divisor: divisor,
unit: unit,
metadata: metadata
})
end
# Interface
def encode_interface({:interface, id, if_index, if_name}) do
Encode.encode_interface(%Types.Interface{
id: id,
if_index: if_index,
if_name: if_name
})
end
# Device (complex with nested structures)
def encode_device({:device, id, name, ip, snmp, poll_interval, sensors, interfaces, monitoring_enabled, check_interval}) do
snmp_struct =
case snmp do
:none ->
nil
{:some, {:snmp_config, enabled, version, community, port, transport}} ->
%Types.SnmpConfig{
enabled: enabled,
version: version,
community: community,
port: port,
transport: transport
}
end
sensor_structs =
Enum.map(sensors, fn {:sensor, id, type, oid, divisor, unit, metadata} ->
%Types.Sensor{
id: id,
sensor_type: type,
oid: oid,
divisor: divisor,
unit: unit,
metadata: metadata
}
end)
interface_structs =
Enum.map(interfaces, fn {:interface, id, if_index, if_name} ->
%Types.Interface{id: id, if_index: if_index, if_name: if_name}
end)
Encode.encode_device(%Types.Device{
id: id,
name: name,
ip_address: ip,
snmp: snmp_struct,
poll_interval_seconds: poll_interval,
sensors: sensor_structs,
interfaces: interface_structs,
monitoring_enabled: monitoring_enabled,
check_interval_seconds: check_interval
})
end
# AgentConfig
def encode_agent_config({:agent_config, version, poll_interval, devices, checks}) do
device_structs = Enum.map(devices, &tuple_device_to_types/1)
check_structs = Enum.map(checks, &tuple_check_to_types/1)
Encode.encode_agent_config(%Types.AgentConfig{
version: version,
poll_interval_seconds: poll_interval,
devices: device_structs,
checks: check_structs
})
end
# SensorReading
def encode_sensor_reading({:sensor_reading, sensor_id, value, status, timestamp}) do
Encode.encode_sensor_reading(%Types.SensorReading{
sensor_id: sensor_id,
value: value,
status: status,
timestamp: timestamp
})
end
# InterfaceStat
def encode_interface_stat(
{:interface_stat, interface_id, if_in_octets, if_out_octets, if_in_errors, if_out_errors, if_in_discards,
if_out_discards, timestamp}
) do
Encode.encode_interface_stat(%Types.InterfaceStat{
interface_id: interface_id,
if_in_octets: if_in_octets,
if_out_octets: if_out_octets,
if_in_errors: if_in_errors,
if_out_errors: if_out_errors,
if_in_discards: if_in_discards,
if_out_discards: if_out_discards,
timestamp: timestamp
})
end
# NeighborDiscovery
def encode_neighbor_discovery(
{:neighbor_discovery, interface_id, protocol, chassis_id, sys_name, sys_desc, platform, port_id, port_desc,
address, capabilities, timestamp}
) do
Encode.encode_neighbor_discovery(%Types.NeighborDiscovery{
interface_id: interface_id,
protocol: protocol,
remote_chassis_id: chassis_id,
remote_system_name: sys_name,
remote_system_description: sys_desc,
remote_platform: platform,
remote_port_id: port_id,
remote_port_description: port_desc,
remote_address: address,
remote_capabilities: capabilities,
timestamp: timestamp
})
end
# MonitoringCheck
def encode_monitoring_check({:monitoring_check, device_id, status, response_time_ms, timestamp}) do
Encode.encode_monitoring_check(%Types.MonitoringCheck{
device_id: device_id,
status: status,
response_time_ms: response_time_ms,
timestamp: timestamp
})
end
# Metric
def encode_metric(tuple_metric) do
metric_struct =
case tuple_metric do
{:sensor_reading_metric, {:sensor_reading, sensor_id, value, status, timestamp}} ->
{:sensor_reading,
%Types.SensorReading{
sensor_id: sensor_id,
value: value,
status: status,
timestamp: timestamp
}}
{:interface_stat_metric,
{:interface_stat, interface_id, if_in_octets, if_out_octets, if_in_errors, if_out_errors, if_in_discards,
if_out_discards, timestamp}} ->
{:interface_stat,
%Types.InterfaceStat{
interface_id: interface_id,
if_in_octets: if_in_octets,
if_out_octets: if_out_octets,
if_in_errors: if_in_errors,
if_out_errors: if_out_errors,
if_in_discards: if_in_discards,
if_out_discards: if_out_discards,
timestamp: timestamp
}}
{:neighbor_discovery_metric,
{:neighbor_discovery, interface_id, protocol, chassis_id, sys_name, sys_desc, platform, port_id, port_desc,
address, capabilities, timestamp}} ->
{:neighbor_discovery,
%Types.NeighborDiscovery{
interface_id: interface_id,
protocol: protocol,
remote_chassis_id: chassis_id,
remote_system_name: sys_name,
remote_system_description: sys_desc,
remote_platform: platform,
remote_port_id: port_id,
remote_port_description: port_desc,
remote_address: address,
remote_capabilities: capabilities,
timestamp: timestamp
}}
{:monitoring_check_metric, {:monitoring_check, device_id, status, response_time_ms, timestamp}} ->
{:monitoring_check,
%Types.MonitoringCheck{
device_id: device_id,
status: status,
response_time_ms: response_time_ms,
timestamp: timestamp
}}
{:check_result_metric, {:check_result, check_id, status, output, response_time_ms, timestamp}} ->
{:check_result,
%Types.CheckResult{
check_id: check_id,
status: status,
output: output,
response_time_ms: response_time_ms,
timestamp: timestamp
}}
end
Encode.encode_metric(metric_struct)
end
# MetricBatch
def encode_metric_batch({:metric_batch, tuple_metrics}) do
metric_structs = Enum.map(tuple_metrics, &tuple_metric_to_types/1)
Encode.encode_metric_batch(%Types.MetricBatch{metrics: metric_structs})
end
# CheckResult
def encode_check_result({:check_result, check_id, status, output, response_time_ms, timestamp}) do
Encode.encode_check_result(%Types.CheckResult{
check_id: check_id,
status: status,
output: output,
response_time_ms: response_time_ms,
timestamp: timestamp
})
end
# CheckList
def encode_check_list({:check_list, tuple_checks}) do
check_structs = Enum.map(tuple_checks, &tuple_check_to_types/1)
Encode.encode_check_list(%Types.CheckList{checks: check_structs})
end
# SnmpDevice
def encode_snmp_device(
{:snmp_device, ip, community, version, port, v3_security_level, v3_username, v3_auth_protocol, v3_auth_password,
v3_priv_protocol, v3_priv_password, transport}
) do
Encode.encode_snmp_device(%Types.SnmpDevice{
ip: ip,
community: community,
version: version,
port: port,
v3_security_level: v3_security_level,
v3_username: v3_username,
v3_auth_protocol: v3_auth_protocol,
v3_auth_password: v3_auth_password,
v3_priv_protocol: v3_priv_protocol,
v3_priv_password: v3_priv_password,
transport: transport
})
end
# SnmpQuery
def encode_snmp_query({:snmp_query, query_type, oids}) do
Encode.encode_snmp_query(%Types.SnmpQuery{
query_type: query_type,
oids: oids
})
end
# AgentJobList
def encode_agent_job_list({:agent_job_list, tuple_jobs}) do
job_structs = Enum.map(tuple_jobs, &tuple_job_to_types/1)
Encode.encode_agent_job_list(%Types.AgentJobList{jobs: job_structs})
end
# AgentJob
def encode_agent_job(tuple_job) do
job_struct = tuple_job_to_types(tuple_job)
Encode.encode_agent_job(job_struct)
end
# AgentError
def encode_agent_error({:agent_error, device_id, job_id, message, timestamp}) do
Encode.encode_agent_error(%Types.AgentError{
device_id: device_id,
job_id: job_id,
message: message,
timestamp: timestamp
})
end
# CredentialTestResult
def encode_credential_test_result(
{:credential_test_result, test_id, success, error_message, system_description, timestamp}
) do
Encode.encode_credential_test_result(%Types.CredentialTestResult{
test_id: test_id,
success: success,
error_message: error_message,
system_description: system_description,
timestamp: timestamp
})
end
# MikrotikResult
def encode_mikrotik_result({:mikrotik_result, device_id, job_id, sentences, error, timestamp}) do
sentence_structs =
Enum.map(sentences, fn {:mikrotik_sentence, attrs} ->
%Types.MikrotikSentence{attributes: attrs}
end)
Encode.encode_mikrotik_result(%Types.MikrotikResult{
device_id: device_id,
job_id: job_id,
sentences: sentence_structs,
error: error,
timestamp: timestamp
})
end
# Helper functions for complex conversions
defp tuple_metric_to_types(tuple_metric) do
case tuple_metric do
{:sensor_reading_metric, {:sensor_reading, sensor_id, value, status, timestamp}} ->
{:sensor_reading,
%Types.SensorReading{
sensor_id: sensor_id,
value: value,
status: status,
timestamp: timestamp
}}
{:interface_stat_metric,
{:interface_stat, interface_id, if_in_octets, if_out_octets, if_in_errors, if_out_errors, if_in_discards,
if_out_discards, timestamp}} ->
{:interface_stat,
%Types.InterfaceStat{
interface_id: interface_id,
if_in_octets: if_in_octets,
if_out_octets: if_out_octets,
if_in_errors: if_in_errors,
if_out_errors: if_out_errors,
if_in_discards: if_in_discards,
if_out_discards: if_out_discards,
timestamp: timestamp
}}
{:neighbor_discovery_metric,
{:neighbor_discovery, interface_id, protocol, chassis_id, sys_name, sys_desc, platform, port_id, port_desc,
address, capabilities, timestamp}} ->
{:neighbor_discovery,
%Types.NeighborDiscovery{
interface_id: interface_id,
protocol: protocol,
remote_chassis_id: chassis_id,
remote_system_name: sys_name,
remote_system_description: sys_desc,
remote_platform: platform,
remote_port_id: port_id,
remote_port_description: port_desc,
remote_address: address,
remote_capabilities: capabilities,
timestamp: timestamp
}}
end
end
defp tuple_check_to_types({:check, id, check_type, interval_seconds, timeout_ms, config}) do
config_struct =
case config do
{:http_config,
{:http_check_config, url, method, expected_status, verify_ssl, headers, body, regex, follow_redirects}} ->
{:http,
%Types.HttpCheckConfig{
url: url,
method: method,
expected_status: expected_status,
verify_ssl: verify_ssl,
headers: headers,
body: body,
regex: regex,
follow_redirects: follow_redirects
}}
{:tcp_config, {:tcp_check_config, host, port, send, expect}} ->
{:tcp, %Types.TcpCheckConfig{host: host, port: port, send: send, expect: expect}}
{:dns_config, {:dns_check_config, hostname, server, record_type, expected}} ->
{:dns,
%Types.DnsCheckConfig{
hostname: hostname,
server: server,
record_type: record_type,
expected: expected
}}
{:ssl_config, {:ssl_check_config, host, port, warning_days}} ->
{:ssl, %Types.SslCheckConfig{host: host, port: port, warning_days: warning_days}}
:no_config ->
nil
end
%Types.Check{
id: id,
check_type: check_type,
interval_seconds: interval_seconds,
timeout_ms: timeout_ms,
config: config_struct
}
end
defp tuple_device_to_types(tuple_device) do
# Stub - implement if needed
tuple_device
end
defp tuple_job_to_types(
{:agent_job, job_id, job_type, device_id, snmp_device, queries, mikrotik_device, mikrotik_commands}
) do
snmp_device_struct =
case snmp_device do
:none ->
nil
{
:some,
# credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
{:snmp_device, ip, community, version, port, v3_security_level, v3_username, v3_auth_protocol, v3_auth_password,
v3_priv_protocol, v3_priv_password, transport}
} ->
%Types.SnmpDevice{
ip: ip,
community: community,
version: version,
port: port,
v3_security_level: v3_security_level,
v3_username: v3_username,
v3_auth_protocol: v3_auth_protocol,
v3_auth_password: v3_auth_password,
v3_priv_protocol: v3_priv_protocol,
v3_priv_password: v3_priv_password,
transport: transport
}
end
query_structs =
Enum.map(queries, fn {:snmp_query, query_type, oids} ->
%Types.SnmpQuery{query_type: query_type, oids: oids}
end)
mikrotik_device_struct =
case mikrotik_device do
:none ->
nil
{:some, {:mikrotik_device, ip, port, username, password, use_ssl, ssh_port}} ->
%Types.MikrotikDevice{
ip: ip,
port: port,
username: username,
password: password,
use_ssl: use_ssl,
ssh_port: ssh_port
}
end
mikrotik_command_structs =
Enum.map(mikrotik_commands, fn {:mikrotik_command, command, args} ->
%Types.MikrotikCommand{command: command, args: args}
end)
%Types.AgentJob{
job_id: job_id,
job_type: job_type,
device_id: device_id,
snmp_device: snmp_device_struct,
queries: query_structs,
mikrotik_device: mikrotik_device_struct,
mikrotik_commands: mikrotik_command_structs
}
end
end