net-snmp2 0.3.0 → 0.3.1

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -1
  3. data/examples/agent.rb +1 -0
  4. data/examples/inform.rb +23 -0
  5. data/examples/trap_handler.rb +15 -0
  6. data/examples/{v1_trap_session.rb → v1_trap.rb} +2 -7
  7. data/examples/{v2_trap_session.rb → v2_trap.rb} +2 -7
  8. data/history.md +17 -0
  9. data/lib/net-snmp2.rb +1 -1
  10. data/lib/net/snmp/agent/agent.rb +44 -13
  11. data/lib/net/snmp/agent/provider.rb +56 -1
  12. data/lib/net/snmp/agent/provider_dsl.rb +11 -0
  13. data/lib/net/snmp/debug.rb +10 -19
  14. data/lib/net/snmp/error.rb +13 -6
  15. data/lib/net/snmp/listener.rb +2 -4
  16. data/lib/net/snmp/message.rb +52 -25
  17. data/lib/net/snmp/mib/mib.rb +2 -1
  18. data/lib/net/snmp/mib/node.rb +1 -1
  19. data/lib/net/snmp/oid.rb +5 -2
  20. data/lib/net/snmp/pdu.rb +15 -10
  21. data/lib/net/snmp/repl/manager_repl.rb +20 -7
  22. data/lib/net/snmp/session.rb +78 -45
  23. data/lib/net/snmp/trap_handler/trap_handler.rb +13 -9
  24. data/lib/net/snmp/trap_handler/v2_inform_dsl.rb +16 -0
  25. data/lib/net/snmp/trap_session.rb +64 -42
  26. data/lib/net/snmp/version.rb +1 -1
  27. data/lib/net/snmp/wrapper.rb +2 -8
  28. data/net-snmp2.gemspec +1 -1
  29. data/spec/README.md +36 -40
  30. data/spec/async_spec.rb +92 -87
  31. data/spec/em_spec.rb +3 -3
  32. data/spec/error_spec.rb +2 -2
  33. data/spec/fiber_spec.rb +8 -7
  34. data/spec/mib_spec.rb +8 -8
  35. data/spec/net-snmp_spec.rb +3 -3
  36. data/spec/oid_spec.rb +5 -5
  37. data/spec/spec_helper.rb +5 -0
  38. data/spec/sync_spec.rb +50 -56
  39. data/spec/test_agent.rb +150 -0
  40. data/spec/test_mib.rb +6 -0
  41. data/spec/thread_spec.rb +4 -4
  42. data/spec/trap_spec.rb +3 -12
  43. data/spec/utility_spec.rb +4 -4
  44. data/spec/wrapper_spec.rb +6 -6
  45. metadata +11 -6
  46. data/lib/net/snmp/agent/request_dispatcher.rb +0 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3b281e88b9dc311d024200f3c6a1490a275aeeb2
4
- data.tar.gz: 7d0fd5cf86fd4bd509458de1bc0ce77fbcc03dcf
3
+ metadata.gz: 764e833cea5160e1dcd90749dc1e34497bc19cd7
4
+ data.tar.gz: dab11ed0e33675f7d37848b71a9eea33f94584e8
5
5
  SHA512:
6
- metadata.gz: 2de1bdd8d99a79ed0849d4546c8af674fed288657c08dfff5631b41c23237ac87ac454f81d5059f4f579c4819da75659a3c324f304246b023ae9375df44cd3a1
7
- data.tar.gz: 86dfd53cd984247500282b01108d96addac69ef79229edc8ea26c1eca4d05861e733c97a81b637ef4ccfe57c95904d6bcc64751bfd0b196cf86696a69ba8c371
6
+ metadata.gz: 9af6ba3d026772a98ee3d08d988859324b53f7e552e938ec400509f59a8fd0598c2716290af3a21b1c1374d8ff81c5b64f3905d21540b17ca5b46edf79b3d67d
7
+ data.tar.gz: 232ffa5c7ffdbe94a06cae89a545bc8795dfaf9f9ba0011c81edd6c85ebdefaa6ca4ab84bbcf20236aa18c5f2375efe76f48ff3ee93ee63e0b1ecce5c3d6404e
data/README.md CHANGED
@@ -1,12 +1,15 @@
1
1
  net-snmp2
2
2
  =========
3
3
 
4
- (Checkout the [wiki](https://github.com/jbreeden/net-snmp2/wiki))
4
+ [![Gem Version](https://badge.fury.io/rb/net-snmp2.svg)](http://badge.fury.io/rb/net-snmp2)
5
+
6
+ Just getting started? Checkout the [wiki](https://github.com/jbreeden/net-snmp2/wiki)
5
7
 
6
8
  An object oriented Ruby wrapper around the C [netsnmp](http://www.net-snmp.org) libraries.
7
9
  It provides classes for manager sessions, agents, pdus, varbinds, MIB inspection, and more.
8
10
 
9
11
  The gem also includes some useful executables including:
12
+
10
13
  - **net-snmp2**
11
14
  + An interactive REPL for inspecting the MIB, and acting as an SNMP manager.
12
15
  + Supports managing multiple agents at once.
@@ -18,6 +21,7 @@ The gem also includes some useful executables including:
18
21
  Features
19
22
  --------
20
23
 
24
+ * Improved Windows support over original net-snmp gem
21
25
  * Supports SNMP versions 1, 2c, and 3
22
26
  * Supports both synchronous and asynchronous calls
23
27
  * Supports sending of snmpv1 traps and snmpv2 traps/informs using TrapSession
@@ -26,3 +30,16 @@ Features
26
30
  * MIB support
27
31
  * Convenience methods such as session.walk, session.get_columns, and session.table
28
32
  * SNMP Agent support
33
+ * Trap handler support
34
+
35
+ Notes
36
+ -----
37
+
38
+ - TrapHandler & Agent support doesn't work on default Windows build of net-snmp libraries, due to certain functions not being exported in the dll.
39
+ + There is a [net-snmp-clone](https://github.com/jbreeden/net-snmp-clone) repo with the fixes made if you'd like to build this from source.
40
+ + Or, you can try my [pre-built DLL](https://github.com/jbreeden/net-snmp-clone/raw/master/win32/bin/release/netsnmp.dll) from the same repo.
41
+ + I've submitted this as a [feature request](https://sourceforge.net/p/net-snmp/feature-requests/181/) & patch to the net-snmp team. If it works for you, please go make a comment indicating that on the feature request to help it get pulled into the core library.
42
+ - Original gem was using a different comment format... I've opted for markdown. The generated rdocs are probably garbage.
43
+ + The [wiki](https://github.com/jbreeden/net-snmp2/wiki) has the best documentation for now.
44
+ + All the specs are passing, so that's another good place to go for information.
45
+ + There is also the built-in net-snmp2 program you can run. This is an interactive Pry shell that is very useful for digging around the code and getting a feel for how to do things.
@@ -1,4 +1,5 @@
1
1
  $: << '../lib'
2
+
2
3
  require 'net-snmp2'
3
4
 
4
5
  # Initialize SNMP and give it a logger
@@ -0,0 +1,23 @@
1
+ $: << '../lib'
2
+ require 'net-snmp2'
3
+
4
+ # Initialize SNMP and give it a logger
5
+ Net::SNMP.init
6
+ Net::SNMP::Debug.logger = Logger.new(STDOUT)
7
+ Net::SNMP::Debug.logger.level = Logger::INFO
8
+
9
+ session = Net::SNMP::TrapSession.open(:peername => 'localhost', :version => '2c', :community => 'public')
10
+
11
+ extend Net::SNMP::Debug
12
+
13
+ 100000.times do |i|
14
+ time "Inform ##{i}" do
15
+ puts "Inform #{i}: " + session.inform(
16
+ oid: '1.3.1.1',
17
+ uptime: 1000,
18
+ varbinds: [
19
+ {oid: '1.3.2.2', value: 'test'}
20
+ ]
21
+ ).to_s
22
+ end
23
+ end
@@ -35,6 +35,21 @@ handler = Net::SNMP::TrapHandler.new do
35
35
  Varbinds: #{varbinds.map {|vb| "#{vb.oid.label}(#{vb.oid}) = #{vb.value}"}.join(', ')}
36
36
  EOF
37
37
  end
38
+
39
+ inform do
40
+ info <<-EOF
41
+
42
+
43
+ Got Inform
44
+ ----------
45
+
46
+ Trap OID: #{trap_oid}
47
+ Uptime: #{uptime}
48
+ Varbinds: #{varbinds.map {|vb| "#{vb.oid.label}(#{vb.oid}) = #{vb.value}"}.join(', ')}
49
+ EOF
50
+
51
+ ok
52
+ end
38
53
  end
39
54
 
40
55
  # If the program gets the interrupt signal, tell the trap handler
@@ -6,19 +6,14 @@ Net::SNMP.init
6
6
  Net::SNMP::Debug.logger = Logger.new(STDOUT)
7
7
  Net::SNMP::Debug.logger.level = Logger::DEBUG
8
8
 
9
- puts "Opening session"
10
9
  session = Net::SNMP::TrapSession.open(:peername => 'localhost', :version => '1', :community => 'public')
11
10
 
12
- puts "Sending trap"
13
-
14
11
  100000.times do |i|
15
- session.trap(
12
+ puts "#{i + 1}: " + session.trap(
16
13
  enterprise: '1.3.1',
17
14
  trap_type: 6,
18
15
  specific_type: 1,
19
16
  uptime: 1000,
20
17
  agent_addr: '127.0.0.1'
21
- )
18
+ ).to_s
22
19
  end
23
-
24
- sleep 20
@@ -6,16 +6,11 @@ Net::SNMP.init
6
6
  Net::SNMP::Debug.logger = Logger.new(STDOUT)
7
7
  Net::SNMP::Debug.logger.level = Logger::DEBUG
8
8
 
9
- puts "Opening session"
10
9
  session = Net::SNMP::TrapSession.open(:peername => 'localhost', :version => '2c', :community => 'public')
11
10
 
12
- puts "Sending trap"
13
-
14
11
  100000.times do |i|
15
- puts session.trap_v2(
12
+ puts "#{i + 1}: " + session.trap_v2(
16
13
  oid: '1.3.1.1',
17
14
  uptime: 1000
18
- )
15
+ ).to_s
19
16
  end
20
-
21
- sleep 20
@@ -0,0 +1,17 @@
1
+ History
2
+ =======
3
+
4
+ Version 0.3.1
5
+ -------------
6
+
7
+ - Internal changes
8
+ + Adding source_address & source_port to message object
9
+ * Changes the callback signature for Listener#on_message
10
+ + Adding Message#respond method
11
+ * Used for creating a one-off session for responding to a message
12
+ and sending a single pdu over it. (Used in TrapHandler & Agent)
13
+
14
+ Version 0.3.0
15
+ -------------
16
+
17
+ - First gem release as net-snmp2
@@ -21,6 +21,7 @@ require 'logger'
21
21
  snmp/trap_handler/trap_handler
22
22
  snmp/trap_handler/v1_trap_dsl
23
23
  snmp/trap_handler/v2_trap_dsl
24
+ snmp/trap_handler/v2_inform_dsl
24
25
  snmp/mib/mib
25
26
  snmp/mib/node
26
27
  snmp/mib/module
@@ -29,7 +30,6 @@ require 'logger'
29
30
  snmp/agent/agent
30
31
  snmp/agent/provider
31
32
  snmp/agent/provider_dsl
32
- snmp/agent/request_dispatcher
33
33
  snmp/message
34
34
  ).each do |f|
35
35
  require "#{File.dirname(__FILE__)}/net/#{f}"
@@ -1,10 +1,8 @@
1
1
  module Net::SNMP
2
2
 
3
- # - Manages the request/response cycle for incoming messages
4
- # + Listens for incoming requests
5
- # + Parses request packets into Message objects
6
- # + Dispatches the messages to (sub) Agents
7
- # + Serializes the response from the subagents and sends it to the caller
3
+ # Agents delegate messages from the Net::SNMP::Listener to
4
+ # providers, which supply the actual responses to the varbinds
5
+ # in the request. See Net::SNMP::ProviderDsl
8
6
 
9
7
  class Agent
10
8
  include Debug
@@ -19,10 +17,16 @@ module Net::SNMP
19
17
  @listener.on_message(&method(:process_message))
20
18
  end
21
19
 
20
+ # This method is called with a block to define a provider
21
+ # for some subtree of the MIB. When a request comes in with varbinds
22
+ # in that providers subtree, the provider's handlers will be called
23
+ # to generate the varbind to send back in the response PDU.
24
+ #
25
+ # Arguments
26
+ #
27
+ # - oid: The root OID of the MIB subtree this provider is responsible for.
28
+ # - &block: A block, to be instance_evaled on the new Provider.
22
29
  def provide(oid = :all, &block)
23
- # Need a trailing dot on the oid so we can avoid
24
- # considering 1.3.22 a child of 1.3.2
25
- oid = (oid.to_sym == :all || oid.end_with?('.')) ? oid : "#{oid}."
26
30
  provider = Provider.new(oid)
27
31
  provider.instance_eval(&block)
28
32
 
@@ -35,13 +39,40 @@ module Net::SNMP
35
39
 
36
40
  private
37
41
 
38
- def process_message(message, from_address, from_port)
39
- response_pdu = RequestDispatcher.dispatch(message, providers)
40
- if response_pdu
41
- Session.open(peername: from_address, port: from_port, version: message.version_name) do |sess|
42
- sess.send_pdu response_pdu
42
+ # The callback given to the Listener object for handling an SNMP message.
43
+ # Calls `dispatch`, then sends the response PDU, if one is returned.
44
+ def process_message(message)
45
+ # TODO: May want to ignore some messages (say, if the community string is wrong)
46
+ message.respond(dispatch(message))
47
+ end
48
+
49
+ # Collects responses for the given message from the available providers
50
+ def dispatch(message)
51
+ response_pdu = message.make_response_pdu
52
+ context = ProviderDsl.new
53
+ context.message = message
54
+ context.response_pdu = response_pdu
55
+ message.pdu.varbinds.each_with_index do |vb, index|
56
+ context.varbind = vb
57
+ provider = providers.find { |p| p.provides?(vb.oid) }
58
+ if provider
59
+ if message.pdu.command == Constants::SNMP_MSG_GETBULK && index < message.pdu.non_repeaters
60
+ handler = provider.handler_for(Constants::SNMP_MSG_GETNEXT)
61
+ else
62
+ handler = provider.handler_for(message)
63
+ end
64
+ if handler
65
+ context.instance_exec(&handler)
66
+ else
67
+ warn "No handler for command: #{message.pdu.command} @ #{vb.oid}"
68
+ context.no_such_object
69
+ end
70
+ else
71
+ warn "No provider for oid: #{vb.oid}"
72
+ context.no_such_object
43
73
  end
44
74
  end
75
+ response_pdu
45
76
  end
46
77
 
47
78
  end
@@ -5,6 +5,39 @@ module Net::SNMP
5
5
  # of the subtree provided, and handlers for the various request types.
6
6
  # The handlers are executed for each varbind of the incoming message
7
7
  # individually in the context of a ProviderDsl object.
8
+ #
9
+ # Clients do not create Providers directly. Instead, they call `provide` on
10
+ # an Agent, passing in a block. Within the block they use the `get`, `get_next`,
11
+ # `get_bulk`, and `set` methods to configure handlers for these request types.
12
+ # Within these handlers, the DSL methods from the ProviderDsl class can be
13
+ # used to inspect the current request.
14
+ #
15
+ # Example
16
+ #
17
+ # require 'net-snmp2'
18
+ # agent = Net::SNMP::Agent.new
19
+ # agent.provide :all do
20
+ #
21
+ # get do
22
+ # reply get_value_somehow(oid)
23
+ # end
24
+ #
25
+ # set do
26
+ # reply set_value_somehow(oid)
27
+ # end
28
+ #
29
+ # get_next do
30
+ # reply get_next_value_somehow(oid)
31
+ # end
32
+ #
33
+ # get_bulk do
34
+ # (0..max_repetitions).each do |i|
35
+ # add get_bulk_vlue_somehow(oid, i)
36
+ # end
37
+ # end
38
+ #
39
+ # end
40
+ # agent.listen(161)
8
41
 
9
42
  class Provider
10
43
  attr_accessor :oid,
@@ -13,10 +46,26 @@ module Net::SNMP
13
46
  :get_next_handler,
14
47
  :get_bulk_handler
15
48
 
49
+ # Creates a new Provider with `oid` as the root of its subtree
16
50
  def initialize(oid)
17
- @oid = oid
51
+ if oid.kind_of?(Symbol)
52
+ unless oid == :all
53
+ raise "Cannot provide symbol '#{oid}'. (Did you mean to use :all?)"
54
+ end
55
+ @oid = oid
56
+ else
57
+ # Guarantee OID is in numeric form
58
+ @oid = OID.new(oid).to_s
59
+ end
18
60
  end
19
61
 
62
+ # Gets the handler for the given command type from this provider.
63
+ #
64
+ # Arguments
65
+ #
66
+ # - command
67
+ # + As an integer, specifies the command type a handler is needed for.
68
+ # + May also be a Message or PDU object, from which the command type is read.
20
69
  def handler_for(command)
21
70
  # User might be tempted to just pass in the message, or pdu,
22
71
  # if so, just pluck the command off of it.
@@ -40,6 +89,12 @@ module Net::SNMP
40
89
  end
41
90
  end
42
91
 
92
+ # Returns a boolean indicating whether this provider provides
93
+ # the given `oid`
94
+ def provides?(oid)
95
+ self.oid == :all || oid.to_s =~ %r[#{self.oid.to_s}(\.|$)]
96
+ end
97
+
43
98
  [:get, :set, :get_next, :get_bulk].each do |request_type|
44
99
  self.class_eval %Q[
45
100
  def #{request_type}(&proc)
@@ -38,6 +38,11 @@ module Net::SNMP
38
38
  varbind.oid
39
39
  end
40
40
 
41
+ # The MIB variable name of the current varbind
42
+ def variable
43
+ oid.label
44
+ end
45
+
41
46
  # The OID of the current varbind being processed as a string.
42
47
  def oid_str
43
48
  varbind.oid.to_s
@@ -101,6 +106,12 @@ module Net::SNMP
101
106
  add_varbind(oid: oid, type: Constants::SNMP_NOSUCHINSTANCE)
102
107
  end
103
108
 
109
+ # Adds a varbind to the response indicating that the END OF MIB has been reached
110
+ def end_of_mib
111
+ oid ||= varbind.oid
112
+ add_varbind(oid: oid, type: Constants::SNMP_ENDOFMIBVIEW)
113
+ end
114
+
104
115
  # Adds a varbind to the response PDU.
105
116
  # MUST use this method (or one that delegates to it)
106
117
  # to set response varbinds on the response_pdu to make sure
@@ -5,31 +5,22 @@ module Debug
5
5
  attr_accessor :logger
6
6
  end
7
7
 
8
- def debug(msg)
9
- Debug.logger.debug msg if Debug.logger
10
- end
11
-
12
- def info(msg)
13
- Debug.logger.info msg if Debug.logger
14
- end
15
-
16
- def warn(msg)
17
- Debug.logger.warn msg if Debug.logger
18
- end
19
-
20
- def error(msg)
21
- Debug.logger.error msg if Debug.logger
22
- end
23
-
24
- def fatal(msg)
25
- Debug.logger.fatal msg if Debug.logger
8
+ [:debug, :info, :warn, :error, :fatal].each do |log_level|
9
+ self.module_eval %Q{
10
+ def #{log_level}(msg = nil, &block)
11
+ if Debug.logger && (Debug.logger.level <= Logger::#{log_level.upcase})
12
+ Debug.logger.send(:#{log_level}, msg)
13
+ block[] if block_given?
14
+ end
15
+ end
16
+ }
26
17
  end
27
18
 
28
19
  def time(label, &block)
29
20
  t_start = Time.now
30
21
  block[]
31
22
  t_end = Time.now
32
- info "#{label}: #{(t_end - t_start)*1000}ms"
23
+ debug "#{label}: #{(t_end - t_start)*1000}ms"
33
24
  end
34
25
 
35
26
  def print_packet(packet)
@@ -1,6 +1,8 @@
1
1
  module Net
2
2
  module SNMP
3
3
  class Error < RuntimeError
4
+ include Debug
5
+
4
6
  attr_accessor :status, :errno, :snmp_err, :snmp_msg
5
7
  def initialize(opts = {})
6
8
  @status = opts[:status]
@@ -14,12 +16,17 @@ module Net
14
16
  end
15
17
 
16
18
  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}"
19
+ message = <<-EOF
20
+
21
+ SNMP Error: #{self.class.to_s}
22
+ message = #{message}
23
+ status = #{@status}
24
+ errno = #{@errno}
25
+ snmp_err = #{@snmp_err}
26
+ snmp_msg = #{@snmp_msg}
27
+ EOF
28
+
29
+ error(message.gsub /^\s*/, '')
23
30
  end
24
31
  end
25
32
 
@@ -37,13 +37,11 @@ module Net::SNMP
37
37
  #
38
38
  # The block provided will be called back for each message as follows:
39
39
  #
40
- # block[message, from_address, from_port]
40
+ # block[message]
41
41
  #
42
42
  # Where
43
43
  #
44
44
  # - `message` is the parsed Net::SNMP::Message object
45
- # - `from_address` is a string representing the address of the host sending the request
46
- # - `from_port` is the port the host sent the request from
47
45
  def on_message(&block)
48
46
  @callback = block
49
47
  end
@@ -62,7 +60,7 @@ module Net::SNMP
62
60
  return if @killed
63
61
  time "Message Processing" do
64
62
  message = Message.parse(@packet)
65
- @callback[message, @packet[1][3], @packet[1][1]] if @callback
63
+ @callback[message] if @callback
66
64
  end
67
65
  rescue Timeout::Error => timeout
68
66
  next