feat(ui): detailed per-pool block with type, capacity bar, scrub state
This commit is contained in:
parent
612091ff1e
commit
f05c20ed0b
2 changed files with 75 additions and 14 deletions
|
|
@ -66,21 +66,35 @@ defmodule ServerWeb.HostDetailLive do
|
||||||
<header><span>ZFS pools</span><span class="mono">{length(pools(@fast))}</span></header>
|
<header><span>ZFS pools</span><span class="mono">{length(pools(@fast))}</span></header>
|
||||||
<div class="body tight">
|
<div class="body tight">
|
||||||
<div :if={pools(@fast) == []} class="empty">No data.</div>
|
<div :if={pools(@fast) == []} class="empty">No data.</div>
|
||||||
<div :for={pool <- pools(@fast)} class="pool-row" style="padding: 0.6rem 0.9rem;">
|
<div :for={pool <- pools(@fast)} class="pool-block">
|
||||||
|
<div class="head">
|
||||||
<div>
|
<div>
|
||||||
<span class="mono" style="color: var(--fg-bright); font-weight: 600;">
|
<span class="mono" style="color: var(--fg-bright); font-weight: 600;">{pool["name"]}</span>
|
||||||
{pool["name"]}
|
<span class="layout">{pool_layout(pool)}</span>
|
||||||
</span>
|
</div>
|
||||||
<span class="badge" style={pool_badge_style(pool["health"])}>{pool["health"]}</span>
|
<span class="badge" style={pool_badge_style(pool["health"])}>{pool["health"]}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="capbar" data-level={capbar_level(pool["capacity_percent"])}>
|
||||||
|
<span style={"width: #{pool["capacity_percent"] || 0}%"}></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sizes">
|
||||||
|
used {format_bytes(pool["allocated_bytes"] || 0)} ·
|
||||||
|
free {format_bytes(pool["free_bytes"] || 0)} ·
|
||||||
|
total {format_bytes(pool["size_bytes"] || 0)}
|
||||||
|
<span class="muted">({pool["capacity_percent"] || 0}%)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="details">
|
<div class="details">
|
||||||
cap {pool["capacity_percent"]}% ·
|
|
||||||
frag {pool["fragmentation_percent"] || 0}% ·
|
frag {pool["fragmentation_percent"] || 0}% ·
|
||||||
err {pool["error_count"] || 0} ·
|
err {pool["error_count"] || 0} ·
|
||||||
vdevs {pool["vdev_count"] || 0} (deg {pool["degraded_vdev_count"] || 0})
|
vdevs {pool["vdev_count"] || 0} (deg {pool["degraded_vdev_count"] || 0}) ·
|
||||||
|
{pool_scrub_line(pool)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="mono muted" style="font-size: 0.75rem; align-self: center; text-align: right;">
|
<div :for={v <- degraded_vdevs(pool)} class="callout err" style="margin-top: 0.4rem;">
|
||||||
scrub<br/>{pool["last_scrub_end"] || "never"}
|
{v["name"]} {v["state"]} · r={v["read_errors"]} w={v["write_errors"]} cksum={v["checksum_errors"]}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -155,6 +169,34 @@ defmodule ServerWeb.HostDetailLive do
|
||||||
defp pool_badge_style("ONLINE"), do: "color: var(--ok);"
|
defp pool_badge_style("ONLINE"), do: "color: var(--ok);"
|
||||||
defp pool_badge_style(_), do: "color: var(--crit);"
|
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(nil), do: "—"
|
||||||
defp sys_line(%{payload: p}) do
|
defp sys_line(%{payload: p}) do
|
||||||
get_in(p, ["system_info", "pve_version"]) || "—"
|
get_in(p, ["system_info", "pve_version"]) || "—"
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,22 @@ defmodule ServerWeb.HostDetailLiveTest do
|
||||||
%{
|
%{
|
||||||
"name" => "rpool",
|
"name" => "rpool",
|
||||||
"health" => "ONLINE",
|
"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,
|
"capacity_percent" => 40,
|
||||||
|
"fragmentation_percent" => 17,
|
||||||
"error_count" => 0,
|
"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 =~ "nginx"
|
||||||
assert html =~ "rpool/data"
|
assert html =~ "rpool/data"
|
||||||
assert html =~ "local"
|
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
|
end
|
||||||
|
|
||||||
test "404 for unknown host", %{conn: conn} do
|
test "404 for unknown host", %{conn: conn} do
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue