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

+33 -18
View File
@@ -49,6 +49,28 @@ function usageBar(used: number | null | undefined, total: number | null | undefi
);
}
function DisksTable({ disks }: { disks: { mount: string; sizeBytes: number; usedBytes: number }[] }) {
if (disks.length === 0) return null;
return (
<div className="mt-3">
<div className="text-secondary small mb-1">Disks</div>
<table className="table table-sm table-vcenter mb-0">
<tbody>
{disks.map((d) => (
<tr key={d.mount}>
<td>{d.mount}</td>
<td>
{formatBytes(d.usedBytes)} / {formatBytes(d.sizeBytes)}
{usageBar(d.usedBytes, d.sizeBytes)}
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
const emptyTaskForm = {
scheduleType: "docker" as ScheduleType,
name: "",
@@ -570,11 +592,21 @@ export default function ServerDetail({ user }: { user: CurrentUser }) {
<div>{formatUptime(hardware.uptime)}</div>
</div>
</div>
<DisksTable disks={hardware.disks ?? []} />
{hardware.guestAgentAvailable === false && (
<div className="text-secondary small mt-3">
IP addresses unavailable — the QEMU guest agent isn't installed or running on this VM.
</div>
)}
{server.proxmoxGuestType === "qemu" &&
hardware.guestAgentAvailable !== false &&
!hardware.disks?.length &&
hardware.status === "running" && (
<div className="text-secondary small mt-3">
Per-disk usage unavailable — the QEMU guest agent doesn't support reporting it (or is still
starting up).
</div>
)}
</>
)}
{hardware.source === "agent" && (
@@ -601,24 +633,7 @@ export default function ServerDetail({ user }: { user: CurrentUser }) {
{usageBar(hardware.memUsedBytes, hardware.memTotalBytes)}
</div>
</div>
{hardware.disks && hardware.disks.length > 0 && (
<div className="mt-3">
<div className="text-secondary small mb-1">Disks</div>
<table className="table table-sm table-vcenter mb-0">
<tbody>
{hardware.disks.map((d) => (
<tr key={d.mount}>
<td>{d.mount}</td>
<td>
{formatBytes(d.usedBytes)} / {formatBytes(d.sizeBytes)}
{usageBar(d.usedBytes, d.sizeBytes)}
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
<DisksTable disks={hardware.disks ?? []} />
</>
)}