normelton-snmp4em 0.1.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.
@@ -0,0 +1,12 @@
1
+ lib/common.rb
2
+ lib/handler.rb
3
+ lib/requests/snmp_get_request.rb
4
+ lib/requests/snmp_getnext_request.rb
5
+ lib/requests/snmp_set_request.rb
6
+ lib/requests/snmp_walk_request.rb
7
+ lib/snmp4em.rb
8
+ lib/snmp_request.rb
9
+ Rakefile
10
+ README
11
+ snmp4em.gemspec
12
+ Manifest
data/README ADDED
@@ -0,0 +1,133 @@
1
+ = SNMP Library for EventMachine
2
+
3
+ == Summary
4
+
5
+ This library extends the Ruby-SNMP[http://snmplib.rubyforge.org] to use the asynchronous EventMachine[http://rubyeventmachine.com] library for added performance and scalability. This allows code to scale monitoring applications to access a very high number of devices without the need for complex asynchronous I/O handling.
6
+
7
+
8
+ == Features
9
+
10
+ The initial version 0.1.0 of this software supports:
11
+
12
+ * SNMP v1 only
13
+ * SNMP Get, GetNext, Set, and Walk requests.
14
+ * Ability to query/set/walk multiple OIDs in parallel.
15
+
16
+ Future revisions of this library may support:
17
+
18
+ * SNMP v2c operations (this should be easy)
19
+ * Ability to act as an SNMP agent, responding to external queries.
20
+ * Ability to send/receive SNMP traps
21
+
22
+ There are no plans to support SNMP v3.
23
+
24
+
25
+ == Acknowledgements
26
+
27
+ * The SNMP packet processing is handled by the Ruby-SNMP[http://snmplib.rubyforge.org] library, by David Halliday
28
+ * EventMachine[http://rubyeventmachine.com], by Francis Cianfrocca and Aman Gupta
29
+ * All the helpful folks on the Freenode #eventmachine channel
30
+
31
+
32
+ == Examples
33
+
34
+ A few definitions:
35
+
36
+ OID_SYSTEM = "1.3.6.1.2.1.1"
37
+ OID_SYSNAME = "1.3.6.1.2.1.1.5.0"
38
+ OID_SYSLOCATION = "1.3.6.1.2.1.1.6.0"
39
+
40
+ A simple SNMP-GET:
41
+
42
+ EM::run do
43
+ snmp = SNMP4EM::SNMPv1.new(:host => "192.168.1.1")
44
+
45
+ request = snmp.get([OID_SYSNAME, OID_SYSLOCATION])
46
+
47
+ request.callback do |response|
48
+ puts "System name = #{response[OID_SYSNAME]}"
49
+ puts "System location = #{response[OID_SYSLOCATION]}"
50
+ end
51
+
52
+ request.errback do |error|
53
+ puts "GET got error #{error}"
54
+ end
55
+ end
56
+
57
+ A simple SNMP-GETNEXT:
58
+
59
+ EM::run do
60
+ snmp = SNMP4EM::SNMPv1.new(:host => "192.168.1.1")
61
+
62
+ request = snmp.getnext([OID_SYSNAME])
63
+
64
+ request.callback do |response|
65
+ r = response[OID_SYSNAME]
66
+ puts "The next OID is #{r[0]}, the next value is #{r[1]}"
67
+ end
68
+
69
+ request.errback do |error|
70
+ puts "GET got error #{error}"
71
+ end
72
+ end
73
+
74
+ A simple SNMP-SET:
75
+
76
+ EM::run do
77
+ snmp = SNMP4EM::SNMPv1.new(:host => "192.168.1.1")
78
+
79
+ request = snmp.set({OID_SYSNAME => "My System Name", OID_SYSLOCATION => "My System Location"})
80
+
81
+ request.callback do |response|
82
+ if (response[OID_SYSNAME] == true)
83
+ puts "System name set successful"
84
+ else
85
+ puts "System name set unsuccessful: #{response[OID_SYSNAME]}"
86
+ end
87
+
88
+ if (response[OID_SYSLOCATION] == true)
89
+ puts "System location set successful"
90
+ else
91
+ puts "System location set unsuccessful: #{response[OID_SYSLOCATION]}"
92
+ end
93
+ end
94
+
95
+ request.errback do |error|
96
+ puts "GET got error #{error}"
97
+ end
98
+ end
99
+
100
+ A simple SNMP-WALK:
101
+
102
+ EM::run do
103
+ snmp = SNMP4EM::SNMPv1.new(:host => "192.168.1.1")
104
+
105
+ request = snmp.walk([OID_SYSTEM])
106
+
107
+ request.callback do |response|
108
+ if (response[OID_SYSTEM].is_a? Array)
109
+ response[OID_SYSTEM].each do |vb|
110
+ puts "#{vb[0]} = #{vb[1]}"
111
+ end
112
+ else
113
+ puts "Got error: #{response[OID_SYSTEM]}"
114
+ end
115
+ end
116
+
117
+ request.errback do |error|
118
+ puts "GET got error #{error}"
119
+ end
120
+ end
121
+
122
+
123
+ == Change Log
124
+
125
+ Version 0.1.0:
126
+
127
+ * Initial deployment, ability to run get/getnext/set/walk requests in parallel
128
+
129
+
130
+ == Developer Contact Information
131
+
132
+ Norman Elton
133
+ normelton@gmail.com
@@ -0,0 +1,12 @@
1
+ require "rubygems"
2
+ require "rake"
3
+ require "echoe"
4
+
5
+ Echoe.new("snmp4em", "0.1.0") do |p|
6
+ p.description = "A high-performance SNMP engine built on EventMachine and Ruby-SNMP"
7
+ p.url = "http://github.com/normelton/snmp4em"
8
+ p.author = "Norman Elton"
9
+ p.email = "normelton@gmail.com"
10
+ p.development_dependencies = []
11
+ p.rdoc_pattern = ["lib", "README"]
12
+ end
@@ -0,0 +1,39 @@
1
+ module SNMP #:nodoc:
2
+ class Null #:nodoc:
3
+ class << self
4
+ def rubify
5
+ nil
6
+ end
7
+ end
8
+ end
9
+
10
+ class OctetString #:nodoc:
11
+ alias :rubify :to_s
12
+ end
13
+
14
+ class Integer #:nodoc:
15
+ alias :rubify :to_i
16
+ end
17
+
18
+ class ObjectId #:nodoc:
19
+ alias :rubify :to_s
20
+ end
21
+
22
+ class IpAddress #:nodoc:
23
+ alias :rubify :to_s
24
+ end
25
+
26
+ class ResponseError
27
+ attr_reader :error_status
28
+ alias :rubify :error_status #:nodoc:
29
+
30
+ def initialize(error_status) #:nodoc:
31
+ @error_status = error_status
32
+ end
33
+
34
+ # String representation of this error
35
+ def to_s
36
+ @error_status.to_s
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,18 @@
1
+ module SNMP4EM
2
+ module Handler #:nodoc:
3
+ def receive_data(data)
4
+ begin
5
+ message = SNMP::Message.decode(data)
6
+ rescue Exception => err
7
+ return
8
+ end
9
+
10
+ response = message.pdu
11
+ request_id = response.request_id
12
+
13
+ if (request = SNMPv1.pending_requests.select{|r| r.snmp_id == request_id}.first)
14
+ request.handle_response(response)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,77 @@
1
+ require "snmp_request.rb"
2
+
3
+ module SNMP4EM
4
+
5
+ # Returned from SNMP4EM::SNMPv1.get(). This implements EM::Deferrable, so you can hang a callback()
6
+ # or errback() to retrieve the results.
7
+
8
+ class SnmpGetRequest < SnmpRequest
9
+ attr_reader :snmp_id
10
+
11
+ # For an SNMP-GET request, @pending_oids will be a ruby array of SNMP::ObjectNames that need to be fetched. As
12
+ # responses come back from the agent, this array will be pruned of any error-producing OIDs. Once no errors
13
+ # are returned, the @responses hash will be populated and returned.
14
+
15
+ def initialize(sender, oids, args = {}) #:nodoc:
16
+ @sender = sender
17
+
18
+ @timeout_timer = nil
19
+ @timeout_retries = @sender.retries
20
+ @error_retries = oids.size
21
+
22
+ @return_raw = args[:return_raw] || false
23
+
24
+ @responses = Hash.new
25
+ @pending_oids = SNMP::VarBindList.new(oids).collect{|r| r.name}
26
+
27
+ init_callbacks
28
+ send
29
+ end
30
+
31
+ def handle_response(response) #:nodoc:
32
+ if (response.error_status == :noError)
33
+ # No errors, populate the @responses object so it can be returned
34
+ response.each_varbind do |vb|
35
+ request_oid = @pending_oids.shift
36
+ @responses[request_oid.to_s] = vb.value
37
+ end
38
+
39
+ else
40
+ # Got an error, remove that oid from @pending_oids so we can try again
41
+ error_oid = @pending_oids.delete_at(response.error_index - 1)
42
+ @responses[error_oid.to_s] = SNMP::ResponseError.new(response.error_status)
43
+ end
44
+
45
+ if (@pending_oids.empty? || @error_retries.zero?)
46
+ until @pending_oids.empty?
47
+ error_oid = @pending_oids.shift
48
+ @responses[error_oid.to_s] = SNMP::ResponseError.new(:genErr)
49
+ end
50
+
51
+ @responses.each_pair do |oid, value|
52
+ @responses[oid] = value.rubify if (!@return_raw && value.respond_to?(:rubify))
53
+ end
54
+
55
+ # Send the @responses back to the requester, we're done!
56
+ succeed @responses
57
+ else
58
+ @error_retries -= 1
59
+ send
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def send
66
+ # Send the contents of @pending_oids
67
+
68
+ @snmp_id = generate_snmp_id
69
+
70
+ vb_list = SNMP::VarBindList.new(@pending_oids)
71
+ request = SNMP::GetRequest.new(@snmp_id, vb_list)
72
+ message = SNMP::Message.new(:SNMPv1, @sender.community_ro, request)
73
+
74
+ super(message)
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,82 @@
1
+ require "snmp_request.rb"
2
+
3
+ module SNMP4EM
4
+
5
+ # Returned from SNMP4EM::SNMPv1.getnext(). This implements EM::Deferrable, so you can hang a callback()
6
+ # or errback() to retrieve the results.
7
+
8
+ class SnmpGetNextRequest < SnmpRequest
9
+ attr_reader :snmp_id
10
+
11
+ # For an SNMP-GETNEXT request, @pending_oids will be a ruby array of SNMP::ObjectNames that need to be fetched. As
12
+ # responses come back from the agent, this array will be pruned of any error-producing OIDs. Once no errors
13
+ # are returned, the @responses hash will be populated and returned. The values of the hash will consist of a
14
+ # two-element array, in the form of [OID, VALUE], showing the next oid & value.
15
+
16
+ def initialize(sender, oids, args = {}) #:nodoc:
17
+ @sender = sender
18
+
19
+ @timeout_timer = nil
20
+ @timeout_retries = @sender.retries
21
+ @error_retries = oids.size
22
+
23
+ @return_raw = args[:return_raw] || false
24
+
25
+ @responses = Hash.new
26
+ @pending_oids = SNMP::VarBindList.new(oids).collect{|r| r.name}
27
+
28
+ init_callbacks
29
+ send
30
+ end
31
+
32
+ def handle_response(response) #:nodoc:
33
+ if (response.error_status == :noError)
34
+ # No errors, populate the @responses object so it can be returned
35
+ response.each_varbind do |vb|
36
+ request_oid = @pending_oids.shift
37
+ @responses[request_oid.to_s] = [vb.name, vb.value]
38
+ end
39
+
40
+ else
41
+ # Got an error, remove that oid from @pending_oids so we can try again
42
+ error_oid = @pending_oids.delete_at(response.error_index - 1)
43
+ @responses[error_oid.to_s] = SNMP::ResponseError.new(response.error_status)
44
+ end
45
+
46
+ if (@pending_oids.empty? || @error_retries.zero?)
47
+ until @pending_oids.empty?
48
+ error_oid = @pending_oids.shift
49
+ @responses[error_oid.to_s] = SNMP::ResponseError.new(:genErr)
50
+ end
51
+
52
+ @responses.each_pair do |oid, value|
53
+ if value.is_a? Array
54
+ @responses[oid][1] = value[1].rubify if (!@return_raw && value[1].respond_to?(:rubify))
55
+ else
56
+ @responses[oid] = value.rubify if (!@return_raw && value.respond_to?(:rubify))
57
+ end
58
+ end
59
+
60
+ # Send the @responses back to the requester, we're done!
61
+ succeed @responses
62
+ else
63
+ @error_retries -= 1
64
+ send
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def send #:nodoc:
71
+ # Send the contents of @pending_oids
72
+
73
+ @snmp_id = generate_snmp_id
74
+
75
+ vb_list = SNMP::VarBindList.new(@pending_oids)
76
+ request = SNMP::GetNextRequest.new(@snmp_id, vb_list)
77
+ message = SNMP::Message.new(:SNMPv1, @sender.community_ro, request)
78
+
79
+ super(message)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,92 @@
1
+ require "snmp_request.rb"
2
+
3
+ module SNMP4EM
4
+
5
+ # Returned from SNMP4EM::SNMPv1.set(). This implements EM::Deferrable, so you can hang a callback()
6
+ # or errback() to retrieve the results.
7
+
8
+ class SnmpSetRequest < SnmpRequest
9
+ attr_reader :snmp_id
10
+
11
+ # For an SNMP-SET request, @pending_varbinds will by an SNMP::VarBindList, initially populated from the
12
+ # provided oids hash. Variables can be passed as specific types from the SNMP library (i.e. SNMP::IpAddress)
13
+ # or as ruby native objects, in which case they will be cast into the appropriate SNMP object. As responses
14
+ # are returned, the @pending_varbinds object will be pruned of any error-producing varbinds. Once no errors
15
+ # are produced, the @responses object is populated and returned.
16
+
17
+ def initialize(sender, oids, args = {}) #:nodoc:
18
+ @sender = sender
19
+
20
+ @timeout_timer = nil
21
+ @timeout_retries = @sender.retries
22
+ @error_retries = oids.size
23
+
24
+ @return_raw = args[:return_raw] || false
25
+
26
+ @responses = Hash.new
27
+ @pending_varbinds = SNMP::VarBindList.new()
28
+
29
+ oids.each_pair do |oid,value|
30
+ if value.is_a? Integer
31
+ snmp_value = SNMP::Integer.new(value)
32
+ elsif value.is_a? String
33
+ snmp_value = SNMP::OctetString.new(value)
34
+ end
35
+
36
+ @pending_varbinds << SNMP::VarBind.new(oid,snmp_value)
37
+ end
38
+
39
+ init_callbacks
40
+ send
41
+ end
42
+
43
+ def handle_response(response) #:nodoc:
44
+ if (response.error_status == :noError)
45
+ # No errors, set any remaining varbinds to true
46
+ response.each_varbind do |vb|
47
+ response_vb = @pending_varbinds.shift
48
+ @responses[response_vb.name.to_s] = true
49
+ end
50
+
51
+ else
52
+ # Got an error, remove that varbind from @pending_varbinds so we can try again
53
+ error_vb = @pending_varbinds.delete_at(response.error_index - 1)
54
+ @responses[error_vb.name.to_s] = SNMP::ResponseError.new(response.error_status)
55
+ end
56
+
57
+ if (@pending_varbinds.empty? || @error_retries.zero?)
58
+ until @pending_varbinds.empty?
59
+ error_vb = @pending_varbinds.shift
60
+ @responses[error_vb.name.to_s] = SNMP::ResponseError.new(:genErr)
61
+ end
62
+
63
+ unless @return_raw
64
+ @responses.each_pair do |oid, value|
65
+ @responses[oid] = value.rubify if value.respond_to?(:rubify)
66
+ end
67
+ end
68
+
69
+ # Send the @responses back to the requester, we're done!
70
+ succeed @responses
71
+ else
72
+ @error_retries -= 1
73
+
74
+ send
75
+ end
76
+ end
77
+
78
+ private
79
+
80
+ def send
81
+ # Send the contents of @pending_varbinds
82
+
83
+ @snmp_id = generate_snmp_id
84
+
85
+ vb_list = SNMP::VarBindList.new(@pending_varbinds)
86
+ request = SNMP::SetRequest.new(@snmp_id, vb_list)
87
+ message = SNMP::Message.new(:SNMPv1, @sender.community_rw, request)
88
+
89
+ super(message)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,106 @@
1
+ require "snmp_request.rb"
2
+
3
+ module SNMP4EM
4
+
5
+ # Returned from SNMP4EM::SNMPv1.walk(). This implements EM::Deferrable, so you can hang a callback()
6
+ # or errback() to retrieve the results.
7
+
8
+ class SnmpWalkRequest < SnmpRequest
9
+ attr_reader :snmp_id
10
+
11
+ # For an SNMP-WALK request, @pending_oids will be a ruby array of SNMP::ObjectNames that need to be walked.
12
+ # Note that this library supports walking multiple OIDs in parallel. Once any error-producing OIDs are removed,
13
+ # a series of SNMP-GETNEXT requests are sent. Each response OID is checked to see if it begins with the walk OID.
14
+ # If so, the incoming OID/value pair is appended to the @response hash, and will be used in subsequent GETNEXT
15
+ # requests. Once an OID is returned that does not begin with the walk OID, that walk OID is removed from the
16
+ # @pending_oids array.
17
+
18
+ def initialize(sender, oids, args = {}) #:nodoc:
19
+ @sender = sender
20
+
21
+ @timeout_timer = nil
22
+ @timeout_retries = @sender.retries
23
+ @error_retries = oids.size
24
+
25
+ @return_raw = args[:return_raw] || false
26
+ @max_results = args[:max_results] || nil
27
+
28
+ @responses = Hash.new
29
+ @pending_oids = SNMP::VarBindList.new(oids).collect{|r| r.name}
30
+
31
+ init_callbacks
32
+ send
33
+ end
34
+
35
+ def handle_response(response) #:nodoc:
36
+ oids_to_delete = []
37
+
38
+ if (response.error_status == :noError)
39
+ response.varbind_list.each_index do |i|
40
+ walk_oid = @pending_oids[i]
41
+ response_vb = response.varbind_list[i]
42
+
43
+ # Initialize the responses array if necessary
44
+ @responses[walk_oid.to_s] ||= Array.new
45
+
46
+ # If the incoming response-oid begins with the walk-oid, then append the pairing
47
+ # to the @response array. Otherwise, add it to the list of oids ready to delete
48
+ if (response_vb.name[0,walk_oid.length] == walk_oid)
49
+ @responses[walk_oid.to_s] << [response_vb.name, response_vb.value]
50
+ else
51
+ # If we were to delete thid oid from @pending_oids now, it would mess up the
52
+ # @pending_oids[i] call above.
53
+ oids_to_delete << walk_oid
54
+ end
55
+ end
56
+
57
+ @max_results -= 1 unless @max_results.nil?
58
+
59
+ else
60
+ error_oid = @pending_oids[response.error_index - 1]
61
+ oids_to_delete << error_oid
62
+
63
+ @responses[error_oid.to_s] = SNMP::ResponseError.new(response.error_status)
64
+ @error_retries -= 1
65
+ end
66
+
67
+ oids_to_delete.each{|oid| @pending_oids.delete oid}
68
+
69
+ if (@pending_oids.empty? || (@error_retries < 0) || (@max_results.to_i < 0))
70
+ @responses.each_pair do |oid, value|
71
+ @responses[oid] = value.rubify if (!@return_raw && value.respond_to?(:rubify))
72
+ end
73
+
74
+ # Send the @responses back to the requester, we're done!
75
+ succeed @responses
76
+ else
77
+ send
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def send
84
+ @snmp_id = generate_snmp_id
85
+
86
+ # This oids array will consist of all the oids that need to be getnext'd
87
+ oids = Array.new
88
+
89
+ @pending_oids.each do |oid|
90
+ # If there's already a response for this walk-oid, then use the last returned oid, otherwise
91
+ # start with the walk-oid.
92
+ if @responses.has_key?(oid.to_s)
93
+ oids << @responses[oid.to_s].last.first
94
+ else
95
+ oids << oid
96
+ end
97
+ end
98
+
99
+ vb_list = SNMP::VarBindList.new(oids)
100
+ request = SNMP::GetNextRequest.new(@snmp_id, vb_list)
101
+ message = SNMP::Message.new(:SNMPv1, @sender.community_ro, request)
102
+
103
+ super(message)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,113 @@
1
+ require "snmp"
2
+ require "common.rb"
3
+ require "handler.rb"
4
+ require "snmp_request.rb"
5
+ require "requests/snmp_get_request.rb"
6
+ require "requests/snmp_getnext_request.rb"
7
+ require "requests/snmp_walk_request.rb"
8
+ require "requests/snmp_set_request.rb"
9
+
10
+ require "pp"
11
+
12
+ # The SNMP4EM library
13
+
14
+ module SNMP4EM
15
+ class SNMPv1
16
+ @pending_requests = []
17
+ @socket = nil
18
+
19
+ class << self
20
+ attr_reader :pending_requests
21
+ attr_reader :socket
22
+
23
+ def init_socket #:nodoc:
24
+ @socket = EM::open_datagram_socket("0.0.0.0", 0, Handler)
25
+ end
26
+ end
27
+
28
+ attr_reader :host, :port, :community_ro, :community_rw, :timeout, :retries
29
+
30
+ # Creates a new object to communicate with SNMPv1 agents. Optionally pass in the following parameters:
31
+ # * _host_ - IP/hostname of remote agent (default: 127.0.0.1)
32
+ # * _port_ - UDP port on remote agent (default: 161)
33
+ # * _community_ - Community string to use (default: public)
34
+ # * _community_ro_ - Read-only community string to use for get/getnext/walk operations (default: public)
35
+ # * _community_rw_ - Read-write community string to use for set operations (default: public)
36
+ # * _timeout_ - Number of seconds to wait before a request times out (default: 1)
37
+ # * _retries_ - Number of retries before failing (default: 3)
38
+
39
+ def initialize(args = {})
40
+ @host = args[:host] || "127.0.0.1"
41
+ @port = args[:port] || 161
42
+ @community_ro = args[:community_ro] || args[:community] || "public"
43
+ @community_rw = args[:community_rw] || args[:community] || "public"
44
+ @timeout = args[:timeout] || 1
45
+ @retries = args[:retries] || 3
46
+
47
+ self.class.init_socket
48
+ end
49
+
50
+ def send(message) #:nodoc:
51
+ self.class.socket.send_datagram message.encode, @host, @port
52
+ end
53
+
54
+ # Sends an SNMP-GET request to the remote agent for all OIDs specified in the _oids_ array. Returns a SnmpGetRequest object,
55
+ # which implements EM::Deferrable. From there, implement a callback/errback to fetch the result. On success, the result will be
56
+ # a hash, mapping requested OID values to results.
57
+ #
58
+ # Optional arguments can be passed into _args_, including:
59
+ # * _return_raw_ - Return objects and errors as their raw SNMP types, such as SNMP::Integer instead of native Ruby integers, SNMP::OctetString instead of native Ruby strings, etc. (default: false)
60
+
61
+ def get(oids, args = {})
62
+ request = SnmpGetRequest.new(self, oids, args)
63
+ self.class.pending_requests << request
64
+ return request
65
+ end
66
+
67
+ # Sends an SNMP-GETNEXT request to the remote agent for all OIDs specified in the _oids_ array. Returns a SnmpGetRequest object,
68
+ # which implements EM::Deferrable. From there, implement a callback/errback to fetch the result. On success, the result will be
69
+ # a hash, mapping requested OID values to two-element arrays consisting of [_next_oid_ , _next_value_]. Any values that produced an
70
+ # error will map to a symbol representing the error.
71
+ #
72
+ # Optional arguments can be passed into _args_, including:
73
+ # * _return_raw_ - Return objects and errors as their raw SNMP types, such as SNMP::Integer instead of native Ruby integers, SNMP::OctetString instead of native Ruby strings, etc. (default: false)
74
+
75
+ def getnext(oids, args = {})
76
+ request = SnmpGetNextRequest.new(self, oids, args)
77
+ self.class.pending_requests << request
78
+ return request
79
+ end
80
+
81
+ # Sends an SNMP-SET request to the remote agent for all OIDs specified in the _oids_ hash. The hash must map OID values to requested
82
+ # values. Values can either be specified as Ruby native strings/integers, or as SNMP-specific classes (SNMP::IpAddress, etc).
83
+ # Returns a SnmpSetRequest object, which implements EM::Deferrable. From there, implement a callback/errback to fetch the result.
84
+ # On success, the result will be a hash, mapping requested OID values to the returned value from the agent. Any values that were stored
85
+ # successfully will map to _true_, otherwise, the value will map to a symbol representing the error.
86
+ #
87
+ # Optional arguments can be passed into _args_, including:
88
+ # * _return_raw_ - Return error objects as SNMP::ResponseError instead of a symbol
89
+
90
+ def set(oids, args = {})
91
+ request = SnmpSetRequest.new(self, oids, args)
92
+ self.class.pending_requests << request
93
+ return request
94
+ end
95
+
96
+ # Sends a series of SNMP-GETNEXT requests to simulate an SNMP "walk" operation. Given an OID prefix, the library will keep requesting the
97
+ # next OID until that returned OID does not begin with the requested prefix. This gives the ability to retrieve entire portions of the
98
+ # SNMP tree in one "operation". Multiple OID prefixes can be passed into the _oids_ array, and will be fetched in parallel. The function returns
99
+ # a SnmpWalkRequest object, which implements EM::Deferrable. From there, implement a callback/errback to fetch the result. On success, the
100
+ # result will be a hash, mapping requested OID prefixes to the returned value. Successful walks will be mapped to an array of two-element arrays,
101
+ # each of which consists of [_oid_ , _value_]. Unsuccessful walks will be mapped to a symbol representing the error.
102
+
103
+ # Optional arguments can be passed into _args_, including:
104
+ # * _return_raw_ - Return objects and errors as their raw SNMP types, such as SNMP::Integer instead of native Ruby integers, SNMP::OctetString instead of native Ruby strings, etc. (default: false)
105
+ # * _max_results_ - Maximum number of results to be returned for any single OID prefix (default: nil = unlimited)
106
+
107
+ def walk(oids, args = {})
108
+ request = SnmpWalkRequest.new(self, oids, args)
109
+ self.class.pending_requests << request
110
+ return request
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,40 @@
1
+ module SNMP4EM
2
+ class SnmpRequest #:nodoc:
3
+ include EM::Deferrable
4
+
5
+ def generate_snmp_id
6
+ begin
7
+ snmp_id = rand(1073741823) # Largest Fixnum
8
+ end until (SNMPv1.pending_requests.select{|r| r.snmp_id == snmp_id}.empty?)
9
+
10
+ return snmp_id
11
+ end
12
+
13
+ def init_callbacks
14
+ self.callback do
15
+ SNMPv1.pending_requests.delete_if {|r| r.snmp_id == @snmp_id}
16
+ @timeout_timer.cancel
17
+ end
18
+
19
+ self.errback do
20
+ SNMPv1.pending_requests.delete_if {|r| r.snmp_id == @snmp_id}
21
+ @timeout_timer.cancel
22
+ end
23
+ end
24
+
25
+ def send(msg)
26
+ @sender.send msg
27
+
28
+ @timeout_timer.cancel if @timeout_timer.is_a?(EM::Timer)
29
+
30
+ @timeout_timer = EM::Timer.new(@sender.timeout) do
31
+ if (@timeout_retries > 0)
32
+ send
33
+ @timeout_retries -= 1
34
+ else
35
+ fail("timeout")
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{snmp4em}
5
+ s.version = "0.1.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Norman Elton"]
9
+ s.date = %q{2009-05-04}
10
+ s.description = %q{A high-performance SNMP engine built on EventMachine and Ruby-SNMP}
11
+ s.email = %q{normelton@gmail.com}
12
+ s.extra_rdoc_files = ["README"]
13
+ s.files = ["lib/common.rb", "lib/handler.rb", "lib/requests/snmp_get_request.rb", "lib/requests/snmp_getnext_request.rb", "lib/requests/snmp_set_request.rb", "lib/requests/snmp_walk_request.rb", "lib/snmp4em.rb", "lib/snmp_request.rb", "Rakefile", "README", "snmp4em.gemspec", "Manifest"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{http://github.com/normelton/snmp4em}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Snmp4em", "--main", "README"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{snmp4em}
19
+ s.rubygems_version = %q{1.3.2}
20
+ s.summary = %q{A high-performance SNMP engine built on EventMachine and Ruby-SNMP}
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 3
25
+
26
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
+ else
28
+ end
29
+ else
30
+ end
31
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: normelton-snmp4em
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Norman Elton
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-04 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A high-performance SNMP engine built on EventMachine and Ruby-SNMP
17
+ email: normelton@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - lib/common.rb
26
+ - lib/handler.rb
27
+ - lib/requests/snmp_get_request.rb
28
+ - lib/requests/snmp_getnext_request.rb
29
+ - lib/requests/snmp_set_request.rb
30
+ - lib/requests/snmp_walk_request.rb
31
+ - lib/snmp4em.rb
32
+ - lib/snmp_request.rb
33
+ - Rakefile
34
+ - README
35
+ - snmp4em.gemspec
36
+ - Manifest
37
+ has_rdoc: true
38
+ homepage: http://github.com/normelton/snmp4em
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --line-numbers
42
+ - --inline-source
43
+ - --title
44
+ - Snmp4em
45
+ - --main
46
+ - README
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "1.2"
60
+ version:
61
+ requirements: []
62
+
63
+ rubyforge_project: snmp4em
64
+ rubygems_version: 1.2.0
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: A high-performance SNMP engine built on EventMachine and Ruby-SNMP
68
+ test_files: []
69
+