1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View file

@ -0,0 +1,174 @@
# DiskIO Input Plugin
This plugin gathers metrics about disk traffic and timing.
⭐ Telegraf v0.10.0
🏷️ system
💻 all
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
In addition to the plugin-specific configuration settings, plugins support
additional global and plugin configuration settings. These settings are used to
modify metrics, tags, and field or create aliases and configure ordering, etc.
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
## Configuration
```toml @sample.conf
# Read metrics about disk IO by device
[[inputs.diskio]]
## Devices to collect stats for
## Wildcards are supported except for disk synonyms like '/dev/disk/by-id'.
## ex. devices = ["sda", "sdb", "vd*", "/dev/disk/by-id/nvme-eui.00123deadc0de123"]
# devices = ["*"]
## Skip gathering of the disk's serial numbers.
# skip_serial_number = true
## Device metadata tags to add on systems supporting it (Linux only)
## Use 'udevadm info -q property -n <device>' to get a list of properties.
## Note: Most, but not all, udev properties can be accessed this way. Properties
## that are currently inaccessible include DEVTYPE, DEVNAME, and DEVPATH.
# device_tags = ["ID_FS_TYPE", "ID_FS_USAGE"]
## Using the same metadata source as device_tags, you can also customize the
## name of the device via templates.
## The 'name_templates' parameter is a list of templates to try and apply to
## the device. The template may contain variables in the form of '$PROPERTY' or
## '${PROPERTY}'. The first template which does not contain any variables not
## present for the device is used as the device name tag.
## The typical use case is for LVM volumes, to get the VG/LV name instead of
## the near-meaningless DM-0 name.
# name_templates = ["$ID_FS_LABEL","$DM_VG_NAME/$DM_LV_NAME"]
```
### Docker container
To monitor the Docker engine host from within a container you will need to
mount the host's filesystem into the container and set the `HOST_PROC`
environment variable to the location of the `/proc` filesystem. Additionally,
it is required to use privileged mode to provide access to `/dev`.
If you are using the `device_tags` or `name_templates` options, you will need
to bind mount `/run/udev` into the container.
```shell
docker run --privileged -v /:/hostfs:ro -v /run/udev:/run/udev:ro -e HOST_PROC=/hostfs/proc telegraf
```
## Metrics
- diskio
- tags:
- name (device name)
- serial (device serial number)
- fields:
- reads (integer, counter)
- writes (integer, counter)
- read_bytes (integer, counter, bytes)
- write_bytes (integer, counter, bytes)
- read_time (integer, counter, milliseconds)
- write_time (integer, counter, milliseconds)
- io_time (integer, counter, milliseconds)
- weighted_io_time (integer, counter, milliseconds)
- iops_in_progress (integer, gauge)
- merged_reads (integer, counter)
- merged_writes (integer, counter)
- io_util (float64, gauge, percent)
- io_await (float64, gauge, milliseconds)
- io_svctm (float64, gauge, milliseconds)
On linux these values correspond to the values in [`/proc/diskstats`][1] and
[`/sys/block/<dev>/stat`][2].
[1]: https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats
[2]: https://www.kernel.org/doc/Documentation/block/stat.txt
### `reads` & `writes`
These values increment when an I/O request completes.
### `read_bytes` & `write_bytes`
These values count the number of bytes read from or written to this
block device.
### `read_time` & `write_time`
These values count the number of milliseconds that I/O requests have
waited on this block device. If there are multiple I/O requests waiting,
these values will increase at a rate greater than 1000/second; for
example, if 60 read requests wait for an average of 30 ms, the read_time
field will increase by 60*30 = 1800.
### `io_time`
This value counts the number of milliseconds during which the device has
had I/O requests queued.
### `weighted_io_time`
This value counts the number of milliseconds that I/O requests have waited
on this block device. If there are multiple I/O requests waiting, this
value will increase as the product of the number of milliseconds times the
number of requests waiting (see `read_time` above for an example).
### `iops_in_progress`
This value counts the number of I/O requests that have been issued to
the device driver but have not yet completed. It does not include I/O
requests that are in the queue but not yet issued to the device driver.
### `merged_reads` & `merged_writes`
Reads and writes which are adjacent to each other may be merged for
efficiency. Thus two 4K reads may become one 8K read before it is
ultimately handed to the disk, and so it will be counted (and queued)
as only one I/O. These fields lets you know how often this was done.
### `io_await`
The average time per I/O operation (ms)
### `io_svctm`
The service time per I/O operation, excluding wait time (ms)
### `io_util`
The percentage of time the disk was active (%)
## Sample Queries
### Calculate percent IO utilization per disk and host
```sql
SELECT non_negative_derivative(last("io_time"),1ms) FROM "diskio" WHERE time > now() - 30m GROUP BY "host","name",time(60s)
```
### Calculate average queue depth
`iops_in_progress` will give you an instantaneous value. This will give you the
average between polling intervals.
```sql
SELECT non_negative_derivative(last("weighted_io_time"),1ms) from "diskio" WHERE time > now() - 30m GROUP BY "host","name",time(60s)
```
## Example Output
```text
diskio,name=sda1 merged_reads=0i,reads=2353i,writes=10i,write_bytes=2117632i,write_time=49i,io_time=1271i,weighted_io_time=1350i,read_bytes=31350272i,read_time=1303i,iops_in_progress=0i,merged_writes=0i 1578326400000000000
diskio,name=centos/var_log reads=1063077i,writes=591025i,read_bytes=139325491712i,write_bytes=144233131520i,read_time=650221i,write_time=24368817i,io_time=852490i,weighted_io_time=25037394i,iops_in_progress=1i,merged_reads=0i,merged_writes=0i 1578326400000000000
diskio,name=sda write_time=49i,io_time=1317i,weighted_io_time=1404i,reads=2495i,read_time=1357i,write_bytes=2117632i,iops_in_progress=0i,merged_reads=0i,merged_writes=0i,writes=10i,read_bytes=38956544i 1578326400000000000
```
```text
diskio,name=sda io_await:0.3317307692307692,io_svctm:0.07692307692307693,io_util:0.5329780146568954 1578326400000000000
diskio,name=sda1 io_await:0.3317307692307692,io_svctm:0.07692307692307693,io_util:0.5329780146568954 1578326400000000000
diskio,name=sda2 io_await:0.3317307692307692,io_svctm:0.07692307692307693,io_util:0.5329780146568954 1578326400000000000
```

View file

@ -0,0 +1,225 @@
//go:generate ../../../tools/readme_config_includer/generator
package diskio
import (
_ "embed"
"fmt"
"regexp"
"strings"
"time"
"github.com/shirou/gopsutil/v4/disk"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/filter"
"github.com/influxdata/telegraf/plugins/common/psutil"
"github.com/influxdata/telegraf/plugins/inputs"
)
//go:embed sample.conf
var sampleConfig string
var (
varRegex = regexp.MustCompile(`\$(?:\w+|\{\w+\})`)
)
type DiskIO struct {
Devices []string `toml:"devices"`
DeviceTags []string `toml:"device_tags"`
NameTemplates []string `toml:"name_templates"`
SkipSerialNumber bool `toml:"skip_serial_number"`
Log telegraf.Logger `toml:"-"`
ps psutil.PS
infoCache map[string]diskInfoCache
deviceFilter filter.Filter
warnDiskName map[string]bool
warnDiskTags map[string]bool
lastIOCounterStat map[string]disk.IOCountersStat
lastCollectTime time.Time
}
func (*DiskIO) SampleConfig() string {
return sampleConfig
}
func (d *DiskIO) Init() error {
for _, device := range d.Devices {
if hasMeta(device) {
deviceFilter, err := filter.Compile(d.Devices)
if err != nil {
return fmt.Errorf("error compiling device pattern: %w", err)
}
d.deviceFilter = deviceFilter
}
}
d.infoCache = make(map[string]diskInfoCache)
d.warnDiskName = make(map[string]bool)
d.warnDiskTags = make(map[string]bool)
d.lastIOCounterStat = make(map[string]disk.IOCountersStat)
return nil
}
func (d *DiskIO) Gather(acc telegraf.Accumulator) error {
var devices []string
if d.deviceFilter == nil {
for _, dev := range d.Devices {
devices = append(devices, resolveName(dev))
}
}
diskio, err := d.ps.DiskIO(devices)
if err != nil {
return fmt.Errorf("error getting disk io info: %w", err)
}
collectTime := time.Now()
for k, io := range diskio {
match := false
if d.deviceFilter != nil && d.deviceFilter.Match(io.Name) {
match = true
}
tags := make(map[string]string)
var devLinks []string
tags["name"], devLinks = d.diskName(io.Name)
if wwid := getDeviceWWID(io.Name); wwid != "" {
tags["wwid"] = wwid
}
if d.deviceFilter != nil && !match {
for _, devLink := range devLinks {
if d.deviceFilter.Match(devLink) {
match = true
break
}
}
if !match {
continue
}
}
for t, v := range d.diskTags(io.Name) {
tags[t] = v
}
if !d.SkipSerialNumber {
if len(io.SerialNumber) != 0 {
tags["serial"] = io.SerialNumber
} else {
tags["serial"] = "unknown"
}
}
fields := map[string]interface{}{
"reads": io.ReadCount,
"writes": io.WriteCount,
"read_bytes": io.ReadBytes,
"write_bytes": io.WriteBytes,
"read_time": io.ReadTime,
"write_time": io.WriteTime,
"io_time": io.IoTime,
"weighted_io_time": io.WeightedIO,
"iops_in_progress": io.IopsInProgress,
"merged_reads": io.MergedReadCount,
"merged_writes": io.MergedWriteCount,
}
if lastValue, exists := d.lastIOCounterStat[k]; exists {
deltaRWCount := float64(io.ReadCount + io.WriteCount - lastValue.ReadCount - lastValue.WriteCount)
deltaRWTime := float64(io.ReadTime + io.WriteTime - lastValue.ReadTime - lastValue.WriteTime)
deltaIOTime := float64(io.IoTime - lastValue.IoTime)
if deltaRWCount > 0 {
fields["io_await"] = deltaRWTime / deltaRWCount
fields["io_svctm"] = deltaIOTime / deltaRWCount
}
itv := float64(collectTime.Sub(d.lastCollectTime).Milliseconds())
if itv > 0 {
fields["io_util"] = 100 * deltaIOTime / itv
}
}
acc.AddCounter("diskio", fields, tags)
}
d.lastCollectTime = collectTime
d.lastIOCounterStat = diskio
return nil
}
// hasMeta reports whether s contains any special glob characters.
func hasMeta(s string) bool {
return strings.ContainsAny(s, "*?[")
}
func (d *DiskIO) diskName(devName string) (string, []string) {
di, err := d.diskInfo(devName)
devLinks := strings.Split(di["DEVLINKS"], " ")
for i, devLink := range devLinks {
devLinks[i] = strings.TrimPrefix(devLink, "/dev/")
}
// Return error after attempting to process some of the devlinks.
// These could exist if we got further along the diskInfo call.
if err != nil {
if ok := d.warnDiskName[devName]; !ok {
d.warnDiskName[devName] = true
d.Log.Warnf("Unable to gather disk name for %q: %s", devName, err)
}
return devName, devLinks
}
if len(d.NameTemplates) == 0 {
return devName, devLinks
}
for _, nt := range d.NameTemplates {
miss := false
name := varRegex.ReplaceAllStringFunc(nt, func(sub string) string {
sub = sub[1:] // strip leading '$'
if sub[0] == '{' {
sub = sub[1 : len(sub)-1] // strip leading & trailing '{' '}'
}
if v, ok := di[sub]; ok {
return v
}
miss = true
return ""
})
if !miss {
return name, devLinks
}
}
return devName, devLinks
}
func (d *DiskIO) diskTags(devName string) map[string]string {
if len(d.DeviceTags) == 0 {
return nil
}
di, err := d.diskInfo(devName)
if err != nil {
if ok := d.warnDiskTags[devName]; !ok {
d.warnDiskTags[devName] = true
d.Log.Warnf("Unable to gather disk tags for %q: %s", devName, err)
}
return nil
}
tags := make(map[string]string, len(d.DeviceTags))
for _, dt := range d.DeviceTags {
if v, ok := di[dt]; ok {
tags[dt] = v
}
}
return tags
}
func init() {
ps := psutil.NewSystemPS()
inputs.Add("diskio", func() telegraf.Input {
return &DiskIO{ps: ps, SkipSerialNumber: true}
})
}

View file

@ -0,0 +1,192 @@
package diskio
import (
"bufio"
"bytes"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"golang.org/x/sys/unix"
)
type diskInfoCache struct {
modifiedAt int64 // Unix Nano timestamp of the last modification of the device. This value is used to invalidate the cache
udevDataPath string
sysBlockPath string
values map[string]string
}
func (d *DiskIO) diskInfo(devName string) (map[string]string, error) {
// Check if the device exists
path := "/dev/" + devName
var stat unix.Stat_t
if err := unix.Stat(path, &stat); err != nil {
return nil, fmt.Errorf("error reading %s: %w", path, err)
}
// Check if we already got a cached and valid entry
ic, ok := d.infoCache[devName]
if ok && stat.Mtim.Nano() == ic.modifiedAt {
return ic.values, nil
}
// Determine udev properties
var udevDataPath string
if ok && len(ic.udevDataPath) > 0 {
// We can reuse the udev data path from a "previous" entry.
// This allows us to also "poison" it during test scenarios
udevDataPath = ic.udevDataPath
} else {
major := unix.Major(uint64(stat.Rdev)) //nolint:unconvert // Conversion needed for some architectures
minor := unix.Minor(uint64(stat.Rdev)) //nolint:unconvert // Conversion needed for some architectures
udevDataPath = fmt.Sprintf("/run/udev/data/b%d:%d", major, minor)
if _, err := os.Stat(udevDataPath); err != nil {
// This path failed, try the fallback .udev style (non-systemd)
udevDataPath = "/dev/.udev/db/block:" + devName
if _, err := os.Stat(udevDataPath); err != nil {
// Giving up, cannot retrieve disk info
return nil, fmt.Errorf("error reading %s: %w", udevDataPath, err)
}
}
}
info, err := readUdevData(udevDataPath)
if err != nil {
return nil, err
}
// Read additional (optional) device properties
var sysBlockPath string
if ok && len(ic.sysBlockPath) > 0 {
// We can reuse the /sys block path from a "previous" entry.
// This allows us to also "poison" it during test scenarios
sysBlockPath = ic.sysBlockPath
} else {
sysBlockPath = "/sys/class/block/" + devName
}
devInfo, err := readDevData(sysBlockPath)
if err == nil {
for k, v := range devInfo {
info[k] = v
}
} else if !errors.Is(err, os.ErrNotExist) {
return nil, err
}
d.infoCache[devName] = diskInfoCache{
modifiedAt: stat.Mtim.Nano(),
udevDataPath: udevDataPath,
values: info,
}
return info, nil
}
func readUdevData(path string) (map[string]string, error) {
// Final open of the confirmed (or the previously detected/used) udev file
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
info := make(map[string]string)
scnr := bufio.NewScanner(f)
var devlinks bytes.Buffer
for scnr.Scan() {
l := scnr.Text()
if len(l) < 4 {
continue
}
if l[:2] == "S:" {
if devlinks.Len() > 0 {
devlinks.WriteString(" ")
}
devlinks.WriteString("/dev/")
devlinks.WriteString(l[2:])
continue
}
if l[:2] != "E:" {
continue
}
kv := strings.SplitN(l[2:], "=", 2)
if len(kv) < 2 {
continue
}
info[kv[0]] = kv[1]
}
if devlinks.Len() > 0 {
info["DEVLINKS"] = devlinks.String()
}
return info, nil
}
func readDevData(path string) (map[string]string, error) {
// Open the file and read line-wise
f, err := os.Open(filepath.Join(path, "uevent"))
if err != nil {
return nil, err
}
defer f.Close()
// Read DEVNAME and DEVTYPE
info := make(map[string]string)
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
if !strings.HasPrefix(line, "DEV") {
continue
}
k, v, found := strings.Cut(line, "=")
if !found {
continue
}
info[strings.TrimSpace(k)] = strings.TrimSpace(v)
}
if d, found := info["DEVNAME"]; found && !strings.HasPrefix(d, "/dev") {
info["DEVNAME"] = "/dev/" + d
}
// Find the DEVPATH property
if devlnk, err := filepath.EvalSymlinks(filepath.Join(path, "device")); err == nil {
devlnk = filepath.Join(devlnk, filepath.Base(path))
devlnk = strings.TrimPrefix(devlnk, "/sys")
info["DEVPATH"] = devlnk
}
return info, nil
}
func resolveName(name string) string {
resolved, err := filepath.EvalSymlinks(name)
if err == nil {
return resolved
}
if !errors.Is(err, fs.ErrNotExist) {
return name
}
// Try to prepend "/dev"
resolved, err = filepath.EvalSymlinks("/dev/" + name)
if err != nil {
return name
}
return resolved
}
func getDeviceWWID(name string) string {
path := fmt.Sprintf("/sys/block/%s/wwid", filepath.Base(name))
buf, err := os.ReadFile(path)
if err != nil {
return ""
}
return strings.TrimSuffix(string(buf), "\n")
}

View file

@ -0,0 +1,84 @@
//go:build linux
package diskio
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func TestDiskInfo(t *testing.T) {
plugin := &DiskIO{
infoCache: map[string]diskInfoCache{
"null": {
modifiedAt: 0,
udevDataPath: "testdata/udev.txt",
sysBlockPath: "testdata",
values: map[string]string{},
},
},
}
di, err := plugin.diskInfo("null")
require.NoError(t, err)
require.Equal(t, "myval1", di["MY_PARAM_1"])
require.Equal(t, "myval2", di["MY_PARAM_2"])
require.Equal(t, "/dev/foo/bar/devlink /dev/foo/bar/devlink1", di["DEVLINKS"])
}
// DiskIOStats.diskName isn't a linux specific function, but dependent
// functions are a no-op on non-Linux.
func TestDiskIOStats_diskName(t *testing.T) {
tests := []struct {
templates []string
expected string
}{
{[]string{"$MY_PARAM_1"}, "myval1"},
{[]string{"${MY_PARAM_1}"}, "myval1"},
{[]string{"x$MY_PARAM_1"}, "xmyval1"},
{[]string{"x${MY_PARAM_1}x"}, "xmyval1x"},
{[]string{"$MISSING", "$MY_PARAM_1"}, "myval1"},
{[]string{"$MY_PARAM_1", "$MY_PARAM_2"}, "myval1"},
{[]string{"$MISSING"}, "null"},
{[]string{"$MY_PARAM_1/$MY_PARAM_2"}, "myval1/myval2"},
{[]string{"$MY_PARAM_2/$MISSING"}, "null"},
}
for i, tc := range tests {
t.Run(fmt.Sprintf("template %d", i), func(t *testing.T) {
plugin := DiskIO{
NameTemplates: tc.templates,
infoCache: map[string]diskInfoCache{
"null": {
modifiedAt: 0,
udevDataPath: "testdata/udev.txt",
sysBlockPath: "testdata",
values: map[string]string{},
},
},
}
name, _ := plugin.diskName("null")
require.Equal(t, tc.expected, name, "Templates: %#v", tc.templates)
})
}
}
// DiskIOStats.diskTags isn't a linux specific function, but dependent
// functions are a no-op on non-Linux.
func TestDiskIOStats_diskTags(t *testing.T) {
plugin := &DiskIO{
DeviceTags: []string{"MY_PARAM_2"},
infoCache: map[string]diskInfoCache{
"null": {
modifiedAt: 0,
udevDataPath: "testdata/udev.txt",
sysBlockPath: "testdata",
values: map[string]string{},
},
},
}
dt := plugin.diskTags("null")
require.Equal(t, map[string]string{"MY_PARAM_2": "myval2"}, dt)
}

View file

@ -0,0 +1,17 @@
//go:build !linux
package diskio
type diskInfoCache struct{}
func (*DiskIO) diskInfo(_ string) (map[string]string, error) {
return nil, nil
}
func resolveName(name string) string {
return name
}
func getDeviceWWID(_ string) string {
return ""
}

View file

@ -0,0 +1,192 @@
package diskio
import (
"testing"
"time"
"github.com/shirou/gopsutil/v4/disk"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf/plugins/common/psutil"
"github.com/influxdata/telegraf/testutil"
)
func TestDiskIO(t *testing.T) {
type Result struct {
stats map[string]disk.IOCountersStat
err error
}
type Metric struct {
tags map[string]string
fields map[string]interface{}
}
tests := []struct {
name string
devices []string
result Result
err error
metrics []Metric
}{
{
name: "minimal",
result: Result{
stats: map[string]disk.IOCountersStat{
"sda": {
ReadCount: 888,
WriteCount: 5341,
ReadBytes: 100000,
WriteBytes: 200000,
ReadTime: 7123,
WriteTime: 9087,
MergedReadCount: 11,
MergedWriteCount: 12,
Name: "sda",
IoTime: 123552,
SerialNumber: "ab-123-ad",
},
},
err: nil,
},
err: nil,
metrics: []Metric{
{
tags: map[string]string{
"name": "sda",
"serial": "ab-123-ad",
},
fields: map[string]interface{}{
"reads": uint64(888),
"writes": uint64(5341),
"read_bytes": uint64(100000),
"write_bytes": uint64(200000),
"read_time": uint64(7123),
"write_time": uint64(9087),
"io_time": uint64(123552),
"weighted_io_time": uint64(0),
"iops_in_progress": uint64(0),
"merged_reads": uint64(11),
"merged_writes": uint64(12),
},
},
},
},
{
name: "glob device",
devices: []string{"sd*"},
result: Result{
stats: map[string]disk.IOCountersStat{
"sda": {
Name: "sda",
ReadCount: 42,
},
"vda": {
Name: "vda",
ReadCount: 42,
},
},
err: nil,
},
err: nil,
metrics: []Metric{
{
tags: map[string]string{
"name": "sda",
"serial": "unknown",
},
fields: map[string]interface{}{
"reads": uint64(42),
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var mps psutil.MockPS
mps.On("DiskIO").Return(tt.result.stats, tt.result.err)
var acc testutil.Accumulator
diskio := &DiskIO{
Log: testutil.Logger{},
ps: &mps,
Devices: tt.devices,
}
require.NoError(t, diskio.Init())
err := diskio.Gather(&acc)
require.Equal(t, tt.err, err)
for _, metric := range tt.metrics {
for k, v := range metric.fields {
require.True(t, acc.HasPoint("diskio", metric.tags, k, v),
"missing point: diskio %v %q: %v", metric.tags, k, v)
}
}
require.Len(t, tt.metrics, int(acc.NMetrics()), "unexpected number of metrics")
require.True(t, mps.AssertExpectations(t))
})
}
}
func TestDiskIOUtil(t *testing.T) {
cts := map[string]disk.IOCountersStat{
"sda": {
ReadCount: 888,
WriteCount: 5341,
ReadBytes: 100000,
WriteBytes: 200000,
ReadTime: 7123,
WriteTime: 9087,
MergedReadCount: 11,
MergedWriteCount: 12,
Name: "sda",
IoTime: 123552,
SerialNumber: "ab-123-ad",
},
}
cts2 := map[string]disk.IOCountersStat{
"sda": {
ReadCount: 1000,
WriteCount: 6000,
ReadBytes: 200000,
WriteBytes: 300000,
ReadTime: 8123,
WriteTime: 9187,
MergedReadCount: 16,
MergedWriteCount: 30,
Name: "sda",
IoTime: 163552,
SerialNumber: "ab-123-ad",
},
}
var acc testutil.Accumulator
var mps psutil.MockPS
mps.On("DiskIO").Return(cts, nil)
diskio := &DiskIO{
Log: testutil.Logger{},
Devices: []string{"sd*"},
ps: &mps,
}
require.NoError(t, diskio.Init())
// gather
require.NoError(t, diskio.Gather(&acc))
// sleep
time.Sleep(1 * time.Second)
// gather twice
mps2 := psutil.MockPS{}
mps2.On("DiskIO").Return(cts2, nil)
diskio.ps = &mps2
err := diskio.Gather(&acc)
require.NoError(t, err)
require.True(t, acc.HasField("diskio", "io_util"), "miss io util")
require.True(t, acc.HasField("diskio", "io_svctm"), "miss io_svctm")
require.True(t, acc.HasField("diskio", "io_await"), "miss io_await")
require.True(t, acc.HasFloatField("diskio", "io_util"), "io_util not have value")
require.True(t, acc.HasFloatField("diskio", "io_svctm"), "io_svctm not have value")
require.True(t, acc.HasFloatField("diskio", "io_await"), "io_await not have value")
}

View file

@ -0,0 +1,25 @@
# Read metrics about disk IO by device
[[inputs.diskio]]
## Devices to collect stats for
## Wildcards are supported except for disk synonyms like '/dev/disk/by-id'.
## ex. devices = ["sda", "sdb", "vd*", "/dev/disk/by-id/nvme-eui.00123deadc0de123"]
# devices = ["*"]
## Skip gathering of the disk's serial numbers.
# skip_serial_number = true
## Device metadata tags to add on systems supporting it (Linux only)
## Use 'udevadm info -q property -n <device>' to get a list of properties.
## Note: Most, but not all, udev properties can be accessed this way. Properties
## that are currently inaccessible include DEVTYPE, DEVNAME, and DEVPATH.
# device_tags = ["ID_FS_TYPE", "ID_FS_USAGE"]
## Using the same metadata source as device_tags, you can also customize the
## name of the device via templates.
## The 'name_templates' parameter is a list of templates to try and apply to
## the device. The template may contain variables in the form of '$PROPERTY' or
## '${PROPERTY}'. The first template which does not contain any variables not
## present for the device is used as the device name tag.
## The typical use case is for LVM volumes, to get the VG/LV name instead of
## the near-meaningless DM-0 name.
# name_templates = ["$ID_FS_LABEL","$DM_VG_NAME/$DM_LV_NAME"]

View file

@ -0,0 +1,4 @@
E:MY_PARAM_1=myval1
E:MY_PARAM_2=myval2
S:foo/bar/devlink
S:foo/bar/devlink1

5
plugins/inputs/diskio/testdata/uevent vendored Normal file
View file

@ -0,0 +1,5 @@
MAJOR=259
MINOR=1
DEVNAME=null
DEVTYPE=disk
DISKSEQ=2