diff options
author | Ilya Mashchenko <ilya@netdata.cloud> | 2024-04-22 11:32:47 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-22 11:32:47 +0300 |
commit | b7c4d442c5e0f61ba5eacd042d921955b3e763b3 (patch) | |
tree | 0e0e0d216f509bf889adb7992f9696fc7867e5fe | |
parent | 827fe2afcf761380c465b7878eced4a0f3a12a6e (diff) |
go.d add sensors (#17466)
17 files changed, 1211 insertions, 2 deletions
diff --git a/src/go/collectors/go.d.plugin/README.md b/src/go/collectors/go.d.plugin/README.md index fc688ada01..b7f35a1e54 100644 --- a/src/go/collectors/go.d.plugin/README.md +++ b/src/go/collectors/go.d.plugin/README.md @@ -76,6 +76,7 @@ see the appropriate collector readme. | [fluentd](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/fluentd) | Fluentd | | [freeradius](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/freeradius) | FreeRADIUS | | [haproxy](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/haproxy) | HAProxy | +| [hddtemp](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/hddtemp) | Disks temperature | | [hdfs](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/hdfs) | HDFS | | [httpcheck](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/httpcheck) | Any HTTP Endpoint | | [intelgpu](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/intelgpu) | Intel integrated GPU | @@ -113,6 +114,7 @@ see the appropriate collector readme. | [rabbitmq](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/rabbitmq) | RabbitMQ | | [redis](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/redis) | Redis | | [scaleio](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/scaleio) | Dell EMC ScaleIO | +| [sensors](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules) | Hardware Sensors | | [SNMP](https://github.com/netdata/netdata/blob/master/src/go/collectors/go.d.plugin/modules/snmp) | SNMP | | [squidlog](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/squidlog) | Squid | | [storcli](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/storcli) | Broadcom Hardware RAID | diff --git a/src/go/collectors/go.d.plugin/config/go.d.conf b/src/go/collectors/go.d.plugin/config/go.d.conf index 9fe91db5d9..928cb15a06 100644 --- a/src/go/collectors/go.d.plugin/config/go.d.conf +++ b/src/go/collectors/go.d.plugin/config/go.d.conf @@ -76,6 +76,7 @@ modules: # rabbitmq: yes # redis: yes # scaleio: yes +# sensors: yes # snmp: yes # squidlog: yes # storcli: yes diff --git a/src/go/collectors/go.d.plugin/config/go.d/sensors.conf b/src/go/collectors/go.d.plugin/config/go.d/sensors.conf new file mode 100644 index 0000000000..3b8febde89 --- /dev/null +++ b/src/go/collectors/go.d.plugin/config/go.d/sensors.conf @@ -0,0 +1,6 @@ +## All available configuration options, their descriptions and default values: +## https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/modules/sensors#readme + +jobs: + - name: sensors + binary_path: /usr/bin/sensors diff --git a/src/go/collectors/go.d.plugin/modules/init.go b/src/go/collectors/go.d.plugin/modules/init.go index 90f1102914..8691ca024d 100644 --- a/src/go/collectors/go.d.plugin/modules/init.go +++ b/src/go/collectors/go.d.plugin/modules/init.go @@ -68,6 +68,7 @@ import ( _ "github.com/netdata/netdata/go/go.d.plugin/modules/rabbitmq" _ "github.com/netdata/netdata/go/go.d.plugin/modules/redis" _ "github.com/netdata/netdata/go/go.d.plugin/modules/scaleio" + _ "github.com/netdata/netdata/go/go.d.plugin/modules/sensors" _ "github.com/netdata/netdata/go/go.d.plugin/modules/snmp" _ "github.com/netdata/netdata/go/go.d.plugin/modules/squidlog" _ "github.com/netdata/netdata/go/go.d.plugin/modules/storcli" diff --git a/src/go/collectors/go.d.plugin/modules/sensors/charts.go b/src/go/collectors/go.d.plugin/modules/sensors/charts.go new file mode 100644 index 0000000000..20df057c88 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/sensors/charts.go @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package sensors + +import ( + "fmt" + "strings" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" +) + +const ( + prioSensorTemperature = module.Priority + iota + prioSensorVoltage + prioSensorCurrent + prioSensorPower + prioSensorFan + prioSensorEnergy + prioSensorHumidity +) + +var sensorTemperatureChartTmpl = module.Chart{ + ID: "sensor_chip_%s_feature_%s_subfeature_%s_temperature", + Title: "Sensor temperature", + Units: "Celsius", + Fam: "temperature", + Ctx: "sensors.sensor_temperature", + Type: module.Line, + Priority: prioSensorTemperature, + Dims: module.Dims{ + {ID: "sensor_chip_%s_feature_%s_subfeature_%s", Name: "temperature", Div: precision}, + }, +} + +var sensorVoltageChartTmpl = module.Chart{ + ID: "sensor_chip_%s_feature_%s_subfeature_%s_voltage", + Title: "Sensor voltage", + Units: "Volts", + Fam: "voltage", + Ctx: "sensors.sensor_voltage", + Type: module.Line, + Priority: prioSensorVoltage, + Dims: module.Dims{ + {ID: "sensor_chip_%s_feature_%s_subfeature_%s", Name: "voltage", Div: precision}, + }, +} + +var sensorCurrentChartTmpl = module.Chart{ + ID: "sensor_chip_%s_feature_%s_subfeature_%s_current", + Title: "Sensor current", + Units: "Amperes", + Fam: "current", + Ctx: "sensors.sensor_current", + Type: module.Line, + Priority: prioSensorCurrent, + Dims: module.Dims{ + {ID: "sensor_chip_%s_feature_%s_subfeature_%s", Name: "current", Div: precision}, + }, +} + +var sensorPowerChartTmpl = module.Chart{ + ID: "sensor_chip_%s_feature_%s_subfeature_%s_power", + Title: "Sensor power", + Units: "Watts", + Fam: "power", + Ctx: "sensors.sensor_power", + Type: module.Line, + Priority: prioSensorPower, + Dims: module.Dims{ + {ID: "sensor_chip_%s_feature_%s_subfeature_%s", Name: "power", Div: precision}, + }, +} + +var sensorFanChartTmpl = module.Chart{ + ID: "sensor_chip_%s_feature_%s_subfeature_%s_fan", + Title: "Sensor fan speed", + Units: "RPM", + Fam: "fan", + Ctx: "sensors.sensor_fan_speed", + Type: module.Line, + Priority: prioSensorFan, + Dims: module.Dims{ + {ID: "sensor_chip_%s_feature_%s_subfeature_%s", Name: "fan", Div: precision}, + }, +} + +var sensorEnergyChartTmpl = module.Chart{ + ID: "sensor_chip_%s_feature_%s_subfeature_%s_energy", + Title: "Sensor energy", + Units: "Joules", + Fam: "energy", + Ctx: "sensors.sensor_energy", + Type: module.Line, + Priority: prioSensorEnergy, + Dims: module.Dims{ + {ID: "sensor_chip_%s_feature_%s_subfeature_%s", Name: "energy", Div: precision}, + }, +} + +var sensorHumidityChartTmpl = module.Chart{ + ID: "sensor_chip_%s_feature_%s_subfeature_%s_humidity", + Title: "Sensor humidity", + Units: "percent", + Fam: "humidity", + Ctx: "sensors.sensor_humidity", + Type: module.Area, + Priority: prioSensorHumidity, + Dims: module.Dims{ + {ID: "sensor_chip_%s_feature_%s_subfeature_%s", Name: "humidity", Div: precision}, + }, +} + +func (s *Sensors) addSensorChart(sn sensorStats) { + var chart *module.Chart + + switch sensorType(sn) { + case sensorTypeTemp: + chart = sensorTemperatureChartTmpl.Copy() + case sensorTypeVoltage: + chart = sensorVoltageChartTmpl.Copy() + case sensorTypePower: + chart = sensorPowerChartTmpl.Copy() + case sensorTypeHumidity: + chart = sensorHumidityChartTmpl.Copy() + case sensorTypeFan: + chart = sensorFanChartTmpl.Copy() + case sensorTypeCurrent: + chart = sensorCurrentChartTmpl.Copy() + case sensorTypeEnergy: + chart = sensorEnergyChartTmpl.Copy() + default: + return + } + + chip, feat, subfeat := snakeCase(sn.chip), snakeCase(sn.feature), snakeCase(sn.subfeature) + + chart.ID = fmt.Sprintf(chart.ID, chip, feat, subfeat) + chart.Labels = []module.Label{ + {Key: "chip", Value: sn.chip}, + {Key: "feature", Value: sn.feature}, + } + for _, dim := range chart.Dims { + dim.ID = fmt.Sprintf(dim.ID, chip, feat, subfeat) + } + + if err := s.Charts().Add(chart); err != nil { + s.Warning(err) + } +} + +func (s *Sensors) removeSensorChart(px string) { + for _, chart := range *s.Charts() { + if strings.HasPrefix(chart.ID, px) { + chart.MarkRemove() + chart.MarkNotCreated() + return + } + } +} diff --git a/src/go/collectors/go.d.plugin/modules/sensors/collect.go b/src/go/collectors/go.d.plugin/modules/sensors/collect.go new file mode 100644 index 0000000000..46e900ad0a --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/sensors/collect.go @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package sensors + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "strconv" + "strings" +) + +type sensorStats struct { + chip string + feature string + subfeature string + value string +} + +func (s *sensorStats) String() string { + return fmt.Sprintf("chip:%s feat:%s subfeat:%s value:%s", s.chip, s.feature, s.subfeature, s.value) +} + +const ( + sensorTypeTemp = "temperature" + sensorTypeVoltage = "voltage" + sensorTypePower = "power" + sensorTypeHumidity = "humidity" + sensorTypeFan = "fan" + sensorTypeCurrent = "current" + sensorTypeEnergy = "energy" +) + +const precision = 1000 + +func (s *Sensors) collect() (map[string]int64, error) { + bs, err := s.exec.sensorsInfo() + if err != nil { + return nil, err + } + + if len(bs) == 0 { + return nil, errors.New("empty response from sensors") + } + + sensors, err := parseSensors(bs) + if err != nil { + return nil, err + } + if len(sensors) == 0 { + return nil, errors.New("no sensors found") + } + + mx := make(map[string]int64) + seen := make(map[string]bool) + + for _, sn := range sensors { + // TODO: Most likely we need different values depending on the type of sensor. + if !strings.HasSuffix(sn.subfeature, "_input") { + s.Debugf("skipping non input sensor: '%s'", sn) + continue + } + + v, err := strconv.ParseFloat(sn.value, 64) + if err != nil { + s.Debugf("parsing value for sensor '%s': %v", sn, err) + continue + } + + if sensorType(sn) == "" { + s.Debugf("can not find type for sensor '%s'", sn) + continue + } + + if minVal, maxVal, ok := sensorLimits(sn); ok && (v < minVal || v > maxVal) { + s.Debugf("value outside limits [%d/%d] for sensor '%s'", int64(minVal), int64(maxVal), sn) + continue + } + + key := fmt.Sprintf("sensor_chip_%s_feature_%s_subfeature_%s", sn.chip, sn.feature, sn.subfeature) + key = snakeCase(key) + if !s.sensors[key] { + s.sensors[key] = true + s.addSensorChart(sn) + } + + seen[key] = true + + mx[key] = int64(v * precision) + } + + for k := range s.sensors { + if !seen[k] { + delete(s.sensors, k) + s.removeSensorChart(k) + } + } + + return mx, nil +} + +func snakeCase(n string) string { + return strings.ToLower(strings.ReplaceAll(n, " ", "_")) +} + +func sensorLimits(sn sensorStats) (minVal float64, maxVal float64, ok bool) { + switch sensorType(sn) { + case sensorTypeTemp: + return -127, 1000, true + case sensorTypeVoltage: + return -400, 400, true + case sensorTypeCurrent: + return -127, 127, true + case sensorTypeFan: + return 0, 65535, true + default: + return 0, 0, false + } +} + +func sensorType(sn sensorStats) string { + switch { + case strings.HasPrefix(sn.subfeature, "temp"): + return sensorTypeTemp + case strings.HasPrefix(sn.subfeature, "in"): + return sensorTypeVoltage + case strings.HasPrefix(sn.subfeature, "power"): + return sensorTypePower + case strings.HasPrefix(sn.subfeature, "humidity"): + return sensorTypeHumidity + case strings.HasPrefix(sn.subfeature, "fan"): + return sensorTypeFan + case strings.HasPrefix(sn.subfeature, "curr"): + return sensorTypeCurrent + case strings.HasPrefix(sn.subfeature, "energy"): + return sensorTypeEnergy + default: + return "" + } +} + +func parseSensors(output []byte) ([]sensorStats, error) { + var sensors []sensorStats + + sc := bufio.NewScanner(bytes.NewReader(output)) + + var chip, feat string + + for sc.Scan() { + text := sc.Text() + if text == "" { + chip, feat = "", "" + continue + } + + switch { + case strings.HasPrefix(text, " ") && chip != "" && feat != "": + parts := strings.Split(text, ":") + if len(parts) != 2 { + continue + } + subfeat, value := strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]) + sensors = append(sensors, sensorStats{ + chip: chip, + feature: feat, + subfeature: subfeat, + value: value, + }) + case strings.HasSuffix(text, ":") && chip != "": + feat = strings.TrimSpace(strings.TrimSuffix(text, ":")) + default: + chip = text + feat = "" + } + } + + return sensors, nil +} diff --git a/src/go/collectors/go.d.plugin/modules/sensors/config_schema.json b/src/go/collectors/go.d.plugin/modules/sensors/config_schema.json new file mode 100644 index 0000000000..6c12ca9b8d --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/sensors/config_schema.json @@ -0,0 +1,47 @@ +{ + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Sensors collector configuration", + "type": "object", + "properties": { + "update_every": { + "title": "Update every", + "description": "Data collection interval, measured in seconds.", + "type": "integer", + "minimum": 1, + "default": 10 + }, + "binary_path": { + "title": "Binary path", + "description": "Path to the `sensors` binary.", + "type": "string", + "default": "/usr/bin/sensors" + }, + "timeout": { + "title": "Timeout", + "description": "Timeout for executing the binary, specified in seconds.", + "type": "number", + "minimum": 0.5, + "default": 2 + } + }, + "required": [ + "binary_path" + ], + "additionalProperties": false, + "patternProperties": { + "^name$": {} + } + }, + "uiSchema": { + "uiOptions": { + "fullPage": true + }, + "binary_path": { + "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." + }, + "timeout": { + "ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)." + } + } +} diff --git a/src/go/collectors/go.d.plugin/modules/sensors/exec.go b/src/go/collectors/go.d.plugin/modules/sensors/exec.go new file mode 100644 index 0000000000..b920da66ec --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/sensors/exec.go @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package sensors + +import ( + "context" + "fmt" + "os/exec" + "time" + + "github.com/netdata/netdata/go/go.d.plugin/logger" +) + +func newSensorsCliExec(binPath string, timeout time.Duration) *sensorsCliExec { + return &sensorsCliExec{ + binPath: binPath, + timeout: timeout, + } +} + +type sensorsCliExec struct { + *logger.Logger + + binPath string + timeout time.Duration +} + +func (e *sensorsCliExec) sensorsInfo() ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), e.timeout) + defer cancel() + + cmd := exec.CommandContext(ctx, e.binPath, "-A", "-u") + e.Debugf("executing '%s'", cmd) + + bs, err := cmd.Output() + if err != nil { + return nil, fmt.Errorf("error on '%s': %v", cmd, err) + } + + return bs, nil +} diff --git a/src/go/collectors/go.d.plugin/modules/sensors/init.go b/src/go/collectors/go.d.plugin/modules/sensors/init.go new file mode 100644 index 0000000000..6753693da5 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/sensors/init.go @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package sensors + +import ( + "errors" + "os" + "os/exec" + "strings" +) + +func (s *Sensors) validateConfig() error { + if s.BinaryPath == "" { + return errors.New("no sensors binary path specified") + } + return nil +} + +func (s *Sensors) initSensorsCliExec() (sensorsCLI, error) { + binPath := s.BinaryPath + + if !strings.HasPrefix(binPath, "/") { + path, err := exec.LookPath(binPath) + if err != nil { + return nil, err + } + binPath = path + } + + if _, err := os.Stat(binPath); err != nil { + return nil, err + } + + sensorsExec := newSensorsCliExec(binPath, s.Timeout.Duration()) + sensorsExec.Logger = s.Logger + + return sensorsExec, nil +} diff --git a/src/go/collectors/go.d.plugin/modules/sensors/metadata.yaml b/src/go/collectors/go.d.plugin/modules/sensors/metadata.yaml new file mode 100644 index 0000000000..5ea94f3982 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/sensors/metadata.yaml @@ -0,0 +1,157 @@ +plugin_name: go.d.plugin +modules: + - meta: + id: collector-go.d.plugin-sensors + plugin_name: go.d.plugin + module_name: sensors + monitored_instance: + name: Linux Sensors (lm-sensors) + link: https://hwmon.wiki.kernel.org/lm_sensors + icon_filename: "microchip.svg" + categories: + - data-collection.hardware-devices-and-sensors + keywords: + - sensors + - temperature + - voltage + - current + - power + - fan + - energy + - humidity + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + most_popular: false + overview: + data_collection: + metrics_description: > + This collector gathers real-time system sensor statistics, + including temperature, voltage, current, power, fan speed, energy consumption, and humidity, + utilizing the [sensors](https://linux.die.net/man/1/sensors) binary. + method_description: "" + supported_platforms: + include: [] + exclude: [] + multi_instance: false + additional_permissions: + description: "" + default_behavior: + auto_detection: + description: | + The following type of sensors are auto-detected: + + - temperature + - fan + - voltage + - current + - power + - energy + - humidity + limits: + description: "" + performance_impact: + description: "" + setup: + prerequisites: + list: + - title: Install lm-sensors + description: | + - Install `lm-sensors` using your distribution's package manager. + - Run `sensors-detect` to detect hardware monitoring chips. + configuration: + file: + name: go.d/sensors.conf + options: + description: | + The following options can be defined globally: update_every. + folding: + title: Config options + enabled: true + list: + - name: update_every + description: Data collection frequency. + default_value: 10 + required: false + - name: binary_path + description: Path to the `sensors` binary. 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. + default_value: /usr/bin/sensors + required: true + - name: timeout + description: Timeout for executing the binary, specified in seconds. + default_value: 2 + required: false + examples: + folding: + title: Config + enabled: true + list: + - name: Custom binary path + description: The executable is not in the directories specified in the PATH environment variable. + config: | + jobs: + - name: sensors + binary_path: /usr/local/sbin/sensors + troubleshooting: + problems: + list: [] + alerts: [] + metrics: + folding: + title: Metrics + enabled: false + description: "" + availability: [] + scopes: + - name: sensor + description: These metrics refer to the sensor. + labels: + - name: chip + description: The hardware component responsible for the sensor monitoring. + - name: feature + description: The specific sensor or monitoring point provided by the chip. + metrics: + - name: sensors.sensor_temperature + description: Sensor temperature + unit: Celsius + chart_type: line + dimensions: + - name: temperature + - name: sensors.sensor_voltage + description: Sensor voltage + unit: Volts + chart_type: line + dimensions: + - name: voltage + - name: sensors.sensor_current + description: Sensor current + unit: Amperes + chart_type: line + dimensions: + - name: current + - name: sensors.sensor_power + description: Sensor power + unit: Watts + chart_type: line + dimensions: + - name: power + - name: sensors.sensor_fan_speed + description: Sensor fan speed + unit: RPM + chart_type: line + dimensions: + - name: fan + - name: sensors.sensor_energy + description: Sensor energy + unit: Joules + chart_type: line + dimensions: + - name: energy + - name: sensors.sensor_humidity + description: Sensor humidity + unit: percent + chart_type: area + dimensions: + - name: humidity diff --git a/src/go/collectors/go.d.plugin/modules/sensors/sensors.go b/src/go/collectors/go.d.plugin/modules/sensors/sensors.go new file mode 100644 index 0000000000..f909811d7c --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/sensors/sensors.go @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package sensors + +import ( + _ "embed" + "errors" + "time" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" +) + +//go:embed "config_schema.json" +var configSchema string + +func init() { + module.Register("sensors", module.Creator{ + JobConfigSchema: configSchema, + Defaults: module.Defaults{ + UpdateEvery: 10, + }, + Create: func() module.Module { return New() }, + }) +} + +func New() *Sensors { + return &Sensors{ + Config: Config{ + BinaryPath: "/usr/bin/sensors", + Timeout: web.Duration(time.Second * 2), + }, + charts: &module.Charts{}, + sensors: make(map[string]bool), + } +} + +type Config struct { + UpdateEvery int `yaml:"update_every" json:"update_every"` + Timeout web.Duration `yaml:"timeout" json:"timeout"` + BinaryPath string `yaml:"binary_path" json:"binary_path"` +} + +type ( + Sensors struct { + module.Base + Config `yaml:",inline" json:""` + + charts *module.Charts + + exec sensorsCLI + + sensors map[string]bool + } + sensorsCLI interface { + sensorsInfo() ([]byte, error) + } +) + +func (s *Sensors) Configuration() any { + return s.Config +} + +func (s *Sensors) Init() error { + if err := s.validateConfig(); err != nil { + s.Errorf("config validation: %s", err) + return err + } + + sensorsExec, err := s.initSensorsCliExec() + if err != nil { + s.Errorf("sensors exec initialization: %v", err) + return err + } + s.exec = sensorsExec + + return nil +} + +func (s *Sensors) Check() error { + mx, err := s.collect() + if err != nil { + s.Error(err) + return err + } + + if len(mx) == 0 { + return errors.New("no metrics collected") + } + + return nil +} + +func (s *Sensors) Charts() *module.Charts { + return s.charts +} + +func (s *Sensors) Collect() map[string]int64 { + mx, err := s.collect() + if err != nil { + s.Error(err) + } + + if len(mx) == 0 { + return nil + } + + return mx +} + +func (s *Sensors) Cleanup() {} diff --git a/src/go/collectors/go.d.plugin/modules/sensors/sensors_test.go b/src/go/collectors/go.d.plugin/modules/sensors/sensors_test.go new file mode 100644 index 0000000000..d9b4242e7b --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/sensors/sensors_test.go @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package sensors + +import ( + "errors" + "os" + "testing" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + dataConfigJSON, _ = os.ReadFile("testdata/config.json") + dataConfigYAML, _ = os.ReadFile("testdata/config.yaml") + + dataSensorsTemp, _ = os.ReadFile("testdata/sensors-temp.txt") + dataSensorsTempInCurrPowerFan, _ = os.ReadFile("testdata/sensors-temp-in-curr-power-fan.txt") +) + +func Test_testDataIsValid(t *testing.T) { + for name, data := range map[string][]byte{ + "dataConfigJSON": dataConfigJSON, + "dataConfigYAML": dataConfigYAML, + + "dataSensorsTemp": dataSensorsTemp, + "dataSensorsTempInCurrPowerFan": dataSensorsTempInCurrPowerFan, + } { + require.NotNil(t, data, name) + + } +} + +func TestSensors_Configuration(t *testing.T) { + module.TestConfigurationSerialize(t, &Sensors{}, dataConfigJSON, dataConfigYAML) +} + +func TestSensors_Init(t *testing.T) { + tests := map[string]struct { + config Config + wantFail bool + }{ + "fails if 'binary_path' is not set": { + wantFail: true, + config: Config{ + BinaryPath: "", + }, + }, + "fails if failed to find binary": { + wantFail: true, + config: Config{ + BinaryPath: "sensors!!!", + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + sensors := New() + sensors.Config = test.config + + if test.wantFail { + assert.Error(t, sensors.Init()) + } else { + assert.NoError(t, sensors.Init()) + } + }) + } +} + +func TestSensors_Cleanup(t *testing.T) { + tests := map[string]struct { + prepare func() *Sensors + }{ + "not initialized exec": { + prepare: func() *Sensors { + return New() + }, + }, + "after che |