master
go 180 lines 3.83 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package upsd
4
5 import (
6 "errors"
7 "strconv"
8 "strings"
9 )
10
11 func (c *Collector) collect() (map[string]int64, error) {
12 if c.conn == nil {
13 conn, err := c.establishConnection()
14 if err != nil {
15 return nil, err
16 }
17 c.conn = conn
18 }
19
20 upsUnits, err := c.conn.upsUnits()
21 if err != nil {
22 if !errors.Is(err, errUpsdCommand) {
23 _ = c.conn.disconnect()
24 c.conn = nil
25 }
26 return nil, err
27 }
28
29 c.Debugf("found %d UPS units", len(upsUnits))
30
31 mx := make(map[string]int64)
32
33 c.collectUPSUnits(mx, upsUnits)
34
35 return mx, nil
36 }
37
38 func (c *Collector) establishConnection() (upsdConn, error) {
39 conn := c.newUpsdConn(c.Config)
40
41 if err := conn.connect(); err != nil {
42 return nil, err
43 }
44
45 if c.Username != "" && c.Password != "" {
46 if err := conn.authenticate(c.Username, c.Password); err != nil {
47 _ = conn.disconnect()
48 return nil, err
49 }
50 }
51
52 return conn, nil
53 }
54
55 func (c *Collector) collectUPSUnits(mx map[string]int64, upsUnits []upsUnit) {
56 seen := make(map[string]bool)
57
58 for _, ups := range upsUnits {
59 seen[ups.name] = true
60 c.Debugf("collecting metrics UPS '%s'", ups.name)
61
62 if !c.upsUnits[ups.name] {
63 c.upsUnits[ups.name] = true
64 c.addUPSCharts(ups)
65 }
66
67 writeVar(mx, ups, varBatteryCharge)
68 writeVar(mx, ups, varBatteryRuntime)
69 writeVar(mx, ups, varBatteryVoltage)
70 writeVar(mx, ups, varBatteryVoltageNominal)
71
72 writeVar(mx, ups, varInputVoltage)
73 writeVar(mx, ups, varInputVoltageNominal)
74 writeVar(mx, ups, varInputCurrent)
75 writeVar(mx, ups, varInputCurrentNominal)
76 writeVar(mx, ups, varInputFrequency)
77 writeVar(mx, ups, varInputFrequencyNominal)
78
79 writeVar(mx, ups, varOutputVoltage)
80 writeVar(mx, ups, varOutputVoltageNominal)
81 writeVar(mx, ups, varOutputCurrent)
82 writeVar(mx, ups, varOutputCurrentNominal)
83 writeVar(mx, ups, varOutputFrequency)
84 writeVar(mx, ups, varOutputFrequencyNominal)
85
86 writeVar(mx, ups, varUpsLoad)
87 writeVar(mx, ups, varUpsRealPowerNominal)
88 writeVar(mx, ups, varUpsTemperature)
89 writeUpsLoadUsage(mx, ups)
90 writeUpsStatus(mx, ups)
91 }
92
93 for name := range c.upsUnits {
94 if !seen[name] {
95 delete(c.upsUnits, name)
96 c.removeUPSCharts(name)
97 }
98 }
99 }
100
101 func writeVar(mx map[string]int64, ups upsUnit, v string) {
102 s, ok := ups.vars[v]
103 if !ok {
104 return
105 }
106 n, err := strconv.ParseFloat(s, 64)
107 if err != nil {
108 return
109 }
110 mx[prefix(ups)+v] = int64(n * varPrecision)
111 }
112
113 func writeUpsLoadUsage(mx map[string]int64, ups upsUnit) {
114 if hasVar(ups.vars, varUpsRealPower) {
115 pow, _ := strconv.ParseFloat(ups.vars[varUpsRealPower], 64)
116 mx[prefix(ups)+"ups.load.usage"] = int64(pow * varPrecision)
117 return
118 }
119
120 if !hasVar(ups.vars, varUpsLoad) || !hasVar(ups.vars, varUpsRealPowerNominal) {
121 return
122 }
123 load, err := strconv.ParseFloat(ups.vars[varUpsLoad], 64)
124 if err != nil {
125 return
126 }
127 nomPower, err := strconv.ParseFloat(ups.vars[varUpsRealPowerNominal], 64)
128 if err != nil || nomPower == 0 {
129 return
130 }
131 mx[prefix(ups)+"ups.load.usage"] = int64((load / 100 * nomPower) * varPrecision)
132 }
133
134 // https://networkupstools.org/docs/developer-guide.chunked/ar01s04.html#_status_data
135 var upsStatuses = map[string]bool{
136 "OL": true,
137 "OB": true,
138 "LB": true,
139 "HB": true,
140 "RB": true,
141 "CHRG": true,
142 "DISCHRG": true,
143 "BYPASS": true,
144 "CAL": true,
145 "OFF": true,
146 "OVER": true,
147 "TRIM": true,
148 "BOOST": true,
149 "FSD": true,
150 }
151
152 func writeUpsStatus(mx map[string]int64, ups upsUnit) {
153 if !hasVar(ups.vars, varUpsStatus) {
154 return
155 }
156
157 px := prefix(ups) + "ups.status."
158
159 for st := range upsStatuses {
160 mx[px+st] = 0
161 }
162 mx[px+"other"] = 0
163
164 for st := range strings.SplitSeq(ups.vars[varUpsStatus], " ") {
165 if _, ok := upsStatuses[st]; ok {
166 mx[px+st] = 1
167 } else {
168 mx[px+"other"] = 1
169 }
170 }
171 }
172
173 func hasVar(vars map[string]string, v string) bool {
174 _, ok := vars[v]
175 return ok
176 }
177
178 func prefix(ups upsUnit) string {
179 return "ups_" + ups.name + "_"
180 }