defmodule ProxmoxAgent.Collectors.SystemInfo do @moduledoc "Slow-path system metadata: pveversion, zfs version, apt upgradable count." @spec collect(keyword()) :: %{ pve_version: String.t() | nil, zfs_version: String.t() | nil, pending_updates: non_neg_integer(), agent_version: String.t(), errors: [map()] } def collect(opts \\ []) do runner = Keyword.get(opts, :runner, &ProxmoxAgent.Shell.run/2) {pve, e1} = trim_output(runner.("pveversion", []), :pveversion) {zfs, e2} = trim_output(runner.("zfs", ["--version"]), :zfs_version) {apt, e3} = runner.("apt", ["list", "--upgradable"]) |> count_upgrades() %{ pve_version: pve, zfs_version: zfs, pending_updates: apt, agent_version: ProxmoxAgent.version(), errors: Enum.filter([e1, e2, e3], & &1) } end defp trim_output({:ok, text}, _tag), do: {String.trim(text) |> first_line(), nil} defp trim_output({:error, reason}, tag), do: {nil, %{tag: Atom.to_string(tag), message: inspect(reason)}} defp first_line(str), do: str |> String.split("\n", parts: 2) |> hd() defp count_upgrades({:ok, text}) do count = text |> String.split("\n", trim: true) |> Enum.count(&String.contains?(&1, "[upgradable")) {count, nil} end defp count_upgrades({:error, reason}), do: {0, %{tag: "apt_upgradable", message: inspect(reason)}} end