allow querying sensors via the open interface

closes #2244
closes #1547
This commit is contained in:
Matteo Cerutti
2017-01-09 10:45:31 +00:00
committed by Cameron Sparr
parent dfba3ff37a
commit a610f8bd03
6 changed files with 315 additions and 194 deletions

View File

@@ -1,48 +1,62 @@
package ipmi_sensor
import (
"fmt"
"os/exec"
"strconv"
"strings"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/plugins/inputs"
)
var (
execCommand = exec.Command // execCommand is used to mock commands in tests.
)
type Ipmi struct {
path string
Servers []string
runner Runner
}
var sampleConfig = `
## specify servers via a url matching:
## optionally specify the path to the ipmitool executable
# path = "/usr/bin/ipmitool"
#
## optionally specify one or more servers via a url matching
## [username[:password]@][protocol[(address)]]
## e.g.
## root:passwd@lan(127.0.0.1)
##
servers = ["USERID:PASSW0RD@lan(192.168.1.1)"]
## if no servers are specified, local machine sensor stats will be queried
##
# servers = ["USERID:PASSW0RD@lan(192.168.1.1)"]
`
func NewIpmi() *Ipmi {
return &Ipmi{
runner: CommandRunner{},
}
}
func (m *Ipmi) SampleConfig() string {
return sampleConfig
}
func (m *Ipmi) Description() string {
return "Read metrics from one or many bare metal servers"
return "Read metrics from the bare metal servers via IPMI"
}
func (m *Ipmi) Gather(acc telegraf.Accumulator) error {
if m.runner == nil {
m.runner = CommandRunner{}
if len(m.path) == 0 {
return fmt.Errorf("ipmitool not found: verify that ipmitool is installed and that ipmitool is in your PATH")
}
for _, serv := range m.Servers {
err := m.gatherServer(serv, acc)
if len(m.Servers) > 0 {
for _, server := range m.Servers {
err := m.parse(acc, server)
if err != nil {
return err
}
}
} else {
err := m.parse(acc, "")
if err != nil {
return err
}
@@ -51,17 +65,26 @@ func (m *Ipmi) Gather(acc telegraf.Accumulator) error {
return nil
}
func (m *Ipmi) gatherServer(serv string, acc telegraf.Accumulator) error {
conn := NewConnection(serv)
func (m *Ipmi) parse(acc telegraf.Accumulator, server string) error {
opts := make([]string, 0)
hostname := ""
res, err := m.runner.Run(conn, "sdr")
if server != "" {
conn := NewConnection(server)
hostname = conn.Hostname
opts = conn.options()
}
opts = append(opts, "sdr")
cmd := execCommand(m.path, opts...)
out, err := internal.CombinedOutputTimeout(cmd, time.Second*5)
if err != nil {
return err
return fmt.Errorf("failed to run command %s: %s - %s", strings.Join(cmd.Args, " "), err, string(out))
}
// each line will look something like
// Planar VBAT | 3.05 Volts | ok
lines := strings.Split(res, "\n")
lines := strings.Split(string(out), "\n")
for i := 0; i < len(lines); i++ {
vals := strings.Split(lines[i], "|")
if len(vals) != 3 {
@@ -69,8 +92,12 @@ func (m *Ipmi) gatherServer(serv string, acc telegraf.Accumulator) error {
}
tags := map[string]string{
"server": conn.Hostname,
"name": transform(vals[0]),
"name": transform(vals[0]),
}
// tag the server is we have one
if hostname != "" {
tags["server"] = hostname
}
fields := make(map[string]interface{})
@@ -99,10 +126,6 @@ func (m *Ipmi) gatherServer(serv string, acc telegraf.Accumulator) error {
return nil
}
type Runner interface {
Run(conn *Connection, args ...string) (string, error)
}
func Atofloat(val string) float64 {
f, err := strconv.ParseFloat(val, 64)
if err != nil {
@@ -123,7 +146,12 @@ func transform(s string) string {
}
func init() {
m := Ipmi{}
path, _ := exec.LookPath("ipmitool")
if len(path) > 0 {
m.path = path
}
inputs.Add("ipmi_sensor", func() telegraf.Input {
return &Ipmi{}
return &m
})
}