diff options
author | Ilya Mashchenko <ilya@netdata.cloud> | 2024-04-10 20:18:05 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-10 20:18:05 +0300 |
commit | 7b4b258879ba4a83817981033dd6342a70cf80af (patch) | |
tree | 9c028b0aac49b5e440c8e7ec66de91d162dd5ebb /src/go/collectors/go.d.plugin/modules/zfspool/collect.go | |
parent | 30d5acdaef786228112ac1b9d9673b1cec6b887b (diff) |
add collector to monitor ZFS pools space usage (#17367)
Diffstat (limited to 'src/go/collectors/go.d.plugin/modules/zfspool/collect.go')
-rw-r--r-- | src/go/collectors/go.d.plugin/modules/zfspool/collect.go | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/zfspool/collect.go b/src/go/collectors/go.d.plugin/modules/zfspool/collect.go new file mode 100644 index 0000000000..43994bfc1b --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/zfspool/collect.go @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package zfspool + +import ( + "bufio" + "bytes" + "fmt" + "strconv" + "strings" +) + +var zpoolHealthStates = []string{ + "online", + "degraded", + "faulted", + "offline", + "removed", + "unavail", + "suspended", +} + +type zpoolStats struct { + name string + sizeBytes string + allocBytes string + freeBytes string + fragPerc string + capPerc string + dedupRatio string + health string +} + +func (z *ZFSPool) collect() (map[string]int64, error) { + bs, err := z.exec.list() + if err != nil { + return nil, err + } + + zpools, err := parseZpoolListOutput(bs) + if err != nil { + return nil, err + } + + mx := make(map[string]int64) + + z.collectZpoolListStats(mx, zpools) + + return mx, nil +} + +func (z *ZFSPool) collectZpoolListStats(mx map[string]int64, zpools []zpoolStats) { + seen := make(map[string]bool) + + for _, zpool := range zpools { + seen[zpool.name] = true + + if !z.zpools[zpool.name] { + z.addZpoolCharts(zpool.name) + z.zpools[zpool.name] = true + } + + px := "zpool_" + zpool.name + "_" + + if v, ok := parseInt(zpool.sizeBytes); ok { + mx[px+"size"] = v + } + if v, ok := parseInt(zpool.freeBytes); ok { + mx[px+"free"] = v + } + if v, ok := parseInt(zpool.allocBytes); ok { + mx[px+"alloc"] = v + } + if v, ok := parseFloat(zpool.capPerc); ok { + mx[px+"cap"] = int64(v) + } + if v, ok := parseFloat(zpool.fragPerc); ok { + mx[px+"frag"] = int64(v) + } + for _, s := range zpoolHealthStates { + mx[px+"health_state_"+s] = 0 + } + mx[px+"health_state_"+zpool.health] = 1 + } + + for name := range z.zpools { + if !seen[name] { + z.removeZpoolCharts(name) + delete(z.zpools, name) + } + } +} + +func parseZpoolListOutput(bs []byte) ([]zpoolStats, error) { + var lines []string + sc := bufio.NewScanner(bytes.NewReader(bs)) + for sc.Scan() { + if text := strings.TrimSpace(sc.Text()); text != "" { + lines = append(lines, text) + } + + } + if len(lines) < 2 { + return nil, fmt.Errorf("unexpected data: wanted >= 2 lines, got %d", len(lines)) + } + + headers := strings.Fields(lines[0]) + if len(headers) == 0 { + return nil, fmt.Errorf("unexpected data: missing headers") + } + + var zpools []zpoolStats + + /* + # zpool list -p + NAME SIZE ALLOC FREE EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT + rpool 21367462298 9051643576 12240656794 - 33 42 1.00 ONLINE - + zion - - - - - - - FAULTED - + */ + + for _, line := range lines[1:] { + values := strings.Fields(line) + if len(values) != len(headers) { + return nil, fmt.Errorf("unequal columns: headers(%d) != values(%d)", len(headers), len(values)) + } + + var zpool zpoolStats + + for i, v := range values { + v = strings.TrimSpace(v) + switch strings.ToLower(headers[i]) { + case "name": + zpool.name = v + case "size": + zpool.sizeBytes = v + case "alloc": + zpool.allocBytes = v + case "free": + zpool.freeBytes = v + case "frag": + zpool.fragPerc = v + case "cap": + zpool.capPerc = v + case "dedup": + zpool.dedupRatio = v + case "health": + zpool.health = strings.ToLower(v) + } + + if last := i+1 == len(headers); last && zpool.name != "" && zpool.health != "" { + zpools = append(zpools, zpool) + } + } + } + + if len(zpools) == 0 { + return nil, fmt.Errorf("unexpected data: missing pools") + } + + return zpools, nil +} + +func parseInt(s string) (int64, bool) { + if s == "-" { + return 0, false + } + v, err := strconv.ParseInt(s, 10, 64) + return v, err == nil +} + +func parseFloat(s string) (float64, bool) { + if s == "-" { + return 0, false + } + v, err := strconv.ParseFloat(s, 64) + return v, err == nil +} |