fix: improve VM disk monitoring to filter network shares and special filesystems (addresses #414)

- Add comprehensive filtering for network filesystems (NFS, CIFS, SMB, FUSE, 9p)
- Skip Docker volumes, snap mounts, and other special mountpoints
- Add detailed logging to track which filesystems are included/excluded
- Add sanity check to detect when reported disk is way larger than allocated
- Improve logging with GB values and more context for debugging

This should prevent Pulse from accidentally including host disk space or
network shares when calculating VM disk usage. Users can use the existing
diagnostics system in the UI to troubleshoot VM disk issues.
This commit is contained in:
Pulse Monitor
2025-09-09 11:12:01 +00:00
parent c9dbae467a
commit 131a6b3cf8
+81 -2
View File
@@ -1292,17 +1292,57 @@ func (m *Monitor) pollVMsAndContainersEfficient(ctx context.Context, instanceNam
// Aggregate disk usage from all filesystems
var totalBytes, usedBytes uint64
var skippedFS []string
var includedFS []string
// Log all filesystems received for debugging
log.Debug().
Str("instance", instanceName).
Str("vm", res.Name).
Int("vmid", res.VMID).
Int("filesystem_count", len(fsInfo)).
Msg("Processing filesystems from guest agent")
for _, fs := range fsInfo {
// Skip special filesystems and mounts
skipReasons := []string{}
shouldSkip := false
// Check filesystem type
fsTypeLower := strings.ToLower(fs.Type)
if fs.Type == "tmpfs" || fs.Type == "devtmpfs" ||
strings.HasPrefix(fs.Mountpoint, "/dev") ||
fs.Type == "cgroup" || fs.Type == "cgroup2" ||
fs.Type == "sysfs" || fs.Type == "proc" ||
fs.Type == "devpts" || fs.Type == "securityfs" ||
fs.Type == "debugfs" || fs.Type == "tracefs" ||
fs.Type == "fusectl" || fs.Type == "configfs" ||
fs.Type == "pstore" || fs.Type == "hugetlbfs" ||
fs.Type == "mqueue" || fs.Type == "bpf" ||
strings.Contains(fsTypeLower, "fuse") || // Skip FUSE mounts (often network/special)
strings.Contains(fsTypeLower, "9p") || // Skip 9p mounts (VM shared folders)
strings.Contains(fsTypeLower, "nfs") || // Skip NFS mounts
strings.Contains(fsTypeLower, "cifs") || // Skip CIFS/SMB mounts
strings.Contains(fsTypeLower, "smb") { // Skip SMB mounts
skipReasons = append(skipReasons, "special-fs-type")
shouldSkip = true
}
// Check mountpoint patterns
if strings.HasPrefix(fs.Mountpoint, "/dev") ||
strings.HasPrefix(fs.Mountpoint, "/proc") ||
strings.HasPrefix(fs.Mountpoint, "/sys") ||
strings.HasPrefix(fs.Mountpoint, "/run") ||
strings.HasPrefix(fs.Mountpoint, "/var/lib/docker") || // Skip Docker volumes
strings.HasPrefix(fs.Mountpoint, "/snap") || // Skip snap mounts
fs.Mountpoint == "/boot/efi" ||
fs.Mountpoint == "System Reserved" || // Windows System Reserved partition
strings.Contains(fs.Mountpoint, "System Reserved") { // Various Windows reserved formats
skippedFS = append(skippedFS, fmt.Sprintf("%s(%s)", fs.Mountpoint, fs.Type))
skipReasons = append(skipReasons, "special-mountpoint")
shouldSkip = true
}
if shouldSkip {
skippedFS = append(skippedFS, fmt.Sprintf("%s(%s,%s)",
fs.Mountpoint, fs.Type, strings.Join(skipReasons, ",")))
continue
}
@@ -1311,18 +1351,25 @@ func (m *Monitor) pollVMsAndContainersEfficient(ctx context.Context, instanceNam
if fs.TotalBytes > 0 {
totalBytes += fs.TotalBytes
usedBytes += fs.UsedBytes
includedFS = append(includedFS, fmt.Sprintf("%s(%s,%.1fGB)",
fs.Mountpoint, fs.Type, float64(fs.TotalBytes)/1073741824))
log.Debug().
Str("instance", instanceName).
Str("vm", res.Name).
Int("vmid", res.VMID).
Str("mountpoint", fs.Mountpoint).
Str("type", fs.Type).
Uint64("total", fs.TotalBytes).
Uint64("used", fs.UsedBytes).
Float64("total_gb", float64(fs.TotalBytes)/1073741824).
Float64("used_gb", float64(fs.UsedBytes)/1073741824).
Msg("Including filesystem in disk usage calculation")
} else if fs.TotalBytes == 0 && len(fs.Mountpoint) > 0 {
skippedFS = append(skippedFS, fmt.Sprintf("%s(%s,0GB)", fs.Mountpoint, fs.Type))
log.Debug().
Str("instance", instanceName).
Str("vm", res.Name).
Int("vmid", res.VMID).
Str("mountpoint", fs.Mountpoint).
Str("type", fs.Type).
Msg("Skipping filesystem with zero total bytes")
@@ -1337,8 +1384,36 @@ func (m *Monitor) pollVMsAndContainersEfficient(ctx context.Context, instanceNam
Msg("Skipped special filesystems")
}
if len(includedFS) > 0 {
log.Info().
Str("instance", instanceName).
Str("vm", res.Name).
Int("vmid", res.VMID).
Strs("included", includedFS).
Msg("Filesystems included in disk calculation")
}
// If we got valid data from guest agent, use it
if totalBytes > 0 {
// Sanity check: if the reported disk is way larger than allocated disk,
// we might be getting host disk info somehow
allocatedDiskGB := float64(res.MaxDisk) / 1073741824
reportedDiskGB := float64(totalBytes) / 1073741824
// If reported disk is more than 2x the allocated disk, log a warning
// This could indicate we're getting host disk or network shares
if allocatedDiskGB > 0 && reportedDiskGB > allocatedDiskGB * 2 {
log.Warn().
Str("instance", instanceName).
Str("vm", res.Name).
Int("vmid", res.VMID).
Float64("allocated_gb", allocatedDiskGB).
Float64("reported_gb", reportedDiskGB).
Float64("ratio", reportedDiskGB/allocatedDiskGB).
Strs("filesystems", includedFS).
Msg("VM reports disk usage significantly larger than allocated disk - possible issue with filesystem detection")
}
diskTotal = totalBytes
diskUsed = usedBytes
diskFree = totalBytes - usedBytes
@@ -1347,8 +1422,12 @@ func (m *Monitor) pollVMsAndContainersEfficient(ctx context.Context, instanceNam
log.Info().
Str("instance", instanceName).
Str("vm", res.Name).
Int("vmid", res.VMID).
Uint64("totalBytes", totalBytes).
Uint64("usedBytes", usedBytes).
Float64("total_gb", float64(totalBytes)/1073741824).
Float64("used_gb", float64(usedBytes)/1073741824).
Float64("allocated_gb", allocatedDiskGB).
Float64("usage", diskUsage).
Msg("Successfully retrieved disk usage from guest agent (cluster/resources showed 0)")
} else {