master
go 133 lines 2.58 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 //go:build linux || netbsd
4
5 package lvm
6
7 import (
8 "encoding/json"
9 "fmt"
10 "strconv"
11 )
12
13 type lvsReport struct {
14 Report []struct {
15 Lv []struct {
16 VGName string `json:"vg_name"`
17 LVName string `json:"lv_name"`
18 LVSize string `json:"lv_size"`
19 DataPercent string `json:"data_percent"`
20 MetadataPercent string `json:"metadata_percent"`
21 LVAttr string `json:"lv_attr"`
22 } `json:"lv"`
23 } `json:"report"`
24 }
25
26 func (c *Collector) collect() (map[string]int64, error) {
27 bs, err := c.exec.lvsReportJson()
28 if err != nil {
29 return nil, err
30 }
31
32 var report lvsReport
33 if err = json.Unmarshal(bs, &report); err != nil {
34 return nil, err
35 }
36
37 mx := make(map[string]int64)
38
39 for _, r := range report.Report {
40 for _, lv := range r.Lv {
41 if lv.VGName == "" || lv.LVName == "" {
42 continue
43 }
44
45 if !isThinPool(lv.LVAttr) {
46 c.Debugf("skipping lv '%s' vg '%s': not a thin pool", lv.LVName, lv.VGName)
47 continue
48 }
49
50 key := fmt.Sprintf("lv_%s_vg_%s", lv.LVName, lv.VGName)
51 if !c.lvmThinPools[key] {
52 c.addLVMThinPoolCharts(lv.LVName, lv.VGName)
53 c.lvmThinPools[key] = true
54 }
55 if v, ok := parseFloat(lv.DataPercent); ok {
56 mx[key+"_data_percent"] = int64(v * 100)
57 }
58 if v, ok := parseFloat(lv.MetadataPercent); ok {
59 mx[key+"_metadata_percent"] = int64(v * 100)
60 }
61 }
62 }
63
64 return mx, nil
65 }
66
67 func isThinPool(lvAttr string) bool {
68 return getLVType(lvAttr) == "thin_pool"
69 }
70
71 func getLVType(lvAttr string) string {
72 if len(lvAttr) == 0 {
73 return ""
74 }
75
76 // https://man7.org/linux/man-pages/man8/lvs.8.html#NOTES
77 switch lvAttr[0] {
78 case 'C':
79 return "cache"
80 case 'm':
81 return "mirrored"
82 case 'M':
83 return "mirrored_without_initial_sync"
84 case 'o':
85 return "origin"
86 case 'O':
87 return "origin_with_merging_snapshot"
88 case 'g':
89 return "integrity"
90 case 'r':
91 return "raid"
92 case 'R':
93 return "raid_without_initial_sync"
94 case 's':
95 return "snapshot"
96 case 'S':
97 return "merging_snapshot"
98 case 'p':
99 return "pvmove"
100 case 'v':
101 return "virtual"
102 case 'i':
103 return "mirror_or_raid_image"
104 case 'I':
105 return "mirror_or_raid_mage_out_of_sync"
106 case 'l':
107 return "log_device"
108 case 'c':
109 return "under_conversion"
110 case 'V':
111 return "thin_volume"
112 case 't':
113 return "thin_pool"
114 case 'T':
115 return "thin_pool_data"
116 case 'd':
117 return "vdo_pool"
118 case 'D':
119 return "vdo_pool_data"
120 case 'e':
121 return "raid_or_pool_metadata"
122 default:
123 return ""
124 }
125 }
126
127 func parseFloat(s string) (float64, bool) {
128 if s == "-" {
129 return 0, false
130 }
131 v, err := strconv.ParseFloat(s, 64)
132 return v, err == nil
133 }