forked from influxdata/telegraf
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ecf94b1
commit 74eb195
Showing
13 changed files
with
3,111 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,3 +21,8 @@ process.yml | |
resource.syso | ||
versioninfo.json | ||
.uuid | ||
|
||
telegraf_debug.exe | ||
*.log | ||
*.out | ||
nul |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
# IPMI Sensor Input Plugin | ||
|
||
Get bare metal metrics using the command line utility | ||
[`ipmitool`](https://github.com/ipmitool/ipmitool). | ||
|
||
If no servers are specified, the plugin will query the local machine sensor | ||
stats via the following command: | ||
|
||
```sh | ||
ipmitool sdr | ||
``` | ||
|
||
or with the version 2 schema: | ||
|
||
```sh | ||
ipmitool sdr elist | ||
``` | ||
|
||
When one or more servers are specified, the plugin will use the following | ||
command to collect remote host sensor stats: | ||
|
||
```sh | ||
ipmitool -I lan -H SERVER -U USERID -P PASSW0RD sdr | ||
``` | ||
|
||
Any of the following parameters will be added to the aforementioned query if | ||
they're configured: | ||
|
||
```sh | ||
-y hex_key -L privilege | ||
``` | ||
|
||
## Global configuration options <!-- @/docs/includes/plugin_config.md --> | ||
|
||
In addition to the plugin-specific configuration settings, plugins support | ||
additional global and plugin configuration settings. These settings are used to | ||
modify metrics, tags, and field or create aliases and configure ordering, etc. | ||
See the [CONFIGURATION.md][CONFIGURATION.md] for more details. | ||
|
||
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins | ||
|
||
## Configuration | ||
|
||
```toml @sample.conf | ||
# Read metrics from the bare metal servers via IPMI | ||
[[inputs.ipmi_sensor]] | ||
## optionally specify the path to the ipmitool executable | ||
# path = "/usr/bin/ipmitool" | ||
## | ||
## Setting 'use_sudo' to true will make use of sudo to run ipmitool. | ||
## Sudo must be configured to allow the telegraf user to run ipmitool | ||
## without a password. | ||
# use_sudo = false | ||
## | ||
## optionally force session privilege level. Can be CALLBACK, USER, OPERATOR, ADMINISTRATOR | ||
# privilege = "ADMINISTRATOR" | ||
## | ||
## optionally specify one or more servers via a url matching | ||
## [username[:password]@][protocol[(address)]] | ||
## e.g. | ||
## root:passwd@lan(127.0.0.1) | ||
## | ||
## if no servers are specified, local machine sensor stats will be queried | ||
## | ||
# servers = ["USERID:PASSW0RD@lan(192.168.1.1)"] | ||
|
||
## Recommended: use metric 'interval' that is a multiple of 'timeout' to avoid | ||
## gaps or overlap in pulled data | ||
interval = "30s" | ||
|
||
## Timeout for the ipmitool command to complete. Default is 20 seconds. | ||
timeout = "20s" | ||
|
||
## Schema Version: (Optional, defaults to version 1) | ||
metric_version = 2 | ||
|
||
## Optionally provide the hex key for the IMPI connection. | ||
# hex_key = "" | ||
|
||
## If ipmitool should use a cache | ||
## for me ipmitool runs about 2 to 10 times faster with cache enabled on HP G10 servers (when using ubuntu20.04) | ||
## the cache file may not work well for you if some sensors come up late | ||
# use_cache = false | ||
|
||
## Path to the ipmitools cache file (defaults to OS temp dir) | ||
## The provided path must exist and must be writable | ||
# cache_path = "" | ||
``` | ||
|
||
## Metrics | ||
|
||
Version 1 schema: | ||
|
||
- ipmi_sensor: | ||
- tags: | ||
- name | ||
- unit | ||
- host | ||
- server (only when retrieving stats from remote servers) | ||
- fields: | ||
- status (int, 1=ok status_code/0=anything else) | ||
- value (float) | ||
|
||
Version 2 schema: | ||
|
||
- ipmi_sensor: | ||
- tags: | ||
- name | ||
- entity_id (can help uniquify duplicate names) | ||
- status_code (two letter code from IPMI documentation) | ||
- status_desc (extended status description field) | ||
- unit (only on analog values) | ||
- host | ||
- server (only when retrieving stats from remote) | ||
- fields: | ||
- value (float) | ||
|
||
### Permissions | ||
|
||
When gathering from the local system, Telegraf will need permission to the | ||
ipmi device node. When using udev you can create the device node giving | ||
`rw` permissions to the `telegraf` user by adding the following rule to | ||
`/etc/udev/rules.d/52-telegraf-ipmi.rules`: | ||
|
||
```sh | ||
KERNEL=="ipmi*", MODE="660", GROUP="telegraf" | ||
``` | ||
|
||
Alternatively, it is possible to use sudo. You will need the following in your | ||
telegraf config: | ||
|
||
```toml | ||
[[inputs.ipmi_sensor]] | ||
use_sudo = true | ||
``` | ||
|
||
You will also need to update your sudoers file: | ||
|
||
```bash | ||
$ visudo | ||
# Add the following line: | ||
Cmnd_Alias IPMITOOL = /usr/bin/ipmitool * | ||
telegraf ALL=(root) NOPASSWD: IPMITOOL | ||
Defaults!IPMITOOL !logfile, !syslog, !pam_session | ||
``` | ||
|
||
## Example Output | ||
|
||
### Version 1 Schema | ||
|
||
When retrieving stats from a remote server: | ||
|
||
```text | ||
ipmi_sensor,server=10.20.2.203,name=uid_light value=0,status=1i 1517125513000000000 | ||
ipmi_sensor,server=10.20.2.203,name=sys._health_led status=1i,value=0 1517125513000000000 | ||
ipmi_sensor,server=10.20.2.203,name=power_supply_1,unit=watts status=1i,value=110 1517125513000000000 | ||
ipmi_sensor,server=10.20.2.203,name=power_supply_2,unit=watts status=1i,value=120 1517125513000000000 | ||
ipmi_sensor,server=10.20.2.203,name=power_supplies value=0,status=1i 1517125513000000000 | ||
ipmi_sensor,server=10.20.2.203,name=fan_1,unit=percent status=1i,value=43.12 1517125513000000000 | ||
``` | ||
|
||
When retrieving stats from the local machine (no server specified): | ||
|
||
```text | ||
ipmi_sensor,name=uid_light value=0,status=1i 1517125513000000000 | ||
ipmi_sensor,name=sys._health_led status=1i,value=0 1517125513000000000 | ||
ipmi_sensor,name=power_supply_1,unit=watts status=1i,value=110 1517125513000000000 | ||
ipmi_sensor,name=power_supply_2,unit=watts status=1i,value=120 1517125513000000000 | ||
ipmi_sensor,name=power_supplies value=0,status=1i 1517125513000000000 | ||
ipmi_sensor,name=fan_1,unit=percent status=1i,value=43.12 1517125513000000000 | ||
``` | ||
|
||
#### Version 2 Schema | ||
|
||
When retrieving stats from the local machine (no server specified): | ||
|
||
```text | ||
ipmi_sensor,name=uid_light,entity_id=23.1,status_code=ok,status_desc=ok value=0 1517125474000000000 | ||
ipmi_sensor,name=sys._health_led,entity_id=23.2,status_code=ok,status_desc=ok value=0 1517125474000000000 | ||
ipmi_sensor,entity_id=10.1,name=power_supply_1,status_code=ok,status_desc=presence_detected,unit=watts value=110 1517125474000000000 | ||
ipmi_sensor,name=power_supply_2,entity_id=10.2,status_code=ok,unit=watts,status_desc=presence_detected value=125 1517125474000000000 | ||
ipmi_sensor,name=power_supplies,entity_id=10.3,status_code=ok,status_desc=fully_redundant value=0 1517125474000000000 | ||
ipmi_sensor,entity_id=7.1,name=fan_1,status_code=ok,status_desc=transition_to_running,unit=percent value=43.12 1517125474000000000 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package ipmi_sensor | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
// Connection properties for a Client | ||
type Connection struct { | ||
Hostname string | ||
Username string | ||
Password string | ||
Port int | ||
Interface string | ||
Privilege string | ||
HexKey string | ||
} | ||
|
||
func NewConnection(server, privilege, hexKey string) *Connection { | ||
conn := &Connection{ | ||
Privilege: privilege, | ||
HexKey: hexKey, | ||
} | ||
inx1 := strings.LastIndex(server, "@") | ||
inx2 := strings.Index(server, "(") | ||
|
||
connstr := server | ||
|
||
if inx1 > 0 { | ||
security := server[0:inx1] | ||
connstr = server[inx1+1:] | ||
up := strings.SplitN(security, ":", 2) | ||
if len(up) == 2 { | ||
conn.Username = up[0] | ||
conn.Password = up[1] | ||
} | ||
} | ||
|
||
if inx2 > 0 { | ||
inx2 = strings.Index(connstr, "(") | ||
inx3 := strings.Index(connstr, ")") | ||
|
||
conn.Interface = connstr[0:inx2] | ||
conn.Hostname = connstr[inx2+1 : inx3] | ||
} | ||
|
||
return conn | ||
} | ||
|
||
func (c *Connection) options() []string { | ||
intf := c.Interface | ||
if intf == "" { | ||
intf = "lan" | ||
} | ||
|
||
options := []string{ | ||
"-H", c.Hostname, | ||
"-U", c.Username, | ||
"-P", c.Password, | ||
"-I", intf, | ||
} | ||
|
||
if c.HexKey != "" { | ||
options = append(options, "-y", c.HexKey) | ||
} | ||
if c.Port != 0 { | ||
options = append(options, "-p", strconv.Itoa(c.Port)) | ||
} | ||
if c.Privilege != "" { | ||
options = append(options, "-L", c.Privilege) | ||
} | ||
return options | ||
} | ||
|
||
// RemoteIP returns the remote (bmc) IP address of the Connection | ||
func (c *Connection) RemoteIP() string { | ||
if net.ParseIP(c.Hostname) == nil { | ||
addrs, err := net.LookupHost(c.Hostname) | ||
if err != nil && len(addrs) > 0 { | ||
return addrs[0] | ||
} | ||
} | ||
return c.Hostname | ||
} | ||
|
||
// LocalIP returns the local (client) IP address of the Connection | ||
func (c *Connection) LocalIP() string { | ||
conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", c.Hostname, c.Port)) | ||
if err != nil { | ||
// don't bother returning an error, since this value will never | ||
// make it to the bmc if we can't connect to it. | ||
return c.Hostname | ||
} | ||
_ = conn.Close() | ||
//nolint:errcheck // unable to propagate | ||
host, _, _ := net.SplitHostPort(conn.LocalAddr().String()) | ||
return host | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package ipmi_sensor | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestNewConnection(t *testing.T) { | ||
testData := []struct { | ||
addr string | ||
con *Connection | ||
}{ | ||
{ | ||
"USERID:PASSW0RD@lan(192.168.1.1)", | ||
&Connection{ | ||
Hostname: "192.168.1.1", | ||
Username: "USERID", | ||
Password: "PASSW0RD", | ||
Interface: "lan", | ||
Privilege: "USER", | ||
HexKey: "0001", | ||
}, | ||
}, | ||
{ | ||
"USERID:PASS:!@#$%^&*(234)_+W0RD@lan(192.168.1.1)", | ||
&Connection{ | ||
Hostname: "192.168.1.1", | ||
Username: "USERID", | ||
Password: "PASS:!@#$%^&*(234)_+W0RD", | ||
Interface: "lan", | ||
Privilege: "USER", | ||
HexKey: "0001", | ||
}, | ||
}, | ||
// test connection doesn't panic if incorrect symbol used | ||
{ | ||
"USERID@PASSW0RD@lan(192.168.1.1)", | ||
&Connection{ | ||
Hostname: "192.168.1.1", | ||
Username: "", | ||
Password: "", | ||
Interface: "lan", | ||
Privilege: "USER", | ||
HexKey: "0001", | ||
}, | ||
}, | ||
} | ||
|
||
for _, v := range testData { | ||
require.EqualValues(t, v.con, NewConnection(v.addr, "USER", "0001")) | ||
} | ||
} | ||
|
||
func TestGetCommandOptions(t *testing.T) { | ||
testData := []struct { | ||
connection *Connection | ||
options []string | ||
}{ | ||
{ | ||
&Connection{ | ||
Hostname: "192.168.1.1", | ||
Username: "user", | ||
Password: "password", | ||
Interface: "lan", | ||
Privilege: "USER", | ||
HexKey: "0001", | ||
}, | ||
[]string{"-H", "192.168.1.1", "-U", "user", "-P", "password", "-I", "lan", "-y", "0001", "-L", "USER"}, | ||
}, | ||
{ | ||
&Connection{ | ||
Hostname: "192.168.1.1", | ||
Username: "user", | ||
Password: "password", | ||
Interface: "lan", | ||
Privilege: "USER", | ||
HexKey: "", | ||
}, | ||
[]string{"-H", "192.168.1.1", "-U", "user", "-P", "password", "-I", "lan", "-L", "USER"}, | ||
}, | ||
} | ||
|
||
for _, data := range testData { | ||
require.EqualValues(t, data.options, data.connection.options()) | ||
} | ||
} |
Oops, something went wrong.