-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Added support for apcupsd #2552
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Does circle-ci not allow tcp server/client? The tests worked on my machine...
I found a bug report here: golang/go#18806 I'll try to fix the tests by binding to ipv4 Edit: ive now fixed the tcp issue using suggested workarround in the golang issue. |
c7615a7
to
d69c277
Compare
@danielnelson have i missed anything or why does approval take such a long time? |
I'm spending most of my time on bugs, but I'm planning to review this soon. |
Normally we don't bundle libraries, where is the apcupsd_client library from? |
Wouldn't it be better to support http://networkupstools.org/ rather than just apcupsd? That way it will cover more UPS manufacturers or am I missing something? |
@danielnelson Just did it the same way as in https://github.com/influxdata/telegraf/tree/master/plugins/inputs/hddtemp/go-hddtemp The alternative would have been to fork it on my github user and then use it as a dep. But since its so small i didnt see the point. Or just put those files directly in the main apcupsd folder. @kylejvrsa I dont know. I've only ever used apcupsd in the last 15 years. |
From what I can tell, according to Debian popcon apcupsd is about twice as popular as nut, though we could definitely have plugins for both of them. Unless there is a common API I think this would be required. @jonaz Have you opened a pull request to mdlayher/apcupsd? Ideally the changes could be incorporated upstream so we don't need to have a fork. |
@danielnelson no i have not opened a PR there. |
Can you open the PR and set this up to use the library? |
Sorry for the delay. Beeing a parent is a new thing for me :) A PR is open upstream now: mdlayher/apcupsd#4 Once thats merged i will update this PR and add it with gdm. |
d69c277
to
55ca027
Compare
@danielnelson now using upstream library! |
plugins/inputs/apcupsd/apcupsd.go
Outdated
"input_voltage": status.LineVoltage, | ||
"load_percent": status.LoadPercent, | ||
"battery_charge_percent": status.BatteryChargePercent, | ||
"time_left": status.TimeLeft.Minutes(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you select minutes as the unit for this field, is this the max resolution?
We should add the unit to the field name too, time_left_minutes
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What we get from apcupsd is in minutes: TIMELEFT : 103.0 Minutes
I will suffix unit.
plugins/inputs/apcupsd/apcupsd.go
Outdated
} | ||
|
||
fields := map[string]interface{}{ | ||
"online": status.Status == "ONLINE", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should have a tag for status? We can still have this information encoded as a field but we should consider encoding it as an int instead of a bool, which while harder to understand does work better with many visualization programs.
plugins/inputs/apcupsd/apcupsd.go
Outdated
} | ||
|
||
func fetchStatus(addr string) (*apcupsd.Status, error) { | ||
client, err := apcupsd.Dial("tcp", addr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to add timeouts on all the network operations, perhaps using a Context
? This would require a modification in the upstream library but would prevent network issues from making Telegraf unresponsive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should that timeout be configurable in the telegraf config or should i just set it to like 5 sec?
PR is open upstream to support DialContext: mdlayher/apcupsd#5
EDIT: i will make it configurable
b68e0ad
to
31c6279
Compare
@danielnelson addressed comments |
can this be merged? I would really like to start using this. |
} | ||
defer client.Close() | ||
|
||
return client.Status() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we get read and write timeouts in apcupsd (StatusContext(ctx)
or such)? We can use the same timeout for the entire operation: dial + status.
plugins/inputs/apcupsd/apcupsd.go
Outdated
var sampleConfig = ` | ||
# a list of running apcupsd server to connect to. | ||
# If not provided will default to 127.0.0.1:3551 | ||
servers = ["127.0.0.1:3551"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I didn't mention this sooner, but let's make this a URL: "tcp://127.0.0.1:3551"
and use url.Parse
to split it up. This will allow ipv6 support and we won't have to adjust it later (something I have been doing a lot of recently).
@jonaz have you the time to look at this again? |
Yes when I'm back from parental leave in October
…On Thu, Jul 19, 2018, 09:58 Daniel Wendler ***@***.***> wrote:
@jonaz <https://github.com/jonaz> have you the time to look at this again?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2552 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABF-FRhSWee4jPc0BiBg_KhJx5AUSspQks5uIDwNgaJpZM4Mi4HW>
.
|
@jonaz, if you'd like to cherry-pick these two commits: ac2b3ca 14bfe14 from this branch, we can move forward merging. |
I suppose 029017e wouldn't hurt either, then merge master in |
@glinton rebased on latest master and merged your 3 commits. |
@@ -3,14 +3,14 @@ | |||
|
|||
[[projects]] | |||
branch = "master" | |||
digest = "1:d7582b4af1b0b953ff2bb9573a50f787c7e1669cb148fb086a3d1c670a1ac955" | |||
digest = "1:fc0802104acded1f48e4860a9f2db85b82b4a754fca9eae750ff4e8b8cdf2116" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to regenerate with dep v0.4.1 to avoid lockfile churn. Should just be installing the right version and running dep ensure
"github.com/influxdata/telegraf/testutil" | ||
) | ||
|
||
func TestApcupsdGather(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to split the tests out into individual test cases. Each test should try to check only one thing, and similar tests can use a table driven test if possible.
} | ||
|
||
if testing.Short() { | ||
t.Skip("Skipping network dependent tests in short mode") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is okay to not skip the network tests so long as they don't require any outside services to be running, and they complete immediately.
in := make([]byte, 128) | ||
n, err := conn.Read(in) | ||
if err != nil { | ||
t.Fatal(fmt.Sprintf("failed to read from connection - %s", err.Error())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use testify for these messages:
require.NoError(t, err)
If you want a custom error message:
require.NoError(t, err, "failed to read from connection")
Make sure you shutdown correctly...
"internal_temp": status.InternalTemp, | ||
"battery_voltage": status.BatteryVoltage, | ||
"input_frequency": status.LineFrequency, | ||
"time_on_battery": status.TimeOnBattery.Minutes(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add units: time_on_battery_minutes
or minutes_on_battery
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I now believe here it would be best to convert to nanoseconds and report the field as time_on_battery_ns
. We are moving to this encoding for time durations and it can be shown easily in Grafana using the time -> nanoseconds unit.
tags := map[string]string{ | ||
"serial": status.SerialNumber, | ||
"ups_name": status.UPSName, | ||
"online": status.Status, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be status=ONLINE
because it would be confusing if we have online=OFFLINE
.
} | ||
|
||
fields := map[string]interface{}{ | ||
"online": boolToInt(status.Status == "ONLINE"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have this be a key that is different from the tag name to simplify querying the data, if we change the tag to status
then this should be status_code
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
status can be all those things. so the most correct think here would be to put status_online
as a 1/0 and keep the whole text in tag status
// Now output status in human readable form
statstr = "";
if (status & UPS_calibration)
statstr += "CAL ";
if (status & UPS_trim)
statstr += "TRIM ";
if (status & UPS_boost)
statstr += "BOOST ";
if (status & UPS_online)
statstr += "ONLINE ";
if (status & UPS_onbatt)
statstr += "ONBATT ";
if (status & UPS_overload)
statstr += "OVERLOAD ";
if (status & UPS_battlow)
statstr += "LOWBATT ";
if (status & UPS_replacebatt)
statstr += "REPLACEBATT ";
if (!(status & UPS_battpresent))
statstr += "NOBATT ";
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, sets are a little tricky for us to encode nicely.
I think this combined status string might be hard to use in queries, because you may need to look for many string combinations to select what the series you are interested in. Looking at that code snippet and also these status-bits, and doing more interpretation (which could get us into trouble), it seems like the UPS status can be consider one of: online, on_battery, or unknown. The battery can be: okay, low, no_battery, or replace. Finally there are warning that output is overloaded, and informational messages about cal, trim, and boost.
What if we expose the ups status values and battery status as tags + enum field, and the other status flags as boolean fields. I'm on the fence if overload should be a tag.
apcupsd,status=online,battery=ok status_code=0i,battery_code=0i,overload=false,smarttrim=false,smartboost=false,calibration=false
@jonaz did you see any chance to get this one ready for 1.10. ? |
@MorphBonehunter I dont think so. My spare time is limited at the moment. But if someone else would like this in and can address the comments in this PR im as happy as the other people waiting to get this in :) |
Required for all PRs: