Skip to content

Commit

Permalink
fix: fix missing storage in container with disk plugin (#10318)
Browse files Browse the repository at this point in the history
  • Loading branch information
srebhan authored Jan 5, 2022
1 parent 876d190 commit 7867ef3
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 12 deletions.
16 changes: 12 additions & 4 deletions plugins/inputs/disk/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type DiskStats struct {

MountPoints []string `toml:"mount_points"`
IgnoreFS []string `toml:"ignore_fs"`

Log telegraf.Logger `toml:"-"`
}

func (ds *DiskStats) Description() string {
Expand All @@ -36,17 +38,24 @@ func (ds *DiskStats) SampleConfig() string {
return diskSampleConfig
}

func (ds *DiskStats) Gather(acc telegraf.Accumulator) error {
func (ds *DiskStats) Init() error {
// Legacy support:
if len(ds.LegacyMountPoints) != 0 {
ds.MountPoints = ds.LegacyMountPoints
}

ps := system.NewSystemPS()
ps.Log = ds.Log
ds.ps = ps

return nil
}

func (ds *DiskStats) Gather(acc telegraf.Accumulator) error {
disks, partitions, err := ds.ps.DiskUsage(ds.MountPoints, ds.IgnoreFS)
if err != nil {
return fmt.Errorf("error getting disk usage info: %s", err)
}

for i, du := range disks {
if du.Total == 0 {
// Skip dummy filesystem (procfs, cgroupfs, ...)
Expand Down Expand Up @@ -102,8 +111,7 @@ func (opts MountOptions) exists(opt string) bool {
}

func init() {
ps := system.NewSystemPS()
inputs.Add("disk", func() telegraf.Input {
return &DiskStats{ps: ps}
return &DiskStats{}
})
}
170 changes: 170 additions & 0 deletions plugins/inputs/disk/disk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ package disk
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"

diskUtil "github.com/shirou/gopsutil/v3/disk"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"

"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs/system"
"github.com/influxdata/telegraf/testutil"
)
Expand Down Expand Up @@ -377,3 +382,168 @@ func TestDiskStats(t *testing.T) {
require.NoError(t, err)
require.Equal(t, 2*expectedAllDiskMetrics+7, acc.NFields())
}

func TestDiskUsageIssues(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("Skipping due to Linux-only test-cases...")
}

tests := []struct {
name string
prefix string
du diskUtil.UsageStat
expected []telegraf.Metric
}{
{
name: "success",
prefix: "",
du: diskUtil.UsageStat{
Total: 256,
Free: 46,
Used: 200,
InodesTotal: 2468,
InodesFree: 468,
InodesUsed: 2000,
},
expected: []telegraf.Metric{
testutil.MustMetric(
"disk",
map[string]string{
"device": "tmpfs",
"fstype": "tmpfs",
"mode": "rw",
"path": "/tmp",
},
map[string]interface{}{
"total": uint64(256),
"used": uint64(200),
"free": uint64(46),
"inodes_total": uint64(2468),
"inodes_free": uint64(468),
"inodes_used": uint64(2000),
"used_percent": float64(81.30081300813008),
},
time.Unix(0, 0),
telegraf.Gauge,
),
testutil.MustMetric(
"disk",
map[string]string{
"device": "nvme0n1p4",
"fstype": "ext4",
"mode": "rw",
"path": "/",
},
map[string]interface{}{
"total": uint64(256),
"used": uint64(200),
"free": uint64(46),
"inodes_total": uint64(2468),
"inodes_free": uint64(468),
"inodes_used": uint64(2000),
"used_percent": float64(81.30081300813008),
},
time.Unix(0, 0),
telegraf.Gauge,
),
},
},
{
name: "issue 10297",
prefix: "/host",
du: diskUtil.UsageStat{
Total: 256,
Free: 46,
Used: 200,
InodesTotal: 2468,
InodesFree: 468,
InodesUsed: 2000,
},
expected: []telegraf.Metric{
testutil.MustMetric(
"disk",
map[string]string{
"device": "sda1",
"fstype": "ext4",
"mode": "rw",
"path": "/",
},
map[string]interface{}{
"total": uint64(256),
"used": uint64(200),
"free": uint64(46),
"inodes_total": uint64(2468),
"inodes_free": uint64(468),
"inodes_used": uint64(2000),
"used_percent": float64(81.30081300813008),
},
time.Unix(0, 0),
telegraf.Gauge,
),
testutil.MustMetric(
"disk",
map[string]string{
"device": "sdb",
"fstype": "ext4",
"mode": "rw",
"path": "/mnt/storage",
},
map[string]interface{}{
"total": uint64(256),
"used": uint64(200),
"free": uint64(46),
"inodes_total": uint64(2468),
"inodes_free": uint64(468),
"inodes_used": uint64(2000),
"used_percent": float64(81.30081300813008),
},
time.Unix(0, 0),
telegraf.Gauge,
),
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Setup the environment
hostMountPrefix := tt.prefix
hostProcPrefix, err := filepath.Abs(filepath.Join("testdata", strings.ReplaceAll(tt.name, " ", "_")))
require.NoError(t, err)

// Get the partitions in the test-case
os.Clearenv()
require.NoError(t, os.Setenv("HOST_PROC", hostProcPrefix))
partitions, err := diskUtil.Partitions(true)
require.NoError(t, err)

// Mock the disk usage
mck := &mock.Mock{}
mps := system.MockPSDisk{SystemPS: &system.SystemPS{PSDiskDeps: &system.MockDiskUsage{Mock: mck}}, Mock: mck}
defer mps.AssertExpectations(t)

mps.On("Partitions", true).Return(partitions, nil)

for _, partition := range partitions {
mountpoint := partition.Mountpoint
if hostMountPrefix != "" {
mountpoint = filepath.Join(hostMountPrefix, partition.Mountpoint)
}
diskUsage := tt.du
diskUsage.Path = mountpoint
diskUsage.Fstype = partition.Fstype
mps.On("PSDiskUsage", mountpoint).Return(&diskUsage, nil)
}
mps.On("OSGetenv", "HOST_MOUNT_PREFIX").Return(hostMountPrefix)

// Setup the plugin and run the test
var acc testutil.Accumulator
plugin := &DiskStats{ps: &mps}
require.NoError(t, plugin.Gather(&acc))

actual := acc.GetTelegrafMetrics()
testutil.RequireMetricsEqual(t, tt.expected, actual, testutil.IgnoreTime(), testutil.SortMetrics())
})
}
os.Clearenv()
}
2 changes: 2 additions & 0 deletions plugins/inputs/disk/testdata/issue_10297/1/mountinfo
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
31 1 8:1 / / rw,relatime shared:1 - ext4 /dev/sda1 rw,discard,errors=remount-ro
126 31 8:16 / /mnt/storage rw,relatime shared:67 - ext4 /dev/sdb rw,discard
2 changes: 2 additions & 0 deletions plugins/inputs/disk/testdata/success/1/mountinfo
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
26 1 259:4 / / rw,relatime shared:1 - ext4 /dev/nvme0n1p4 rw
39 26 0:32 / /tmp rw,nosuid,nodev shared:17 - tmpfs tmpfs rw,size=16427752k,nr_inodes=409600,inode64
46 changes: 38 additions & 8 deletions plugins/inputs/system/ps.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"path/filepath"
"strings"

"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"

"github.com/shirou/gopsutil/v3/cpu"
Expand Down Expand Up @@ -34,11 +35,12 @@ type PSDiskDeps interface {
}

func NewSystemPS() *SystemPS {
return &SystemPS{&SystemPSDisk{}}
return &SystemPS{PSDiskDeps: &SystemPSDisk{}}
}

type SystemPS struct {
PSDiskDeps
Log telegraf.Logger `toml:"-"`
}

type SystemPSDisk struct{}
Expand Down Expand Up @@ -97,33 +99,61 @@ func (s *SystemPS) DiskUsage(
for i := range parts {
p := parts[i]

if s.Log != nil {
s.Log.Debugf("[SystemPS] partition %d: %v", i, p)
}

if len(mountPointFilter) > 0 {
// If the mount point is not a member of the filter set,
// don't gather info on it.
if _, ok := mountPointFilterSet[p.Mountpoint]; !ok {
if s.Log != nil {
s.Log.Debug("[SystemPS] => dropped by mount-point filter")
}
continue
}
}

// If the mount point is a member of the exclude set,
// don't gather info on it.
if _, ok := fstypeExcludeSet[p.Fstype]; ok {
if s.Log != nil {
s.Log.Debug("[SystemPS] => dropped by filesystem-type filter")
}
continue
}

// If there's a host mount prefix, exclude any paths which conflict
// with the prefix.
if len(hostMountPrefix) > 0 &&
!strings.HasPrefix(p.Mountpoint, hostMountPrefix) &&
paths[hostMountPrefix+p.Mountpoint] {
continue
// If there's a host mount prefix use it as newer gopsutil version check for
// the init's mountpoints usually pointing to the host-mountpoint but in the
// container. This won't work for checking the disk-usage as the disks are
// mounted at HOST_MOUNT_PREFIX...
mountpoint := p.Mountpoint
if hostMountPrefix != "" && !strings.HasPrefix(p.Mountpoint, hostMountPrefix) {
mountpoint = filepath.Join(hostMountPrefix, p.Mountpoint)
// Exclude conflicting paths
if paths[mountpoint] {
if s.Log != nil {
s.Log.Debug("[SystemPS] => dropped by mount prefix")
}
continue
}
}
if s.Log != nil {
s.Log.Debugf("[SystemPS] -> using mountpoint %q...", mountpoint)
}

du, err := s.PSDiskUsage(p.Mountpoint)
du, err := s.PSDiskUsage(mountpoint)
if err != nil {
if s.Log != nil {
s.Log.Debugf("[SystemPS] => dropped by disk usage (%q): %v", mountpoint, err)
}
continue
}

if s.Log != nil {
s.Log.Debug("[SystemPS] => kept...")
}

du.Path = filepath.Join("/", strings.TrimPrefix(p.Mountpoint, hostMountPrefix))
du.Fstype = p.Fstype
usage = append(usage, du)
Expand Down

0 comments on commit 7867ef3

Please sign in to comment.