feat(metrics): real-time VM disk fill, IOPS and network I/O

Add live per-disk usage, disk read/write and network in/out rates to the
instance Overview tab, the instances list and the dashboard top-loaded list.

Backend:
- get_vm_fsinfo() pulls per-filesystem usage via QEMU guest agent
- metrics_broadcaster computes I/O rates (bytes/sec) from cumulative counters
- instances-list loop now refreshes each running VM via status/current
  (1s-granular) instead of cluster/resources (10s pvestatd), with bounded
  concurrency, and paces both realtime loops to a 1s cadence

Frontend:
- OverviewTab: per-disk cards, Network I/O and I/O-rate cards via WS
- InstancesPage: Net and Disk I/O columns; Disk% shown for LXC only
- DashboardPage: top-loaded list overlaid with realtime metrics

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-20 08:26:29 +05:00
parent 6fc617d3e5
commit a507e3c4bc
9 changed files with 480 additions and 38 deletions

View File

@@ -168,6 +168,37 @@ class VmMixin:
"""Получить статистику VM (CPU, память, диск)"""
return self.get_vm_status(node, vmid)
def get_vm_fsinfo(self, node: str, vmid: int) -> list:
"""Return per-filesystem info via QEMU guest agent (get-fsinfo).
Returns [] if agent is not installed or unreachable."""
if not self.proxmox:
return []
try:
result = self.proxmox.nodes(node).qemu(vmid).agent('get-fsinfo').get()
fslist = result if isinstance(result, list) else result.get('result', [])
disks = []
seen_disks: set = set()
for fs in fslist:
if not isinstance(fs, dict):
continue
total = fs.get('total-bytes', 0) or 0
if total == 0:
continue
# Deduplicate by disk+partition key to avoid pseudo-filesystems
disk_key = fs.get('name', '') or fs.get('mountpoint', '')
if disk_key in seen_disks:
continue
seen_disks.add(disk_key)
disks.append({
'name': fs.get('name', ''),
'mountpoint': fs.get('mountpoint', '/'),
'used': fs.get('used-bytes', 0) or 0,
'total': total,
})
return disks
except Exception:
return []
def get_vm_rrddata(self, node: str, vmid: int, timeframe: str = "hour") -> Dict:
"""
Получить исторические данные VM для графиков (CPU, Memory, Network, Disk IO)