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.
@@ -1,22 +1,30 @@
1
1
  module Net
2
2
  module SNMP
3
3
  class Error < RuntimeError
4
- #attr :status, :errno, :snmp_err, :snmp_msg
5
-
4
+ attr_accessor :status, :errno, :snmp_err, :snmp_msg
6
5
  def initialize(opts = {})
7
6
  @status = opts[:status]
7
+ @fiber = opts[:fiber]
8
8
  if opts[:session]
9
- errno_ptr = FFI::MemoryPointer.new(:int)
10
- snmp_err_ptr = FFI::MemoryPointer.new(:int)
11
- msg_ptr = FFI::MemoryPointer.new(:pointer)
12
- Wrapper.snmp_error(opts[:session].pointer, errno_ptr, snmp_err_ptr, msg_ptr)
13
-
14
- @errno = errno_ptr.read_int
15
- @snmp_err = snmp_err_ptr.read_int
16
- @snmp_msg = msg_ptr.read_pointer.read_string
9
+ @errno = opts[:session].errno
10
+ @snmp_err = opts[:session].snmp_err
11
+ @snmp_msg = opts[:session].error_message
17
12
  end
13
+
14
+ end
15
+
16
+ def print
17
+ puts "SNMP Error: #{self.class.to_s}"
18
+ puts "message = #{message}"
19
+ puts "status = #{@status}"
20
+ puts "errno = #{@errno}"
21
+ puts "snmp_err = #{@snmp_err}"
22
+ puts "snmp_msg = #{@snmp_msg}"
18
23
  end
19
24
  end
25
+
26
+ class TimeoutError < Error
27
+ end
20
28
  end
21
29
  end
22
30
 
@@ -5,21 +5,11 @@ module Net
5
5
 
6
6
  inline do |builder|
7
7
  builder.include "sys/select.h"
8
- builder.include "stdio.h"
9
- builder.library "netsnmp"
8
+ #builder.include "stdio.h"
9
+ #builder.library "netsnmp"
10
10
  builder.c %q{
11
- int snmp_process_callbacks() {
12
- int fds = 0, block = 1;
13
- fd_set fdset;
14
- struct timeval timeout;
15
- FD_ZERO(&fdset);
16
- snmp_select_info(&fds, &fdset, &timeout, &block);
17
- fds = select(fds, &fdset, 0,0, block ? 0 : &timeout);
18
- if(fds) {
19
- snmp_read(&fdset);
20
- } else {
21
- snmp_timeout();
22
- }
11
+ int fd_setsize() {
12
+ return(FD_SETSIZE);
23
13
  }
24
14
  }
25
15
 
data/lib/net/snmp/oid.rb CHANGED
@@ -2,12 +2,15 @@ module Net
2
2
  module SNMP
3
3
  class OID
4
4
  attr_reader :oid, :pointer, :length_pointer
5
+
6
+ def from_pointer(ptr, len)
7
+
8
+ end
5
9
  def initialize(oid)
6
10
  @oid = oid
7
11
  @pointer = FFI::MemoryPointer.new(:ulong, Constants::MAX_OID_LEN)
8
12
  @length_pointer = FFI::MemoryPointer.new(:size_t)
9
13
  @length_pointer.write_int(Constants::MAX_OID_LEN)
10
-
11
14
  if @oid =~ /^[\d\.]*$/
12
15
  if Wrapper.read_objid(@oid, @pointer, @length_pointer) == 0
13
16
  Wrapper.snmp_perror(@oid)
@@ -22,8 +25,9 @@ module Net
22
25
  end
23
26
 
24
27
  def size
25
- @length_pointer.read_int
28
+ @length_pointer.read_int * 8 # XXX 8 = sizeof(oid) on my system. Not sure if it's different on others
26
29
  end
30
+
27
31
  def oid
28
32
  @oid
29
33
  end
data/lib/net/snmp/pdu.rb CHANGED
@@ -1,15 +1,25 @@
1
1
  module Net
2
2
  module SNMP
3
3
  class PDU
4
+ # == Represents an SNMP PDU
5
+ #
6
+ # Wrapper around netsnmp_pdu.
7
+ # * +varbinds+ returns a list of variable bindings
8
+ # * +errstat+ returns the error code. See constants.rb
9
+ # * +errindex+ returns the index of the varbind causing the error.
4
10
  extend Forwardable
5
- attr_accessor :struct, :varbinds, :callback
11
+ include Net::SNMP::Debug
12
+ attr_accessor :struct, :varbinds, :callback, :command
6
13
  def_delegators :struct, :pointer
7
14
 
8
- def initialize(arg)
15
+ # Create a new PDU object.
16
+ # +pdu_type+ The type of the PDU. For example, Net::SNMP::SNMP_MSG_GET. See constants.rb
17
+ def initialize(pdu_type)
9
18
  @varbinds = []
10
- case arg
19
+ case pdu_type
11
20
  when FFI::Pointer
12
- @struct = Wrapper::SnmpPdu.new(arg)
21
+ @struct = Wrapper::SnmpPdu.new(pdu_type)
22
+ @command = @struct.command
13
23
  v = @struct[:variables]
14
24
  if v
15
25
  @varbinds << Varbind.from_pointer(v)
@@ -19,9 +29,10 @@ module Net
19
29
  @varbinds << Varbind.from_pointer(v)
20
30
  end
21
31
  when Fixnum
22
- @struct = Wrapper.snmp_pdu_create(arg)
32
+ @struct = Wrapper.snmp_pdu_create(pdu_type)
33
+ @command = pdu_type
23
34
  else
24
- raise "invalid type"
35
+ raise Error.new, "invalid pdu type"
25
36
  end
26
37
  end
27
38
 
@@ -65,12 +76,20 @@ module Net
65
76
  end
66
77
  end
67
78
 
79
+ # Adds a variable binding to the pdu.
80
+ # Options:
81
+ # * +oid+ The SNMP OID
82
+ # * +type+ The data type. Possible values include Net::SNMP::ASN_OCTET_STR, Net::SNMP::ASN_COUNTER, etc. See constants.rb
83
+ # * +value+ The value of the varbind. default is nil.
68
84
  def add_varbind(options)
85
+ #puts "adding varbind #{options.inspect}"
69
86
  options[:type] ||= case options[:value]
70
87
  when String
71
88
  Constants::ASN_OCTET_STR
72
89
  when Fixnum
73
90
  Constants::ASN_INTEGER
91
+ when Net::SNMP::OID
92
+ Constants::ASN_OBJECT_ID
74
93
  when nil
75
94
  Constants::ASN_NULL
76
95
  else
@@ -78,39 +97,59 @@ module Net
78
97
  end
79
98
 
80
99
  value = options[:value]
81
- value_len = case options[:value]
82
- when String
83
- options[:value].length
84
- when nil
100
+ value_len = case options[:type]
101
+ when Constants::ASN_NULL
85
102
  0
86
103
  else
87
104
  options[:value].size
88
105
  end
89
106
 
90
-
91
- if value.respond_to?(:pointer)
92
- value = value.pointer
107
+ value = case options[:type]
108
+ when Constants::ASN_INTEGER, Constants::ASN_GAUGE, Constants::ASN_COUNTER, Constants::ASN_TIMETICKS, Constants::ASN_UNSIGNED
109
+ new_val = FFI::MemoryPointer.new(:long)
110
+ new_val.write_long(value)
111
+ new_val
112
+ when Constants::ASN_OCTET_STR, Constants::ASN_BIT_STR, Constants::ASN_OPAQUE
113
+ value
114
+ when Constants::ASN_IPADDRESS
115
+ # TODO
116
+ when Constants::ASN_OBJECT_ID
117
+ value.pointer
118
+ when Constants::ASN_NULL
119
+ nil
120
+ else
121
+ if value.respond_to?(:pointer)
122
+ value.pointer
123
+ else
124
+ raise Net::SNMP::Error.new, "Unknown variable type #{options[:type]}"
125
+ end
93
126
  end
94
127
 
95
- #oid = options[:oid].kind_of?(Net::SNMP::OID) ? options[:oid] : Net::SNMP::OID.new(options[:oid])
96
- oid = Net::SNMP::OID.new(options[:oid])
128
+ oid = options[:oid].kind_of?(Net::SNMP::OID) ? options[:oid] : Net::SNMP::OID.new(options[:oid])
97
129
  var_ptr = Wrapper.snmp_pdu_add_variable(@struct.pointer, oid.pointer, oid.length_pointer.read_int, options[:type], value, value_len)
98
130
  varbind = Varbind.new(var_ptr)
99
131
  #Wrapper.print_varbind(varbind.struct)
100
132
  varbinds << varbind
101
133
  end
102
-
134
+
135
+ # Returns true if pdu is in error
103
136
  def error?
104
- errstat != 0
137
+ self.errstat != 0
105
138
  end
106
-
139
+
140
+ # A descriptive error message
107
141
  def error_message
108
- Wrapper::snmp_errstring(errstat)
142
+ Wrapper::snmp_errstring(self.errstat)
109
143
  end
110
144
 
145
+ # Free the pdu
111
146
  def free
112
147
  Wrapper.snmp_free_pdu(@struct.pointer)
113
148
  end
149
+
150
+ def print
151
+ puts pdu.inspect
152
+ end
114
153
  end
115
154
  end
116
155
  end
@@ -4,7 +4,11 @@ require 'pp'
4
4
  module Net
5
5
  module SNMP
6
6
  class Session
7
+ # == SNMP Session
8
+ #
9
+ # Provides API for interacting with a host with snmp
7
10
  extend Forwardable
11
+ include Net::SNMP::Debug
8
12
  attr_accessor :struct, :callback, :requests, :peername, :community
9
13
  attr_reader :version
10
14
  def_delegator :@struct, :pointer
@@ -14,17 +18,34 @@ module Net
14
18
 
15
19
  class << self
16
20
  attr_accessor :sessions, :lock
21
+
22
+ # Open a new session. Accepts a block which yields the session.
23
+ #
24
+ # Net::SNMP::Session.open(:peername => 'test.net-snmp.org', :community => 'public') do |sess|
25
+ # pdu = sess.get(["sysDescr.0"])
26
+ # pdu.print
27
+ # end
28
+ # Options:
29
+ # * +peername+ - hostname
30
+ # * +community+ - snmp community string. Default is public
31
+ # * +version+ - snmp version. Possible values include 1, '2c', and 3. Default is 1.
32
+ # * +timeout+ - snmp timeout in seconds
33
+ # * +retries+ - snmp retries. default = 5
34
+ # Returns:
35
+ # Net::SNMP::Session
36
+
17
37
  def open(options = {})
18
38
  #puts "building session"
19
39
  session = new(options)
20
- @lock.synchronize {
21
- @sessions[session.sessid] = session
22
- }
23
- #puts "done building"
40
+ if Net::SNMP::thread_safe
41
+ Net::SNMP::Session.lock.synchronize {
42
+ Net::SNMP::Session.sessions[session.sessid] = session
43
+ }
44
+ else
45
+ Net::SNMP::Session.sessions[session.sessid] = session
46
+ end
24
47
  if block_given?
25
- #puts "calling block"
26
48
  yield session
27
-
28
49
  end
29
50
  session
30
51
  end
@@ -32,13 +53,15 @@ module Net
32
53
 
33
54
  def initialize(options = {})
34
55
  #puts "in initialize"
56
+ @timeout = options[:timeout] || 1
57
+ @retries = options[:retries] || 5
35
58
  @requests = {}
36
59
  @peername = options[:peername] || 'localhost'
60
+ @peername = "#{@peername}:#{options[:port]}" if options[:port]
37
61
  @community = options[:community] || "public"
38
62
  options[:community_len] = @community.length
39
63
  @version = options[:version] || 1
40
64
  options[:version] ||= Constants::SNMP_VERSION_1
41
- @callback = options[:callback]
42
65
  @version = options[:version] || 1
43
66
  #self.class.sessions << self
44
67
  @sess = Wrapper::SnmpSession.new(nil)
@@ -47,6 +70,7 @@ module Net
47
70
  @sess.community = FFI::MemoryPointer.from_string(@community)
48
71
  @sess.community_len = @community.length
49
72
  @sess.peername = FFI::MemoryPointer.from_string(@peername)
73
+ #@sess.remote_port = options[:port] || 162
50
74
  @sess.version = case options[:version].to_s
51
75
  when '1'
52
76
  Constants::SNMP_VERSION_1
@@ -57,23 +81,19 @@ module Net
57
81
  else
58
82
  Constants::SNMP_VERSION_1
59
83
  end
84
+ debug "setting timeout = #{@timeout} retries = #{@retries}"
85
+ @sess.timeout = @timeout * 1000000
86
+ @sess.retries = @retries
60
87
 
61
- if options[:timeout]
62
- @sess.timeout = options[:timeout] * 1000000
63
- end
64
- if options[:retries]
65
- @sess.retries = options[:retries]
66
- end
67
88
  if @sess.version == Constants::SNMP_VERSION_3
68
89
  @sess.securityLevel = options[:security_level] || Constants::SNMP_SEC_LEVEL_NOAUTH
69
-
70
90
  @sess.securityAuthProto = case options[:auth_protocol]
71
91
  when :sha1
72
- Net::SNMP::OID.new("1.3.6.1.6.3.10.1.1.3").pointer
92
+ OID.new("1.3.6.1.6.3.10.1.1.3").pointer
73
93
  when :md5
74
- Net::SNMP::OID.new("1.3.6.1.6.3.10.1.1.2").pointer
94
+ OID.new("1.3.6.1.6.3.10.1.1.2").pointer
75
95
  when nil
76
- Net::SNMP::OID.new("1.3.6.1.6.3.10.1.1.1").pointer
96
+ OID.new("1.3.6.1.6.3.10.1.1.1").pointer
77
97
  end
78
98
 
79
99
  @sess.securityAuthProtoLen = 10
@@ -97,32 +117,28 @@ module Net
97
117
  end
98
118
  end
99
119
  # General callback just takes the pdu, calls the session callback if any, then the request specific callback.
100
- @sess.callback = lambda do |operation, session, reqid, pdu_ptr, magic|
101
- #puts "callback is #{callback.inspect}"
102
- #callback.call(operation, reqid, pdu, magic) if callback
103
120
 
104
- if @requests[reqid]
105
- pdu = Net::SNMP::PDU.new(pdu_ptr)
106
- @requests[reqid].call(pdu)
107
- @requests.delete(reqid)
108
- end
109
- 0
110
- end
111
121
  @struct = Wrapper.snmp_sess_open(@sess.pointer)
112
- #@handle = Wrapper.snmp_sess_open(@sess.pointer)
113
- #@struct = Wrapper.snmp_sess_session(@handle)
114
122
  end
115
123
 
116
124
 
125
+ # Close the snmp session and free associated resources.
117
126
  def close
118
- Net::SNMP::Session.lock.synchronize {
127
+ if Net::SNMP.thread_safe
128
+ self.class.lock.synchronize {
129
+ Wrapper.snmp_sess_close(@struct)
130
+ self.class.sessions.delete(self.sessid)
131
+ }
132
+ else
119
133
  Wrapper.snmp_sess_close(@struct)
120
- Net::SNMP::Session.sessions.delete(self.sessid)
121
- }
134
+ self.class.sessions.delete(self.sessid)
135
+ end
122
136
  end
123
137
 
124
- def get(oidlist, options = {}, &block)
125
- pdu = Net::SNMP::PDU.new(Constants::SNMP_MSG_GET)
138
+ # Issue an SNMP GET Request.
139
+ # See #send_pdu
140
+ def get(oidlist, &block)
141
+ pdu = PDU.new(Constants::SNMP_MSG_GET)
126
142
  oidlist = [oidlist] unless oidlist.kind_of?(Array)
127
143
  oidlist.each do |oid|
128
144
  pdu.add_varbind(:oid => oid)
@@ -130,8 +146,10 @@ module Net
130
146
  send_pdu(pdu, &block)
131
147
  end
132
148
 
133
- def get_next(oidlist, options = {}, &block)
134
- pdu = Net::SNMP::PDU.new(Constants::SNMP_MSG_GETNEXT)
149
+ # Issue an SNMP GETNEXT Request
150
+ # See #send_pdu
151
+ def get_next(oidlist, &block)
152
+ pdu = PDU.new(Constants::SNMP_MSG_GETNEXT)
135
153
  oidlist = [oidlist] unless oidlist.kind_of?(Array)
136
154
  oidlist.each do |oid|
137
155
  pdu.add_varbind(:oid => oid)
@@ -139,8 +157,10 @@ module Net
139
157
  send_pdu(pdu, &block)
140
158
  end
141
159
 
160
+ # Issue an SNMP GETBULK Request
161
+ # See #send_pdu
142
162
  def get_bulk(oidlist, options = {}, &block)
143
- pdu = Net::SNMP::PDU.new(Constants::SNMP_MSG_GETBULK)
163
+ pdu = PDU.new(Constants::SNMP_MSG_GETBULK)
144
164
  oidlist = [oidlist] unless oidlist.kind_of?(Array)
145
165
  oidlist.each do |oid|
146
166
  pdu.add_varbind(:oid => oid)
@@ -151,9 +171,10 @@ module Net
151
171
  end
152
172
 
153
173
 
154
-
155
- def set(oidlist, options = {}, &block)
156
- pdu = Net::SNMP::PDU.new(Constants::SNMP_MSG_SET)
174
+ # Issue an SNMP Set Request
175
+ # See #send_pdu
176
+ def set(oidlist, &block)
177
+ pdu = PDU.new(Constants::SNMP_MSG_SET)
157
178
  oidlist.each do |oid|
158
179
  pdu.add_varbind(:oid => oid[0], :type => oid[1], :value => oid[2])
159
180
  end
@@ -169,187 +190,260 @@ module Net
169
190
  end
170
191
 
171
192
 
172
- # XXX This needs work. Need to use getbulk for speed, guess maxrepeaters, etc..
173
- # Also need to figure out how we can tell column names from something like ifTable
174
- # instead of ifEntry. Needs to handle errors, there are probably offset problems
175
- # in cases of bad data, and various other problems. Also need to add async support.
176
- # Maybe return a hash with index as key?
177
- def get_table(table_name, options = {})
178
- column_names = options[:columns] || Net::SNMP::MIB::Node.get_node(table_name).children.collect {|c| c.label }
179
- results = []
180
-
181
- first_result = get_next(column_names)
182
- oidlist = []
183
- good_column_names = []
184
- row = {}
185
-
186
- first_result.varbinds.each_with_index do |vb, idx|
187
- oid = vb.oid
188
- if oid.label[0..column_names[idx].length - 1] == column_names[idx]
189
- oidlist << oid.label
190
- good_column_names << column_names[idx]
191
- row[column_names[idx]] = vb.value
192
- end
193
- end
194
- results << row
195
193
 
196
- catch :break_main_loop do
197
- while(result = get_next(oidlist))
198
- oidlist = []
199
- row = {}
200
- result.varbinds.each_with_index do |vb, idx|
201
- #puts "got #{vb.oid.label} #{vb.value.inspect}, type = #{vb.object_type}"
202
- row[good_column_names[idx]] = vb.value
203
- oidlist << vb.oid.label
204
- if vb.oid.label[0..good_column_names[idx].length - 1] != good_column_names[idx]
205
- throw :break_main_loop
206
- end
207
- end
208
- results << row
209
- end
210
- end
211
- results
212
- end
213
-
214
194
 
215
195
  def default_max_repeaters
216
196
  # We could do something based on transport here. 25 seems safe
217
197
  25
218
198
  end
219
199
 
220
- def get_columns(columns, options = {})
221
- column_oids = columns.map {|c| Net::SNMP::OID.new(c)}
222
- options[:max_repetitions] ||= default_max_repeaters / columns.size
223
- results = {}
224
- if version == 1
225
- else
226
- while(result = get_bulk(columns, options))
227
- result.varbinds.each do |vb|
228
- match = column_oids.select {|c| vb.oid.oid =~ /^#{c.oid}/ }
229
- end
230
- end
231
200
 
232
- end
233
- end
234
201
 
235
- def error(msg)
236
- Wrapper.snmp_perror(msg)
237
- Wrapper.snmp_sess_perror(msg, @sess.pointer)
238
-
239
- #Wrapper.print_session(self.struct)
240
- raise Net::SNMP::Error.new({:session => self}), msg
202
+ # Raise a NET::SNMP::Error with the session attached
203
+ def error(msg, options = {})
204
+ #Wrapper.snmp_sess_perror(msg, @sess.pointer)
205
+ raise Error.new({:session => self}.merge(options)), msg
241
206
  end
242
207
 
243
208
 
244
- def trap(options = {})
245
- pdu = PDU.new(Constants::SNMP_MSG_TRAP)
246
- options[:enterprise] ||= '1.3.6.1.4.1.3.1.1' # uh, just send netsnmp enterprise i guess
247
- pdu.enterprise = OID.new(options[:enterprise])
248
- pdu.trap_type = options[:trap_type] || 1 # need to check all these defaults
249
- pdu.specific_type = options[:specific_type] || 0
250
- pdu.time = 1 # put what here?
251
- send_pdu(pdu)
252
- true
253
- end
254
-
255
-
256
- def trap_v2(options = {})
257
- pdu = PDU.new(Constants::SNMP_MSG_TRAP2)
258
- build_trap_pdu(pdu, options)
259
- send_pdu(pdu)
260
- end
261
-
262
- def inform(options = {}, &block)
263
- pdu = PDU.new(Constants::SNMP_MSG_INFORM)
264
- build_trap_pdu(pdu, options)
265
- send_pdu(pdu, &block)
266
- end
267
-
268
- def poll(timeout = nil)
269
-
270
- fdset = Net::SNMP::Wrapper.get_fd_set
209
+ # Check the session for SNMP responses from asynchronous SNMP requests
210
+ # This method will check for new responses and call the associated
211
+ # response callbacks.
212
+ # +timeout+ A timeout of nil indicates a poll and will return immediately.
213
+ # A value of false will block until data is available. Otherwise, pass
214
+ # the number of seconds to block.
215
+ # Returns the number of file descriptors handled.
216
+ def select(timeout = nil)
217
+ fdset = FFI::MemoryPointer.new(:pointer, Net::SNMP::Inline.fd_setsize / 8)
271
218
  num_fds = FFI::MemoryPointer.new(:int)
272
219
  tv_sec = timeout ? timeout.round : 0
273
220
  tv_usec = timeout ? (timeout - timeout.round) * 1000000 : 0
274
- tval = Net::SNMP::Wrapper::TimeVal.new(:tv_sec => tv_sec, :tv_usec => tv_usec)
221
+ tval = Wrapper::TimeVal.new(:tv_sec => tv_sec, :tv_usec => tv_usec)
275
222
  block = FFI::MemoryPointer.new(:int)
276
-
277
223
  if timeout.nil?
278
224
  block.write_int(0)
279
225
  else
280
226
  block.write_int(1)
281
227
  end
282
- #puts "calling snmp_select_info"
283
- num = Net::SNMP::Wrapper.snmp_sess_select_info(@struct, num_fds, fdset, tval.pointer, block )
284
- num_ready = 0
285
- #puts "block = #{block.read_int}"
286
-
287
- #puts "numready = #{num_fds.read_int}"
288
- #puts "tv = #{tval[:tv_sec]} #{tval[:tv_usec]}"
289
- #puts "timeout = #{timeout}"
228
+
229
+ Wrapper.snmp_sess_select_info(@struct, num_fds, fdset, tval.pointer, block )
290
230
  tv = (timeout == false ? nil : tval)
291
- #puts "calling select"
292
- #puts "tv = #{tv.inspect}"
293
- #puts "calling select with #{num_fds.read_int}"
294
- #num_ready = RubyWrapper.rb_thread_select(num_fds.read_int, fdset, nil, nil, tv)
295
- num_ready = Net::SNMP::Wrapper.select(num_fds.read_int, fdset, nil, nil, tv)
296
- #puts "done select. num_ready = #{num_ready}"
231
+ debug "Calling select #{Time.now}"
232
+ num_ready = FFI::LibC.select(num_fds.read_int, fdset, nil, nil, tv)
233
+ debug "Done select #{Time.now}"
297
234
  if num_ready > 0
298
- Net::SNMP::Wrapper.snmp_sess_read(@struct, fdset)
235
+ Wrapper.snmp_sess_read(@struct, fdset)
299
236
  elsif num_ready == 0
300
- # timeout. do something here? or just return 0?
237
+ Wrapper.snmp_sess_timeout(@struct)
301
238
  elsif num_ready == -1
302
239
  # error. check snmp_error?
240
+ error("select")
303
241
  else
304
- # uhhh
242
+ error("wtf is wrong with select?")
305
243
  end
306
- #puts "done snmp_read"
307
244
  num_ready
308
245
  end
309
246
 
310
- private
311
- def send_pdu(pdu, &block)
247
+ alias :poll :select
248
+
249
+
250
+
251
+ # Send a PDU
252
+ # +pdu+ The Net::SNMP::PDU object to send. Usually created by Session.get, Session.getnext, etc.
253
+ # +callback+ An optional callback. It should take two parameters, status and response_pdu.
254
+ # If no +callback+ is given, the call will block until the response is available and will return
255
+ # the response pdu. If an error occurs, a Net::SNMP::Error will be thrown.
256
+ # If +callback+ is passed, the PDU will be sent and +send_pdu+ will return immediately. You must
257
+ # then call Session.select to invoke the callback. This is usually done in some sort of event loop.
258
+ # See Net::SNMP::Dispatcher.
259
+ #
260
+ # If you're running inside eventmachine and have fibers (ruby 1.9, jruby, etc), sychronous calls will
261
+ # actually run asynchronously behind the scenes. Just run Net::SNMP::Dispatcher.fiber_loop in your
262
+ # reactor.
263
+ #
264
+ # pdu = Net::SNMP::PDU.new(Constants::SNMP_MSG_GET)
265
+ # pdu.add_varbind(:oid => 'sysDescr.0')
266
+ # session.send_pdu(pdu) do |status, pdu|
267
+ # if status == :success
268
+ # pdu.print
269
+ # elsif status == :timeout
270
+ # puts "Timed Out"
271
+ # else
272
+ # puts "A problem occurred"
273
+ # end
274
+ # end
275
+ # session.select(false) #block until data is ready. Callback will be called.
276
+ # begin
277
+ # result = session.send_pdu(pdu)
278
+ # puts result.inspect
279
+ # rescue Net::SNMP::Error => e
280
+ # puts e.message
281
+ # end
282
+ def send_pdu(pdu, &callback)
312
283
  #puts "send_pdu #{Fiber.current.inspect}"
313
- if defined?(EM) && EM.reactor_running? && !block_given?
314
- #puts "REACTORRUNNING"
315
- f = Fiber.current
316
-
317
- send_pdu pdu do | response |
318
- f.resume(response)
284
+ if block_given?
285
+ @requests[pdu.reqid] = callback
286
+ puts "calling async_send"
287
+ if Wrapper.snmp_sess_async_send(@struct, pdu.pointer, sess_callback, nil) == 0
288
+ error("snmp_get async failed")
319
289
  end
320
- Fiber.yield
290
+ #pdu.free
291
+ nil
321
292
  else
322
- if block
323
- @requests[pdu.reqid] = block
324
- if (status = Net::SNMP::Wrapper.snmp_sess_send(@struct, pdu.pointer)) == 0
325
- error("snmp_get async failed")
293
+ if defined?(EM) && EM.reactor_running? && defined?(Fiber)
294
+ f = Fiber.current
295
+ send_pdu pdu do | op, response_pdu |
296
+ f.resume([op, response_pdu])
297
+ end
298
+ op, result = Fiber.yield
299
+ case op
300
+ when :timeout
301
+ raise TimeoutError.new, "timeout"
302
+ when :send_failed
303
+ error "send failed"
304
+ when :success
305
+ result
306
+ when :connect, :disconnect
307
+ nil #does this ever happen?
308
+ else
309
+ error "unknown operation #{op}"
326
310
  end
327
- #pdu.free
328
- nil
329
311
  else
330
312
  response_ptr = FFI::MemoryPointer.new(:pointer)
331
- #Net::SNMP::Wrapper.print_session(@struct)
332
- #Net::SNMP::Wrapper.print_pdu(pdu.struct)
333
- #if pdu.command == Net::SNMP::Constants::SNMP_MSG_TRAP
334
- # status = Net::SNMP::Wrapper.snmp_sess_send(@struct, pdu.pointer) == 1 ? 0 : 1
335
- #else
336
- status = Net::SNMP::Wrapper.snmp_sess_synch_response(@struct, pdu.pointer, response_ptr)
337
- #end
338
- #pdu.free #causing segfaults
339
- if status != 0
340
- error("snmp_get failed #{status}")
313
+ if [Constants::SNMP_MSG_TRAP, Constants::SNMP_MSG_TRAP2].include?(pdu.command)
314
+ status = Wrapper.snmp_sess_send(@struct, pdu.pointer)
315
+ if status == 0
316
+ error("snmp_sess_send")
317
+ end
341
318
  else
342
- #Net::SNMP::Wrapper.print_pdu(Net::SNMP::Wrapper::SnmpPdu.new(response_ptr.read_pointer))
343
- #if pdu.command == Net::SNMP::Constants::SNMP_MSG_TRAP
344
- Net::SNMP::PDU.new(response_ptr.read_pointer)
345
- #else
346
- # 1
347
- #end
348
-
319
+ status = Wrapper.snmp_sess_synch_response(@struct, pdu.pointer, response_ptr)
320
+ unless status == Constants::STAT_SUCCESS
321
+ error("snmp_sess_synch_response", :status => status)
322
+ end
349
323
  end
324
+ if [Constants::SNMP_MSG_TRAP, Constants::SNMP_MSG_TRAP2].include?(pdu.command)
325
+ 1
326
+ else
327
+ PDU.new(response_ptr.read_pointer)
328
+ end
329
+ end
330
+ end
331
+ end
332
+
333
+ def errno
334
+ get_error
335
+ @errno
336
+ end
337
+
338
+ # The SNMP Session error code
339
+ def snmp_err
340
+ get_error
341
+ @snmp_err
342
+ end
343
+
344
+ # The SNMP Session error message
345
+ def error_message
346
+ get_error
347
+ @snmp_msg
348
+ end
349
+
350
+ private
351
+ def sess_callback
352
+ @sess_callback ||= FFI::Function.new(:int, [:int, :pointer, :int, :pointer, :pointer]) do |operation, session, reqid, pdu_ptr, magic|
353
+ #puts "in callback #{operation.inspect} #{session.inspect}"
354
+ op = case operation
355
+ when Constants::NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE
356
+ :success
357
+ when Constants::NETSNMP_CALLBACK_OP_TIMED_OUT
358
+ :timeout
359
+ when Constants::NETSNMP_CALLBACK_OP_SEND_FAILED
360
+ :send_failed
361
+ when Constants::NETSNMP_CALLBACK_OP_CONNECT
362
+ :connect
363
+ when Constants::NETSNMP_CALLBACK_OP_DISCONNECT
364
+ :disconnect
365
+ else
366
+ error "Invalid PDU Operation"
367
+ end
368
+
369
+ if @requests[reqid]
370
+ pdu = PDU.new(pdu_ptr)
371
+ callback_return = @requests[reqid].call(op, pdu)
372
+ @requests.delete(reqid)
373
+ callback_return == false ? 0 : 1 #if callback returns false (failure), pass it on. otherwise return 1 (success)
374
+ else
375
+ 0# Do what here? Can this happen? Maybe request timed out and was deleted?
350
376
  end
351
377
  end
352
378
  end
379
+ def get_error
380
+ errno_ptr = FFI::MemoryPointer.new(:int)
381
+ snmp_err_ptr = FFI::MemoryPointer.new(:int)
382
+ msg_ptr = FFI::MemoryPointer.new(:pointer)
383
+ Wrapper.snmp_sess_error(@struct.pointer, errno_ptr, snmp_err_ptr, msg_ptr)
384
+ @errno = errno_ptr.read_int
385
+ @snmp_err = snmp_err_ptr.read_int
386
+ @snmp_msg = msg_ptr.read_pointer.read_string
387
+ end
388
+
389
+ public
390
+ # XXX This needs work. Need to use getbulk for speed, guess maxrepeaters, etc..
391
+ # Also need to figure out how we can tell column names from something like ifTable
392
+ # instead of ifEntry. Needs to handle errors, there are probably offset problems
393
+ # in cases of bad data, and various other problems. Also need to add async support.
394
+ # Maybe return a hash with index as key?
395
+ def get_table(table_name, options = {})
396
+ column_names = options[:columns] || MIB::Node.get_node(table_name).children.collect {|c| c.label }
397
+ results = []
398
+
399
+ # repeat_count = if @version.to_s == '1' || options[:no_getbulk]
400
+ # 1
401
+ # elsif options[:repeat_count]
402
+ # options[:repeat_count]
403
+ # else
404
+ # (1000 / 36 / (column_names.size + 1)).to_i
405
+ # end
406
+ #
407
+ # res = if @version.to_s != '1' && !options[:no_getbulk]
408
+ # get_bulk(column_names, :max_repetitions => repeat_count)
409
+ # else
410
+ # get_next(column_names)
411
+ # end
412
+
413
+
414
+ first_result = get_next(column_names)
415
+ oidlist = []
416
+ good_column_names = []
417
+ row = {}
418
+
419
+ first_result.varbinds.each_with_index do |vb, idx|
420
+ oid = vb.oid
421
+ if oid.label[0..column_names[idx].length - 1] == column_names[idx]
422
+ oidlist << oid.label
423
+ good_column_names << column_names[idx]
424
+ row[column_names[idx]] = vb.value
425
+ end
426
+ end
427
+ results << row
428
+
429
+ catch :break_main_loop do
430
+ while(result = get_next(oidlist))
431
+ oidlist = []
432
+ row = {}
433
+ result.varbinds.each_with_index do |vb, idx|
434
+ #puts "got #{vb.oid.label} #{vb.value.inspect}, type = #{vb.object_type}"
435
+ row[good_column_names[idx]] = vb.value
436
+ oidlist << vb.oid.label
437
+ if vb.oid.label[0..good_column_names[idx].length - 1] != good_column_names[idx]
438
+ throw :break_main_loop
439
+ end
440
+ end
441
+ results << row
442
+ end
443
+ end
444
+ results
445
+ end
446
+
353
447
 
354
448
  # def get_entries_cb(pdu, columns, options)
355
449
  # cache = {}
@@ -372,22 +466,8 @@ module Net
372
466
  # end
373
467
  # end
374
468
  # end
375
-
376
-
377
-
378
-
379
-
380
- def build_trap_pdu(pdu, options = {})
381
- pdu.add_varbind(:oid => OID.new('sysUpTime'), :type => Constants::ASN_TIMETICKS, :value => 0)
382
- pdu.add_varbind(:oid => OID.new('snmpTrapOID'), :type => Constants::ASN_OBJECT_ID, :value => options[:oid])
383
- if options[:varbinds]
384
- options[:varbinds].each do |vb|
385
- pdu.add_varbind(vb)
386
- end
387
- end
388
- end
389
-
390
-
391
469
  end
392
470
  end
393
471
  end
472
+
473
+