defmodule ToweropsWeb.Api.V1.CheckResultsController do @moduledoc "API controller for device checks, metrics, and interfaces." use ToweropsWeb, :controller alias Towerops.Devices alias Towerops.Monitoring alias Towerops.Snmp def checks(conn, %{"device_id" => device_id}) do organization_id = conn.assigns.current_organization_id case get_org_device(device_id, organization_id) do {:ok, device} -> checks = organization_id |> Monitoring.list_checks(device_id: device.id) |> Enum.map(fn check -> latest = Monitoring.get_latest_check_result(check.id) %{ id: check.id, name: check.name, check_type: check.check_type, enabled: check.enabled, interval_seconds: check.interval_seconds, latest_status: latest && latest.status, latest_value: latest && latest.value, latest_checked_at: latest && latest.checked_at } end) json(conn, %{data: checks}) {:error, status} -> conn |> put_status(status) |> json(%{error: "Device not found"}) end end def metrics(conn, %{"device_id" => device_id} = params) do organization_id = conn.assigns.current_organization_id case get_org_device(device_id, organization_id) do {:ok, device} -> check_type = params["sensor_type"] || params["check_type"] hours = parse_int(params["hours"], 24) from_time = DateTime.add(DateTime.utc_now(), -hours * 3600, :second) checks = Monitoring.list_checks(organization_id, device_id: device.id, check_type: check_type) metrics = Enum.map(checks, fn check -> results = Monitoring.get_check_results(check.id, from: from_time) %{ check_id: check.id, check_name: check.name, check_type: check.check_type, data: Enum.map(results, fn r -> %{timestamp: r.checked_at, value: r.value, status: r.status} end) } end) json(conn, %{data: metrics}) {:error, status} -> conn |> put_status(status) |> json(%{error: "Device not found"}) end end def interfaces(conn, %{"device_id" => device_id}) do organization_id = conn.assigns.current_organization_id case get_org_device(device_id, organization_id) do {:ok, device} -> device = Towerops.Repo.preload(device, :snmp_device) interfaces = case device.snmp_device do nil -> [] snmp_device -> format_interfaces(snmp_device) end json(conn, %{data: interfaces}) {:error, status} -> conn |> put_status(status) |> json(%{error: "Device not found"}) end end defp get_org_device(device_id, organization_id) do device = Devices.get_device!(device_id) if device.organization_id == organization_id do {:ok, device} else {:error, :forbidden} end rescue Ecto.NoResultsError -> {:error, :not_found} end defp parse_int(nil, default), do: default defp parse_int(val, default) when is_binary(val) do case Integer.parse(val) do {n, _} -> n :error -> default end end defp parse_int(val, _default) when is_integer(val), do: val defp format_interfaces(snmp_device) do snmp_device.id |> Snmp.list_interfaces() |> Enum.map(fn iface -> %{ id: iface.id, if_index: iface.if_index, if_name: iface.if_name, if_descr: iface.if_descr, if_alias: iface.if_alias, if_speed: iface.if_speed, if_admin_status: iface.if_admin_status, if_oper_status: iface.if_oper_status, monitored: iface.monitored } end) end end