netsnmp 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +11 -0
- data/.rubocop_todo.yml +69 -0
- data/.travis.yml +6 -14
- data/Gemfile +7 -5
- data/README.md +66 -32
- data/Rakefile +7 -9
- data/lib/netsnmp/client.rb +42 -39
- data/lib/netsnmp/encryption/aes.rb +22 -22
- data/lib/netsnmp/encryption/des.rb +20 -21
- data/lib/netsnmp/encryption/none.rb +3 -3
- data/lib/netsnmp/errors.rb +1 -0
- data/lib/netsnmp/message.rb +31 -30
- data/lib/netsnmp/oid.rb +5 -4
- data/lib/netsnmp/pdu.rb +66 -69
- data/lib/netsnmp/scoped_pdu.rb +5 -7
- data/lib/netsnmp/security_parameters.rb +73 -54
- data/lib/netsnmp/session.rb +22 -24
- data/lib/netsnmp/timeticks.rb +8 -10
- data/lib/netsnmp/v3_session.rb +11 -13
- data/lib/netsnmp/varbind.rb +53 -49
- data/lib/netsnmp/version.rb +2 -1
- data/lib/netsnmp.rb +32 -12
- data/netsnmp.gemspec +10 -10
- data/spec/client_spec.rb +69 -49
- data/spec/handlers/celluloid_spec.rb +14 -10
- data/spec/oid_spec.rb +5 -3
- data/spec/pdu_spec.rb +14 -7
- data/spec/security_parameters_spec.rb +50 -18
- data/spec/session_spec.rb +9 -6
- data/spec/spec_helper.rb +14 -65
- data/spec/support/Dockerfile +3 -3
- data/spec/support/celluloid.rb +12 -6
- data/spec/support/request_examples.rb +19 -8
- data/spec/support/specs.sh +39 -0
- data/spec/support/{start_docker.sh → start-docker.sh} +4 -4
- data/spec/timeticks_spec.rb +2 -0
- data/spec/v3_session_spec.rb +8 -4
- data/spec/varbind_spec.rb +12 -0
- metadata +13 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: faea873fa6ca9eaf4c453c77dc2f897af13b523521344f8e39e72006f1493d23
|
4
|
+
data.tar.gz: 6e671ad1fb6ecb01ddbad7647ce2c01f532537845cd74fcd3db188a8af543010
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a49e950ce387bf6c0e41d928b7a2617540990b399814cc5811969cedd4a65483454af203b990e4dcc041aa95cac7cdb19aa2c2fba3c7b16cfb24c424eef5b43
|
7
|
+
data.tar.gz: 007eb164271090585dbcab085a0a4bdef899c8acbb8b79ac43cba99c7b859326b7c2b89c08af03716f24d2980110196ac8f1795b527377fedb178b53ca013055
|
data/.rubocop.yml
ADDED
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2018-02-07 22:49:11 +0000 using RuboCop version 0.52.1.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
Metrics/BlockLength:
|
10
|
+
Max: 200
|
11
|
+
|
12
|
+
# Offense count: 17
|
13
|
+
Metrics/AbcSize:
|
14
|
+
Max: 27
|
15
|
+
|
16
|
+
# Offense count: 2
|
17
|
+
# Configuration parameters: CountComments.
|
18
|
+
Metrics/ClassLength:
|
19
|
+
Max: 150
|
20
|
+
|
21
|
+
# Offense count: 8
|
22
|
+
Metrics/CyclomaticComplexity:
|
23
|
+
Max: 20
|
24
|
+
|
25
|
+
# Offense count: 22
|
26
|
+
# Configuration parameters: CountComments.
|
27
|
+
Metrics/MethodLength:
|
28
|
+
Max: 30
|
29
|
+
|
30
|
+
# Offense count: 2
|
31
|
+
# Configuration parameters: CountKeywordArgs.
|
32
|
+
Metrics/ParameterLists:
|
33
|
+
Max: 7
|
34
|
+
|
35
|
+
# Offense count: 1
|
36
|
+
Metrics/PerceivedComplexity:
|
37
|
+
Max: 9
|
38
|
+
|
39
|
+
# Offense count: 10
|
40
|
+
Style/Documentation:
|
41
|
+
Exclude:
|
42
|
+
- 'spec/**/*'
|
43
|
+
- 'test/**/*'
|
44
|
+
- 'lib/netsnmp.rb'
|
45
|
+
- 'lib/netsnmp/encryption/aes.rb'
|
46
|
+
- 'lib/netsnmp/encryption/des.rb'
|
47
|
+
- 'lib/netsnmp/encryption/none.rb'
|
48
|
+
- 'lib/netsnmp/scoped_pdu.rb'
|
49
|
+
- 'lib/netsnmp/session.rb'
|
50
|
+
- 'lib/netsnmp/timeticks.rb'
|
51
|
+
|
52
|
+
# Offense count: 8
|
53
|
+
# Cop supports --auto-correct.
|
54
|
+
# Configuration parameters: Strict.
|
55
|
+
Style/NumericLiterals:
|
56
|
+
MinDigits: 11
|
57
|
+
|
58
|
+
# Offense count: 264
|
59
|
+
# Cop supports --auto-correct.
|
60
|
+
# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
|
61
|
+
# SupportedStyles: single_quotes, double_quotes
|
62
|
+
Style/StringLiterals:
|
63
|
+
EnforcedStyle: double_quotes
|
64
|
+
|
65
|
+
# Offense count: 112
|
66
|
+
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
67
|
+
# URISchemes: http, https
|
68
|
+
Metrics/LineLength:
|
69
|
+
Max: 189
|
data/.travis.yml
CHANGED
@@ -5,23 +5,14 @@ services:
|
|
5
5
|
before_install:
|
6
6
|
- sudo apt-add-repository multiverse
|
7
7
|
- sudo apt-get update
|
8
|
-
- sudo apt-get install -y
|
9
|
-
- sudo apt-get install -y snmp
|
10
|
-
- sudo apt-get install -y libsnmp-dev
|
11
|
-
- sudo apt-get install -y snmp-mibs-downloader
|
12
|
-
- sudo apt-get install -o Dpkg::Options::="--force-confold" --force-yes -y docker-engine
|
13
|
-
|
14
|
-
before_script:
|
15
|
-
- sudo download-mibs
|
16
|
-
- sudo sed -i 's/^mibs/#mibs/g' /etc/snmp/snmp.conf
|
17
|
-
- spec/support/start_docker.sh
|
18
|
-
after_script: spec/support/stop_docker.sh
|
8
|
+
- sudo apt-get install -o Dpkg::Options::="--force-confold" --force-yes -y docker-ce
|
19
9
|
|
20
10
|
install:
|
11
|
+
- gem update --system
|
21
12
|
- bundle install --path .bundle
|
22
13
|
|
23
14
|
script:
|
24
|
-
-
|
15
|
+
- spec/support/specs.sh
|
25
16
|
|
26
17
|
language: ruby
|
27
18
|
rvm:
|
@@ -29,8 +20,9 @@ rvm:
|
|
29
20
|
- 2.2
|
30
21
|
- 2.3
|
31
22
|
- 2.4
|
23
|
+
- 2.5
|
32
24
|
- ruby-head
|
33
|
-
- jruby-9.1.
|
25
|
+
- jruby-9.1.15.0
|
34
26
|
- jruby-head
|
35
27
|
- rbx-2
|
36
28
|
matrix:
|
@@ -38,5 +30,5 @@ matrix:
|
|
38
30
|
- rvm: ruby-head
|
39
31
|
- rvm: jruby-head
|
40
32
|
# to figure out later
|
41
|
-
- rvm: jruby-9.1.
|
33
|
+
- rvm: jruby-9.1.15.0
|
42
34
|
- rvm: rbx-2
|
data/Gemfile
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
source "https://rubygems.org/"
|
2
4
|
ruby RUBY_VERSION
|
3
5
|
|
4
6
|
gemspec
|
5
7
|
|
6
|
-
gem
|
8
|
+
gem "coveralls", require: false
|
7
9
|
|
8
10
|
group :development do
|
9
|
-
gem
|
11
|
+
gem "pry"
|
10
12
|
end
|
11
13
|
|
12
|
-
if RUBY_VERSION < "2.2"
|
13
|
-
gem "nio4r", "~> 1.2"
|
14
|
-
end
|
14
|
+
gem "nio4r", "~> 1.2" if RUBY_VERSION < "2.2"
|
15
15
|
|
16
16
|
platforms :mri do
|
17
17
|
gem "pry-byebug", require: false
|
@@ -19,3 +19,5 @@ platforms :mri do
|
|
19
19
|
end
|
20
20
|
|
21
21
|
gem "xorcist"
|
22
|
+
|
23
|
+
gem "rubocop", require: false
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# netsnmp
|
1
|
+
# netsnmp
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/swisscom/ruby-netsnmp.svg?branch=master)](https://travis-ci.org/swisscom/ruby-netsnmp)
|
4
4
|
[![Coverage Status](https://coveralls.io/repos/github/swisscom/ruby-netsnmp/badge.svg?branch=master)](https://coveralls.io/github/swisscom/ruby-netsnmp?branch=master)
|
@@ -11,7 +11,7 @@ The `netsnmp` gem provides a ruby native implementation of the SNMP protocol (v1
|
|
11
11
|
|
12
12
|
Add this line to your application's Gemfile:
|
13
13
|
|
14
|
-
```ruby
|
14
|
+
```ruby
|
15
15
|
gem 'netsnmp'
|
16
16
|
```
|
17
17
|
|
@@ -27,7 +27,7 @@ Or install it yourself as:
|
|
27
27
|
$ gem install netsnmp
|
28
28
|
```
|
29
29
|
|
30
|
-
## Features
|
30
|
+
## Features
|
31
31
|
|
32
32
|
This gem provides:
|
33
33
|
|
@@ -38,46 +38,48 @@ This gem provides:
|
|
38
38
|
|
39
39
|
## Why?
|
40
40
|
|
41
|
-
If you look for snmp gems in ruby toolbox, you'll find a bunch.
|
42
|
-
You may ask, why not just use one of them?
|
41
|
+
If you look for snmp gems in ruby toolbox, you'll find a bunch.
|
42
|
+
You may ask, why not just use one of them?
|
43
43
|
|
44
44
|
Most of them only implement v1 and v2, so if your requirement is to use v3, you're left with only 2 choices: [net-snmp](https://github.com/mixtli/net-snmp) (unmantained since 2013) and its follow-up [net-snmp2](https://github.com/jbreeden/net-snmp2), which started as a fork to fix some bugs left unattended. Both libraries wrap the C netsnmp library using FFI, which leaves them vulnerable to the following bugs (experienced in both libraries):
|
45
45
|
|
46
|
-
* Dependency of specific versions of netsnmp C package.
|
46
|
+
* Dependency of specific versions of netsnmp C package.
|
47
47
|
* Memory Leaks.
|
48
48
|
* Doesn't work reliable in ruby > 2.0.0-p576, crashing the VM.
|
49
49
|
* Network I/O done by the library, thereby blocking the GVL, thereby making all snmp calls block the whole ruby VM.
|
50
|
-
* This means, multi-threading is impossible.
|
50
|
+
* This means, multi-threading is impossible.
|
51
51
|
* This means, evented I/O is impossible.
|
52
52
|
|
53
|
-
All of these issues are resolved here.
|
53
|
+
All of these issues are resolved here.
|
54
54
|
|
55
55
|
## Features
|
56
56
|
|
57
57
|
* Client Interface, which supports SNMP v3, v2c, and v1
|
58
|
-
* Supports get, getnext, set and walk calls.
|
58
|
+
* Supports get, getnext, set and walk calls.
|
59
59
|
* Proxy IO object support (for eventmachine/celluloid-io)
|
60
|
-
* Ruby >= 2.1 support
|
60
|
+
* Ruby >= 2.1 support (modern)
|
61
61
|
* Pure Ruby (no FFI)
|
62
62
|
|
63
63
|
## Examples
|
64
64
|
|
65
|
-
You can use the docker container provided under spec/support to test against these examples (the port used in the examples should be the docker external port mapped to port 161).
|
65
|
+
You can use the docker container provided under spec/support to test against these examples (the port used in the examples should be the docker external port mapped to port 161).
|
66
66
|
|
67
67
|
```ruby
|
68
68
|
require 'netsnmp'
|
69
69
|
|
70
|
-
# example you can test against the docker simulator provided. port attribute might be different.
|
70
|
+
# example you can test against the docker simulator provided. port attribute might be different.
|
71
71
|
manager = NETSNMP::Client.new(host: "localhost", port: 33445, username: "simulator",
|
72
|
-
auth_password: "auctoritas", auth_protocol: :md5,
|
72
|
+
auth_password: "auctoritas", auth_protocol: :md5,
|
73
73
|
priv_password: "privatus", priv_protocol: :des,
|
74
74
|
context: "a172334d7d97871b72241397f713fa12")
|
75
75
|
|
76
76
|
# SNMP get
|
77
|
-
|
77
|
+
# sysName.0
|
78
|
+
manager.get(oid: "1.3.6.1.2.1.1.0") #=> 'tt'
|
78
79
|
|
79
80
|
# SNMP walk
|
80
|
-
|
81
|
+
# sysORDescr
|
82
|
+
manager.walk(oid: "1.3.6.1.2.1.1.1").each do |oid_code, value|
|
81
83
|
# do something with them
|
82
84
|
puts "for #{oid_code}: #{value}"
|
83
85
|
end
|
@@ -86,18 +88,51 @@ manager.close
|
|
86
88
|
|
87
89
|
# SNMP set
|
88
90
|
manager2 = NETSNMP::Client.new(host: "localhost", port: 33445, username: "simulator",
|
89
|
-
auth_password: "auctoritas", auth_protocol: :md5,
|
91
|
+
auth_password: "auctoritas", auth_protocol: :md5,
|
90
92
|
priv_password: "privatus", priv_protocol: :des,
|
91
93
|
context: "0886e1397d572377c17c15036a1e6c66")
|
92
94
|
|
93
95
|
# setting to 43, becos yes
|
94
|
-
|
96
|
+
# sysUpTimeInstance
|
97
|
+
manager2.set("1.3.6.1.2.1.1.3.0", value: 43)
|
95
98
|
|
96
99
|
manager2.close
|
97
100
|
```
|
98
101
|
|
99
|
-
SNMP v2/v1 examples will be similar (beware of the differences in the initialization attributes).
|
102
|
+
SNMP v2/v1 examples will be similar (beware of the differences in the initialization attributes).
|
103
|
+
|
104
|
+
## SNMP Application Types
|
105
|
+
|
106
|
+
All previous examples were done specifying primitive types, i.e. unless specified otherwise, it's gonna try to convert a ruby "primitive" type to an ASN.1 primitive type, and vice-versa:
|
107
|
+
|
108
|
+
* Integer -> ASN.1 Integer
|
109
|
+
* String -> ASN.1 Octet String
|
110
|
+
* nil -> ASN.1 Null
|
111
|
+
* true, false -> ASN.1 Boolean
|
112
|
+
|
113
|
+
That means that, if you pass `value: 43` to the `#set` call, it's going to build a varbind with an ASN.1 Integer. If You issue a `#get` and the response contains an ASN.1 Integer, it's going to return an Integer.
|
114
|
+
|
115
|
+
However, SNMP defines application-specific ASN.1 types, for which there is support, albeit limited. Currently, there is support for ip addresses and timeticks.
|
116
|
+
|
117
|
+
* IPAddr -> ASN.1 context-specific
|
100
118
|
|
119
|
+
If you create an `IPAddr` object (ruby standard library `ipaddr`) and pass it to the `#set` call, it will map to the SNMP content-specific code. If the response of a `#get` call contains an ip address, it will map to an `IPAddr` object.
|
120
|
+
|
121
|
+
* NETSNMP::Timeticks -> ASN.1 content-specific
|
122
|
+
|
123
|
+
The `NETSNMP::Timeticks` type is internal to this library, but it is a ruby `Numeric` type. You are safe to use it "as a numeric", that is, perform calculations.
|
124
|
+
|
125
|
+
|
126
|
+
You can find usage examples [here](https://github.com/swisscom/ruby-netsnmp/blob/master/spec/varbind_spec.rb). If you need support to a missing type, you have the following options:
|
127
|
+
|
128
|
+
* Use the `:type` parameter in `#set` calls:
|
129
|
+
```ruby
|
130
|
+
# as a symbol
|
131
|
+
manager.set("somecounteroid", value: 999999, type: :counter64)
|
132
|
+
# as the SNMP specific type id, if you're familiar with the protocol
|
133
|
+
manager.set("somecounteroid", value: 999999, type: 6)
|
134
|
+
```
|
135
|
+
* Fork this library, extend support, write a test and submit a PR (the desired solution ;) )
|
101
136
|
|
102
137
|
## Concurrency
|
103
138
|
|
@@ -106,8 +141,8 @@ In ruby, you are usually adviced not to share IO objects across threads. The sam
|
|
106
141
|
```ruby
|
107
142
|
general_options = { auth_protocol: ....
|
108
143
|
routers.map do |r|
|
109
|
-
Thread.start do
|
110
|
-
NETSNMP::Client.new(general_options.merge(host: r)) do |cl|
|
144
|
+
Thread.start do
|
145
|
+
NETSNMP::Client.new(general_options.merge(host: r)) do |cl|
|
111
146
|
cli.get(oid: "1.6.3.......
|
112
147
|
|
113
148
|
end
|
@@ -115,13 +150,13 @@ routers.map do |r|
|
|
115
150
|
end.each(&:join)
|
116
151
|
```
|
117
152
|
|
118
|
-
Evented IO is also supported, in that you can pass a `:proxy` object as an already opened channel of communication to the client. Very important: you have to take care of the lifecycle, as the client will not connect and will not close the object, it will assume no control over it.
|
153
|
+
Evented IO is also supported, in that you can pass a `:proxy` object as an already opened channel of communication to the client. Very important: you have to take care of the lifecycle, as the client will not connect and will not close the object, it will assume no control over it.
|
119
154
|
|
120
|
-
When passing a proxy object, you can omit the `:host` parameter.
|
155
|
+
When passing a proxy object, you can omit the `:host` parameter.
|
121
156
|
|
122
157
|
The proxy object will have to be a duck-type implementing `#send`, which is a method receiving the sending PDU payload, and return the payload of the receiving PDU.
|
123
158
|
|
124
|
-
Here is a small pseudo-code example:
|
159
|
+
Here is a small pseudo-code example:
|
125
160
|
|
126
161
|
```ruby
|
127
162
|
# beware, we are inside a warp-speed loop!!!
|
@@ -136,14 +171,14 @@ end
|
|
136
171
|
proxy.close
|
137
172
|
```
|
138
173
|
|
139
|
-
For more information about this subject, the specs test this feature against celluloid-io. An eventmachine could be added, if someone would be kind enough to provide an implementation.
|
174
|
+
For more information about this subject, the specs test this feature against celluloid-io. An eventmachine could be added, if someone would be kind enough to provide an implementation.
|
140
175
|
|
141
176
|
## Performance
|
142
177
|
|
143
178
|
|
144
179
|
### XOR
|
145
180
|
|
146
|
-
This library has some workarounds to some missing features in the ruby language, namely the inexistence of a byte array structure. The closest we have is a byte stream presented as a String with ASCII encoding. A method was added to the String class called `#xor` for some operations needed internally. To prevent needless monkey-patches, Refinements have been employed.
|
181
|
+
This library has some workarounds to some missing features in the ruby language, namely the inexistence of a byte array structure. The closest we have is a byte stream presented as a String with ASCII encoding. A method was added to the String class called `#xor` for some operations needed internally. To prevent needless monkey-patches, Refinements have been employed.
|
147
182
|
|
148
183
|
If `#xor` becomes at some point the bottleneck of your usage, this gem has also support for [xorcist](https://github.com/fny/xorcist/). You just have to add it to your Gemfile (or install it in the system):
|
149
184
|
|
@@ -152,12 +187,12 @@ If `#xor` becomes at some point the bottleneck of your usage, this gem has also
|
|
152
187
|
|
153
188
|
gem 'netsnmp'
|
154
189
|
|
155
|
-
# or, in the command line
|
190
|
+
# or, in the command line
|
156
191
|
|
157
192
|
$ gem install netsnmp
|
158
193
|
```
|
159
194
|
|
160
|
-
and `netsnmp` will automatically pick it up.
|
195
|
+
and `netsnmp` will automatically pick it up.
|
161
196
|
|
162
197
|
## Auth/Priv Key
|
163
198
|
|
@@ -166,7 +201,7 @@ If you'll use this gem often with SNMP v3 and auth/priv security level enabled,
|
|
166
201
|
There is a recommended work-around, but this is only usable **if you are using the same user/authpass/privpass on all the hosts!!!**. Use this with care, then:
|
167
202
|
|
168
203
|
```ruby
|
169
|
-
$shared_security_parameters = NETSNMP::SecurityParameters.new(security_level: :authpriv, username: "mustermann",
|
204
|
+
$shared_security_parameters = NETSNMP::SecurityParameters.new(security_level: :authpriv, username: "mustermann",
|
170
205
|
auth_protocol: :md5, priv_protocol: :aes, ....
|
171
206
|
# this will eager-load the auth/priv_key
|
172
207
|
...
|
@@ -184,9 +219,9 @@ All encoding/decoding/encryption/decryption/digests are done using `openssl`, wh
|
|
184
219
|
|
185
220
|
## Tests
|
186
221
|
|
187
|
-
This library uses RSpec. The client specs are "integration" tests, in that we communicate with an snmp agent simulator.
|
222
|
+
This library uses RSpec. The client specs are "integration" tests, in that we communicate with an snmp agent simulator.
|
188
223
|
|
189
|
-
To start the simulator locally, you'll need docker 1.9 or higher (Why 1.9? ```--build-arg``` parameter support was needed for our builds in the CI. You could use a lower version by providing the proxy environment variables in the Dockerfile directly, provided you don't merge these changes to master, thereby exposing your proxy).
|
224
|
+
To start the simulator locally, you'll need docker 1.9 or higher (Why 1.9? ```--build-arg``` parameter support was needed for our builds in the CI. You could use a lower version by providing the proxy environment variables in the Dockerfile directly, provided you don't merge these changes to master, thereby exposing your proxy).
|
190
225
|
|
191
226
|
```
|
192
227
|
> spec/support/start_docker.sh
|
@@ -217,7 +252,6 @@ There are some features which this gem doesn't support. It was built to provide
|
|
217
252
|
|
218
253
|
* No MIB support (you can only work with OIDs)
|
219
254
|
* No server (Agent, in SNMP-ish) implementation.
|
220
|
-
* No getbulk support.
|
255
|
+
* No getbulk support.
|
221
256
|
|
222
257
|
So if you like the gem, but would rather have these features implemented, please help by sending us a PR and we'll gladly review it.
|
223
|
-
|
data/Rakefile
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rspec/core/rake_task"
|
5
|
+
require "coveralls/rake/task"
|
4
6
|
|
5
7
|
desc "runs the tests and sends to coveralls server"
|
6
8
|
Coveralls::RakeTask.new
|
@@ -10,17 +12,13 @@ RSpec::Core::RakeTask.new
|
|
10
12
|
|
11
13
|
task default: [:spec]
|
12
14
|
|
13
|
-
|
14
|
-
|
15
15
|
namespace :spec do
|
16
16
|
desc "runs the tests in coverage mode"
|
17
17
|
task :coverage do
|
18
|
-
ENV[
|
18
|
+
ENV["COVERAGE"] = "true"
|
19
19
|
Rake::Task["spec"].execute
|
20
20
|
end
|
21
21
|
|
22
22
|
desc "runs tests, check coverage, pushes to coverage server"
|
23
|
-
task :
|
23
|
+
task ci: ["spec:coverage", "coveralls:push"]
|
24
24
|
end
|
25
|
-
|
26
|
-
|
data/lib/netsnmp/client.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "timeout"
|
4
|
+
|
2
5
|
module NETSNMP
|
3
6
|
# Main Entity, provides the user-facing API to communicate with SNMP Agents
|
4
7
|
#
|
5
8
|
# Under the hood it creates a "session" (analogous to the net-snmp C session), which will be used
|
6
9
|
# to proxy all the communication to the agent. the Client ensures that you only write pure ruby and
|
7
|
-
# read pure ruby, not concerning with snmp-speak like PDUs, varbinds and the like.
|
10
|
+
# read pure ruby, not concerning with snmp-speak like PDUs, varbinds and the like.
|
8
11
|
#
|
9
12
|
#
|
10
13
|
class Client
|
@@ -14,7 +17,7 @@ module NETSNMP
|
|
14
17
|
# @option options [String, Integer, nil] :version the version of the protocol (defaults to 3).
|
15
18
|
# also accepts common known declarations like :v3, "v2c", etc
|
16
19
|
# @option options [Integer] :retries number of retries for each failed PDU (after which it raise timeout error. Defaults to {RETRIES} retries)
|
17
|
-
# @yield [client] the instantiated client, after which it closes it for use.
|
20
|
+
# @yield [client] the instantiated client, after which it closes it for use.
|
18
21
|
# @example Yielding a clinet
|
19
22
|
# NETSNMP::Client.new(host: "241.232.22.12") do |client|
|
20
23
|
# puts client.get(oid: "1.3.6.1.2.1.1.5.0")
|
@@ -22,25 +25,24 @@ module NETSNMP
|
|
22
25
|
#
|
23
26
|
def initialize(**options)
|
24
27
|
version = options[:version]
|
25
|
-
version = case version
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
version = case version
|
29
|
+
when Integer then version # assume the use know what he's doing
|
30
|
+
when /v?1/ then 0
|
31
|
+
when /v?2c?/ then 1
|
32
|
+
when /v?3/, nil then 3
|
33
|
+
end
|
31
34
|
|
32
35
|
@retries = options.fetch(:retries, RETRIES)
|
33
36
|
@session ||= version == 3 ? V3Session.new(options) : Session.new(options)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
37
|
+
return unless block_given?
|
38
|
+
begin
|
39
|
+
yield self
|
40
|
+
ensure
|
41
|
+
close
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
43
|
-
# Closes the inner section
|
45
|
+
# Closes the inner section
|
44
46
|
def close
|
45
47
|
@session.close
|
46
48
|
end
|
@@ -49,23 +51,24 @@ module NETSNMP
|
|
49
51
|
#
|
50
52
|
# @see {NETSNMP::Varbind#new}
|
51
53
|
#
|
52
|
-
def get(oid_opts)
|
53
|
-
request = @session.build_pdu(:get, oid_opts)
|
54
|
+
def get(*oid_opts)
|
55
|
+
request = @session.build_pdu(:get, *oid_opts)
|
54
56
|
response = handle_retries { @session.send(request) }
|
55
57
|
yield response if block_given?
|
56
|
-
response.varbinds.
|
58
|
+
values = response.varbinds.map(&:value)
|
59
|
+
values.size > 1 ? values : values.first
|
57
60
|
end
|
58
61
|
|
59
62
|
# Performs an SNMP GETNEXT Request
|
60
|
-
#
|
63
|
+
#
|
61
64
|
# @see {NETSNMP::Varbind#new}
|
62
65
|
#
|
63
|
-
def get_next(oid_opts)
|
64
|
-
request = @session.build_pdu(:getnext, oid_opts)
|
66
|
+
def get_next(*oid_opts)
|
67
|
+
request = @session.build_pdu(:getnext, *oid_opts)
|
65
68
|
response = handle_retries { @session.send(request) }
|
66
69
|
yield response if block_given?
|
67
|
-
|
68
|
-
|
70
|
+
values = response.varbinds.map { |v| [v.oid, v.value] }
|
71
|
+
values.size > 1 ? values : values.first
|
69
72
|
end
|
70
73
|
|
71
74
|
# Perform a SNMP Walk (issues multiple subsequent GENEXT requests within the subtree rooted on an OID)
|
@@ -74,7 +77,7 @@ module NETSNMP
|
|
74
77
|
#
|
75
78
|
# @return [Enumerator] the enumerator-collection of the oid-value pairs
|
76
79
|
#
|
77
|
-
def walk(oid:
|
80
|
+
def walk(oid:)
|
78
81
|
walkoid = oid
|
79
82
|
Enumerator.new do |y|
|
80
83
|
code = walkoid
|
@@ -84,10 +87,10 @@ module NETSNMP
|
|
84
87
|
get_next(oid: code) do |response|
|
85
88
|
response.varbinds.each do |varbind|
|
86
89
|
code = varbind.oid
|
87
|
-
if !OID.parent?(walkoid, code)
|
88
|
-
|
89
|
-
|
90
|
-
throw(:walk)
|
90
|
+
if !OID.parent?(walkoid, code) ||
|
91
|
+
varbind.value.eql?(:endofmibview) ||
|
92
|
+
(code == first_response_code)
|
93
|
+
throw(:walk)
|
91
94
|
else
|
92
95
|
y << [code, varbind.value]
|
93
96
|
end
|
@@ -102,13 +105,13 @@ module NETSNMP
|
|
102
105
|
# Perform a SNMP GETBULK Request (performs multiple GETNEXT)
|
103
106
|
#
|
104
107
|
# @param [String] oid the first oid
|
105
|
-
# @param [Hash] options the varbind options
|
108
|
+
# @param [Hash] options the varbind options
|
106
109
|
# @option options [Integer] :errstat sets the number of objects expected for the getnext instance
|
107
|
-
# @option options [Integer] :errindex number of objects repeating for all the repeating IODs.
|
110
|
+
# @option options [Integer] :errindex number of objects repeating for all the repeating IODs.
|
108
111
|
#
|
109
112
|
# @return [Enumerator] the enumerator-collection of the oid-value pairs
|
110
113
|
#
|
111
|
-
#def get_bulk(oid)
|
114
|
+
# def get_bulk(oid)
|
112
115
|
# request = @session.build_pdu(:getbulk, *oids)
|
113
116
|
# request[:error_status] = options.delete(:non_repeaters) || 0
|
114
117
|
# request[:error_index] = options.delete(:max_repetitions) || 10
|
@@ -118,29 +121,29 @@ module NETSNMP
|
|
118
121
|
# y << [ varbind.oid, varbind.value ]
|
119
122
|
# end
|
120
123
|
# end
|
121
|
-
#end
|
124
|
+
# end
|
122
125
|
|
123
126
|
# Perform a SNMP SET Request
|
124
127
|
#
|
125
128
|
# @see {NETSNMP::Varbind#new}
|
126
129
|
#
|
127
|
-
def set(oid_opts)
|
128
|
-
request = @session.build_pdu(:set, oid_opts)
|
130
|
+
def set(*oid_opts)
|
131
|
+
request = @session.build_pdu(:set, *oid_opts)
|
129
132
|
response = handle_retries { @session.send(request) }
|
130
|
-
yield response if block_given?
|
131
|
-
response.varbinds.map(&:value)
|
133
|
+
yield response if block_given?
|
134
|
+
values = response.varbinds.map(&:value)
|
135
|
+
values.size > 1 ? values : values.first
|
132
136
|
end
|
133
137
|
|
134
|
-
|
135
138
|
private
|
136
139
|
|
137
|
-
# Handles timeout errors by reissuing the same pdu until it runs out or retries.
|
140
|
+
# Handles timeout errors by reissuing the same pdu until it runs out or retries.
|
138
141
|
def handle_retries
|
139
142
|
retries = @retries
|
140
143
|
begin
|
141
144
|
yield
|
142
145
|
rescue Timeout::Error => e
|
143
|
-
raise e if retries
|
146
|
+
raise e if retries.zero?
|
144
147
|
retries -= 1
|
145
148
|
retry
|
146
149
|
end
|