mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-19 02:28:59 +00:00
Keep virtio and Xen disks in host disk I/O collection
99ad8c2c4routed collectDiskIO through fsfilters.IsVirtualBlockDevice to stop ZFS zvols being collected. That helper answers "can this device report SMART", which is the wrong question for I/O accounting: it also matches vd* and xvd*, so every agent running inside a KVM, Proxmox or Xen VM lost host disk I/O entirely, since vda is the real disk on those machines. It also dropped md, nbd, rbd, drbd and pmem, none of which were part of the report. Add IsNonPhysicalDiskIODevice for the I/O question and use it here. It covers only loop, ram, zram, dm- and zd, so the zvol fix for #1671 stands while everything that was collected before99ad8c2c4is collected again. Tests assert vda and xvda survive the I/O filter while still being excluded from SMART, so the two predicates cannot be conflated again. Contract-Neutral: behavioural regression fix, no contract delta Refs #1671
This commit is contained in:
parent
6be2af1c19
commit
4e67a93d49
4 changed files with 87 additions and 15 deletions
|
|
@ -412,13 +412,16 @@ func collectDiskIO(ctx context.Context, diskExclude []string) []agentshost.DiskI
|
|||
if isPartition(name) {
|
||||
continue
|
||||
}
|
||||
// Skip virtual/pseudo block devices (loop, ram, dm-, md, zvols,
|
||||
// zram, nbd, rbd, drbd, ...). ZFS-backed Proxmox hosts expose one
|
||||
// zd<N> device per zvol, so without this a host with a few hundred
|
||||
// guests reports a few hundred phantom disks on every collection
|
||||
// cycle (issue #1671). The prefix list is shared with SMART
|
||||
// collection and the server-side resource registry.
|
||||
if fsfilters.IsVirtualBlockDevice(name) {
|
||||
// Skip devices whose counters are not host physical disk activity
|
||||
// (loop, ram, zram, dm- aggregates, and ZFS zvols). ZFS-backed
|
||||
// Proxmox hosts expose one zd<N> device per zvol, so without this a
|
||||
// host with a few hundred guests reports a few hundred phantom
|
||||
// disks on every collection cycle (issue #1671).
|
||||
//
|
||||
// Deliberately NOT fsfilters.IsVirtualBlockDevice: that answers
|
||||
// "can this report SMART" and would drop vd*/xvd*, which are the
|
||||
// real disks of any agent running inside a VM.
|
||||
if fsfilters.IsNonPhysicalDiskIODevice(name) {
|
||||
continue
|
||||
}
|
||||
// Skip user-excluded devices (issue #1142)
|
||||
|
|
|
|||
|
|
@ -189,30 +189,31 @@ func TestCollectDiskIOFiltersAndError(t *testing.T) {
|
|||
"ram0": {ReadBytes: 7},
|
||||
"dm-0": {ReadBytes: 8},
|
||||
"sdb": {ReadBytes: 9},
|
||||
// Aggregate virtual devices whose counters restate the I/O of
|
||||
// the physical members underneath them.
|
||||
"md0": {ReadBytes: 10},
|
||||
"md0": {ReadBytes: 10},
|
||||
// ZFS zvols. A Proxmox host with ZFS-backed guest storage
|
||||
// exposes one of these per zvol (issue #1671).
|
||||
"zd0": {ReadBytes: 11},
|
||||
"zd16": {ReadBytes: 12},
|
||||
"zd160": {ReadBytes: 13},
|
||||
"zram0": {ReadBytes: 14},
|
||||
"nbd0": {ReadBytes: 15},
|
||||
"rbd0": {ReadBytes: 16},
|
||||
// Real disks of a virtualised host. These cannot report SMART
|
||||
// but their I/O counters are genuine host disk activity, so
|
||||
// they must survive the filter.
|
||||
"vda": {ReadBytes: 15},
|
||||
"xvda": {ReadBytes: 16},
|
||||
}, nil
|
||||
}
|
||||
|
||||
disks := collectDiskIO(context.Background(), []string{"sdb"})
|
||||
if len(disks) != 2 {
|
||||
t.Fatalf("expected 2 disk I/O entries after filtering, got %d: %+v", len(disks), disks)
|
||||
if len(disks) != 5 {
|
||||
t.Fatalf("expected 5 disk I/O entries after filtering, got %d: %+v", len(disks), disks)
|
||||
}
|
||||
|
||||
gotDevices := make([]string, 0, len(disks))
|
||||
for _, disk := range disks {
|
||||
gotDevices = append(gotDevices, disk.Device)
|
||||
}
|
||||
wantDevices := []string{"nvme0n1", "sda"}
|
||||
wantDevices := []string{"md0", "nvme0n1", "sda", "vda", "xvda"}
|
||||
if !reflect.DeepEqual(gotDevices, wantDevices) {
|
||||
t.Fatalf("unexpected disk I/O devices: got %v, want %v", gotDevices, wantDevices)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,6 +269,44 @@ var virtualBlockDevicePrefixes = []string{
|
|||
"zram",
|
||||
}
|
||||
|
||||
// nonPhysicalIODevicePrefixes are device-name prefixes whose kernel I/O
|
||||
// counters do not describe host physical disk activity, either because the
|
||||
// device is purely virtual (loop, ram, zram), because it is guest storage
|
||||
// layered on the real disks (ZFS zvols), or because it is a mapper
|
||||
// aggregate that restates the I/O of its own members (dm-).
|
||||
//
|
||||
// This list is deliberately NARROWER than virtualBlockDevicePrefixes, and
|
||||
// the two must not be conflated. That list answers "can this device report
|
||||
// SMART", which is a different question: a virtio (vd*) or Xen (xvd*) disk
|
||||
// cannot report SMART, but its I/O counters are the real disk activity of a
|
||||
// virtualised host, so excluding those from I/O collection blanks disk I/O
|
||||
// for every agent running inside a VM.
|
||||
var nonPhysicalIODevicePrefixes = []string{
|
||||
"dm-",
|
||||
"loop",
|
||||
"ram",
|
||||
"zd",
|
||||
"zram",
|
||||
}
|
||||
|
||||
// IsNonPhysicalDiskIODevice reports whether a block-device name should be
|
||||
// left out of host disk I/O collection because its counters do not
|
||||
// represent physical disk activity on this host. Use this for I/O
|
||||
// accounting. Use IsVirtualBlockDevice for SMART capability.
|
||||
func IsNonPhysicalDiskIODevice(name string) bool {
|
||||
trimmed := strings.ToLower(strings.TrimSpace(name))
|
||||
trimmed = strings.TrimPrefix(trimmed, "/dev/")
|
||||
if trimmed == "" {
|
||||
return false
|
||||
}
|
||||
for _, prefix := range nonPhysicalIODevicePrefixes {
|
||||
if strings.HasPrefix(trimmed, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsVirtualBlockDevice reports whether a block-device name (with or
|
||||
// without the /dev/ prefix) looks like a virtual or pseudo device that
|
||||
// cannot provide SMART data or meaningful physical-disk metrics.
|
||||
|
|
|
|||
|
|
@ -364,3 +364,33 @@ func TestIsVirtualBlockDevice(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Regression guard: IsNonPhysicalDiskIODevice answers a different question
|
||||
// from IsVirtualBlockDevice. Conflating them once blanked host disk I/O for
|
||||
// every agent running inside a VM, because virtio and Xen disks cannot
|
||||
// report SMART but their I/O counters are real host disk activity.
|
||||
func TestIsNonPhysicalDiskIODevice(t *testing.T) {
|
||||
excluded := []string{"loop0", "ram0", "zram0", "dm-0", "zd0", "zd16", "zd160", "/dev/zd0"}
|
||||
for _, device := range excluded {
|
||||
if !IsNonPhysicalDiskIODevice(device) {
|
||||
t.Errorf("IsNonPhysicalDiskIODevice(%q) = false, want true", device)
|
||||
}
|
||||
}
|
||||
|
||||
kept := []string{"sda", "nvme0n1", "vda", "vdb", "xvda", "md0", "pmem0", "nbd0", "rbd0", "/dev/vda"}
|
||||
for _, device := range kept {
|
||||
if IsNonPhysicalDiskIODevice(device) {
|
||||
t.Errorf("IsNonPhysicalDiskIODevice(%q) = true, want false", device)
|
||||
}
|
||||
}
|
||||
|
||||
// vd*/xvd* must stay excluded for SMART while staying included for I/O.
|
||||
for _, device := range []string{"vda", "xvda"} {
|
||||
if !IsVirtualBlockDevice(device) {
|
||||
t.Errorf("IsVirtualBlockDevice(%q) = false, want true", device)
|
||||
}
|
||||
if IsNonPhysicalDiskIODevice(device) {
|
||||
t.Errorf("IsNonPhysicalDiskIODevice(%q) = true, want false", device)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue