netsnmp 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +17 -21
- data/lib/netsnmp.rb +12 -0
- data/lib/netsnmp/client.rb +2 -3
- data/lib/netsnmp/oid.rb +3 -2
- data/lib/netsnmp/pdu.rb +4 -4
- data/lib/netsnmp/scoped_pdu.rb +2 -0
- data/lib/netsnmp/security_parameters.rb +3 -3
- data/lib/netsnmp/session.rb +5 -16
- data/lib/netsnmp/v3_session.rb +5 -3
- data/lib/netsnmp/varbind.rb +28 -9
- data/lib/netsnmp/version.rb +1 -1
- data/sig/client.rbs +24 -0
- data/sig/message.rbs +7 -0
- data/sig/netsnmp.rbs +23 -0
- data/sig/oid.rbs +18 -0
- data/sig/openssl.rbs +20 -0
- data/sig/pdu.rbs +48 -0
- data/sig/scoped_pdu.rbs +15 -0
- data/sig/security_parameters.rbs +56 -0
- data/sig/session.rbs +36 -0
- data/sig/timeticks.rbs +7 -0
- data/sig/v3_session.rbs +21 -0
- data/sig/varbind.rbs +30 -0
- data/spec/client_spec.rb +2 -2
- data/spec/handlers/celluloid_spec.rb +3 -2
- data/spec/session_spec.rb +2 -2
- data/spec/spec_helper.rb +7 -4
- data/spec/support/request_examples.rb +2 -2
- data/spec/v3_session_spec.rb +4 -4
- data/spec/varbind_spec.rb +100 -3
- metadata +26 -90
- data/.coveralls.yml +0 -1
- data/.gitignore +0 -14
- data/.rspec +0 -2
- data/.rubocop.yml +0 -11
- data/.rubocop_todo.yml +0 -69
- data/.travis.yml +0 -32
- data/Gemfile +0 -22
- data/Rakefile +0 -30
- data/netsnmp.gemspec +0 -31
- data/spec/support/Dockerfile +0 -14
- data/spec/support/specs.sh +0 -51
- data/spec/support/stop_docker.sh +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6bb62b9abd416188660e04be1c12cf009b1df0e307030b9c239ea4b2acd73cc
|
4
|
+
data.tar.gz: ee88f7eb292f1a0077ab25e4677713290447e0e7755049bbe6db50f07d15d6a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc90cc626d4be2a04d598ec267bcfc108e87aea8e8a585a7611033fc56cb6c174a222250bb9eeeb05b2d03d058983b106c92c30ba3d91e922b171ecfbc37041a
|
7
|
+
data.tar.gz: '09dbbb277ae24d9b760c0de70738a02037da527d653d5e003632c1b41d808cc21c44034fb51bec63394a132fe3c30ffaac1adc846dc061ca483e13fc0719af97'
|
data/README.md
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# netsnmp
|
2
2
|
|
3
|
-
|
4
|
-
[](https://coveralls.io/github/swisscom/ruby-netsnmp?branch=master)
|
3
|
+

|
5
4
|
[](https://codeclimate.com/github/swisscom/ruby-netsnmp)
|
6
5
|
[](https://www.rubydoc.info/github/swisscom/ruby-netsnmp/master)
|
7
6
|
|
@@ -123,6 +122,8 @@ If you create an `IPAddr` object (ruby standard library `ipaddr`) and pass it to
|
|
123
122
|
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
123
|
|
125
124
|
|
125
|
+
Counter32 and Counter64 types will map to plain integers.
|
126
|
+
|
126
127
|
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
|
|
128
129
|
* Use the `:type` parameter in `#set` calls:
|
@@ -221,6 +222,7 @@ All encoding/decoding/encryption/decryption/digests are done using `openssl`, wh
|
|
221
222
|
|
222
223
|
This library uses RSpec. The client specs are "integration" tests, in that we communicate with an [snmpsim-built snmp agent simulator](https://github.com/etingof/snmpsim).
|
223
224
|
|
225
|
+
|
224
226
|
### RSpec
|
225
227
|
|
226
228
|
You can run all tests by typing:
|
@@ -232,37 +234,31 @@ You can run all tests by typing:
|
|
232
234
|
...
|
233
235
|
```
|
234
236
|
|
235
|
-
### SNMP Simulator
|
236
237
|
|
237
|
-
|
238
|
+
### Docker
|
238
239
|
|
239
|
-
|
240
|
-
|
241
|
-
The preferred way to use the snmp simulator is by using docker.
|
242
|
-
|
243
|
-
In order to start the simulator container, run:
|
240
|
+
The most straightforward way of running the tests is by using the `docker-compose` setup (which is also what's used in the CI). Run it against the ruby version you're targeting:
|
244
241
|
|
245
242
|
```
|
246
|
-
>
|
243
|
+
> docker-compose -f docker-compose.yml -f docker-compose-ruby-${RUBY_MAJOR_VERSION}.${RUBY_MAJOR_VERSION}.yml run netsnmp
|
247
244
|
```
|
248
245
|
|
249
|
-
|
246
|
+
The CI runs the tests against all supported ruby versions. If changes break a specific version of ruby, make sure you commit appropriate changes addressing the edge case, or let me know in the issues board, so I can help.
|
250
247
|
|
251
|
-
|
252
|
-
> docker port test-snmp 1161/udp
|
253
|
-
```
|
248
|
+
### SNMP Simulator
|
254
249
|
|
255
|
-
|
250
|
+
The SNMP simulator runs in its own container in the `docker` setup.
|
256
251
|
|
257
|
-
|
252
|
+
You can install the package yourself (ex: `pip install snmpsim`) and run the server locally, and then set the `SNMP_PORT` environment variable, where the snmp simulator is running.
|
258
253
|
|
259
|
-
|
254
|
+
#### CI
|
260
255
|
|
261
|
-
|
262
|
-
> spec/support/spec.sh run
|
263
|
-
```
|
256
|
+
The job of the CI is:
|
264
257
|
|
265
|
-
|
258
|
+
* Run all the tests;
|
259
|
+
* Make sure the tests cover an appropriate surface of the code;
|
260
|
+
* Lint the code;
|
261
|
+
* (for ruby 3.0) type check the code;
|
266
262
|
|
267
263
|
|
268
264
|
## Contributing
|
data/lib/netsnmp.rb
CHANGED
@@ -34,6 +34,18 @@ rescue LoadError
|
|
34
34
|
end
|
35
35
|
|
36
36
|
module NETSNMP
|
37
|
+
module StringExtensions
|
38
|
+
# If you wonder why this is there: the oauth feature uses a refinement to enhance the
|
39
|
+
# Regexp class locally with #match? , but this is never tested, because ActiveSupport
|
40
|
+
# monkey-patches the same method... Please ActiveSupport, stop being so intrusive!
|
41
|
+
# :nocov:
|
42
|
+
refine(String) do
|
43
|
+
def match?(*args)
|
44
|
+
!match(*args).nil?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
37
49
|
def self.debug=(io)
|
38
50
|
@debug_output = io
|
39
51
|
end
|
data/lib/netsnmp/client.rb
CHANGED
@@ -23,8 +23,7 @@ module NETSNMP
|
|
23
23
|
# puts client.get(oid: "1.3.6.1.2.1.1.5.0")
|
24
24
|
# end
|
25
25
|
#
|
26
|
-
def initialize(**options)
|
27
|
-
version = options[:version]
|
26
|
+
def initialize(version: nil, **options)
|
28
27
|
version = case version
|
29
28
|
when Integer then version # assume the use know what he's doing
|
30
29
|
when /v?1/ then 0
|
@@ -33,7 +32,7 @@ module NETSNMP
|
|
33
32
|
end
|
34
33
|
|
35
34
|
@retries = options.fetch(:retries, RETRIES)
|
36
|
-
@session ||= version == 3 ? V3Session.new(options) : Session.new(options)
|
35
|
+
@session ||= version == 3 ? V3Session.new(**options) : Session.new(version: version, **options)
|
37
36
|
return unless block_given?
|
38
37
|
begin
|
39
38
|
yield self
|
data/lib/netsnmp/oid.rb
CHANGED
@@ -4,13 +4,14 @@ module NETSNMP
|
|
4
4
|
# Abstracts the OID structure
|
5
5
|
#
|
6
6
|
module OID
|
7
|
+
using StringExtensions unless String.method_defined?(:match?)
|
8
|
+
|
7
9
|
OIDREGEX = /^[\d\.]*$/
|
8
10
|
|
9
11
|
module_function
|
10
12
|
|
11
13
|
def build(o)
|
12
14
|
case o
|
13
|
-
when OID then o
|
14
15
|
when Array
|
15
16
|
o.join(".")
|
16
17
|
when OIDREGEX
|
@@ -29,7 +30,7 @@ module NETSNMP
|
|
29
30
|
# @return [true, false] whether the given OID belongs to the sub-tree
|
30
31
|
#
|
31
32
|
def parent?(parent_oid, child_oid)
|
32
|
-
child_oid.match(/\A#{parent_oid}\./)
|
33
|
+
child_oid.match?(/\A#{parent_oid}\./)
|
33
34
|
end
|
34
35
|
end
|
35
36
|
end
|
data/lib/netsnmp/pdu.rb
CHANGED
@@ -55,7 +55,7 @@ module NETSNMP
|
|
55
55
|
when :response then 2
|
56
56
|
else raise Error, "#{type} is not supported as type"
|
57
57
|
end
|
58
|
-
new(
|
58
|
+
new(type: typ, **args)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
@@ -64,7 +64,7 @@ module NETSNMP
|
|
64
64
|
attr_reader :version, :community, :request_id
|
65
65
|
|
66
66
|
def initialize(type:, headers:,
|
67
|
-
request_id:
|
67
|
+
request_id: SecureRandom.random_number(MAXREQUESTID),
|
68
68
|
error_status: 0,
|
69
69
|
error_index: 0,
|
70
70
|
varbinds: [])
|
@@ -75,9 +75,9 @@ module NETSNMP
|
|
75
75
|
@type = type
|
76
76
|
@varbinds = []
|
77
77
|
varbinds.each do |varbind|
|
78
|
-
add_varbind(varbind)
|
78
|
+
add_varbind(**varbind)
|
79
79
|
end
|
80
|
-
@request_id = request_id
|
80
|
+
@request_id = request_id
|
81
81
|
check_error_status(@error_status)
|
82
82
|
end
|
83
83
|
|
data/lib/netsnmp/scoped_pdu.rb
CHANGED
@@ -24,7 +24,7 @@ module NETSNMP
|
|
24
24
|
|
25
25
|
# @param [String] username the snmp v3 username
|
26
26
|
# @param [String] engine_id the device engine id (initialized to '' for report)
|
27
|
-
# @param [Symbol, integer] security_level allowed snmp v3 security level (:auth_priv, :
|
27
|
+
# @param [Symbol, integer] security_level allowed snmp v3 security level (:auth_priv, :auth_no_priv, etc)
|
28
28
|
# @param [Symbol, nil] auth_protocol a supported authentication protocol (currently supported: :md5, :sha)
|
29
29
|
# @param [Symbol, nil] priv_protocol a supported privacy protocol (currently supported: :des, :aes)
|
30
30
|
# @param [String, nil] auth_password the authentication password
|
@@ -99,7 +99,7 @@ module NETSNMP
|
|
99
99
|
# @note this method is used in the process of authenticating a message
|
100
100
|
def sign(message)
|
101
101
|
# don't sign unless you have to
|
102
|
-
return
|
102
|
+
return unless @auth_protocol
|
103
103
|
|
104
104
|
key = auth_key.dup
|
105
105
|
|
@@ -211,7 +211,7 @@ module NETSNMP
|
|
211
211
|
end
|
212
212
|
|
213
213
|
def authorizable?
|
214
|
-
@auth_protocol
|
214
|
+
@auth_protocol != :none
|
215
215
|
end
|
216
216
|
end
|
217
217
|
end
|
data/lib/netsnmp/session.rb
CHANGED
@@ -10,7 +10,7 @@ module NETSNMP
|
|
10
10
|
def initialize(version: 1, community: "public", **options)
|
11
11
|
@version = version
|
12
12
|
@community = community
|
13
|
-
validate(options)
|
13
|
+
validate(**options)
|
14
14
|
end
|
15
15
|
|
16
16
|
# Closes the session
|
@@ -36,23 +36,20 @@ module NETSNMP
|
|
36
36
|
# @return [NETSNMP::PDU] the response pdu
|
37
37
|
#
|
38
38
|
def send(pdu)
|
39
|
-
encoded_request =
|
39
|
+
encoded_request = pdu.to_der
|
40
40
|
encoded_response = @transport.send(encoded_request)
|
41
|
-
decode(encoded_response)
|
41
|
+
PDU.decode(encoded_response)
|
42
42
|
end
|
43
43
|
|
44
44
|
private
|
45
45
|
|
46
|
-
def validate(**
|
47
|
-
proxy = options[:proxy]
|
46
|
+
def validate(host: nil, port: 161, proxy: nil, timeout: TIMEOUT, **)
|
48
47
|
if proxy
|
49
48
|
@proxy = true
|
50
49
|
@transport = proxy
|
51
50
|
else
|
52
|
-
host, port = options.values_at(:host, :port)
|
53
51
|
raise "you must provide an hostname/ip under :host" unless host
|
54
|
-
|
55
|
-
@transport = Transport.new(host, port.to_i, timeout: options.fetch(:timeout, TIMEOUT))
|
52
|
+
@transport = Transport.new(host, port.to_i, timeout: timeout)
|
56
53
|
end
|
57
54
|
@version = case @version
|
58
55
|
when Integer then @version # assume the use know what he's doing
|
@@ -64,14 +61,6 @@ module NETSNMP
|
|
64
61
|
end
|
65
62
|
end
|
66
63
|
|
67
|
-
def encode(pdu)
|
68
|
-
pdu.to_der
|
69
|
-
end
|
70
|
-
|
71
|
-
def decode(stream)
|
72
|
-
PDU.decode(stream)
|
73
|
-
end
|
74
|
-
|
75
64
|
class Transport
|
76
65
|
MAXPDUSIZE = 0xffff + 1
|
77
66
|
|
data/lib/netsnmp/v3_session.rb
CHANGED
@@ -4,7 +4,7 @@ module NETSNMP
|
|
4
4
|
# Abstraction for the v3 semantics.
|
5
5
|
class V3Session < Session
|
6
6
|
# @param [String, Integer] version SNMP version (always 3)
|
7
|
-
def initialize(
|
7
|
+
def initialize(context: "", **opts)
|
8
8
|
@context = context
|
9
9
|
@security_parameters = opts.delete(:security_parameters)
|
10
10
|
super
|
@@ -19,8 +19,10 @@ module NETSNMP
|
|
19
19
|
end
|
20
20
|
|
21
21
|
# @see {NETSNMP::Session#send}
|
22
|
-
def send(
|
23
|
-
|
22
|
+
def send(pdu)
|
23
|
+
encoded_request = encode(pdu)
|
24
|
+
encoded_response = @transport.send(encoded_request)
|
25
|
+
pdu, = decode(encoded_response)
|
24
26
|
pdu
|
25
27
|
end
|
26
28
|
|
data/lib/netsnmp/varbind.rb
CHANGED
@@ -6,7 +6,7 @@ module NETSNMP
|
|
6
6
|
class Varbind
|
7
7
|
attr_reader :oid, :value
|
8
8
|
|
9
|
-
def initialize(oid, value: nil, type: nil
|
9
|
+
def initialize(oid, value: nil, type: nil)
|
10
10
|
@oid = OID.build(oid)
|
11
11
|
@type = type
|
12
12
|
@value = convert_val(value) if value
|
@@ -81,17 +81,25 @@ module NETSNMP
|
|
81
81
|
when :ipaddress then 0
|
82
82
|
when :counter32
|
83
83
|
asn_val = [value].pack("N*")
|
84
|
-
asn_val = asn_val[1..-1] while asn_val
|
84
|
+
asn_val = asn_val[1..-1] while asn_val[0] == "\x00".b && asn_val[1].unpack("B").first != "1"
|
85
85
|
1
|
86
86
|
when :gauge
|
87
87
|
asn_val = [value].pack("N*")
|
88
|
-
asn_val = asn_val[1..-1] while asn_val
|
88
|
+
asn_val = asn_val[1..-1] while asn_val[0] == "\x00".b && asn_val[1].unpack("B").first != "1"
|
89
89
|
2
|
90
90
|
when :timetick
|
91
91
|
return Timetick.new(value).to_asn
|
92
92
|
when :opaque then 4
|
93
93
|
when :nsap then 5
|
94
|
-
when :counter64
|
94
|
+
when :counter64
|
95
|
+
asn_val = [
|
96
|
+
(value >> 96) & 0xFFFFFFFF,
|
97
|
+
(value >> 64) & 0xFFFFFFFF,
|
98
|
+
(value >> 32) & 0xFFFFFFFF,
|
99
|
+
value & 0xFFFFFFFF
|
100
|
+
].pack("NNNN")
|
101
|
+
asn_val = asn_val[1..-1] while asn_val.start_with?("\x00")
|
102
|
+
6
|
95
103
|
when :uinteger then 7
|
96
104
|
else
|
97
105
|
raise Error, "#{typ}: unsupported application type"
|
@@ -106,16 +114,27 @@ module NETSNMP
|
|
106
114
|
IPAddr.new_ntoh(asn.value)
|
107
115
|
when 1, # ASN counter 32
|
108
116
|
2 # gauge
|
109
|
-
|
110
|
-
val.prepend("\x00") while val.bytesize < 4
|
111
|
-
asn.value.unpack("N*")[0] || 0
|
117
|
+
unpack_32bit_integer(asn.value)
|
112
118
|
when 3 # timeticks
|
113
|
-
Timetick.new(asn.value
|
119
|
+
Timetick.new(unpack_32bit_integer(asn.value))
|
114
120
|
# when 4 # opaque
|
115
121
|
# when 5 # NSAP
|
116
|
-
|
122
|
+
when 6 # ASN Counter 64
|
123
|
+
unpack_64bit_integer(asn.value)
|
117
124
|
# when 7 # ASN UInteger
|
118
125
|
end
|
119
126
|
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def unpack_32bit_integer(payload)
|
131
|
+
payload.prepend("\x00") until (payload.bytesize % 4).zero?
|
132
|
+
payload.unpack("N*")[-1] || 0
|
133
|
+
end
|
134
|
+
|
135
|
+
def unpack_64bit_integer(payload)
|
136
|
+
payload.prepend("\x00") until (payload.bytesize % 16).zero?
|
137
|
+
payload.unpack("NNNN").reduce(0) { |sum, elem| (sum << 32) + elem }
|
138
|
+
end
|
120
139
|
end
|
121
140
|
end
|
data/lib/netsnmp/version.rb
CHANGED
data/sig/client.rbs
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module NETSNMP
|
2
|
+
class Client
|
3
|
+
|
4
|
+
def get: (*untyped) -> untyped
|
5
|
+
| (*untyped) { (PDU) -> void } -> untyped
|
6
|
+
|
7
|
+
def get_next: (*untyped) -> untyped
|
8
|
+
| (*untyped) { (PDU) -> void } -> untyped
|
9
|
+
|
10
|
+
def set: (*untyped) -> untyped
|
11
|
+
| (*untyped) { (PDU) -> void } -> untyped
|
12
|
+
|
13
|
+
def inform: (*untyped) -> untyped
|
14
|
+
| (*untyped) { (PDU) -> void } -> untyped
|
15
|
+
|
16
|
+
def walk: (oid: oid) -> _Each[oid_type, oid_value]
|
17
|
+
|
18
|
+
def close: () -> void
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def initialize: (?version?: snmp_version, **untyped) -> untyped
|
23
|
+
end
|
24
|
+
end
|
data/sig/message.rbs
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
module NETSNMP
|
2
|
+
module Message
|
3
|
+
def self?.decode: (String stream, security_parameters: SecurityParameters) -> [ScopedPDU, String, Integer, Integer]
|
4
|
+
|
5
|
+
def self?.encode: (ScopedPDU pdu, security_parameters: SecurityParameters, ?engine_boots: Integer, ?engine_time: Integer) -> String
|
6
|
+
end
|
7
|
+
end
|
data/sig/netsnmp.rbs
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module NETSNMP
|
2
|
+
interface _Logger
|
3
|
+
def <<: (string) -> void
|
4
|
+
end
|
5
|
+
|
6
|
+
interface _Authenticate
|
7
|
+
def reset: () -> void
|
8
|
+
def <<: (string) -> void
|
9
|
+
def digest: () -> String
|
10
|
+
end
|
11
|
+
|
12
|
+
interface _Encrypt
|
13
|
+
def encrypt: (String payload, engine_boots: Integer, engine_time: Integer) -> String
|
14
|
+
|
15
|
+
def decrypt: (String payload, salt: String, engine_boots: Integer, engine_time: Integer) -> String
|
16
|
+
end
|
17
|
+
|
18
|
+
type snmp_version = 0 | 1 | 3 | :v1 | :v2c | :v3 | nil
|
19
|
+
|
20
|
+
def self.debug=: (_Logger) -> void
|
21
|
+
|
22
|
+
def self.debug: { () -> string } -> void
|
23
|
+
end
|
data/sig/oid.rbs
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module NETSNMP
|
2
|
+
type oid = String | Array[Integer]
|
3
|
+
|
4
|
+
type oid_type = Integer | :ipaddress | :counter32 | :gauge | :timetick | :opaque | :nsap | :counter64 | :uinteger
|
5
|
+
|
6
|
+
type oid_value = String | Integer | Timetick | true | false | nil | IPAddr
|
7
|
+
|
8
|
+
# type oid_options = {}
|
9
|
+
|
10
|
+
module OID
|
11
|
+
|
12
|
+
def self?.build: (oid) -> String
|
13
|
+
|
14
|
+
def self?.to_asn: (String oid) -> OpenSSL::ASN1::ObjectId
|
15
|
+
|
16
|
+
def self?.parent?: (String oid, String oid) -> bool
|
17
|
+
end
|
18
|
+
end
|