feat: add mount option filtering to disk plugin (#11039)
This commit is contained in:
parent
fa723355f5
commit
62922de631
|
|
@ -17,6 +17,11 @@ Note that `used_percent` is calculated by doing `used / (used + free)`, _not_
|
||||||
|
|
||||||
## Ignore mount points by filesystem type.
|
## Ignore mount points by filesystem type.
|
||||||
ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs"]
|
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
|
### Docker container
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,9 @@ type DiskStats struct {
|
||||||
|
|
||||||
LegacyMountPoints []string `toml:"mountpoints" deprecated:"0.10.2;2.0.0;use 'mount_points' instead"`
|
LegacyMountPoints []string `toml:"mountpoints" deprecated:"0.10.2;2.0.0;use 'mount_points' instead"`
|
||||||
|
|
||||||
MountPoints []string `toml:"mount_points"`
|
MountPoints []string `toml:"mount_points"`
|
||||||
IgnoreFS []string `toml:"ignore_fs"`
|
IgnoreFS []string `toml:"ignore_fs"`
|
||||||
|
IgnoreMountOpts []string `toml:"ignore_mount_opts"`
|
||||||
|
|
||||||
Log telegraf.Logger `toml:"-"`
|
Log telegraf.Logger `toml:"-"`
|
||||||
}
|
}
|
||||||
|
|
@ -34,7 +35,7 @@ func (ds *DiskStats) Init() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ds *DiskStats) Gather(acc telegraf.Accumulator) 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 {
|
if err != nil {
|
||||||
return fmt.Errorf("error getting disk usage info: %s", err)
|
return fmt.Errorf("error getting disk usage info: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,12 @@ func TestDiskUsage(t *testing.T) {
|
||||||
Fstype: "ext4",
|
Fstype: "ext4",
|
||||||
Opts: []string{"rw", "noatime", "nodiratime", "errors=remount-ro"},
|
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{
|
duAll := []diskUtil.UsageStat{
|
||||||
{
|
{
|
||||||
|
|
@ -65,18 +71,29 @@ func TestDiskUsage(t *testing.T) {
|
||||||
InodesFree: 468,
|
InodesFree: 468,
|
||||||
InodesUsed: 2000,
|
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("Partitions", true).Return(psAll, nil)
|
||||||
mps.On("OSGetenv", "HOST_MOUNT_PREFIX").Return("")
|
mps.On("OSGetenv", "HOST_MOUNT_PREFIX").Return("")
|
||||||
mps.On("PSDiskUsage", "/").Return(&duAll[0], nil)
|
mps.On("PSDiskUsage", "/").Return(&duAll[0], nil)
|
||||||
mps.On("PSDiskUsage", "/home").Return(&duAll[1], nil)
|
mps.On("PSDiskUsage", "/home").Return(&duAll[1], nil)
|
||||||
|
mps.On("PSDiskUsage", "/var/rootbind").Return(&duAll[2], nil)
|
||||||
|
|
||||||
err = (&DiskStats{ps: mps}).Gather(&acc)
|
err = (&DiskStats{ps: mps}).Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
numDiskMetrics := acc.NFields()
|
numDiskMetrics := acc.NFields()
|
||||||
expectedAllDiskMetrics := 14
|
expectedAllDiskMetrics := 21
|
||||||
require.Equal(t, expectedAllDiskMetrics, numDiskMetrics)
|
require.Equal(t, expectedAllDiskMetrics, numDiskMetrics)
|
||||||
|
|
||||||
tags1 := map[string]string{
|
tags1 := map[string]string{
|
||||||
|
|
@ -91,6 +108,12 @@ func TestDiskUsage(t *testing.T) {
|
||||||
"device": "sdb",
|
"device": "sdb",
|
||||||
"mode": "rw",
|
"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{}{
|
fields1 := map[string]interface{}{
|
||||||
"total": uint64(128),
|
"total": uint64(128),
|
||||||
|
|
@ -110,20 +133,35 @@ func TestDiskUsage(t *testing.T) {
|
||||||
"inodes_used": uint64(2000),
|
"inodes_used": uint64(2000),
|
||||||
"used_percent": float64(81.30081300813008),
|
"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", fields1, tags1)
|
||||||
acc.AssertContainsTaggedFields(t, "disk", fields2, tags2)
|
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
|
// and /home not matching the /dev in MountPoints
|
||||||
err = (&DiskStats{ps: &mps, MountPoints: []string{"/", "/dev"}}).Gather(&acc)
|
err = (&DiskStats{ps: &mps, MountPoints: []string{"/", "/dev"}}).Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expectedAllDiskMetrics+7, acc.NFields())
|
require.Equal(t, expectedAllDiskMetrics+7, acc.NFields())
|
||||||
|
|
||||||
// We should see all the diskpoints as MountPoints includes both
|
// We should see all the diskpoints as MountPoints includes both
|
||||||
// / and /home
|
// /, /home, and /var/rootbind
|
||||||
err = (&DiskStats{ps: &mps, MountPoints: []string{"/", "/home"}}).Gather(&acc)
|
err = (&DiskStats{ps: &mps, MountPoints: []string{"/", "/home", "/var/rootbind"}}).Gather(&acc)
|
||||||
require.NoError(t, err)
|
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) {
|
func TestDiskUsageHostMountPrefix(t *testing.T) {
|
||||||
|
|
@ -287,8 +325,18 @@ func TestDiskStats(t *testing.T) {
|
||||||
InodesFree: 468,
|
InodesFree: 468,
|
||||||
InodesUsed: 2000,
|
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: "/",
|
Path: "/",
|
||||||
Fstype: "ext4",
|
Fstype: "ext4",
|
||||||
|
|
@ -300,6 +348,28 @@ func TestDiskStats(t *testing.T) {
|
||||||
InodesUsed: 1000,
|
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{
|
psAll := []*diskUtil.PartitionStat{
|
||||||
{
|
{
|
||||||
|
|
@ -314,9 +384,15 @@ func TestDiskStats(t *testing.T) {
|
||||||
Fstype: "ext4",
|
Fstype: "ext4",
|
||||||
Opts: []string{"rw", "noatime", "nodiratime", "errors=remount-ro"},
|
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",
|
Device: "/dev/sda",
|
||||||
Mountpoint: "/",
|
Mountpoint: "/",
|
||||||
|
|
@ -324,16 +400,31 @@ func TestDiskStats(t *testing.T) {
|
||||||
Opts: []string{"ro", "noatime", "nodiratime"},
|
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(nil), []string(nil), []string(nil)).Return(duAll, psAll, nil)
|
||||||
mps.On("DiskUsage", []string{"/", "/dev"}, []string(nil)).Return(duFiltered, psFiltered, nil)
|
mps.On("DiskUsage", []string{"/", "/dev"}, []string(nil), []string(nil)).Return(duMountFiltered, psMountFiltered, nil)
|
||||||
mps.On("DiskUsage", []string{"/", "/home"}, []string(nil)).Return(duAll, psAll, 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)
|
err = (&DiskStats{ps: &mps}).Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
numDiskMetrics := acc.NFields()
|
numDiskMetrics := acc.NFields()
|
||||||
expectedAllDiskMetrics := 14
|
expectedAllDiskMetrics := 21
|
||||||
require.Equal(t, expectedAllDiskMetrics, numDiskMetrics)
|
require.Equal(t, expectedAllDiskMetrics, numDiskMetrics)
|
||||||
|
|
||||||
tags1 := map[string]string{
|
tags1 := map[string]string{
|
||||||
|
|
@ -370,17 +461,22 @@ func TestDiskStats(t *testing.T) {
|
||||||
acc.AssertContainsTaggedFields(t, "disk", fields1, tags1)
|
acc.AssertContainsTaggedFields(t, "disk", fields1, tags1)
|
||||||
acc.AssertContainsTaggedFields(t, "disk", fields2, tags2)
|
acc.AssertContainsTaggedFields(t, "disk", fields2, tags2)
|
||||||
|
|
||||||
// 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
|
// and /home and /var/rootbind not matching the /dev in MountPoints
|
||||||
err = (&DiskStats{ps: &mps, MountPoints: []string{"/", "/dev"}}).Gather(&acc)
|
err = (&DiskStats{ps: &mps, MountPoints: []string{"/", "/dev"}}).Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expectedAllDiskMetrics+7, acc.NFields())
|
require.Equal(t, expectedAllDiskMetrics+7, acc.NFields())
|
||||||
|
|
||||||
// We should see all the diskpoints as MountPoints includes both
|
// We should see all the diskpoints as MountPoints includes both
|
||||||
// / and /home
|
// /, /home, and /var/rootbind
|
||||||
err = (&DiskStats{ps: &mps, MountPoints: []string{"/", "/home"}}).Gather(&acc)
|
err = (&DiskStats{ps: &mps, MountPoints: []string{"/", "/home", "/var/rootbind"}}).Gather(&acc)
|
||||||
require.NoError(t, err)
|
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) {
|
func TestDiskUsageIssues(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,8 @@ func (m *MockPS) CPUTimes(_, _ bool) ([]cpu.TimesStat, error) {
|
||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockPS) DiskUsage(mountPointFilter []string, fstypeExclude []string) ([]*disk.UsageStat, []*disk.PartitionStat, error) {
|
func (m *MockPS) DiskUsage(mountPointFilter []string, mountOptsExclude []string, fstypeExclude []string) ([]*disk.UsageStat, []*disk.PartitionStat, error) {
|
||||||
ret := m.Called(mountPointFilter, fstypeExclude)
|
ret := m.Called(mountPointFilter, mountOptsExclude, fstypeExclude)
|
||||||
|
|
||||||
r0 := ret.Get(0).([]*disk.UsageStat)
|
r0 := ret.Get(0).([]*disk.UsageStat)
|
||||||
r1 := ret.Get(1).([]*disk.PartitionStat)
|
r1 := ret.Get(1).([]*disk.PartitionStat)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import (
|
||||||
|
|
||||||
type PS interface {
|
type PS interface {
|
||||||
CPUTimes(perCPU, totalCPU bool) ([]cpu.TimesStat, error)
|
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)
|
NetIO() ([]net.IOCountersStat, error)
|
||||||
NetProto() ([]net.ProtoCountersStat, error)
|
NetProto() ([]net.ProtoCountersStat, error)
|
||||||
DiskIO(names []string) (map[string]disk.IOCountersStat, error)
|
DiskIO(names []string) (map[string]disk.IOCountersStat, error)
|
||||||
|
|
@ -91,6 +91,7 @@ func newSet() *set {
|
||||||
|
|
||||||
func (s *SystemPS) DiskUsage(
|
func (s *SystemPS) DiskUsage(
|
||||||
mountPointFilter []string,
|
mountPointFilter []string,
|
||||||
|
mountOptsExclude []string,
|
||||||
fstypeExclude []string,
|
fstypeExclude []string,
|
||||||
) ([]*disk.UsageStat, []*disk.PartitionStat, error) {
|
) ([]*disk.UsageStat, []*disk.PartitionStat, error) {
|
||||||
parts, err := s.Partitions(true)
|
parts, err := s.Partitions(true)
|
||||||
|
|
@ -102,6 +103,10 @@ func (s *SystemPS) DiskUsage(
|
||||||
for _, filter := range mountPointFilter {
|
for _, filter := range mountPointFilter {
|
||||||
mountPointFilterSet.add(filter)
|
mountPointFilterSet.add(filter)
|
||||||
}
|
}
|
||||||
|
mountOptFilterSet := newSet()
|
||||||
|
for _, filter := range mountOptsExclude {
|
||||||
|
mountOptFilterSet.add(filter)
|
||||||
|
}
|
||||||
fstypeExcludeSet := newSet()
|
fstypeExcludeSet := newSet()
|
||||||
for _, filter := range fstypeExclude {
|
for _, filter := range fstypeExclude {
|
||||||
fstypeExcludeSet.add(filter)
|
fstypeExcludeSet.add(filter)
|
||||||
|
|
@ -120,9 +125,15 @@ func (s *SystemPS) DiskUsage(
|
||||||
var partitions []*disk.PartitionStat
|
var partitions []*disk.PartitionStat
|
||||||
hostMountPrefix := s.OSGetenv("HOST_MOUNT_PREFIX")
|
hostMountPrefix := s.OSGetenv("HOST_MOUNT_PREFIX")
|
||||||
|
|
||||||
|
partitionRange:
|
||||||
for i := range parts {
|
for i := range parts {
|
||||||
p := parts[i]
|
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
|
// 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.
|
// member of the filter set, don't gather info on it.
|
||||||
if !mountPointFilterSet.empty() && !mountPointFilterSet.has(p.Mountpoint) {
|
if !mountPointFilterSet.empty() && !mountPointFilterSet.has(p.Mountpoint) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue