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.
- data/Gemfile +2 -0
- data/README.rdoc +41 -20
- data/lib/net-snmp.rb +6 -2
- data/lib/net/snmp.rb +7 -4
- data/lib/net/snmp/constants.rb +73 -56
- data/lib/net/snmp/debug.rb +17 -0
- data/lib/net/snmp/dispatcher.rb +43 -65
- data/lib/net/snmp/error.rb +18 -10
- data/lib/net/snmp/inline.rb +4 -14
- data/lib/net/snmp/oid.rb +6 -2
- data/lib/net/snmp/pdu.rb +58 -19
- data/lib/net/snmp/session.rb +275 -195
- data/lib/net/snmp/trap_session.rb +64 -0
- data/lib/net/snmp/varbind.rb +28 -6
- data/lib/net/snmp/version.rb +1 -1
- data/lib/net/snmp/wrapper.rb +33 -8
- data/spec/async_spec.rb +78 -30
- data/spec/em_spec.rb +1 -1
- data/spec/error_spec.rb +36 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/sync_spec.rb +9 -1
- data/spec/thread_spec.rb +3 -1
- data/spec/trap_spec.rb +12 -10
- data/spec/wrapper_spec.rb +2 -2
- metadata +8 -4
data/lib/net/snmp/error.rb
CHANGED
@@ -1,22 +1,30 @@
|
|
1
1
|
module Net
|
2
2
|
module SNMP
|
3
3
|
class Error < RuntimeError
|
4
|
-
|
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
|
-
|
10
|
-
|
11
|
-
|
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
|
|
data/lib/net/snmp/inline.rb
CHANGED
@@ -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
|
12
|
-
|
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
|
-
|
11
|
+
include Net::SNMP::Debug
|
12
|
+
attr_accessor :struct, :varbinds, :callback, :command
|
6
13
|
def_delegators :struct, :pointer
|
7
14
|
|
8
|
-
|
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
|
19
|
+
case pdu_type
|
11
20
|
when FFI::Pointer
|
12
|
-
@struct = Wrapper::SnmpPdu.new(
|
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(
|
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[:
|
82
|
-
when
|
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
|
-
|
92
|
-
|
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
|
-
|
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
|
data/lib/net/snmp/session.rb
CHANGED
@@ -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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
92
|
+
OID.new("1.3.6.1.6.3.10.1.1.3").pointer
|
73
93
|
when :md5
|
74
|
-
|
94
|
+
OID.new("1.3.6.1.6.3.10.1.1.2").pointer
|
75
95
|
when nil
|
76
|
-
|
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
|
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
|
-
|
121
|
-
|
134
|
+
self.class.sessions.delete(self.sessid)
|
135
|
+
end
|
122
136
|
end
|
123
137
|
|
124
|
-
|
125
|
-
|
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
|
-
|
134
|
-
|
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 =
|
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
|
-
|
156
|
-
|
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
|
-
|
236
|
-
|
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
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
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 =
|
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
|
-
|
283
|
-
|
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
|
-
|
292
|
-
|
293
|
-
|
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
|
-
|
235
|
+
Wrapper.snmp_sess_read(@struct, fdset)
|
299
236
|
elsif num_ready == 0
|
300
|
-
|
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
|
-
|
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
|
-
|
311
|
-
|
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
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
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
|
-
|
290
|
+
#pdu.free
|
291
|
+
nil
|
321
292
|
else
|
322
|
-
if
|
323
|
-
|
324
|
-
|
325
|
-
|
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
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
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
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
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
|
+
|