master
go 403 lines 11.7 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package upsd
4
5 import (
6 "fmt"
7 "strings"
8
9 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
10 )
11
12 const (
13 prioUpsLoad = collectorapi.Priority + iota
14 prioUpsLoadUsage
15 prioUpsStatus
16 prioUpsTemperature
17
18 prioBatteryCharge
19 prioBatteryEstimatedRuntime
20 prioBatteryVoltage
21 prioBatteryVoltageNominal
22
23 prioInputVoltage
24 prioInputVoltageNominal
25 prioInputCurrent
26 prioInputCurrentNominal
27 prioInputFrequency
28 prioInputFrequencyNominal
29
30 prioOutputVoltage
31 prioOutputVoltageNominal
32 prioOutputCurrent
33 prioOutputCurrentNominal
34 prioOutputFrequency
35 prioOutputFrequencyNominal
36 )
37
38 var upsChartsTmpl = collectorapi.Charts{
39 upsLoadChartTmpl.Copy(),
40 upsLoadUsageChartTmpl.Copy(),
41 upsStatusChartTmpl.Copy(),
42 upsTemperatureChartTmpl.Copy(),
43
44 upsBatteryChargePercentChartTmpl.Copy(),
45 upsBatteryEstimatedRuntimeChartTmpl.Copy(),
46 upsBatteryVoltageChartTmpl.Copy(),
47 upsBatteryVoltageNominalChartTmpl.Copy(),
48
49 upsInputVoltageChartTmpl.Copy(),
50 upsInputVoltageNominalChartTmpl.Copy(),
51 upsInputCurrentChartTmpl.Copy(),
52 upsInputCurrentNominalChartTmpl.Copy(),
53 upsInputFrequencyChartTmpl.Copy(),
54 upsInputFrequencyNominalChartTmpl.Copy(),
55
56 upsOutputVoltageChartTmpl.Copy(),
57 upsOutputVoltageNominalChartTmpl.Copy(),
58 upsOutputCurrentChartTmpl.Copy(),
59 upsOutputCurrentNominalChartTmpl.Copy(),
60 upsOutputFrequencyChartTmpl.Copy(),
61 upsOutputFrequencyNominalChartTmpl.Copy(),
62 }
63
64 var (
65 upsLoadChartTmpl = collectorapi.Chart{
66 IDSep: true,
67 ID: "%s.load_percentage",
68 Title: "UPS load",
69 Units: "percentage",
70 Fam: "ups",
71 Ctx: "upsd.ups_load",
72 Priority: prioUpsLoad,
73 Type: collectorapi.Area,
74 Dims: collectorapi.Dims{
75 {ID: "ups_%s_ups.load", Name: "load", Div: varPrecision},
76 },
77 }
78 upsLoadUsageChartTmpl = collectorapi.Chart{
79 IDSep: true,
80 ID: "%s.load_usage",
81 Title: "UPS load usage (power output)",
82 Units: "Watts",
83 Fam: "ups",
84 Ctx: "upsd.ups_load_usage",
85 Priority: prioUpsLoadUsage,
86 Dims: collectorapi.Dims{
87 {ID: "ups_%s_ups.load.usage", Name: "load_usage", Div: varPrecision},
88 },
89 }
90 upsStatusChartTmpl = collectorapi.Chart{
91 IDSep: true,
92 ID: "%s.status",
93 Title: "UPS status",
94 Units: "status",
95 Fam: "ups",
96 Ctx: "upsd.ups_status",
97 Priority: prioUpsStatus,
98 Dims: collectorapi.Dims{
99 {ID: "ups_%s_ups.status.OL", Name: "on_line"},
100 {ID: "ups_%s_ups.status.OB", Name: "on_battery"},
101 {ID: "ups_%s_ups.status.LB", Name: "low_battery"},
102 {ID: "ups_%s_ups.status.HB", Name: "high_battery"},
103 {ID: "ups_%s_ups.status.RB", Name: "replace_battery"},
104 {ID: "ups_%s_ups.status.CHRG", Name: "charging"},
105 {ID: "ups_%s_ups.status.DISCHRG", Name: "discharging"},
106 {ID: "ups_%s_ups.status.BYPASS", Name: "bypass"},
107 {ID: "ups_%s_ups.status.CAL", Name: "calibration"},
108 {ID: "ups_%s_ups.status.OFF", Name: "offline"},
109 {ID: "ups_%s_ups.status.OVER", Name: "overloaded"},
110 {ID: "ups_%s_ups.status.TRIM", Name: "trim_input_voltage"},
111 {ID: "ups_%s_ups.status.BOOST", Name: "boost_input_voltage"},
112 {ID: "ups_%s_ups.status.FSD", Name: "forced_shutdown"},
113 {ID: "ups_%s_ups.status.other", Name: "other"},
114 },
115 }
116 upsTemperatureChartTmpl = collectorapi.Chart{
117 IDSep: true,
118 ID: "%s.temperature",
119 Title: "UPS temperature",
120 Units: "Celsius",
121 Fam: "ups",
122 Ctx: "upsd.ups_temperature",
123 Priority: prioUpsTemperature,
124 Dims: collectorapi.Dims{
125 {ID: "ups_%s_ups.temperature", Name: "temperature", Div: varPrecision},
126 },
127 }
128 )
129
130 var (
131 upsBatteryChargePercentChartTmpl = collectorapi.Chart{
132 IDSep: true,
133 ID: "%s.battery_charge_percentage",
134 Title: "UPS Battery charge",
135 Units: "percentage",
136 Fam: "battery",
137 Ctx: "upsd.ups_battery_charge",
138 Priority: prioBatteryCharge,
139 Type: collectorapi.Area,
140 Dims: collectorapi.Dims{
141 {ID: "ups_%s_battery.charge", Name: "charge", Div: varPrecision},
142 },
143 }
144 upsBatteryEstimatedRuntimeChartTmpl = collectorapi.Chart{
145 IDSep: true,
146 ID: "%s.battery_estimated_runtime",
147 Title: "UPS Battery estimated runtime",
148 Units: "seconds",
149 Fam: "battery",
150 Ctx: "upsd.ups_battery_estimated_runtime",
151 Priority: prioBatteryEstimatedRuntime,
152 Dims: collectorapi.Dims{
153 {ID: "ups_%s_battery.runtime", Name: "runtime", Div: varPrecision},
154 },
155 }
156 upsBatteryVoltageChartTmpl = collectorapi.Chart{
157 IDSep: true,
158 ID: "%s.battery_voltage",
159 Title: "UPS Battery voltage",
160 Units: "Volts",
161 Fam: "battery",
162 Ctx: "upsd.ups_battery_voltage",
163 Priority: prioBatteryVoltage,
164 Dims: collectorapi.Dims{
165 {ID: "ups_%s_battery.voltage", Name: "voltage", Div: varPrecision},
166 },
167 }
168 upsBatteryVoltageNominalChartTmpl = collectorapi.Chart{
169 IDSep: true,
170 ID: "%s.battery_voltage_nominal",
171 Title: "UPS Battery voltage nominal",
172 Units: "Volts",
173 Fam: "battery",
174 Ctx: "upsd.ups_battery_voltage_nominal",
175 Priority: prioBatteryVoltageNominal,
176 Dims: collectorapi.Dims{
177 {ID: "ups_%s_battery.voltage.nominal", Name: "nominal_voltage", Div: varPrecision},
178 },
179 }
180 )
181
182 var (
183 upsInputVoltageChartTmpl = collectorapi.Chart{
184 IDSep: true,
185 ID: "%s.input_voltage",
186 Title: "UPS Input voltage",
187 Units: "Volts",
188 Fam: "input",
189 Ctx: "upsd.ups_input_voltage",
190 Priority: prioInputVoltage,
191 Dims: collectorapi.Dims{
192 {ID: "ups_%s_input.voltage", Name: "voltage", Div: varPrecision},
193 },
194 }
195 upsInputVoltageNominalChartTmpl = collectorapi.Chart{
196 IDSep: true,
197 ID: "%s.input_voltage_nominal",
198 Title: "UPS Input voltage nominal",
199 Units: "Volts",
200 Fam: "input",
201 Ctx: "upsd.ups_input_voltage_nominal",
202 Priority: prioInputVoltageNominal,
203 Dims: collectorapi.Dims{
204 {ID: "ups_%s_input.voltage.nominal", Name: "nominal_voltage", Div: varPrecision},
205 },
206 }
207 upsInputCurrentChartTmpl = collectorapi.Chart{
208 IDSep: true,
209 ID: "%s.input_current",
210 Title: "UPS Input current",
211 Units: "Ampere",
212 Fam: "input",
213 Ctx: "upsd.ups_input_current",
214 Priority: prioInputCurrent,
215 Dims: collectorapi.Dims{
216 {ID: "ups_%s_input.current", Name: "current", Div: varPrecision},
217 },
218 }
219 upsInputCurrentNominalChartTmpl = collectorapi.Chart{
220 IDSep: true,
221 ID: "%s.input_current_nominal",
222 Title: "UPS Input current nominal",
223 Units: "Ampere",
224 Fam: "input",
225 Ctx: "upsd.ups_input_current_nominal",
226 Priority: prioInputCurrentNominal,
227 Dims: collectorapi.Dims{
228 {ID: "ups_%s_input.current.nominal", Name: "nominal_current", Div: varPrecision},
229 },
230 }
231 upsInputFrequencyChartTmpl = collectorapi.Chart{
232 IDSep: true,
233 ID: "%s.input_frequency",
234 Title: "UPS Input frequency",
235 Units: "Hz",
236 Fam: "input",
237 Ctx: "upsd.ups_input_frequency",
238 Priority: prioInputFrequency,
239 Dims: collectorapi.Dims{
240 {ID: "ups_%s_input.frequency", Name: "frequency", Div: varPrecision},
241 },
242 }
243 upsInputFrequencyNominalChartTmpl = collectorapi.Chart{
244 IDSep: true,
245 ID: "%s.input_frequency_nominal",
246 Title: "UPS Input frequency nominal",
247 Units: "Hz",
248 Fam: "input",
249 Ctx: "upsd.ups_input_frequency_nominal",
250 Priority: prioInputFrequencyNominal,
251 Dims: collectorapi.Dims{
252 {ID: "ups_%s_input.frequency.nominal", Name: "nominal_frequency", Div: varPrecision},
253 },
254 }
255 )
256
257 var (
258 upsOutputVoltageChartTmpl = collectorapi.Chart{
259 IDSep: true,
260 ID: "%s.output_voltage",
261 Title: "UPS Output voltage",
262 Units: "Volts",
263 Fam: "output",
264 Ctx: "upsd.ups_output_voltage",
265 Priority: prioOutputVoltage,
266 Dims: collectorapi.Dims{
267 {ID: "ups_%s_output.voltage", Name: "voltage", Div: varPrecision},
268 },
269 }
270 upsOutputVoltageNominalChartTmpl = collectorapi.Chart{
271 IDSep: true,
272 ID: "%s.output_voltage_nominal",
273 Title: "UPS Output voltage nominal",
274 Units: "Volts",
275 Fam: "output",
276 Ctx: "upsd.ups_output_voltage_nominal",
277 Priority: prioOutputVoltageNominal,
278 Dims: collectorapi.Dims{
279 {ID: "ups_%s_output.voltage.nominal", Name: "nominal_voltage", Div: varPrecision},
280 },
281 }
282 upsOutputCurrentChartTmpl = collectorapi.Chart{
283 IDSep: true,
284 ID: "%s.output_current",
285 Title: "UPS Output current",
286 Units: "Ampere",
287 Fam: "output",
288 Ctx: "upsd.ups_output_current",
289 Priority: prioOutputCurrent,
290 Dims: collectorapi.Dims{
291 {ID: "ups_%s_output.current", Name: "current", Div: varPrecision},
292 },
293 }
294 upsOutputCurrentNominalChartTmpl = collectorapi.Chart{
295 IDSep: true,
296 ID: "%s.output_current_nominal",
297 Title: "UPS Output current nominal",
298 Units: "Ampere",
299 Fam: "output",
300 Ctx: "upsd.ups_output_current_nominal",
301 Priority: prioOutputCurrentNominal,
302 Dims: collectorapi.Dims{
303 {ID: "ups_%s_output.current.nominal", Name: "nominal_current", Div: varPrecision},
304 },
305 }
306 upsOutputFrequencyChartTmpl = collectorapi.Chart{
307 IDSep: true,
308 ID: "%s.output_frequency",
309 Title: "UPS Output frequency",
310 Units: "Hz",
311 Fam: "output",
312 Ctx: "upsd.ups_output_frequency",
313 Priority: prioOutputFrequency,
314 Dims: collectorapi.Dims{
315 {ID: "ups_%s_output.frequency", Name: "frequency", Div: varPrecision},
316 },
317 }
318 upsOutputFrequencyNominalChartTmpl = collectorapi.Chart{
319 IDSep: true,
320 ID: "%s.output_frequency_nominal",
321 Title: "UPS Output frequency nominal",
322 Units: "Hz",
323 Fam: "output",
324 Ctx: "upsd.ups_output_frequency_nominal",
325 Priority: prioOutputFrequencyNominal,
326 Dims: collectorapi.Dims{
327 {ID: "ups_%s_output.frequency.nominal", Name: "nominal_frequency", Div: varPrecision},
328 },
329 }
330 )
331
332 func (c *Collector) addUPSCharts(ups upsUnit) {
333 charts := upsChartsTmpl.Copy()
334
335 var removed []string
336 for _, v := range []struct{ v, id string }{
337 {varUpsLoad, upsLoadChartTmpl.ID},
338 {varUpsLoad, upsLoadUsageChartTmpl.ID},
339
340 {varBatteryVoltage, upsBatteryVoltageChartTmpl.ID},
341 {varBatteryVoltageNominal, upsBatteryVoltageNominalChartTmpl.ID},
342
343 {varUpsTemperature, upsTemperatureChartTmpl.ID},
344
345 {varInputVoltage, upsInputVoltageChartTmpl.ID},
346 {varInputVoltageNominal, upsInputVoltageNominalChartTmpl.ID},
347 {varInputCurrent, upsInputCurrentChartTmpl.ID},
348 {varInputCurrentNominal, upsInputCurrentNominalChartTmpl.ID},
349 {varInputFrequency, upsInputFrequencyChartTmpl.ID},
350 {varInputFrequencyNominal, upsInputFrequencyNominalChartTmpl.ID},
351
352 {varOutputVoltage, upsOutputVoltageChartTmpl.ID},
353 {varOutputVoltageNominal, upsOutputVoltageNominalChartTmpl.ID},
354 {varOutputCurrent, upsOutputCurrentChartTmpl.ID},
355 {varOutputCurrentNominal, upsOutputCurrentNominalChartTmpl.ID},
356 {varOutputFrequency, upsOutputFrequencyChartTmpl.ID},
357 {varOutputFrequencyNominal, upsOutputFrequencyNominalChartTmpl.ID},
358 } {
359 if !hasVar(ups.vars, v.v) {
360 removed = append(removed, v.v)
361 _ = charts.Remove(v.id)
362 }
363 }
364
365 c.Debugf("UPS '%s' no metrics: %v", ups.name, removed)
366
367 name := cleanUpsName(ups.name)
368
369 for _, chart := range *charts {
370 chart.ID = fmt.Sprintf(chart.ID, name)
371 chart.Labels = []collectorapi.Label{
372 {Key: "ups_name", Value: ups.name},
373 {Key: "battery_type", Value: ups.vars[varBatteryType]},
374 {Key: "device_model", Value: ups.vars[varDeviceModel]},
375 {Key: "device_serial", Value: ups.vars[varDeviceSerial]},
376 {Key: "device_manufacturer", Value: ups.vars[varDeviceMfr]},
377 {Key: "device_type", Value: ups.vars[varDeviceType]},
378 }
379 for _, dim := range chart.Dims {
380 dim.ID = fmt.Sprintf(dim.ID, ups.name)
381 }
382 }
383
384 if err := c.Charts().Add(*charts...); err != nil {
385 c.Warning(err)
386 }
387 }
388
389 func (c *Collector) removeUPSCharts(name string) {
390 name = cleanUpsName(name)
391 for _, chart := range *c.Charts() {
392 if strings.HasPrefix(chart.ID, name) {
393 chart.MarkRemove()
394 chart.MarkNotCreated()
395 }
396 }
397 }
398
399 func cleanUpsName(name string) string {
400 name = strings.ReplaceAll(name, " ", "_")
401 name = strings.ReplaceAll(name, ".", "_")
402 return name
403 }