Class: Net::SNMP::Session

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Debug
Defined in:
lib/net/snmp/session.rb

Direct Known Subclasses

TrapSession

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Debug

#debug, debug=

Constructor Details

#initialize(options = {}) ⇒ Session

Returns a new instance of Session.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/net/snmp/session.rb', line 54

def initialize(options = {})
  #puts "in initialize"
  @timeout = options[:timeout] || 1
  @retries = options[:retries] || 5
  @requests = {}
  @peername = options[:peername] || 'localhost'
  @peername = "#{@peername}:#{options[:port]}" if options[:port]
  @community = options[:community] || "public"
  options[:community_len] = @community.length
  @version = options[:version] || 1
  options[:version] ||= Constants::SNMP_VERSION_1
  @version = options[:version] || 1
  #self.class.sessions << self
  @sess = Wrapper::SnmpSession.new(nil)
  Wrapper.snmp_sess_init(@sess.pointer)
  #options.each_pair {|k,v| ptr.send("#{k}=", v)}
  @sess.community = FFI::MemoryPointer.from_string(@community)
  @sess.community_len = @community.length
  @sess.peername = FFI::MemoryPointer.from_string(@peername)
  #@sess.remote_port = options[:port] || 162
  @sess.version = case options[:version].to_s
  when '1'
    Constants::SNMP_VERSION_1
  when '2', '2c'
    Constants::SNMP_VERSION_2c
  when '3'
    Constants::SNMP_VERSION_3
  else
    Constants::SNMP_VERSION_1
  end
  debug "setting timeout = #{@timeout} retries = #{@retries}"
  @sess.timeout = @timeout * 1000000
  @sess.retries = @retries

  if @sess.version == Constants::SNMP_VERSION_3
    @sess.securityLevel = options[:security_level] || Constants::SNMP_SEC_LEVEL_NOAUTH
    @sess.securityAuthProto = case options[:auth_protocol]
        when :sha1
          OID.new("1.3.6.1.6.3.10.1.1.3").pointer
        when :md5
          OID.new("1.3.6.1.6.3.10.1.1.2").pointer
        when nil
          OID.new("1.3.6.1.6.3.10.1.1.1").pointer
    end
    
    @sess.securityAuthProtoLen = 10
    @sess.securityAuthKeyLen = Constants::USM_AUTH_KU_LEN

    if options[:context]
      @sess.contextName = FFI::MemoryPointer.from_string(options[:context])
      @sess.contextNameLen = options[:context].length
    end

    if options[:username]
      @sess.securityName = FFI::MemoryPointer.from_string(options[:username])
      @sess.securityNameLen = options[:username].length
    end
    auth_len_ptr = FFI::MemoryPointer.new(:size_t)
    auth_len_ptr.write_int(Constants::USM_AUTH_KU_LEN)
    key_result = Wrapper.generate_Ku(@sess.securityAuthProto, @sess.securityAuthProtoLen, options[:password], options[:password].length, @sess.securityAuthKey, auth_len_ptr)
    @sess.securityAuthKeyLen = auth_len_ptr.read_int
    unless key_result == Constants::SNMPERR_SUCCESS
      Wrapper.snmp_perror("netsnmp")
    end
  end
  # General callback just takes the pdu, calls the session callback if any, then the request specific callback.

  @struct = Wrapper.snmp_sess_open(@sess.pointer)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(m, *args) ⇒ Object



184
185
186
187
188
189
190
# File 'lib/net/snmp/session.rb', line 184

def method_missing(m, *args)
  if @struct.respond_to?(m)
    @struct.send(m, *args)
  else
    super
  end
end

Class Attribute Details

.lockObject

Returns the value of attribute lock.



20
21
22
# File 'lib/net/snmp/session.rb', line 20

def lock
  @lock
end

.sessionsObject

Returns the value of attribute sessions.



20
21
22
# File 'lib/net/snmp/session.rb', line 20

def sessions
  @sessions
end

Instance Attribute Details

#callbackObject

Returns the value of attribute callback.



12
13
14
# File 'lib/net/snmp/session.rb', line 12

def callback
  @callback
end

#communityObject

Returns the value of attribute community.



12
13
14
# File 'lib/net/snmp/session.rb', line 12

def community
  @community
end

#peernameObject

Returns the value of attribute peername.



12
13
14
# File 'lib/net/snmp/session.rb', line 12

def peername
  @peername
end

#requestsObject

Returns the value of attribute requests.



12
13
14
# File 'lib/net/snmp/session.rb', line 12

def requests
  @requests
end

#structObject

Returns the value of attribute struct.



12
13
14
# File 'lib/net/snmp/session.rb', line 12

def struct
  @struct
end

#versionObject (readonly)

Returns the value of attribute version.



13
14
15
# File 'lib/net/snmp/session.rb', line 13

def version
  @version
end

Class Method Details

.open(options = {}) ⇒ Object

Open a new session. Accepts a block which yields the session.

Net::SNMP::Session.open(:peername => 'test.net-snmp.org', :community => 'public') do |sess|
  pdu = sess.get(["sysDescr.0"])
  pdu.print
end

Options:

  • peername - hostname

  • community - snmp community string. Default is public

  • version - snmp version. Possible values include 1, ‘2c’, and 3. Default is 1.

  • timeout - snmp timeout in seconds

  • retries - snmp retries. default = 5

Returns: Net::SNMP::Session



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/net/snmp/session.rb', line 37

def open(options = {})
  #puts "building session"
  session = new(options)
  if Net::SNMP::thread_safe
    Net::SNMP::Session.lock.synchronize {
      Net::SNMP::Session.sessions[session.sessid] = session
    }
  else
    Net::SNMP::Session.sessions[session.sessid] = session
  end
  if block_given?
    yield session
  end
  session
end

Instance Method Details

#closeObject

Close the snmp session and free associated resources.



126
127
128
129
130
131
132
133
134
135
136
# File 'lib/net/snmp/session.rb', line 126

def close
  if Net::SNMP.thread_safe
    self.class.lock.synchronize {
      Wrapper.snmp_sess_close(@struct)
      self.class.sessions.delete(self.sessid)
    }
  else
    Wrapper.snmp_sess_close(@struct)
    self.class.sessions.delete(self.sessid)
  end
end

#default_max_repeatersObject



195
196
197
198
# File 'lib/net/snmp/session.rb', line 195

def default_max_repeaters
  # We could do something based on transport here.  25 seems safe
  25
end

#errnoObject



333
334
335
336
# File 'lib/net/snmp/session.rb', line 333

def errno
  get_error
  @errno
end

#error(msg, options = {}) ⇒ Object

Raise a NET::SNMP::Error with the session attached

Raises:

  • (Error.new({:session => self}.merge(options)))


203
204
205
206
# File 'lib/net/snmp/session.rb', line 203

def error(msg, options = {})
  #Wrapper.snmp_sess_perror(msg, @sess.pointer)
  raise Error.new({:session => self}.merge(options)), msg
end

#error_messageObject

The SNMP Session error message



345
346
347
348
# File 'lib/net/snmp/session.rb', line 345

def error_message
  get_error
  @snmp_msg
end

#get(oidlist, &block) ⇒ Object

Issue an SNMP GET Request. See #send_pdu



140
141
142
143
144
145
146
147
# File 'lib/net/snmp/session.rb', line 140

def get(oidlist, &block)
  pdu = PDU.new(Constants::SNMP_MSG_GET)
  oidlist = [oidlist] unless oidlist.kind_of?(Array)
  oidlist.each do |oid|
    pdu.add_varbind(:oid => oid)
  end
  send_pdu(pdu, &block)
end

#get_bulk(oidlist, options = {}, &block) ⇒ Object

Issue an SNMP GETBULK Request See #send_pdu



162
163
164
165
166
167
168
169
170
171
# File 'lib/net/snmp/session.rb', line 162

def get_bulk(oidlist, options = {}, &block)
  pdu = PDU.new(Constants::SNMP_MSG_GETBULK)
  oidlist = [oidlist] unless oidlist.kind_of?(Array)
  oidlist.each do |oid|
    pdu.add_varbind(:oid => oid)
  end
  pdu.non_repeaters = options[:non_repeaters] || 0
  pdu.max_repetitions = options[:max_repetitions] || 10
  send_pdu(pdu,&block)
end

#get_next(oidlist, &block) ⇒ Object

Issue an SNMP GETNEXT Request See #send_pdu



151
152
153
154
155
156
157
158
# File 'lib/net/snmp/session.rb', line 151

def get_next(oidlist, &block)
  pdu = PDU.new(Constants::SNMP_MSG_GETNEXT)
  oidlist = [oidlist] unless oidlist.kind_of?(Array)
  oidlist.each do |oid|
    pdu.add_varbind(:oid => oid)
  end
  send_pdu(pdu, &block)
end

#get_table(table_name, options = {}) ⇒ Object

XXX This needs work. Need to use getbulk for speed, guess maxrepeaters, etc.. Also need to figure out how we can tell column names from something like ifTable instead of ifEntry. Needs to handle errors, there are probably offset problems in cases of bad data, and various other problems. Also need to add async support. Maybe return a hash with index as key?



395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
# File 'lib/net/snmp/session.rb', line 395

def get_table(table_name, options = {})
  column_names = options[:columns] || MIB::Node.get_node(table_name).children.collect {|c| c.label }
  results = []

#        repeat_count = if @version.to_s == '1' || options[:no_getbulk]
#            1
#          elsif options[:repeat_count]
#            options[:repeat_count]
#          else
#            (1000 / 36 / (column_names.size + 1)).to_i
#        end
#
#        res = if @version.to_s != '1' && !options[:no_getbulk]
#          get_bulk(column_names, :max_repetitions => repeat_count)
#        else
#          get_next(column_names)
#        end


  first_result = get_next(column_names)
  oidlist = []
  good_column_names = []
  row = {}

  first_result.varbinds.each_with_index do |vb, idx|
    oid = vb.oid
    if oid.label[0..column_names[idx].length - 1] == column_names[idx]
      oidlist << oid.label
      good_column_names << column_names[idx]
      row[column_names[idx]] = vb.value
    end
  end
  results << row

  catch :break_main_loop do
    while(result = get_next(oidlist))
      oidlist = []
      row = {}
      result.varbinds.each_with_index do |vb, idx|
        #puts "got #{vb.oid.label} #{vb.value.inspect}, type = #{vb.object_type}"
        row[good_column_names[idx]] = vb.value
        oidlist << vb.oid.label
        if vb.oid.label[0..good_column_names[idx].length - 1] != good_column_names[idx]
          throw :break_main_loop
        end
      end
      results << row
    end
  end
  results
end

#select(timeout = nil) ⇒ Object Also known as: poll

Check the session for SNMP responses from asynchronous SNMP requests This method will check for new responses and call the associated response callbacks. timeout A timeout of nil indicates a poll and will return immediately. A value of false will block until data is available. Otherwise, pass the number of seconds to block. Returns the number of file descriptors handled.



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/net/snmp/session.rb', line 216

def select(timeout = nil)
    fdset = FFI::MemoryPointer.new(:pointer, Net::SNMP::Inline.fd_setsize / 8)
    num_fds = FFI::MemoryPointer.new(:int)
    tv_sec = timeout ? timeout.round : 0
    tv_usec = timeout ? (timeout - timeout.round) * 1000000 : 0
    tval = Wrapper::TimeVal.new(:tv_sec => tv_sec, :tv_usec => tv_usec)
    block = FFI::MemoryPointer.new(:int)
    if timeout.nil?
      block.write_int(0)
    else
      block.write_int(1)
    end

    Wrapper.snmp_sess_select_info(@struct, num_fds, fdset, tval.pointer, block )
    tv = (timeout == false ? nil : tval)
    debug "Calling select #{Time.now}"
    num_ready = FFI::LibC.select(num_fds.read_int, fdset, nil, nil, tv)
    debug "Done select #{Time.now}"
    if num_ready > 0
      Wrapper.snmp_sess_read(@struct, fdset)
    elsif num_ready == 0
      Wrapper.snmp_sess_timeout(@struct)
    elsif num_ready == -1
      # error.  check snmp_error?
      error("select")
    else
      error("wtf is wrong with select?")
    end
    num_ready
end

#send_pdu(pdu, &callback) ⇒ Object

Send a PDU pdu The Net::SNMP::PDU object to send. Usually created by Session.get, Session.getnext, etc. callback An optional callback. It should take two parameters, status and response_pdu. If no callback is given, the call will block until the response is available and will return the response pdu. If an error occurs, a Net::SNMP::Error will be thrown. If callback is passed, the PDU will be sent and send_pdu will return immediately. You must then call Session.select to invoke the callback. This is usually done in some sort of event loop. See Net::SNMP::Dispatcher.

If you’re running inside eventmachine and have fibers (ruby 1.9, jruby, etc), sychronous calls will actually run asynchronously behind the scenes. Just run Net::SNMP::Dispatcher.fiber_loop in your reactor.

pdu = Net::SNMP::PDU.new(Constants::SNMP_MSG_GET)
pdu.add_varbind(:oid => 'sysDescr.0')
session.send_pdu(pdu) do |status, pdu|
  if status == :success
    pdu.print
  elsif status == :timeout
    puts "Timed Out"
  else
    puts "A problem occurred"
  end
end
session.select(false)  #block until data is ready.  Callback will be called.
begin
  result = session.send_pdu(pdu)
  puts result.inspect
rescue Net::SNMP::Error => e
  puts e.message
end


282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'lib/net/snmp/session.rb', line 282

def send_pdu(pdu, &callback)
  #puts "send_pdu #{Fiber.current.inspect}"
  if block_given?
    @requests[pdu.reqid] = callback
    puts "calling async_send"
    if Wrapper.snmp_sess_async_send(@struct, pdu.pointer, sess_callback, nil) == 0
      error("snmp_get async failed")
    end
    #pdu.free
    nil
  else
    if defined?(EM) && EM.reactor_running? && defined?(Fiber)
      f = Fiber.current
      send_pdu pdu do | op, response_pdu |
        f.resume([op, response_pdu])
      end
      op, result = Fiber.yield
      case op
        when :timeout
          raise TimeoutError.new, "timeout"
        when :send_failed
          error "send failed"
        when :success
          result
        when :connect, :disconnect
          nil   #does this ever happen?
        else
          error "unknown operation #{op}"
      end
    else
      response_ptr = FFI::MemoryPointer.new(:pointer)
      if [Constants::SNMP_MSG_TRAP, Constants::SNMP_MSG_TRAP2].include?(pdu.command)
        status = Wrapper.snmp_sess_send(@struct, pdu.pointer)
        if status == 0
          error("snmp_sess_send")
        end
      else
        status = Wrapper.snmp_sess_synch_response(@struct, pdu.pointer, response_ptr)
        unless status == Constants::STAT_SUCCESS
          error("snmp_sess_synch_response", :status => status)
        end
      end
      if [Constants::SNMP_MSG_TRAP, Constants::SNMP_MSG_TRAP2].include?(pdu.command)
        1
      else
        PDU.new(response_ptr.read_pointer)
      end
    end
  end
end

#set(oidlist, &block) ⇒ Object

Issue an SNMP Set Request See #send_pdu



176
177
178
179
180
181
182
# File 'lib/net/snmp/session.rb', line 176

def set(oidlist, &block)
  pdu = PDU.new(Constants::SNMP_MSG_SET)
  oidlist.each do |oid|
    pdu.add_varbind(:oid => oid[0], :type => oid[1], :value => oid[2])
  end
  send_pdu(pdu, &block)
end

#snmp_errObject

The SNMP Session error code



339
340
341
342
# File 'lib/net/snmp/session.rb', line 339

def snmp_err
  get_error
  @snmp_err
end