Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow querying IPMI sensors via the open interface #2244

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions plugins/inputs/ipmi_sensor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,47 @@ Get bare metal metrics using the command line utility `ipmitool`

see ipmitool(https://sourceforge.net/projects/ipmitool/files/ipmitool/)

The plugin will use the following command to collect remote host sensor stats:
If no servers are specified, the plugin will query the local machine sensor stats via the following command:

ipmitool -I lan -H 192.168.1.1 -U USERID -P PASSW0RD sdr
```
ipmitool sdr
```

When one or more servers are specified, the plugin will use the following command to collect remote host sensor stats:

```
ipmitool -I lan -H SERVER -U USERID -P PASSW0RD sdr
```

## Measurements

- ipmi_sensor:

* Tags: `name`, `server`, `unit`
* Tags: `name`, `unit`
* Fields:
- status
- value

The `server` tag will be made available when retrieving stats from remote server(s).

## Configuration

```toml
[[inputs.ipmi_sensor]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should change the SampleConfig to indicate that servers is optional

## 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. If servers is omitted, the local machine sensors will be queried.
## [username[:password]@][protocol[(address)]]
## e.g.
## root:passwd@lan(127.0.0.1)
##
servers = ["USERID:PASSW0RD@lan(10.20.2.203)"]
## servers = ["USERID:PASSW0RD@lan(10.20.2.203)"]
```

## Output

When retrieving stats from a remote server:
```
> ipmi_sensor,server=10.20.2.203,unit=degrees_c,name=ambient_temp status=1i,value=20 1458488465012559455
> ipmi_sensor,server=10.20.2.203,unit=feet,name=altitude status=1i,value=80 1458488465012688613
Expand All @@ -40,3 +54,14 @@ ipmitool -I lan -H 192.168.1.1 -U USERID -P PASSW0RD sdr
> ipmi_sensor,server=10.20.2.203,unit=rpm,name=fan_1a_tach status=1i,value=2610 1458488465013137932
> ipmi_sensor,server=10.20.2.203,unit=rpm,name=fan_1b_tach status=1i,value=1775 1458488465013279896
```

When retrieving stats from the local machine (no server specified):
```
> ipmi_sensor,unit=degrees_c,name=ambient_temp status=1i,value=20 1458488465012559455
> ipmi_sensor,unit=feet,name=altitude status=1i,value=80 1458488465012688613
> ipmi_sensor,unit=watts,name=avg_power status=1i,value=220 1458488465012776511
> ipmi_sensor,unit=volts,name=planar_3.3v status=1i,value=3.28 1458488465012861875
> ipmi_sensor,unit=volts,name=planar_vbat status=1i,value=3.04 1458488465013072508
> ipmi_sensor,unit=rpm,name=fan_1a_tach status=1i,value=2610 1458488465013137932
> ipmi_sensor,unit=rpm,name=fan_1b_tach status=1i,value=1775 1458488465013279896
```
35 changes: 0 additions & 35 deletions plugins/inputs/ipmi_sensor/command.go

This file was deleted.

1 change: 0 additions & 1 deletion plugins/inputs/ipmi_sensor/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ type Connection struct {
Hostname string
Username string
Password string
Path string
Port int
Interface string
}
Expand Down
79 changes: 53 additions & 26 deletions plugins/inputs/ipmi_sensor/ipmi.go
Original file line number Diff line number Diff line change
@@ -1,48 +1,61 @@
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
## if no servers are specified, the local machine sensor stats will be queried
## [username[:password]@][protocol[(address)]]
## e.g.
## root:passwd@lan(127.0.0.1)
##
servers = ["USERID:PASSW0RD@lan(192.168.1.1)"]
## 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
}
Expand All @@ -51,26 +64,39 @@ 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 := ""

if server != "" {
conn := NewConnection(server)
hostname = conn.Hostname
opts = conn.options()
}

res, err := m.runner.Run(conn, "sdr")
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 {
continue
}

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{})
Expand Down Expand Up @@ -99,10 +125,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 {
Expand All @@ -123,7 +145,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
})
}
Loading