Convert ipmi stats/tags to underscore and lowercase

closes #888
This commit is contained in:
Cameron Sparr 2016-03-23 09:40:38 -06:00 committed by Michele Fadda
parent f95ec898a9
commit a85697efb5
9 changed files with 221 additions and 82 deletions

View File

@ -11,6 +11,7 @@ import (
"os" "os"
"strings" "strings"
"time" "time"
"unicode"
) )
const alphanum string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" const alphanum string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
@ -122,6 +123,23 @@ func GetTLSConfig(
return t, nil return t, nil
} }
// SnakeCase converts the given string to snake case following the Golang format:
// acronyms are converted to lower-case and preceded by an underscore.
func SnakeCase(in string) string {
runes := []rune(in)
length := len(runes)
var out []rune
for i := 0; i < length; i++ {
if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
out = append(out, '_')
}
out = append(out, unicode.ToLower(runes[i]))
}
return string(out)
}
// Glob will test a string pattern, potentially containing globs, against a // Glob will test a string pattern, potentially containing globs, against a
// subject string. The result is a simple true/false, determining whether or // subject string. The result is a simple true/false, determining whether or
// not the glob pattern matched the subject text. // not the glob pattern matched the subject text.

View File

@ -42,3 +42,32 @@ func TestGlob(t *testing.T) {
testGlobNoMatch(t, pattern, "this_is_a_test") testGlobNoMatch(t, pattern, "this_is_a_test")
} }
} }
type SnakeTest struct {
input string
output string
}
var tests = []SnakeTest{
{"a", "a"},
{"snake", "snake"},
{"A", "a"},
{"ID", "id"},
{"MOTD", "motd"},
{"Snake", "snake"},
{"SnakeTest", "snake_test"},
{"APIResponse", "api_response"},
{"SnakeID", "snake_id"},
{"SnakeIDGoogle", "snake_id_google"},
{"LinuxMOTD", "linux_motd"},
{"OMGWTFBBQ", "omgwtfbbq"},
{"omg_wtf_bbq", "omg_wtf_bbq"},
}
func TestSnakeCase(t *testing.T) {
for _, test := range tests {
if SnakeCase(test.input) != test.output {
t.Errorf(`SnakeCase("%s"), wanted "%s", got \%s"`, test.input, test.output, SnakeCase(test.input))
}
}
}

View File

@ -16,7 +16,7 @@ import (
_ "github.com/influxdata/telegraf/plugins/inputs/haproxy" _ "github.com/influxdata/telegraf/plugins/inputs/haproxy"
_ "github.com/influxdata/telegraf/plugins/inputs/httpjson" _ "github.com/influxdata/telegraf/plugins/inputs/httpjson"
_ "github.com/influxdata/telegraf/plugins/inputs/influxdb" _ "github.com/influxdata/telegraf/plugins/inputs/influxdb"
_ "github.com/influxdata/telegraf/plugins/inputs/ipmi" _ "github.com/influxdata/telegraf/plugins/inputs/ipmi_sensor"
_ "github.com/influxdata/telegraf/plugins/inputs/jolokia" _ "github.com/influxdata/telegraf/plugins/inputs/jolokia"
_ "github.com/influxdata/telegraf/plugins/inputs/kafka_consumer" _ "github.com/influxdata/telegraf/plugins/inputs/kafka_consumer"
_ "github.com/influxdata/telegraf/plugins/inputs/leofs" _ "github.com/influxdata/telegraf/plugins/inputs/leofs"

View File

@ -1,50 +0,0 @@
# Telegraf ipmi plugin
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:
ipmitool -I lan -H 192.168.1.1 -U USERID -P PASSW0RD sdr
## Measurements
- ipmi_sensor:
* Tags: `server`,`host`
* Fields:
- status
- value
## Configuration
```toml
[[inputs.ipmi]]
## specify 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)"]
```
## Output
> ipmi_sensor,host=10.20.2.203,inst=Ambient\ Temp status=1i,value=20 1458488465012559455
> ipmi_sensor,host=10.20.2.203,inst=Altitude status=1i,value=80 1458488465012688613
> ipmi_sensor,host=10.20.2.203,inst=Avg\ Power status=1i,value=220 1458488465012776511
> ipmi_sensor,host=10.20.2.203,inst=Planar\ 3.3V status=1i,value=3.28 1458488465012861875
> ipmi_sensor,host=10.20.2.203,inst=Planar\ 5V status=1i,value=4.9 1458488465012944188
> ipmi_sensor,host=10.20.2.203,inst=Planar\ 12V status=1i,value=12.04 1458488465013008485
> ipmi_sensor,host=10.20.2.203,inst=Planar\ VBAT status=1i,value=3.04 1458488465013072508
> ipmi_sensor,host=10.20.2.203,inst=Fan\ 1A\ Tach status=1i,value=2610 1458488465013137932
> ipmi_sensor,host=10.20.2.203,inst=Fan\ 1B\ Tach status=1i,value=1775 1458488465013279896
> ipmi_sensor,host=10.20.2.203,inst=Fan\ 2A\ Tach status=1i,value=1972 1458488465013358177
> ipmi_sensor,host=10.20.2.203,inst=Fan\ 2B\ Tach status=1i,value=1275 1458488465013434023
> ipmi_sensor,host=10.20.2.203,inst=Fan\ 3A\ Tach status=1i,value=2929 1458488465013514567
> ipmi_sensor,host=10.20.2.203,inst=Fan\ 3B\ Tach status=1i,value=2125 1458488465013582616
> ipmi_sensor,host=10.20.2.203,inst=Fan\ 1 status=1i,value=0 1458488465013643746
> ipmi_sensor,host=10.20.2.203,inst=Fan\ 2 status=1i,value=0 1458488465013714887
> ipmi_sensor,host=10.20.2.203,inst=Fan\ 3 status=1i,value=0 1458488465013861854

View File

@ -0,0 +1,42 @@
# Telegraf ipmi plugin
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:
ipmitool -I lan -H 192.168.1.1 -U USERID -P PASSW0RD sdr
## Measurements
- ipmi_sensor:
* Tags: `name`, `server`, `unit`
* Fields:
- status
- value
## Configuration
```toml
[[inputs.ipmi]]
## specify 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)"]
```
## Output
```
> 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
> ipmi_sensor,server=10.20.2.203,unit=watts,name=avg_power status=1i,value=220 1458488465012776511
> ipmi_sensor,server=10.20.2.203,unit=volts,name=planar_3.3v status=1i,value=3.28 1458488465012861875
> ipmi_sensor,server=10.20.2.203,unit=volts,name=planar_vbat status=1i,value=3.04 1458488465013072508
> 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
```

View File

@ -1,5 +1,4 @@
// command package ipmi_sensor
package ipmi
import ( import (
"bytes" "bytes"

View File

@ -1,5 +1,4 @@
// connection package ipmi_sensor
package ipmi
import ( import (
"fmt" "fmt"

View File

@ -1,5 +1,4 @@
// ipmi package ipmi_sensor
package ipmi
import ( import (
"strconv" "strconv"
@ -60,30 +59,41 @@ func (m *Ipmi) gatherServer(serv string, acc telegraf.Accumulator) error {
return err return err
} }
// each line will look something like
// Planar VBAT | 3.05 Volts | ok
lines := strings.Split(res, "\n") lines := strings.Split(res, "\n")
for i := 0; i < len(lines); i++ { for i := 0; i < len(lines); i++ {
vals := strings.Split(lines[i], "|") vals := strings.Split(lines[i], "|")
if len(vals) == 3 { if len(vals) != 3 {
tags := map[string]string{"server": conn.Hostname, "name": trim(vals[0])} continue
fields := make(map[string]interface{})
if strings.EqualFold("ok", trim(vals[2])) {
fields["status"] = 1
} else {
fields["status"] = 0
}
val1 := trim(vals[1])
if strings.Index(val1, " ") > 0 {
val := strings.Split(val1, " ")[0]
fields["value"] = Atofloat(val)
} else {
fields["value"] = 0.0
}
acc.AddFields("ipmi_sensor", fields, tags, time.Now())
} }
tags := map[string]string{
"server": conn.Hostname,
"name": transform(vals[0]),
}
fields := make(map[string]interface{})
if strings.EqualFold("ok", trim(vals[2])) {
fields["status"] = 1
} else {
fields["status"] = 0
}
val1 := trim(vals[1])
if strings.Index(val1, " ") > 0 {
// split middle column into value and unit
valunit := strings.SplitN(val1, " ", 2)
fields["value"] = Atofloat(valunit[0])
if len(valunit) > 1 {
tags["unit"] = transform(valunit[1])
}
} else {
fields["value"] = 0.0
}
acc.AddFields("ipmi_sensor", fields, tags, time.Now())
} }
return nil return nil
@ -96,9 +106,9 @@ type Runner interface {
func Atofloat(val string) float64 { func Atofloat(val string) float64 {
f, err := strconv.ParseFloat(val, 64) f, err := strconv.ParseFloat(val, 64)
if err != nil { if err != nil {
return float64(0) return 0.0
} else { } else {
return float64(f) return f
} }
} }
@ -106,8 +116,14 @@ func trim(s string) string {
return strings.TrimSpace(s) return strings.TrimSpace(s)
} }
func transform(s string) string {
s = trim(s)
s = strings.ToLower(s)
return strings.Replace(s, " ", "_", -1)
}
func init() { func init() {
inputs.Add("ipmi", func() telegraf.Input { inputs.Add("ipmi_sensor", func() telegraf.Input {
return &Ipmi{} return &Ipmi{}
}) })
} }

View File

@ -1,5 +1,4 @@
// ipmi_test package ipmi_sensor
package ipmi
import ( import (
"testing" "testing"
@ -179,6 +178,93 @@ func TestIpmi(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, acc.NFields(), 266, "non-numeric measurements should be ignored") assert.Equal(t, acc.NFields(), 266, "non-numeric measurements should be ignored")
var tests = []struct {
fields map[string]interface{}
tags map[string]string
}{
{
map[string]interface{}{
"value": float64(20),
"status": int(1),
},
map[string]string{
"name": "ambient_temp",
"server": "192.168.1.1",
"unit": "degrees_c",
},
},
{
map[string]interface{}{
"value": float64(80),
"status": int(1),
},
map[string]string{
"name": "altitude",
"server": "192.168.1.1",
"unit": "feet",
},
},
{
map[string]interface{}{
"value": float64(210),
"status": int(1),
},
map[string]string{
"name": "avg_power",
"server": "192.168.1.1",
"unit": "watts",
},
},
{
map[string]interface{}{
"value": float64(4.9),
"status": int(1),
},
map[string]string{
"name": "planar_5v",
"server": "192.168.1.1",
"unit": "volts",
},
},
{
map[string]interface{}{
"value": float64(3.05),
"status": int(1),
},
map[string]string{
"name": "planar_vbat",
"server": "192.168.1.1",
"unit": "volts",
},
},
{
map[string]interface{}{
"value": float64(2610),
"status": int(1),
},
map[string]string{
"name": "fan_1a_tach",
"server": "192.168.1.1",
"unit": "rpm",
},
},
{
map[string]interface{}{
"value": float64(1775),
"status": int(1),
},
map[string]string{
"name": "fan_1b_tach",
"server": "192.168.1.1",
"unit": "rpm",
},
},
}
for _, test := range tests {
acc.AssertContainsTaggedFields(t, "ipmi_sensor", test.fields, test.tags)
}
} }
func TestIpmiConnection(t *testing.T) { func TestIpmiConnection(t *testing.T) {