// +build linux package zfs import ( "fmt" "path/filepath" "strconv" "strings" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/internal" "github.com/influxdata/telegraf/plugins/inputs" ) type poolInfo struct { name string ioFilename string } func getPools(kstatPath string) []poolInfo { pools := make([]poolInfo, 0) poolsDirs, _ := filepath.Glob(kstatPath + "/*/io") for _, poolDir := range poolsDirs { poolDirSplit := strings.Split(poolDir, "/") pool := poolDirSplit[len(poolDirSplit)-2] pools = append(pools, poolInfo{name: pool, ioFilename: poolDir}) } return pools } func getTags(pools []poolInfo) map[string]string { var poolNames string for _, pool := range pools { if len(poolNames) != 0 { poolNames += "::" } poolNames += pool.name } return map[string]string{"pools": poolNames} } func gatherPoolStats(pool poolInfo, acc telegraf.Accumulator) error { lines, err := internal.ReadLines(pool.ioFilename) 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": pool.name} fields := make(map[string]interface{}) for i := 0; i < keyCount; i++ { value, err := strconv.ParseInt(values[i], 10, 64) if err != nil { return err } fields[keys[i]] = value } acc.AddFields("zfs_pool", fields, tag) return nil } func (z *Zfs) Gather(acc telegraf.Accumulator) error { kstatMetrics := z.KstatMetrics if len(kstatMetrics) == 0 { // vdev_cache_stats is deprecated // xuio_stats are ignored because as of Sep-2016, no known // consumers of xuio exist on Linux kstatMetrics = []string{"abdstats", "arcstats", "dnodestats", "dbufcachestats", "dmu_tx", "fm", "vdev_mirror_stats", "zfetchstats", "zil"} } kstatPath := z.KstatPath if len(kstatPath) == 0 { kstatPath = "/proc/spl/kstat/zfs" } pools := getPools(kstatPath) tags := getTags(pools) if z.PoolMetrics { for _, pool := range pools { err := gatherPoolStats(pool, acc) if err != nil { return err } } } fields := make(map[string]interface{}) for _, metric := range kstatMetrics { lines, err := internal.ReadLines(kstatPath + "/" + metric) if err != nil { continue } for i, line := range lines { if i == 0 || i == 1 { continue } if len(line) < 1 { continue } rawData := strings.Split(line, " ") key := metric + "_" + rawData[0] if metric == "zil" || metric == "dmu_tx" || metric == "dnodestats" { key = rawData[0] } rawValue := rawData[len(rawData)-1] value, _ := strconv.ParseInt(rawValue, 10, 64) fields[key] = value } } acc.AddFields("zfs", fields, tags) return nil } func init() { inputs.Add("zfs", func() telegraf.Input { return &Zfs{} }) }