net-snmp 0.1.7 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|