diff --git a/plugins/inputs/upsd/README.md b/plugins/inputs/upsd/README.md index 250ecf6c4..fabec8b68 100644 --- a/plugins/inputs/upsd/README.md +++ b/plugins/inputs/upsd/README.md @@ -26,6 +26,12 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details. # port = 3493 # username = "user" # password = "password" + + ## Force parsing numbers as floats + ## It is highly recommended to enable this setting to parse numbers + ## consistently as floats to avoid database conflicts where some numbers are + ## parsed as integers and others as floats. + # force_float = false ``` ## Metrics diff --git a/plugins/inputs/upsd/sample.conf b/plugins/inputs/upsd/sample.conf index 2c0a1960a..03387e504 100644 --- a/plugins/inputs/upsd/sample.conf +++ b/plugins/inputs/upsd/sample.conf @@ -5,3 +5,9 @@ # port = 3493 # username = "user" # password = "password" + + ## Force parsing numbers as floats + ## It is highly recommended to enable this setting to parse numbers + ## consistently as floats to avoid database conflicts where some numbers are + ## parsed as integers and others as floats. + # force_float = false diff --git a/plugins/inputs/upsd/upsd.go b/plugins/inputs/upsd/upsd.go index 9b2876709..6571c7cb1 100644 --- a/plugins/inputs/upsd/upsd.go +++ b/plugins/inputs/upsd/upsd.go @@ -22,11 +22,13 @@ const defaultAddress = "127.0.0.1" const defaultPort = 3493 type Upsd struct { - Server string - Port int - Username string - Password string - Log telegraf.Logger `toml:"-"` + Server string `toml:"server"` + Port int `toml:"port"` + Username string `toml:"username"` + Password string `toml:"password"` + ForceFloat bool `toml:"force_float"` + + Log telegraf.Logger `toml:"-"` batteryRuntimeTypeWarningIssued bool } @@ -71,27 +73,51 @@ func (u *Upsd) gatherUps(acc telegraf.Accumulator, name string, variables []nut. } fields := map[string]interface{}{ - "status_flags": status, - "ups_status": metrics["ups.status"], - "input_voltage": metrics["input.voltage"], - "load_percent": metrics["ups.load"], - "battery_charge_percent": metrics["battery.charge"], - "time_left_ns": timeLeftS * 1_000_000_000, //Compatibility with apcupsd metrics format - "output_voltage": metrics["output.voltage"], - "internal_temp": metrics["ups.temperature"], - "battery_voltage": metrics["battery.voltage"], - "input_frequency": metrics["input.frequency"], - "nominal_input_voltage": metrics["input.voltage.nominal"], - "nominal_battery_voltage": metrics["battery.voltage.nominal"], - "nominal_power": metrics["ups.realpower.nominal"], - "battery_date": metrics["battery.mfr.date"], + "battery_date": metrics["battery.mfr.date"], + "status_flags": status, + //Compatibility with apcupsd metrics format + "time_left_ns": timeLeftS * 1_000_000_000, + "ups_status": metrics["ups.status"], + } + + floatValues := map[string]string{ + "battery_charge_percent": "battery.charge", + "battery_voltage": "battery.voltage", + "input_frequency": "input.frequency", + "input_voltage": "input.voltage", + "internal_temp": "ups.temperature", + "load_percent": "ups.load", + "nominal_battery_voltage": "battery.voltage.nominal", + "nominal_input_voltage": "input.voltage.nominal", + "nominal_power": "ups.realpower.nominal", + "output_voltage": "output.voltage", + } + + for key, rawValue := range floatValues { + if metrics[rawValue] == nil { + continue + } + + if !u.ForceFloat { + fields[key] = metrics[rawValue] + continue + } + + // Force expected float values to actually being float (e.g. if delivered as int) + float, err := internal.ToFloat64(metrics[rawValue]) + if err != nil { + acc.AddError(fmt.Errorf("converting %s=%v failed: %v", rawValue, metrics[rawValue], err)) + continue + } + fields[key] = float } val, err := internal.ToString(metrics["ups.firmware"]) if err != nil { acc.AddError(fmt.Errorf("converting ups.firmware=%v failed: %v", metrics["ups.firmware"], err)) + } else { + fields["firmware"] = val } - fields["firmware"] = val acc.AddFields("upsd", fields, tags) } diff --git a/plugins/inputs/upsd/upsd_test.go b/plugins/inputs/upsd/upsd_test.go index c3a611b76..75bf23d52 100644 --- a/plugins/inputs/upsd/upsd_test.go +++ b/plugins/inputs/upsd/upsd_test.go @@ -15,15 +15,44 @@ func TestUpsdGather(t *testing.T) { var ( tests = []struct { - name string - err bool - tags map[string]string - fields map[string]interface{} - out func() []interaction + name string + forceFloat bool + err bool + tags map[string]string + fields map[string]interface{} + out func() []interaction }{ { - name: "test listening server with output", - err: false, + name: "test listening server with output", + forceFloat: false, + err: false, + tags: map[string]string{ + "serial": "ABC123", + "ups_name": "fake", + "model": "Model 12345", + "status_OL": "true", + }, + fields: map[string]interface{}{ + "status_flags": uint64(8), + "ups_status": "OL", + "battery_charge_percent": float64(100), + "battery_voltage": float64(13.4), + "input_voltage": float64(242), + "load_percent": float64(23), + "output_voltage": float64(230), + "time_left_ns": int64(600000000000), + "nominal_input_voltage": float64(230), + "nominal_battery_voltage": float64(24), + "nominal_power": int64(700), + "firmware": "CUSTOM_FIRMWARE", + "battery_date": "2016-07-26", + }, + out: genOutput, + }, + { + name: "test listening server with output & force floats", + forceFloat: true, + err: false, tags: map[string]string{ "serial": "ABC123", "ups_name": "fake", @@ -35,9 +64,7 @@ func TestUpsdGather(t *testing.T) { "ups_status": "OL", "battery_charge_percent": float64(100), "battery_voltage": float64(13.4), - "input_frequency": nil, "input_voltage": float64(242), - "internal_temp": nil, "load_percent": float64(23), "output_voltage": float64(230), "time_left_ns": int64(600000000000), @@ -63,6 +90,7 @@ func TestUpsdGather(t *testing.T) { nut.Server = (lAddr.(*net.TCPAddr)).IP.String() nut.Port = (lAddr.(*net.TCPAddr)).Port + nut.ForceFloat = tt.forceFloat err = nut.Gather(&acc) if tt.err {