-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Add ARP collector for Linux #540
Conversation
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.
Thanks, looks good in general. The consensus has been to handle procfs parsing in https://github.com/prometheus/procfs and use that implementation here though.
collector/arp_linux.go
Outdated
scanner := bufio.NewScanner(data) | ||
entries := make(map[string]uint32) | ||
|
||
for scanner.Scan() { |
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.
scanner.Scan()
can fail. You need to check scanner.Err()
and handle that.
collector/arp_linux.go
Outdated
return parseArpEntries(file), nil | ||
} | ||
|
||
func parseArpEntries(data *os.File) map[string]uint32 { |
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.
The only requirement is to pass something implementing the io.Reader
interface, so I'd reflect that in the function signature.
collector/arp_linux.go
Outdated
entries := make(map[string]uint32) | ||
|
||
for scanner.Scan() { | ||
columns := strings.Split(string(scanner.Text()), " ") |
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.
scanner.Text()
returns a string. The type conversion is unnecessary.
collector/arp_linux.go
Outdated
entries := make(map[string]uint32) | ||
|
||
for scanner.Scan() { | ||
columns := strings.Split(string(scanner.Text()), " ") |
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.
Also, check strings.Fields()
Thanks for the insanely quick feedback @grobie! 🙇♂️ I've adopted the changes you suggested.
This PR implements only a tiny portion of what potentially needs to get parsed from |
You're lucky I had to push my hiking trip back by one day. Now I'm bored in
Patagonia and have time to review PRs ;-)
I'm fine with either merging the current state with a comment saying
something along the lines of "if more /proc/net/arp parsing support is
needed, move this to prometheus/procfs" or by adding full parsing support
to procfs now. Shouldn't be to hard to parse the whole line and return a
slice like []ARPEntry.
…On Sun, Apr 2, 2017 at 4:39 PM Sam Kottler ***@***.***> wrote:
Thanks for the insanely quick feedback @grobie <https://github.com/grobie>!
🙇 I've adopted the changes you suggested.
The consensus has been to handle procfs parsing in
https://github.com/prometheus/procfs and use that implementation here
though.
This PR implements only a tiny portion of what potentially needs to get
parsed from /proc/arp/net as it only covers what's needed to expose the
count per device. Is it okay to add a very much incomplete API to the
procfs package or would you prefer that more complete parsing support gets
implemented first?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#540 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAANaGCHJLZgjnK7MJcKmrMSgmEGoFuHks5rr_lvgaJpZM4Mw4_K>
.
|
collector/arp_linux.go
Outdated
entries := make(map[string]uint32) | ||
|
||
for scanner.Scan() { | ||
if err := scanner.Err(); err != nil { |
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.
Err()
will always be nil if Scan()
is not nil. Check the example in https://golang.org/pkg/bufio/#example_Scanner_custom
I added a comment in that vain to the current parsing function. If you're comfortable merging this in its current state I'm happy to follow up with |
The tests look to be failing on netbsd with ARMv5. I hope that's a CI or build issue rather than actual failure because debugging that is gonna be...challenging 😂 |
collector/arp_linux.go
Outdated
Factories["arp"] = NewArpCollector | ||
} | ||
|
||
// NewArpCollector returns a new Collector exposing ARP stats. |
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.
Arp -> ARP (https://github.com/golang/go/wiki/CodeReviewComments#initialisms)
Same below.
collector/arp_linux.go
Outdated
return &arpCollector{ | ||
count: prometheus.NewDesc( | ||
prometheus.BuildFQName(Namespace, "arp", | ||
"count"), "ARP entries by device", |
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.
This is kind of a confusing indentation/line break combination. If a function call is broken into multiple lines, I wouldn't then expect some other argument to follow it on the same line...
How about:
count: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, "arp", "count"),
"ARP entries by device",
[]string{"device"}, nil,
),
collector/arp_linux.go
Outdated
return entries, nil | ||
} | ||
|
||
// This should get extracted to the github.com/prometheus/procfs package to |
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 a TODO:
to the beginning of this.
@skottler Regarding the test errors, I don't see how you could be causing them, given that you are not introducing any architecture-specific functionality, or even cgo stuff... |
collector/arp_linux.go
Outdated
for scanner.Scan() { | ||
columns := strings.Fields(scanner.Text()) | ||
|
||
if columns[0] != "IP" { |
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.
This will panic if strings.Fields
returns an empty slice, so it's probably worth adding a bounds check too.
https://play.golang.org/p/09sEiI2gEN
👋 Hey Sam!
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.
Yes, always bounds check first!
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.
A few more comments ;-)
collector/arp_linux.go
Outdated
func NewARPCollector() (Collector, error) { | ||
return &arpCollector{ | ||
count: prometheus.NewDesc( | ||
prometheus.BuildFQName(Namespace, "arp", "count"), |
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 don't include things like count
in gauge metric names. I'd suggest node_arp_entries
or maybe node_arp_hosts
?
collector/arp_linux.go
Outdated
for scanner.Scan() { | ||
columns := strings.Fields(scanner.Text()) | ||
|
||
if len(columns) > 0 { |
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 for all the nitpicking. Idiomatic go tries to reduce the amount of nesting as much as possible, so if len(columns) == 0 { continue }
would be more common here. Though, I think we should actually return an error here, such line would be completely unexpected and tell us about either a broken or changed ARP file. So I suggest:
if len(columns) < 6 {
return nil, fmt.Errorf("unexpected ARP table file")
}
// ...
collector/arp_linux.go
Outdated
// to support more complete parsing of /proc/net/arp. Instead of adding | ||
// more fields to this function's return values it should get moved and | ||
// changed to support each field. | ||
func parseArpEntries(data io.Reader) (map[string]uint32, 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.
s/Arp/ARP/
collector/arp_linux.go
Outdated
}, nil | ||
} | ||
|
||
func getArpEntries() (map[string]uint32, 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.
s/Arp/ARP/
return fmt.Errorf("could not get ARP entries: %s", err) | ||
} | ||
|
||
for device, entryCount := range entries { |
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.
As a note, it's common in go to use single letter variables in such short loops. I'm fine with the descriptive names though.
Seems like this would be a reasonable collector to enable by default as well. Thoughts on that from anyone? |
👍
Please also add it to the readme.
…On Sun, Apr 2, 2017, 21:19 Matt Layher ***@***.***> wrote:
Seems like this would be a reasonable collector to enable by default as
well. Thoughts on that from anyone?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#540 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAANaOGIDJtC4cUvesSdBasE9llh7T1_ks5rsDsWgaJpZM4Mw4_K>
.
|
I believe the test failures are the result of @juliusv latest change to build Prometheus components with go1.8 by default. Maybe our build yoda @sdurrheimer can help here? :-) |
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.
This is great, thanks!
Build issues fixed in master. |
* Implement commonalities and linux support for ARP collection * Add ARP collector to fixtures and run as part of e2e tests * Bubble up scanner errors * Use single return values where it makes sense * Add missing annotation * Move arp_common into arp_linux * Add license header to arp_linux.go * Address initial feedback * Use strings.Fields instead of strings.Split * Deal with scanner.Err() rather than throwing away errors * Check for scan errors in-line before interacting with the entries map * Don't interact with potentially empty text from scan * Check for scan errors outside the scan loop * Add comment about moving procfs parsing * Add more direct comment * Update initialism style to match go style guide * Put function args on the same line * Add TODO in front of comment about procfs extraction * Guard against strings.Fields returning an empty slice * Be more defensive about ARP table format and use upcase more broadly * Enable the ARP collector by default * Add ARP collector to the README * Remove 'entry'
This introduces a simple ARP collector with initial support for Linux. It parses
/proc/net/arp
and emits a count of ARP entries on a per-device basis./cc #535