Add zfs pool stats collection.
This commit is contained in:
parent
d62e63c448
commit
eb78b9268f
|
@ -1,6 +1,7 @@
|
||||||
package zfs
|
package zfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -12,6 +13,7 @@ import (
|
||||||
type Zfs struct {
|
type Zfs struct {
|
||||||
KstatPath string
|
KstatPath string
|
||||||
KstatMetrics []string
|
KstatMetrics []string
|
||||||
|
PoolMetrics bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var sampleConfig = `
|
var sampleConfig = `
|
||||||
|
@ -22,6 +24,9 @@ var sampleConfig = `
|
||||||
# By default, telegraf gather all zfs stats
|
# By default, telegraf gather all zfs stats
|
||||||
# If not specified, then default is:
|
# If not specified, then default is:
|
||||||
# kstatMetrics = ["arcstats", "zfetchstats", "vdev_cache_stats"]
|
# kstatMetrics = ["arcstats", "zfetchstats", "vdev_cache_stats"]
|
||||||
|
#
|
||||||
|
# By default, don't gather zpool stats
|
||||||
|
# poolMetrics = false
|
||||||
`
|
`
|
||||||
|
|
||||||
func (z *Zfs) SampleConfig() string {
|
func (z *Zfs) SampleConfig() string {
|
||||||
|
@ -32,7 +37,7 @@ func (z *Zfs) Description() string {
|
||||||
return "Read metrics of ZFS from arcstats, zfetchstats and vdev_cache_stats"
|
return "Read metrics of ZFS from arcstats, zfetchstats and vdev_cache_stats"
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTags(kstatPath string) map[string]string {
|
func getPoolStats(kstatPath string, poolMetrics bool, acc plugins.Accumulator) (map[string]string, error) {
|
||||||
var pools string
|
var pools string
|
||||||
poolsDirs, _ := filepath.Glob(kstatPath + "/*/io")
|
poolsDirs, _ := filepath.Glob(kstatPath + "/*/io")
|
||||||
for _, poolDir := range poolsDirs {
|
for _, poolDir := range poolsDirs {
|
||||||
|
@ -42,8 +47,49 @@ func getTags(kstatPath string) map[string]string {
|
||||||
pools += "::"
|
pools += "::"
|
||||||
}
|
}
|
||||||
pools += pool
|
pools += pool
|
||||||
|
|
||||||
|
if poolMetrics {
|
||||||
|
err := readPoolStats(poolDir, pool, acc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return map[string]string{"pools": pools}
|
|
||||||
|
return map[string]string{"pools": pools}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readPoolStats(poolIoPath string, poolName string, acc plugins.Accumulator) error {
|
||||||
|
lines, err := internal.ReadLines(poolIoPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(lines) != 3 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := strings.Fields(lines[1])
|
||||||
|
values := strings.Fields(lines[2])
|
||||||
|
|
||||||
|
keyCount := len(keys)
|
||||||
|
|
||||||
|
if keyCount != len(values) {
|
||||||
|
return fmt.Errorf("Key and value count don't match Keys:%v Values:%v", keys, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
tag := map[string]string{"pool": poolName}
|
||||||
|
|
||||||
|
for i := 0; i < keyCount; i++ {
|
||||||
|
value, err := strconv.ParseInt(values[i], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
acc.Add(keys[i], value, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (z *Zfs) Gather(acc plugins.Accumulator) error {
|
func (z *Zfs) Gather(acc plugins.Accumulator) error {
|
||||||
|
@ -57,7 +103,10 @@ func (z *Zfs) Gather(acc plugins.Accumulator) error {
|
||||||
kstatPath = "/proc/spl/kstat/zfs"
|
kstatPath = "/proc/spl/kstat/zfs"
|
||||||
}
|
}
|
||||||
|
|
||||||
tags := getTags(kstatPath)
|
tags, err := getPoolStats(kstatPath, z.PoolMetrics, acc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for _, metric := range kstatMetrics {
|
for _, metric := range kstatMetrics {
|
||||||
lines, err := internal.ReadLines(kstatPath + "/" + metric)
|
lines, err := internal.ReadLines(kstatPath + "/" + metric)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package zfs
|
package zfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -121,6 +120,10 @@ delegations 4 0
|
||||||
hits 4 0
|
hits 4 0
|
||||||
misses 4 0
|
misses 4 0
|
||||||
`
|
`
|
||||||
|
const pool_ioContents = `11 3 0x00 1 80 2225326830828 32953476980628
|
||||||
|
nread nwritten reads writes wtime wlentime wupdate rtime rlentime rupdate wcnt rcnt
|
||||||
|
1884160 6450688 22 978 272187126 2850519036 2263669418655 424226814 2850519036 2263669871823 0 0
|
||||||
|
`
|
||||||
|
|
||||||
var testKstatPath = os.TempDir() + "/telegraf/proc/spl/kstat/zfs"
|
var testKstatPath = os.TempDir() + "/telegraf/proc/spl/kstat/zfs"
|
||||||
|
|
||||||
|
@ -129,6 +132,50 @@ type metrics struct {
|
||||||
value int64
|
value int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestZfsPoolMetrics(t *testing.T) {
|
||||||
|
err := os.MkdirAll(testKstatPath, 0755)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = os.MkdirAll(testKstatPath+"/HOME", 0755)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(testKstatPath+"/HOME/io", []byte(pool_ioContents), 0644)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(testKstatPath+"/arcstats", []byte(arcstatsContents), 0644)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
poolMetrics := getPoolMetrics()
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
|
//one pool, all metrics
|
||||||
|
tags := map[string]string{
|
||||||
|
"pool": "HOME",
|
||||||
|
}
|
||||||
|
|
||||||
|
z := &Zfs{KstatPath: testKstatPath, KstatMetrics: []string{"arcstats"}}
|
||||||
|
err = z.Gather(&acc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, metric := range poolMetrics {
|
||||||
|
assert.True(t, !acc.HasIntValue(metric.name), metric.name)
|
||||||
|
assert.True(t, !acc.CheckTaggedValue(metric.name, metric.value, tags))
|
||||||
|
}
|
||||||
|
|
||||||
|
z = &Zfs{KstatPath: testKstatPath, KstatMetrics: []string{"arcstats"}, PoolMetrics: true}
|
||||||
|
err = z.Gather(&acc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, metric := range poolMetrics {
|
||||||
|
assert.True(t, acc.HasIntValue(metric.name), metric.name)
|
||||||
|
assert.True(t, acc.CheckTaggedValue(metric.name, metric.value, tags))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.RemoveAll(os.TempDir() + "/telegraf")
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestZfsGeneratesMetrics(t *testing.T) {
|
func TestZfsGeneratesMetrics(t *testing.T) {
|
||||||
err := os.MkdirAll(testKstatPath, 0755)
|
err := os.MkdirAll(testKstatPath, 0755)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -148,7 +195,60 @@ func TestZfsGeneratesMetrics(t *testing.T) {
|
||||||
err = ioutil.WriteFile(testKstatPath+"/vdev_cache_stats", []byte(vdev_cache_statsContents), 0644)
|
err = ioutil.WriteFile(testKstatPath+"/vdev_cache_stats", []byte(vdev_cache_statsContents), 0644)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
intMetrics := []*metrics{
|
intMetrics := getKstatMetrics()
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
|
//one pool, all metrics
|
||||||
|
tags := map[string]string{
|
||||||
|
"pools": "HOME",
|
||||||
|
}
|
||||||
|
|
||||||
|
z := &Zfs{KstatPath: testKstatPath}
|
||||||
|
err = z.Gather(&acc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, metric := range intMetrics {
|
||||||
|
assert.True(t, acc.HasIntValue(metric.name), metric.name)
|
||||||
|
assert.True(t, acc.CheckTaggedValue(metric.name, metric.value, tags))
|
||||||
|
}
|
||||||
|
|
||||||
|
//two pools, all metrics
|
||||||
|
err = os.MkdirAll(testKstatPath+"/STORAGE", 0755)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(testKstatPath+"/STORAGE/io", []byte(""), 0644)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tags = map[string]string{
|
||||||
|
"pools": "HOME::STORAGE",
|
||||||
|
}
|
||||||
|
|
||||||
|
z = &Zfs{KstatPath: testKstatPath}
|
||||||
|
err = z.Gather(&acc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, metric := range intMetrics {
|
||||||
|
assert.True(t, acc.HasIntValue(metric.name), metric.name)
|
||||||
|
assert.True(t, acc.CheckTaggedValue(metric.name, metric.value, tags))
|
||||||
|
}
|
||||||
|
|
||||||
|
//two pools, one metric
|
||||||
|
z = &Zfs{KstatPath: testKstatPath, KstatMetrics: []string{"arcstats"}}
|
||||||
|
err = z.Gather(&acc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, metric := range intMetrics {
|
||||||
|
assert.True(t, acc.HasIntValue(metric.name), metric.name)
|
||||||
|
assert.True(t, acc.CheckTaggedValue(metric.name, metric.value, tags))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.RemoveAll(os.TempDir() + "/telegraf")
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getKstatMetrics() []*metrics {
|
||||||
|
return []*metrics{
|
||||||
{
|
{
|
||||||
name: "arcstats_hits",
|
name: "arcstats_hits",
|
||||||
value: 5968846374,
|
value: 5968846374,
|
||||||
|
@ -550,54 +650,57 @@ func TestZfsGeneratesMetrics(t *testing.T) {
|
||||||
value: 0,
|
value: 0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
var acc testutil.Accumulator
|
|
||||||
|
func getPoolMetrics() []*metrics {
|
||||||
//one pool, all metrics
|
return []*metrics{
|
||||||
tags := map[string]string{
|
{
|
||||||
"pools": "HOME",
|
name: "nread",
|
||||||
}
|
value: 1884160,
|
||||||
|
},
|
||||||
z := &Zfs{KstatPath: testKstatPath}
|
{
|
||||||
err = z.Gather(&acc)
|
name: "nwritten",
|
||||||
require.NoError(t, err)
|
value: 6450688,
|
||||||
|
},
|
||||||
for _, metric := range intMetrics {
|
{
|
||||||
fmt.Println(metric.name)
|
name: "reads",
|
||||||
assert.True(t, acc.HasIntValue(metric.name), metric.name)
|
value: 22,
|
||||||
assert.True(t, acc.CheckTaggedValue(metric.name, metric.value, tags))
|
},
|
||||||
}
|
{
|
||||||
|
name: "writes",
|
||||||
//two pools, all metrics
|
value: 978,
|
||||||
err = os.MkdirAll(testKstatPath+"/STORAGE", 0755)
|
},
|
||||||
require.NoError(t, err)
|
{
|
||||||
|
name: "wtime",
|
||||||
err = ioutil.WriteFile(testKstatPath+"/STORAGE/io", []byte(""), 0644)
|
value: 272187126,
|
||||||
require.NoError(t, err)
|
},
|
||||||
|
{
|
||||||
tags = map[string]string{
|
name: "wlentime",
|
||||||
"pools": "HOME::STORAGE",
|
value: 2850519036,
|
||||||
}
|
},
|
||||||
|
{
|
||||||
z = &Zfs{KstatPath: testKstatPath}
|
name: "wupdate",
|
||||||
err = z.Gather(&acc)
|
value: 2263669418655,
|
||||||
require.NoError(t, err)
|
},
|
||||||
|
{
|
||||||
for _, metric := range intMetrics {
|
name: "rtime",
|
||||||
assert.True(t, acc.HasIntValue(metric.name), metric.name)
|
value: 424226814,
|
||||||
assert.True(t, acc.CheckTaggedValue(metric.name, metric.value, tags))
|
},
|
||||||
}
|
{
|
||||||
|
name: "rlentime",
|
||||||
//two pools, one metric
|
value: 2850519036,
|
||||||
z = &Zfs{KstatPath: testKstatPath, KstatMetrics: []string{"arcstats"}}
|
},
|
||||||
err = z.Gather(&acc)
|
{
|
||||||
require.NoError(t, err)
|
name: "rupdate",
|
||||||
|
value: 2263669871823,
|
||||||
for _, metric := range intMetrics {
|
},
|
||||||
assert.True(t, acc.HasIntValue(metric.name), metric.name)
|
{
|
||||||
assert.True(t, acc.CheckTaggedValue(metric.name, metric.value, tags))
|
name: "wcnt",
|
||||||
}
|
value: 0,
|
||||||
|
},
|
||||||
err = os.RemoveAll(os.TempDir() + "/telegraf")
|
{
|
||||||
require.NoError(t, err)
|
name: "rcnt",
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue