1
0
Fork 0
telegraf/plugins/inputs/sensors/sensors.go
Daniel Baumann 4978089aab
Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-05-24 07:26:29 +02:00

135 lines
3.1 KiB
Go

//go:generate ../../../tools/readme_config_includer/generator
//go:build linux
package sensors
import (
_ "embed"
"errors"
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/plugins/inputs"
)
//go:embed sample.conf
var sampleConfig string
var (
execCommand = exec.Command // execCommand is used to mock commands in tests.
numberRegp = regexp.MustCompile("[0-9]+")
defaultTimeout = config.Duration(5 * time.Second)
)
const cmd = "sensors"
type Sensors struct {
RemoveNumbers bool `toml:"remove_numbers"`
Timeout config.Duration `toml:"timeout"`
path string
}
func (*Sensors) SampleConfig() string {
return sampleConfig
}
func (s *Sensors) Init() error {
// Set defaults
if s.path == "" {
path, err := exec.LookPath(cmd)
if err != nil {
return fmt.Errorf("looking up %q failed: %w", cmd, err)
}
s.path = path
}
// Check parameters
if s.path == "" {
return fmt.Errorf("no path specified for %q", cmd)
}
return nil
}
func (s *Sensors) Gather(acc telegraf.Accumulator) error {
if len(s.path) == 0 {
return errors.New("sensors not found: verify that lm-sensors package is installed and that sensors is in your PATH")
}
return s.parse(acc)
}
// parse forks the command:
//
// sensors -u -A
//
// and parses the output to add it to the telegraf.Accumulator.
func (s *Sensors) parse(acc telegraf.Accumulator) error {
tags := make(map[string]string)
fields := make(map[string]interface{})
chip := ""
cmd := execCommand(s.path, "-A", "-u")
out, err := internal.StdOutputTimeout(cmd, time.Duration(s.Timeout))
if err != nil {
return fmt.Errorf("failed to run command %q: %w - %s", strings.Join(cmd.Args, " "), err, string(out))
}
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
for _, line := range lines {
if len(line) == 0 {
acc.AddFields("sensors", fields, tags)
chip = ""
tags = make(map[string]string)
fields = make(map[string]interface{})
continue
}
if len(chip) == 0 {
chip = line
tags["chip"] = chip
continue
}
if !strings.HasPrefix(line, " ") {
if len(tags) > 1 {
acc.AddFields("sensors", fields, tags)
}
fields = make(map[string]interface{})
tags = map[string]string{
"chip": chip,
"feature": strings.TrimRight(snake(line), ":"),
}
} else {
splitted := strings.Split(line, ":")
fieldName := strings.TrimSpace(splitted[0])
if s.RemoveNumbers {
fieldName = numberRegp.ReplaceAllString(fieldName, "")
}
fieldValue, err := strconv.ParseFloat(strings.TrimSpace(splitted[1]), 64)
if err != nil {
return err
}
fields[fieldName] = fieldValue
}
}
acc.AddFields("sensors", fields, tags)
return nil
}
// snake converts string to snake case
func snake(input string) string {
return strings.ToLower(strings.ReplaceAll(strings.TrimSpace(input), " ", "_"))
}
func init() {
inputs.Add("sensors", func() telegraf.Input {
return &Sensors{
RemoveNumbers: true,
Timeout: defaultTimeout,
}
})
}