master
go 554 lines 14.2 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 //go:build linux
4
5 package sensors
6
7 import (
8 "fmt"
9 "strings"
10
11 "github.com/netdata/netdata/go/plugins/pkg/matcher"
12 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
13 "github.com/netdata/netdata/go/plugins/plugin/go.d/collector/sensors/lmsensors"
14 )
15
16 const (
17 prioTemperatureSensorInput = collectorapi.Priority + iota
18 prioTemperatureSensorAlarm
19
20 prioVoltageSensorInput
21 prioVoltageSensorAverage
22 prioVoltageSensorAlarm
23
24 prioFanSensorInput
25 prioFanSensorAlarm
26
27 prioCurrentSensorInput
28 prioCurrentSensorAverage
29 prioCurrentSensorAlarm
30
31 prioPowerSensorInput
32 prioPowerSensorAverage
33 prioPowerSensorAlarm
34
35 prioEnergySensorInput
36
37 prioHumiditySensorInput
38
39 prioIntrusionSensorAlarm
40 )
41
42 var temperatureSensorChartsTmpl = collectorapi.Charts{
43 temperatureSensorInputChartTmpl.Copy(),
44 temperatureSensorAlarmChartTmpl.Copy(),
45 }
46
47 var voltageSensorChartsTmpl = collectorapi.Charts{
48 voltageSensorInputChartTmpl.Copy(),
49 voltageSensorAverageChartTmpl.Copy(),
50 voltageSensorAlarmChartTmpl.Copy(),
51 }
52
53 var fanSensorChartsTmpl = collectorapi.Charts{
54 fanSensorInputChartTmpl.Copy(),
55 fanSensorAlarmChartTmpl.Copy(),
56 }
57
58 var currentSensorChartsTmpl = collectorapi.Charts{
59 currentSensorInputChartTmpl.Copy(),
60 currentSensorAverageChartTmpl.Copy(),
61 currentSensorAlarmChartTmpl.Copy(),
62 }
63
64 var powerSensorChartsTmpl = collectorapi.Charts{
65 powerSensorInputChartTmpl.Copy(),
66 powerSensorAverageChartTmpl.Copy(),
67 powerSensorAlarmChartTmpl.Copy(),
68 }
69
70 var energySensorChartsTmpl = collectorapi.Charts{
71 energySensorInputChartTmpl.Copy(),
72 }
73
74 var humiditySensorChartsTmpl = collectorapi.Charts{
75 humiditySensorInputChartTmpl.Copy(),
76 }
77
78 var intrusionSensorChartsTmpl = collectorapi.Charts{
79 intrusionSensorAlarmChartTmpl.Copy(),
80 }
81
82 var (
83 temperatureSensorInputChartTmpl = collectorapi.Chart{
84 ID: "%s_%s_temperature",
85 Title: "Sensor Temperature",
86 Units: "Celsius",
87 Fam: "temperature",
88 Ctx: "sensors.chip_sensor_temperature",
89 Type: collectorapi.Line,
90 Priority: prioTemperatureSensorInput,
91 Dims: collectorapi.Dims{
92 {ID: "chip_%s_sensor_%s_input", Name: "input", Div: precision},
93 },
94 }
95 temperatureSensorAlarmChartTmpl = collectorapi.Chart{
96 ID: "%s_%s_temperature_alarm",
97 Title: "Temperature Sensor Alarm",
98 Units: "status",
99 Fam: "temperature",
100 Ctx: "sensors.chip_sensor_temperature_alarm",
101 Type: collectorapi.Line,
102 Priority: prioTemperatureSensorAlarm,
103 Dims: collectorapi.Dims{
104 {ID: "chip_%s_sensor_%s_alarm_clear", Name: "clear"},
105 {ID: "chip_%s_sensor_%s_alarm_triggered", Name: "triggered"},
106 },
107 }
108 )
109
110 var (
111 voltageSensorInputChartTmpl = collectorapi.Chart{
112 ID: "%s_%s_voltage",
113 Title: "Sensor Voltage",
114 Units: "Volts",
115 Fam: "voltage",
116 Ctx: "sensors.chip_sensor_voltage",
117 Type: collectorapi.Line,
118 Priority: prioVoltageSensorInput,
119 Dims: collectorapi.Dims{
120 {ID: "chip_%s_sensor_%s_input", Name: "input", Div: precision},
121 },
122 }
123 voltageSensorAverageChartTmpl = collectorapi.Chart{
124 ID: "%s_%s_voltage_average",
125 Title: "Sensor Voltage Average",
126 Units: "Volts",
127 Fam: "voltage",
128 Ctx: "sensors.chip_sensor_voltage_average",
129 Type: collectorapi.Line,
130 Priority: prioVoltageSensorAverage,
131 Dims: collectorapi.Dims{
132 {ID: "chip_%s_sensor_%s_average", Name: "average", Div: precision},
133 },
134 }
135 voltageSensorAlarmChartTmpl = collectorapi.Chart{
136 ID: "%s_%s_voltage_alarm",
137 Title: "Voltage Sensor Alarm",
138 Units: "status",
139 Fam: "voltage",
140 Ctx: "sensors.chip_sensor_voltage_alarm",
141 Type: collectorapi.Line,
142 Priority: prioVoltageSensorAlarm,
143 Dims: collectorapi.Dims{
144 {ID: "chip_%s_sensor_%s_alarm_clear", Name: "clear"},
145 {ID: "chip_%s_sensor_%s_alarm_triggered", Name: "triggered"},
146 },
147 }
148 )
149
150 var (
151 fanSensorInputChartTmpl = collectorapi.Chart{
152 ID: "%s_%s_fan",
153 Title: "Sensor Fan",
154 Units: "RPM",
155 Fam: "fan",
156 Ctx: "sensors.chip_sensor_fan",
157 Type: collectorapi.Line,
158 Priority: prioFanSensorInput,
159 Dims: collectorapi.Dims{
160 {ID: "chip_%s_sensor_%s_input", Name: "input", Div: precision},
161 },
162 }
163 fanSensorAlarmChartTmpl = collectorapi.Chart{
164 ID: "%s_%s_fan_alarm",
165 Title: "Fan Sensor Alarm",
166 Units: "status",
167 Fam: "fan",
168 Ctx: "sensors.chip_sensor_fan_alarm",
169 Type: collectorapi.Line,
170 Priority: prioFanSensorAlarm,
171 Dims: collectorapi.Dims{
172 {ID: "chip_%s_sensor_%s_alarm_clear", Name: "clear"},
173 {ID: "chip_%s_sensor_%s_alarm_triggered", Name: "triggered"},
174 },
175 }
176 )
177
178 var (
179 currentSensorInputChartTmpl = collectorapi.Chart{
180 ID: "%s_%s_current",
181 Title: "Sensor Current",
182 Units: "Amperes",
183 Fam: "current",
184 Ctx: "sensors.chip_sensor_current",
185 Type: collectorapi.Line,
186 Priority: prioCurrentSensorInput,
187 Dims: collectorapi.Dims{
188 {ID: "chip_%s_sensor_%s_input", Name: "input", Div: precision},
189 },
190 }
191 currentSensorAverageChartTmpl = collectorapi.Chart{
192 ID: "%s_%s_current_average",
193 Title: "Sensor Current Average",
194 Units: "Amperes",
195 Fam: "current",
196 Ctx: "sensors.chip_sensor_current_average",
197 Type: collectorapi.Line,
198 Priority: prioCurrentSensorAverage,
199 Dims: collectorapi.Dims{
200 {ID: "chip_%s_sensor_%s_average", Name: "average", Div: precision},
201 },
202 }
203 currentSensorAlarmChartTmpl = collectorapi.Chart{
204 ID: "%s_%s_current_alarm",
205 Title: "Sensor Alarm",
206 Units: "status",
207 Fam: "current",
208 Ctx: "sensors.chip_sensor_current_alarm",
209 Type: collectorapi.Line,
210 Priority: prioCurrentSensorAlarm,
211 Dims: collectorapi.Dims{
212 {ID: "chip_%s_sensor_%s_alarm_clear", Name: "clear"},
213 {ID: "chip_%s_sensor_%s_alarm_triggered", Name: "triggered"},
214 },
215 }
216 )
217
218 var (
219 powerSensorInputChartTmpl = collectorapi.Chart{
220 ID: "%s_%s_power",
221 Title: "Sensor Power",
222 Units: "Watts",
223 Fam: "power",
224 Ctx: "sensors.chip_sensor_power",
225 Type: collectorapi.Line,
226 Priority: prioPowerSensorInput,
227 Dims: collectorapi.Dims{
228 {ID: "chip_%s_sensor_%s_input", Name: "input", Div: precision},
229 },
230 }
231 powerSensorAverageChartTmpl = collectorapi.Chart{
232 ID: "%s_%s_power_average",
233 Title: "Sensor Power Average",
234 Units: "Watts",
235 Fam: "power",
236 Ctx: "sensors.chip_sensor_power_average",
237 Type: collectorapi.Line,
238 Priority: prioPowerSensorAverage,
239 Dims: collectorapi.Dims{
240 {ID: "chip_%s_sensor_%s_average", Name: "average", Div: precision},
241 },
242 }
243 powerSensorAlarmChartTmpl = collectorapi.Chart{
244 ID: "%s_%s_power_alarm",
245 Title: "Power Sensor Alarm",
246 Units: "status",
247 Fam: "current",
248 Ctx: "sensors.chip_sensor_power_alarm",
249 Type: collectorapi.Line,
250 Priority: prioPowerSensorAlarm,
251 Dims: collectorapi.Dims{
252 {ID: "chip_%s_sensor_%s_alarm_clear", Name: "clear"},
253 {ID: "chip_%s_sensor_%s_alarm_triggered", Name: "triggered"},
254 },
255 }
256 )
257
258 var (
259 energySensorInputChartTmpl = collectorapi.Chart{
260 ID: "%s_%s_energy",
261 Title: "Sensor Energy",
262 Units: "Joules",
263 Fam: "energy",
264 Ctx: "sensors.chip_sensor_energy",
265 Type: collectorapi.Line,
266 Priority: prioEnergySensorInput,
267 Dims: collectorapi.Dims{
268 {ID: "chip_%s_sensor_%s_input", Name: "input", Div: precision},
269 },
270 }
271 )
272
273 var (
274 humiditySensorInputChartTmpl = collectorapi.Chart{
275 ID: "%s_%s_humidity",
276 Title: "Sensor Humidity",
277 Units: "percent",
278 Fam: "humidity",
279 Ctx: "sensors.chip_sensor_humidity",
280 Type: collectorapi.Line,
281 Priority: prioHumiditySensorInput,
282 Dims: collectorapi.Dims{
283 {ID: "chip_%s_sensor_%s_input", Name: "input", Div: precision},
284 },
285 }
286 )
287
288 var (
289 intrusionSensorAlarmChartTmpl = collectorapi.Chart{
290 ID: "%s_%s_intrusion_alarm",
291 Title: "Sensor Intrusion Alarm",
292 Units: "status",
293 Fam: "intrusion",
294 Ctx: "sensors.chip_sensor_intrusion_alarm",
295 Type: collectorapi.Line,
296 Priority: prioIntrusionSensorAlarm,
297 Dims: collectorapi.Dims{
298 {ID: "chip_%s_sensor_%s_alarm_clear", Name: "clear"},
299 {ID: "chip_%s_sensor_%s_alarm_triggered", Name: "triggered"},
300 },
301 }
302 )
303
304 func (c *Collector) updateCharts(chips []*lmsensors.Chip) {
305 seen := make(map[string]bool)
306
307 for _, chip := range chips {
308 for _, sn := range chip.Sensors.Voltage {
309 key := chip.UniqueName + "_" + sn.Name
310 seen[key] = true
311 if !c.seenSensors[key] {
312 c.seenSensors[key] = true
313 c.addVoltageCharts(chip, sn)
314 }
315 }
316 for _, sn := range chip.Sensors.Fan {
317 key := chip.UniqueName + "_" + sn.Name
318 seen[key] = true
319 if !c.seenSensors[key] {
320 c.seenSensors[key] = true
321 c.addFanCharts(chip, sn)
322 }
323 }
324 for _, sn := range chip.Sensors.Temperature {
325 key := chip.UniqueName + "_" + sn.Name
326 seen[key] = true
327 if !c.seenSensors[key] {
328 c.seenSensors[key] = true
329 c.addTemperatureCharts(chip, sn)
330 }
331 }
332 for _, sn := range chip.Sensors.Current {
333 key := chip.UniqueName + "_" + sn.Name
334 seen[key] = true
335 if !c.seenSensors[key] {
336 c.seenSensors[key] = true
337 c.addCurrentCharts(chip, sn)
338 }
339 }
340 for _, sn := range chip.Sensors.Power {
341 key := chip.UniqueName + "_" + sn.Name
342 seen[key] = true
343 if !c.seenSensors[key] {
344 c.seenSensors[key] = true
345 c.addPowerCharts(chip, sn)
346 }
347 }
348 for _, sn := range chip.Sensors.Energy {
349 key := chip.UniqueName + "_" + sn.Name
350 seen[key] = true
351 if !c.seenSensors[key] {
352 c.seenSensors[key] = true
353 c.addEnergyCharts(chip, sn)
354 }
355 }
356 for _, sn := range chip.Sensors.Humidity {
357 key := chip.UniqueName + "_" + sn.Name
358 seen[key] = true
359 if !c.seenSensors[key] {
360 c.seenSensors[key] = true
361 c.addHumidityCharts(chip, sn)
362 }
363 }
364 for _, sn := range chip.Sensors.Intrusion {
365 key := chip.UniqueName + "_" + sn.Name
366 seen[key] = true
367 if !c.seenSensors[key] {
368 c.seenSensors[key] = true
369 c.addIntrusionCharts(chip, sn)
370 }
371 }
372 }
373
374 for key := range c.seenSensors {
375 if !seen[key] {
376 delete(c.seenSensors, key)
377 c.removeSensorChart(key)
378 }
379 }
380 }
381
382 func (c *Collector) addTemperatureCharts(chip *lmsensors.Chip, sn *lmsensors.TemperatureSensor) {
383 charts := temperatureSensorChartsTmpl.Copy()
384
385 if sn.Input == nil {
386 _ = charts.Remove(temperatureSensorInputChartTmpl.ID)
387 }
388 if sn.Alarm == nil {
389 _ = charts.Remove(temperatureSensorAlarmChartTmpl.ID)
390 }
391
392 c.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
393 }
394
395 func (c *Collector) addVoltageCharts(chip *lmsensors.Chip, sn *lmsensors.VoltageSensor) {
396 charts := voltageSensorChartsTmpl.Copy()
397
398 if sn.Input == nil {
399 _ = charts.Remove(voltageSensorInputChartTmpl.ID)
400 }
401 if sn.Average == nil {
402 _ = charts.Remove(voltageSensorAverageChartTmpl.ID)
403 }
404 if sn.Alarm == nil {
405 _ = charts.Remove(voltageSensorAlarmChartTmpl.ID)
406 }
407
408 c.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
409 }
410
411 func (c *Collector) addFanCharts(chip *lmsensors.Chip, sn *lmsensors.FanSensor) {
412 charts := fanSensorChartsTmpl.Copy()
413
414 if sn.Input == nil {
415 _ = charts.Remove(fanSensorInputChartTmpl.ID)
416 }
417 if sn.Alarm == nil {
418 _ = charts.Remove(fanSensorAlarmChartTmpl.ID)
419 }
420
421 c.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
422 }
423
424 func (c *Collector) addCurrentCharts(chip *lmsensors.Chip, sn *lmsensors.CurrentSensor) {
425 charts := currentSensorChartsTmpl.Copy()
426
427 if sn.Input == nil {
428 _ = charts.Remove(currentSensorInputChartTmpl.ID)
429 }
430 if sn.Average == nil {
431 _ = charts.Remove(currentSensorAverageChartTmpl.ID)
432 }
433 if sn.Alarm == nil {
434 _ = charts.Remove(currentSensorAlarmChartTmpl.ID)
435 }
436
437 c.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
438 }
439
440 func (c *Collector) addPowerCharts(chip *lmsensors.Chip, sn *lmsensors.PowerSensor) {
441 charts := powerSensorChartsTmpl.Copy()
442
443 if sn.Input == nil {
444 _ = charts.Remove(powerSensorInputChartTmpl.ID)
445 }
446 if sn.Average == nil {
447 _ = charts.Remove(powerSensorAverageChartTmpl.ID)
448 }
449 if sn.Alarm == nil {
450 _ = charts.Remove(powerSensorAlarmChartTmpl.ID)
451 }
452
453 c.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
454 }
455
456 func (c *Collector) addEnergyCharts(chip *lmsensors.Chip, sn *lmsensors.EnergySensor) {
457 charts := energySensorChartsTmpl.Copy()
458
459 if sn.Input == nil {
460 _ = charts.Remove(energySensorInputChartTmpl.ID)
461 }
462
463 c.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
464 }
465
466 func (c *Collector) addHumidityCharts(chip *lmsensors.Chip, sn *lmsensors.HumiditySensor) {
467 charts := humiditySensorChartsTmpl.Copy()
468
469 if sn.Input == nil {
470 _ = charts.Remove(humiditySensorInputChartTmpl.ID)
471 }
472
473 c.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
474 }
475
476 func (c *Collector) addIntrusionCharts(chip *lmsensors.Chip, sn *lmsensors.IntrusionSensor) {
477 charts := intrusionSensorChartsTmpl.Copy()
478
479 if sn.Alarm == nil {
480 _ = charts.Remove(intrusionSensorAlarmChartTmpl.ID)
481 }
482
483 c.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
484 }
485
486 func (c *Collector) addCharts(charts *collectorapi.Charts, chipUniqueName, chipSysDevice, snName, snLabel string) {
487 if len(*charts) == 0 {
488 return
489 }
490
491 if lbl := c.relabel(chipUniqueName, snName); lbl != "" {
492 snLabel = lbl
493 }
494
495 for _, chart := range *charts {
496 chart.ID = fmt.Sprintf(chart.ID, chipUniqueName, snName)
497 chart.ID = cleanChartId(chart.ID)
498 chart.Labels = []collectorapi.Label{
499 {Key: "chip", Value: chipSysDevice},
500 {Key: "chip_id", Value: chipUniqueName},
501 {Key: "sensor", Value: snName},
502 {Key: "label", Value: snLabel},
503 }
504 for _, dim := range chart.Dims {
505 dim.ID = fmt.Sprintf(dim.ID, chipUniqueName, snName)
506 }
507 }
508
509 if err := c.Charts().Add(*charts...); err != nil {
510 c.Warning(err)
511 }
512 }
513
514 func (c *Collector) removeSensorChart(px string) {
515 px = cleanChartId(px)
516
517 for _, chart := range *c.Charts() {
518 if strings.HasPrefix(chart.ID, px) {
519 chart.MarkRemove()
520 chart.MarkNotCreated()
521 return
522 }
523 }
524 }
525
526 func (c *Collector) relabel(chipUniqueName, snName string) string {
527 for _, rv := range c.Relabel {
528 if rv.Chip == "" {
529 return ""
530 }
531
532 mr, err := matcher.NewSimplePatternsMatcher(rv.Chip)
533 if err != nil {
534 c.Debugf("failed to create simple pattern matcher from '%s': %v", rv.Chip, err)
535 return ""
536 }
537
538 if !mr.MatchString(chipUniqueName) {
539 return ""
540 }
541
542 for _, sv := range rv.Sensors {
543 if sv.Name == snName {
544 return sv.Label
545 }
546 }
547 }
548 return ""
549 }
550
551 func cleanChartId(id string) string {
552 r := strings.NewReplacer(" ", "_", ".", "_")
553 return strings.ToLower(r.Replace(id))
554 }