- cap {pool["capacity_percent"]}% ·
- frag {pool["fragmentation_percent"] || 0}% ·
- err {pool["error_count"] || 0} ·
- vdevs {pool["vdev_count"] || 0} (deg {pool["degraded_vdev_count"] || 0})
+
+
+
+ {pool["name"]}
+ {pool_layout(pool)}
+
{pool["health"]}
-
- scrub
{pool["last_scrub_end"] || "never"}
+
+
+
+
+
+
+ used {format_bytes(pool["allocated_bytes"] || 0)} ·
+ free {format_bytes(pool["free_bytes"] || 0)} ·
+ total {format_bytes(pool["size_bytes"] || 0)}
+ ({pool["capacity_percent"] || 0}%)
+
+
+
+ frag {pool["fragmentation_percent"] || 0}% ·
+ err {pool["error_count"] || 0} ·
+ vdevs {pool["vdev_count"] || 0} (deg {pool["degraded_vdev_count"] || 0}) ·
+ {pool_scrub_line(pool)}
+
+
+
+ {v["name"]} {v["state"]} · r={v["read_errors"]} w={v["write_errors"]} cksum={v["checksum_errors"]}
@@ -155,6 +169,34 @@ defmodule ServerWeb.HostDetailLive do
defp pool_badge_style("ONLINE"), do: "color: var(--ok);"
defp pool_badge_style(_), do: "color: var(--crit);"
+ defp pool_layout(pool) do
+ case pool["pool_type"] do
+ nil -> "—"
+ "" -> "—"
+ t -> t
+ end
+ end
+
+ defp capbar_level(cap) when is_number(cap) and cap >= 90, do: "crit"
+ defp capbar_level(cap) when is_number(cap) and cap >= 80, do: "warn"
+ defp capbar_level(_), do: "ok"
+
+ defp pool_scrub_line(%{"scan_state" => "SCANNING"}), do: "scrub scanning"
+
+ defp pool_scrub_line(%{"scan_state" => "FINISHED", "last_scrub_end" => end_time})
+ when is_binary(end_time) and end_time != "",
+ do: "scrub #{end_time}"
+
+ defp pool_scrub_line(%{"last_scrub_end" => end_time}) when is_binary(end_time) and end_time != "",
+ do: "scrub #{end_time}"
+
+ defp pool_scrub_line(_), do: "scrub never"
+
+ defp degraded_vdevs(pool) do
+ (pool["vdevs"] || [])
+ |> Enum.filter(fn v -> Map.get(v, "state") not in [nil, "ONLINE"] end)
+ end
+
defp sys_line(nil), do: "—"
defp sys_line(%{payload: p}) do
get_in(p, ["system_info", "pve_version"]) || "—"
diff --git a/server/test/server_web/live/host_detail_live_test.exs b/server/test/server_web/live/host_detail_live_test.exs
index a561f88..cec01b6 100644
--- a/server/test/server_web/live/host_detail_live_test.exs
+++ b/server/test/server_web/live/host_detail_live_test.exs
@@ -17,9 +17,22 @@ defmodule ServerWeb.HostDetailLiveTest do
%{
"name" => "rpool",
"health" => "ONLINE",
+ "pool_type" => "mirror",
+ "size_bytes" => 500_000_000_000,
+ "allocated_bytes" => 200_000_000_000,
+ "free_bytes" => 300_000_000_000,
"capacity_percent" => 40,
+ "fragmentation_percent" => 17,
"error_count" => 0,
- "last_scrub_end" => "Sat Apr 19 02:00:00 2026"
+ "vdev_count" => 1,
+ "degraded_vdev_count" => 0,
+ "scan_function" => "scrub",
+ "scan_state" => "FINISHED",
+ "last_scrub_end" => "Sat Apr 19 02:00:00 2026",
+ "vdevs" => [
+ %{"name" => "mirror-0", "type" => "mirror", "state" => "ONLINE",
+ "read_errors" => 0, "write_errors" => 0, "checksum_errors" => 0}
+ ]
}
]
},
@@ -75,6 +88,12 @@ defmodule ServerWeb.HostDetailLiveTest do
assert html =~ "nginx"
assert html =~ "rpool/data"
assert html =~ "local"
+ assert html =~ "mirror"
+ assert html =~ "465.7 GB" # size_bytes formatted
+ assert html =~ "186.3 GB" # allocated_bytes formatted
+ assert html =~ "279.4 GB" # free_bytes formatted
+ assert html =~ "capbar"
+ assert html =~ "scrub"
end
test "404 for unknown host", %{conn: conn} do