Skip to content

Commit

Permalink
allow querying sensors via the open interface
Browse files Browse the repository at this point in the history
closes #2244
closes #1547
  • Loading branch information
Matteo Cerutti authored and sparrc committed Feb 2, 2017
1 parent cf3fcf3 commit c8de483
Show file tree
Hide file tree
Showing 6 changed files with 313 additions and 192 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ It is highly recommended that all users migrate to the new riemann output plugin
- [#1453](https://github.com/influxdata/telegraf/pull/1453): diskio: add support for name templates and udev tags.
- [#2277](https://github.com/influxdata/telegraf/pull/2277): add integer metrics for Consul check health state.
- [#2201](https://github.com/influxdata/telegraf/pull/2201): Add lock option to the IPtables input plugin.
- [#2244](https://github.com/influxdata/telegraf/pull/2244): Support ipmi_sensor plugin querying local ipmi sensors.

### Bugfixes

Expand Down
38 changes: 33 additions & 5 deletions plugins/inputs/ipmi_sensor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,50 @@ 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
# Read metrics from the bare metal servers via IPMI
[[inputs.ipmi_sensor]]
## 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(10.20.2.203)"]
## if no servers are specified, local machine sensor stats will be queried
##
# servers = ["USERID:PASSW0RD@lan(192.168.1.1)"]
```

## 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 +57,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
80 changes: 54 additions & 26 deletions plugins/inputs/ipmi_sensor/ipmi.go
Original file line number Diff line number Diff line change
@@ -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
}
Expand All @@ -51,26 +65,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 +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 {
Expand All @@ -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
})
}
Loading

0 comments on commit c8de483

Please sign in to comment.