Skip to content

Commit 21ece2d

Browse files
committed
Convert ipmi stats/tags to underscore and lowercase
closes #888
1 parent d055d7f commit 21ece2d

File tree

9 files changed

+218
-79
lines changed

9 files changed

+218
-79
lines changed

internal/internal.go

+18
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"os"
1212
"strings"
1313
"time"
14+
"unicode"
1415
)
1516

1617
const alphanum string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
@@ -122,6 +123,23 @@ func GetTLSConfig(
122123
return t, nil
123124
}
124125

126+
// SnakeCase converts the given string to snake case following the Golang format:
127+
// acronyms are converted to lower-case and preceded by an underscore.
128+
func SnakeCase(in string) string {
129+
runes := []rune(in)
130+
length := len(runes)
131+
132+
var out []rune
133+
for i := 0; i < length; i++ {
134+
if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
135+
out = append(out, '_')
136+
}
137+
out = append(out, unicode.ToLower(runes[i]))
138+
}
139+
140+
return string(out)
141+
}
142+
125143
// Glob will test a string pattern, potentially containing globs, against a
126144
// subject string. The result is a simple true/false, determining whether or
127145
// not the glob pattern matched the subject text.

internal/internal_test.go

+29
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,32 @@ func TestGlob(t *testing.T) {
4242
testGlobNoMatch(t, pattern, "this_is_a_test")
4343
}
4444
}
45+
46+
type SnakeTest struct {
47+
input string
48+
output string
49+
}
50+
51+
var tests = []SnakeTest{
52+
{"a", "a"},
53+
{"snake", "snake"},
54+
{"A", "a"},
55+
{"ID", "id"},
56+
{"MOTD", "motd"},
57+
{"Snake", "snake"},
58+
{"SnakeTest", "snake_test"},
59+
{"APIResponse", "api_response"},
60+
{"SnakeID", "snake_id"},
61+
{"SnakeIDGoogle", "snake_id_google"},
62+
{"LinuxMOTD", "linux_motd"},
63+
{"OMGWTFBBQ", "omgwtfbbq"},
64+
{"omg_wtf_bbq", "omg_wtf_bbq"},
65+
}
66+
67+
func TestSnakeCase(t *testing.T) {
68+
for _, test := range tests {
69+
if SnakeCase(test.input) != test.output {
70+
t.Errorf(`SnakeCase("%s"), wanted "%s", got \%s"`, test.input, test.output, SnakeCase(test.input))
71+
}
72+
}
73+
}

plugins/inputs/all/all.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
_ "github.com/influxdata/telegraf/plugins/inputs/haproxy"
1717
_ "github.com/influxdata/telegraf/plugins/inputs/httpjson"
1818
_ "github.com/influxdata/telegraf/plugins/inputs/influxdb"
19-
_ "github.com/influxdata/telegraf/plugins/inputs/ipmi"
19+
_ "github.com/influxdata/telegraf/plugins/inputs/ipmi_sensor"
2020
_ "github.com/influxdata/telegraf/plugins/inputs/jolokia"
2121
_ "github.com/influxdata/telegraf/plugins/inputs/kafka_consumer"
2222
_ "github.com/influxdata/telegraf/plugins/inputs/leofs"

plugins/inputs/ipmi/README.md

-50
This file was deleted.

plugins/inputs/ipmi_sensor/README.md

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Telegraf ipmi plugin
2+
3+
Get bare metal metrics using the command line utility `ipmitool`
4+
5+
see ipmitool(https://sourceforge.net/projects/ipmitool/files/ipmitool/)
6+
7+
The plugin will use the following command to collect remote host sensor stats:
8+
9+
ipmitool -I lan -H 192.168.1.1 -U USERID -P PASSW0RD sdr
10+
11+
## Measurements
12+
13+
- ipmi_sensor:
14+
15+
* Tags: `name`, `server`, `unit`
16+
* Fields:
17+
- status
18+
- value
19+
20+
## Configuration
21+
22+
```toml
23+
[[inputs.ipmi]]
24+
## specify servers via a url matching:
25+
## [username[:password]@][protocol[(address)]]
26+
## e.g.
27+
## root:passwd@lan(127.0.0.1)
28+
##
29+
servers = ["USERID:PASSW0RD@lan(10.20.2.203)"]
30+
```
31+
32+
## Output
33+
34+
```
35+
> ipmi_sensor,server=10.20.2.203,unit=degrees_c,name=ambient_temp status=1i,value=20 1458488465012559455
36+
> ipmi_sensor,server=10.20.2.203,unit=feet,name=altitude status=1i,value=80 1458488465012688613
37+
> ipmi_sensor,server=10.20.2.203,unit=watts,name=avg_power status=1i,value=220 1458488465012776511
38+
> ipmi_sensor,server=10.20.2.203,unit=volts,name=planar_3.3v status=1i,value=3.28 1458488465012861875
39+
> ipmi_sensor,server=10.20.2.203,unit=volts,name=planar_vbat status=1i,value=3.04 1458488465013072508
40+
> ipmi_sensor,server=10.20.2.203,unit=rpm,name=fan_1a_tach status=1i,value=2610 1458488465013137932
41+
> ipmi_sensor,server=10.20.2.203,unit=rpm,name=fan_1b_tach status=1i,value=1775 1458488465013279896
42+
```

plugins/inputs/ipmi/command.go renamed to plugins/inputs/ipmi_sensor/command.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// command
2-
package ipmi
1+
package ipmi_sensor
32

43
import (
54
"bytes"

plugins/inputs/ipmi/connection.go renamed to plugins/inputs/ipmi_sensor/connection.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// connection
2-
package ipmi
1+
package ipmi_sensor
32

43
import (
54
"fmt"

plugins/inputs/ipmi/ipmi.go renamed to plugins/inputs/ipmi_sensor/ipmi.go

+38-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// ipmi
2-
package ipmi
1+
package ipmi_sensor
32

43
import (
54
"strconv"
@@ -60,30 +59,41 @@ func (m *Ipmi) gatherServer(serv string, acc telegraf.Accumulator) error {
6059
return err
6160
}
6261

62+
// each line will look something like
63+
// Planar VBAT | 3.05 Volts | ok
6364
lines := strings.Split(res, "\n")
64-
6565
for i := 0; i < len(lines); i++ {
6666
vals := strings.Split(lines[i], "|")
67-
if len(vals) == 3 {
68-
tags := map[string]string{"server": conn.Hostname, "name": trim(vals[0])}
69-
fields := make(map[string]interface{})
70-
if strings.EqualFold("ok", trim(vals[2])) {
71-
fields["status"] = 1
72-
} else {
73-
fields["status"] = 0
74-
}
67+
if len(vals) != 3 {
68+
continue
69+
}
7570

76-
val1 := trim(vals[1])
71+
tags := map[string]string{
72+
"server": conn.Hostname,
73+
"name": transform(vals[0]),
74+
}
7775

78-
if strings.Index(val1, " ") > 0 {
79-
val := strings.Split(val1, " ")[0]
80-
fields["value"] = Atofloat(val)
81-
} else {
82-
fields["value"] = 0.0
83-
}
76+
fields := make(map[string]interface{})
77+
if strings.EqualFold("ok", trim(vals[2])) {
78+
fields["status"] = 1
79+
} else {
80+
fields["status"] = 0
81+
}
8482

85-
acc.AddFields("ipmi_sensor", fields, tags, time.Now())
83+
val1 := trim(vals[1])
84+
85+
if strings.Index(val1, " ") > 0 {
86+
// split middle column into value and unit
87+
valunit := strings.SplitN(val1, " ", 2)
88+
fields["value"] = Atofloat(valunit[0])
89+
if len(valunit) > 1 {
90+
tags["unit"] = transform(valunit[1])
91+
}
92+
} else {
93+
fields["value"] = 0.0
8694
}
95+
96+
acc.AddFields("ipmi_sensor", fields, tags, time.Now())
8797
}
8898

8999
return nil
@@ -96,18 +106,24 @@ type Runner interface {
96106
func Atofloat(val string) float64 {
97107
f, err := strconv.ParseFloat(val, 64)
98108
if err != nil {
99-
return float64(0)
109+
return 0.0
100110
} else {
101-
return float64(f)
111+
return f
102112
}
103113
}
104114

105115
func trim(s string) string {
106116
return strings.TrimSpace(s)
107117
}
108118

119+
func transform(s string) string {
120+
s = trim(s)
121+
s = strings.ToLower(s)
122+
return strings.Replace(s, " ", "_", -1)
123+
}
124+
109125
func init() {
110-
inputs.Add("ipmi", func() telegraf.Input {
126+
inputs.Add("ipmi_sensor", func() telegraf.Input {
111127
return &Ipmi{}
112128
})
113129
}

plugins/inputs/ipmi/ipmi_test.go renamed to plugins/inputs/ipmi_sensor/ipmi_test.go

+88-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// ipmi_test
2-
package ipmi
1+
package ipmi_sensor
32

43
import (
54
"testing"
@@ -179,6 +178,93 @@ func TestIpmi(t *testing.T) {
179178
require.NoError(t, err)
180179

181180
assert.Equal(t, acc.NFields(), 266, "non-numeric measurements should be ignored")
181+
182+
var tests = []struct {
183+
fields map[string]interface{}
184+
tags map[string]string
185+
}{
186+
{
187+
map[string]interface{}{
188+
"value": float64(20),
189+
"status": int(1),
190+
},
191+
map[string]string{
192+
"name": "ambient_temp",
193+
"server": "192.168.1.1",
194+
"unit": "degrees_c",
195+
},
196+
},
197+
{
198+
map[string]interface{}{
199+
"value": float64(80),
200+
"status": int(1),
201+
},
202+
map[string]string{
203+
"name": "altitude",
204+
"server": "192.168.1.1",
205+
"unit": "feet",
206+
},
207+
},
208+
{
209+
map[string]interface{}{
210+
"value": float64(210),
211+
"status": int(1),
212+
},
213+
map[string]string{
214+
"name": "avg_power",
215+
"server": "192.168.1.1",
216+
"unit": "watts",
217+
},
218+
},
219+
{
220+
map[string]interface{}{
221+
"value": float64(4.9),
222+
"status": int(1),
223+
},
224+
map[string]string{
225+
"name": "planar_5v",
226+
"server": "192.168.1.1",
227+
"unit": "volts",
228+
},
229+
},
230+
{
231+
map[string]interface{}{
232+
"value": float64(3.05),
233+
"status": int(1),
234+
},
235+
map[string]string{
236+
"name": "planar_vbat",
237+
"server": "192.168.1.1",
238+
"unit": "volts",
239+
},
240+
},
241+
{
242+
map[string]interface{}{
243+
"value": float64(2610),
244+
"status": int(1),
245+
},
246+
map[string]string{
247+
"name": "fan_1a_tach",
248+
"server": "192.168.1.1",
249+
"unit": "rpm",
250+
},
251+
},
252+
{
253+
map[string]interface{}{
254+
"value": float64(1775),
255+
"status": int(1),
256+
},
257+
map[string]string{
258+
"name": "fan_1b_tach",
259+
"server": "192.168.1.1",
260+
"unit": "rpm",
261+
},
262+
},
263+
}
264+
265+
for _, test := range tests {
266+
acc.AssertContainsTaggedFields(t, "ipmi_sensor", test.fields, test.tags)
267+
}
182268
}
183269

184270
func TestIpmiConnection(t *testing.T) {

0 commit comments

Comments
 (0)