go.d/sensors refactor (#18581)
go.d/sensors refactor wip
Ilya Mashchenko committed
Sep 21, 2024 at 20:47 UTC
9764e763720f3de0747dd553eb60970fd27c3e38
25 files changed
+2115
-2282
src/go/plugin/go.d/modules/sensors/charts.go
+479
-179
@@ -11,207 +11,502 @@ import (
11
)
12
13
const (
14
- prioSensorTemperature = module.Priority + iota
15
- prioSensorVoltage
16
- prioSensorCurrent
17
- prioSensorPower
18
- prioSensorFan
19
- prioSensorEnergy
20
- prioSensorHumidity
21
- prioSensorIntrusion
14
+ prioTemperatureSensorInput = module.Priority + iota
15
+ prioTemperatureSensorAlarm
16
+
17
+ prioVoltageSensorInput
18
+ prioVoltageSensorAverage
19
+ prioVoltageSensorAlarm
20
+
21
+ prioFanSensorInput
22
+ prioFanSensorAlarm
23
+
24
+ prioCurrentSensorInput
25
+ prioCurrentSensorAverage
26
+ prioCurrentSensorAlarm
27
+
28
+ prioPowerSensorInput
29
+ prioPowerSensorAverage
30
+ prioPowerSensorAlarm
31
+
32
+ prioEnergySensorInput
33
+
34
+ prioHumiditySensorInput
35
+
36
+ prioIntrusionSensorAlarm
37
)
38
24
-var sensorTemperatureChartTmpl = module.Chart{
25
- ID: "sensor_chip_%s_feature_%s_subfeature_%s_temperature",
26
- Title: "Sensor temperature",
27
- Units: "Celsius",
28
- Fam: "temperature",
29
- Ctx: "sensors.sensor_temperature",
30
- Type: module.Line,
31
- Priority: prioSensorTemperature,
32
- Dims: module.Dims{
33
- {ID: "sensor_chip_%s_feature_%s_subfeature_%s", Name: "temperature", Div: precision},
34
- },
35
-}
36
-
37
-var sensorVoltageChartTmpl = module.Chart{
38
- ID: "sensor_chip_%s_feature_%s_subfeature_%s_voltage",
39
- Title: "Sensor voltage",
40
- Units: "Volts",
41
- Fam: "voltage",
42
- Ctx: "sensors.sensor_voltage",
43
- Type: module.Line,
44
- Priority: prioSensorVoltage,
45
- Dims: module.Dims{
46
- {ID: "sensor_chip_%s_feature_%s_subfeature_%s", Name: "voltage", Div: precision},
47
- },
48
-}
49
-
50
-var sensorCurrentChartTmpl = module.Chart{
51
- ID: "sensor_chip_%s_feature_%s_subfeature_%s_current",
52
- Title: "Sensor current",
53
- Units: "Amperes",
54
- Fam: "current",
55
- Ctx: "sensors.sensor_current",
56
- Type: module.Line,
57
- Priority: prioSensorCurrent,
58
- Dims: module.Dims{
59
- {ID: "sensor_chip_%s_feature_%s_subfeature_%s", Name: "current", Div: precision},
60
- },
61
-}
62
-
63
-var sensorPowerChartTmpl = module.Chart{
64
- ID: "sensor_chip_%s_feature_%s_subfeature_%s_power",
65
- Title: "Sensor power",
66
- Units: "Watts",
67
- Fam: "power",
68
- Ctx: "sensors.sensor_power",
69
- Type: module.Line,
70
- Priority: prioSensorPower,
71
- Dims: module.Dims{
72
- {ID: "sensor_chip_%s_feature_%s_subfeature_%s", Name: "power", Div: precision},
73
- },
74
-}
75
-
76
-var sensorFanChartTmpl = module.Chart{
77
- ID: "sensor_chip_%s_feature_%s_subfeature_%s_fan",
78
- Title: "Sensor fan speed",
79
- Units: "RPM",
80
- Fam: "fan",
81
- Ctx: "sensors.sensor_fan_speed",
82
- Type: module.Line,
83
- Priority: prioSensorFan,
84
- Dims: module.Dims{
85
- {ID: "sensor_chip_%s_feature_%s_subfeature_%s", Name: "fan", Div: precision},
86
- },
87
-}
88
-
89
-var sensorEnergyChartTmpl = module.Chart{
90
- ID: "sensor_chip_%s_feature_%s_subfeature_%s_energy",
91
- Title: "Sensor energy",
92
- Units: "Joules",
93
- Fam: "energy",
94
- Ctx: "sensors.sensor_energy",
95
- Type: module.Line,
96
- Priority: prioSensorEnergy,
97
- Dims: module.Dims{
98
- {ID: "sensor_chip_%s_feature_%s_subfeature_%s", Name: "energy", Div: precision},
99
- },
100
-}
101
-
102
-var sensorHumidityChartTmpl = module.Chart{
103
- ID: "sensor_chip_%s_feature_%s_subfeature_%s_humidity",
104
- Title: "Sensor humidity",
105
- Units: "percent",
106
- Fam: "humidity",
107
- Ctx: "sensors.sensor_humidity",
108
- Type: module.Area,
109
- Priority: prioSensorHumidity,
110
- Dims: module.Dims{
111
- {ID: "sensor_chip_%s_feature_%s_subfeature_%s", Name: "humidity", Div: precision},
112
- },
113
-}
114
-
115
-var sensorIntrusionChartTmpl = module.Chart{
116
- ID: "sensor_chip_%s_feature_%s_subfeature_%s_intrusion",
117
- Title: "Sensor intrusion",
118
- Units: "status",
119
- Fam: "intrusion",
120
- Ctx: "sensors.sensor_intrusion",
121
- Type: module.Line,
122
- Priority: prioSensorIntrusion,
123
- Dims: module.Dims{
124
- {ID: "sensor_chip_%s_feature_%s_subfeature_%s_clear", Name: "alarm_clear"},
125
- {ID: "sensor_chip_%s_feature_%s_subfeature_%s_triggered", Name: "alarm_triggered"},
126
- },
127
-}
128
-
129
-func (s *Sensors) addExecSensorChart(sn execSensor) {
130
- var chart *module.Chart
131
-
132
- switch sn.sensorType() {
133
- case sensorTypeTemp:
134
- chart = sensorTemperatureChartTmpl.Copy()
135
- case sensorTypeVoltage:
136
- chart = sensorVoltageChartTmpl.Copy()
137
- case sensorTypePower:
138
- chart = sensorPowerChartTmpl.Copy()
139
- case sensorTypeHumidity:
140
- chart = sensorHumidityChartTmpl.Copy()
141
- case sensorTypeFan:
142
- chart = sensorFanChartTmpl.Copy()
143
- case sensorTypeCurrent:
144
- chart = sensorCurrentChartTmpl.Copy()
145
- case sensorTypeEnergy:
146
- chart = sensorEnergyChartTmpl.Copy()
147
- case sensorTypeIntrusion:
148
- chart = sensorIntrusionChartTmpl.Copy()
149
- default:
150
- return
39
+var temperatureSensorChartsTmpl = module.Charts{
40
+ temperatureSensorInputChartTmpl.Copy(),
41
+ temperatureSensorAlarmChartTmpl.Copy(),
42
+}
43
+
44
+var voltageSensorChartsTmpl = module.Charts{
45
+ voltageSensorInputChartTmpl.Copy(),
46
+ voltageSensorAverageChartTmpl.Copy(),
47
+ voltageSensorAlarmChartTmpl.Copy(),
48
+}
49
+
50
+var fanSensorChartsTmpl = module.Charts{
51
+ fanSensorInputChartTmpl.Copy(),
52
+ fanSensorAlarmChartTmpl.Copy(),
53
+}
54
+
55
+var currentSensorChartsTmpl = module.Charts{
56
+ currentSensorInputChartTmpl.Copy(),
57
+ currentSensorAverageChartTmpl.Copy(),
58
+ currentSensorAlarmChartTmpl.Copy(),
59
+}
60
+
61
+var powerSensorChartsTmpl = module.Charts{
62
+ powerSensorInputChartTmpl.Copy(),
63
+ powerSensorAverageChartTmpl.Copy(),
64
+ powerSensorAlarmChartTmpl.Copy(),
65
+}
66
+
67
+var energySensorChartsTmpl = module.Charts{
68
+ energySensorInputChartTmpl.Copy(),
69
+}
70
+
71
+var humiditySensorChartsTmpl = module.Charts{
72
+ humiditySensorInputChartTmpl.Copy(),
73
+}
74
+
75
+var intrusionSensorChartsTmpl = module.Charts{
76
+ intrusionSensorAlarmChartTmpl.Copy(),
77
+}
78
+
79
+var (
80
+ temperatureSensorInputChartTmpl = module.Chart{
81
+ ID: "%s_%s_temperature",
82
+ Title: "Sensor Temperature",
83
+ Units: "Celsius",
84
+ Fam: "temperature",
85
+ Ctx: "sensors.chip_sensor_temperature",
86
+ Type: module.Line,
87
+ Priority: prioTemperatureSensorInput,
88
+ Dims: module.Dims{
89
+ {ID: "chip_%s_sensor_%s_input", Name: "input", Div: precision},
90
+ },
91
+ }
92
+ temperatureSensorAlarmChartTmpl = module.Chart{
93
+ ID: "%s_%s_temperature_alarm",
94
+ Title: "Temperature Sensor Alarm",
95
+ Units: "status",
96
+ Fam: "temperature",
97
+ Ctx: "sensors.chip_sensor_temperature_alarm",
98
+ Type: module.Line,
99
+ Priority: prioTemperatureSensorAlarm,
100
+ Dims: module.Dims{
101
+ {ID: "chip_%s_sensor_%s_alarm_clear", Name: "clear"},
102
+ {ID: "chip_%s_sensor_%s_alarm_triggered", Name: "triggered"},
103
+ },
104
+ }
105
+)
106
+
107
+var (
108
+ voltageSensorInputChartTmpl = module.Chart{
109
+ ID: "%s_%s_voltage",
110
+ Title: "Sensor Voltage",
111
+ Units: "Volts",
112
+ Fam: "voltage",
113
+ Ctx: "sensors.chip_sensor_voltage",
114
+ Type: module.Line,
115
+ Priority: prioVoltageSensorInput,
116
+ Dims: module.Dims{
117
+ {ID: "chip_%s_sensor_%s_input", Name: "input", Div: precision},
118
+ },
119
+ }
120
+ voltageSensorAverageChartTmpl = module.Chart{
121
+ ID: "%s_%s_voltage_average",
122
+ Title: "Sensor Voltage Average",
123
+ Units: "Volts",
124
+ Fam: "voltage",
125
+ Ctx: "sensors.chip_sensor_voltage_average",
126
+ Type: module.Line,
127
+ Priority: prioVoltageSensorAverage,
128
+ Dims: module.Dims{
129
+ {ID: "chip_%s_sensor_%s_average", Name: "average", Div: precision},
130
+ },
131
}
132
+ voltageSensorAlarmChartTmpl = module.Chart{
133
+ ID: "%s_%s_voltage_alarm",
134
+ Title: "Voltage Sensor Alarm",
135
+ Units: "status",
136
+ Fam: "voltage",
137
+ Ctx: "sensors.chip_sensor_voltage_alarm",
138
+ Type: module.Line,
139
+ Priority: prioVoltageSensorAlarm,
140
+ Dims: module.Dims{
141
+ {ID: "chip_%s_sensor_%s_alarm_clear", Name: "clear"},
142
+ {ID: "chip_%s_sensor_%s_alarm_triggered", Name: "triggered"},
143
+ },
144
+ }
145
+)
146
153
- chip, feat, subfeat := snakeCase(sn.chip), snakeCase(sn.feature), snakeCase(sn.subfeature)
147
+var (
148
+ fanSensorInputChartTmpl = module.Chart{
149
+ ID: "%s_%s_fan",
150
+ Title: "Sensor Fan",
151
+ Units: "RPM",
152
+ Fam: "fan",
153
+ Ctx: "sensors.chip_sensor_fan",
154
+ Type: module.Line,
155
+ Priority: prioFanSensorInput,
156
+ Dims: module.Dims{
157
+ {ID: "chip_%s_sensor_%s_input", Name: "input", Div: precision},
158
+ },
159
+ }
160
+ fanSensorAlarmChartTmpl = module.Chart{
161
+ ID: "%s_%s_fan_alarm",
162
+ Title: "Fan Sensor Alarm",
163
+ Units: "status",
164
+ Fam: "fan",
165
+ Ctx: "sensors.chip_sensor_fan_alarm",
166
+ Type: module.Line,
167
+ Priority: prioFanSensorAlarm,
168
+ Dims: module.Dims{
169
+ {ID: "chip_%s_sensor_%s_alarm_clear", Name: "clear"},
170
+ {ID: "chip_%s_sensor_%s_alarm_triggered", Name: "triggered"},
171
+ },
172
+ }
173
+)
174
155
- chart.ID = fmt.Sprintf(chart.ID, chip, feat, subfeat)
156
- chart.Labels = []module.Label{
157
- {Key: "chip", Value: sn.chip},
158
- {Key: "feature", Value: sn.feature},
175
+var (
176
+ currentSensorInputChartTmpl = module.Chart{
177
+ ID: "%s_%s_current",
178
+ Title: "Sensor Current",
179
+ Units: "Amperes",
180
+ Fam: "current",
181
+ Ctx: "sensors.chip_sensor_current",
182
+ Type: module.Line,
183
+ Priority: prioCurrentSensorInput,
184
+ Dims: module.Dims{
185
+ {ID: "chip_%s_sensor_%s_input", Name: "input", Div: precision},
186
+ },
187
+ }
188
+ currentSensorAverageChartTmpl = module.Chart{
189
+ ID: "%s_%s_current_average",
190
+ Title: "Sensor Current Average",
191
+ Units: "Amperes",
192
+ Fam: "current",
193
+ Ctx: "sensors.chip_sensor_current_average",
194
+ Type: module.Line,
195
+ Priority: prioCurrentSensorAverage,
196
+ Dims: module.Dims{
197
+ {ID: "chip_%s_sensor_%s_average", Name: "average", Div: precision},
198
+ },
199
}
160
- for _, dim := range chart.Dims {
161
- dim.ID = fmt.Sprintf(dim.ID, chip, feat, subfeat)
200
+ currentSensorAlarmChartTmpl = module.Chart{
201
+ ID: "%s_%s_current_alarm",
202
+ Title: "Sensor Alarm",
203
+ Units: "status",
204
+ Fam: "current",
205
+ Ctx: "sensors.chip_sensor_current_alarm",
206
+ Type: module.Line,
207
+ Priority: prioCurrentSensorAlarm,
208
+ Dims: module.Dims{
209
+ {ID: "chip_%s_sensor_%s_alarm_clear", Name: "clear"},
210
+ {ID: "chip_%s_sensor_%s_alarm_triggered", Name: "triggered"},
211
+ },
212
}
213
+)
214
164
- if err := s.Charts().Add(chart); err != nil {
165
- s.Warning(err)
215
+var (
216
+ powerSensorInputChartTmpl = module.Chart{
217
+ ID: "%s_%s_power",
218
+ Title: "Sensor Power",
219
+ Units: "Watts",
220
+ Fam: "power",
221
+ Ctx: "sensors.chip_sensor_power",
222
+ Type: module.Line,
223
+ Priority: prioPowerSensorInput,
224
+ Dims: module.Dims{
225
+ {ID: "chip_%s_sensor_%s_input", Name: "input", Div: precision},
226
+ },
227
+ }
228
+ powerSensorAverageChartTmpl = module.Chart{
229
+ ID: "%s_%s_power_average",
230
+ Title: "Sensor Power Average",
231
+ Units: "Watts",
232
+ Fam: "power",
233
+ Ctx: "sensors.chip_sensor_power_average",
234
+ Type: module.Line,
235
+ Priority: prioPowerSensorAverage,
236
+ Dims: module.Dims{
237
+ {ID: "chip_%s_sensor_%s_average", Name: "average", Div: precision},
238
+ },
239
+ }
240
+ powerSensorAlarmChartTmpl = module.Chart{
241
+ ID: "%s_%s_power_alarm",
242
+ Title: "Power Sensor Alarm",
243
+ Units: "status",
244
+ Fam: "current",
245
+ Ctx: "sensors.chip_sensor_power_alarm",
246
+ Type: module.Line,
247
+ Priority: prioPowerSensorAlarm,
248
+ Dims: module.Dims{
249
+ {ID: "chip_%s_sensor_%s_alarm_clear", Name: "clear"},
250
+ {ID: "chip_%s_sensor_%s_alarm_triggered", Name: "triggered"},
251
+ },
252
+ }
253
+)
254
+
255
+var (
256
+ energySensorInputChartTmpl = module.Chart{
257
+ ID: "%s_%s_energy",
258
+ Title: "Sensor Energy",
259
+ Units: "Joules",
260
+ Fam: "energy",
261
+ Ctx: "sensors.chip_sensor_energy",
262
+ Type: module.Line,
263
+ Priority: prioEnergySensorInput,
264
+ Dims: module.Dims{
265
+ {ID: "chip_%s_sensor_%s_input", Name: "input", Div: precision},
266
+ },
267
+ }
268
+)
269
+
270
+var (
271
+ humiditySensorInputChartTmpl = module.Chart{
272
+ ID: "%s_%s_humidity",
273
+ Title: "Sensor Humidity",
274
+ Units: "percent",
275
+ Fam: "humidity",
276
+ Ctx: "sensors.chip_sensor_humidity",
277
+ Type: module.Line,
278
+ Priority: prioHumiditySensorInput,
279
+ Dims: module.Dims{
280
+ {ID: "chip_%s_sensor_%s_input", Name: "input", Div: precision},
281
+ },
282
+ }
283
+)
284
+
285
+var (
286
+ intrusionSensorAlarmChartTmpl = module.Chart{
287
+ ID: "%s_%s_intrusion_alarm",
288
+ Title: "Sensor Intrusion Alarm",
289
+ Units: "status",
290
+ Fam: "intrusion",
291
+ Ctx: "sensors.chip_sensor_intrusion_alarm",
292
+ Type: module.Line,
293
+ Priority: prioIntrusionSensorAlarm,
294
+ Dims: module.Dims{
295
+ {ID: "chip_%s_sensor_%s_alarm_clear", Name: "clear"},
296
+ {ID: "chip_%s_sensor_%s_alarm_triggered", Name: "triggered"},
297
+ },
298
+ }
299
+)
300
+
301
+func (s *Sensors) updateCharts(chips []*lmsensors.Chip) {
302
+ seen := make(map[string]bool)
303
+
304
+ for _, chip := range chips {
305
+ for _, sn := range chip.Sensors.Voltage {
306
+ key := chip.UniqueName + "_" + sn.Name
307
+ seen[key] = true
308
+ if !s.seenSensors[key] {
309
+ s.seenSensors[key] = true
310
+ s.addVoltageCharts(chip, sn)
311
+ }
312
+ }
313
+ for _, sn := range chip.Sensors.Fan {
314
+ key := chip.UniqueName + "_" + sn.Name
315
+ seen[key] = true
316
+ if !s.seenSensors[key] {
317
+ s.seenSensors[key] = true
318
+ s.addFanCharts(chip, sn)
319
+ }
320
+ }
321
+ for _, sn := range chip.Sensors.Temperature {
322
+ key := chip.UniqueName + "_" + sn.Name
323
+ seen[key] = true
324
+ if !s.seenSensors[key] {
325
+ s.seenSensors[key] = true
326
+ s.addTemperatureCharts(chip, sn)
327
+ }
328
+ }
329
+ for _, sn := range chip.Sensors.Current {
330
+ key := chip.UniqueName + "_" + sn.Name
331
+ seen[key] = true
332
+ if !s.seenSensors[key] {
333
+ s.seenSensors[key] = true
334
+ s.addCurrentCharts(chip, sn)
335
+ }
336
+ }
337
+ for _, sn := range chip.Sensors.Power {
338
+ key := chip.UniqueName + "_" + sn.Name
339
+ seen[key] = true
340
+ if !s.seenSensors[key] {
341
+ s.seenSensors[key] = true
342
+ s.addPowerCharts(chip, sn)
343
+ }
344
+ }
345
+ for _, sn := range chip.Sensors.Energy {
346
+ key := chip.UniqueName + "_" + sn.Name
347
+ seen[key] = true
348
+ if !s.seenSensors[key] {
349
+ s.seenSensors[key] = true
350
+ s.addEnergyCharts(chip, sn)
351
+ }
352
+ }
353
+ for _, sn := range chip.Sensors.Humidity {
354
+ key := chip.UniqueName + "_" + sn.Name
355
+ seen[key] = true
356
+ if !s.seenSensors[key] {
357
+ s.seenSensors[key] = true
358
+ s.addHumidityCharts(chip, sn)
359
+ }
360
+ }
361
+ for _, sn := range chip.Sensors.Intrusion {
362
+ key := chip.UniqueName + "_" + sn.Name
363
+ seen[key] = true
364
+ if !s.seenSensors[key] {
365
+ s.seenSensors[key] = true
366
+ s.addIntrusionCharts(chip, sn)
367
+ }
368
+ }
369
+ }
370
+
371
+ for key := range s.seenSensors {
372
+ if !seen[key] {
373
+ delete(s.seenSensors, key)
374
+ s.removeSensorChart(key)
375
+ }
376
}
377
}
378
169
-func (s *Sensors) addSysfsSensorChart(devName string, sn lmsensors.Sensor) {
170
- var chart *module.Chart
171
- var feat, subfeat string
172
- devName = snakeCase(devName)
173
-
174
- switch v := sn.(type) {
175
- case *lmsensors.TemperatureSensor:
176
- chart = sensorTemperatureChartTmpl.Copy()
177
- feat, subfeat = firstNotEmpty(v.Label, v.Name), v.Name+"_input"
178
- case *lmsensors.VoltageSensor:
179
- chart = sensorVoltageChartTmpl.Copy()
180
- feat, subfeat = firstNotEmpty(v.Label, v.Name), v.Name+"_input"
181
- case *lmsensors.CurrentSensor:
182
- chart = sensorCurrentChartTmpl.Copy()
183
- feat, subfeat = firstNotEmpty(v.Label, v.Name), v.Name+"_input"
184
- case *lmsensors.PowerSensor:
185
- chart = sensorPowerChartTmpl.Copy()
186
- feat, subfeat = firstNotEmpty(v.Label, v.Name), v.Name+"_average"
187
- case *lmsensors.FanSensor:
188
- chart = sensorFanChartTmpl.Copy()
189
- feat, subfeat = firstNotEmpty(v.Label, v.Name), v.Name+"_input"
190
- case *lmsensors.IntrusionSensor:
191
- chart = sensorIntrusionChartTmpl.Copy()
192
- feat, subfeat = firstNotEmpty(v.Label, v.Name), v.Name+"_alarm"
193
- default:
194
- return
379
+func (s *Sensors) addTemperatureCharts(chip *lmsensors.Chip, sn *lmsensors.TemperatureSensor) {
380
+ charts := temperatureSensorChartsTmpl.Copy()
381
+
382
+ if sn.Input == nil {
383
+ _ = charts.Remove(temperatureSensorInputChartTmpl.ID)
384
+ }
385
+ if sn.Alarm == nil {
386
+ _ = charts.Remove(temperatureSensorAlarmChartTmpl.ID)
387
+ }
388
+
389
+ s.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
390
+}
391
+
392
+func (s *Sensors) addVoltageCharts(chip *lmsensors.Chip, sn *lmsensors.VoltageSensor) {
393
+ charts := voltageSensorChartsTmpl.Copy()
394
+
395
+ if sn.Input == nil {
396
+ _ = charts.Remove(voltageSensorInputChartTmpl.ID)
397
+ }
398
+ if sn.Average == nil {
399
+ _ = charts.Remove(voltageSensorAverageChartTmpl.ID)
400
+ }
401
+ if sn.Alarm == nil {
402
+ _ = charts.Remove(voltageSensorAlarmChartTmpl.ID)
403
+ }
404
+
405
+ s.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
406
+}
407
+
408
+func (s *Sensors) addFanCharts(chip *lmsensors.Chip, sn *lmsensors.FanSensor) {
409
+ charts := fanSensorChartsTmpl.Copy()
410
+
411
+ if sn.Input == nil {
412
+ _ = charts.Remove(fanSensorInputChartTmpl.ID)
413
+ }
414
+ if sn.Alarm == nil {
415
+ _ = charts.Remove(fanSensorAlarmChartTmpl.ID)
416
+ }
417
+
418
+ s.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
419
+}
420
+
421
+func (s *Sensors) addCurrentCharts(chip *lmsensors.Chip, sn *lmsensors.CurrentSensor) {
422
+ charts := currentSensorChartsTmpl.Copy()
423
+
424
+ if sn.Input == nil {
425
+ _ = charts.Remove(currentSensorInputChartTmpl.ID)
426
+ }
427
+ if sn.Average == nil {
428
+ _ = charts.Remove(currentSensorAverageChartTmpl.ID)
429
+ }
430
+ if sn.Alarm == nil {
431
+ _ = charts.Remove(currentSensorAlarmChartTmpl.ID)
432
+ }
433
+
434
+ s.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
435
+}
436
+
437
+func (s *Sensors) addPowerCharts(chip *lmsensors.Chip, sn *lmsensors.PowerSensor) {
438
+ charts := powerSensorChartsTmpl.Copy()
439
+
440
+ if sn.Input == nil {
441
+ _ = charts.Remove(powerSensorInputChartTmpl.ID)
442
}
443
+ if sn.Average == nil {
444
+ _ = charts.Remove(powerSensorAverageChartTmpl.ID)
445
+ }
446
+ if sn.Alarm == nil {
447
+ _ = charts.Remove(powerSensorAlarmChartTmpl.ID)
448
+ }
449
+
450
+ s.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
451
+}
452
197
- origFeat := feat
198
- feat, subfeat = snakeCase(feat), snakeCase(subfeat)
453
+func (s *Sensors) addEnergyCharts(chip *lmsensors.Chip, sn *lmsensors.EnergySensor) {
454
+ charts := energySensorChartsTmpl.Copy()
455
200
- chart.ID = fmt.Sprintf(chart.ID, devName, feat, subfeat)
201
- chart.Labels = []module.Label{
202
- {Key: "chip", Value: devName},
203
- {Key: "feature", Value: origFeat},
456
+ if sn.Input == nil {
457
+ _ = charts.Remove(energySensorInputChartTmpl.ID)
458
}
205
- for _, dim := range chart.Dims {
206
- dim.ID = fmt.Sprintf(dim.ID, devName, feat, subfeat)
459
+
460
+ s.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
461
+}
462
+
463
+func (s *Sensors) addHumidityCharts(chip *lmsensors.Chip, sn *lmsensors.HumiditySensor) {
464
+ charts := humiditySensorChartsTmpl.Copy()
465
+
466
+ if sn.Input == nil {
467
+ _ = charts.Remove(humiditySensorInputChartTmpl.ID)
468
}
469
209
- if err := s.Charts().Add(chart); err != nil {
470
+ s.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
471
+}
472
+
473
+func (s *Sensors) addIntrusionCharts(chip *lmsensors.Chip, sn *lmsensors.IntrusionSensor) {
474
+ charts := intrusionSensorChartsTmpl.Copy()
475
+
476
+ if sn.Alarm == nil {
477
+ _ = charts.Remove(intrusionSensorAlarmChartTmpl.ID)
478
+ }
479
+
480
+ s.addCharts(charts, chip.UniqueName, chip.SysDevice, sn.Name, sn.Label)
481
+}
482
+
483
+func (s *Sensors) addCharts(charts *module.Charts, chipUniqueName, chipSysDevice, snName, snLabel string) {
484
+ if len(*charts) == 0 {
485
+ return
486
+ }
487
+
488
+ for _, chart := range *charts {
489
+ chart.ID = fmt.Sprintf(chart.ID, chipUniqueName, snName)
490
+ chart.ID = cleanChartId(chart.ID)
491
+ chart.Labels = []module.Label{
492
+ {Key: "chip", Value: chipSysDevice},
493
+ {Key: "chip_id", Value: chipUniqueName},
494
+ {Key: "sensor", Value: snName},
495
+ {Key: "label", Value: snLabel},
496
+ }
497
+ for _, dim := range chart.Dims {
498
+ dim.ID = fmt.Sprintf(dim.ID, chipUniqueName, snName)
499
+ }
500
+ }
501
+
502
+ if err := s.Charts().Add(*charts...); err != nil {
503
s.Warning(err)
504
}
505
}
506
507
func (s *Sensors) removeSensorChart(px string) {
508
+ px = cleanChartId(px)
509
+
510
for _, chart := range *s.Charts() {
511
if strings.HasPrefix(chart.ID, px) {
512
chart.MarkRemove()
@@ -220,3 +515,8 @@ func (s *Sensors) removeSensorChart(px string) {
515
}
516
}
517
}
518
+
519
+func cleanChartId(id string) string {
520
+ r := strings.NewReplacer(" ", "_", ".", "_")
521
+ return strings.ToLower(r.Replace(id))
522
+}
src/go/plugin/go.d/modules/sensors/collect.go
+175
-3
@@ -2,11 +2,183 @@
2
3
package sensors
4
5
+import (
6
+ "errors"
7
+ "fmt"
8
+
9
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/sensors/lmsensors"
10
+)
11
+
12
const precision = 1000
13
14
+func ptr[T any](v T) *T { return &v }
15
+
16
func (s *Sensors) collect() (map[string]int64, error) {
8
- if s.exec != nil {
9
- return s.collectExec()
17
+ if s.sc == nil {
18
+ return nil, errors.New("sysfs scanner is not initialized")
19
+ }
20
+
21
+ chips, err := s.sc.Scan()
22
+ if err != nil {
23
+ return nil, err
24
+ }
25
+
26
+ if len(chips) == 0 {
27
+ return nil, errors.New("no chips found on the system")
28
+ }
29
+
30
+ mx := make(map[string]int64)
31
+
32
+ for _, chip := range chips {
33
+ for _, sn := range chip.Sensors.Voltage {
34
+ writeVoltage(mx, chip, sn)
35
+ }
36
+ for _, sn := range chip.Sensors.Fan {
37
+ writeFan(mx, chip, sn)
38
+ }
39
+ for _, sn := range chip.Sensors.Temperature {
40
+ writeTemperature(mx, chip, sn)
41
+ }
42
+ for _, sn := range chip.Sensors.Current {
43
+ writeCurrent(mx, chip, sn)
44
+ }
45
+ for _, sn := range chip.Sensors.Power {
46
+ writePower(mx, chip, sn)
47
+ }
48
+ for _, sn := range chip.Sensors.Energy {
49
+ writeEnergy(mx, chip, sn)
50
+ }
51
+ for _, sn := range chip.Sensors.Humidity {
52
+ writeHumidity(mx, chip, sn)
53
+ }
54
+ for _, sn := range chip.Sensors.Intrusion {
55
+ writeIntrusion(mx, chip, sn)
56
+ }
57
+ }
58
+
59
+ s.updateCharts(chips)
60
+
61
+ return mx, nil
62
+}
63
+
64
+func writeVoltage(mx map[string]int64, chip *lmsensors.Chip, sn *lmsensors.VoltageSensor) {
65
+ px := sensorPrefix(chip.UniqueName, sn.Name)
66
+
67
+ mx[px+"read_time"] = sn.ReadTime.Milliseconds()
68
+ writeMetricAlarm(mx, px, sn.Alarm)
69
+ writeMetric(mx, px+"input", sn.Input)
70
+ writeMetric(mx, px+"average", sn.Average)
71
+ writeMetric(mx, px+"min", sn.Min)
72
+ writeMetric(mx, px+"max", sn.Max)
73
+ writeMetric(mx, px+"lcrit", sn.CritMin)
74
+ writeMetric(mx, px+"crit", sn.CritMax)
75
+ writeMetric(mx, px+"lowest", sn.Lowest)
76
+ writeMetric(mx, px+"highest", sn.Highest)
77
+}
78
+
79
+func writeFan(mx map[string]int64, chip *lmsensors.Chip, sn *lmsensors.FanSensor) {
80
+ px := sensorPrefix(chip.UniqueName, sn.Name)
81
+
82
+ mx[px+"read_time"] = sn.ReadTime.Milliseconds()
83
+ writeMetricAlarm(mx, px, sn.Alarm)
84
+ writeMetric(mx, px+"input", sn.Input)
85
+ writeMetric(mx, px+"min", sn.Min)
86
+ writeMetric(mx, px+"max", sn.Max)
87
+ writeMetric(mx, px+"target", sn.Target)
88
+}
89
+
90
+func writeTemperature(mx map[string]int64, chip *lmsensors.Chip, sn *lmsensors.TemperatureSensor) {
91
+ px := sensorPrefix(chip.UniqueName, sn.Name)
92
+
93
+ mx[px+"read_time"] = sn.ReadTime.Milliseconds()
94
+ writeMetricAlarm(mx, px, sn.Alarm)
95
+ writeMetric(mx, px+"input", sn.Input)
96
+ writeMetric(mx, px+"min", sn.Min)
97
+ writeMetric(mx, px+"max", sn.Max)
98
+ writeMetric(mx, px+"lcrit", sn.CritMin)
99
+ writeMetric(mx, px+"crit", sn.CritMax)
100
+ writeMetric(mx, px+"emergency", sn.Emergency)
101
+ writeMetric(mx, px+"lowest", sn.Lowest)
102
+ writeMetric(mx, px+"highest", sn.Highest)
103
+}
104
+
105
+func writeCurrent(mx map[string]int64, chip *lmsensors.Chip, sn *lmsensors.CurrentSensor) {
106
+ px := sensorPrefix(chip.UniqueName, sn.Name)
107
+
108
+ mx[px+"read_time"] = sn.ReadTime.Milliseconds()
109
+ writeMetricAlarm(mx, px, sn.Alarm)
110
+ writeMetric(mx, px+"max", sn.Max)
111
+ writeMetric(mx, px+"min", sn.Min)
112
+ writeMetric(mx, px+"lcrit", sn.CritMin)
113
+ writeMetric(mx, px+"crit", sn.CritMax)
114
+ writeMetric(mx, px+"input", sn.Input)
115
+ writeMetric(mx, px+"average", sn.Average)
116
+ writeMetric(mx, px+"lowest", sn.Lowest)
117
+ writeMetric(mx, px+"highest", sn.Highest)
118
+}
119
+
120
+func writePower(mx map[string]int64, chip *lmsensors.Chip, sn *lmsensors.PowerSensor) {
121
+ px := sensorPrefix(chip.UniqueName, sn.Name)
122
+
123
+ mx[px+"read_time"] = sn.ReadTime.Milliseconds()
124
+ writeMetricAlarm(mx, px, sn.Alarm)
125
+ writeMetric(mx, px+"average", sn.Average)
126
+ writeMetric(mx, px+"average_highest", sn.AverageHighest)
127
+ writeMetric(mx, px+"average_lowest", sn.AverageLowest)
128
+ writeMetric(mx, px+"average_max", sn.AverageMax)
129
+ writeMetric(mx, px+"average_min", sn.AverageMin)
130
+ writeMetric(mx, px+"input", sn.Input)
131
+ writeMetric(mx, px+"input_highest", sn.InputHighest)
132
+ writeMetric(mx, px+"input_lowest", sn.InputLowest)
133
+ writeMetric(mx, px+"accuracy", sn.Accuracy)
134
+ writeMetric(mx, px+"cap", sn.Cap)
135
+ writeMetric(mx, px+"cap_max", sn.CapMax)
136
+ writeMetric(mx, px+"cap_min", sn.CapMin)
137
+ writeMetric(mx, px+"max", sn.Max)
138
+ writeMetric(mx, px+"crit", sn.CritMax)
139
+}
140
+
141
+func writeEnergy(mx map[string]int64, chip *lmsensors.Chip, sn *lmsensors.EnergySensor) {
142
+ px := sensorPrefix(chip.UniqueName, sn.Name)
143
+
144
+ mx[px+"read_time"] = sn.ReadTime.Milliseconds()
145
+ writeMetric(mx, px+"input", sn.Input)
146
+}
147
+
148
+func writeHumidity(mx map[string]int64, chip *lmsensors.Chip, sn *lmsensors.HumiditySensor) {
149
+ px := sensorPrefix(chip.UniqueName, sn.Name)
150
+
151
+ mx[px+"read_time"] = sn.ReadTime.Milliseconds()
152
+ writeMetric(mx, px+"input", sn.Input)
153
+}
154
+
155
+func writeIntrusion(mx map[string]int64, chip *lmsensors.Chip, sn *lmsensors.IntrusionSensor) {
156
+ px := sensorPrefix(chip.UniqueName, sn.Name)
157
+
158
+ mx[px+"read_time"] = sn.ReadTime.Milliseconds()
159
+ writeMetricAlarm(mx, px, sn.Alarm)
160
+}
161
+
162
+func writeMetric(mx map[string]int64, key string, value *float64) {
163
+ if value != nil {
164
+ mx[key] = int64(*value * precision)
165
+ }
166
+}
167
+
168
+func writeMetricAlarm(mx map[string]int64, px string, value *bool) {
169
+ if value != nil {
170
+ mx[px+"alarm_clear"] = boolToInt(!*value)
171
+ mx[px+"alarm_triggered"] = boolToInt(*value)
172
+ }
173
+}
174
+
175
+func sensorPrefix(chip, sensor string) string {
176
+ return fmt.Sprintf("chip_%s_sensor_%s_", chip, sensor)
177
+}
178
+
179
+func boolToInt(b bool) int64 {
180
+ if b {
181
+ return 1
182
}
11
- return s.collectSysfs()
183
+ return 0
184
}
src/go/plugin/go.d/modules/sensors/collect_exec.go
deleted
-200
@@ -1,200 +0,0 @@
1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-package sensors
4
-
5
-import (
6
- "bufio"
7
- "bytes"
8
- "errors"
9
- "fmt"
10
- "strconv"
11
- "strings"
12
-)
13
-
14
-const (
15
- sensorTypeTemp = "temperature"
16
- sensorTypeVoltage = "voltage"
17
- sensorTypePower = "power"
18
- sensorTypeHumidity = "humidity"
19
- sensorTypeFan = "fan"
20
- sensorTypeCurrent = "current"
21
- sensorTypeEnergy = "energy"
22
- sensorTypeIntrusion = "intrusion"
23
-)
24
-
25
-type execSensor struct {
26
- chip string
27
- feature string
28
- subfeature string
29
- value string
30
-}
31
-
32
-func (s *execSensor) String() string {
33
- return fmt.Sprintf("chip:%s feat:%s subfeat:%s value:%s", s.chip, s.feature, s.subfeature, s.value)
34
-}
35
-
36
-func (s *execSensor) sensorType() string {
37
- switch {
38
- case strings.HasPrefix(s.subfeature, "temp"):
39
- return sensorTypeTemp
40
- case strings.HasPrefix(s.subfeature, "intrusion"):
41
- return sensorTypeIntrusion
42
- case strings.HasPrefix(s.subfeature, "in"):
43
- return sensorTypeVoltage
44
- case strings.HasPrefix(s.subfeature, "power"):
45
- return sensorTypePower
46
- case strings.HasPrefix(s.subfeature, "humidity"):
47
- return sensorTypeHumidity
48
- case strings.HasPrefix(s.subfeature, "fan"):
49
- return sensorTypeFan
50
- case strings.HasPrefix(s.subfeature, "curr"):
51
- return sensorTypeCurrent
52
- case strings.HasPrefix(s.subfeature, "energy"):
53
- return sensorTypeEnergy
54
- default:
55
- return ""
56
- }
57
-}
58
-
59
-func (s *execSensor) limits() (minVal float64, maxVal float64, ok bool) {
60
- switch s.sensorType() {
61
- case sensorTypeTemp:
62
- return -127, 1000, true
63
- case sensorTypeVoltage:
64
- return -400, 400, true
65
- case sensorTypeCurrent:
66
- return -127, 127, true
67
- case sensorTypeFan:
68
- return 0, 65535, true
69
- default:
70
- return 0, 0, false
71
- }
72
-}
73
-
74
-func (s *Sensors) collectExec() (map[string]int64, error) {
75
- if s.exec == nil {
76
- return nil, errors.New("exec sensor is not initialized")
77
- }
78
-
79
- s.Debugf("using sensors binary to collect metrics")
80
-
81
- bs, err := s.exec.sensorsInfo()
82
- if err != nil {
83
- return nil, err
84
- }
85
-
86
- if len(bs) == 0 {
87
- return nil, errors.New("empty response from sensors")
88
- }
89
-
90
- sensors, err := parseExecSensors(bs)
91
- if err != nil {
92
- return nil, err
93
- }
94
- if len(sensors) == 0 {
95
- return nil, errors.New("no sensors found")
96
- }
97
-
98
- mx := make(map[string]int64)
99
- seen := make(map[string]bool)
100
-
101
- for _, sn := range sensors {
102
- var sx string
103
-
104
- switch sn.sensorType() {
105
- case "":
106
- s.Debugf("can not find type for sensor '%s'", sn)
107
- continue
108
- case sensorTypePower:
109
- sx = "_average"
110
- case sensorTypeIntrusion:
111
- sx = "_alarm"
112
- default:
113
- sx = "_input"
114
- }
115
-
116
- if !strings.HasSuffix(sn.subfeature, sx) {
117
- s.Debugf("skipping sensor: '%s'", sn)
118
- continue
119
- }
120
-
121
- v, err := strconv.ParseFloat(sn.value, 64)
122
- if err != nil {
123
- s.Debugf("parsing value for sensor '%s': %v", sn, err)
124
- continue
125
- }
126
-
127
- if minVal, maxVal, ok := sn.limits(); ok && (v < minVal || v > maxVal) {
128
- s.Debugf("value outside limits [%d/%d] for sensor '%s'", int64(minVal), int64(maxVal), sn)
129
- continue
130
- }
131
-
132
- key := fmt.Sprintf("sensor_chip_%s_feature_%s_subfeature_%s", sn.chip, sn.feature, sn.subfeature)
133
- key = snakeCase(key)
134
-
135
- if !s.sensors[key] {
136
- s.sensors[key] = true
137
- s.addExecSensorChart(sn)
138
- }
139
-
140
- seen[key] = true
141
-
142
- if sn.sensorType() == sensorTypeIntrusion {
143
- mx[key+"_triggered"] = boolToInt(v != 0)
144
- mx[key+"_clear"] = boolToInt(v == 0)
145
- } else {
146
- mx[key] = int64(v * precision)
147
- }
148
- }
149
-
150
- for k := range s.sensors {
151
- if !seen[k] {
152
- delete(s.sensors, k)
153
- s.removeSensorChart(k)
154
- }
155
- }
156
-
157
- return mx, nil
158
-}
159
-
160
-func snakeCase(n string) string {
161
- return strings.ToLower(strings.ReplaceAll(n, " ", "_"))
162
-}
163
-
164
-func parseExecSensors(output []byte) ([]execSensor, error) {
165
- var sensors []execSensor
166
-
167
- sc := bufio.NewScanner(bytes.NewReader(output))
168
-
169
- var chip, feat string
170
-
171
- for sc.Scan() {
172
- text := sc.Text()
173
- if text == "" {
174
- chip, feat = "", ""
175
- continue
176
- }
177
-
178
- switch {
179
- case strings.HasPrefix(text, " ") && chip != "" && feat != "":
180
- parts := strings.Split(text, ":")
181
- if len(parts) != 2 {
182
- continue
183
- }
184
- subfeat, value := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
185
- sensors = append(sensors, execSensor{
186
- chip: chip,
187
- feature: feat,
188
- subfeature: subfeat,
189
- value: value,
190
- })
191
- case strings.HasSuffix(text, ":") && chip != "":
192
- feat = strings.TrimSpace(strings.TrimSuffix(text, ":"))
193
- default:
194
- chip = text
195
- feat = ""
196
- }
197
- }
198
-
199
- return sensors, nil
200
-}
src/go/plugin/go.d/modules/sensors/collect_sysfs.go
deleted
-97
@@ -1,97 +0,0 @@
1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-package sensors
4
-
5
-import (
6
- "errors"
7
- "fmt"
8
-
9
- "github.com/netdata/netdata/go/plugins/plugin/go.d/modules/sensors/lmsensors"
10
-)
11
-
12
-func (s *Sensors) collectSysfs() (map[string]int64, error) {
13
- if s.sc == nil {
14
- return nil, errors.New("sysfs scanner is not initialized")
15
- }
16
-
17
- s.Debugf("using sysfs scan to collect metrics")
18
-
19
- devices, err := s.sc.Scan()
20
- if err != nil {
21
- return nil, err
22
- }
23
-
24
- if len(devices) == 0 {
25
- return nil, errors.New("sysfs scanner: devices found")
26
- }
27
-
28
- seen := make(map[string]bool)
29
- mx := make(map[string]int64)
30
-
31
- for _, dev := range devices {
32
- for _, sn := range dev.Sensors {
33
- var key string
34
-
35
- switch v := sn.(type) {
36
- case *lmsensors.TemperatureSensor:
37
- key = snakeCase(fmt.Sprintf("sensor_chip_%s_feature_%s_subfeature_%s_input", dev.Name, firstNotEmpty(v.Label, v.Name), v.Name))
38
- mx[key] = int64(v.Input * precision)
39
- case *lmsensors.VoltageSensor:
40
- key = snakeCase(fmt.Sprintf("sensor_chip_%s_feature_%s_subfeature_%s_input", dev.Name, firstNotEmpty(v.Label, v.Name), v.Name))
41
- mx[key] = int64(v.Input * precision)
42
- case *lmsensors.CurrentSensor:
43
- key = snakeCase(fmt.Sprintf("sensor_chip_%s_feature_%s_subfeature_%s_input", dev.Name, firstNotEmpty(v.Label, v.Name), v.Name))
44
- mx[key] = int64(v.Input * precision)
45
- case *lmsensors.PowerSensor:
46
- key = snakeCase(fmt.Sprintf("sensor_chip_%s_feature_%s_subfeature_%s_average", dev.Name, firstNotEmpty(v.Label, v.Name), v.Name))
47
- mx[key] = int64(v.Average * precision)
48
- case *lmsensors.FanSensor:
49
- key = snakeCase(fmt.Sprintf("sensor_chip_%s_feature_%s_subfeature_%s_input", dev.Name, firstNotEmpty(v.Label, v.Name), v.Name))
50
- mx[key] = int64(v.Input * precision)
51
- case *lmsensors.IntrusionSensor:
52
- key = snakeCase(fmt.Sprintf("sensor_chip_%s_feature_%s_subfeature_%s_alarm", dev.Name, firstNotEmpty(v.Label, v.Name), v.Name))
53
- mx[key+"_triggered"] = boolToInt(v.Alarm)
54
- mx[key+"_clear"] = boolToInt(!v.Alarm)
55
- default:
56
- s.Debugf("unexpected sensor type: %T", v)
57
- continue
58
- }
59
-
60
- seen[key] = true
61
-
62
- if !s.sensors[key] {
63
- s.sensors[key] = true
64
- s.addSysfsSensorChart(dev.Name, sn)
65
- }
66
- }
67
- }
68
-
69
- if len(mx) == 0 {
70
- return nil, errors.New("sysfs scanner: no metrics collected")
71
- }
72
-
73
- for k := range s.sensors {
74
- if !seen[k] {
75
- delete(s.sensors, k)
76
- s.removeSensorChart(k)
77
- }
78
- }
79
-
80
- return mx, nil
81
-}
82
-
83
-func firstNotEmpty(s ...string) string {
84
- for _, v := range s {
85
- if v != "" {
86
- return v
87
- }
88
- }
89
- return ""
90
-}
91
-
92
-func boolToInt(b bool) int64 {
93
- if b {
94
- return 1
95
- }
96
- return 0
97
-}
src/go/plugin/go.d/modules/sensors/config_schema.json
-19
@@ -10,19 +10,6 @@
10
"type": "integer",
11
"minimum": 1,
12
"default": 10
13
- },
14
- "binary_path": {
15
- "title": "Binary path",
16
- "description": "Path to the `sensors` binary. If left empty or if the binary is not found, [**sysfs**](https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface) will be used to collect sensor statistics.",
17
- "type": "string",
18
- "default": "/usr/bin/sensors"
19
- },
20
- "timeout": {
21
- "title": "Timeout",
22
- "description": "Timeout for executing the binary, specified in seconds.",
23
- "type": "number",
24
- "minimum": 0.5,
25
- "default": 2
13
}
14
},
15
"required": [
@@ -35,12 +22,6 @@
22
"uiSchema": {
23
"uiOptions": {
24
"fullPage": true
38
- },
39
- "binary_path": {
40
- "ui:help": "If an absolute path is provided, the collector will use it directly; otherwise, it will search for the binary in directories specified in the PATH environment variable."
41
- },
42
- "timeout": {
43
- "ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)."
25
}
26
}
27
}
src/go/plugin/go.d/modules/sensors/exec.go
deleted
-41
@@ -1,41 +0,0 @@
1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-package sensors
4
-
5
-import (
6
- "context"
7
- "fmt"
8
- "os/exec"
9
- "time"
10
-
11
- "github.com/netdata/netdata/go/plugins/logger"
12
-)
13
-
14
-func newSensorsCliExec(binPath string, timeout time.Duration) *sensorsCliExec {
15
- return &sensorsCliExec{
16
- binPath: binPath,
17
- timeout: timeout,
18
- }
19
-}
20
-
21
-type sensorsCliExec struct {
22
- *logger.Logger
23
-
24
- binPath string
25
- timeout time.Duration
26
-}
27
-
28
-func (e *sensorsCliExec) sensorsInfo() ([]byte, error) {
29
- ctx, cancel := context.WithTimeout(context.Background(), e.timeout)
30
- defer cancel()
31
-
32
- cmd := exec.CommandContext(ctx, e.binPath, "-A", "-u")
33
- e.Debugf("executing '%s'", cmd)
34
-
35
- bs, err := cmd.Output()
36
- if err != nil {
37
- return nil, fmt.Errorf("error on '%s': %v", cmd, err)
38
- }
39
-
40
- return bs, nil
41
-}
src/go/plugin/go.d/modules/sensors/init.go
deleted
-34
@@ -1,34 +0,0 @@
1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-package sensors
4
-
5
-import (
6
- "os"
7
- "os/exec"
8
- "strings"
9
-)
10
-
11
-func (s *Sensors) initSensorsBinary() (sensorsBinary, error) {
12
- if s.BinaryPath == "" {
13
- return nil, nil
14
- }
15
-
16
- binPath := s.BinaryPath
17
-
18
- if !strings.HasPrefix(binPath, "/") {
19
- path, err := exec.LookPath(binPath)
20
- if err != nil {
21
- return nil, err
22
- }
23
- binPath = path
24
- }
25
-
26
- if _, err := os.Stat(binPath); err != nil {
27
- return nil, err
28
- }
29
-
30
- sensorsExec := newSensorsCliExec(binPath, s.Timeout.Duration())
31
- sensorsExec.Logger = s.Logger
32
-
33
- return sensorsExec, nil
34
-}
src/go/plugin/go.d/modules/sensors/lmsensors/currentsensor.go
deleted
-62
@@ -1,62 +0,0 @@
1
-package lmsensors
2
-
3
-import (
4
- "strconv"
5
-)
6
-
7
-var _ Sensor = &CurrentSensor{}
8
-
9
-// A CurrentSensor is a Sensor that detects current in Amperes.
10
-type CurrentSensor struct {
11
- // The name of the sensor.
12
- Name string
13
-
14
- // A label that describes what the sensor is monitoring. Label may be empty.
15
- Label string
16
-
17
- // Whether the sensor has an alarm triggered.
18
- Alarm bool
19
-
20
- // The input current, in Amperes, indicated by the sensor.
21
- Input float64
22
-
23
- // The maximum current threshold, in Amperes, indicated by the sensor.
24
- Maximum float64
25
-
26
- // The critical current threshold, in Amperes, indicated by the sensor.
27
- Critical float64
28
-}
29
-
30
-func (s *CurrentSensor) Type() SensorType { return SensorTypeCurrent }
31
-
32
-func (s *CurrentSensor) parse(raw map[string]string) error {
33
- for k, v := range raw {
34
- switch k {
35
- case "crit", "input", "max":
36
- f, err := strconv.ParseFloat(v, 64)
37
- if err != nil {
38
- return err
39
- }
40
-
41
- // Raw current values are scaled by 1000
42
- f /= 1000
43
-
44
- switch k {
45
- case "crit":
46
- s.Critical = f
47
- case "input":
48
- s.Input = f
49
- case "max":
50
- s.Maximum = f
51
- }
52
- case "alarm":
53
- s.Alarm = v != "0"
54
- case "label":
55
- s.Label = v
56
- }
57
- }
58
-
59
- return nil
60
-}
61
-
62
-func (s *CurrentSensor) name() string { return s.Name }
src/go/plugin/go.d/modules/sensors/lmsensors/doc.go
+1
-2
@@ -1,3 +1,2 @@
1
-// Package lmsensors provides access to Linux monitoring sensors data, such
2
-// as temperatures, voltage, and fan speeds.
1
+// Package lmsensors provides access to Linux monitoring sensors data, such as temperatures, voltage, and fan speeds.
2
package lmsensors
src/go/plugin/go.d/modules/sensors/lmsensors/fansensor.go
deleted
-64
@@ -1,64 +0,0 @@
1
-package lmsensors
2
-
3
-import (
4
- "strconv"
5
-)
6
-
7
-var _ Sensor = &FanSensor{}
8
-
9
-// A FanSensor is a Sensor that detects fan speeds in rotations per minute.
10
-type FanSensor struct {
11
- // The name of the sensor.
12
- Name string
13
-
14
- // A label that describes what the sensor is monitoring. Label may be empty.
15
- Label string
16
-
17
- // Whether the fan speed is below the minimum threshold.
18
- Alarm bool
19
-
20
- // Whether the fan will sound an audible alarm when fan speed is below the minimum threshold.
21
- Beep bool
22
-
23
- // The input fan speed, in rotations per minute, indicated by the sensor.
24
- Input float64
25
-
26
- // The low threshold fan speed, in rotations per minute, indicated by the sensor.
27
- Minimum float64
28
-
29
- // The high threshold fan speed, in rotations per minute, indicated by the sensor.
30
- Maximum float64
31
-}
32
-
33
-func (s *FanSensor) Type() SensorType { return SensorTypeFan }
34
-
35
-func (s *FanSensor) parse(raw map[string]string) error {
36
- for k, v := range raw {
37
- switch k {
38
- case "input", "min", "max":
39
- f, err := strconv.ParseFloat(v, 64)
40
- if err != nil {
41
- return err
42
- }
43
-
44
- switch k {
45
- case "input":
46
- s.Input = f
47
- case "min":
48
- s.Minimum = f
49
- case "max":
50
- s.Maximum = f
51
- }
52
- case "alarm":
53
- s.Alarm = v != "0"
54
- case "beep":
55
- s.Beep = v != "0"
56
- case "label":
57
- s.Label = v
58
- }
59
- }
60
-
61
- return nil
62
-}
63
-
64
-func (s *FanSensor) name() string { return s.Name }
src/go/plugin/go.d/modules/sensors/lmsensors/fs.go
new
+44
@@ -0,0 +1,44 @@
1
+package lmsensors
2
+
3
+import (
4
+ "io/fs"
5
+ "os"
6
+ "path/filepath"
7
+ "strings"
8
+)
9
+
10
+// A filesystem is an interface to a filesystem, used for testing.
11
+type filesystem interface {
12
+ ReadDir(name string) ([]fs.DirEntry, error)
13
+ ReadFile(filename string) (string, error)
14
+ Readlink(name string) (string, error)
15
+ Stat(name string) (os.FileInfo, error)
16
+ WalkDir(root string, walkFn fs.WalkDirFunc) error
17
+}
18
+
19
+// A systemFilesystem is a filesystem which uses operations on the host filesystem.
20
+type systemFilesystem struct{}
21
+
22
+func (s *systemFilesystem) ReadDir(name string) ([]fs.DirEntry, error) {
23
+ return os.ReadDir(name)
24
+}
25
+
26
+func (s *systemFilesystem) ReadFile(filename string) (string, error) {
27
+ b, err := os.ReadFile(filename)
28
+ if err != nil {
29
+ return "", err
30
+ }
31
+ return strings.TrimSpace(string(b)), nil
32
+}
33
+
34
+func (s *systemFilesystem) Readlink(name string) (string, error) {
35
+ return os.Readlink(name)
36
+}
37
+
38
+func (s *systemFilesystem) Stat(name string) (os.FileInfo, error) {
39
+ return os.Stat(name)
40
+}
41
+
42
+func (s *systemFilesystem) WalkDir(root string, walkFn fs.WalkDirFunc) error {
43
+ return filepath.WalkDir(root, walkFn)
44
+}
src/go/plugin/go.d/modules/sensors/lmsensors/intrusionsensor.go
deleted
-32
@@ -1,32 +0,0 @@
1
-package lmsensors
2
-
3
-var _ Sensor = &IntrusionSensor{}
4
-
5
-// An IntrusionSensor is a Sensor that detects when the machine's chassis has been opened.
6
-type IntrusionSensor struct {
7
- // The name of the sensor.
8
- Name string
9
-
10
- // A label that describes what the sensor is monitoring. Label may be empty.
11
- Label string
12
-
13
- // Whether the machine's chassis has been opened, and the alarm has been triggered.
14
- Alarm bool
15
-}
16
-
17
-func (s *IntrusionSensor) Type() SensorType { return SensorTypeIntrusion }
18
-
19
-func (s *IntrusionSensor) parse(raw map[string]string) error {
20
- for k, v := range raw {
21
- switch k {
22
- case "alarm":
23
- s.Alarm = v != "0"
24
- case "label":
25
- s.Label = v
26
- }
27
- }
28
-
29
- return nil
30
-}
31
-
32
-func (s *IntrusionSensor) name() string { return s.Name }
src/go/plugin/go.d/modules/sensors/lmsensors/parse.go
new
+363
@@ -0,0 +1,363 @@
1
+package lmsensors
2
+
3
+import (
4
+ "fmt"
5
+ "strconv"
6
+ "strings"
7
+ "time"
8
+ "unicode"
9
+)
10
+
11
+type (
12
+ rawSensors map[string]map[string]rawValue // e.g. [temp1][input]
13
+
14
+ rawValue struct {
15
+ value string
16
+ readTime time.Duration
17
+ }
18
+)
19
+
20
+// parseSensors parses all Sensors from an input raw data slice, produced during a filesystem walk.
21
+func parseSensors(rawSns rawSensors) (*Sensors, error) {
22
+ var sensors Sensors
23
+
24
+ for name, values := range rawSns {
25
+ typ := name
26
+ if i := strings.IndexFunc(name, unicode.IsDigit); i > 0 {
27
+ typ = name[:i]
28
+ }
29
+
30
+ switch typ {
31
+ case "in":
32
+ sn := &VoltageSensor{Name: name}
33
+ if err := parseVoltage(sn, values); err != nil {
34
+ return nil, fmt.Errorf("voltage sensor '%s': %w", name, err)
35
+ }
36
+ sensors.Voltage = append(sensors.Voltage, sn)
37
+ case "fan":
38
+ sn := &FanSensor{Name: name}
39
+ if err := parseFan(sn, values); err != nil {
40
+ return nil, fmt.Errorf("fan sensor '%s': %w", name, err)
41
+ }
42
+ sensors.Fan = append(sensors.Fan, sn)
43
+ case "temp":
44
+ sn := &TemperatureSensor{Name: name}
45
+ if err := parseTemperature(sn, values); err != nil {
46
+ return nil, fmt.Errorf("temperature sensor '%s': %w", name, err)
47
+ }
48
+ sensors.Temperature = append(sensors.Temperature, sn)
49
+ case "curr":
50
+ sn := &CurrentSensor{Name: name}
51
+ if err := parseCurrent(sn, values); err != nil {
52
+ return nil, fmt.Errorf("current sensor '%s': %w", name, err)
53
+ }
54
+ sensors.Current = append(sensors.Current, sn)
55
+ case "power":
56
+ sn := &PowerSensor{Name: name}
57
+ if err := parsePower(sn, values); err != nil {
58
+ return nil, fmt.Errorf("power sensor '%s': %w", name, err)
59
+ }
60
+ sensors.Power = append(sensors.Power, sn)
61
+ case "energy":
62
+ sn := &EnergySensor{Name: name}
63
+ if err := parseEnergy(sn, values); err != nil {
64
+ return nil, fmt.Errorf("energy sensor '%s': %w", name, err)
65
+ }
66
+ sensors.Energy = append(sensors.Energy, sn)
67
+ case "humidity":
68
+ sn := &HumiditySensor{Name: name}
69
+ if err := parseHumidity(sn, values); err != nil {
70
+ return nil, fmt.Errorf("humidity sensor '%s': %w", name, err)
71
+ }
72
+ sensors.Humidity = append(sensors.Humidity, sn)
73
+ case "intrusion":
74
+ sn := &IntrusionSensor{Name: name}
75
+ if err := parseIntrusion(sn, values); err != nil {
76
+ return nil, fmt.Errorf("intrusion sensor '%s': %w", name, err)
77
+ }
78
+ sensors.Intrusion = append(sensors.Intrusion, sn)
79
+ default:
80
+ continue
81
+ }
82
+ }
83
+
84
+ return &sensors, nil
85
+}
86
+
87
+func parseVoltage(s *VoltageSensor, values map[string]rawValue) error {
88
+ const div = 1e3 // raw in milli degree Celsius
89
+
90
+ for name, val := range values {
91
+ var err error
92
+
93
+ s.ReadTime += val.readTime
94
+ v := val.value
95
+
96
+ switch name {
97
+ case "label":
98
+ s.Label = v
99
+ case "alarm":
100
+ s.Alarm = ptr(v != "0")
101
+ case "min":
102
+ s.Min, err = parseFloat(v, div)
103
+ case "lcrit":
104
+ s.CritMin, err = parseFloat(v, div)
105
+ case "max":
106
+ s.Max, err = parseFloat(v, div)
107
+ case "crit":
108
+ s.CritMax, err = parseFloat(v, div)
109
+ case "input":
110
+ s.Input, err = parseFloat(v, div)
111
+ case "average":
112
+ s.Average, err = parseFloat(v, div)
113
+ case "lowest":
114
+ s.Lowest, err = parseFloat(v, div)
115
+ case "highest":
116
+ s.Highest, err = parseFloat(v, div)
117
+ }
118
+ if err != nil {
119
+ return fmt.Errorf("subfeature '%s' value '%s': %v", name, v, err)
120
+ }
121
+ }
122
+
123
+ return nil
124
+}
125
+
126
+func parseFan(s *FanSensor, values map[string]rawValue) error {
127
+ const div = 1 // raw in revolution/min (RPM)
128
+
129
+ for name, val := range values {
130
+ var err error
131
+
132
+ s.ReadTime += val.readTime
133
+ v := val.value
134
+
135
+ switch name {
136
+ case "input":
137
+ s.Input, err = parseFloat(v, div)
138
+ case "min":
139
+ s.Min, err = parseFloat(v, div)
140
+ case "max":
141
+ s.Max, err = parseFloat(v, div)
142
+ case "target":
143
+ s.Target, err = parseFloat(v, div)
144
+ case "alarm":
145
+ s.Alarm = ptr(v != "0")
146
+ case "label":
147
+ s.Label = v
148
+ }
149
+ if err != nil {
150
+ return fmt.Errorf("subfeature '%s' value '%s': %v", name, v, err)
151
+ }
152
+ }
153
+
154
+ return nil
155
+}
156
+
157
+func parseTemperature(s *TemperatureSensor, values map[string]rawValue) error {
158
+ const div = 1000 // raw in milli degree Celsius
159
+
160
+ for name, val := range values {
161
+ var err error
162
+
163
+ s.ReadTime += val.readTime
164
+ v := val.value
165
+
166
+ switch name {
167
+ case "max":
168
+ s.Max, err = parseFloat(v, div)
169
+ case "min":
170
+ s.Min, err = parseFloat(v, div)
171
+ case "input":
172
+ s.Input, err = parseFloat(v, div)
173
+ case "crit":
174
+ s.CritMax, err = parseFloat(v, div)
175
+ case "emergency":
176
+ s.Emergency, err = parseFloat(v, div)
177
+ case "lcrit":
178
+ s.CritMin, err = parseFloat(v, div)
179
+ case "lowest":
180
+ s.Lowest, err = parseFloat(v, div)
181
+ case "highest":
182
+ s.Highest, err = parseFloat(v, div)
183
+ case "alarm":
184
+ s.Alarm = ptr(v != "0")
185
+ case "type":
186
+ t, err := strconv.Atoi(v)
187
+ if err != nil {
188
+ return err
189
+ }
190
+ s.TempTypeRaw = t
191
+ case "label":
192
+ s.Label = v
193
+ }
194
+ if err != nil {
195
+ return fmt.Errorf("subfeature '%s' value '%s': %v", name, v, err)
196
+ }
197
+ }
198
+
199
+ return nil
200
+}
201
+
202
+func parseCurrent(s *CurrentSensor, values map[string]rawValue) error {
203
+ const div = 1e3 // raw in milli ampere
204
+
205
+ for name, val := range values {
206
+ var err error
207
+
208
+ s.ReadTime += val.readTime
209
+ v := val.value
210
+
211
+ switch name {
212
+ case "max":
213
+ s.Max, err = parseFloat(v, div)
214
+ case "min":
215
+ s.Min, err = parseFloat(v, div)
216
+ case "lcrit":
217
+ s.CritMin, err = parseFloat(v, div)
218
+ case "crit":
219
+ s.CritMax, err = parseFloat(v, div)
220
+ case "input":
221
+ s.Input, err = parseFloat(v, div)
222
+ case "average":
223
+ s.Average, err = parseFloat(v, div)
224
+ case "lowest":
225
+ s.Lowest, err = parseFloat(v, div)
226
+ case "highest":
227
+ s.Highest, err = parseFloat(v, div)
228
+ case "alarm":
229
+ s.Alarm = ptr(v != "0")
230
+ case "label":
231
+ s.Label = v
232
+ }
233
+ if err != nil {
234
+ return fmt.Errorf("subfeature '%s' value '%s': %v", name, v, err)
235
+ }
236
+ }
237
+
238
+ return nil
239
+}
240
+
241
+func parsePower(s *PowerSensor, values map[string]rawValue) error {
242
+ const div = 1e6 // raw in microWatt
243
+
244
+ for name, val := range values {
245
+ var err error
246
+
247
+ s.ReadTime += val.readTime
248
+ v := val.value
249
+
250
+ switch name {
251
+ case "label":
252
+ s.Label = v
253
+ case "alarm":
254
+ s.Alarm = ptr(v != "0")
255
+ case "average":
256
+ s.Average, err = parseFloat(v, div)
257
+ case "average_highest":
258
+ s.AverageHighest, err = parseFloat(v, div)
259
+ case "average_lowest":
260
+ s.AverageLowest, err = parseFloat(v, div)
261
+ case "average_max":
262
+ s.AverageMax, err = parseFloat(v, div)
263
+ case "average_min":
264
+ s.AverageMin, err = parseFloat(v, div)
265
+ case "input":
266
+ s.Input, err = parseFloat(v, div)
267
+ case "input_highest":
268
+ s.InputHighest, err = parseFloat(v, div)
269
+ case "input_lowest":
270
+ s.InputLowest, err = parseFloat(v, div)
271
+ case "accuracy":
272
+ s.Accuracy, err = parseFloat(v, 1)
273
+ case "cap":
274
+ s.Cap, err = parseFloat(v, div)
275
+ case "cap_max":
276
+ s.CapMax, err = parseFloat(v, div)
277
+ case "cap_min":
278
+ s.CapMin, err = parseFloat(v, div)
279
+ case "max":
280
+ s.Max, err = parseFloat(v, div)
281
+ case "crit":
282
+ s.CritMax, err = parseFloat(v, div)
283
+ }
284
+ if err != nil {
285
+ return fmt.Errorf("subfeature '%s' value '%s': %v", name, v, err)
286
+ }
287
+ }
288
+
289
+ return nil
290
+}
291
+
292
+func parseEnergy(s *EnergySensor, values map[string]rawValue) error {
293
+ const div = 1e6 // raw in microJoule
294
+
295
+ for name, val := range values {
296
+ var err error
297
+
298
+ s.ReadTime += val.readTime
299
+ v := val.value
300
+
301
+ switch name {
302
+ case "label":
303
+ s.Label = v
304
+ case "input":
305
+ s.Input, err = parseFloat(v, div)
306
+ }
307
+ if err != nil {
308
+ return fmt.Errorf("subfeature '%s' value '%s': %v", name, v, err)
309
+ }
310
+ }
311
+
312
+ return nil
313
+}
314
+
315
+func parseHumidity(s *HumiditySensor, values map[string]rawValue) error {
316
+ const div = 1e3 // raw in milli percent
317
+
318
+ for name, val := range values {
319
+ var err error
320
+
321
+ s.ReadTime += val.readTime
322
+ v := val.value
323
+
324
+ switch name {
325
+ case "label":
326
+ s.Label = v
327
+ case "input":
328
+ s.Input, err = parseFloat(v, div)
329
+ }
330
+ if err != nil {
331
+ return fmt.Errorf("subfeature '%s' value '%s': %v", name, v, err)
332
+ }
333
+ }
334
+
335
+ return nil
336
+}
337
+
338
+func parseIntrusion(s *IntrusionSensor, values map[string]rawValue) error {
339
+ for name, val := range values {
340
+ s.ReadTime += val.readTime
341
+ v := val.value
342
+
343
+ switch name {
344
+ case "label":
345
+ s.Label = v
346
+ case "alarm":
347
+ s.Alarm = ptr(v != "0")
348
+ }
349
+ }
350
+
351
+ return nil
352
+}
353
+
354
+func parseFloat(s string, div float64) (*float64, error) {
355
+ f, err := strconv.ParseFloat(s, 64)
356
+ if err != nil {
357
+ return nil, err
358
+ }
359
+
360
+ return ptr(f / div), nil
361
+}
362
+
363
+func ptr[T any](v T) *T { return &v }
src/go/plugin/go.d/modules/sensors/lmsensors/powersensor.go
deleted
-75
@@ -1,75 +0,0 @@
1
-package lmsensors
2
-
3
-import (
4
- "strconv"
5
- "time"
6
-)
7
-
8
-var _ Sensor = &PowerSensor{}
9
-
10
-// A PowerSensor is a Sensor that detects average electrical power consumption in watts.
11
-type PowerSensor struct {
12
- // The name of the sensor.
13
- Name string
14
-
15
- // A label that describes what the sensor is monitoring. Label may be empty.
16
- Label string
17
-
18
- // The average electrical power consumption, in watts, indicated by the sensor.
19
- Average float64
20
-
21
- // The interval of time over which the average electrical power consumption is collected.
22
- AverageInterval time.Duration
23
-
24
- // Whether this sensor has a battery.
25
- Battery bool
26
-
27
- // The model number of the sensor.
28
- ModelNumber string
29
-
30
- // Miscellaneous OEM information about the sensor.
31
- OEMInfo string
32
-
33
- // The serial number of the sensor.
34
- SerialNumber string
35
-}
36
-
37
-func (s *PowerSensor) Type() SensorType { return SensorTypePower }
38
-
39
-func (s *PowerSensor) parse(raw map[string]string) error {
40
- for k, v := range raw {
41
- switch k {
42
- case "average":
43
- f, err := strconv.ParseFloat(v, 64)
44
- if err != nil {
45
- return err
46
- }
47
-
48
- // Raw temperature values are scaled by one million
49
- f /= 1000000
50
- s.Average = f
51
- case "average_interval":
52
- // Time values in milliseconds
53
- d, err := time.ParseDuration(v + "ms")
54
- if err != nil {
55
- return err
56
- }
57
-
58
- s.AverageInterval = d
59
- case "is_battery":
60
- s.Battery = v != "0"
61
- case "model_number":
62
- s.ModelNumber = v
63
- case "oem_info":
64
- s.OEMInfo = v
65
- case "serial_number":
66
- s.SerialNumber = v
67
- case "label":
68
- s.Label = v
69
- }
70
- }
71
-
72
- return nil
73
-}
74
-
75
-func (s *PowerSensor) name() string { return s.Name }
src/go/plugin/go.d/modules/sensors/lmsensors/scanner.go
+80
-102
@@ -1,6 +1,8 @@
1
package lmsensors
2
3
import (
4
+ "encoding/hex"
5
+ "errors"
6
"fmt"
7
"io/fs"
8
"os"
@@ -8,15 +10,16 @@ import (
10
"strings"
11
"time"
12
13
+ "github.com/cloudflare/cfssl/scan/crypto/sha1"
14
+
15
"github.com/netdata/netdata/go/plugins/logger"
16
)
17
14
-// A filesystem is an interface to a filesystem, used for testing.
15
-type filesystem interface {
16
- ReadFile(filename string) (string, error)
17
- Readlink(name string) (string, error)
18
- Stat(name string) (os.FileInfo, error)
19
- WalkDir(root string, walkFn fs.WalkDirFunc) error
18
+// New creates a new Scanner.
19
+func New() *Scanner {
20
+ return &Scanner{
21
+ fs: &systemFilesystem{},
22
+ }
23
}
24
25
// A Scanner scans for Devices, so data can be read from their Sensors.
@@ -26,15 +29,8 @@ type Scanner struct {
29
fs filesystem
30
}
31
29
-// New creates a new Scanner.
30
-func New() *Scanner {
31
- return &Scanner{
32
- fs: &systemFilesystem{},
33
- }
34
-}
35
-
32
// Scan scans for Devices and their Sensors.
37
-func (sc *Scanner) Scan() ([]*Device, error) {
33
+func (sc *Scanner) Scan() ([]*Chip, error) {
34
paths, err := sc.detectDevicePaths()
35
if err != nil {
36
return nil, err
@@ -42,93 +38,71 @@ func (sc *Scanner) Scan() ([]*Device, error) {
38
39
sc.Debugf("sysfs scanner: found %d paths", len(paths))
40
45
- var devices []*Device
41
+ var chips []*Chip
42
47
- for _, rootPath := range paths {
48
- sc.Debugf("sysfs scanner: scanning %s", rootPath)
43
+ for _, path := range paths {
44
+ sc.Debugf("sysfs scanner: scanning %s", path)
45
50
- dev := &Device{}
51
- raw := make(map[string]map[string]string)
46
+ var chip Chip
47
53
- // Walk filesystem paths to fetch devices and sensors
54
- err := sc.fs.WalkDir(rootPath, func(path string, de fs.DirEntry, err error) error {
55
- if err != nil {
56
- return err
57
- }
48
+ rawSns := make(rawSensors)
49
59
- if de.IsDir() || !de.Type().IsRegular() {
60
- if de.IsDir() && path != rootPath {
61
- return fs.SkipDir
62
- }
63
- return nil
64
- }
50
+ des, err := sc.fs.ReadDir(path)
51
+ if err != nil {
52
+ return nil, err
53
+ }
54
66
- // Skip some files that can't be read or don't provide useful sensor information
67
- file := filepath.Base(path)
68
- if shouldSkip(file) {
69
- return nil
55
+ for _, de := range des {
56
+ if !de.Type().IsRegular() || shouldSkip(de.Name()) {
57
+ continue
58
}
59
60
+ filePath := filepath.Join(path, de.Name())
61
+
62
now := time.Now()
73
- s, err := sc.fs.ReadFile(path)
63
+ content, err := sc.fs.ReadFile(filePath)
64
if err != nil {
75
- return nil
65
+ sc.Debugf("sysfs scanner: failed to read '%s': %v", filePath, err)
66
+ continue
67
}
77
- sc.Debugf("sysfs scanner: reading file '%s' took %s", path, time.Since(now))
68
+ since := time.Since(now)
69
+
70
+ sc.Debugf("sysfs scanner: reading file '%s' took %s", filePath, since)
71
79
- if file == "name" {
80
- dev.Name = s
81
- return nil
72
+ if de.Name() == "name" {
73
+ chip.Name = content
74
+ continue
75
}
76
77
// Sensor names in format "sensor#_foo", e.g. "temp1_input"
85
- parts := strings.SplitN(file, "_", 2)
86
- if len(parts) != 2 {
87
- return nil
78
+ feat, subfeat, ok := strings.Cut(de.Name(), "_")
79
+ if !ok {
80
+ continue
81
}
82
90
- if _, ok := raw[parts[0]]; !ok {
91
- raw[parts[0]] = make(map[string]string)
83
+ if _, ok := rawSns[feat]; !ok {
84
+ rawSns[feat] = make(map[string]rawValue)
85
}
86
94
- raw[parts[0]][parts[1]] = s
95
-
96
- return nil
97
- })
98
- if err != nil {
99
- return nil, err
87
+ rawSns[feat][subfeat] = rawValue{value: content, readTime: since}
88
}
89
102
- sensors, err := parseSensors(raw)
90
+ sensors, err := parseSensors(rawSns)
91
if err != nil {
104
- return nil, err
92
+ return nil, fmt.Errorf("sysfs scanner: failed to parse (device '%s', path '%s'): %v", chip.Name, path, err)
93
}
94
107
- for _, sn := range sensors {
108
- sc.Debugf("sysfs scanner: found sensor %+v", sn)
95
+ if sensors != nil {
96
+ chip.Sensors = *sensors
97
}
98
111
- dev.Sensors = sensors
112
- devices = append(devices, dev)
113
- }
114
-
115
- renameDevices(devices)
99
+ chip.SysDevice = getDevicePath(path)
100
+ chip.UniqueName = fmt.Sprintf("%s-%s-%s", chip.Name, getBusType(chip.SysDevice), getHash(chip.SysDevice))
101
117
- return devices, nil
118
-}
119
-
120
-// renameDevices renames devices in place to prevent duplicate device names, and to number each device.
121
-func renameDevices(devices []*Device) {
122
- nameCount := make(map[string]int)
123
-
124
- for i := range devices {
125
- name := devices[i].Name
126
- devices[i].Name = fmt.Sprintf("%s-%02d",
127
- name,
128
- nameCount[name],
129
- )
130
- nameCount[name]++
102
+ chips = append(chips, &chip)
103
}
104
+
105
+ return chips, nil
106
}
107
108
// detectDevicePaths performs a filesystem walk to paths where devices may reside on Linux.
@@ -136,6 +110,7 @@ func (sc *Scanner) detectDevicePaths() ([]string, error) {
110
const lookPath = "/sys/class/hwmon"
111
112
var paths []string
113
+
114
err := sc.fs.WalkDir(lookPath, func(path string, de os.DirEntry, err error) error {
115
if err != nil {
116
return err
@@ -154,7 +129,7 @@ func (sc *Scanner) detectDevicePaths() ([]string, error) {
129
130
// Symlink destination has a file called name, meaning a sensor exists here and data can be retrieved
131
fi, err := sc.fs.Stat(filepath.Join(dest, "name"))
157
- if err != nil && !os.IsNotExist(err) {
132
+ if err != nil && !errors.Is(err, fs.ErrNotExist) {
133
return err
134
}
135
if err == nil && fi.Mode().IsRegular() {
@@ -166,7 +141,7 @@ func (sc *Scanner) detectDevicePaths() ([]string, error) {
141
device := filepath.Join(dest, "device")
142
fi, err = sc.fs.Stat(device)
143
if err != nil {
169
- if !os.IsNotExist(err) {
144
+ if !errors.Is(err, fs.ErrNotExist) {
145
return err
146
}
147
return nil
@@ -185,7 +160,7 @@ func (sc *Scanner) detectDevicePaths() ([]string, error) {
160
161
// Symlink destination has a file called name, meaning a sensor exists here and data can be retrieved
162
if _, err := sc.fs.Stat(filepath.Join(dest, "name")); err != nil {
188
- if !os.IsNotExist(err) {
163
+ if !errors.Is(err, fs.ErrNotExist) {
164
return err
165
}
166
return nil
@@ -199,6 +174,34 @@ func (sc *Scanner) detectDevicePaths() ([]string, error) {
174
return paths, err
175
}
176
177
+func getDevicePath(path string) string {
178
+ devPath, err := filepath.EvalSymlinks(filepath.Join(path, "device"))
179
+ if err != nil {
180
+ devPath = path
181
+ if i := strings.Index(devPath, "/hwmon"); i > 0 {
182
+ devPath = devPath[:i]
183
+ }
184
+ }
185
+ return strings.TrimPrefix(devPath, "/sys/devices/")
186
+}
187
+
188
+func getHash(devPath string) string {
189
+ hash := sha1.Sum([]byte(devPath))
190
+ return hex.EncodeToString(hash[:])[:8]
191
+}
192
+
193
+func getBusType(devPath string) string {
194
+ devPath = filepath.Join("/", devPath)
195
+ devPath = strings.ToLower(devPath)
196
+
197
+ for _, v := range []string{"i2c", "isa", "pci", "spi", "virtual", "acpi", "hid", "mdio", "scsi"} {
198
+ if strings.Contains(devPath, "/"+v) {
199
+ return v
200
+ }
201
+ }
202
+ return "unk"
203
+}
204
+
205
// shouldSkip indicates if a given filename should be skipped during the filesystem walk operation.
206
func shouldSkip(file string) bool {
207
if strings.HasPrefix(file, "runtime_") {
@@ -218,28 +221,3 @@ func shouldSkip(file string) bool {
221
222
return true
223
}
221
-
222
-var _ filesystem = &systemFilesystem{}
223
-
224
-// A systemFilesystem is a filesystem which uses operations on the host filesystem.
225
-type systemFilesystem struct{}
226
-
227
-func (fs *systemFilesystem) ReadFile(filename string) (string, error) {
228
- b, err := os.ReadFile(filename)
229
- if err != nil {
230
- return "", err
231
- }
232
- return strings.TrimSpace(string(b)), nil
233
-}
234
-
235
-func (fs *systemFilesystem) Readlink(name string) (string, error) {
236
- return os.Readlink(name)
237
-}
238
-
239
-func (fs *systemFilesystem) Stat(name string) (os.FileInfo, error) {
240
- return os.Stat(name)
241
-}
242
-
243
-func (fs *systemFilesystem) WalkDir(root string, walkFn fs.WalkDirFunc) error {
244
- return filepath.WalkDir(root, walkFn)
245
-}
src/go/plugin/go.d/modules/sensors/lmsensors/scanner_test.go
+386
-672
@@ -4,750 +4,469 @@ import (
4
"fmt"
5
"io/fs"
6
"os"
7
- "reflect"
7
+ "path/filepath"
8
+ "slices"
9
"strings"
10
"testing"
11
"time"
11
-)
12
13
-// TODO(mdlayher): why does scanning work if device file isn't a symlink,
14
-// even though it is in the actual filesystem (and the actual filesystem
15
-// exhibits the same behavior)?
13
+ "github.com/stretchr/testify/require"
14
+)
15
17
-func TestScannerScan(t *testing.T) {
18
- tests := []struct {
19
- name string
20
- fs filesystem
21
- devices []*Device
16
+func TestScanner_Scan(t *testing.T) {
17
+ var tests = map[string]struct {
18
+ fs filesystem
19
+ wantDevices []*Chip
20
}{
23
- {
24
- name: "power_meter device",
21
+ "Power sensor": {
22
+ wantDevices: []*Chip{
23
+ {
24
+ Name: "power_meter",
25
+ UniqueName: "power_meter-acpi-b37a4ed3",
26
+ SysDevice: "LNXSYSTM:00/device:00/ACPI0000:00",
27
+ Sensors: Sensors{
28
+ Power: []*PowerSensor{
29
+ {
30
+ Name: "power1",
31
+ Label: "some_label",
32
+ Alarm: ptr(false),
33
+ Average: ptr(345.0),
34
+ AverageHighest: ptr(345.0),
35
+ AverageLowest: ptr(345.0),
36
+ AverageMax: ptr(345.0),
37
+ AverageMin: ptr(345.0),
38
+ Input: ptr(345.0),
39
+ InputHighest: ptr(345.0),
40
+ InputLowest: ptr(345.0),
41
+ Accuracy: ptr(34.5),
42
+ Cap: ptr(345.0),
43
+ CapMax: ptr(345.0),
44
+ CapMin: ptr(345.0),
45
+ Max: ptr(345.0),
46
+ CritMax: ptr(345.0),
47
+ },
48
+ },
49
+ }},
50
+ },
51
fs: &memoryFilesystem{
52
symlinks: map[string]string{
53
"/sys/class/hwmon/hwmon0": "../../devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0",
54
"/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device": "../../../ACPI0000:00",
55
},
56
files: []memoryFile{
31
- {
32
- name: "/sys/class/hwmon",
33
- dirEntry: &memoryDirEntry{
34
- isDir: true,
35
- },
36
- },
37
- {
38
- name: "/sys/class/hwmon/hwmon0",
39
- dirEntry: &memoryDirEntry{
40
- mode: os.ModeSymlink,
41
- },
42
- },
43
- {
44
- name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00",
45
- dirEntry: &memoryDirEntry{
46
- isDir: true,
47
- },
48
- },
49
- {
50
- name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/name",
51
- err: os.ErrNotExist,
52
- },
53
- {
54
- name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device",
55
- dirEntry: &memoryDirEntry{
56
- // mode: os.ModeSymlink,
57
- },
58
- },
59
- {
60
- name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/name",
61
- contents: "power_meter",
62
- },
63
- {
64
- name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_average",
65
- contents: "345000000",
66
- },
67
- {
68
- name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_average_interval",
69
- contents: "1000",
70
- },
71
- {
72
- name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_is_battery",
73
- contents: "0",
74
- },
75
- {
76
- name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_model_number",
77
- contents: "Intel(R) Node Manager",
78
- },
79
- {
80
- name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_oem_info",
81
- contents: "Meter measures total domain",
82
- },
83
- {
84
- name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_serial_number",
85
- contents: "",
86
- },
57
+ {name: "/sys/class/hwmon", de: &memoryDirEntry{isDir: true}},
58
+ {name: "/sys/class/hwmon/hwmon0", de: &memoryDirEntry{mode: os.ModeSymlink}},
59
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00", de: &memoryDirEntry{isDir: true}},
60
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/name", err: os.ErrNotExist},
61
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device", de: &memoryDirEntry{}},
62
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/name", val: "power_meter"},
63
+
64
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_label", val: "some_label"},
65
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_alarm", val: "0"},
66
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_average", val: "345000000"},
67
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_average_highest", val: "345000000"},
68
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_average_lowest", val: "345000000"},
69
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_average_max", val: "345000000"},
70
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_average_min", val: "345000000"},
71
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_input", val: "345000000"},
72
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_input_highest", val: "345000000"},
73
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_input_lowest", val: "345000000"},
74
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_accuracy", val: "34.5"},
75
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_cap", val: "345000000"},
76
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_cap_max", val: "345000000"},
77
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_cap_min", val: "345000000"},
78
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_max", val: "345000000"},
79
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/power1_crit", val: "345000000"},
80
},
81
},
89
- devices: []*Device{{
90
- Name: "power_meter-00",
91
- Sensors: []Sensor{
92
- &PowerSensor{
93
- Name: "power1",
94
- Average: 345.0,
95
- AverageInterval: 1 * time.Second,
96
- Battery: false,
97
- ModelNumber: "Intel(R) Node Manager",
98
- OEMInfo: "Meter measures total domain",
99
- SerialNumber: "",
100
- },
101
- },
102
- }},
82
},
104
- {
105
- name: "acpitz device",
83
+ "Temperature sensor": {
84
+ wantDevices: []*Chip{
85
+ {
86
+ Name: "temp_meter",
87
+ UniqueName: "temp_meter-pci-e3b89088",
88
+ SysDevice: "pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0",
89
+ Sensors: Sensors{
90
+ Temperature: []*TemperatureSensor{
91
+ {
92
+ Name: "temp1",
93
+ Label: "some_label",
94
+ Alarm: ptr(false),
95
+ TempTypeRaw: 1,
96
+ Input: ptr(42.0),
97
+ Max: ptr(42.0),
98
+ Min: ptr(42.0),
99
+ CritMin: ptr(42.0),
100
+ CritMax: ptr(42.0),
101
+ Emergency: ptr(42.0),
102
+ Lowest: ptr(42.0),
103
+ Highest: ptr(42.0),
104
+ },
105
+ },
106
+ }},
107
+ },
108
fs: &memoryFilesystem{
109
symlinks: map[string]string{
108
- "/sys/class/hwmon/hwmon0": "../../devices/virtual/hwmon/hwmon0",
110
+ "/sys/class/hwmon/hwmon0": "../../devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0/hwmon0",
111
+ "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0/hwmon0/device": "../../../0000:81:00.0",
112
},
113
files: []memoryFile{
111
- {
112
- name: "/sys/class/hwmon",
113
- dirEntry: &memoryDirEntry{
114
- isDir: true,
115
- },
116
- },
117
- {
118
- name: "/sys/class/hwmon/hwmon0",
119
- dirEntry: &memoryDirEntry{
120
- mode: os.ModeSymlink,
121
- },
122
- },
123
- {
124
- name: "/sys/devices/virtual/hwmon/hwmon0",
125
- dirEntry: &memoryDirEntry{
126
- isDir: true,
127
- },
128
- },
129
- {
130
- name: "/sys/devices/virtual/hwmon/hwmon0/name",
131
- contents: "acpitz",
132
- },
133
- {
134
- name: "/sys/devices/virtual/hwmon/hwmon0/temp1_crit",
135
- contents: "105000",
136
- },
137
- {
138
- name: "/sys/devices/virtual/hwmon/hwmon0/temp1_input",
139
- contents: "27800",
140
- },
114
+ {name: "/sys/class/hwmon", de: &memoryDirEntry{isDir: true}},
115
+ {name: "/sys/class/hwmon/hwmon0", de: &memoryDirEntry{mode: os.ModeSymlink}},
116
+ {name: "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0/hwmon0", de: &memoryDirEntry{isDir: true}},
117
+ {name: "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/hwmon/hwmon0/name", err: os.ErrNotExist},
118
+ {name: "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/hwmon/hwmon0/device", de: &memoryDirEntry{}},
119
+ {name: "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0/hwmon0/name", val: "temp_meter"},
120
+
121
+ {name: "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0/hwmon0/temp1_label", val: "some_label"},
122
+ {name: "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0/hwmon0/temp1_alarm", val: "0"},
123
+ {name: "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0/hwmon0/temp1_type", val: "1"},
124
+ {name: "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0/hwmon0/temp1_max", val: "42000"},
125
+ {name: "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0/hwmon0/temp1_min", val: "42000"},
126
+ {name: "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0/hwmon0/temp1_input", val: "42000"},
127
+ {name: "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0/hwmon0/temp1_crit", val: "42000"},
128
+ {name: "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0/hwmon0/temp1_emergency", val: "42000"},
129
+ {name: "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0/hwmon0/temp1_lcrit", val: "42000"},
130
+ {name: "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0/hwmon0/temp1_lowest", val: "42000"},
131
+ {name: "/sys/devices/pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0/hwmon0/temp1_highest", val: "42000"},
132
},
133
},
143
- devices: []*Device{{
144
- Name: "acpitz-00",
145
- Sensors: []Sensor{
146
- &TemperatureSensor{
147
- Name: "temp1",
148
- Input: 27.8,
149
- Critical: 105.0,
150
- CriticalAlarm: false,
151
- },
152
- },
153
- }},
134
},
155
- {
156
- name: "coretemp device",
135
+ "Voltage sensor": {
136
+ wantDevices: []*Chip{
137
+ {
138
+ Name: "voltage_meter",
139
+ UniqueName: "voltage_meter-acpi-b37a4ed3",
140
+ SysDevice: "LNXSYSTM:00/device:00/ACPI0000:00",
141
+ Sensors: Sensors{
142
+ Voltage: []*VoltageSensor{
143
+ {
144
+ Name: "in1",
145
+ Label: "some_label",
146
+ Alarm: ptr(false),
147
+ Input: ptr(42.0),
148
+ Average: ptr(42.0),
149
+ Min: ptr(42.0),
150
+ Max: ptr(42.0),
151
+ CritMin: ptr(42.0),
152
+ CritMax: ptr(42.0),
153
+ Lowest: ptr(42.0),
154
+ Highest: ptr(42.0),
155
+ },
156
+ },
157
+ }},
158
+ },
159
fs: &memoryFilesystem{
160
symlinks: map[string]string{
159
- "/sys/class/hwmon/hwmon1": "../../devices/platform/coretemp.0/hwmon/hwmon1",
160
- "/sys/devices/platform/coretemp.0/hwmon/hwmon1/device": "../../../coretemp.0",
161
+ "/sys/class/hwmon/hwmon0": "../../devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0",
162
+ "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device": "../../../ACPI0000:00",
163
},
164
files: []memoryFile{
163
- {
164
- name: "/sys/class/hwmon",
165
- dirEntry: &memoryDirEntry{
166
- isDir: true,
167
- },
168
- },
169
- {
170
- name: "/sys/class/hwmon/hwmon1",
171
- dirEntry: &memoryDirEntry{
172
- mode: os.ModeSymlink,
173
- },
174
- },
175
- {
176
- name: "/sys/devices/platform/coretemp.0",
177
- dirEntry: &memoryDirEntry{
178
- isDir: true,
179
- },
180
- },
181
- {
182
- name: "/sys/devices/platform/coretemp.0/hwmon/hwmon1/name",
183
- err: os.ErrNotExist,
184
- },
185
- {
186
- name: "/sys/devices/platform/coretemp.0/hwmon/hwmon1/device",
187
- dirEntry: &memoryDirEntry{
188
- // mode: os.ModeSymlink,
189
- },
190
- },
191
- {
192
- name: "/sys/devices/platform/coretemp.0/name",
193
- contents: "coretemp",
194
- },
195
- {
196
- name: "/sys/devices/platform/coretemp.0/temp1_crit",
197
- contents: "100000",
198
- },
199
- {
200
- name: "/sys/devices/platform/coretemp.0/temp1_crit_alarm",
201
- contents: "0",
202
- },
203
- {
204
- name: "/sys/devices/platform/coretemp.0/temp1_input",
205
- contents: "40000",
206
- },
207
- {
208
- name: "/sys/devices/platform/coretemp.0/temp1_label",
209
- contents: "Core 0",
210
- },
211
- {
212
- name: "/sys/devices/platform/coretemp.0/temp1_max",
213
- contents: "80000",
214
- },
215
- {
216
- name: "/sys/devices/platform/coretemp.0/temp2_crit",
217
- contents: "100000",
218
- },
219
- {
220
- name: "/sys/devices/platform/coretemp.0/temp2_crit_alarm",
221
- contents: "0",
222
- },
223
- {
224
- name: "/sys/devices/platform/coretemp.0/temp2_input",
225
- contents: "42000",
226
- },
227
- {
228
- name: "/sys/devices/platform/coretemp.0/temp2_label",
229
- contents: "Core 1",
230
- },
231
- {
232
- name: "/sys/devices/platform/coretemp.0/temp2_max",
233
- contents: "80000",
234
- },
165
+ {name: "/sys/class/hwmon", de: &memoryDirEntry{isDir: true}},
166
+ {name: "/sys/class/hwmon/hwmon0", de: &memoryDirEntry{mode: os.ModeSymlink}},
167
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00", de: &memoryDirEntry{isDir: true}},
168
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/name", err: os.ErrNotExist},
169
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device", de: &memoryDirEntry{}},
170
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/name", val: "voltage_meter"},
171
+
172
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/in1_label", val: "some_label"},
173
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/in1_alarm", val: "0"},
174
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/in1_input", val: "42000"},
175
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/in1_average", val: "42000"},
176
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/in1_min", val: "42000"},
177
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/in1_max", val: "42000"},
178
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/in1_lcrit", val: "42000"},
179
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/in1_crit", val: "42000"},
180
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/in1_lowest", val: "42000"},
181
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/in1_highest", val: "42000"},
182
},
183
},
237
- devices: []*Device{{
238
- Name: "coretemp-00",
239
- Sensors: []Sensor{
240
- &TemperatureSensor{
241
- Name: "temp1",
242
- Label: "Core 0",
243
- Input: 40.0,
244
- Maximum: 80.0,
245
- Critical: 100.0,
246
- CriticalAlarm: false,
247
- },
248
- &TemperatureSensor{
249
- Name: "temp2",
250
- Label: "Core 1",
251
- Input: 42.0,
252
- Maximum: 80.0,
253
- Critical: 100.0,
254
- CriticalAlarm: false,
255
- },
256
- },
257
- }},
184
},
259
- {
260
- name: "it8728 device",
185
+ "Fan sensor": {
186
+ wantDevices: []*Chip{
187
+ {
188
+ Name: "fan_meter",
189
+ UniqueName: "fan_meter-acpi-b37a4ed3",
190
+ SysDevice: "LNXSYSTM:00/device:00/ACPI0000:00",
191
+ Sensors: Sensors{
192
+ Fan: []*FanSensor{
193
+ {
194
+ Name: "fan1",
195
+ Label: "some_label",
196
+ Alarm: ptr(false),
197
+ Input: ptr(42.0),
198
+ Min: ptr(42.0),
199
+ Max: ptr(42.0),
200
+ Target: ptr(42.0),
201
+ },
202
+ },
203
+ }},
204
+ },
205
fs: &memoryFilesystem{
206
symlinks: map[string]string{
263
- "/sys/class/hwmon/hwmon2": "../../devices/platform/it87.2608/hwmon/hwmon2",
264
- "/sys/devices/platform/it87.2608/hwmon/hwmon2/device": "../../../it87.2608",
207
+ "/sys/class/hwmon/hwmon0": "../../devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0",
208
+ "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device": "../../../ACPI0000:00",
209
},
210
files: []memoryFile{
267
- {
268
- name: "/sys/class/hwmon",
269
- dirEntry: &memoryDirEntry{
270
- isDir: true,
271
- },
272
- },
273
- {
274
- name: "/sys/class/hwmon/hwmon2",
275
- dirEntry: &memoryDirEntry{
276
- mode: os.ModeSymlink,
277
- },
278
- },
279
- {
280
- name: "/sys/devices/platform/it87.2608",
281
- dirEntry: &memoryDirEntry{
282
- isDir: true,
283
- },
284
- },
285
- {
286
- name: "/sys/devices/platform/it87.2608/hwmon/hwmon2/name",
287
- err: os.ErrNotExist,
288
- },
289
- {
290
- name: "/sys/devices/platform/it87.2608/hwmon/hwmon2/device",
291
- dirEntry: &memoryDirEntry{
292
- // mode: os.ModeSymlink,
293
- },
294
- },
295
- {
296
- name: "/sys/devices/platform/it87.2608/name",
297
- contents: "it8728",
298
- },
299
- {
300
- name: "/sys/devices/platform/it87.2608/fan1_alarm",
301
- contents: "0",
302
- },
303
- {
304
- name: "/sys/devices/platform/it87.2608/fan1_beep",
305
- contents: "1",
306
- },
307
- {
308
- name: "/sys/devices/platform/it87.2608/fan1_input",
309
- contents: "1010",
310
- },
311
- {
312
- name: "/sys/devices/platform/it87.2608/fan1_min",
313
- contents: "10",
314
- },
315
- {
316
- name: "/sys/devices/platform/it87.2608/in0_alarm",
317
- contents: "0",
318
- },
319
- {
320
- name: "/sys/devices/platform/it87.2608/in0_beep",
321
- contents: "0",
322
- },
323
- {
324
- name: "/sys/devices/platform/it87.2608/in0_input",
325
- contents: "1056",
326
- },
327
- {
328
- name: "/sys/devices/platform/it87.2608/in0_max",
329
- contents: "3060",
330
- },
331
- {
332
- name: "/sys/devices/platform/it87.2608/in1_alarm",
333
- contents: "0",
334
- },
335
- {
336
- name: "/sys/devices/platform/it87.2608/in1_beep",
337
- contents: "0",
338
- },
339
- {
340
- name: "/sys/devices/platform/it87.2608/in1_input",
341
- contents: "3384",
342
- },
343
- {
344
- name: "/sys/devices/platform/it87.2608/in1_label",
345
- contents: "3VSB",
346
- },
347
- {
348
- name: "/sys/devices/platform/it87.2608/in1_max",
349
- contents: "6120",
350
- },
351
- {
352
- name: "/sys/devices/platform/it87.2608/intrusion0_alarm",
353
- contents: "1",
354
- },
355
- {
356
- name: "/sys/devices/platform/it87.2608/temp1_alarm",
357
- contents: "0",
358
- },
359
- {
360
- name: "/sys/devices/platform/it87.2608/temp1_beep",
361
- contents: "1",
362
- },
363
- {
364
- name: "/sys/devices/platform/it87.2608/temp1_input",
365
- contents: "43000",
366
- },
367
- {
368
- name: "/sys/devices/platform/it87.2608/temp1_max",
369
- contents: "127000",
370
- },
371
- {
372
- name: "/sys/devices/platform/it87.2608/temp1_type",
373
- contents: "4",
374
- },
211
+ {name: "/sys/class/hwmon", de: &memoryDirEntry{isDir: true}},
212
+ {name: "/sys/class/hwmon/hwmon0", de: &memoryDirEntry{mode: os.ModeSymlink}},
213
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00", de: &memoryDirEntry{isDir: true}},
214
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/name", err: os.ErrNotExist},
215
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device", de: &memoryDirEntry{}},
216
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/name", val: "fan_meter"},
217
+
218
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/fan1_label", val: "some_label"},
219
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/fan1_alarm", val: "0"},
220
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/fan1_input", val: "42"},
221
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/fan1_min", val: "42"},
222
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/fan1_max", val: "42"},
223
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/fan1_target", val: "42"},
224
},
225
},
377
- devices: []*Device{{
378
- Name: "it8728-00",
379
- Sensors: []Sensor{
380
- &FanSensor{
381
- Name: "fan1",
382
- Alarm: false,
383
- Beep: true,
384
- Input: 1010,
385
- Minimum: 10,
386
- },
387
- &VoltageSensor{
388
- Name: "in0",
389
- Alarm: false,
390
- Beep: false,
391
- Input: 1.056,
392
- Maximum: 3.060,
393
- },
394
- &VoltageSensor{
395
- Name: "in1",
396
- Label: "3VSB",
397
- Alarm: false,
398
- Beep: false,
399
- Input: 3.384,
400
- Maximum: 6.120,
401
- },
402
- &IntrusionSensor{
403
- Name: "intrusion0",
404
- Alarm: true,
405
- },
406
- &TemperatureSensor{
407
- Name: "temp1",
408
- Alarm: false,
409
- Beep: true,
410
- TempType: TemperatureSensorTypeThermistor,
411
- Input: 43.0,
412
- Maximum: 127.0,
413
- },
414
- },
415
- }},
226
},
417
- {
418
- name: "multiple coretemp devices",
227
+ "Energy sensor": {
228
+ wantDevices: []*Chip{
229
+ {
230
+ Name: "energy_meter",
231
+ UniqueName: "energy_meter-acpi-b37a4ed3",
232
+ SysDevice: "LNXSYSTM:00/device:00/ACPI0000:00",
233
+ Sensors: Sensors{
234
+ Energy: []*EnergySensor{
235
+ {
236
+ Name: "energy1",
237
+ Label: "some_label",
238
+ Input: ptr(42.0),
239
+ },
240
+ },
241
+ }},
242
+ },
243
fs: &memoryFilesystem{
244
symlinks: map[string]string{
421
- "/sys/class/hwmon/hwmon1": "../../devices/platform/coretemp.0/hwmon/hwmon1",
422
- "/sys/class/hwmon/hwmon2": "../../devices/platform/coretemp.1/hwmon/hwmon2",
423
- "/sys/devices/platform/coretemp.0/hwmon/hwmon1/device": "../../../coretemp.0",
424
- "/sys/devices/platform/coretemp.1/hwmon/hwmon2/device": "../../../coretemp.1",
245
+ "/sys/class/hwmon/hwmon0": "../../devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0",
246
+ "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device": "../../../ACPI0000:00",
247
},
248
files: []memoryFile{
427
- {
428
- name: "/sys/class/hwmon",
429
- dirEntry: &memoryDirEntry{
430
- isDir: true,
431
- },
432
- },
433
- {
434
- name: "/sys/class/hwmon/hwmon1",
435
- dirEntry: &memoryDirEntry{
436
- mode: os.ModeSymlink,
437
- },
438
- },
439
- {
440
- name: "/sys/class/hwmon/hwmon2",
441
- dirEntry: &memoryDirEntry{
442
- mode: os.ModeSymlink,
443
- },
444
- },
445
- {
446
- name: "/sys/devices/platform/coretemp.0",
447
- dirEntry: &memoryDirEntry{
448
- isDir: true,
449
- },
450
- },
451
- {
452
- name: "/sys/devices/platform/coretemp.0/hwmon/hwmon1/name",
453
- err: os.ErrNotExist,
454
- },
455
- {
456
- name: "/sys/devices/platform/coretemp.0/hwmon/hwmon1/device",
457
- dirEntry: &memoryDirEntry{
458
- // mode: os.ModeSymlink,
459
- },
460
- },
461
- {
462
- name: "/sys/devices/platform/coretemp.0/name",
463
- contents: "coretemp",
464
- },
465
- {
466
- name: "/sys/devices/platform/coretemp.0/temp1_crit",
467
- contents: "100000",
468
- },
469
- {
470
- name: "/sys/devices/platform/coretemp.0/temp1_crit_alarm",
471
- contents: "0",
472
- },
473
- {
474
- name: "/sys/devices/platform/coretemp.0/temp1_input",
475
- contents: "40000",
476
- },
477
- {
478
- name: "/sys/devices/platform/coretemp.0/temp1_label",
479
- contents: "Core 0",
480
- },
481
- {
482
- name: "/sys/devices/platform/coretemp.0/temp1_max",
483
- contents: "80000",
484
- },
485
- {
486
- name: "/sys/devices/platform/coretemp.0/temp2_crit",
487
- contents: "100000",
488
- },
489
- {
490
- name: "/sys/devices/platform/coretemp.0/temp2_crit_alarm",
491
- contents: "0",
492
- },
493
- {
494
- name: "/sys/devices/platform/coretemp.0/temp2_input",
495
- contents: "42000",
496
- },
497
- {
498
- name: "/sys/devices/platform/coretemp.0/temp2_label",
499
- contents: "Core 1",
500
- },
501
- {
502
- name: "/sys/devices/platform/coretemp.0/temp2_max",
503
- contents: "80000",
504
- },
505
- {
506
- name: "/sys/devices/platform/coretemp.1",
507
- dirEntry: &memoryDirEntry{
508
- isDir: true,
509
- },
510
- },
511
- {
512
- name: "/sys/devices/platform/coretemp.1/hwmon/hwmon2/name",
513
- err: os.ErrNotExist,
514
- },
515
- {
516
- name: "/sys/devices/platform/coretemp.1/hwmon/hwmon2/device",
517
- dirEntry: &memoryDirEntry{
518
- // mode: os.ModeSymlink,
519
- },
520
- },
521
- {
522
- name: "/sys/devices/platform/coretemp.1/name",
523
- contents: "coretemp",
524
- },
525
- {
526
- name: "/sys/devices/platform/coretemp.1/temp1_crit",
527
- contents: "100000",
528
- },
529
- {
530
- name: "/sys/devices/platform/coretemp.1/temp1_crit_alarm",
531
- contents: "0",
532
- },
533
- {
534
- name: "/sys/devices/platform/coretemp.1/temp1_input",
535
- contents: "38000",
536
- },
537
- {
538
- name: "/sys/devices/platform/coretemp.1/temp1_label",
539
- contents: "Core 0",
540
- },
541
- {
542
- name: "/sys/devices/platform/coretemp.1/temp1_max",
543
- contents: "80000",
544
- },
545
- {
546
- name: "/sys/devices/platform/coretemp.1/temp2_crit",
547
- contents: "100000",
548
- },
549
- {
550
- name: "/sys/devices/platform/coretemp.1/temp2_crit_alarm",
551
- contents: "0",
552
- },
553
- {
554
- name: "/sys/devices/platform/coretemp.1/temp2_input",
555
- contents: "37000",
556
- },
557
- {
558
- name: "/sys/devices/platform/coretemp.1/temp2_label",
559
- contents: "Core 1",
560
- },
561
- {
562
- name: "/sys/devices/platform/coretemp.1/temp2_max",
563
- contents: "80000",
564
- },
249
+ {name: "/sys/class/hwmon", de: &memoryDirEntry{isDir: true}},
250
+ {name: "/sys/class/hwmon/hwmon0", de: &memoryDirEntry{mode: os.ModeSymlink}},
251
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00", de: &memoryDirEntry{isDir: true}},
252
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/name", err: os.ErrNotExist},
253
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device", de: &memoryDirEntry{}},
254
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/name", val: "energy_meter"},
255
+
256
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/energy1_label", val: "some_label"},
257
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/energy1_alarm", val: "0"},
258
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/energy1_input", val: "42000000"},
259
},
260
},
567
- devices: []*Device{
261
+ },
262
+ "Current sensor": {
263
+ wantDevices: []*Chip{
264
{
569
- Name: "coretemp-00",
570
- Sensors: []Sensor{
571
- &TemperatureSensor{
572
- Name: "temp1",
573
- Label: "Core 0",
574
- Input: 40.0,
575
- Maximum: 80.0,
576
- Critical: 100.0,
577
- CriticalAlarm: false,
578
- },
579
- &TemperatureSensor{
580
- Name: "temp2",
581
- Label: "Core 1",
582
- Input: 42.0,
583
- Maximum: 80.0,
584
- Critical: 100.0,
585
- CriticalAlarm: false,
265
+ Name: "current_meter",
266
+ UniqueName: "current_meter-acpi-b37a4ed3",
267
+ SysDevice: "LNXSYSTM:00/device:00/ACPI0000:00",
268
+ Sensors: Sensors{
269
+ Current: []*CurrentSensor{
270
+ {
271
+ Name: "curr1",
272
+ Label: "some_label",
273
+ Alarm: ptr(false),
274
+ Max: ptr(42.0),
275
+ Min: ptr(42.0),
276
+ CritMin: ptr(42.0),
277
+ CritMax: ptr(42.0),
278
+ Input: ptr(42.0),
279
+ Average: ptr(42.0),
280
+ Lowest: ptr(42.0),
281
+ Highest: ptr(42.0),
282
+ },
283
},
587
- },
284
+ }},
285
+ },
286
+ fs: &memoryFilesystem{
287
+ symlinks: map[string]string{
288
+ "/sys/class/hwmon/hwmon0": "../../devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0",
289
+ "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device": "../../../ACPI0000:00",
290
},
291
+ files: []memoryFile{
292
+ {name: "/sys/class/hwmon", de: &memoryDirEntry{isDir: true}},
293
+ {name: "/sys/class/hwmon/hwmon0", de: &memoryDirEntry{mode: os.ModeSymlink}},
294
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00", de: &memoryDirEntry{isDir: true}},
295
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/name", err: os.ErrNotExist},
296
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device", de: &memoryDirEntry{}},
297
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/name", val: "current_meter"},
298
+
299
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/curr1_label", val: "some_label"},
300
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/curr1_alarm", val: "0"},
301
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/curr1_max", val: "42000"},
302
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/curr1_min", val: "42000"},
303
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/curr1_lcrit", val: "42000"},
304
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/curr1_crit", val: "42000"},
305
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/curr1_input", val: "42000"},
306
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/curr1_average", val: "42000"},
307
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/curr1_lowest", val: "42000"},
308
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/curr1_highest", val: "42000"},
309
+ },
310
+ },
311
+ },
312
+ "Humidity sensor": {
313
+ wantDevices: []*Chip{
314
{
590
- Name: "coretemp-01",
591
- Sensors: []Sensor{
592
- &TemperatureSensor{
593
- Name: "temp1",
594
- Label: "Core 0",
595
- Input: 38.0,
596
- Maximum: 80.0,
597
- Critical: 100.0,
598
- CriticalAlarm: false,
315
+ Name: "humidity_meter",
316
+ UniqueName: "humidity_meter-acpi-b37a4ed3",
317
+ SysDevice: "LNXSYSTM:00/device:00/ACPI0000:00",
318
+ Sensors: Sensors{
319
+ Humidity: []*HumiditySensor{
320
+ {
321
+ Name: "humidity1",
322
+ Label: "some_label",
323
+ Input: ptr(42.0),
324
+ },
325
},
600
- &TemperatureSensor{
601
- Name: "temp2",
602
- Label: "Core 1",
603
- Input: 37.0,
604
- Maximum: 80.0,
605
- Critical: 100.0,
606
- CriticalAlarm: false,
607
- },
608
- },
326
+ }},
327
+ },
328
+ fs: &memoryFilesystem{
329
+ symlinks: map[string]string{
330
+ "/sys/class/hwmon/hwmon0": "../../devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0",
331
+ "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device": "../../../ACPI0000:00",
332
+ },
333
+ files: []memoryFile{
334
+ {name: "/sys/class/hwmon", de: &memoryDirEntry{isDir: true}},
335
+ {name: "/sys/class/hwmon/hwmon0", de: &memoryDirEntry{mode: os.ModeSymlink}},
336
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00", de: &memoryDirEntry{isDir: true}},
337
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/name", err: os.ErrNotExist},
338
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device", de: &memoryDirEntry{}},
339
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/name", val: "humidity_meter"},
340
+
341
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/humidity1_label", val: "some_label"},
342
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/humidity1_alarm", val: "0"},
343
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/humidity1_input", val: "42000"},
344
},
345
},
346
},
612
- {
613
- name: "sfc device",
347
+ "Intrusion sensor": {
348
+ wantDevices: []*Chip{
349
+ {
350
+ Name: "intrusion_meter",
351
+ UniqueName: "intrusion_meter-acpi-b37a4ed3",
352
+ SysDevice: "LNXSYSTM:00/device:00/ACPI0000:00",
353
+ Sensors: Sensors{
354
+ Intrusion: []*IntrusionSensor{
355
+ {
356
+ Name: "intrusion1",
357
+ Label: "some_label",
358
+ Alarm: ptr(false),
359
+ },
360
+ },
361
+ }},
362
+ },
363
fs: &memoryFilesystem{
364
symlinks: map[string]string{
616
- "/sys/class/hwmon/hwmon0": "../../devices/pci0000:00/0000:00:02.0/0000:03:00.0/hwmon/hwmon0",
617
- "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/hwmon/hwmon0/device": "../../../0000:03:00.0",
365
+ "/sys/class/hwmon/hwmon0": "../../devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0",
366
+ "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device": "../../../ACPI0000:00",
367
},
368
files: []memoryFile{
620
- {
621
- name: "/sys/class/hwmon",
622
- dirEntry: &memoryDirEntry{
623
- isDir: true,
624
- },
625
- },
626
- {
627
- name: "/sys/class/hwmon/hwmon0",
628
- dirEntry: &memoryDirEntry{
629
- mode: os.ModeSymlink,
630
- },
631
- },
632
- {
633
- name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0",
634
- dirEntry: &memoryDirEntry{
635
- isDir: true,
636
- },
637
- },
638
- {
639
- name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/hwmon/hwmon0/name",
640
- err: os.ErrNotExist,
641
- },
642
- {
643
- name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/hwmon/hwmon0/device",
644
- dirEntry: &memoryDirEntry{
645
- // mode: os.ModeSymlink,
646
- },
647
- },
648
- {
649
- name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/name",
650
- contents: "sfc",
651
- },
652
- {
653
- name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/curr1_alarm",
654
- contents: "0",
655
- },
656
- {
657
- name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/curr1_crit",
658
- contents: "18000",
659
- },
660
- {
661
- name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/curr1_input",
662
- contents: "7624",
663
- },
664
- {
665
- name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/curr1_label",
666
- contents: "0.9V supply current",
667
- },
668
- {
669
- name: "/sys/devices/pci0000:00/0000:00:02.0/0000:03:00.0/curr1_max",
670
- contents: "16000",
671
- },
369
+ {name: "/sys/class/hwmon", de: &memoryDirEntry{isDir: true}},
370
+ {name: "/sys/class/hwmon/hwmon0", de: &memoryDirEntry{mode: os.ModeSymlink}},
371
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00", de: &memoryDirEntry{isDir: true}},
372
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/name", err: os.ErrNotExist},
373
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/hwmon/hwmon0/device", de: &memoryDirEntry{}},
374
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/name", val: "intrusion_meter"},
375
+
376
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/intrusion1_label", val: "some_label"},
377
+ {name: "/sys/devices/LNXSYSTM:00/device:00/ACPI0000:00/intrusion1_alarm", val: "0"},
378
},
379
},
674
- devices: []*Device{{
675
- Name: "sfc-00",
676
- Sensors: []Sensor{
677
- &CurrentSensor{
678
- Name: "curr1",
679
- Label: "0.9V supply current",
680
- Alarm: false,
681
- Input: 7.624,
682
- Maximum: 16.0,
683
- Critical: 18.0,
684
- },
685
- },
686
- }},
380
},
381
}
382
+ for name, test := range tests {
383
+ t.Run(name, func(t *testing.T) {
384
+ sc := New()
385
+ sc.fs = test.fs
386
690
- for _, tt := range tests {
691
- t.Run(tt.name, func(t *testing.T) {
692
- s := &Scanner{fs: tt.fs}
387
+ devices, err := sc.Scan()
388
+ require.NoError(t, err)
389
694
- devices, err := s.Scan()
695
- if err != nil {
696
- t.Fatalf("unexpected error: %v", err)
390
+ for _, dev := range devices {
391
+ for _, sn := range dev.Sensors.Voltage {
392
+ require.NotZerof(t, sn.ReadTime, "zero read time: [voltage] dev '%s' sensor '%s'", dev.Name, sn.Name)
393
+ sn.ReadTime = 0
394
+ }
395
+ for _, sn := range dev.Sensors.Fan {
396
+ require.NotZerof(t, sn.ReadTime, "zero read time: [fan] dev '%s' sensor '%s'", dev.Name, sn.Name)
397
+ sn.ReadTime = 0
398
+ }
399
+ for _, sn := range dev.Sensors.Temperature {
400
+ require.NotZerof(t, sn.ReadTime, "zero read time: [temp] dev '%s' sensor '%s'", dev.Name, sn.Name)
401
+ sn.ReadTime = 0
402
+ }
403
+ for _, sn := range dev.Sensors.Current {
404
+ require.NotZerof(t, sn.ReadTime, "zero read time: [curr] dev '%s' sensor '%s'", dev.Name, sn.Name)
405
+ sn.ReadTime = 0
406
+ }
407
+ for _, sn := range dev.Sensors.Power {
408
+ require.NotZerof(t, sn.ReadTime, "zero read time: [power] dev '%s' sensor '%s'", dev.Name, sn.Name)
409
+ sn.ReadTime = 0
410
+ }
411
+ for _, sn := range dev.Sensors.Energy {
412
+ require.NotZerof(t, sn.ReadTime, "zero read time: [energy] dev '%s' sensor '%s'", dev.Name, sn.Name)
413
+ sn.ReadTime = 0
414
+ }
415
+ for _, sn := range dev.Sensors.Humidity {
416
+ require.NotZerof(t, sn.ReadTime, "zero read time: [humidity] dev '%s' sensor '%s'", dev.Name, sn.Name)
417
+ sn.ReadTime = 0
418
+ }
419
+ for _, sn := range dev.Sensors.Intrusion {
420
+ require.NotZerof(t, sn.ReadTime, "zero read time: [intrusion] dev '%s' sensor '%s'", dev.Name, sn.Name)
421
+ sn.ReadTime = 0
422
+ }
423
}
424
699
- if want, got := tt.devices, devices; !reflect.DeepEqual(want, got) {
700
- t.Fatalf("unexpected Devices:\n- want:\n%v\n- got:\n%v",
701
- devicesStr(want), devicesStr(got))
702
- }
425
+ require.Equal(t, test.wantDevices, devices)
426
})
427
}
428
}
429
707
-func devicesStr(ds []*Device) string {
708
- var out string
709
- for _, d := range ds {
710
- out += fmt.Sprintf("device: %q [%d sensors]\n", d.Name, len(d.Sensors))
711
-
712
- for _, s := range d.Sensors {
713
- out += fmt.Sprintf(" - sensor: %#v\n", s)
714
- }
715
- }
716
-
717
- return out
718
-}
719
-
720
-var _ filesystem = &memoryFilesystem{}
721
-
722
-// A memoryFilesystem is an in-memory implementation of filesystem, used for
723
-// tests.
430
type memoryFilesystem struct {
431
symlinks map[string]string
432
files []memoryFile
433
}
434
729
-func (fs *memoryFilesystem) ReadFile(filename string) (string, error) {
730
- for _, f := range fs.files {
435
+func (m *memoryFilesystem) ReadDir(name string) ([]fs.DirEntry, error) {
436
+ if !slices.ContainsFunc(m.files, func(file memoryFile) bool { return file.name == name }) {
437
+ return nil, fmt.Errorf("readdir: dir %s not in memory", name)
438
+ }
439
+ var des []fs.DirEntry
440
+ for _, v := range m.files {
441
+ if strings.HasPrefix(v.name, name) {
442
+ des = append(des, &memoryDirEntry{name: filepath.Base(v.name), isDir: false})
443
+ }
444
+ }
445
+ return des, nil
446
+}
447
+
448
+func (m *memoryFilesystem) ReadFile(filename string) (string, error) {
449
+ for _, f := range m.files {
450
if f.name == filename {
732
- return f.contents, nil
451
+ return f.val, nil
452
}
453
}
454
455
return "", fmt.Errorf("readfile: file %q not in memory", filename)
456
}
457
739
-func (fs *memoryFilesystem) Readlink(name string) (string, error) {
740
- if l, ok := fs.symlinks[name]; ok {
458
+func (m *memoryFilesystem) Readlink(name string) (string, error) {
459
+ if l, ok := m.symlinks[name]; ok {
460
return l, nil
461
}
462
463
return "", fmt.Errorf("readlink: symlink %q not in memory", name)
464
}
465
747
-func (fs *memoryFilesystem) Stat(name string) (os.FileInfo, error) {
748
- for _, f := range fs.files {
466
+func (m *memoryFilesystem) Stat(name string) (os.FileInfo, error) {
467
+ for _, f := range m.files {
468
if f.name == name {
750
- de := f.dirEntry
469
+ de := f.de
470
if de == nil {
471
de = &memoryDirEntry{}
472
}
@@ -759,18 +478,17 @@ func (fs *memoryFilesystem) Stat(name string) (os.FileInfo, error) {
478
return nil, fmt.Errorf("stat: file %q not in memory", name)
479
}
480
762
-func (fs *memoryFilesystem) WalkDir(root string, walkFn fs.WalkDirFunc) error {
763
- if _, err := fs.Stat(root); err != nil {
481
+func (m *memoryFilesystem) WalkDir(root string, walkFn fs.WalkDirFunc) error {
482
+ if _, err := m.Stat(root); err != nil {
483
return err
484
}
485
767
- for _, f := range fs.files {
768
- // Only walk paths under the specified root
486
+ for _, f := range m.files {
487
if !strings.HasPrefix(f.name, root) {
488
continue
489
}
490
773
- de := f.dirEntry
491
+ de := f.de
492
if de == nil {
493
de = &memoryDirEntry{}
494
}
@@ -783,17 +501,13 @@ func (fs *memoryFilesystem) WalkDir(root string, walkFn fs.WalkDirFunc) error {
501
return nil
502
}
503
786
-// A memoryFile is an in-memory file used by memoryFilesystem.
504
type memoryFile struct {
788
- name string
789
- contents string
790
- dirEntry fs.DirEntry
791
- err error
505
+ name string
506
+ val string
507
+ de fs.DirEntry
508
+ err error
509
}
510
794
-var _ fs.DirEntry = &memoryDirEntry{}
795
-
796
-// A memoryDirEntry is a fs.DirEntry used by memoryFiles.
511
type memoryDirEntry struct {
512
name string
513
mode os.FileMode
src/go/plugin/go.d/modules/sensors/lmsensors/sensor.go
+160
-75
@@ -1,92 +1,177 @@
1
package lmsensors
2
3
import (
4
- "sort"
5
- "strings"
4
+ "time"
5
)
6
8
-// A Device is a physical or virtual device which may have zero or
9
-// more Sensors.
10
-type Device struct {
11
- // The name of the device.
12
- Name string
7
+// A Chip is a physical or virtual device which may have zero or more Sensors.
8
+type Chip struct {
9
+ Name string
10
+ UniqueName string
11
+ SysDevice string
12
+ Sensors Sensors
13
+}
14
14
- // Any Sensors that belong to this Device. Use type assertions to
15
- // check for specific Sensor types and fetch their data.
16
- Sensors []Sensor
15
+type Sensors struct {
16
+ // https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
17
+ Voltage []*VoltageSensor
18
+ Fan []*FanSensor
19
+ Temperature []*TemperatureSensor
20
+ Current []*CurrentSensor
21
+ Power []*PowerSensor
22
+ Energy []*EnergySensor
23
+ Humidity []*HumiditySensor
24
+ Intrusion []*IntrusionSensor
25
}
26
19
-type SensorType string
27
+// A VoltageSensor is a Sensor that detects voltage.
28
+type VoltageSensor struct {
29
+ ReadTime time.Duration
30
21
-const (
22
- SensorTypeCurrent SensorType = "current"
23
- SensorTypeFan SensorType = "fan"
24
- SensorTypeIntrusion SensorType = "intrusion"
25
- SensorTypePower SensorType = "power"
26
- SensorTypeTemperature SensorType = "temperature"
27
- SensorTypeVoltage SensorType = "voltage"
28
-)
31
+ Name string
32
+ Label string
33
+
34
+ Alarm *bool
35
+
36
+ Input *float64
37
+ Average *float64
38
+
39
+ Lowest *float64
40
+ Highest *float64
41
+
42
+ Min *float64
43
+ Max *float64
44
+ CritMin *float64
45
+ CritMax *float64
46
+}
47
+
48
+// A FanSensor is a Sensor that detects fan speeds in rotations per minute.
49
+type FanSensor struct {
50
+ ReadTime time.Duration
51
+
52
+ Name string
53
+ Label string
54
+
55
+ Alarm *bool
56
30
-// A Sensor is a hardware sensor, used to retrieve device temperatures, fan speeds, voltages, etc.
31
-// Use type assertions to check for specific
32
-// Sensor types and fetch their data.
33
-type Sensor interface {
34
- Type() SensorType
57
+ Input *float64
58
+ Min *float64
59
+ Max *float64
60
+ Target *float64
61
}
62
37
-// parseSensors parses all Sensors from an input raw data slice, produced during a filesystem walk.
38
-func parseSensors(raw map[string]map[string]string) ([]Sensor, error) {
39
- sensors := make([]Sensor, 0, len(raw))
40
- for k, v := range raw {
41
- var sn Sensor
42
- var err error
43
-
44
- switch {
45
- case strings.HasPrefix(k, "curr"):
46
- s := &CurrentSensor{Name: k}
47
- sn = s
48
- err = s.parse(v)
49
- case strings.HasPrefix(k, "intrusion"):
50
- s := &IntrusionSensor{Name: k}
51
- sn = s
52
- err = s.parse(v)
53
- case strings.HasPrefix(k, "in"):
54
- s := &VoltageSensor{Name: k}
55
- sn = s
56
- err = s.parse(v)
57
- case strings.HasPrefix(k, "fan"):
58
- s := &FanSensor{Name: k}
59
- sn = s
60
- err = s.parse(v)
61
- case strings.HasPrefix(k, "power"):
62
- s := &PowerSensor{Name: k}
63
- sn = s
64
- err = s.parse(v)
65
- case strings.HasPrefix(k, "temp"):
66
- s := &TemperatureSensor{Name: k}
67
- sn = s
68
- err = s.parse(v)
69
- default:
70
- continue
71
- }
72
- if err != nil {
73
- return nil, err
74
- }
75
-
76
- if sn == nil {
77
- continue
78
- }
79
-
80
- sensors = append(sensors, sn)
63
+// A TemperatureSensor is a Sensor that detects temperatures in degrees Celsius.
64
+type TemperatureSensor struct {
65
+ ReadTime time.Duration
66
+
67
+ Name string
68
+ Label string
69
+ TempTypeRaw int
70
+
71
+ Alarm *bool
72
+
73
+ Input *float64
74
+ Lowest *float64
75
+ Highest *float64
76
+ Min *float64
77
+ Max *float64
78
+ CritMin *float64
79
+ CritMax *float64
80
+ Emergency *float64
81
+}
82
+
83
+func (s TemperatureSensor) TempType() string {
84
+ switch s.TempTypeRaw {
85
+ case 1:
86
+ return "CPU embedded diode"
87
+ case 2:
88
+ return "3904 transistor"
89
+ case 3:
90
+ return "thermal diode"
91
+ case 4:
92
+ return "thermistor"
93
+ case 5:
94
+ return "AMD AMDSI"
95
+ case 6:
96
+ return "Intel PECI"
97
+ default:
98
+ return ""
99
}
100
+}
101
+
102
+// A CurrentSensor is a Sensor that detects current in Amperes.
103
+type CurrentSensor struct {
104
+ ReadTime time.Duration
105
+
106
+ Name string
107
+ Label string
108
+
109
+ Alarm *bool
110
+
111
+ Input *float64
112
+ Lowest *float64
113
+ Highest *float64
114
+ Min *float64
115
+ Max *float64
116
+ CritMin *float64
117
+ CritMax *float64
118
+
119
+ Average *float64
120
+}
121
+
122
+// A PowerSensor is a Sensor that detects average electrical power consumption in watts.
123
+type PowerSensor struct {
124
+ ReadTime time.Duration
125
+
126
+ Name string
127
+ Label string
128
+
129
+ Alarm *bool
130
+
131
+ Input *float64
132
+ InputLowest *float64
133
+ InputHighest *float64
134
+ Cap *float64
135
+ CapMin *float64
136
+ CapMax *float64
137
+ Max *float64
138
+ CritMax *float64
139
+
140
+ Average *float64
141
+ AverageMin *float64
142
+ AverageMax *float64
143
+ AverageLowest *float64
144
+ AverageHighest *float64
145
+
146
+ Accuracy *float64
147
+}
148
+
149
+// A EnergySensor is a Sensor that detects energy in microJoule.
150
+type EnergySensor struct {
151
+ ReadTime time.Duration
152
+
153
+ Name string
154
+ Label string
155
+
156
+ Input *float64
157
+}
158
+
159
+// A HumiditySensor is a Sensor that detects humidity in milli-percent.
160
+type HumiditySensor struct {
161
+ ReadTime time.Duration
162
+
163
+ Name string
164
+ Label string
165
+
166
+ Input *float64
167
+}
168
83
- type namer interface{ name() string }
169
+// An IntrusionSensor is a Sensor that detects when the machine's chassis has been opened.
170
+type IntrusionSensor struct {
171
+ ReadTime time.Duration
172
85
- sort.Slice(sensors, func(i, j int) bool {
86
- v1, ok1 := sensors[i].(namer)
87
- v2, ok2 := sensors[j].(namer)
88
- return ok1 && ok2 && v1.name() < v2.name()
89
- })
173
+ Name string
174
+ Label string
175
91
- return sensors, nil
176
+ Alarm *bool
177
}
src/go/plugin/go.d/modules/sensors/lmsensors/temperaturesensor.go
deleted
-108
@@ -1,108 +0,0 @@
1
-package lmsensors
2
-
3
-import (
4
- "strconv"
5
-)
6
-
7
-// A TemperatureSensorType is value that indicates the type of TemperatureSensor.
8
-type TemperatureSensorType int
9
-
10
-// All possible TemperatureSensorType constants.
11
-const (
12
- TemperatureSensorUnknown TemperatureSensorType = 0
13
- TemperatureSensorTypePIICeleronDiode TemperatureSensorType = 1
14
- TemperatureSensorType3904Transistor TemperatureSensorType = 2
15
- TemperatureSensorTypeThermalDiode TemperatureSensorType = 3
16
- TemperatureSensorTypeThermistor TemperatureSensorType = 4
17
- TemperatureSensorTypeAMDAMDSI TemperatureSensorType = 5
18
- TemperatureSensorTypeIntelPECI TemperatureSensorType = 6
19
-)
20
-
21
-var _ Sensor = &TemperatureSensor{}
22
-
23
-// A TemperatureSensor is a Sensor that detects temperatures in degrees
24
-// Celsius.
25
-type TemperatureSensor struct {
26
- // The name of the sensor.
27
- Name string
28
-
29
- // A label that describes what the sensor is monitoring. Label may be empty.
30
- Label string
31
-
32
- // Whether the sensor has an alarm triggered.
33
- Alarm bool
34
-
35
- // Whether the sensor will sound an audible alarm if an alarm
36
- // is triggered.
37
- Beep bool
38
-
39
- // The type of sensor used to report temperatures.
40
- TempType TemperatureSensorType
41
-
42
- // The input temperature, in degrees Celsius, indicated by the sensor.
43
- Input float64
44
-
45
- // A low threshold temperature, in degrees Celsius, indicated by the sensor.
46
- Minimum float64
47
-
48
- // A high threshold temperature, in degrees Celsius, indicated by the sensor.
49
- Maximum float64
50
-
51
- // A critical threshold temperature, in degrees Celsius, indicated by the sensor.
52
- Critical float64
53
-
54
- // An emergency threshold temperature, in degrees Celsius, indicated by the sensor.
55
- Emergency float64
56
-
57
- // Whether the temperature is past the critical threshold.
58
- CriticalAlarm bool
59
-}
60
-
61
-func (s *TemperatureSensor) Type() SensorType { return SensorTypeTemperature }
62
-
63
-func (s *TemperatureSensor) parse(raw map[string]string) error {
64
- for k, v := range raw {
65
- switch k {
66
- case "input", "min", "max", "crit", "emergency":
67
- f, err := strconv.ParseFloat(v, 64)
68
- if err != nil {
69
- return err
70
- }
71
-
72
- // Raw temperature values are scaled by 1000
73
- f /= 1000
74
-
75
- switch k {
76
- case "input":
77
- s.Input = f
78
- case "min":
79
- s.Minimum = f
80
- case "max":
81
- s.Maximum = f
82
- case "crit":
83
- s.Critical = f
84
- case "emergency":
85
- s.Emergency = f
86
- }
87
- case "alarm":
88
- s.Alarm = v != "0"
89
- case "beep":
90
- s.Beep = v != "0"
91
- case "type":
92
- t, err := strconv.Atoi(v)
93
- if err != nil {
94
- return err
95
- }
96
-
97
- s.TempType = TemperatureSensorType(t)
98
- case "crit_alarm":
99
- s.CriticalAlarm = v != "0"
100
- case "label":
101
- s.Label = v
102
- }
103
- }
104
-
105
- return nil
106
-}
107
-
108
-func (s *TemperatureSensor) name() string { return s.Name }
src/go/plugin/go.d/modules/sensors/lmsensors/voltagesensor.go
deleted
-68
@@ -1,68 +0,0 @@
1
-package lmsensors
2
-
3
-import (
4
- "strconv"
5
-)
6
-
7
-var _ Sensor = &VoltageSensor{}
8
-
9
-// A VoltageSensor is a Sensor that detects voltage.
10
-type VoltageSensor struct {
11
- // The name of the sensor.
12
- Name string
13
-
14
- // A label that describes what the sensor is monitoring. Label may be empty.
15
- Label string
16
-
17
- // Whether the sensor has an alarm triggered.
18
- Alarm bool
19
-
20
- // Whether the sensor will sound an audible alarm when an alarm
21
- // is triggered.
22
- Beep bool
23
-
24
- // The input voltage indicated by the sensor.
25
- Input float64
26
-
27
- // The minimum voltage threshold indicated by the sensor.
28
- Min float64
29
-
30
- // The maximum voltage threshold indicated by the sensor.
31
- Maximum float64
32
-}
33
-
34
-func (s *VoltageSensor) Type() SensorType { return SensorTypeVoltage }
35
-
36
-func (s *VoltageSensor) parse(raw map[string]string) error {
37
- for k, v := range raw {
38
- switch k {
39
- case "input", "min", "max":
40
- f, err := strconv.ParseFloat(v, 64)
41
- if err != nil {
42
- return err
43
- }
44
-
45
- // Raw temperature values are scaled by 1000
46
- f /= 1000
47
-
48
- switch k {
49
- case "input":
50
- s.Input = f
51
- case "min":
52
- s.Min = f
53
- case "max":
54
- s.Maximum = f
55
- }
56
- case "alarm":
57
- s.Alarm = v != "0"
58
- case "beep":
59
- s.Beep = v != "0"
60
- case "label":
61
- s.Label = v
62
- }
63
- }
64
-
65
- return nil
66
-}
67
-
68
-func (s *VoltageSensor) name() string { return s.Name }
src/go/plugin/go.d/modules/sensors/metadata.yaml
+97
-51
@@ -5,7 +5,7 @@ modules:
5
plugin_name: go.d.plugin
6
module_name: sensors
7
monitored_instance:
8
- name: Linux Sensors (lm-sensors)
8
+ name: Linux Sensors
9
link: https://hwmon.wiki.kernel.org/lm_sensors
10
icon_filename: "microchip.svg"
11
categories:
@@ -19,6 +19,7 @@ modules:
19
- fan
20
- energy
21
- humidity
22
+ - intrusion
23
related_resources:
24
integrations:
25
list: []
@@ -30,7 +31,7 @@ modules:
31
metrics_description: >
32
This collector gathers real-time system sensor statistics,
33
including temperature, voltage, current, power, fan speed, energy consumption, and humidity,
33
- utilizing the [sensors](https://linux.die.net/man/1/sensors) binary or [sysfs](https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface).
34
+ utilizing the [sysfs](https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface) interface.
35
method_description: ""
36
supported_platforms:
37
include: []
@@ -44,12 +45,13 @@ modules:
45
The following type of sensors are auto-detected:
46
47
- temperature
47
- - fan
48
- voltage
49
+ - fan
50
- current
51
- power
52
- energy
53
- humidity
54
+ - intrusion
55
limits:
56
description: ""
57
performance_impact:
@@ -71,31 +73,17 @@ modules:
73
description: Data collection frequency.
74
default_value: 10
75
required: false
74
- - name: binary_path
75
- description: Path to the `sensors` binary. If left empty or if the binary is not found, [sysfs](https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface) will be used to collect sensor statistics.
76
- default_value: /usr/bin/sensors
77
- required: true
78
- - name: timeout
79
- description: Timeout for executing the binary, specified in seconds.
80
- default_value: 2
81
- required: false
76
examples:
77
folding:
78
title: Config
79
enabled: true
80
list:
87
- - name: Custom binary path
88
- description: The executable is not in the directories specified in the PATH environment variable.
81
+ - name: Custom update_every
82
+ description: Allows you to override the default data collection interval.
83
config: |
84
jobs:
85
- name: sensors
92
- binary_path: /usr/local/sbin/sensors
93
- - name: Use sysfs instead of sensors
94
- description: Set `binary_path` to an empty string to use sysfs.
95
- config: |
96
- jobs:
97
- - name: sensors
98
- binary_path: ""
86
+ update_every: 5 # Collect sensors statistics every 5 seconds
87
troubleshooting:
88
problems:
89
list: []
@@ -108,59 +96,117 @@ modules:
96
availability: []
97
scopes:
98
- name: sensor
111
- description: These metrics refer to the sensor.
99
+ description: These metrics refer to the system sensor.
100
labels:
101
- name: chip
114
- description: The hardware component responsible for the sensor monitoring.
115
- - name: feature
116
- description: The specific sensor or monitoring point provided by the chip.
102
+ description: The path to the sensor's chip device, excluding the /sys/devices prefix. This provides a unique identifier for the physical hardware component.
103
+ - name: chip_id
104
+ description: A unique identifier for the sensor's chip, formatted as `chipName-busType-hash`.
105
+ - name: sensor
106
+ description: The name of the specific sensor within the chip device. This provides a direct identifier for the individual measurement point.
107
+ - name: label
108
+ description: A label provided by the kernel driver to indicate the intended use or purpose of the sensor.
109
+
110
metrics:
118
- - name: sensors.sensor_temperature
119
- description: Sensor temperature
111
+ - name: sensors.chip_sensor_temperature
112
+ description: Sensor Temperature
113
unit: Celsius
114
chart_type: line
115
dimensions:
123
- - name: temperature
124
- - name: sensors.sensor_voltage
125
- description: Sensor voltage
116
+ - name: input
117
+ - name: sensors.chip_sensor_temperature_alarm
118
+ description: Temperature Sensor Alarm
119
+ unit: status
120
+ chart_type: line
121
+ dimensions:
122
+ - name: clear
123
+ - name: triggered
124
+ - name: sensors.chip_sensor_voltage
125
+ description: Sensor Voltage
126
+ unit: Volts
127
+ chart_type: line
128
+ dimensions:
129
+ - name: input
130
+ - name: sensors.chip_sensor_voltage_average
131
+ description: Sensor Voltage Average
132
unit: Volts
133
chart_type: line
134
dimensions:
129
- - name: voltage
130
- - name: sensors.sensor_current
131
- description: Sensor current
135
+ - name: average
136
+ - name: sensors.chip_sensor_voltage_alarm
137
+ description: Voltage Sensor Alarm
138
+ unit: status
139
+ chart_type: line
140
+ dimensions:
141
+ - name: clear
142
+ - name: triggered
143
+ - name: sensors.chip_sensor_fan
144
+ description: Sensor Fan
145
+ unit: RPM
146
+ chart_type: line
147
+ dimensions:
148
+ - name: input
149
+ - name: sensors.chip_sensor_fan_alarm
150
+ description: Fan Sensor Alarm
151
+ unit: status
152
+ chart_type: line
153
+ dimensions:
154
+ - name: clear
155
+ - name: triggered
156
+ - name: sensors.chip_sensor_current
157
+ description: Sensor Current
158
unit: Amperes
159
chart_type: line
160
dimensions:
135
- - name: current
136
- - name: sensors.sensor_power
137
- description: Sensor power
161
+ - name: input
162
+ - name: sensors.chip_sensor_current_average
163
+ description: Sensor Current Average
164
+ unit: Amperes
165
+ chart_type: line
166
+ dimensions:
167
+ - name: average
168
+ - name: sensors.chip_sensor_current_alarm
169
+ description: Current Sensor Alarm
170
+ unit: status
171
+ chart_type: line
172
+ dimensions:
173
+ - name: clear
174
+ - name: triggered
175
+ - name: sensors.chip_sensor_power
176
+ description: Sensor Power
177
unit: Watts
178
chart_type: line
179
dimensions:
141
- - name: power
142
- - name: sensors.sensor_fan_speed
143
- description: Sensor fan speed
144
- unit: RPM
180
+ - name: input
181
+ - name: sensors.chip_sensor_power_average
182
+ description: Sensor Power Average
183
+ unit: Watts
184
chart_type: line
185
dimensions:
147
- - name: fan
148
- - name: sensors.sensor_energy
149
- description: Sensor energy
186
+ - name: average
187
+ - name: sensors.chip_sensor_power_alarm
188
+ description: Power Sensor Alarm
189
+ unit: status
190
+ chart_type: line
191
+ dimensions:
192
+ - name: clear
193
+ - name: triggered
194
+ - name: sensors.chip_sensor_energy
195
+ description: Sensor Energy
196
unit: Joules
197
chart_type: line
198
dimensions:
153
- - name: energy
154
- - name: sensors.sensor_humidity
155
- description: Sensor humidity
199
+ - name: input
200
+ - name: sensors.chip_sensor_humidity
201
+ description: Sensor Humidity
202
unit: percent
157
- chart_type: area
203
+ chart_type: line
204
dimensions:
159
- - name: humidity
160
- - name: sensors.sensor_intrusion
161
- description: Sensor intrusion
205
+ - name: input
206
+ - name: sensors.chip_sensor_intrusion_alarm
207
+ description: Intrusion Sensor Alarm
208
unit: status
209
chart_type: line
210
dimensions:
165
- - name: alarm_clear
166
- - name: alarm_triggered
211
+ - name: clear
212
+ - name: triggered
src/go/plugin/go.d/modules/sensors/sensors.go
+7
-24
@@ -5,11 +5,9 @@ package sensors
5
import (
6
_ "embed"
7
"errors"
8
- "time"
8
9
"github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
10
"github.com/netdata/netdata/go/plugins/plugin/go.d/modules/sensors/lmsensors"
12
- "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/confopt"
11
)
12
13
//go:embed "config_schema.json"
@@ -28,19 +26,14 @@ func init() {
26
27
func New() *Sensors {
28
return &Sensors{
31
- Config: Config{
32
- BinaryPath: "/usr/bin/sensors",
33
- Timeout: confopt.Duration(time.Second * 2),
34
- },
35
- charts: &module.Charts{},
36
- sensors: make(map[string]bool),
29
+ Config: Config{},
30
+ charts: &module.Charts{},
31
+ seenSensors: make(map[string]bool),
32
}
33
}
34
35
type Config struct {
41
- UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
42
- Timeout confopt.Duration `yaml:"timeout,omitempty" json:"timeout"`
43
- BinaryPath string `yaml:"binary_path" json:"binary_path"`
36
+ UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
37
}
38
39
type (
@@ -50,16 +43,12 @@ type (
43
44
charts *module.Charts
45
53
- exec sensorsBinary
54
- sc sysfsScanner
46
+ sc sysfsScanner
47
56
- sensors map[string]bool
57
- }
58
- sensorsBinary interface {
59
- sensorsInfo() ([]byte, error)
48
+ seenSensors map[string]bool
49
}
50
sysfsScanner interface {
62
- Scan() ([]*lmsensors.Device, error)
51
+ Scan() ([]*lmsensors.Chip, error)
52
}
53
)
54
@@ -68,12 +57,6 @@ func (s *Sensors) Configuration() any {
57
}
58
59
func (s *Sensors) Init() error {
71
- if sb, err := s.initSensorsBinary(); err != nil {
72
- s.Infof("sensors exec initialization: %v", err)
73
- } else if sb != nil {
74
- s.exec = sb
75
- }
76
-
60
sc := lmsensors.New()
61
sc.Logger = s.Logger
62
s.sc = sc
src/go/plugin/go.d/modules/sensors/sensors_test.go
+322
-290
@@ -17,16 +17,12 @@ import (
17
var (
18
dataConfigJSON, _ = os.ReadFile("testdata/config.json")
19
dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
20
-
21
- dataSensors, _ = os.ReadFile("testdata/sensors.txt")
20
)
21
22
func Test_testDataIsValid(t *testing.T) {
23
for name, data := range map[string][]byte{
24
"dataConfigJSON": dataConfigJSON,
25
"dataConfigYAML": dataConfigYAML,
28
-
29
- "dataSensors": dataSensors,
26
} {
27
require.NotNil(t, data, name)
28
@@ -42,17 +38,9 @@ func TestSensors_Init(t *testing.T) {
38
config Config
39
wantFail bool
40
}{
45
- "success if 'binary_path' is not set": {
46
- wantFail: false,
47
- config: Config{
48
- BinaryPath: "",
49
- },
50
- },
51
- "success if failed to find binary": {
41
+ "success with default config": {
42
wantFail: false,
53
- config: Config{
54
- BinaryPath: "sensors!!!",
55
- },
43
+ config: New().Config,
44
},
45
}
46
@@ -82,7 +70,7 @@ func TestSensors_Cleanup(t *testing.T) {
70
"after check": {
71
prepare: func() *Sensors {
72
sensors := New()
85
- sensors.exec = prepareMockExecOk()
73
+ sensors.sc = prepareMockScannerOk()
74
_ = sensors.Check()
75
return sensors
76
},
@@ -90,7 +78,7 @@ func TestSensors_Cleanup(t *testing.T) {
78
"after collect": {
79
prepare: func() *Sensors {
80
sensors := New()
93
- sensors.exec = prepareMockExecOk()
81
+ sensors.sc = prepareMockScannerOk()
82
_ = sensors.Collect()
83
return sensors
84
},
@@ -112,32 +100,23 @@ func TestSensors_Charts(t *testing.T) {
100
101
func TestSensors_Check(t *testing.T) {
102
tests := map[string]struct {
115
- prepareMock func() *mockSensorsBinary
103
+ prepareMock func() *mockScanner
104
wantFail bool
105
}{
118
- "exec: only temperature": {
106
+ "multiple sensors": {
107
wantFail: false,
120
- prepareMock: prepareMockExecOk,
121
- },
122
- "exec: error on sensors info call": {
123
- wantFail: true,
124
- prepareMock: prepareMockExecErr,
108
+ prepareMock: prepareMockScannerOk,
109
},
126
- "exec: empty response": {
110
+ "error on scan": {
111
wantFail: true,
128
- prepareMock: prepareMockExecEmptyResponse,
129
- },
130
- "exec: unexpected response": {
131
- wantFail: true,
132
- prepareMock: prepareMockExecUnexpectedResponse,
112
+ prepareMock: prepareMockScannerErr,
113
},
114
}
115
116
for name, test := range tests {
117
t.Run(name, func(t *testing.T) {
118
sensors := New()
139
- mock := test.prepareMock()
140
- sensors.exec = mock
119
+ sensors.sc = test.prepareMock()
120
121
if test.wantFail {
122
assert.Error(t, sensors.Check())
@@ -150,99 +129,141 @@ func TestSensors_Check(t *testing.T) {
129
130
func TestSensors_Collect(t *testing.T) {
131
tests := map[string]struct {
153
- prepareExecMock func() *mockSensorsBinary
154
- prepareSysfsMock func() *mockSysfsScanner
155
- wantMetrics map[string]int64
156
- wantCharts int
132
+ prepareScanner func() *mockScanner
133
+ wantMetrics map[string]int64
134
+ wantCharts int
135
}{
158
- "exec: multiple sensors": {
159
- prepareExecMock: prepareMockExecOk,
160
- wantCharts: 22,
136
+ "multiple sensors": {
137
+ prepareScanner: prepareMockScannerOk,
138
+ wantCharts: 24,
139
wantMetrics: map[string]int64{
162
- "sensor_chip_acpitz-acpi-0_feature_temp1_subfeature_temp1_input": 88000,
163
- "sensor_chip_amdgpu-pci-0300_feature_edge_subfeature_temp1_input": 53000,
164
- "sensor_chip_amdgpu-pci-0300_feature_fan1_subfeature_fan1_input": 0,
165
- "sensor_chip_amdgpu-pci-0300_feature_junction_subfeature_temp2_input": 58000,
166
- "sensor_chip_amdgpu-pci-0300_feature_mem_subfeature_temp3_input": 57000,
167
- "sensor_chip_amdgpu-pci-0300_feature_ppt_subfeature_power1_average": 29000,
168
- "sensor_chip_amdgpu-pci-0300_feature_vddgfx_subfeature_in0_input": 787,
169
- "sensor_chip_amdgpu-pci-6700_feature_edge_subfeature_temp1_input": 60000,
170
- "sensor_chip_amdgpu-pci-6700_feature_ppt_subfeature_power1_average": 5088,
171
- "sensor_chip_amdgpu-pci-6700_feature_vddgfx_subfeature_in0_input": 1335,
172
- "sensor_chip_amdgpu-pci-6700_feature_vddnb_subfeature_in1_input": 973,
173
- "sensor_chip_asus-isa-0000_feature_cpu_fan_subfeature_fan1_input": 5700000,
174
- "sensor_chip_asus-isa-0000_feature_gpu_fan_subfeature_fan2_input": 6600000,
175
- "sensor_chip_bat0-acpi-0_feature_in0_subfeature_in0_input": 17365,
176
- "sensor_chip_k10temp-pci-00c3_feature_tctl_subfeature_temp1_input": 90000,
177
- "sensor_chip_nct6779-isa-0290_feature_intrusion0_subfeature_intrusion0_alarm_clear": 0,
178
- "sensor_chip_nct6779-isa-0290_feature_intrusion0_subfeature_intrusion0_alarm_triggered": 1,
179
- "sensor_chip_nct6779-isa-0290_feature_intrusion1_subfeature_intrusion1_alarm_clear": 0,
180
- "sensor_chip_nct6779-isa-0290_feature_intrusion1_subfeature_intrusion1_alarm_triggered": 1,
181
- "sensor_chip_nvme-pci-0600_feature_composite_subfeature_temp1_input": 33850,
182
- "sensor_chip_nvme-pci-0600_feature_sensor_1_subfeature_temp2_input": 48850,
183
- "sensor_chip_nvme-pci-0600_feature_sensor_2_subfeature_temp3_input": 33850,
184
- "sensor_chip_ucsi_source_psy_usbc000:001-isa-0000_feature_curr1_subfeature_curr1_input": 0,
185
- "sensor_chip_ucsi_source_psy_usbc000:001-isa-0000_feature_in0_subfeature_in0_input": 0,
140
+ "chip_chip0-pci-xxxxxxxx_sensor_curr1_alarm_clear": 1,
141
+ "chip_chip0-pci-xxxxxxxx_sensor_curr1_alarm_triggered": 0,
142
+ "chip_chip0-pci-xxxxxxxx_sensor_curr1_average": 42000,
143
+ "chip_chip0-pci-xxxxxxxx_sensor_curr1_crit": 42000,
144
+ "chip_chip0-pci-xxxxxxxx_sensor_curr1_highest": 42000,
145
+ "chip_chip0-pci-xxxxxxxx_sensor_curr1_input": 42000,
146
+ "chip_chip0-pci-xxxxxxxx_sensor_curr1_lcrit": 42000,
147
+ "chip_chip0-pci-xxxxxxxx_sensor_curr1_lowest": 42000,
148
+ "chip_chip0-pci-xxxxxxxx_sensor_curr1_max": 42000,
149
+ "chip_chip0-pci-xxxxxxxx_sensor_curr1_min": 42000,
150
+ "chip_chip0-pci-xxxxxxxx_sensor_curr1_read_time": 0,
151
+ "chip_chip0-pci-xxxxxxxx_sensor_curr2_crit": 42000,
152
+ "chip_chip0-pci-xxxxxxxx_sensor_curr2_highest": 42000,
153
+ "chip_chip0-pci-xxxxxxxx_sensor_curr2_input": 42000,
154
+ "chip_chip0-pci-xxxxxxxx_sensor_curr2_lcrit": 42000,
155
+ "chip_chip0-pci-xxxxxxxx_sensor_curr2_lowest": 42000,
156
+ "chip_chip0-pci-xxxxxxxx_sensor_curr2_max": 42000,
157
+ "chip_chip0-pci-xxxxxxxx_sensor_curr2_min": 42000,
158
+ "chip_chip0-pci-xxxxxxxx_sensor_curr2_read_time": 0,
159
+ "chip_chip0-pci-xxxxxxxx_sensor_energy1_input": 42000,
160
+ "chip_chip0-pci-xxxxxxxx_sensor_energy1_read_time": 0,
161
+ "chip_chip0-pci-xxxxxxxx_sensor_energy2_input": 42000,
162
+ "chip_chip0-pci-xxxxxxxx_sensor_energy2_read_time": 0,
163
+ "chip_chip0-pci-xxxxxxxx_sensor_fan1_alarm_clear": 1,
164
+ "chip_chip0-pci-xxxxxxxx_sensor_fan1_alarm_triggered": 0,
165
+ "chip_chip0-pci-xxxxxxxx_sensor_fan1_input": 42000,
166
+ "chip_chip0-pci-xxxxxxxx_sensor_fan1_max": 42000,
167
+ "chip_chip0-pci-xxxxxxxx_sensor_fan1_min": 42000,
168
+ "chip_chip0-pci-xxxxxxxx_sensor_fan1_read_time": 0,
169
+ "chip_chip0-pci-xxxxxxxx_sensor_fan1_target": 42000,
170
+ "chip_chip0-pci-xxxxxxxx_sensor_fan2_input": 42000,
171
+ "chip_chip0-pci-xxxxxxxx_sensor_fan2_max": 42000,
172
+ "chip_chip0-pci-xxxxxxxx_sensor_fan2_min": 42000,
173
+ "chip_chip0-pci-xxxxxxxx_sensor_fan2_read_time": 0,
174
+ "chip_chip0-pci-xxxxxxxx_sensor_fan2_target": 42000,
175
+ "chip_chip0-pci-xxxxxxxx_sensor_humidity1_input": 42000,
176
+ "chip_chip0-pci-xxxxxxxx_sensor_humidity1_read_time": 0,
177
+ "chip_chip0-pci-xxxxxxxx_sensor_humidity2_input": 42000,
178
+ "chip_chip0-pci-xxxxxxxx_sensor_humidity2_read_time": 0,
179
+ "chip_chip0-pci-xxxxxxxx_sensor_in1_alarm_clear": 1,
180
+ "chip_chip0-pci-xxxxxxxx_sensor_in1_alarm_triggered": 0,
181
+ "chip_chip0-pci-xxxxxxxx_sensor_in1_average": 42000,
182
+ "chip_chip0-pci-xxxxxxxx_sensor_in1_crit": 42000,
183
+ "chip_chip0-pci-xxxxxxxx_sensor_in1_highest": 42000,
184
+ "chip_chip0-pci-xxxxxxxx_sensor_in1_input": 42000,
185
+ "chip_chip0-pci-xxxxxxxx_sensor_in1_lcrit": 42000,
186
+ "chip_chip0-pci-xxxxxxxx_sensor_in1_lowest": 42000,
187
+ "chip_chip0-pci-xxxxxxxx_sensor_in1_max": 42000,
188
+ "chip_chip0-pci-xxxxxxxx_sensor_in1_min": 42000,
189
+ "chip_chip0-pci-xxxxxxxx_sensor_in1_read_time": 0,
190
+ "chip_chip0-pci-xxxxxxxx_sensor_in2_crit": 42000,
191
+ "chip_chip0-pci-xxxxxxxx_sensor_in2_highest": 42000,
192
+ "chip_chip0-pci-xxxxxxxx_sensor_in2_input": 42000,
193
+ "chip_chip0-pci-xxxxxxxx_sensor_in2_lcrit": 42000,
194
+ "chip_chip0-pci-xxxxxxxx_sensor_in2_lowest": 42000,
195
+ "chip_chip0-pci-xxxxxxxx_sensor_in2_max": 42000,
196
+ "chip_chip0-pci-xxxxxxxx_sensor_in2_min": 42000,
197
+ "chip_chip0-pci-xxxxxxxx_sensor_in2_read_time": 0,
198
+ "chip_chip0-pci-xxxxxxxx_sensor_intrusion1_alarm_clear": 1,
199
+ "chip_chip0-pci-xxxxxxxx_sensor_intrusion1_alarm_triggered": 0,
200
+ "chip_chip0-pci-xxxxxxxx_sensor_intrusion1_read_time": 0,
201
+ "chip_chip0-pci-xxxxxxxx_sensor_intrusion2_alarm_clear": 0,
202
+ "chip_chip0-pci-xxxxxxxx_sensor_intrusion2_alarm_triggered": 1,
203
+ "chip_chip0-pci-xxxxxxxx_sensor_intrusion2_read_time": 0,
204
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_accuracy": 34500,
205
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_alarm_clear": 1,
206
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_alarm_triggered": 0,
207
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_average": 345000,
208
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_average_highest": 345000,
209
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_average_lowest": 345000,
210
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_average_max": 345000,
211
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_average_min": 345000,
212
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_cap": 345000,
213
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_cap_max": 345000,
214
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_cap_min": 345000,
215
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_crit": 345000,
216
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_input": 345000,
217
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_input_highest": 345000,
218
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_input_lowest": 345000,
219
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_max": 345000,
220
+ "chip_chip0-pci-xxxxxxxx_sensor_power1_read_time": 0,
221
+ "chip_chip0-pci-xxxxxxxx_sensor_power2_accuracy": 34500,
222
+ "chip_chip0-pci-xxxxxxxx_sensor_power2_average_highest": 345000,
223
+ "chip_chip0-pci-xxxxxxxx_sensor_power2_average_lowest": 345000,
224
+ "chip_chip0-pci-xxxxxxxx_sensor_power2_average_max": 345000,
225
+ "chip_chip0-pci-xxxxxxxx_sensor_power2_average_min": 345000,
226
+ "chip_chip0-pci-xxxxxxxx_sensor_power2_cap": 345000,
227
+ "chip_chip0-pci-xxxxxxxx_sensor_power2_cap_max": 345000,
228
+ "chip_chip0-pci-xxxxxxxx_sensor_power2_cap_min": 345000,
229
+ "chip_chip0-pci-xxxxxxxx_sensor_power2_crit": 345000,
230
+ "chip_chip0-pci-xxxxxxxx_sensor_power2_input": 345000,
231
+ "chip_chip0-pci-xxxxxxxx_sensor_power2_input_highest": 345000,
232
+ "chip_chip0-pci-xxxxxxxx_sensor_power2_input_lowest": 345000,
233
+ "chip_chip0-pci-xxxxxxxx_sensor_power2_max": 345000,
234
+ "chip_chip0-pci-xxxxxxxx_sensor_power2_read_time": 0,
235
+ "chip_chip0-pci-xxxxxxxx_sensor_temp1_alarm_clear": 1,
236
+ "chip_chip0-pci-xxxxxxxx_sensor_temp1_alarm_triggered": 0,
237
+ "chip_chip0-pci-xxxxxxxx_sensor_temp1_crit": 42000,
238
+ "chip_chip0-pci-xxxxxxxx_sensor_temp1_emergency": 42000,
239
+ "chip_chip0-pci-xxxxxxxx_sensor_temp1_highest": 42000,
240
+ "chip_chip0-pci-xxxxxxxx_sensor_temp1_input": 42000,
241
+ "chip_chip0-pci-xxxxxxxx_sensor_temp1_lcrit": 42000,
242
+ "chip_chip0-pci-xxxxxxxx_sensor_temp1_lowest": 42000,
243
+ "chip_chip0-pci-xxxxxxxx_sensor_temp1_max": 42000,
244
+ "chip_chip0-pci-xxxxxxxx_sensor_temp1_min": 42000,
245
+ "chip_chip0-pci-xxxxxxxx_sensor_temp1_read_time": 0,
246
+ "chip_chip0-pci-xxxxxxxx_sensor_temp2_crit": 42000,
247
+ "chip_chip0-pci-xxxxxxxx_sensor_temp2_emergency": 42000,
248
+ "chip_chip0-pci-xxxxxxxx_sensor_temp2_highest": 42000,
249
+ "chip_chip0-pci-xxxxxxxx_sensor_temp2_input": 42000,
250
+ "chip_chip0-pci-xxxxxxxx_sensor_temp2_lcrit": 42000,
251
+ "chip_chip0-pci-xxxxxxxx_sensor_temp2_lowest": 42000,
252
+ "chip_chip0-pci-xxxxxxxx_sensor_temp2_max": 42000,
253
+ "chip_chip0-pci-xxxxxxxx_sensor_temp2_min": 42000,
254
+ "chip_chip0-pci-xxxxxxxx_sensor_temp2_read_time": 0,
255
},
256
},
188
- "exec: error on sensors info call": {
189
- prepareExecMock: prepareMockExecErr,
190
- wantMetrics: nil,
191
- },
192
- "exec: empty response": {
193
- prepareExecMock: prepareMockExecEmptyResponse,
194
- wantMetrics: nil,
195
- },
196
- "exec: unexpected response": {
197
- prepareExecMock: prepareMockExecUnexpectedResponse,
198
- wantMetrics: nil,
199
- },
200
-
201
- "sysfs: multiple sensors": {
202
- prepareSysfsMock: prepareMockSysfsScannerOk,
203
- wantCharts: 21,
204
- wantMetrics: map[string]int64{
205
- "sensor_chip_acpitz-acpi-0_feature_temp1_subfeature_temp1_input": 88000,
206
- "sensor_chip_amdgpu-pci-0300_feature_edge_subfeature_temp1_input": 53000,
207
- "sensor_chip_amdgpu-pci-0300_feature_fan1_subfeature_fan1_input": 0,
208
- "sensor_chip_amdgpu-pci-0300_feature_junction_subfeature_temp2_input": 58000,
209
- "sensor_chip_amdgpu-pci-0300_feature_mem_subfeature_temp3_input": 57000,
210
- "sensor_chip_amdgpu-pci-0300_feature_ppt_subfeature_power1_average": 29000,
211
- "sensor_chip_amdgpu-pci-0300_feature_vddgfx_subfeature_in0_input": 787,
212
- "sensor_chip_amdgpu-pci-6700_feature_edge_subfeature_temp1_input": 60000,
213
- "sensor_chip_amdgpu-pci-6700_feature_ppt_subfeature_power1_average": 5088,
214
- "sensor_chip_amdgpu-pci-6700_feature_vddgfx_subfeature_in0_input": 1335,
215
- "sensor_chip_amdgpu-pci-6700_feature_vddnb_subfeature_in1_input": 973,
216
- "sensor_chip_asus-isa-0000_feature_cpu_fan_subfeature_fan1_input": 5700000,
217
- "sensor_chip_asus-isa-0000_feature_gpu_fan_subfeature_fan2_input": 6600000,
218
- "sensor_chip_asus-isa-0000_feature_intrusion0_subfeature_intrusion0_alarm_clear": 0,
219
- "sensor_chip_asus-isa-0000_feature_intrusion0_subfeature_intrusion0_alarm_triggered": 1,
220
- "sensor_chip_bat0-acpi-0_feature_in0_subfeature_in0_input": 17365,
221
- "sensor_chip_k10temp-pci-00c3_feature_tctl_subfeature_temp1_input": 90000,
222
- "sensor_chip_nvme-pci-0600_feature_composite_subfeature_temp1_input": 33850,
223
- "sensor_chip_nvme-pci-0600_feature_sensor_1_subfeature_temp2_input": 48850,
224
- "sensor_chip_nvme-pci-0600_feature_sensor_2_subfeature_temp3_input": 33850,
225
- "sensor_chip_ucsi_source_psy_usbc000:001-isa-0000_feature_curr1_subfeature_curr1_input": 0,
226
- "sensor_chip_ucsi_source_psy_usbc000:001-isa-0000_feature_in0_subfeature_in0_input": 0,
227
- },
228
- },
229
- "sysfs: error on scan": {
230
- prepareSysfsMock: prepareMockSysfsScannerErr,
231
- wantMetrics: nil,
257
+ "error on scan": {
258
+ prepareScanner: prepareMockScannerErr,
259
+ wantMetrics: nil,
260
},
261
}
262
263
for name, test := range tests {
264
t.Run(name, func(t *testing.T) {
265
sensors := New()
238
-
239
- if test.prepareExecMock != nil {
240
- sensors.exec = test.prepareExecMock()
241
- } else if test.prepareSysfsMock != nil {
242
- sensors.sc = test.prepareSysfsMock()
243
- } else {
244
- t.Fail()
245
- }
266
+ sensors.sc = test.prepareScanner()
267
268
var mx map[string]int64
269
@@ -261,201 +282,212 @@ func TestSensors_Collect(t *testing.T) {
282
}
283
}
284
264
-func prepareMockExecOk() *mockSensorsBinary {
265
- return &mockSensorsBinary{
266
- sensorsInfoData: dataSensors,
267
- }
268
-}
269
-
270
-func prepareMockExecErr() *mockSensorsBinary {
271
- return &mockSensorsBinary{
272
- errOnSensorsInfo: true,
285
+func prepareMockScannerOk() *mockScanner {
286
+ return &mockScanner{
287
+ scanData: mockChips(),
288
}
289
}
290
276
-func prepareMockExecUnexpectedResponse() *mockSensorsBinary {
277
- return &mockSensorsBinary{
278
- sensorsInfoData: []byte(`
279
-Lorem ipsum dolor sit amet, consectetur adipiscing elit.
280
-Nulla malesuada erat id magna mattis, eu viverra tellus rhoncus.
281
-Fusce et felis pulvinar, posuere sem non, porttitor eros.
282
-`),
291
+func prepareMockScannerErr() *mockScanner {
292
+ return &mockScanner{
293
+ errOnScan: true,
294
}
295
}
296
286
-func prepareMockExecEmptyResponse() *mockSensorsBinary {
287
- return &mockSensorsBinary{}
288
-}
289
-
290
-type mockSensorsBinary struct {
291
- errOnSensorsInfo bool
292
- sensorsInfoData []byte
297
+type mockScanner struct {
298
+ errOnScan bool
299
+ scanData []*lmsensors.Chip
300
}
301
295
-func (m *mockSensorsBinary) sensorsInfo() ([]byte, error) {
296
- if m.errOnSensorsInfo {
297
- return nil, errors.New("mock.sensorsInfo() error")
302
+func (m *mockScanner) Scan() ([]*lmsensors.Chip, error) {
303
+ if m.errOnScan {
304
+ return nil, errors.New("mock.scan() error")
305
}
299
-
300
- return m.sensorsInfoData, nil
306
+ return m.scanData, nil
307
}
308
303
-func prepareMockSysfsScannerOk() *mockSysfsScanner {
304
- return &mockSysfsScanner{
305
- scanData: []*lmsensors.Device{
306
- {Name: "asus-isa-0000", Sensors: []lmsensors.Sensor{
307
- &lmsensors.FanSensor{
308
- Name: "fan1",
309
- Label: "cpu_fan",
310
- Input: 5700,
311
- },
312
- &lmsensors.FanSensor{
313
- Name: "fan2",
314
- Label: "gpu_fan",
315
- Input: 6600,
316
- },
317
- &lmsensors.IntrusionSensor{
318
- Name: "intrusion0",
319
- Label: "intrusion0",
320
- Alarm: true,
321
- },
322
- }},
323
- {Name: "nvme-pci-0600", Sensors: []lmsensors.Sensor{
324
- &lmsensors.TemperatureSensor{
325
- Name: "temp1",
326
- Label: "Composite",
327
- Input: 33.85,
328
- Maximum: 83.85,
329
- Minimum: -40.15,
330
- Critical: 87.85,
331
- Alarm: false,
332
- },
333
- &lmsensors.TemperatureSensor{
334
- Name: "temp2",
335
- Label: "Sensor 1",
336
- Input: 48.85,
337
- Maximum: 65261.85,
338
- Minimum: -273.15,
339
- },
340
- &lmsensors.TemperatureSensor{
341
- Name: "temp3",
342
- Label: "Sensor 2",
343
- Input: 33.85,
344
- Maximum: 65261.85,
345
- Minimum: -273.15,
346
- },
347
- }},
348
- {Name: "amdgpu-pci-6700", Sensors: []lmsensors.Sensor{
349
- &lmsensors.VoltageSensor{
350
- Name: "in0",
351
- Label: "vddgfx",
352
- Input: 1.335,
353
- },
354
- &lmsensors.VoltageSensor{
355
- Name: "in1",
356
- Label: "vddnb",
357
- Input: 0.973,
358
- },
359
- &lmsensors.TemperatureSensor{
360
- Name: "temp1",
361
- Label: "edge",
362
- Input: 60.000,
363
- },
364
- &lmsensors.PowerSensor{
365
- Name: "power1",
366
- Label: "PPT",
367
- Average: 5.088,
368
- },
369
- }},
370
- {Name: "BAT0-acpi-0", Sensors: []lmsensors.Sensor{
371
- &lmsensors.VoltageSensor{
372
- Name: "in0",
373
- Label: "in0",
374
- Input: 17.365,
375
- },
376
- }},
377
- {Name: "ucsi_source_psy_USBC000:001-isa-0000", Sensors: []lmsensors.Sensor{
378
- &lmsensors.VoltageSensor{
379
- Name: "in0",
380
- Label: "in0",
381
- Input: 0.000,
382
- },
383
- &lmsensors.CurrentSensor{
384
- Name: "curr1",
385
- Label: "curr1",
386
- Input: 0.000,
387
- },
388
- }},
389
- {Name: "k10temp-pci-00c3", Sensors: []lmsensors.Sensor{
390
- &lmsensors.TemperatureSensor{
391
- Name: "temp1",
392
- Label: "Tctl",
393
- Input: 90,
309
+func mockChips() []*lmsensors.Chip {
310
+ return []*lmsensors.Chip{
311
+ {
312
+ Name: "chip0",
313
+ UniqueName: "chip0-pci-xxxxxxxx",
314
+ SysDevice: "pci0000:80/0000:80:01.4/0000:81:00.0/nvme/nvme0",
315
+ Sensors: lmsensors.Sensors{
316
+ Voltage: []*lmsensors.VoltageSensor{
317
+ {
318
+ Name: "in1",
319
+ Label: "some_label1",
320
+ Alarm: ptr(false),
321
+ Input: ptr(42.0),
322
+ Average: ptr(42.0),
323
+ Min: ptr(42.0),
324
+ Max: ptr(42.0),
325
+ CritMin: ptr(42.0),
326
+ CritMax: ptr(42.0),
327
+ Lowest: ptr(42.0),
328
+ Highest: ptr(42.0),
329
+ },
330
+ {
331
+ Name: "in2",
332
+ Label: "some_label2",
333
+ Input: ptr(42.0),
334
+ Min: ptr(42.0),
335
+ Max: ptr(42.0),
336
+ CritMin: ptr(42.0),
337
+ CritMax: ptr(42.0),
338
+ Lowest: ptr(42.0),
339
+ Highest: ptr(42.0),
340
+ },
341
},
395
- }},
396
- {Name: "amdgpu-pci-0300", Sensors: []lmsensors.Sensor{
397
- &lmsensors.VoltageSensor{
398
- Name: "in0",
399
- Label: "vddgfx",
400
- Input: 0.787,
342
+ Fan: []*lmsensors.FanSensor{
343
+ {
344
+ Name: "fan1",
345
+ Label: "some_label1",
346
+ Alarm: ptr(false),
347
+ Input: ptr(42.0),
348
+ Min: ptr(42.0),
349
+ Max: ptr(42.0),
350
+ Target: ptr(42.0),
351
+ },
352
+ {
353
+ Name: "fan2",
354
+ Label: "some_label2",
355
+ Input: ptr(42.0),
356
+ Min: ptr(42.0),
357
+ Max: ptr(42.0),
358
+ Target: ptr(42.0),
359
+ },
360
},
402
- &lmsensors.FanSensor{
403
- Name: "fan1",
404
- Label: "fan1",
405
- Maximum: 4900,
361
+ Temperature: []*lmsensors.TemperatureSensor{
362
+ {
363
+ Name: "temp1",
364
+ Label: "some_label1",
365
+ Alarm: ptr(false),
366
+ TempTypeRaw: 1,
367
+ Input: ptr(42.0),
368
+ Max: ptr(42.0),
369
+ Min: ptr(42.0),
370
+ CritMin: ptr(42.0),
371
+ CritMax: ptr(42.0),
372
+ Emergency: ptr(42.0),
373
+ Lowest: ptr(42.0),
374
+ Highest: ptr(42.0),
375
+ },
376
+ {
377
+ Name: "temp2",
378
+ Label: "some_label2",
379
+ TempTypeRaw: 1,
380
+ Input: ptr(42.0),
381
+ Max: ptr(42.0),
382
+ Min: ptr(42.0),
383
+ CritMin: ptr(42.0),
384
+ CritMax: ptr(42.0),
385
+ Emergency: ptr(42.0),
386
+ Lowest: ptr(42.0),
387
+ Highest: ptr(42.0),
388
+ },
389
},
407
- &lmsensors.TemperatureSensor{
408
- Name: "temp1",
409
- Label: "edge",
410
- Input: 53,
411
- Critical: 100,
412
- Emergency: 105,
390
+ Current: []*lmsensors.CurrentSensor{
391
+ {
392
+ Name: "curr1",
393
+ Label: "some_label1",
394
+ Alarm: ptr(false),
395
+ Max: ptr(42.0),
396
+ Min: ptr(42.0),
397
+ CritMin: ptr(42.0),
398
+ CritMax: ptr(42.0),
399
+ Input: ptr(42.0),
400
+ Average: ptr(42.0),
401
+ Lowest: ptr(42.0),
402
+ Highest: ptr(42.0),
403
+ },
404
+ {
405
+ Name: "curr2",
406
+ Label: "some_label2",
407
+ Max: ptr(42.0),
408
+ Min: ptr(42.0),
409
+ CritMin: ptr(42.0),
410
+ CritMax: ptr(42.0),
411
+ Input: ptr(42.0),
412
+ Lowest: ptr(42.0),
413
+ Highest: ptr(42.0),
414
+ },
415
},
414
- &lmsensors.TemperatureSensor{
415
- Name: "temp2",
416
- Label: "junction",
417
- Input: 58,
418
- Critical: 100,
419
- Emergency: 105,
416
+ Power: []*lmsensors.PowerSensor{
417
+ {
418
+ Name: "power1",
419
+ Label: "some_label1",
420
+ Alarm: ptr(false),
421
+ Average: ptr(345.0),
422
+ AverageHighest: ptr(345.0),
423
+ AverageLowest: ptr(345.0),
424
+ AverageMax: ptr(345.0),
425
+ AverageMin: ptr(345.0),
426
+ Input: ptr(345.0),
427
+ InputHighest: ptr(345.0),
428
+ InputLowest: ptr(345.0),
429
+ Accuracy: ptr(34.5),
430
+ Cap: ptr(345.0),
431
+ CapMax: ptr(345.0),
432
+ CapMin: ptr(345.0),
433
+ Max: ptr(345.0),
434
+ CritMax: ptr(345.0),
435
+ },
436
+ {
437
+ Name: "power2",
438
+ Label: "some_label2",
439
+ AverageHighest: ptr(345.0),
440
+ AverageLowest: ptr(345.0),
441
+ AverageMax: ptr(345.0),
442
+ AverageMin: ptr(345.0),
443
+ Input: ptr(345.0),
444
+ InputHighest: ptr(345.0),
445
+ InputLowest: ptr(345.0),
446
+ Accuracy: ptr(34.5),
447
+ Cap: ptr(345.0),
448
+ CapMax: ptr(345.0),
449
+ CapMin: ptr(345.0),
450
+ Max: ptr(345.0),
451
+ CritMax: ptr(345.0),
452
+ },
453
},
421
- &lmsensors.TemperatureSensor{
422
- Name: "temp3",
423
- Label: "mem",
424
- Input: 57,
425
- Critical: 106,
426
- Emergency: 110,
454
+ Energy: []*lmsensors.EnergySensor{
455
+ {
456
+ Name: "energy1",
457
+ Label: "some_label1",
458
+ Input: ptr(42.0),
459
+ },
460
+ {
461
+ Name: "energy2",
462
+ Label: "some_label2",
463
+ Input: ptr(42.0),
464
+ },
465
},
428
- &lmsensors.PowerSensor{
429
- Name: "power1",
430
- Label: "PPT",
431
- Average: 29,
466
+ Humidity: []*lmsensors.HumiditySensor{
467
+ {
468
+ Name: "humidity1",
469
+ Label: "some_label1",
470
+ Input: ptr(42.0),
471
+ },
472
+ {
473
+ Name: "humidity2",
474
+ Label: "some_label2",
475
+ Input: ptr(42.0),
476
+ },
477
},
433
- }},
434
- {Name: "acpitz-acpi-0", Sensors: []lmsensors.Sensor{
435
- &lmsensors.FanSensor{
436
- Name: "temp1",
437
- Label: "temp1",
438
- Input: 88,
478
+ Intrusion: []*lmsensors.IntrusionSensor{
479
+ {
480
+ Name: "intrusion1",
481
+ Label: "some_label1",
482
+ Alarm: ptr(false),
483
+ },
484
+ {
485
+ Name: "intrusion2",
486
+ Label: "some_label2",
487
+ Alarm: ptr(true),
488
+ },
489
},
440
- }},
490
+ },
491
},
492
}
493
}
444
-
445
-func prepareMockSysfsScannerErr() *mockSysfsScanner {
446
- return &mockSysfsScanner{
447
- errOnScan: true,
448
- }
449
-}
450
-
451
-type mockSysfsScanner struct {
452
- errOnScan bool
453
- scanData []*lmsensors.Device
454
-}
455
-
456
-func (m *mockSysfsScanner) Scan() ([]*lmsensors.Device, error) {
457
- if m.errOnScan {
458
- return nil, errors.New("mock.scan() error")
459
- }
460
- return m.scanData, nil
461
-}
src/go/plugin/go.d/modules/sensors/testdata/config.json
+1
-3
@@ -1,5 +1,3 @@
1
{
2
- "update_every": 123,
3
- "timeout": 123.123,
4
- "binary_path": "ok"
2
+ "update_every": 123
3
}
src/go/plugin/go.d/modules/sensors/testdata/config.yaml
-2
@@ -1,3 +1 @@
1
update_every: 123
2
-timeout: 123.123
3
-binary_path: "ok"
src/go/plugin/go.d/modules/sensors/testdata/sensors.txt
deleted
-79
@@ -1,79 +0,0 @@
1
-asus-isa-0000
2
-cpu_fan:
3
- fan1_input: 5700.000
4
-gpu_fan:
5
- fan2_input: 6600.000
6
-nvme-pci-0600
7
-Composite:
8
- temp1_input: 33.850
9
- temp1_max: 83.850
10
- temp1_min: -40.150
11
- temp1_crit: 87.850
12
- temp1_alarm: 0.000
13
-Sensor 1:
14
- temp2_input: 48.850
15
- temp2_max: 65261.850
16
- temp2_min: -273.150
17
-Sensor 2:
18
- temp3_input: 33.850
19
- temp3_max: 65261.850
20
- temp3_min: -273.150
21
-amdgpu-pci-6700
22
-vddgfx:
23
- in0_input: 1.335
24
-vddnb:
25
- in1_input: 0.973
26
-edge:
27
- temp1_input: 60.000
28
-PPT:
29
- power1_average: 5.088
30
- power1_input: 8.144
31
-BAT0-acpi-0
32
-in0:
33
- in0_input: 17.365
34
-ucsi_source_psy_USBC000:001-isa-0000
35
-in0:
36
- in0_input: 0.000
37
- in0_min: 0.000
38
- in0_max: 0.000
39
-curr1:
40
- curr1_input: 0.000
41
- curr1_max: 0.000
42
-k10temp-pci-00c3
43
-Tctl:
44
- temp1_input: 90.000
45
-amdgpu-pci-0300
46
-vddgfx:
47
- in0_input: 0.787
48
-fan1:
49
- fan1_input: 0.000
50
- fan1_min: 0.000
51
- fan1_max: 4900.000
52
-edge:
53
- temp1_input: 53.000
54
- temp1_crit: 100.000
55
- temp1_crit_hyst: -273.150
56
- temp1_emergency: 105.000
57
-junction:
58
- temp2_input: 58.000
59
- temp2_crit: 100.000
60
- temp2_crit_hyst: -273.150
61
- temp2_emergency: 105.000
62
-mem:
63
- temp3_input: 57.000
64
- temp3_crit: 105.000
65
- temp3_crit_hyst: -273.150
66
- temp3_emergency: 110.000
67
-PPT:
68
- power1_average: 29.000
69
- power1_cap: 120.000
70
-acpitz-acpi-0
71
-temp1:
72
- temp1_input: 88.000
73
-nct6779-isa-0290
74
-intrusion0:
75
- intrusion0_alarm: 1.000
76
- intrusion0_beep: 0.000
77
-intrusion1:
78
- intrusion1_alarm: 1.000
79
- intrusion1_beep: 0.000