feat(agent): enrich zpool summary with type, scan state, vdev list

This commit is contained in:
Carsten 2026-04-22 17:44:07 +02:00
parent a4f4d3ca51
commit e763ea96bd
3 changed files with 61 additions and 5 deletions

View file

@ -4,6 +4,15 @@ defmodule ProxmoxAgent.Collectors.Zfs do
Delegates shelling out to an injectable runner so tests can supply fixtures.
"""
@type vdev_summary :: %{
name: String.t(),
type: String.t(),
state: String.t(),
read_errors: non_neg_integer(),
write_errors: non_neg_integer(),
checksum_errors: non_neg_integer()
}
@type pool_summary :: %{
name: String.t(),
health: String.t(),
@ -15,7 +24,11 @@ defmodule ProxmoxAgent.Collectors.Zfs do
error_count: non_neg_integer(),
vdev_count: non_neg_integer(),
degraded_vdev_count: non_neg_integer(),
last_scrub_end: String.t() | nil
pool_type: String.t(),
scan_function: String.t() | nil,
scan_state: String.t() | nil,
last_scrub_end: String.t() | nil,
vdevs: [vdev_summary()]
}
@spec collect_pools(keyword()) :: %{pools: [pool_summary()], errors: [map()]}
@ -71,7 +84,8 @@ defmodule ProxmoxAgent.Collectors.Zfs do
defp merge_pools(%{"pools" => list_pools}, %{"pools" => status_pools}) do
Enum.map(list_pools, fn {name, list_info} ->
status_info = Map.get(status_pools, name, %{})
vdevs = Map.get(status_info, "vdevs", %{}) |> Map.values()
raw_vdevs = Map.get(status_info, "vdevs", %{}) |> Map.values()
vdevs = Enum.map(raw_vdevs, &vdev_summary/1)
%{
name: name,
@ -83,12 +97,45 @@ defmodule ProxmoxAgent.Collectors.Zfs do
capacity_percent: Map.get(list_info, "cap", 0),
error_count: to_int(Map.get(status_info, "error_count", "0")),
vdev_count: length(vdevs),
degraded_vdev_count: Enum.count(vdevs, &(&1["state"] != "ONLINE")),
last_scrub_end: get_in(status_info, ["scan", "end_time"])
degraded_vdev_count: Enum.count(vdevs, &(&1.state != "ONLINE")),
pool_type: derive_pool_type(vdevs),
scan_function: get_in(status_info, ["scan", "function"]),
scan_state: get_in(status_info, ["scan", "state"]),
last_scrub_end: get_in(status_info, ["scan", "end_time"]),
vdevs: vdevs
}
end)
end
defp vdev_summary(v) do
%{
name: Map.get(v, "name"),
type: Map.get(v, "vdev_type"),
state: Map.get(v, "state"),
read_errors: to_int(Map.get(v, "read_errors", "0")),
write_errors: to_int(Map.get(v, "write_errors", "0")),
checksum_errors: to_int(Map.get(v, "checksum_errors", "0"))
}
end
@data_vdev_types ~w(mirror raidz1 raidz2 raidz3 disk)
@special_vdev_types ~w(log cache spare dedup special)
defp derive_pool_type(vdevs) do
data_types =
vdevs
|> Enum.map(& &1.type)
|> Enum.reject(&(&1 in @special_vdev_types))
|> Enum.uniq()
case data_types do
[] -> "unknown"
["disk"] -> "stripe"
[t] when t in @data_vdev_types -> t
_ -> "mixed"
end
end
defp summarize_datasets(nil), do: []
defp summarize_datasets(%{"datasets" => datasets}) do