Extract filesystem filtering logic into pkg/fsfilters
Move the inline filesystem skip logic from pollVMsAndContainersEfficient into a reusable ShouldSkipFilesystem function. This consolidates filtering for virtual filesystems (tmpfs, cgroup, etc.), network mounts (nfs, cifs, fuse), and special mountpoints (/dev, /proc, /snap, etc.) into one tested location. Reduces cyclomatic complexity of pollVMsAndContainersEfficient and adds 28 test cases covering virtual fs types, network mounts, special mounts, Windows paths, and edge cases.
This commit is contained in:
parent
69a7d96e4b
commit
61bb124692
3 changed files with 159 additions and 53 deletions
|
|
@ -35,6 +35,7 @@ import (
|
|||
"github.com/rcourtman/pulse-go-rewrite/internal/websocket"
|
||||
agentsdocker "github.com/rcourtman/pulse-go-rewrite/pkg/agents/docker"
|
||||
agentshost "github.com/rcourtman/pulse-go-rewrite/pkg/agents/host"
|
||||
"github.com/rcourtman/pulse-go-rewrite/pkg/fsfilters"
|
||||
"github.com/rcourtman/pulse-go-rewrite/pkg/pbs"
|
||||
"github.com/rcourtman/pulse-go-rewrite/pkg/pmg"
|
||||
"github.com/rcourtman/pulse-go-rewrite/pkg/proxmox"
|
||||
|
|
@ -6585,62 +6586,25 @@ func (m *Monitor) pollVMsAndContainersEfficient(ctx context.Context, instanceNam
|
|||
|
||||
for _, fs := range fsInfo {
|
||||
// Skip special filesystems and mounts
|
||||
skipReasons := []string{}
|
||||
reasonReadOnly := ""
|
||||
shouldSkip := false
|
||||
|
||||
// Check filesystem type
|
||||
fsTypeLower := strings.ToLower(fs.Type)
|
||||
if reason, skip := readOnlyFilesystemReason(fs.Type, fs.TotalBytes, fs.UsedBytes); skip {
|
||||
skipReasons = append(skipReasons, fmt.Sprintf("read-only-%s", reason))
|
||||
reasonReadOnly = reason
|
||||
shouldSkip = true
|
||||
}
|
||||
if fs.Type == "tmpfs" || fs.Type == "devtmpfs" ||
|
||||
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
|
||||
skipReasons = append(skipReasons, "special-mountpoint")
|
||||
shouldSkip = true
|
||||
}
|
||||
|
||||
shouldSkip, reasons := fsfilters.ShouldSkipFilesystem(fs.Type, fs.Mountpoint, fs.TotalBytes, fs.UsedBytes)
|
||||
if shouldSkip {
|
||||
if reasonReadOnly != "" {
|
||||
log.Debug().
|
||||
Str("instance", instanceName).
|
||||
Str("vm", res.Name).
|
||||
Int("vmid", res.VMID).
|
||||
Str("mountpoint", fs.Mountpoint).
|
||||
Str("type", fs.Type).
|
||||
Float64("total_gb", float64(fs.TotalBytes)/1073741824).
|
||||
Float64("used_gb", float64(fs.UsedBytes)/1073741824).
|
||||
Msg("Skipping read-only filesystem from disk aggregation")
|
||||
// Check if any reason is read-only for detailed logging
|
||||
for _, r := range reasons {
|
||||
if strings.HasPrefix(r, "read-only-") {
|
||||
log.Debug().
|
||||
Str("instance", instanceName).
|
||||
Str("vm", res.Name).
|
||||
Int("vmid", res.VMID).
|
||||
Str("mountpoint", fs.Mountpoint).
|
||||
Str("type", fs.Type).
|
||||
Float64("total_gb", float64(fs.TotalBytes)/1073741824).
|
||||
Float64("used_gb", float64(fs.UsedBytes)/1073741824).
|
||||
Msg("Skipping read-only filesystem from disk aggregation")
|
||||
break
|
||||
}
|
||||
}
|
||||
skippedFS = append(skippedFS, fmt.Sprintf("%s(%s,%s)",
|
||||
fs.Mountpoint, fs.Type, strings.Join(skipReasons, ",")))
|
||||
fs.Mountpoint, fs.Type, strings.Join(reasons, ",")))
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,3 +54,83 @@ func ShouldIgnoreReadOnlyFilesystem(fsType string, totalBytes, usedBytes uint64)
|
|||
_, skip := ReadOnlyFilesystemReason(fsType, totalBytes, usedBytes)
|
||||
return skip
|
||||
}
|
||||
|
||||
// virtualFSTypes are filesystem types that represent virtual/pseudo filesystems
|
||||
// which should not be counted toward disk usage.
|
||||
var virtualFSTypes = map[string]bool{
|
||||
"tmpfs": true,
|
||||
"devtmpfs": true,
|
||||
"cgroup": true,
|
||||
"cgroup2": true,
|
||||
"sysfs": true,
|
||||
"proc": true,
|
||||
"devpts": true,
|
||||
"securityfs": true,
|
||||
"debugfs": true,
|
||||
"tracefs": true,
|
||||
"fusectl": true,
|
||||
"configfs": true,
|
||||
"pstore": true,
|
||||
"hugetlbfs": true,
|
||||
"mqueue": true,
|
||||
"bpf": true,
|
||||
}
|
||||
|
||||
// networkFSPatterns are substrings that indicate network/remote filesystems.
|
||||
var networkFSPatterns = []string{"fuse", "9p", "nfs", "cifs", "smb"}
|
||||
|
||||
// specialMountPrefixes are mountpoint prefixes that indicate system mounts.
|
||||
var specialMountPrefixes = []string{
|
||||
"/dev",
|
||||
"/proc",
|
||||
"/sys",
|
||||
"/run",
|
||||
"/var/lib/docker",
|
||||
"/snap",
|
||||
}
|
||||
|
||||
// ShouldSkipFilesystem determines if a filesystem should be excluded from disk
|
||||
// usage aggregation. It checks for read-only filesystems, virtual/pseudo filesystems,
|
||||
// network mounts, and special system mountpoints. Returns skip=true if the filesystem
|
||||
// should be excluded, along with a list of reason strings.
|
||||
func ShouldSkipFilesystem(fsType, mountpoint string, totalBytes, usedBytes uint64) (skip bool, reasons []string) {
|
||||
fsTypeLower := strings.ToLower(strings.TrimSpace(fsType))
|
||||
|
||||
// Check read-only filesystems (existing logic)
|
||||
if reason, isReadOnly := ReadOnlyFilesystemReason(fsType, totalBytes, usedBytes); isReadOnly {
|
||||
reasons = append(reasons, "read-only-"+reason)
|
||||
}
|
||||
|
||||
// Check virtual filesystem types
|
||||
if virtualFSTypes[fsTypeLower] {
|
||||
reasons = append(reasons, "special-fs-type")
|
||||
}
|
||||
|
||||
// Check network filesystem patterns
|
||||
for _, pattern := range networkFSPatterns {
|
||||
if strings.Contains(fsTypeLower, pattern) {
|
||||
reasons = append(reasons, "special-fs-type")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check special mountpoint prefixes
|
||||
for _, prefix := range specialMountPrefixes {
|
||||
if strings.HasPrefix(mountpoint, prefix) {
|
||||
reasons = append(reasons, "special-mountpoint")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check specific special mountpoints
|
||||
if mountpoint == "/boot/efi" {
|
||||
reasons = append(reasons, "special-mountpoint")
|
||||
}
|
||||
|
||||
// Windows System Reserved partition
|
||||
if mountpoint == "System Reserved" || strings.Contains(mountpoint, "System Reserved") {
|
||||
reasons = append(reasons, "special-mountpoint")
|
||||
}
|
||||
|
||||
return len(reasons) > 0, reasons
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,3 +141,65 @@ func TestShouldIgnoreReadOnlyFilesystem(t *testing.T) {
|
|||
t.Fatalf("expected ext4 to be included")
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldSkipFilesystem(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fsType string
|
||||
mountpoint string
|
||||
totalBytes uint64
|
||||
usedBytes uint64
|
||||
expectSkip bool
|
||||
}{
|
||||
// Virtual filesystem types
|
||||
{"tmpfs", "tmpfs", "/tmp", 1024, 512, true},
|
||||
{"devtmpfs", "devtmpfs", "/dev", 1024, 100, true},
|
||||
{"cgroup2", "cgroup2", "/sys/fs/cgroup", 0, 0, true},
|
||||
{"sysfs", "sysfs", "/sys", 0, 0, true},
|
||||
{"proc", "proc", "/proc", 0, 0, true},
|
||||
{"virtual fs case insensitive", "TMPFS", "/tmp", 1024, 512, true},
|
||||
|
||||
// Network filesystem types
|
||||
{"nfs mount", "nfs4", "/mnt/nas", 1000000, 500000, true},
|
||||
{"cifs mount", "cifs", "/mnt/share", 1000000, 500000, true},
|
||||
{"fuse.sshfs", "fuse.sshfs", "/mnt/remote", 1000000, 500000, true},
|
||||
{"9p VM shared folder", "9p", "/mnt/host", 1000000, 500000, true},
|
||||
|
||||
// Special mountpoint prefixes
|
||||
{"/dev prefix", "ext4", "/dev/shm", 1024, 100, true},
|
||||
{"/proc prefix", "ext4", "/proc/sys", 1024, 100, true},
|
||||
{"/sys prefix", "ext4", "/sys/kernel", 1024, 100, true},
|
||||
{"/run prefix", "ext4", "/run/user/1000", 1024, 100, true},
|
||||
{"/var/lib/docker", "ext4", "/var/lib/docker/overlay2", 1000000, 500000, true},
|
||||
{"/snap prefix", "ext4", "/snap/core/12345", 1000000, 500000, true},
|
||||
{"/boot/efi exact", "vfat", "/boot/efi", 512 * 1024 * 1024, 50 * 1024 * 1024, true},
|
||||
|
||||
// Windows paths
|
||||
{"Windows System Reserved", "NTFS", "System Reserved", 500 * 1024 * 1024, 100 * 1024 * 1024, true},
|
||||
{"Windows C drive - should NOT skip", "NTFS", "C:\\", 500 * 1024 * 1024 * 1024, 200 * 1024 * 1024 * 1024, false},
|
||||
{"Windows D drive - should NOT skip", "NTFS", "D:\\", 1000 * 1024 * 1024 * 1024, 500 * 1024 * 1024 * 1024, false},
|
||||
|
||||
// Regular filesystems that should NOT be skipped
|
||||
{"ext4 root", "ext4", "/", 100 * 1024 * 1024 * 1024, 50 * 1024 * 1024 * 1024, false},
|
||||
{"xfs data", "xfs", "/data", 500 * 1024 * 1024 * 1024, 200 * 1024 * 1024 * 1024, false},
|
||||
{"btrfs home", "btrfs", "/home", 200 * 1024 * 1024 * 1024, 100 * 1024 * 1024 * 1024, false},
|
||||
{"zfs pool", "zfs", "/tank", 10 * 1024 * 1024 * 1024 * 1024, 5 * 1024 * 1024 * 1024 * 1024, false},
|
||||
|
||||
// Edge cases
|
||||
{"empty fsType", "", "/mnt/data", 1000000, 500000, false},
|
||||
{"empty mountpoint", "ext4", "", 1000000, 500000, false},
|
||||
{"whitespace fsType", " tmpfs ", "/tmp", 1024, 512, true},
|
||||
|
||||
// Read-only filesystems (should still work through new function)
|
||||
{"squashfs via ShouldSkipFilesystem", "squashfs", "/snap/firefox/123", 100 * 1024 * 1024, 100 * 1024 * 1024, true},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
skip, reasons := ShouldSkipFilesystem(tc.fsType, tc.mountpoint, tc.totalBytes, tc.usedBytes)
|
||||
if skip != tc.expectSkip {
|
||||
t.Errorf("expected skip=%t, got skip=%t (reasons: %v)", tc.expectSkip, skip, reasons)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue