Skip to content

Commit

Permalink
feat: add mount option filtering to disk plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
nferch committed Apr 27, 2022
1 parent a6f278a commit c85a6ff
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 22 deletions.
3 changes: 3 additions & 0 deletions etc/telegraf.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3166,6 +3166,9 @@
## Ignore mount points by filesystem type.
ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs"]

## Ignore mount points by mount options.
ignore_mount_opts = ["bind"]


# Read metrics about disk IO by device
[[inputs.diskio]]
Expand Down
3 changes: 3 additions & 0 deletions plugins/inputs/disk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ 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.
ignore_mount_opts = ["bind"]
```

### Docker container
Expand Down
7 changes: 4 additions & 3 deletions plugins/inputs/disk/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ type DiskStats struct {
// Legacy support
LegacyMountPoints []string `toml:"mountpoints"`

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:"-"`
}
Expand All @@ -35,7 +36,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)
}
Expand Down
128 changes: 112 additions & 16 deletions plugins/inputs/disk/disk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
{
Expand All @@ -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{
Expand All @@ -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),
Expand All @@ -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) {
Expand Down Expand Up @@ -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",
Expand All @@ -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{
{
Expand All @@ -314,26 +384,47 @@ 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: "/",
Fstype: "ext4",
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{
Expand Down Expand Up @@ -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, 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, 2*expectedAllDiskMetrics+7, acc.NFields())
require.Equal(t, expectedAllDiskMetrics+7*6, acc.NFields())
}

func TestDiskUsageIssues(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions plugins/inputs/system/mock_PS.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
13 changes: 12 additions & 1 deletion plugins/inputs/system/ps.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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) {
Expand Down

0 comments on commit c85a6ff

Please sign in to comment.