net-snmp 0.1.7 → 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.
- data/Gemfile +2 -0
- data/README.rdoc +41 -20
- data/lib/net-snmp.rb +6 -2
- data/lib/net/snmp.rb +7 -4
- data/lib/net/snmp/constants.rb +73 -56
- data/lib/net/snmp/debug.rb +17 -0
- data/lib/net/snmp/dispatcher.rb +43 -65
- data/lib/net/snmp/error.rb +18 -10
- data/lib/net/snmp/inline.rb +4 -14
- data/lib/net/snmp/oid.rb +6 -2
- data/lib/net/snmp/pdu.rb +58 -19
- data/lib/net/snmp/session.rb +275 -195
- data/lib/net/snmp/trap_session.rb +64 -0
- data/lib/net/snmp/varbind.rb +28 -6
- data/lib/net/snmp/version.rb +1 -1
- data/lib/net/snmp/wrapper.rb +33 -8
- data/spec/async_spec.rb +78 -30
- data/spec/em_spec.rb +1 -1
- data/spec/error_spec.rb +36 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/sync_spec.rb +9 -1
- data/spec/thread_spec.rb +3 -1
- data/spec/trap_spec.rb +12 -10
- data/spec/wrapper_spec.rb +2 -2
- metadata +8 -4
@@ -0,0 +1,64 @@
|
|
1
|
+
module Net
|
2
|
+
module SNMP
|
3
|
+
class TrapSession < Session
|
4
|
+
# == Represents a session for sending SNMP traps
|
5
|
+
def initialize(options = {})
|
6
|
+
options[:peername] = "#{options[:peername]}:162"
|
7
|
+
super(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Send an SNMPv1 trap
|
11
|
+
# +options+
|
12
|
+
# * :enterprise The Oid of the enterprise
|
13
|
+
# * :trap_type The generic trap type.
|
14
|
+
# * :specific_type The specific trap type
|
15
|
+
def trap(options = {})
|
16
|
+
pdu = PDU.new(Constants::SNMP_MSG_TRAP)
|
17
|
+
options[:enterprise] ||= '1.3.6.1.4.1.3.1.1' # uh, just send netsnmp enterprise i guess
|
18
|
+
pdu.enterprise = OID.new(options[:enterprise])
|
19
|
+
pdu.trap_type = options[:trap_type] || 1 # need to check all these defaults
|
20
|
+
pdu.specific_type = options[:specific_type] || 0
|
21
|
+
pdu.time = 1 # put what here?
|
22
|
+
send_pdu(pdu)
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
# Send an SNMPv2 trap
|
27
|
+
# +options
|
28
|
+
# * :oid The Oid of the trap
|
29
|
+
# * :varbinds A list of Varbind objects to send with the trap
|
30
|
+
def trap_v2(options = {})
|
31
|
+
if options[:oid].kind_of?(String)
|
32
|
+
options[:oid] = Net::SNMP::OID.new(options[:oid])
|
33
|
+
end
|
34
|
+
pdu = PDU.new(Constants::SNMP_MSG_TRAP2)
|
35
|
+
build_trap_pdu(pdu, options)
|
36
|
+
send_pdu(pdu)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Send an SNMPv2 inform. Can accept a callback to execute on confirmation of the inform
|
40
|
+
# +options
|
41
|
+
# * :oid The OID of the inform
|
42
|
+
# * :varbinds A list of Varbind objects to send with the inform
|
43
|
+
def inform(options = {}, &callback)
|
44
|
+
if options[:oid].kind_of?(String)
|
45
|
+
options[:oid] = Net::SNMP::OID.new(options[:oid])
|
46
|
+
end
|
47
|
+
pdu = PDU.new(Constants::SNMP_MSG_INFORM)
|
48
|
+
build_trap_pdu(pdu, options)
|
49
|
+
send_pdu(pdu, &callback)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def build_trap_pdu(pdu, options = {})
|
54
|
+
pdu.add_varbind(:oid => OID.new('sysUpTime.0'), :type => Constants::ASN_TIMETICKS, :value => 42)
|
55
|
+
pdu.add_varbind(:oid => OID.new('snmpTrapOID.0'), :type => Constants::ASN_OBJECT_ID, :value => options[:oid])
|
56
|
+
if options[:varbinds]
|
57
|
+
options[:varbinds].each do |vb|
|
58
|
+
pdu.add_varbind(vb)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/net/snmp/varbind.rb
CHANGED
@@ -1,34 +1,56 @@
|
|
1
1
|
module Net
|
2
2
|
module SNMP
|
3
3
|
class Varbind
|
4
|
+
# == Represents an SNMP Variable Binding
|
5
|
+
|
4
6
|
attr_accessor :struct
|
5
7
|
|
6
|
-
def initialize(ptr)
|
8
|
+
def initialize(ptr = nil)
|
7
9
|
@struct = Net::SNMP::Wrapper::VariableList.new(ptr)
|
8
10
|
end
|
9
11
|
|
10
12
|
def self.from_pointer(ptr)
|
11
13
|
new(ptr)
|
12
14
|
end
|
13
|
-
|
15
|
+
|
16
|
+
# Returns the data type of the varbind
|
14
17
|
def object_type
|
15
18
|
@struct.type
|
16
19
|
end
|
17
20
|
|
21
|
+
# Returns the OID associated with the varbind
|
18
22
|
def oid
|
19
23
|
@oid ||= Net::SNMP::OID.new(@struct.name.read_array_of_long(@struct.name_length).join("."))
|
20
24
|
end
|
21
|
-
|
22
|
-
|
23
25
|
|
26
|
+
# Returns the value of the varbind
|
24
27
|
def value
|
25
28
|
case object_type
|
26
|
-
when Constants::ASN_OCTET_STR
|
29
|
+
when Constants::ASN_OCTET_STR, Constants::ASN_OPAQUE
|
27
30
|
struct.val[:string].read_string(struct.val_len)
|
28
|
-
when Constants::ASN_INTEGER
|
31
|
+
when Constants::ASN_INTEGER
|
29
32
|
struct.val[:integer].read_long
|
33
|
+
when Constants::ASN_UINTEGER, Constants::ASN_TIMETICKS, Constants::ASN_COUNTER, Constants::ASN_GAUGE
|
34
|
+
struct.val[:integer].read_ulong
|
30
35
|
when Constants::ASN_IPADDRESS
|
31
36
|
struct.val[:objid].read_string(struct.val_len).unpack('CCCC').join(".")
|
37
|
+
when Constants::ASN_NULL
|
38
|
+
nil
|
39
|
+
when Constants::ASN_OBJECT_ID
|
40
|
+
Net::SNMP::OID.new(struct.val[:objid].read_array_of_long(struct.val_len).join("."))
|
41
|
+
when Constants::ASN_COUNTER64
|
42
|
+
counter = Wrapper::Counter64.new(struct.val[:counter64])
|
43
|
+
counter.high * 2^32 + counter.low
|
44
|
+
when Constants::ASN_BIT_STR
|
45
|
+
# XXX not sure what to do here. Is this obsolete?
|
46
|
+
when Constants::SNMP_ENDOFMIBVIEW
|
47
|
+
:endofmibview
|
48
|
+
when Constants::SNMP_NOSUCHOBJECT
|
49
|
+
:nosuchobject
|
50
|
+
when Constants::SNMP_NOSUCHINSTANCE
|
51
|
+
:nosuchinstance
|
52
|
+
else
|
53
|
+
raise Net::SNMP::Error.new, "Unknown value type #{object_type}"
|
32
54
|
end
|
33
55
|
end
|
34
56
|
end
|
data/lib/net/snmp/version.rb
CHANGED
data/lib/net/snmp/wrapper.rb
CHANGED
@@ -12,7 +12,7 @@ module Wrapper
|
|
12
12
|
:low, :u_long
|
13
13
|
)
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
class TimeVal < NiceFFI::Struct
|
17
17
|
layout(:tv_sec, :long, :tv_usec, :long)
|
18
18
|
end
|
@@ -20,7 +20,7 @@ module Wrapper
|
|
20
20
|
puts "tv_sec = #{tv.tv_sec}, tv_usec = #{tv.tv_usec} "
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
|
24
24
|
class NetsnmpVardata < FFI::Union
|
25
25
|
layout(
|
26
26
|
:integer, :pointer,
|
@@ -288,7 +288,7 @@ module Wrapper
|
|
288
288
|
attach_function :snmp_add, [ :pointer, :pointer, callback([ :pointer, :pointer, :pointer, :int ], :int), callback([ :pointer, :pointer, :int ], :int) ], :pointer
|
289
289
|
attach_function :snmp_add_full, [ :pointer, :pointer, callback([ :pointer, :pointer, :pointer, :int ], :int), callback([ :pointer, :pointer, :pointer, :uint ], :int), callback([ :pointer, :pointer, :int ], :int), callback([ :pointer, :pointer, :pointer, :pointer ], :int), callback([ :pointer, :pointer, :pointer, :pointer, :pointer ], :int), callback([ :pointer, :uint ], :int), callback([ :pointer, :pointer, :uint ], :pointer) ], :pointer
|
290
290
|
attach_function :snmp_sess_send, [ :pointer, :pointer ], :int
|
291
|
-
attach_function :snmp_sess_async_send, [ :pointer, :pointer, :
|
291
|
+
attach_function :snmp_sess_async_send, [ :pointer, :pointer, :snmp_callback, :pointer ], :int
|
292
292
|
attach_function :snmp_sess_select_info, [ :pointer, :pointer, :pointer, :pointer, :pointer ], :int
|
293
293
|
attach_function :snmp_sess_read, [ :pointer, :pointer ], :int
|
294
294
|
attach_function :snmp_sess_timeout, [ :pointer ], :void
|
@@ -344,8 +344,7 @@ module Wrapper
|
|
344
344
|
attach_function :generate_Ku, [:pointer, :int, :string, :int, :pointer, :pointer], :int
|
345
345
|
|
346
346
|
|
347
|
-
|
348
|
-
attach_function :select, [:int, :pointer, :pointer, :pointer, :pointer], :int
|
347
|
+
|
349
348
|
# MIB functions
|
350
349
|
attach_function :init_mib, [], :void
|
351
350
|
attach_function :read_all_mibs, [], :void
|
@@ -369,9 +368,35 @@ end
|
|
369
368
|
end
|
370
369
|
end
|
371
370
|
|
371
|
+
|
372
372
|
#module RubyWrapper
|
373
|
-
# extend
|
374
|
-
#
|
375
|
-
# ffi_lib "/Users/rmcclain/.rvm/rubies/ruby-1.9.2-preview3/lib/libruby.dylib"
|
373
|
+
# extend FFI::Library
|
374
|
+
# ffi_lib FFI::CURRENT_PROCESS
|
376
375
|
# attach_function :rb_thread_select, [:int, :pointer, :pointer, :pointer, :pointer], :int
|
377
376
|
#end
|
377
|
+
|
378
|
+
|
379
|
+
|
380
|
+
module FFI
|
381
|
+
module LibC
|
382
|
+
extend FFI::Library
|
383
|
+
ffi_lib [FFI::CURRENT_PROCESS, 'c']
|
384
|
+
|
385
|
+
typedef :pointer, :FILE
|
386
|
+
typedef :uint32, :in_addr_t
|
387
|
+
typedef :uint16, :in_port_t
|
388
|
+
|
389
|
+
class Timeval < FFI::Struct
|
390
|
+
layout :tv_sec, :time_t,
|
391
|
+
:tv_usec, :suseconds_t
|
392
|
+
end
|
393
|
+
|
394
|
+
# Standard IO functions
|
395
|
+
#@blocking = true # some undocumented voodoo that tells the next attach_function to release the GIL
|
396
|
+
attach_function :select, [:int, :pointer, :pointer, :pointer, :pointer], :int
|
397
|
+
attach_variable :errno, :int
|
398
|
+
attach_function :malloc, [:size_t], :pointer
|
399
|
+
attach_function :free, [:pointer], :void
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
data/spec/async_spec.rb
CHANGED
@@ -3,106 +3,154 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
3
3
|
describe "async" do
|
4
4
|
context "version 1" do
|
5
5
|
it "get should work" do
|
6
|
+
#pending
|
6
7
|
did_callback = false
|
7
|
-
|
8
|
-
|
8
|
+
puts "started test #{Time.now}"
|
9
|
+
sess = Net::SNMP::Session.open(:peername => 'test.net-snmp.org', :community => 'demopublic') do |s|
|
10
|
+
s.get(["sysDescr.0", "sysContact.0"]) do |op, pdu|
|
9
11
|
did_callback = true
|
10
|
-
|
11
|
-
|
12
|
+
pdu.varbinds[0].value.should eql("test.net-snmp.org")
|
13
|
+
pdu.varbinds[1].value.should match(/Coders/)
|
12
14
|
end
|
13
15
|
end
|
14
|
-
Net::SNMP::Dispatcher.
|
16
|
+
Net::SNMP::Dispatcher.select(false)
|
15
17
|
did_callback.should be(true)
|
18
|
+
puts "ended test #{Time.now}"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "get should return an error" do
|
22
|
+
Net::SNMP.thread_safe = true
|
23
|
+
puts "started test #{Time.now}"
|
24
|
+
|
25
|
+
did_callback = false
|
26
|
+
sess = Net::SNMP::Session.open(:peername => 'www.yahoo.com', :timeout => 5, :retries => 0) do |sess|
|
27
|
+
sess.get("sysDescr.0") do |op, pdu|
|
28
|
+
puts "IN GET CALLBACK"
|
29
|
+
puts pdu.inspect
|
30
|
+
did_callback = true
|
31
|
+
op.should eql(:timeout)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
puts"selecting #{Time.now}"
|
35
|
+
sleep 2
|
36
|
+
pdu = sess.select(10)
|
37
|
+
puts "and now #{Time.now}"
|
38
|
+
puts "sess err = #{sess.s_snmp_errno}"
|
39
|
+
|
40
|
+
pdu.should eql(0)
|
41
|
+
did_callback.should be(true)
|
42
|
+
puts "ended test #{Time.now}"
|
43
|
+
Net::SNMP.thread_safe = false
|
16
44
|
end
|
17
45
|
|
18
46
|
it "getnext should work" do
|
47
|
+
puts "started test #{Time.now}"
|
48
|
+
|
49
|
+
#pending
|
19
50
|
puts "testing getnext"
|
20
51
|
did_callback = false
|
21
52
|
Net::SNMP::Session.open(:peername => 'test.net-snmp.org', :community => 'demopublic') do |s|
|
22
|
-
s.get_next(["sysDescr", "sysContact"]) do |
|
53
|
+
s.get_next(["sysDescr", "sysContact"]) do |op, pdu|
|
23
54
|
did_callback = true
|
24
|
-
|
25
|
-
|
55
|
+
pdu.varbinds[0].value.should eql("test.net-snmp.org")
|
56
|
+
pdu.varbinds[1].value.should match(/Coders/)
|
26
57
|
end
|
27
58
|
end
|
28
|
-
Net::SNMP::Dispatcher.
|
59
|
+
Net::SNMP::Dispatcher.select(false)
|
29
60
|
puts "done getnext"
|
30
61
|
did_callback.should be(true)
|
62
|
+
puts "ended test #{Time.now}"
|
63
|
+
|
31
64
|
end
|
32
65
|
|
33
66
|
end
|
34
67
|
|
35
68
|
context "version 2" do
|
36
69
|
it "get should work" do
|
70
|
+
puts "started test #{Time.now}"
|
71
|
+
|
72
|
+
#pending
|
37
73
|
did_callback = false
|
38
74
|
Net::SNMP::Session.open(:peername => 'test.net-snmp.org', :community => 'demopublic', :version => '2c') do |s|
|
39
|
-
s.get(["sysDescr.0", "sysContact.0"]) do |
|
75
|
+
s.get(["sysDescr.0", "sysContact.0"]) do |op, pdu|
|
40
76
|
did_callback = true
|
41
|
-
|
42
|
-
|
77
|
+
pdu.varbinds[0].value.should eql("test.net-snmp.org")
|
78
|
+
pdu.varbinds[1].value.should match(/Coders/)
|
43
79
|
end
|
44
80
|
end
|
45
|
-
Net::SNMP::Dispatcher.
|
81
|
+
Net::SNMP::Dispatcher.select(false)
|
46
82
|
did_callback.should be(true)
|
83
|
+
puts "ended test #{Time.now}"
|
84
|
+
|
47
85
|
end
|
48
86
|
|
49
87
|
it "getnext should work" do
|
88
|
+
#pending
|
89
|
+
puts "started test #{Time.now}"
|
90
|
+
|
50
91
|
did_callback = false
|
51
92
|
Net::SNMP::Session.open(:peername => 'test.net-snmp.org', :community => 'demopublic', :version => '2c') do |s|
|
52
|
-
s.get_next(["sysDescr", "sysContact"]) do |
|
93
|
+
s.get_next(["sysDescr", "sysContact"]) do |op, pdu|
|
53
94
|
did_callback = true
|
54
|
-
|
55
|
-
|
95
|
+
pdu.varbinds[0].value.should eql("test.net-snmp.org")
|
96
|
+
pdu.varbinds[1].value.should match(/Coders/)
|
56
97
|
end
|
57
98
|
end
|
58
|
-
Net::SNMP::Dispatcher.
|
99
|
+
Net::SNMP::Dispatcher.select(false)
|
59
100
|
did_callback.should be(true)
|
101
|
+
puts "ended test #{Time.now}"
|
102
|
+
|
60
103
|
end
|
104
|
+
|
61
105
|
end
|
62
106
|
|
63
107
|
context "version 3" do
|
64
108
|
#failing intermittently
|
65
109
|
it "should get async using snmpv3" do
|
110
|
+
#pending
|
66
111
|
did_callback = false
|
67
112
|
Net::SNMP::Session.open(:peername => 'test.net-snmp.org', :version => 3, :username => 'MD5User',:security_level => Net::SNMP::Constants::SNMP_SEC_LEVEL_AUTHNOPRIV, :auth_protocol => :md5, :password => 'The Net-SNMP Demo Password') do |sess|
|
68
|
-
sess.get(["sysDescr.0"]) do |
|
113
|
+
sess.get(["sysDescr.0"]) do |op, pdu|
|
69
114
|
did_callback = true
|
70
|
-
|
115
|
+
pdu.varbinds[0].value.should eql('test.net-snmp.org')
|
71
116
|
end
|
72
117
|
sleep(0.5)
|
73
|
-
Net::SNMP::Dispatcher.
|
74
|
-
#Net::SNMP::Dispatcher.
|
75
|
-
puts "done
|
118
|
+
Net::SNMP::Dispatcher.select(false)
|
119
|
+
#Net::SNMP::Dispatcher.select(false)
|
120
|
+
puts "done select"
|
76
121
|
did_callback.should be(true)
|
77
122
|
end
|
78
123
|
end
|
79
124
|
#
|
80
125
|
#
|
81
126
|
it "get should work" do
|
127
|
+
#pending
|
82
128
|
did_callback = false
|
83
129
|
sess = Net::SNMP::Session.open(:peername => 'test.net-snmp.org', :version => 3, :username => 'MD5User',:security_level => Net::SNMP::Constants::SNMP_SEC_LEVEL_AUTHNOPRIV, :auth_protocol => :md5, :password => 'The Net-SNMP Demo Password') do |sess|
|
84
|
-
sess.get(["sysDescr.0", "sysContact.0"]) do |
|
130
|
+
sess.get(["sysDescr.0", "sysContact.0"]) do |op,pdu|
|
85
131
|
did_callback = true
|
86
|
-
|
87
|
-
|
132
|
+
pdu.varbinds[0].value.should eql("test.net-snmp.org")
|
133
|
+
pdu.varbinds[1].value.should match(/Coders/)
|
88
134
|
end
|
89
135
|
end
|
90
|
-
Net::SNMP::Dispatcher.
|
136
|
+
Net::SNMP::Dispatcher.select(false)
|
91
137
|
sess.close
|
92
138
|
did_callback.should be(true)
|
93
139
|
end
|
94
140
|
|
141
|
+
# XXX occasionally segfaulting
|
95
142
|
it "getnext should work" do
|
143
|
+
#pending
|
96
144
|
did_callback = false
|
97
145
|
|
98
146
|
sess = Net::SNMP::Session.open(:peername => 'test.net-snmp.org', :version => 3, :username => 'MD5User',:security_level => Net::SNMP::Constants::SNMP_SEC_LEVEL_AUTHNOPRIV, :auth_protocol => :md5, :password => 'The Net-SNMP Demo Password') do |sess|
|
99
|
-
sess.get_next(["sysDescr", "sysContact"]) do |
|
147
|
+
sess.get_next(["sysDescr", "sysContact"]) do |op, pdu|
|
100
148
|
did_callback = true
|
101
|
-
|
102
|
-
|
149
|
+
pdu.varbinds[0].value.should eql("test.net-snmp.org")
|
150
|
+
pdu.varbinds[1].value.should match(/Coders/)
|
103
151
|
end
|
104
152
|
end
|
105
|
-
Net::SNMP::Dispatcher.
|
153
|
+
Net::SNMP::Dispatcher.select(false)
|
106
154
|
sess.close
|
107
155
|
did_callback.should be(true)
|
108
156
|
end
|
data/spec/em_spec.rb
CHANGED
@@ -9,7 +9,7 @@ describe "em" do
|
|
9
9
|
Net::SNMP::Dispatcher.em_loop
|
10
10
|
|
11
11
|
session = Net::SNMP::Session.open(:peername => 'test.net-snmp.org', :community => 'demopublic') do |s|
|
12
|
-
s.get("sysDescr.0") do |result|
|
12
|
+
s.get("sysDescr.0") do |op, result|
|
13
13
|
did_callback = true
|
14
14
|
result.varbinds[0].value.should eql("test.net-snmp.org")
|
15
15
|
end
|
data/spec/error_spec.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'eventmachine'
|
3
|
+
|
4
|
+
describe "snmp errors" do
|
5
|
+
it "should rescue a timeout error" do
|
6
|
+
Net::SNMP::Session.open(:peername => 'www.yahoo.com') do |sess|
|
7
|
+
begin
|
8
|
+
sess.get("sysDescr.0")
|
9
|
+
rescue Net::SNMP::Error => e
|
10
|
+
e.print
|
11
|
+
e.status.should eql(Net::SNMP::Constants::STAT_TIMEOUT)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should rescue timeout error in a fiber" do
|
17
|
+
got_error = false
|
18
|
+
EM.run {
|
19
|
+
Fiber.new {
|
20
|
+
Net::SNMP::Dispatcher.fiber_loop
|
21
|
+
Net::SNMP::Session.open(:peername => 'www.yahoo.com') do |sess|
|
22
|
+
begin
|
23
|
+
puts "sending get pdu"
|
24
|
+
sess.get("sysDescr.0")
|
25
|
+
puts "done sess.get"
|
26
|
+
rescue Net::SNMP::TimeoutError => e
|
27
|
+
got_error = true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
EM.stop
|
31
|
+
}.resume(nil)
|
32
|
+
}
|
33
|
+
got_error.should be_true
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|