From fa7a9e4e1490db26555ca55c54214046d6ed3562 Mon Sep 17 00:00:00 2001 From: Sven Rebhan Date: Tue, 24 Oct 2023 18:51:57 +0200 Subject: [PATCH] fix(inputs.zfs): Parse metrics correctly on FreeBSD 14.0-RC2 --- .../testcases/freebsd/freebsd14/expected.out | 16 ++ .../testcases/freebsd/freebsd14/sysctl.json | 159 +++++++++++ .../testcases/freebsd/freebsd14/telegraf.conf | 4 + .../zfs/testcases/freebsd/freebsd14/uname.txt | 1 + .../testcases/freebsd/freebsd14/zdataset.txt | 14 + .../zfs/testcases/freebsd/freebsd14/zpool.txt | 1 + plugins/inputs/zfs/zfs.go | 10 +- plugins/inputs/zfs/zfs_freebsd.go | 264 +++++++++++------- plugins/inputs/zfs/zfs_freebsd_test.go | 12 + 9 files changed, 373 insertions(+), 108 deletions(-) create mode 100644 plugins/inputs/zfs/testcases/freebsd/freebsd14/expected.out create mode 100644 plugins/inputs/zfs/testcases/freebsd/freebsd14/sysctl.json create mode 100644 plugins/inputs/zfs/testcases/freebsd/freebsd14/telegraf.conf create mode 100644 plugins/inputs/zfs/testcases/freebsd/freebsd14/uname.txt create mode 100644 plugins/inputs/zfs/testcases/freebsd/freebsd14/zdataset.txt create mode 100644 plugins/inputs/zfs/testcases/freebsd/freebsd14/zpool.txt diff --git a/plugins/inputs/zfs/testcases/freebsd/freebsd14/expected.out b/plugins/inputs/zfs/testcases/freebsd/freebsd14/expected.out new file mode 100644 index 0000000000000..2e9b581e079a0 --- /dev/null +++ b/plugins/inputs/zfs/testcases/freebsd/freebsd14/expected.out @@ -0,0 +1,16 @@ +zfs_pool,health=ONLINE,pool=zroot allocated=11959578624i,capacity=17i,dedupratio=1,fragmentation=4i,free=55149285376i,size=67108864000i 1698172931876196974 +zfs_dataset,dataset=zroot avail=53069045760i,used=11959443456i,usedds=0i,usedsnap=0i 1698172931876204237 +zfs_dataset,dataset=zroot/ROOT avail=53069045760i,used=4685123584i,usedds=0i,usedsnap=0i 1698172931876207310 +zfs_dataset,dataset=zroot/ROOT/default avail=53069045760i,used=4684689408i,usedds=0i,usedsnap=0i 1698172931876213735 +zfs_dataset,dataset=zroot/home avail=53069045760i,used=7263580160i,usedds=0i,usedsnap=0i 1698172931876217926 +zfs_dataset,dataset=zroot/tmp avail=53069045760i,used=684032i,usedds=0i,usedsnap=0i 1698172931876220720 +zfs_dataset,dataset=zroot/usr avail=53069045760i,used=1724416i,usedds=0i,usedsnap=0i 1698172931876224631 +zfs_dataset,dataset=zroot/usr/obj avail=53069045760i,used=430080i,usedds=0i,usedsnap=0i 1698172931876227424 +zfs_dataset,dataset=zroot/usr/ports avail=53069045760i,used=430080i,usedds=0i,usedsnap=0i 1698172931876230218 +zfs_dataset,dataset=zroot/usr/src avail=53069045760i,used=430080i,usedds=0i,usedsnap=0i 1698172931876233291 +zfs_dataset,dataset=zroot/var avail=53069045760i,used=2269184i,usedds=0i,usedsnap=0i 1698172931876237481 +zfs_dataset,dataset=zroot/var/audit avail=53069045760i,used=438272i,usedds=0i,usedsnap=0i 1698172931876240554 +zfs_dataset,dataset=zroot/var/log avail=53069045760i,used=544768i,usedds=0i,usedsnap=0i 1698172931876243348 +zfs_dataset,dataset=zroot/var/mail avail=53069045760i,used=425984i,usedds=0i,usedsnap=0i 1698172931876246980 +zfs_dataset,dataset=zroot/var/tmp avail=53069045760i,used=425984i,usedds=0i,usedsnap=0i 1698172931876249774 +zfs,datasets=zroot::zroot/ROOT::zroot/ROOT/default::zroot/home::zroot/tmp::zroot/usr::zroot/usr/obj::zroot/usr/ports::zroot/usr/src::zroot/var::zroot/var/audit::zroot/var/log::zroot/var/mail::zroot/var/tmp,pools=zroot zfetchstats_hits=6439i,zfetchstats_io_active=0i,zfetchstats_io_issued=2197i,zfetchstats_max_streams=9411i,zfetchstats_misses=14538i 1698172931876287767 diff --git a/plugins/inputs/zfs/testcases/freebsd/freebsd14/sysctl.json b/plugins/inputs/zfs/testcases/freebsd/freebsd14/sysctl.json new file mode 100644 index 0000000000000..c4aa784002437 --- /dev/null +++ b/plugins/inputs/zfs/testcases/freebsd/freebsd14/sysctl.json @@ -0,0 +1,159 @@ +{ + "archstats": [ + "kstat.zfs.misc.arcstats.abd_chunk_waste_size: 626176", + "kstat.zfs.misc.arcstats.cached_only_in_progress: 0", + "kstat.zfs.misc.arcstats.arc_raw_size: 0", + "kstat.zfs.misc.arcstats.arc_sys_free: 0", + "kstat.zfs.misc.arcstats.arc_need_free: 0", + "kstat.zfs.misc.arcstats.demand_iohit_prescient_prefetch: 1", + "kstat.zfs.misc.arcstats.demand_hit_prescient_prefetch: 14", + "kstat.zfs.misc.arcstats.prescient_prefetch: 15", + "kstat.zfs.misc.arcstats.demand_iohit_predictive_prefetch: 218", + "kstat.zfs.misc.arcstats.demand_hit_predictive_prefetch: 1778", + "kstat.zfs.misc.arcstats.predictive_prefetch: 5099", + "kstat.zfs.misc.arcstats.async_upgrade_sync: 135", + "kstat.zfs.misc.arcstats.arc_dnode_limit: 317881139", + "kstat.zfs.misc.arcstats.arc_meta_used: 80185232", + "kstat.zfs.misc.arcstats.arc_prune: 25", + "kstat.zfs.misc.arcstats.arc_loaned_bytes: 0", + "kstat.zfs.misc.arcstats.arc_tempreserve: 0", + "kstat.zfs.misc.arcstats.arc_no_grow: 0", + "kstat.zfs.misc.arcstats.memory_available_bytes: 3663839232", + "kstat.zfs.misc.arcstats.memory_free_bytes: 3752042496", + "kstat.zfs.misc.arcstats.memory_all_bytes: 4252553216", + "kstat.zfs.misc.arcstats.memory_indirect_count: 0", + "kstat.zfs.misc.arcstats.memory_direct_count: 0", + "kstat.zfs.misc.arcstats.memory_throttle_count: 0", + "kstat.zfs.misc.arcstats.l2_rebuild_log_blks: 0", + "kstat.zfs.misc.arcstats.l2_rebuild_bufs_precached: 0", + "kstat.zfs.misc.arcstats.l2_rebuild_bufs: 0", + "kstat.zfs.misc.arcstats.l2_rebuild_asize: 0", + "kstat.zfs.misc.arcstats.l2_rebuild_size: 0", + "kstat.zfs.misc.arcstats.l2_rebuild_lowmem: 0", + "kstat.zfs.misc.arcstats.l2_rebuild_cksum_lb_errors: 0", + "kstat.zfs.misc.arcstats.l2_rebuild_dh_errors: 0", + "kstat.zfs.misc.arcstats.l2_rebuild_io_errors: 0", + "kstat.zfs.misc.arcstats.l2_rebuild_unsupported: 0", + "kstat.zfs.misc.arcstats.l2_rebuild_success: 0", + "kstat.zfs.misc.arcstats.l2_data_to_meta_ratio: 0", + "kstat.zfs.misc.arcstats.l2_log_blk_count: 0", + "kstat.zfs.misc.arcstats.l2_log_blk_asize: 0", + "kstat.zfs.misc.arcstats.l2_log_blk_avg_asize: 0", + "kstat.zfs.misc.arcstats.l2_log_blk_writes: 0", + "kstat.zfs.misc.arcstats.l2_hdr_size: 0", + "kstat.zfs.misc.arcstats.l2_asize: 0", + "kstat.zfs.misc.arcstats.l2_size: 0", + "kstat.zfs.misc.arcstats.l2_io_error: 0", + "kstat.zfs.misc.arcstats.l2_cksum_bad: 0", + "kstat.zfs.misc.arcstats.l2_abort_lowmem: 0", + "kstat.zfs.misc.arcstats.l2_free_on_write: 0", + "kstat.zfs.misc.arcstats.l2_evict_l1cached: 0", + "kstat.zfs.misc.arcstats.l2_evict_reading: 0", + "kstat.zfs.misc.arcstats.l2_evict_lock_retry: 0", + "kstat.zfs.misc.arcstats.l2_writes_lock_retry: 0", + "kstat.zfs.misc.arcstats.l2_writes_error: 0", + "kstat.zfs.misc.arcstats.l2_writes_done: 0", + "kstat.zfs.misc.arcstats.l2_writes_sent: 0", + "kstat.zfs.misc.arcstats.l2_write_bytes: 0", + "kstat.zfs.misc.arcstats.l2_read_bytes: 0", + "kstat.zfs.misc.arcstats.l2_rw_clash: 0", + "kstat.zfs.misc.arcstats.l2_feeds: 0", + "kstat.zfs.misc.arcstats.l2_bufc_metadata_asize: 0", + "kstat.zfs.misc.arcstats.l2_bufc_data_asize: 0", + "kstat.zfs.misc.arcstats.l2_mfu_asize: 0", + "kstat.zfs.misc.arcstats.l2_mru_asize: 0", + "kstat.zfs.misc.arcstats.l2_prefetch_asize: 0", + "kstat.zfs.misc.arcstats.l2_misses: 0", + "kstat.zfs.misc.arcstats.l2_hits: 0", + "kstat.zfs.misc.arcstats.uncached_evictable_metadata: 0", + "kstat.zfs.misc.arcstats.uncached_evictable_data: 0", + "kstat.zfs.misc.arcstats.uncached_metadata: 0", + "kstat.zfs.misc.arcstats.uncached_data: 0", + "kstat.zfs.misc.arcstats.uncached_size: 0", + "kstat.zfs.misc.arcstats.mfu_ghost_evictable_metadata: 35231232", + "kstat.zfs.misc.arcstats.mfu_ghost_evictable_data: 31780352", + "kstat.zfs.misc.arcstats.mfu_ghost_metadata: 35231232", + "kstat.zfs.misc.arcstats.mfu_ghost_data: 31780352", + "kstat.zfs.misc.arcstats.mfu_ghost_size: 67011584", + "kstat.zfs.misc.arcstats.mfu_evictable_metadata: 648192", + "kstat.zfs.misc.arcstats.mfu_evictable_data: 29985280", + "kstat.zfs.misc.arcstats.mfu_metadata: 9697280", + "kstat.zfs.misc.arcstats.mfu_data: 31448576", + "kstat.zfs.misc.arcstats.mfu_size: 41145856", + "kstat.zfs.misc.arcstats.mru_ghost_evictable_metadata: 27670528", + "kstat.zfs.misc.arcstats.mru_ghost_evictable_data: 39195136", + "kstat.zfs.misc.arcstats.mru_ghost_metadata: 27670528", + "kstat.zfs.misc.arcstats.mru_ghost_data: 39195136", + "kstat.zfs.misc.arcstats.mru_ghost_size: 66865664", + "kstat.zfs.misc.arcstats.mru_evictable_metadata: 4007424", + "kstat.zfs.misc.arcstats.mru_evictable_data: 21310976", + "kstat.zfs.misc.arcstats.mru_metadata: 32238592", + "kstat.zfs.misc.arcstats.mru_data: 25252864", + "kstat.zfs.misc.arcstats.mru_size: 57491456", + "kstat.zfs.misc.arcstats.anon_evictable_metadata: 0", + "kstat.zfs.misc.arcstats.anon_evictable_data: 0", + "kstat.zfs.misc.arcstats.anon_metadata: 0", + "kstat.zfs.misc.arcstats.anon_data: 0", + "kstat.zfs.misc.arcstats.anon_size: 0", + "kstat.zfs.misc.arcstats.other_size: 36376512", + "kstat.zfs.misc.arcstats.bonus_size: 7930560", + "kstat.zfs.misc.arcstats.dnode_size: 20521584", + "kstat.zfs.misc.arcstats.dbuf_size: 7924368", + "kstat.zfs.misc.arcstats.metadata_size: 41935872", + "kstat.zfs.misc.arcstats.data_size: 56701440", + "kstat.zfs.misc.arcstats.hdr_size: 1872848", + "kstat.zfs.misc.arcstats.overhead_size: 29338112", + "kstat.zfs.misc.arcstats.uncompressed_size: 121843712", + "kstat.zfs.misc.arcstats.compressed_size: 69299200", + "kstat.zfs.misc.arcstats.size: 137512848", + "kstat.zfs.misc.arcstats.c_max: 3178811392", + "kstat.zfs.misc.arcstats.c_min: 132892288", + "kstat.zfs.misc.arcstats.c: 176014976", + "kstat.zfs.misc.arcstats.pm: 2306048980", + "kstat.zfs.misc.arcstats.pd: 1302662522", + "kstat.zfs.misc.arcstats.meta: 1110189320", + "kstat.zfs.misc.arcstats.hash_chain_max: 4", + "kstat.zfs.misc.arcstats.hash_chains: 68", + "kstat.zfs.misc.arcstats.hash_collisions: 15034", + "kstat.zfs.misc.arcstats.hash_elements_max: 91215", + "kstat.zfs.misc.arcstats.hash_elements: 7890", + "kstat.zfs.misc.arcstats.evict_l2_skip: 0", + "kstat.zfs.misc.arcstats.evict_l2_ineligible: 65306624", + "kstat.zfs.misc.arcstats.evict_l2_eligible_mru: 1369077760", + "kstat.zfs.misc.arcstats.evict_l2_eligible_mfu: 940835840", + "kstat.zfs.misc.arcstats.evict_l2_eligible: 2309913600", + "kstat.zfs.misc.arcstats.evict_l2_cached: 0", + "kstat.zfs.misc.arcstats.evict_not_enough: 12", + "kstat.zfs.misc.arcstats.evict_skip: 445", + "kstat.zfs.misc.arcstats.access_skip: 0", + "kstat.zfs.misc.arcstats.mutex_miss: 0", + "kstat.zfs.misc.arcstats.deleted: 90151", + "kstat.zfs.misc.arcstats.uncached_hits: 0", + "kstat.zfs.misc.arcstats.mfu_ghost_hits: 6108", + "kstat.zfs.misc.arcstats.mfu_hits: 1240173", + "kstat.zfs.misc.arcstats.mru_ghost_hits: 2692", + "kstat.zfs.misc.arcstats.mru_hits: 233633", + "kstat.zfs.misc.arcstats.prefetch_metadata_misses: 406", + "kstat.zfs.misc.arcstats.prefetch_metadata_iohits: 2315", + "kstat.zfs.misc.arcstats.prefetch_metadata_hits: 196", + "kstat.zfs.misc.arcstats.prefetch_data_misses: 2092", + "kstat.zfs.misc.arcstats.prefetch_data_iohits: 0", + "kstat.zfs.misc.arcstats.prefetch_data_hits: 105", + "kstat.zfs.misc.arcstats.demand_metadata_misses: 12021", + "kstat.zfs.misc.arcstats.demand_metadata_iohits: 87", + "kstat.zfs.misc.arcstats.demand_metadata_hits: 906938", + "kstat.zfs.misc.arcstats.demand_data_misses: 48482", + "kstat.zfs.misc.arcstats.demand_data_iohits: 128", + "kstat.zfs.misc.arcstats.demand_data_hits: 566567", + "kstat.zfs.misc.arcstats.misses: 63001", + "kstat.zfs.misc.arcstats.iohits: 2530", + "kstat.zfs.misc.arcstats.hits: 1473806" + ], + "zfetchstats": [ + "kstat.zfs.misc.zfetchstats.io_active: 0", + "kstat.zfs.misc.zfetchstats.io_issued: 2197", + "kstat.zfs.misc.zfetchstats.max_streams: 9411", + "kstat.zfs.misc.zfetchstats.misses: 14538", + "kstat.zfs.misc.zfetchstats.hits: 6439" + ] +} \ No newline at end of file diff --git a/plugins/inputs/zfs/testcases/freebsd/freebsd14/telegraf.conf b/plugins/inputs/zfs/testcases/freebsd/freebsd14/telegraf.conf new file mode 100644 index 0000000000000..06e527832d14a --- /dev/null +++ b/plugins/inputs/zfs/testcases/freebsd/freebsd14/telegraf.conf @@ -0,0 +1,4 @@ +[[inputs.zfs]] + kstatMetrics = ["arcstats", "zfetchstats", "vdev_cache_stats"] + poolMetrics = true + datasetMetrics = true \ No newline at end of file diff --git a/plugins/inputs/zfs/testcases/freebsd/freebsd14/uname.txt b/plugins/inputs/zfs/testcases/freebsd/freebsd14/uname.txt new file mode 100644 index 0000000000000..5672e83cabe11 --- /dev/null +++ b/plugins/inputs/zfs/testcases/freebsd/freebsd14/uname.txt @@ -0,0 +1 @@ +14.0-RC2 \ No newline at end of file diff --git a/plugins/inputs/zfs/testcases/freebsd/freebsd14/zdataset.txt b/plugins/inputs/zfs/testcases/freebsd/freebsd14/zdataset.txt new file mode 100644 index 0000000000000..2c2599f5fa9f4 --- /dev/null +++ b/plugins/inputs/zfs/testcases/freebsd/freebsd14/zdataset.txt @@ -0,0 +1,14 @@ +zroot 53069045760 11959443456 - - +zroot/ROOT 53069045760 4685123584 - - +zroot/ROOT/default 53069045760 4684689408 - - +zroot/home 53069045760 7263580160 - - +zroot/tmp 53069045760 684032 - - +zroot/usr 53069045760 1724416 - - +zroot/usr/obj 53069045760 430080 - - +zroot/usr/ports 53069045760 430080 - - +zroot/usr/src 53069045760 430080 - - +zroot/var 53069045760 2269184 - - +zroot/var/audit 53069045760 438272 - - +zroot/var/log 53069045760 544768 - - +zroot/var/mail 53069045760 425984 - - +zroot/var/tmp 53069045760 425984 - - diff --git a/plugins/inputs/zfs/testcases/freebsd/freebsd14/zpool.txt b/plugins/inputs/zfs/testcases/freebsd/freebsd14/zpool.txt new file mode 100644 index 0000000000000..0d86c0ce4744b --- /dev/null +++ b/plugins/inputs/zfs/testcases/freebsd/freebsd14/zpool.txt @@ -0,0 +1 @@ +zroot ONLINE 67108864000 11959578624 55149285376 4 17 1.00 diff --git a/plugins/inputs/zfs/zfs.go b/plugins/inputs/zfs/zfs.go index 262cd55d44015..9b135acd9f8de 100644 --- a/plugins/inputs/zfs/zfs.go +++ b/plugins/inputs/zfs/zfs.go @@ -13,16 +13,20 @@ var sampleConfig string type Sysctl func(metric string) ([]string, error) type Zpool func() ([]string, error) type Zdataset func(properties []string) ([]string, error) +type Uname func() (string, error) type Zfs struct { KstatPath string KstatMetrics []string PoolMetrics bool DatasetMetrics bool - sysctl Sysctl //nolint:unused // False positive - this var is used for non-default build tag: freebsd - zpool Zpool //nolint:unused // False positive - this var is used for non-default build tag: freebsd - zdataset Zdataset //nolint:unused // False positive - this var is used for non-default build tag: freebsd Log telegraf.Logger `toml:"-"` + + sysctl Sysctl //nolint:unused // False positive - this var is used for non-default build tag: freebsd + zpool Zpool //nolint:unused // False positive - this var is used for non-default build tag: freebsd + zdataset Zdataset //nolint:unused // False positive - this var is used for non-default build tag: freebsd + uname Uname //nolint:unused // False positive - this var is used for non-default build tag: freebsd + version int64 //nolint:unused // False positive - this var is used for non-default build tag: freebsd } func (*Zfs) SampleConfig() string { diff --git a/plugins/inputs/zfs/zfs_freebsd.go b/plugins/inputs/zfs/zfs_freebsd.go index a15302be49107..4d684aca33974 100644 --- a/plugins/inputs/zfs/zfs_freebsd.go +++ b/plugins/inputs/zfs/zfs_freebsd.go @@ -11,8 +11,84 @@ import ( "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/plugins/inputs" + "golang.org/x/sys/unix" ) +func (z *Zfs) Init() error { + // Determine the kernel version to adapt parsing + release, err := z.uname() + if err != nil { + return fmt.Errorf("determining uname failed: %w", err) + } + parts := strings.SplitN(release, ".", 2) + z.version, err = strconv.ParseInt(parts[0], 10, 64) + if err != nil { + return fmt.Errorf("determining version from %q failed: %w", release, err) + } + + // Setup default metrics if they are not specified. + // Please note that starting from FreeBSD 14 the 'vdev_cache_stats' are + // no longer available. + if len(z.KstatMetrics) == 0 { + if z.version < 14 { + z.KstatMetrics = []string{"arcstats", "zfetchstats", "vdev_cache_stats"} + } else { + z.KstatMetrics = []string{"arcstats", "zfetchstats"} + } + } + + return nil +} + +func (z *Zfs) Gather(acc telegraf.Accumulator) error { + tags := map[string]string{} + + poolNames, err := z.gatherPoolStats(acc) + if err != nil { + return err + } + if poolNames != "" { + tags["pools"] = poolNames + } + + datasetNames, err := z.gatherDatasetStats(acc) + if err != nil { + return err + } + if datasetNames != "" { + tags["datasets"] = datasetNames + } + + // Gather information form the kernel using sysctl + fields := make(map[string]interface{}) + var removeIndices []int + for i, metric := range z.KstatMetrics { + stdout, err := z.sysctl(metric) + if err != nil { + z.Log.Warnf("sysctl for 'kstat.zfs.misc.%s' failed: %v; removing metric", metric, err) + removeIndices = append(removeIndices, i) + continue + } + for _, line := range stdout { + rawData := strings.Split(line, ": ") + key := metric + "_" + strings.Split(rawData[0], ".")[4] + value, _ := strconv.ParseInt(rawData[1], 10, 64) + fields[key] = value + } + } + acc.AddFields("zfs", fields, tags) + + // Remove the invalid kstat metrics + if len(removeIndices) > 0 { + for i := len(removeIndices) - 1; i >= 0; i-- { + idx := removeIndices[i] + z.KstatMetrics = append(z.KstatMetrics[:idx], z.KstatMetrics[idx+1:]...) + } + } + + return nil +} + func (z *Zfs) gatherPoolStats(acc telegraf.Accumulator) (string, error) { lines, err := z.zpool() if err != nil { @@ -22,62 +98,63 @@ func (z *Zfs) gatherPoolStats(acc telegraf.Accumulator) (string, error) { pools := []string{} for _, line := range lines { col := strings.Split(line, "\t") - pools = append(pools, col[0]) } - if z.PoolMetrics { - for _, line := range lines { - col := strings.Split(line, "\t") - if len(col) != 8 { - continue + if !z.PoolMetrics { + return strings.Join(pools, "::"), nil + } + + for _, line := range lines { + col := strings.Split(line, "\t") + if len(col) != 8 { + continue + } + + tags := map[string]string{"pool": col[0], "health": col[1]} + fields := map[string]interface{}{} + + if tags["health"] == "UNAVAIL" { + fields["size"] = int64(0) + } else { + size, err := strconv.ParseInt(col[2], 10, 64) + if err != nil { + return "", fmt.Errorf("Error parsing size: %s", err) } + fields["size"] = size - tags := map[string]string{"pool": col[0], "health": col[1]} - fields := map[string]interface{}{} - - if tags["health"] == "UNAVAIL" { - fields["size"] = int64(0) - } else { - size, err := strconv.ParseInt(col[2], 10, 64) - if err != nil { - return "", fmt.Errorf("Error parsing size: %s", err) - } - fields["size"] = size - - alloc, err := strconv.ParseInt(col[3], 10, 64) - if err != nil { - return "", fmt.Errorf("Error parsing allocation: %s", err) - } - fields["allocated"] = alloc - - free, err := strconv.ParseInt(col[4], 10, 64) - if err != nil { - return "", fmt.Errorf("Error parsing free: %s", err) - } - fields["free"] = free - - frag, err := strconv.ParseInt(strings.TrimSuffix(col[5], "%"), 10, 0) - if err != nil { // This might be - for RO devs - frag = 0 - } - fields["fragmentation"] = frag - - capval, err := strconv.ParseInt(col[6], 10, 0) - if err != nil { - return "", fmt.Errorf("Error parsing capacity: %s", err) - } - fields["capacity"] = capval - - dedup, err := strconv.ParseFloat(strings.TrimSuffix(col[7], "x"), 32) - if err != nil { - return "", fmt.Errorf("Error parsing dedupratio: %s", err) - } - fields["dedupratio"] = dedup + alloc, err := strconv.ParseInt(col[3], 10, 64) + if err != nil { + return "", fmt.Errorf("Error parsing allocation: %s", err) } + fields["allocated"] = alloc - acc.AddFields("zfs_pool", fields, tags) + free, err := strconv.ParseInt(col[4], 10, 64) + if err != nil { + return "", fmt.Errorf("Error parsing free: %s", err) + } + fields["free"] = free + + frag, err := strconv.ParseInt(strings.TrimSuffix(col[5], "%"), 10, 0) + if err != nil { // This might be - for RO devs + frag = 0 + } + fields["fragmentation"] = frag + + capval, err := strconv.ParseInt(col[6], 10, 0) + if err != nil { + return "", fmt.Errorf("Error parsing capacity: %s", err) + } + fields["capacity"] = capval + + dedup, err := strconv.ParseFloat(strings.TrimSuffix(col[7], "x"), 32) + if err != nil { + return "", fmt.Errorf("Error parsing dedupratio: %s", err) + } + fields["dedupratio"] = dedup } + + acc.AddFields("zfs_pool", fields, tags) } return strings.Join(pools, "::"), nil @@ -97,70 +174,37 @@ func (z *Zfs) gatherDatasetStats(acc telegraf.Accumulator) (string, error) { datasets = append(datasets, col[0]) } - if z.DatasetMetrics { - for _, line := range lines { - col := strings.Split(line, "\t") - if len(col) != len(properties) { - z.Log.Warnf("Invalid number of columns for line: %s", line) - continue - } - - tags := map[string]string{"dataset": col[0]} - fields := map[string]interface{}{} - - for i, key := range properties[1:] { - value, err := strconv.ParseInt(col[i+1], 10, 64) - if err != nil { - return "", fmt.Errorf("Error parsing %s %q: %s", key, col[i+1], err) - } - fields[key] = value - } - - acc.AddFields("zfs_dataset", fields, tags) - } + if !z.DatasetMetrics { + return strings.Join(datasets, "::"), nil } - return strings.Join(datasets, "::"), nil -} - -func (z *Zfs) Gather(acc telegraf.Accumulator) error { - kstatMetrics := z.KstatMetrics - if len(kstatMetrics) == 0 { - kstatMetrics = []string{"arcstats", "zfetchstats", "vdev_cache_stats"} - } - - tags := map[string]string{} - poolNames, err := z.gatherPoolStats(acc) - if err != nil { - return err - } - if poolNames != "" { - tags["pools"] = poolNames - } + for _, line := range lines { + col := strings.Split(line, "\t") + if len(col) != len(properties) { + z.Log.Warnf("Invalid number of columns for line: %s", line) + continue + } - datasetNames, err := z.gatherDatasetStats(acc) - if err != nil { - return err - } - if datasetNames != "" { - tags["datasets"] = datasetNames - } + tags := map[string]string{"dataset": col[0]} + fields := map[string]interface{}{} - fields := make(map[string]interface{}) - for _, metric := range kstatMetrics { - stdout, err := z.sysctl(metric) - if err != nil { - return err - } - for _, line := range stdout { - rawData := strings.Split(line, ": ") - key := metric + "_" + strings.Split(rawData[0], ".")[4] - value, _ := strconv.ParseInt(rawData[1], 10, 64) + for i, key := range properties[1:] { + // Treat '-' entries as zero + if col[i+1] == "-" { + fields[key] = int64(0) + continue + } + value, err := strconv.ParseInt(col[i+1], 10, 64) + if err != nil { + return "", fmt.Errorf("Error parsing %s %q: %s", key, col[i+1], err) + } fields[key] = value } + + acc.AddFields("zfs_dataset", fields, tags) } - acc.AddFields("zfs", fields, tags) - return nil + + return strings.Join(datasets, "::"), nil } func run(command string, args ...string) ([]string, error) { @@ -194,12 +238,22 @@ func sysctl(metric string) ([]string, error) { return run("sysctl", []string{"-q", fmt.Sprintf("kstat.zfs.misc.%s", metric)}...) } +func uname() (string, error) { + var info unix.Utsname + if err := unix.Uname(&info); err != nil { + return "", err + } + release := unix.ByteSliceToString(info.Release[:]) + return release, nil +} + func init() { inputs.Add("zfs", func() telegraf.Input { return &Zfs{ sysctl: sysctl, zpool: zpool, zdataset: zdataset, + uname: uname, } }) } diff --git a/plugins/inputs/zfs/zfs_freebsd_test.go b/plugins/inputs/zfs/zfs_freebsd_test.go index f673fe4c9b3e6..401ca553dc5e6 100644 --- a/plugins/inputs/zfs/zfs_freebsd_test.go +++ b/plugins/inputs/zfs/zfs_freebsd_test.go @@ -44,6 +44,7 @@ func TestCases(t *testing.T) { inputSysctlFilename := filepath.Join(testcasePath, "sysctl.json") inputZPoolFilename := filepath.Join(testcasePath, "zpool.txt") inputZDatasetFilename := filepath.Join(testcasePath, "zdataset.txt") + inputUnameFilename := filepath.Join(testcasePath, "uname.txt") expectedFilename := filepath.Join(testcasePath, "expected.out") // Load the input data @@ -51,11 +52,20 @@ func TestCases(t *testing.T) { require.NoError(t, err) var sysctl map[string][]string require.NoError(t, json.Unmarshal(buf, &sysctl)) + zpool, err := testutil.ParseLinesFromFile(inputZPoolFilename) require.NoError(t, err) + zdataset, err := testutil.ParseLinesFromFile(inputZDatasetFilename) require.NoError(t, err) + // Try to read release from file and default to FreeBSD 13 if + // an error occurs. + uname := "13.2-STABLE" + if buf, err := os.ReadFile(inputUnameFilename); err == nil { + uname = string(buf) + } + // Prepare the influx parser for expectations parser := &influx.Parser{} require.NoError(t, parser.Init()) @@ -79,7 +89,9 @@ func TestCases(t *testing.T) { } plugin.zpool = func() ([]string, error) { return zpool, nil } plugin.zdataset = func(_ []string) ([]string, error) { return zdataset, nil } + plugin.uname = func() (string, error) { return uname, nil } plugin.Log = testutil.Logger{} + require.NoError(t, plugin.Init()) // Gather and test var acc testutil.Accumulator