normelton-snmp4em 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+