Add live VM CPU and memory usage via virsh domstats
Parse domstats --cpu-total --balloon for all VMs in a single call. Track CPU time deltas between samples to compute per-VM CPU %. Compute guest memory usage from balloon stats (available - unused). Split VM caching: base info (dominfo) 30s TTL, live stats 5s TTL. UI shows CPU % column (color-coded) and memory used/total with bars. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -153,6 +153,13 @@
|
||||
.vm-state.shut-off { background: #484f58; color: #c9d1d9; }
|
||||
.vm-state.other { background: #d29922; color: #fff; }
|
||||
.vm-none { color: #484f58; font-style: italic; padding: 16px; }
|
||||
.vm-mem-bar {
|
||||
display: inline-block; width: 60px; height: 8px; background: #21262d;
|
||||
border-radius: 4px; overflow: hidden; vertical-align: middle; margin-left: 6px;
|
||||
}
|
||||
.vm-mem-fill { height: 100%; border-radius: 4px; transition: width 0.3s; }
|
||||
.vm-table td.vm-cpu { font-weight: bold; }
|
||||
.vm-table td.vm-mem { white-space: nowrap; }
|
||||
|
||||
@media (max-width: 700px) {
|
||||
body { padding: 12px; }
|
||||
@@ -261,7 +268,15 @@ function renderDashboard(servers) {
|
||||
'<div class="card-stat-value">' + srv.uptime.human + '</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
(vmCount > 0 ? '<div style="margin-top:10px;font-size:12px;color:#8b949e">VMs: ' + runningVms + ' running / ' + vmCount + ' total</div>' : '') +
|
||||
(vmCount > 0 ? (function() {
|
||||
const running = (srv.vms || []).filter(v => v.state === 'running');
|
||||
const vmMemUsed = running.reduce((s, v) => s + (v.memory_used_mb || 0), 0);
|
||||
const vmMemTotal = running.reduce((s, v) => s + (v.memory_total_mb || v.memory_mb || 0), 0);
|
||||
return '<div style="margin-top:10px;font-size:12px;color:#8b949e">' +
|
||||
'VMs: ' + runningVms + ' running / ' + vmCount + ' total' +
|
||||
(vmMemTotal > 0 ? ' · Mem: ' + formatMB(vmMemUsed) + ' / ' + formatMB(vmMemTotal) : '') +
|
||||
'</div>';
|
||||
})() : '') +
|
||||
'</div>';
|
||||
}
|
||||
html += '</div>';
|
||||
@@ -338,14 +353,24 @@ function renderDetail(srv) {
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
html += '<table class="vm-table"><thead><tr>' +
|
||||
'<th>Name</th><th>State</th><th>vCPUs</th><th>RAM</th><th>Autostart</th>' +
|
||||
'<th>Name</th><th>State</th><th>CPU</th><th>Memory</th><th>vCPUs</th><th>Autostart</th>' +
|
||||
'</tr></thead><tbody>';
|
||||
for (const vm of sorted) {
|
||||
const isRunning = vm.state === 'running';
|
||||
const cpuPct = vm.cpu_percent || 0;
|
||||
const memUsed = vm.memory_used_mb || 0;
|
||||
const memTotal = vm.memory_total_mb || vm.memory_mb || 0;
|
||||
const memPct = memTotal > 0 ? (memUsed / memTotal * 100) : 0;
|
||||
html += '<tr>' +
|
||||
'<td>' + vm.name + '</td>' +
|
||||
'<td><span class="vm-state ' + vmStateClass(vm.state) + '">' + vm.state + '</span></td>' +
|
||||
'<td class="vm-cpu" style="color:' + (isRunning ? usageColor(cpuPct) : '#484f58') + '">' + (isRunning ? cpuPct + '%' : '—') + '</td>' +
|
||||
'<td class="vm-mem">' + (isRunning && memTotal > 0 ?
|
||||
formatMB(memUsed) + ' / ' + formatMB(memTotal) +
|
||||
'<div class="vm-mem-bar"><div class="vm-mem-fill" style="width:' + memPct + '%;background:' + usageColor(memPct) + '"></div></div>'
|
||||
: (isRunning ? formatMB(vm.memory_mb) : '—')) +
|
||||
'</td>' +
|
||||
'<td>' + vm.vcpus + '</td>' +
|
||||
'<td>' + formatMB(vm.memory_mb) + '</td>' +
|
||||
'<td>' + (vm.autostart ? 'yes' : 'no') + '</td>' +
|
||||
'</tr>';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user