From a85697efb5b19c49aef85acff6f387cf64264584 Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Wed, 23 Mar 2016 09:40:38 -0600 Subject: [PATCH] Convert ipmi stats/tags to underscore and lowercase closes #888 --- internal/internal.go | 18 ++++ internal/internal_test.go | 29 ++++++ plugins/inputs/all/all.go | 2 +- plugins/inputs/ipmi/README.md | 50 ----------- plugins/inputs/ipmi_sensor/README.md | 42 +++++++++ .../inputs/{ipmi => ipmi_sensor}/command.go | 3 +- .../{ipmi => ipmi_sensor}/connection.go | 3 +- plugins/inputs/{ipmi => ipmi_sensor}/ipmi.go | 66 ++++++++------ .../inputs/{ipmi => ipmi_sensor}/ipmi_test.go | 90 ++++++++++++++++++- 9 files changed, 221 insertions(+), 82 deletions(-) delete mode 100644 plugins/inputs/ipmi/README.md create mode 100644 plugins/inputs/ipmi_sensor/README.md rename plugins/inputs/{ipmi => ipmi_sensor}/command.go (96%) rename plugins/inputs/{ipmi => ipmi_sensor}/connection.go (98%) rename plugins/inputs/{ipmi => ipmi_sensor}/ipmi.go (61%) rename plugins/inputs/{ipmi => ipmi_sensor}/ipmi_test.go (80%) diff --git a/internal/internal.go b/internal/internal.go index 8a427909e..ff73aae84 100644 --- a/internal/internal.go +++ b/internal/internal.go @@ -11,6 +11,7 @@ import ( "os" "strings" "time" + "unicode" ) const alphanum string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" @@ -122,6 +123,23 @@ func GetTLSConfig( 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 // subject string. The result is a simple true/false, determining whether or // not the glob pattern matched the subject text. diff --git a/internal/internal_test.go b/internal/internal_test.go index 7f0c687a8..e4a5eed14 100644 --- a/internal/internal_test.go +++ b/internal/internal_test.go @@ -42,3 +42,32 @@ func TestGlob(t *testing.T) { 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)) + } + } +} diff --git a/plugins/inputs/all/all.go b/plugins/inputs/all/all.go index 6c0d933c7..4f7d45f60 100644 --- a/plugins/inputs/all/all.go +++ b/plugins/inputs/all/all.go @@ -16,7 +16,7 @@ import ( _ "github.com/influxdata/telegraf/plugins/inputs/haproxy" _ "github.com/influxdata/telegraf/plugins/inputs/httpjson" _ "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/kafka_consumer" _ "github.com/influxdata/telegraf/plugins/inputs/leofs" diff --git a/plugins/inputs/ipmi/README.md b/plugins/inputs/ipmi/README.md deleted file mode 100644 index ca42f7d10..000000000 --- a/plugins/inputs/ipmi/README.md +++ /dev/null @@ -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 - diff --git a/plugins/inputs/ipmi_sensor/README.md b/plugins/inputs/ipmi_sensor/README.md new file mode 100644 index 000000000..2ece4ea20 --- /dev/null +++ b/plugins/inputs/ipmi_sensor/README.md @@ -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 +``` diff --git a/plugins/inputs/ipmi/command.go b/plugins/inputs/ipmi_sensor/command.go similarity index 96% rename from plugins/inputs/ipmi/command.go rename to plugins/inputs/ipmi_sensor/command.go index 4b3a2e81b..353c27d36 100644 --- a/plugins/inputs/ipmi/command.go +++ b/plugins/inputs/ipmi_sensor/command.go @@ -1,5 +1,4 @@ -// command -package ipmi +package ipmi_sensor import ( "bytes" diff --git a/plugins/inputs/ipmi/connection.go b/plugins/inputs/ipmi_sensor/connection.go similarity index 98% rename from plugins/inputs/ipmi/connection.go rename to plugins/inputs/ipmi_sensor/connection.go index de555a4c4..3f4461438 100644 --- a/plugins/inputs/ipmi/connection.go +++ b/plugins/inputs/ipmi_sensor/connection.go @@ -1,5 +1,4 @@ -// connection -package ipmi +package ipmi_sensor import ( "fmt" diff --git a/plugins/inputs/ipmi/ipmi.go b/plugins/inputs/ipmi_sensor/ipmi.go similarity index 61% rename from plugins/inputs/ipmi/ipmi.go rename to plugins/inputs/ipmi_sensor/ipmi.go index 8a34c8d3f..aec56a0e4 100644 --- a/plugins/inputs/ipmi/ipmi.go +++ b/plugins/inputs/ipmi_sensor/ipmi.go @@ -1,5 +1,4 @@ -// ipmi -package ipmi +package ipmi_sensor import ( "strconv" @@ -60,30 +59,41 @@ func (m *Ipmi) gatherServer(serv string, acc telegraf.Accumulator) error { return err } + // each line will look something like + // Planar VBAT | 3.05 Volts | ok lines := strings.Split(res, "\n") - for i := 0; i < len(lines); i++ { vals := strings.Split(lines[i], "|") - if len(vals) == 3 { - tags := map[string]string{"server": conn.Hostname, "name": trim(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 { - val := strings.Split(val1, " ")[0] - fields["value"] = Atofloat(val) - } else { - fields["value"] = 0.0 - } - - acc.AddFields("ipmi_sensor", fields, tags, time.Now()) + if len(vals) != 3 { + continue } + + 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 @@ -96,9 +106,9 @@ type Runner interface { func Atofloat(val string) float64 { f, err := strconv.ParseFloat(val, 64) if err != nil { - return float64(0) + return 0.0 } else { - return float64(f) + return f } } @@ -106,8 +116,14 @@ func trim(s string) string { return strings.TrimSpace(s) } +func transform(s string) string { + s = trim(s) + s = strings.ToLower(s) + return strings.Replace(s, " ", "_", -1) +} + func init() { - inputs.Add("ipmi", func() telegraf.Input { + inputs.Add("ipmi_sensor", func() telegraf.Input { return &Ipmi{} }) } diff --git a/plugins/inputs/ipmi/ipmi_test.go b/plugins/inputs/ipmi_sensor/ipmi_test.go similarity index 80% rename from plugins/inputs/ipmi/ipmi_test.go rename to plugins/inputs/ipmi_sensor/ipmi_test.go index 1e0a57bd1..c62447e39 100644 --- a/plugins/inputs/ipmi/ipmi_test.go +++ b/plugins/inputs/ipmi_sensor/ipmi_test.go @@ -1,5 +1,4 @@ -// ipmi_test -package ipmi +package ipmi_sensor import ( "testing" @@ -179,6 +178,93 @@ func TestIpmi(t *testing.T) { require.NoError(t, err) 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) {