Expose v4/v6-only connection-schemes through GosnmpWrapper#8804
Expose v4/v6-only connection-schemes through GosnmpWrapper#8804reimda merged 10 commits intoinfluxdata:masterfrom
Conversation
There was a problem hiding this comment.
Thanks so much for the pull request!
🤝 ✒️ Just a reminder that the CLA has not yet been signed, and we'll need it before merging. Please sign the CLA when you get a chance, then post a comment here saying !signed-cla
|
!signed-cla |
There was a problem hiding this comment.
🤝 ✒️ Just a reminder that the CLA has not yet been signed, and we'll need it before merging. Please sign the CLA when you get a chance, then post a comment here saying !signed-cla
|
!signed-cla |
| ## scheme: either udp, udp4, udp6, tcp, tcp4, tcp6. | ||
| ## default is udp | ||
| ## example: agents = ["udp://127.0.0.1:161"] | ||
| ## agents = ["tcp://127.0.0.1:161"] |
There was a problem hiding this comment.
I would keep the tcp example.
There was a problem hiding this comment.
I have re-added the example.
plugins/inputs/snmp/README.md
Outdated
| ```toml | ||
| [[inputs.snmp]] | ||
| ## Agent addresses to retrieve values from. | ||
| ## format: agents = ["<scheme>://<hostname>:<port>"] |
There was a problem hiding this comment.
I would mention that scheme and port are optional.
|
Could you check your markup for the checkbox? Should be |
internal/snmp/wrapper.go
Outdated
| gs.Transport = "udp4" | ||
| case "udp6": | ||
| gs.Transport = "udp6" | ||
| case "", "udp": |
There was a problem hiding this comment.
I assume the empty string can be removed?
| case "", "udp": | |
| case "udp": |
There was a problem hiding this comment.
Gosnmp will default to "udp" if no scheme is specified.
However, in userconfig we can omit the "udp://" which will then cause the switch-case to fall through to the default-case.
Regarding #7879:
The default case is reached when an invalid scheme is specified, but telegraf does not output the error nor stop execution. I will look into it further.
There was a problem hiding this comment.
Correct, but isn't that handled by this part?
telegraf/internal/snmp/wrapper.go
Lines 158 to 160 in 3b8df55
There was a problem hiding this comment.
yes, you're right.
I removed the line.
plugins/inputs/snmp/snmp.go
Outdated
|
|
||
| const description = `Retrieves SNMP values from remote agents` | ||
| const sampleConfig = ` | ||
| [[inputs.snmp]] |
There was a problem hiding this comment.
I think this line addition is causing the test suite to fail..
There was a problem hiding this comment.
I removed the line.
Thank you for your patience!
internal/snmp/wrapper.go
Outdated
| case "udp4": | ||
| gs.Transport = "udp4" | ||
| case "udp6": | ||
| gs.Transport = "udp6" | ||
| case "udp": | ||
| gs.Transport = "udp" |
There was a problem hiding this comment.
Maybe keep a more logical ordering?
| case "udp4": | |
| gs.Transport = "udp4" | |
| case "udp6": | |
| gs.Transport = "udp6" | |
| case "udp": | |
| gs.Transport = "udp" | |
| case "udp": | |
| gs.Transport = "udp" | |
| case "udp4": | |
| gs.Transport = "udp4" | |
| case "udp6": | |
| gs.Transport = "udp6" |
| switch u.Scheme { | ||
| case "tcp": | ||
| gs.Transport = "tcp" | ||
| case "tcp4": | ||
| gs.Transport = "tcp4" | ||
| case "tcp6": | ||
| gs.Transport = "tcp6" | ||
| case "udp": | ||
| gs.Transport = "udp" | ||
| case "udp4": | ||
| gs.Transport = "udp4" | ||
| case "udp6": | ||
| gs.Transport = "udp6" | ||
| case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": | ||
| gs.Transport = u.Scheme | ||
| default: | ||
| return fmt.Errorf("unsupported scheme: %v", u.Scheme) | ||
| } |
There was a problem hiding this comment.
This got me thinking, why even bother checking it? This block could be replaced with gs.Transport = u.Scheme and gosnmp will handle it further when connecting. So the snmp input plugin will always support all the schemes that gosnmp is supporting..
There was a problem hiding this comment.
Hi and thank you for you reviews! This is a pleasant experience!
I thought the same thing but I am not sure of the side-effects that the remaining schemes like unix-sockets have. I am not sure if this plays well with windows or if it would play well with non-privileged users on Unix.
After all, the only schemes we do not support with the above code are "unix", "unixgram" and "unixpacket".
Usually I prefer to just expose everything the library has to offer and let the user handle the aftermath, but in this case I am a little intimidated by the size of the project here.
I think it would be better to limit the connection possibilities to the "well-known" ones.
What do you think of that?
There was a problem hiding this comment.
My opinion is still to let gosnmp handle it. I'm calling @reimda in to hear his opinion.
There was a problem hiding this comment.
I did a little digging and I think I know what the intention behind the check in gosnmpwrapper was:
gosnmp will happily take arbitrary values as connetion scheme and not complain about them. The first time the code can fail due to a wrong connection scheme is when gs.Connect in plugins/input/snmp.go is called.
However, the error raised by gosnmp is quite descriptive of what is going on:
agent upd4://test: setting up connection: error establishing connection to host: dial upd4:test: unknown network upd
Additionally, this is error is handeled here (line 576 ongoing):
if err := gs.Connect(); err != nil {
return nil, fmt.Errorf("setting up connection: %w", err)
}
So indeed: There should be no problem in letting gosnmp handle the error and just feeding it with anything inu.Scheme.
Additionally this is more future-proof... maybe gosnmp will add some more schemes later on.
There was a problem hiding this comment.
Correct indeed, that's what I meant.
There was a problem hiding this comment.
Currently I am looking into why the schemes ip, ip4 and ip6 always seem to fail and return UnknownNetworkError.
I traced it down to dial.go in the go-sources line 175.
167 func parseNetwork(ctx context.Context, network string, needsProto bool) (afnet string, proto int, err error) {
168 i := last(network, ':')
169 if i < 0 { // no colon
170 switch network {
171 case "tcp", "tcp4", "tcp6":
172 case "udp", "udp4", "udp6":
173 case "ip", "ip4", "ip6":
174 if needsProto {
175 return "", 0, UnknownNetworkError(network)
176 }
177 case "unix", "unixgram", "unixpacket":
178 default:
179 return "", 0, UnknownNetworkError(network)
180 }
181 return network, 0, nil
182 }
Of course: As soon as you specify a port, you also need to specify the protocol! However, the error is not really clear on whats happening.
Since we are using port 161 by default for snmp, it is clear that the aformentiones schemes will not work. And in fact, they don't really make any sense to me... I really don't know why go-snmp offers them in the first place.
For the sockets as connection-schemes:
A socket does not need a port. Unfortunately, specifying something like unix://test:123 will also result in an UnknownNetworkError. I think this can be quite confusing for the user.
Maybe, after all, it does make sense to limit the connection-schemes to udp{4,6} and tcp{4,6}. Or we have to implement checks in snmp.go to ensure correct formatting of the schemes.
Maybe this is the reason why the connection-schemes were limited in the first place.
Otherwise, mis-configuration may result in confusing error-messages for the user.
Penny for your thoughts :)
There was a problem hiding this comment.
Okay, you convinced me 😆 Fine for me to keep it as is.
There was a problem hiding this comment.
I added some documentation, so the next guy is spared of this discovery-process :)
Thanks for your input. Greatly appreciated!
plugins/inputs/snmp/snmp.go
Outdated
| if err := gs.SetAgent(agent); err != nil { | ||
| return nil, fmt.Errorf("setting gosnmpwrapper agent: %w", err) |
There was a problem hiding this comment.
I think the idea was to behave it similar like the lines above, so it should have been err = gs.SetAgent(agent)
| if err := gs.SetAgent(agent); err != nil { | |
| return nil, fmt.Errorf("setting gosnmpwrapper agent: %w", err) | |
| err = gs.SetAgent(agent) | |
| if err != nil { | |
| return nil, err |
Hipska
left a comment
There was a problem hiding this comment.
Do you run go test before you commit? Anyway, last commit is not well formatted.
plugins/inputs/snmp/README.md
Outdated
| ## port: optional | ||
| ## example: agents = ["udp://127.0.0.1:161"] | ||
| ## agents = ["tcp://127.0.0.1:161"] | ||
| ## agents = ["udpv4://v4only-snmp-agent"] |
There was a problem hiding this comment.
Is there an extra "v" in the scheme in this example?
| ## agents = ["udpv4://v4only-snmp-agent"] | |
| ## agents = ["udp4://v4only-snmp-agent"] |
plugins/inputs/snmp/snmp.go
Outdated
| ## port: optional | ||
| ## example: agents = ["udp://127.0.0.1:161"] | ||
| ## agents = ["tcp://127.0.0.1:161"] | ||
| ## agents = ["udpv4://v4only-snmp-agent"] |
There was a problem hiding this comment.
Uhhh.
How many Typos can there be in a few-line-change!
Thanks for your input! :)
There was a problem hiding this comment.
That's what reviews are for!
Hey I noticed you said you're just getting started with go. If you're looking for more opportunities to write go code, feel free to grab issues with the "enhancement" or "good first issue" label. The influx and community devs and I are happy to give you feedback on PRs.
Thanks again for this PR!
(cherry picked from commit 198bcc8)
Hi,
This PR fixes #7879.
I modified the Gosnmpwrapper to expose tcp{4,6} and udp{4,6} as connection-scheme and pass them to Gosnmp.
This way, one can indirectly specify the address-family to be used for snmp-requests.
I updated the according README-files.
In my small test-setup everything is working as intended.
However, I did not find a unit-test for the snmp input-plugin or the wrapper to alter, so I assume this small 4 line change can (hopefully) be reviewed by hand.
This is my first PR on a go-project ever, so please be gentle! :)
Have a nice day.
Required for all PRs: