master
go 141 lines 3.21 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package megacli
4
5 import (
6 "bufio"
7 "bytes"
8 "fmt"
9 "strconv"
10 "strings"
11 )
12
13 type megaBBU struct {
14 adapterNumber string
15 batteryType string
16 temperature string
17 rsoc string
18 asoc string // apparently can be 0 while relative > 0 (e.g. relative 91%, absolute 0%)
19 cycleCount string
20 fullChargeCap string
21 designCap string
22 }
23
24 func (c *Collector) collectBBU(mx map[string]int64) error {
25 bs, err := c.exec.bbuInfo()
26 if err != nil {
27 return err
28 }
29
30 bbus, err := parseBBUInfo(bs)
31 if err != nil {
32 return err
33 }
34
35 if len(bbus) == 0 {
36 c.Debugf("no BBUs found")
37 return nil
38 }
39
40 for _, bbu := range bbus {
41 if !c.bbu[bbu.adapterNumber] {
42 c.bbu[bbu.adapterNumber] = true
43 c.addBBUCharts(bbu)
44 }
45
46 px := fmt.Sprintf("bbu_adapter_%s_", bbu.adapterNumber)
47
48 writeInt(mx, px+"temperature", bbu.temperature)
49 writeInt(mx, px+"relative_state_of_charge", bbu.rsoc)
50 writeInt(mx, px+"absolute_state_of_charge", bbu.asoc)
51 writeInt(mx, px+"cycle_count", bbu.cycleCount)
52 if v, ok := calcCapDegradationPerc(bbu); ok {
53 mx[px+"capacity_degradation_perc"] = v
54 }
55 }
56
57 c.Debugf("found %d BBUs", len(c.bbu))
58
59 return nil
60 }
61
62 func parseBBUInfo(bs []byte) (map[string]*megaBBU, error) {
63 bbus := make(map[string]*megaBBU)
64
65 var section string
66 var bbu *megaBBU
67
68 sc := bufio.NewScanner(bytes.NewReader(bs))
69
70 for sc.Scan() {
71 line := strings.TrimSpace(sc.Text())
72
73 switch {
74 case strings.HasPrefix(line, "BBU status for Adapter"):
75 section = "status"
76 ad := getColonSepValue(line)
77 if _, ok := bbus[ad]; !ok {
78 bbu = &megaBBU{adapterNumber: ad}
79 bbus[ad] = bbu
80 }
81 continue
82 case strings.HasPrefix(line, "BBU Capacity Info for Adapter"):
83 section = "capacity"
84 continue
85 case strings.HasPrefix(line, "BBU Design Info for Adapter"):
86 section = "design"
87 continue
88 case strings.HasPrefix(line, "BBU Firmware Status"),
89 strings.HasPrefix(line, "BBU GasGauge Status"),
90 strings.HasPrefix(line, "BBU Properties for Adapter"):
91 section = ""
92 continue
93 }
94
95 if bbu == nil {
96 continue
97 }
98
99 switch section {
100 case "status":
101 switch {
102 case strings.HasPrefix(line, "BatteryType:"):
103 bbu.batteryType = getColonSepValue(line)
104 case strings.HasPrefix(line, "Temperature:"):
105 bbu.temperature = getColonSepNumValue(line)
106 }
107 case "capacity":
108 switch {
109 case strings.HasPrefix(line, "Relative State of Charge:"):
110 bbu.rsoc = getColonSepNumValue(line)
111 case strings.HasPrefix(line, "Absolute State of charge:"):
112 bbu.asoc = getColonSepNumValue(line)
113 case strings.HasPrefix(line, "Full Charge Capacity:"):
114 bbu.fullChargeCap = getColonSepNumValue(line)
115 case strings.HasPrefix(line, "Cycle Count:"):
116 bbu.cycleCount = getColonSepNumValue(line)
117 }
118 case "design":
119 if strings.HasPrefix(line, "Design Capacity:") {
120 bbu.designCap = getColonSepNumValue(line)
121 }
122 }
123 }
124
125 return bbus, nil
126 }
127
128 func calcCapDegradationPerc(bbu *megaBBU) (int64, bool) {
129 full, err := strconv.ParseInt(bbu.fullChargeCap, 10, 64)
130 if err != nil || full == 0 {
131 return 0, false
132 }
133 design, err := strconv.ParseInt(bbu.designCap, 10, 64)
134 if err != nil || design == 0 {
135 return 0, false
136 }
137
138 v := 100 - float64(full)/float64(design)*100
139
140 return int64(v), true
141 }