net-snmp2 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +20 -0
  6. data/README.md +28 -0
  7. data/Rakefile +24 -0
  8. data/TODO.md +6 -0
  9. data/bin/mib2rb +129 -0
  10. data/bin/net-snmp2 +64 -0
  11. data/examples/agent.rb +104 -0
  12. data/examples/manager.rb +11 -0
  13. data/examples/trap_handler.rb +46 -0
  14. data/examples/v1_trap_session.rb +24 -0
  15. data/examples/v2_trap_session.rb +21 -0
  16. data/lib/net-snmp2.rb +85 -0
  17. data/lib/net/snmp.rb +27 -0
  18. data/lib/net/snmp/agent/agent.rb +48 -0
  19. data/lib/net/snmp/agent/provider.rb +51 -0
  20. data/lib/net/snmp/agent/provider_dsl.rb +124 -0
  21. data/lib/net/snmp/agent/request_dispatcher.rb +38 -0
  22. data/lib/net/snmp/constants.rb +287 -0
  23. data/lib/net/snmp/debug.rb +54 -0
  24. data/lib/net/snmp/dispatcher.rb +108 -0
  25. data/lib/net/snmp/error.rb +29 -0
  26. data/lib/net/snmp/listener.rb +76 -0
  27. data/lib/net/snmp/message.rb +142 -0
  28. data/lib/net/snmp/mib/mib.rb +67 -0
  29. data/lib/net/snmp/mib/module.rb +39 -0
  30. data/lib/net/snmp/mib/node.rb +122 -0
  31. data/lib/net/snmp/mib/templates.rb +48 -0
  32. data/lib/net/snmp/oid.rb +134 -0
  33. data/lib/net/snmp/pdu.rb +235 -0
  34. data/lib/net/snmp/repl/manager_repl.rb +243 -0
  35. data/lib/net/snmp/session.rb +560 -0
  36. data/lib/net/snmp/trap_handler/trap_handler.rb +42 -0
  37. data/lib/net/snmp/trap_handler/v1_trap_dsl.rb +44 -0
  38. data/lib/net/snmp/trap_handler/v2_trap_dsl.rb +38 -0
  39. data/lib/net/snmp/trap_session.rb +92 -0
  40. data/lib/net/snmp/utility.rb +10 -0
  41. data/lib/net/snmp/varbind.rb +57 -0
  42. data/lib/net/snmp/version.rb +5 -0
  43. data/lib/net/snmp/wrapper.rb +450 -0
  44. data/net-snmp2.gemspec +30 -0
  45. data/spec/README.md +105 -0
  46. data/spec/async_spec.rb +123 -0
  47. data/spec/em_spec.rb +23 -0
  48. data/spec/error_spec.rb +34 -0
  49. data/spec/fiber_spec.rb +41 -0
  50. data/spec/mib_spec.rb +68 -0
  51. data/spec/net-snmp_spec.rb +18 -0
  52. data/spec/oid_spec.rb +21 -0
  53. data/spec/spec_helper.rb +10 -0
  54. data/spec/sync_spec.rb +132 -0
  55. data/spec/thread_spec.rb +19 -0
  56. data/spec/trap_spec.rb +45 -0
  57. data/spec/utility_spec.rb +10 -0
  58. data/spec/wrapper_spec.rb +69 -0
  59. metadata +166 -0
@@ -0,0 +1,39 @@
1
+ # Ruby api to the Wrapper::Module class (represents netsnmp's `struct module`)
2
+
3
+ module Net::SNMP
4
+ class Module
5
+ extend Forwardable
6
+ def_delegators :struct, :modid, :no_imports
7
+
8
+ # Gets a module node by its id
9
+ def self.find(module_id)
10
+ if module_id < 0
11
+ nil
12
+ else
13
+ new(Wrapper.find_module(module_id))
14
+ end
15
+ end
16
+
17
+ def initialize(struct)
18
+ raise "Tried to initialize null module" if struct.null?
19
+ @struct = struct
20
+ end
21
+
22
+ def name
23
+ @struct.name.read_string
24
+ end
25
+
26
+ def file
27
+ @struct.file.read_string
28
+ end
29
+
30
+ def next
31
+ if @struct.next.null?
32
+ nil
33
+ else
34
+ self.class.new(@struct.next)
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,122 @@
1
+ # Ruby api to the Wrapper::Tree class (represents netsnmp's `struct tree`)
2
+
3
+ module Net::SNMP
4
+ module MIB
5
+ class Node
6
+ include Debug
7
+ extend Forwardable
8
+ attr_accessor :struct, :is_in_mib
9
+ def_delegators :struct, :label, :type, :access, :status, :modid, :subid
10
+
11
+ class << self
12
+ include Debug
13
+ def get_node(oid)
14
+ if oid.kind_of?(String)
15
+ oid = OID.new(oid)
16
+ end
17
+ struct = Wrapper.get_tree(oid.pointer, oid.length_pointer.read_int, Wrapper.get_tree_head().pointer)
18
+ node = new(struct.pointer)
19
+ warn "OID #{oid.to_s} not found in MIB" unless node.in_mib?
20
+ node
21
+ end
22
+ end
23
+
24
+ def initialize(arg)
25
+ @oid = nil
26
+ @module = nil
27
+ case arg
28
+ when Wrapper::Tree
29
+ @struct = arg
30
+ when FFI::Pointer
31
+ @struct = Wrapper::Tree.new(arg)
32
+ else
33
+ raise "invalid type"
34
+ end
35
+ end
36
+
37
+ def exists_in_mib?
38
+ # The structure can be created without the oid actually
39
+ # existing in the mib, but the parent will be null.
40
+ # Of course, the parent is null for the root node as well,
41
+ # so ignore that case.
42
+ (!@struct.parent.null?) || oid.to_s == '1'
43
+ end
44
+ alias in_mib? exists_in_mib?
45
+
46
+ def module
47
+ unless @module
48
+ @module = Module.find(modid)
49
+ end
50
+ @module
51
+ end
52
+
53
+ def description
54
+ if @struct.description.null?
55
+ nil
56
+ else
57
+ @struct.description.read_string
58
+ end
59
+ end
60
+
61
+ def oid
62
+ return @oid if @oid
63
+ @oid = OID.new(label)
64
+ end
65
+
66
+ # actually seems like list is linked backward, so this will retrieve the previous oid numerically
67
+ def next
68
+ return nil if @struct.next.null?
69
+ self.class.new(@struct.next)
70
+ end
71
+
72
+ def next_peer
73
+ return nil if @struct.next_peer.null?
74
+ self.class.new(@struct.next_peer)
75
+ end
76
+
77
+ def parent
78
+ return nil if @struct.parent.null?
79
+ self.class.new(@struct.parent)
80
+ end
81
+
82
+ def children
83
+ return to_enum __method__ unless block_given?
84
+ return if @struct.child_list.null?
85
+ child = self.class.new(@struct.child_list)
86
+ yield child
87
+ while child = child.next_peer
88
+ yield child
89
+ end
90
+ end
91
+
92
+ # Depth-first traversal of all descendants
93
+ def descendants(&block)
94
+ return to_enum __method__ unless block_given?
95
+ return if @struct.child_list.null?
96
+ child = self.class.new(@struct.child_list)
97
+ block[child]
98
+ child.descendants(&block)
99
+ while child = child.next_peer
100
+ block[child]
101
+ child.descendants(&block)
102
+ end
103
+ end
104
+
105
+ def peers
106
+ return [] if oid.to_s == '1'
107
+ parent.children.reject { |n| n.oid.to_s == oid.to_s }
108
+ end
109
+ alias siblings peers
110
+
111
+ def enums
112
+ return to_enum __method__ unless block_given?
113
+ enum = struct.enums
114
+ while !enum.null?
115
+ yield({value: enum.value, label: enum.label.read_string})
116
+ enum = enum.next
117
+ end
118
+ end
119
+
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,48 @@
1
+ module Net::SNMP::MIB
2
+ module Templates
3
+ DESCRIBE = '
4
+ <% nodes.each do |node| -%>
5
+ <%= node.module.nil? ? "" : "#{node.module.name}::" %><%= node.label %>
6
+ - oid: <%= node.oid %>
7
+ - type: <%= node.type %>
8
+ - file: <%= node.module.file unless node.module.nil? %>
9
+ - descr: <%= "\"#{node.description}\"" %>
10
+ - enums: <%=
11
+ if !(node.enums.count == 0)
12
+ node.enums.map { |e| "#{e[:label]}(#{e[:value]})" }.join(", ")
13
+ else
14
+ "NONE"
15
+ end
16
+ %>
17
+ - parent: <%= "#{node.parent.label}(#{node.parent.oid})" unless node.parent.nil? %>
18
+ - peers: <%= node.peers.map { |n| "#{n.label}(#{n.subid})"}.join(", ") %>
19
+ - next: <%= node.next.oid unless node.next.nil? %>
20
+ - next_peer: <%= node.next_peer.oid unless node.next_peer.nil? %>
21
+ - children: <%=
22
+ if node.children.count > 0
23
+ node.children.map { |n| "#{n.label}(#{n.subid})"}.join(", ")
24
+ else
25
+ "NONE"
26
+ end
27
+ %>
28
+ <% end -%>
29
+ '.sub!("\n", "") # Remove leading newline
30
+
31
+ JSON = json_template = '
32
+ [
33
+ <% nodes.each_with_index { |node, index| -%>
34
+ <%= ",\n" if index > 0 -%>
35
+ {
36
+ "name": "<%= node.module.nil? ? "" : "#{node.module.name}::" %><%= node.label %>",
37
+ "oid": "<%= node.oid %>",
38
+ "type": <%= node.type %>,
39
+ <% if node.enums && node.enums.count > 0 -%>
40
+ "enums": { <%= node.enums.map { |enum| "\"#{enum[:label]}\": #{enum[:value]}" }.join(", ") %> },
41
+ <% end -%>
42
+ "parent": <%= node.parent ? %Q["#{node.parent.oid}"] : "undefined" %>
43
+ }<% } -%>
44
+
45
+ ]
46
+ '.sub!("\n", "")
47
+ end
48
+ end
@@ -0,0 +1,134 @@
1
+ module Net
2
+ module SNMP
3
+ class OID
4
+ extend Debug
5
+ include Debug
6
+
7
+ attr_reader :oid, :pointer, :length_pointer
8
+ @oid_size = nil
9
+ @sub_id_bit_width = nil
10
+
11
+ def self.from_pointer(ptr, sub_id_count)
12
+ OID.new(OID.read_pointer(ptr, sub_id_count))
13
+ end
14
+
15
+ def self.oid_size
16
+ unless @oid_size
17
+ determine_oid_size
18
+ end
19
+ @oid_size
20
+ end
21
+
22
+ def self.determine_oid_size
23
+ if Net::SNMP.initialized?
24
+ oid_ptr = FFI::MemoryPointer.new(:ulong, 8)
25
+ length_ptr = FFI::MemoryPointer.new(:size_t, 1)
26
+ length_ptr.write_int(oid_ptr.total)
27
+
28
+ Wrapper.read_objid('1.1', oid_ptr, length_ptr)
29
+ oid_str = oid_ptr.read_array_of_uint8(oid_ptr.total).map{|byte| byte.to_s(2).rjust(8, '0') }.join('')
30
+
31
+ @oid_size = (oid_str[/10*1/].length - 1) / 8
32
+ else
33
+ @oid_size = FFI::MemoryPointer.new(:ulong).total
34
+ warn "SNMP not initialized\n" + <<-WARNING
35
+ Cannot determine OID sub-id size, assuming common case of sizeof(ulong)
36
+ On this platform, sizeof(ulong) = #{@oid_size} bytes
37
+ To avoid this warning, call `Net::SNMP.init`
38
+ WARNING
39
+ end
40
+ end
41
+
42
+ def self.read_pointer(pointer, sub_id_count)
43
+ unless @sub_id_bit_width
44
+ @sub_id_bit_width = OID.oid_size * 8
45
+ end
46
+ pointer.send("read_array_of_uint#{@sub_id_bit_width}", sub_id_count).join('.')
47
+ end
48
+
49
+ def write_to_buffer(buffer)
50
+ unless @sub_id_bit_width
51
+ @sub_id_bit_width = OID.oid_size * 8
52
+ end
53
+ buffer.send("write_array_of_uint#{@sub_id_bit_width}", self.to_s.split('.').map{ |subid| subid.to_i })
54
+ end
55
+
56
+ def initialize(oid)
57
+ @oid = oid
58
+ @pointer = FFI::MemoryPointer.new(Net::SNMP::OID.oid_size * Constants::MAX_OID_LEN)
59
+ @length_pointer = FFI::MemoryPointer.new(:size_t)
60
+ @length_pointer.write_int(Constants::MAX_OID_LEN)
61
+ if @oid =~ /^[\d\.]*$/
62
+ if Wrapper.read_objid(@oid, @pointer, @length_pointer) == 0
63
+ Wrapper.snmp_perror(@oid)
64
+ end
65
+ else
66
+ if Wrapper.get_node(@oid, @pointer, @length_pointer) == 0
67
+ raise "No such node: #{oid}"
68
+ end
69
+ @oid = to_s
70
+ end
71
+ end
72
+
73
+ def size
74
+ @length_pointer.read_int * Net::SNMP::OID.oid_size
75
+ end
76
+
77
+ def length
78
+ @length_pointer.read_int
79
+ end
80
+
81
+ def oid
82
+ @oid
83
+ end
84
+
85
+ def name
86
+ @oid
87
+ end
88
+
89
+ def to_s
90
+ OID.read_pointer(@pointer, length)
91
+ end
92
+
93
+ # Gets the MIB node corresponding to this OID.
94
+ # Note that if this OID represents a MIB node, like '1.3',
95
+ # then `oid.to_s == oid.node.oid.to_s`. However, if there
96
+ # are indices on this oid, then `oid.to_s.start_with?(oid.node.oid.to_s)`
97
+ def node
98
+ MIB::Node.get_node(oid)
99
+ end
100
+
101
+ # The instance index string
102
+ # Note that some MIB nodes specify the instance implicitly,
103
+ # like sysUpTimeInstance. So, this may be an empty string.
104
+ def index
105
+ # Strip the name
106
+ index = oid.sub(node.oid.name, '')
107
+ # Strip any leading dot
108
+ index.sub(/^\./, '')
109
+ end
110
+
111
+ # The label for the this OID. Includes any instance indexes.
112
+ def label
113
+ "#{node.label}#{index.length > 0 ? "." + index : ""}"
114
+ end
115
+
116
+ def <=>(o)
117
+ a = self._packed
118
+ b = o._packed
119
+ a <=> b
120
+ end
121
+
122
+ def _packed
123
+ i = self.to_s.dup
124
+ i.sub!(/^\./,'')
125
+ i.gsub!(/ /, '.0')
126
+ i.replace(i.split('.').map(&:to_i).pack('N*'))
127
+ end
128
+
129
+ def parent_of?(o)
130
+ o.to_s =~ /^#{self.to_s}\./
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,235 @@
1
+ module Net
2
+ module SNMP
3
+ # Wrapper around netsnmp_pdu.
4
+ class PDU
5
+ extend Forwardable
6
+ include Net::SNMP::Debug
7
+ attr_accessor :struct, :varbinds, :callback, :command
8
+ def_delegators :struct, :pointer
9
+
10
+ # Create a new PDU object.
11
+ # +pdu_type+ The type of the PDU. For example, Net::SNMP::SNMP_MSG_GET. See constants.rb
12
+ def initialize(pdu_type)
13
+ @varbinds = []
14
+ case pdu_type
15
+ when FFI::Pointer
16
+ @struct = Wrapper::SnmpPdu.new(pdu_type)
17
+ @command = @struct.command
18
+ v = @struct.variables
19
+ unless v.null?
20
+ @varbinds << Varbind.from_pointer(v)
21
+ while( !(v = v.next_variable).null? )
22
+ @varbinds << Varbind.from_pointer(v)
23
+ end
24
+ end
25
+ when Fixnum
26
+ @struct = Wrapper.snmp_pdu_create(pdu_type)
27
+ @command = pdu_type
28
+ else
29
+ raise Error.new, "invalid pdu type"
30
+ end
31
+ end
32
+
33
+ # Specifies the number of non-repeating, regular objects at the start of
34
+ # the variable list in the request.
35
+ # (For getbulk requests only, non-repeaters is stored in errstat location)
36
+ def non_repeaters
37
+ @struct.errstat
38
+ end
39
+
40
+ def non_repeaters=(nr)
41
+ @struct.errstat = nr
42
+ end
43
+
44
+ # The number of iterations in the table to be read for the repeating
45
+ # objects that follow the non-repeating objects.
46
+ # (For getbulk requests only, max-repititions are stored in errindex location)
47
+ def max_repetitions=(mr)
48
+ @struct.errindex = mr
49
+ end
50
+
51
+ def max_repetitions
52
+ @struct.errindex
53
+ end
54
+
55
+ # Sets the enterprise OID of this PDU
56
+ # (Valid for SNMPv1 traps only)
57
+ def enterprise=(oid)
58
+ @i_own_enterprise = true
59
+ oid = OID.new(oid) if oid.kind_of?(String)
60
+ @struct.enterprise = FFI::LibC.calloc(oid.length, OID.oid_size)
61
+ oid.write_to_buffer(@struct.enterprise)
62
+ @struct.enterprise_length = oid.length
63
+ end
64
+
65
+ # The enterprise OID of this PDU
66
+ # (Valid for SNMPv1 traps only)
67
+ def enterprise
68
+ OID.from_pointer(@struct.enterprise, @struct.enterprise_length)
69
+ end
70
+
71
+ # Sets the address of the agent that sent this PDU
72
+ # (Valid for SNMPv1 traps only)
73
+ def agent_addr=(addr)
74
+ # @struct.agent_addr is a binary array of 4 characters,
75
+ # so pack the provided string into four bytes and we can assign it
76
+ @struct.agent_addr = addr.split('.').map{ |octet| octet.to_i }.pack("CCCC")
77
+ end
78
+
79
+ # The address of the agent that sent this PDU
80
+ # (Valid for SNMPv1 traps only)
81
+ def agent_addr
82
+ # @struct.agent_addr is a binary array of 4 characters,
83
+ # to_a converts this to a ruby array of Integers, then join get's us
84
+ # back to the string form
85
+ @struct.agent_addr.to_a.join('.')
86
+ end
87
+
88
+ # The uptime for the PDU
89
+ # (Only valid for SNMPv1 traps)
90
+ def uptime
91
+ @struct.time
92
+ end
93
+
94
+ # The uptime for the PDU
95
+ # (Only valid for SNMPv1 traps)
96
+ def uptime=(value)
97
+ @struct.time = value.to_i
98
+ end
99
+
100
+ # Returns true if pdu is in error
101
+ def error?
102
+ self.errstat != 0
103
+ end
104
+
105
+ # Sets the pdu errstat
106
+ def error=(value)
107
+ @struct.errstat = value
108
+ end
109
+ alias errstat= error=
110
+ alias error_status= error=
111
+
112
+ # Sets the error index
113
+ def error_index=(index)
114
+ @struct.errindex = index
115
+ end
116
+ alias errindex= error_index=
117
+
118
+ # A descriptive error message
119
+ def error_message
120
+ Wrapper::snmp_errstring(self.errstat)
121
+ end
122
+
123
+ # Tries to delegate missing methods to the underlying Wrapper::SnmpPdu object.
124
+ # If it does not respond to the method, calls super.
125
+ def method_missing(m, *args)
126
+ if @struct.respond_to?(m)
127
+ @struct.send(m, *args)
128
+ else
129
+ super
130
+ end
131
+ end
132
+
133
+ # Adds a variable binding to the pdu.
134
+ # Options:
135
+ # * +oid+ The SNMP OID
136
+ # * +type+ The data type. Possible values include Net::SNMP::ASN_OCTET_STR, Net::SNMP::ASN_COUNTER, etc. See constants.rb
137
+ # * +value+ The value of the varbind. default is nil.
138
+ def add_varbind(options)
139
+ options[:type] ||= case options[:value]
140
+ when String
141
+ Constants::ASN_OCTET_STR
142
+ when Fixnum
143
+ Constants::ASN_INTEGER
144
+ when Net::SNMP::OID
145
+ Constants::ASN_OBJECT_ID
146
+ when nil
147
+ Constants::ASN_NULL
148
+ else
149
+ raise "Unknown value type"
150
+ end
151
+
152
+ value = options[:value]
153
+ value_len = case options[:type]
154
+ when Constants::ASN_NULL,
155
+ Constants::SNMP_NOSUCHOBJECT,
156
+ Constants::SNMP_NOSUCHINSTANCE,
157
+ Constants::SNMP_ENDOFMIBVIEW
158
+ 0
159
+ else
160
+ options[:value].size
161
+ end
162
+
163
+ value = case options[:type]
164
+ when Constants::ASN_INTEGER,
165
+ Constants::ASN_GAUGE,
166
+ Constants::ASN_COUNTER,
167
+ Constants::ASN_TIMETICKS,
168
+ Constants::ASN_UNSIGNED
169
+ new_val = FFI::MemoryPointer.new(:long)
170
+ new_val.write_long(value)
171
+ new_val
172
+ when Constants::ASN_OCTET_STR,
173
+ Constants::ASN_BIT_STR,
174
+ Constants::ASN_OPAQUE
175
+ value
176
+ when Constants::ASN_IPADDRESS
177
+ # TODO
178
+ when Constants::ASN_OBJECT_ID
179
+ value.pointer
180
+ when Constants::ASN_NULL,
181
+ Constants::SNMP_NOSUCHOBJECT,
182
+ Constants::SNMP_NOSUCHINSTANCE,
183
+ Constants::SNMP_ENDOFMIBVIEW
184
+ nil
185
+ else
186
+ if value.respond_to?(:pointer)
187
+ value.pointer
188
+ else
189
+ raise Net::SNMP::Error.new, "Unknown variable type #{options[:type]}"
190
+ end
191
+ end
192
+
193
+ oid = options[:oid].kind_of?(OID) ? options[:oid] : OID.new(options[:oid])
194
+ var_ptr = Wrapper.snmp_pdu_add_variable(@struct.pointer, oid.pointer, oid.length_pointer.read_int, options[:type], value, value_len)
195
+ varbind = Varbind.new(var_ptr)
196
+ varbinds << varbind
197
+ end
198
+
199
+ def print_errors
200
+ puts "errstat = #{self.errstat}, index = #{self.errindex}, message = #{self.error_message}"
201
+ end
202
+
203
+ # Free the pdu
204
+ # Segfaults at the moment - most of the time, anyway.
205
+ # This is troublesome...
206
+ def free
207
+ # HACK
208
+ # snmp_free_pdu segfaults intermittently when freeing the enterprise
209
+ # oid if we've allocated it. Can't figure out why. For now, freeing it manually
210
+ # before calling snmp_free_pdu does the trick
211
+ if @i_own_enterprise
212
+ FFI::LibC.free(@struct.enterprise) unless @struct.enterprise.null?
213
+ @struct.enterprise = FFI::Pointer::NULL
214
+ end
215
+ Wrapper.snmp_free_pdu(@struct.pointer)
216
+ end
217
+
218
+ def print
219
+ puts "PDU"
220
+ if command == Constants::SNMP_MSG_TRAP
221
+ puts " - Enterprise: #{enterprise}"
222
+ puts " - Trap Type: #{trap_type}"
223
+ puts " - Specific Type: #{specific_type}"
224
+ puts " - Agent Addr: #{agent_addr}"
225
+ end
226
+
227
+ puts " - Varbinds:"
228
+ varbinds.each do |v|
229
+ puts " + #{MIB.translate(v.oid.to_s)}(#{v.oid}) = #{v.value}"
230
+ end
231
+ end
232
+
233
+ end
234
+ end
235
+ end