Show per-disk usage for Proxmox-linked servers too, not just a total

The agent path showed a per-mount usage table; a Proxmox-linked server
only ever got a single "Disk (allocated)" figure, since that's all the
VM/LXC config alone can tell you -- it's the attached disk's declared
size, not how full it actually is inside the guest. Fix the actual gap
instead of just matching the display: fetch real usage where Proxmox
can see it.

adapter.getGuestDetail() gained a `disks` field (same {mount,
sizeBytes, usedBytes} shape the agent already reports, so the frontend
renders both identically):
- LXC: the host can read straight into the container's root
  filesystem, no agent needed -- status/current's disk/maxdisk fields
  are real usage, not just allocation.
- QEMU: the hypervisor can't see inside a virtual disk at all without
  help, so this calls the QEMU guest agent's get-fsinfo command (same
  "gracefully degrade if the agent's missing/older" tolerance already
  used for its IP-address lookup, and independent of it -- one
  command failing doesn't take out the other). Pseudo-filesystems
  (tmpfs, etc.) are filtered out by checking for a non-empty backing
  `disk` array, the common convention for this endpoint.

Extracted the disks-table JSX (previously only in the agent branch)
into a shared DisksTable component and used it in both branches, and
added a note explaining an empty result when a running QEMU VM's
guest agent doesn't support get-fsinfo (an older agent version).

Verified against a mock Proxmox API over real TLS: an LXC's root
usage, a QEMU VM's real fsinfo mounts (with the disk-less tmpfs entry
correctly filtered), and a QEMU VM whose get-fsinfo fails outright --
confirming that degrades to an empty disks list without throwing and
without affecting the separate network-get-interfaces result.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
bobbanandClaude Sonnet 5 committed 2026-09-18 23:32:46 +02:00
1 parent 08a984719f
commit 81e55fc792
4 files changed
+77 -21

No files matched your search

+38 -1
View File
@@ -50,9 +50,17 @@ export interface ProxmoxGuestDetail {
memoryBytes: number | null;
memUsedBytes: number | null;
diskBytes: number | null;
/**
* Per-mount usage where Proxmox can actually see it: the LXC root
* filesystem (host can see straight into it, no agent needed) or, for a
* QEMU VM, whatever the QEMU guest agent reports from inside the guest.
* Empty when neither is available (e.g. no guest agent) — diskBytes above
* (allocated size) is still shown in that case, just not usage.
*/
disks: { mount: string; sizeBytes: number; usedBytes: number }[];
uptime: number | null;
ipAddresses: string[];
/** QEMU only — false when the guest agent call failed (not installed/running). Always true for LXC (IPs read from config, no agent needed). */
/** QEMU only — false when the guest agent call failed (not installed/running). Always true for LXC (IPs/disk usage read directly, no agent needed). */
guestAgentAvailable: boolean;
}
@@ -228,6 +236,7 @@ export function createProxmoxAdapter(config: ProxmoxConfig): ProxmoxAdapter {
let cpuCores: number | null = null;
let diskBytes: number | null = null;
const ipAddresses: string[] = [];
const disks: { mount: string; sizeBytes: number; usedBytes: number }[] = [];
let guestAgentAvailable = true;
if (type === "lxc") {
@@ -241,6 +250,12 @@ export function createProxmoxAdapter(config: ProxmoxConfig): ProxmoxAdapter {
if (ip) ipAddresses.push(ip);
}
}
// The host can see straight into an LXC's root filesystem — no agent
// needed — but the API only exposes the root mount this way, not any
// additional mount points configured on the container.
if (typeof status.disk === "number" && typeof status.maxdisk === "number" && status.maxdisk > 0) {
disks.push({ mount: "/", sizeBytes: status.maxdisk, usedBytes: status.disk });
}
} else {
const sockets = typeof config.sockets === "number" ? config.sockets : 1;
cpuCores = typeof config.cores === "number" ? config.cores * sockets : null;
@@ -271,6 +286,27 @@ export function createProxmoxAdapter(config: ProxmoxConfig): ProxmoxAdapter {
} catch {
guestAgentAvailable = false;
}
// Unlike LXC, the hypervisor can't see inside a QEMU disk image at
// all — actual filesystem usage only exists if the guest agent
// reports it from inside the guest, same availability caveat as the
// network call above (a separate try/catch since one agent command
// failing, e.g. on an older guest agent version, shouldn't hide IPs
// the other command already got, or vice versa).
try {
const fsData = await api("GET", `/nodes/${node}/qemu/${vmid}/agent/get-fsinfo`);
for (const fs of fsData?.result ?? []) {
// Entries with no backing "disk" (tmpfs, proc, overlay, snap loop
// mounts, ...) aren't real storage — skip them, same convention
// widely used for this endpoint.
if (!Array.isArray(fs.disk) || fs.disk.length === 0) continue;
if (typeof fs["total-bytes"] !== "number" || typeof fs["used-bytes"] !== "number") continue;
disks.push({ mount: fs.mountpoint ?? fs.name ?? "?", sizeBytes: fs["total-bytes"], usedBytes: fs["used-bytes"] });
}
} catch {
// Guest agent unavailable or too old to support get-fsinfo — leave
// disks empty, diskBytes (allocated size) is still shown.
}
}
const memoryBytes = typeof config.memory === "number" ? config.memory * 1024 * 1024 : null;
@@ -286,6 +322,7 @@ export function createProxmoxAdapter(config: ProxmoxConfig): ProxmoxAdapter {
memoryBytes,
memUsedBytes: typeof status.mem === "number" ? status.mem : null,
diskBytes,
disks,
uptime: typeof status.uptime === "number" ? status.uptime : null,
ipAddresses,
guestAgentAvailable: type === "qemu" ? guestAgentAvailable : true,