feat: add mount option filtering to disk plugin (#11039)

This commit is contained in:
Nathan Ferch 2022-05-12 17:16:19 -04:00 committed by GitHub
parent fa723355f5
commit 62922de631
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 135 additions and 22 deletions

View File

@ -17,6 +17,11 @@ Note that `used_percent` is calculated by doing `used / (used + free)`, _not_
## Ignore mount points by filesystem type.
ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs"]
## Ignore mount points by mount options.
## The 'mount' command reports options of all mounts in parathesis.
## Bind mounts can be ignored with the special 'bind' option.
# ignore_mount_opts = []
```
### Docker container

View File

@ -14,8 +14,9 @@ type DiskStats struct {
LegacyMountPoints []string `toml:"mountpoints" deprecated:"0.10.2;2.0.0;use 'mount_points' instead"`
MountPoints []string `toml:"mount_points"`
IgnoreFS []string `toml:"ignore_fs"`
MountPoints []string `toml:"mount_points"`
IgnoreFS []string `toml:"ignore_fs"`
IgnoreMountOpts []string `toml:"ignore_mount_opts"`
Log telegraf.Logger `toml:"-"`
}
@ -34,7 +35,7 @@ func (ds *DiskStats) Init() error {
}
func (ds *DiskStats) Gather(acc telegraf.Accumulator) error {
disks, partitions, err := ds.ps.DiskUsage(ds.MountPoints, ds.IgnoreFS)
disks, partitions, err := ds.ps.DiskUsage(ds.MountPoints, ds.IgnoreMountOpts, ds.IgnoreFS)
if err != nil {
return fmt.Errorf("error getting disk usage info: %s", err)
}

View File

@ -43,6 +43,12 @@ func TestDiskUsage(t *testing.T) {
Fstype: "ext4",
Opts: []string{"rw", "noatime", "nodiratime", "errors=remount-ro"},
},
{
Device: "/dev/sda",
Mountpoint: "/var/rootbind",
Fstype: "ext4",
Opts: []string{"ro", "noatime", "nodiratime", "bind"},
},
}
duAll := []diskUtil.UsageStat{
{
@ -65,18 +71,29 @@ func TestDiskUsage(t *testing.T) {
InodesFree: 468,
InodesUsed: 2000,
},
{
Path: "/var/rootbind",
Fstype: "ext4",
Total: 128,
Free: 23,
Used: 100,
InodesTotal: 1234,
InodesFree: 234,
InodesUsed: 1000,
},
}
mps.On("Partitions", true).Return(psAll, nil)
mps.On("OSGetenv", "HOST_MOUNT_PREFIX").Return("")
mps.On("PSDiskUsage", "/").Return(&duAll[0], nil)
mps.On("PSDiskUsage", "/home").Return(&duAll[1], nil)
mps.On("PSDiskUsage", "/var/rootbind").Return(&duAll[2], nil)
err = (&DiskStats{ps: mps}).Gather(&acc)
require.NoError(t, err)
numDiskMetrics := acc.NFields()
expectedAllDiskMetrics := 14
expectedAllDiskMetrics := 21
require.Equal(t, expectedAllDiskMetrics, numDiskMetrics)
tags1 := map[string]string{
@ -91,6 +108,12 @@ func TestDiskUsage(t *testing.T) {
"device": "sdb",
"mode": "rw",
}
tags3 := map[string]string{
"path": fmt.Sprintf("%cvar%crootbind", os.PathSeparator, os.PathSeparator),
"fstype": "ext4",
"device": "sda",
"mode": "ro",
}
fields1 := map[string]interface{}{
"total": uint64(128),
@ -110,20 +133,35 @@ func TestDiskUsage(t *testing.T) {
"inodes_used": uint64(2000),
"used_percent": float64(81.30081300813008),
}
fields3 := map[string]interface{}{
"total": uint64(128),
"used": uint64(100),
"free": uint64(23),
"inodes_total": uint64(1234),
"inodes_free": uint64(234),
"inodes_used": uint64(1000),
"used_percent": float64(81.30081300813008),
}
acc.AssertContainsTaggedFields(t, "disk", fields1, tags1)
acc.AssertContainsTaggedFields(t, "disk", fields2, tags2)
acc.AssertContainsTaggedFields(t, "disk", fields3, tags3)
// We expect 6 more DiskMetrics to show up with an explicit match on "/"
// We expect 7 more DiskMetrics to show up with an explicit match on "/"
// and /home not matching the /dev in MountPoints
err = (&DiskStats{ps: &mps, MountPoints: []string{"/", "/dev"}}).Gather(&acc)
require.NoError(t, err)
require.Equal(t, expectedAllDiskMetrics+7, acc.NFields())
// We should see all the diskpoints as MountPoints includes both
// / and /home
err = (&DiskStats{ps: &mps, MountPoints: []string{"/", "/home"}}).Gather(&acc)
// /, /home, and /var/rootbind
err = (&DiskStats{ps: &mps, MountPoints: []string{"/", "/home", "/var/rootbind"}}).Gather(&acc)
require.NoError(t, err)
require.Equal(t, 2*expectedAllDiskMetrics+7, acc.NFields())
require.Equal(t, expectedAllDiskMetrics+7*4, acc.NFields())
// We should see all the mounts as MountPoints except the bind mound
err = (&DiskStats{ps: &mps, IgnoreMountOpts: []string{"bind"}}).Gather(&acc)
require.NoError(t, err)
require.Equal(t, expectedAllDiskMetrics+7*6, acc.NFields())
}
func TestDiskUsageHostMountPrefix(t *testing.T) {
@ -287,8 +325,18 @@ func TestDiskStats(t *testing.T) {
InodesFree: 468,
InodesUsed: 2000,
},
{
Path: "/var/rootbind",
Fstype: "ext4",
Total: 128,
Free: 23,
Used: 100,
InodesTotal: 1234,
InodesFree: 234,
InodesUsed: 1000,
},
}
duFiltered := []*diskUtil.UsageStat{
duMountFiltered := []*diskUtil.UsageStat{
{
Path: "/",
Fstype: "ext4",
@ -300,6 +348,28 @@ func TestDiskStats(t *testing.T) {
InodesUsed: 1000,
},
}
duOptFiltered := []*diskUtil.UsageStat{
{
Path: "/",
Fstype: "ext4",
Total: 128,
Free: 23,
Used: 100,
InodesTotal: 1234,
InodesFree: 234,
InodesUsed: 1000,
},
{
Path: "/home",
Fstype: "ext4",
Total: 256,
Free: 46,
Used: 200,
InodesTotal: 2468,
InodesFree: 468,
InodesUsed: 2000,
},
}
psAll := []*diskUtil.PartitionStat{
{
@ -314,9 +384,15 @@ func TestDiskStats(t *testing.T) {
Fstype: "ext4",
Opts: []string{"rw", "noatime", "nodiratime", "errors=remount-ro"},
},
{
Device: "/dev/sda",
Mountpoint: "/var/rootbind",
Fstype: "ext4",
Opts: []string{"ro", "noatime", "nodiratime", "bind"},
},
}
psFiltered := []*diskUtil.PartitionStat{
psMountFiltered := []*diskUtil.PartitionStat{
{
Device: "/dev/sda",
Mountpoint: "/",
@ -324,16 +400,31 @@ func TestDiskStats(t *testing.T) {
Opts: []string{"ro", "noatime", "nodiratime"},
},
}
psOptFiltered := []*diskUtil.PartitionStat{
{
Device: "/dev/sda",
Mountpoint: "/",
Fstype: "ext4",
Opts: []string{"ro", "noatime", "nodiratime"},
},
{
Device: "/dev/sdb",
Mountpoint: "/home",
Fstype: "ext4",
Opts: []string{"rw", "noatime", "nodiratime", "errors=remount-ro"},
},
}
mps.On("DiskUsage", []string(nil), []string(nil)).Return(duAll, psAll, nil)
mps.On("DiskUsage", []string{"/", "/dev"}, []string(nil)).Return(duFiltered, psFiltered, nil)
mps.On("DiskUsage", []string{"/", "/home"}, []string(nil)).Return(duAll, psAll, nil)
mps.On("DiskUsage", []string(nil), []string(nil), []string(nil)).Return(duAll, psAll, nil)
mps.On("DiskUsage", []string{"/", "/dev"}, []string(nil), []string(nil)).Return(duMountFiltered, psMountFiltered, nil)
mps.On("DiskUsage", []string{"/", "/home", "/var/rootbind"}, []string(nil), []string(nil)).Return(duAll, psAll, nil)
mps.On("DiskUsage", []string(nil), []string{"bind"}, []string(nil)).Return(duOptFiltered, psOptFiltered, nil)
err = (&DiskStats{ps: &mps}).Gather(&acc)
require.NoError(t, err)
numDiskMetrics := acc.NFields()
expectedAllDiskMetrics := 14
expectedAllDiskMetrics := 21
require.Equal(t, expectedAllDiskMetrics, numDiskMetrics)
tags1 := map[string]string{
@ -370,17 +461,22 @@ func TestDiskStats(t *testing.T) {
acc.AssertContainsTaggedFields(t, "disk", fields1, tags1)
acc.AssertContainsTaggedFields(t, "disk", fields2, tags2)
// We expect 6 more DiskMetrics to show up with an explicit match on "/"
// and /home not matching the /dev in MountPoints
// We expect 7 more DiskMetrics to show up with an explicit match on "/"
// and /home and /var/rootbind not matching the /dev in MountPoints
err = (&DiskStats{ps: &mps, MountPoints: []string{"/", "/dev"}}).Gather(&acc)
require.NoError(t, err)
require.Equal(t, expectedAllDiskMetrics+7, acc.NFields())
// We should see all the diskpoints as MountPoints includes both
// / and /home
err = (&DiskStats{ps: &mps, MountPoints: []string{"/", "/home"}}).Gather(&acc)
// /, /home, and /var/rootbind
err = (&DiskStats{ps: &mps, MountPoints: []string{"/", "/home", "/var/rootbind"}}).Gather(&acc)
require.NoError(t, err)
require.Equal(t, 2*expectedAllDiskMetrics+7, acc.NFields())
require.Equal(t, expectedAllDiskMetrics+7*4, acc.NFields())
// We should see all the mounts as MountPoints except the bind mound
err = (&DiskStats{ps: &mps, IgnoreMountOpts: []string{"bind"}}).Gather(&acc)
require.NoError(t, err)
require.Equal(t, expectedAllDiskMetrics+7*6, acc.NFields())
}
func TestDiskUsageIssues(t *testing.T) {

View File

@ -46,8 +46,8 @@ func (m *MockPS) CPUTimes(_, _ bool) ([]cpu.TimesStat, error) {
return r0, r1
}
func (m *MockPS) DiskUsage(mountPointFilter []string, fstypeExclude []string) ([]*disk.UsageStat, []*disk.PartitionStat, error) {
ret := m.Called(mountPointFilter, fstypeExclude)
func (m *MockPS) DiskUsage(mountPointFilter []string, mountOptsExclude []string, fstypeExclude []string) ([]*disk.UsageStat, []*disk.PartitionStat, error) {
ret := m.Called(mountPointFilter, mountOptsExclude, fstypeExclude)
r0 := ret.Get(0).([]*disk.UsageStat)
r1 := ret.Get(1).([]*disk.PartitionStat)

View File

@ -17,7 +17,7 @@ import (
type PS interface {
CPUTimes(perCPU, totalCPU bool) ([]cpu.TimesStat, error)
DiskUsage(mountPointFilter []string, fstypeExclude []string) ([]*disk.UsageStat, []*disk.PartitionStat, error)
DiskUsage(mountPointFilter []string, mountOptsExclude []string, fstypeExclude []string) ([]*disk.UsageStat, []*disk.PartitionStat, error)
NetIO() ([]net.IOCountersStat, error)
NetProto() ([]net.ProtoCountersStat, error)
DiskIO(names []string) (map[string]disk.IOCountersStat, error)
@ -91,6 +91,7 @@ func newSet() *set {
func (s *SystemPS) DiskUsage(
mountPointFilter []string,
mountOptsExclude []string,
fstypeExclude []string,
) ([]*disk.UsageStat, []*disk.PartitionStat, error) {
parts, err := s.Partitions(true)
@ -102,6 +103,10 @@ func (s *SystemPS) DiskUsage(
for _, filter := range mountPointFilter {
mountPointFilterSet.add(filter)
}
mountOptFilterSet := newSet()
for _, filter := range mountOptsExclude {
mountOptFilterSet.add(filter)
}
fstypeExcludeSet := newSet()
for _, filter := range fstypeExclude {
fstypeExcludeSet.add(filter)
@ -120,9 +125,15 @@ func (s *SystemPS) DiskUsage(
var partitions []*disk.PartitionStat
hostMountPrefix := s.OSGetenv("HOST_MOUNT_PREFIX")
partitionRange:
for i := range parts {
p := parts[i]
for _, o := range p.Opts {
if !mountOptFilterSet.empty() && mountOptFilterSet.has(o) {
continue partitionRange
}
}
// If there is a filter set and if the mount point is not a
// member of the filter set, don't gather info on it.
if !mountPointFilterSet.empty() && !mountPointFilterSet.has(p.Mountpoint) {