arborist-snmp 0.1.0.pre20161005111600 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 639227f1252bedce794f5bfabada161a9ff3d8a2
4
- data.tar.gz: 713cc9b8bf1fc6fc467b688d9d38656be3e193c9
3
+ metadata.gz: 833fc064ac6b821016f02ec2b427c7861cc4ebd2
4
+ data.tar.gz: d14a89c993c13e874b9201388999bb77ac85c06f
5
5
  SHA512:
6
- metadata.gz: 94b2a8a87fe45817464ddff645ab1058a6ab3577afd893cfc0b2837737ce3240567cf52c6982379a2886ba9590cf0674106ab601cf5ad0e82cc869e005754ffb
7
- data.tar.gz: b34cc56ac99c63333a020b5ecf201daf74d310dbe2447fec3568713e128fec3c0cdaaa36a15d7833b4cc2975f5bb14943b9127ef3b62ca3c884106fc1a203212
6
+ metadata.gz: be00731e69eae712e71e9881abb20ba9d1393de4acad424b8b2c7225b95d351e211a9c20551556f76fde34e59a551ff2716f9f220a247afa35b529b688301ca5
7
+ data.tar.gz: bd6b4d1292adec637932fc8edccd62fe1db5fbaea2350995ab9c504e2eb002e36470ca7280742fb9b4eb231523fc8744bcb0d5135017bdf4bae8e23c8d5757bd
@@ -1,402 +1,144 @@
1
1
  # -*- ruby -*-
2
2
  # vim: set noet nosta sw=4 ts=4 :
3
3
  #encoding: utf-8
4
- #
4
+
5
+ require 'arborist/monitor' unless defined?( Arborist::Monitor )
6
+ require 'net-snmp2'
7
+
5
8
  # SNMP checks for Arborist. Requires an SNMP agent to be installed
6
- # on target machine, and the various "pieces" enabled. For your platform.
9
+ # on target machine, and the various "pieces" enabled for your platform.
7
10
  #
8
11
  # For example, for disk monitoring with Net-SNMP, you'll want to set
9
12
  # 'includeAllDisks' in the snmpd.conf. bsnmpd on FreeBSD benefits from
10
13
  # the 'bsnmp-ucd' package. Etc.
11
14
  #
15
+ module Arborist::Monitor::SNMP
16
+ using Arborist::TimeRefinements
17
+ extend Configurability, Loggability
12
18
 
13
- require 'loggability'
14
- require 'arborist/monitor' unless defined?( Arborist::Monitor )
15
- require 'snmp'
16
-
17
- using Arborist::TimeRefinements
18
-
19
- # SNMP specific monitors and monitor logic.
20
- #
21
- class Arborist::Monitor::SNMP
22
- extend Loggability
23
- log_to :arborist
24
-
25
- # The version of this library.
26
- VERSION = '0.1.0'
19
+ # Loggability API
20
+ log_to :arborist_snmp
27
21
 
28
- # "Modes" that this monitor understands.
29
- VALID_MODES = %i[ disk load memory swap process ]
22
+ # Always request the node addresses and any config.
23
+ USED_PROPERTIES = [ :addresses, :config ].freeze
30
24
 
31
25
  # The OID that returns the system environment.
32
26
  IDENTIFICATION_OID = '1.3.6.1.2.1.1.1.0'
33
27
 
34
- # For net-snmp systems, ignore mount types that match
35
- # this regular expression. This includes null/union mounts
36
- # and NFS, currently.
37
- STORAGE_IGNORE = %r{25.3.9.(?:2|14)$}
38
-
39
- # The OID that matches a local windows hard disk. Anything else
40
- # is a remote (SMB) mount.
41
- WINDOWS_DEVICE = '1.3.6.1.2.1.25.2.1.4'
42
-
43
- # OIDS required to pull disk information from net-snmp.
28
+ # Global defaults for instances of this monitor
44
29
  #
45
- STORAGE_NET_SNMP = [
46
- '1.3.6.1.4.1.2021.9.1.2', # paths
47
- '1.3.6.1.2.1.25.3.8.1.4', # types
48
- '1.3.6.1.4.1.2021.9.1.9' # percents
49
- ]
50
-
51
- # OIDS required to pull disk information from Windows.
52
- #
53
- STORAGE_WINDOWS = [
54
- '1.3.6.1.2.1.25.2.3.1.2', # types
55
- '1.3.6.1.2.1.25.2.3.1.3', # paths
56
- '1.3.6.1.2.1.25.2.3.1.5', # totalsize
57
- '1.3.6.1.2.1.25.2.3.1.6' # usedsize
58
- ]
59
-
60
- # OIDS for discovering memory usage.
61
- #
62
- MEMORY = {
63
- swap_total: '1.3.6.1.4.1.2021.4.3.0',
64
- swap_avail: '1.3.6.1.4.1.2021.4.4.0',
65
- mem_avail: '1.3.6.1.4.1.2021.4.6.0'
66
- }
67
-
68
- # OIDS for discovering system load.
69
- #
70
- LOAD = {
71
- five_min: '1.3.6.1.4.1.2021.10.1.3.2'
72
- }
73
-
74
- # OIDS for discovering running processes.
75
- #
76
- PROCESS = {
77
- list: '1.3.6.1.2.1.25.4.2.1.4',
78
- args: '1.3.6.1.2.1.25.4.2.1.5'
79
- }
80
-
81
-
82
- # Defaults for instances of this monitor
83
- #
84
- DEFAULT_OPTIONS = {
85
- timeout: 2,
86
- retries: 1,
87
- community: 'public',
88
- port: 161,
89
- storage_error_at: 95, # in percent full
90
- load_error_at: 7,
91
- swap_error_at: 25, # in percent remaining
92
- mem_error_at: 51200, # in kilobytes
93
- processes: [] # list of procs to match
94
- }
95
-
96
-
97
- ### This monitor is complex enough to require creating an instance from the caller.
98
- ### Provide a friendlier error message the class was provided to exec() directly.
99
- ###
100
- def self::run( nodes )
101
- self.log.error "Please use %s via an instance." % [ self.name ]
102
- return {}
30
+ configurability( 'arborist.snmp' ) do
31
+ setting :timeout, default: 2
32
+ setting :retries, default: 1
33
+ setting :community, default: 'public'
34
+ setting :version, default: '2c'
35
+ setting :port, default: 161
36
+
37
+ # How many hosts to check simultaneously
38
+ setting :batchsize, default: 25
103
39
  end
104
40
 
105
-
106
- ### Create a new instance of this monitor.
107
- ###
108
- def initialize( options=DEFAULT_OPTIONS )
109
- options = DEFAULT_OPTIONS.merge( options || {} )
110
-
111
- options.each do |name, value|
112
- self.public_send( "#{name}=", value )
113
- end
114
- end
41
+ # Indicate to FFI that we're using threads.
42
+ Net::SNMP.thread_safe = true
115
43
 
116
44
 
117
- # The mode (section) that this SMMP instance should check.
118
- # Must be a +VALID_MODES+ mode.
119
- attr_reader :mode
45
+ # The system type, as advertised.
46
+ attr_reader :system
120
47
 
121
- # Mapping of node addresses back to the node identifier.
48
+ # The mapping of addresses back to node identifiers.
122
49
  attr_reader :identifiers
123
50
 
124
- # The results from the SNMP daemons, keyed by address.
51
+ # The results hash that is sent back to the manager.
125
52
  attr_reader :results
126
53
 
127
- # A timeout in seconds if the SNMP server isn't responding.
128
- attr_accessor :timeout
129
-
130
- # Retry with the timeout this many times. Defaults to 1.
131
- attr_accessor :retries
132
-
133
- # The SNMP UDP port, if running on non default.
134
- attr_accessor :port
135
-
136
- # The community string to connect with.
137
- attr_accessor :community
138
-
139
- # Set an error if mount points are above this percentage.
140
- attr_accessor :storage_error_at
141
54
 
142
- # Set an error if the 5 minute load average exceeds this.
143
- attr_accessor :load_error_at
144
-
145
- # Set an error if used swap exceeds this percentage.
146
- attr_accessor :swap_error_at
147
-
148
- # Set an error if memory used is below this many kilobytes.
149
- attr_accessor :mem_error_at
150
-
151
- # Set an error if processes in this array aren't running.
152
- attr_accessor :processes
153
-
154
-
155
- ### Set the SNMP mode, after validation.
156
- ###
157
- def mode=( mode )
158
- unless VALID_MODES.include?( mode.to_sym )
159
- self.log.error "Unknown SNMP mode: %s" % [ mode ]
160
- return nil
161
- end
162
-
163
- @mode = mode.to_sym
164
- @results = {}
165
- end
166
-
167
-
168
- ### Perform the monitoring checks.
55
+ ### Connect to the SNMP daemon and yield.
169
56
  ###
170
57
  def run( nodes )
171
- self.log.debug "Got nodes to SNMP check: %p" % [ nodes ]
172
-
173
- # Sanity check.
174
- #
175
- unless self.mode
176
- self.log.error "You must set the 'mode' for the SNMP monitor. (%s)" % [ VALID_MODES.join( ', ' ) ]
177
- return {}
178
- end
179
58
 
180
- # Create mapping of addresses back to node identifiers.
59
+ # Create mapping of addresses back to node identifiers,
60
+ # and retain any custom (overrides) config per node.
181
61
  #
182
- @identifiers = nodes.each_with_object({}) do |(identifier, props), hash|
62
+ @identifiers = {}
63
+ @results = {}
64
+ nodes.each_pair do |(identifier, props)|
183
65
  next unless props.key?( 'addresses' )
184
66
  address = props[ 'addresses' ].first
185
- hash[ address ] = identifier
67
+ self.identifiers[ address ] = [ identifier, props['config'] ]
186
68
  end
187
69
 
188
70
  # Perform the work!
189
71
  #
190
- threads = []
191
- self.identifiers.keys.each do |host|
192
- thr = Thread.new do
193
- Thread.current.abort_on_exception = true
194
- opts = {
195
- host: host,
196
- port: self.port,
197
- community: self.community,
198
- timeout: self.timeout,
199
- retries: self.retries
200
- }
72
+ mainstart = Time.now
73
+ threads = ThreadGroup.new
74
+ batchcount = nodes.size / Arborist::Monitor::SNMP.batchsize
75
+ self.log.debug "Starting SNMP run for %d nodes" % [ nodes.size ]
76
+
77
+ self.identifiers.keys.each_slice( Arborist::Monitor::SNMP.batchsize ).each_with_index do |slice, batch|
78
+ slicestart = Time.now
79
+ self.log.debug " %d hosts (batch %d of %d)" % [
80
+ slice.size,
81
+ batch + 1,
82
+ batchcount + 1
83
+ ]
84
+
85
+ slice.each do |host|
86
+ thr = Thread.new do
87
+ config = self.identifiers[ host ].last || {}
88
+ opts = {
89
+ peername: host,
90
+ port: config[ 'port' ] || Arborist::Monitor::SNMP.port,
91
+ version: config[ 'version' ] || Arborist::Monitor::SNMP.version,
92
+ community: config[ 'community' ] || Arborist::Monitor::SNMP.community,
93
+ timeout: config[ 'timeout' ] || Arborist::Monitor::SNMP.timeout,
94
+ retries: config[ 'retries' ] || Arborist::Monitor::SNMP.retries
95
+ }
201
96
 
202
- begin
203
- SNMP::Manager.open( opts ) do |snmp|
204
- case self.mode
205
- when :disk
206
- self.gather_disks( snmp, host )
207
- when :load
208
- self.gather_load( snmp, host )
209
- when :memory
210
- self.gather_free_memory( snmp, host )
211
- when :swap
212
- self.gather_swap( snmp, host )
213
- when :process
214
- self.gather_processlist( snmp, host )
215
- end
97
+ snmp = Net::SNMP::Session.open( opts )
98
+ begin
99
+ @system = snmp.get( IDENTIFICATION_OID ).varbinds.first.value
100
+ yield( host, snmp )
101
+
102
+ rescue Net::SNMP::TimeoutError, Net::SNMP::Error => err
103
+ self.log.error "%s: %s %s" % [ host, err.message, snmp.error_message ]
104
+ self.results[ host ] = {
105
+ error: "%s" % [ snmp.error_message ]
106
+ }
107
+ rescue => err
108
+ self.results[ host ] = {
109
+ error: "Uncaught exception. (%s: %s)" % [ err.class.name, err.message ]
110
+ }
111
+ ensure
112
+ snmp.close
216
113
  end
217
- rescue SNMP::RequestTimeout
218
- self.results[ host ] = {
219
- error: "Host is not responding to SNMP requests."
220
- }
221
- rescue StandardError => err
222
- self.results[ host ] = {
223
- error: "Network is not accessible. (%s: %s)" % [ err.class.name, err.message ]
224
- }
225
114
  end
115
+
116
+ threads.add( thr )
226
117
  end
227
- threads << thr
228
- end
229
118
 
230
- # Wait for thread completion
231
- threads.map( &:join )
119
+ # Wait for thread completions
120
+ threads.list.map( &:join )
121
+ self.log.debug " finished after %0.1f seconds." % [ Time.now - slicestart ]
122
+ end
123
+ self.log.debug "Completed SNMP run for %d nodes after %0.1f seconds." % [ nodes.size, Time.now - mainstart ]
232
124
 
233
125
  # Map everything back to identifier -> attribute(s), and send to the manager.
234
126
  #
235
127
  reply = self.results.each_with_object({}) do |(address, results), hash|
236
128
  identifier = self.identifiers[ address ] or next
237
- hash[ identifier ] = results
129
+ hash[ identifier.first ] = results
238
130
  end
239
- self.log.debug "Sending to manager: %p" % [ reply ]
240
131
  return reply
241
- end
242
-
243
-
244
- #########
245
- protected
246
- #########
247
-
248
- ### Collect the load information for +host+ from an existing
249
- ### (and open) +snmp+ connection.
250
- ###
251
- def gather_load( snmp, host )
252
- self.log.debug "Getting system load for: %s" % [ host ]
253
- load5 = snmp.get( SNMP::ObjectId.new( LOAD[:five_min] ) ).varbind_list.first.value.to_f
254
- self.log.debug " Load on %s: %0.2f" % [ host, load5 ]
255
-
256
- if load5 >= self.load_error_at
257
- self.results[ host ] = {
258
- error: "Load has exceeded %0.2f over a 5 minute average" % [ self.load_error_at ],
259
- load5: load5
260
- }
261
- else
262
- self.results[ host ] = { load5: load5 }
263
- end
264
- end
265
-
266
-
267
- ### Collect available memory information for +host+ from an existing
268
- ### (and open) +snmp+ connection.
269
- ###
270
- def gather_free_memory( snmp, host )
271
- self.log.debug "Getting available memory for: %s" % [ host ]
272
- mem_avail = snmp.get( SNMP::ObjectId.new( MEMORY[:mem_avail] ) ).varbind_list.first.value.to_f
273
- self.log.debug " Available memory on %s: %0.2f" % [ host, mem_avail ]
274
-
275
- if mem_avail <= self.mem_error_at
276
- self.results[ host ] = {
277
- error: "Available memory is under %0.1fMB" % [ self.mem_error_at.to_f / 1024 ],
278
- available_memory: mem_avail
279
- }
280
- else
281
- self.results[ host ] = { available_memory: mem_avail }
282
- end
283
- end
284
-
285
-
286
- ### Collect used swap information for +host+ from an existing (and
287
- ### open) +snmp+ connection.
288
- ###
289
- def gather_swap( snmp, host )
290
- self.log.debug "Getting used swap for: %s" % [ host ]
291
-
292
- swap_total = snmp.get( SNMP::ObjectId.new(MEMORY[:swap_total]) ).varbind_list.first.value.to_f
293
- swap_avail = snmp.get( SNMP::ObjectId.new(MEMORY[:swap_avail]) ).varbind_list.first.value.to_f
294
- swap_used = ( "%0.2f" % ((swap_avail / swap_total.to_f * 100 ) - 100).abs ).to_f
295
- self.log.debug " Swap in use on %s: %0.2f" % [ host, swap_used ]
296
-
297
- if swap_used >= self.swap_error_at
298
- self.results[ host ] = {
299
- error: "%0.2f%% swap in use" % [ swap_used ],
300
- swap_used: swap_used
301
- }
302
- else
303
- self.results[ host ] = { swap_used: swap_used }
304
- end
305
- end
306
-
307
-
308
- ### Collect mount point usage for +host+ from an existing (and open)
309
- #### +snmp+ connection.
310
- ###
311
- def gather_disks( snmp, host )
312
- self.log.debug "Getting disk information for %s" % [ host ]
313
- errors = []
314
- results = {}
315
- mounts = self.get_disk_percentages( snmp )
316
-
317
- mounts.each_pair do |path, percentage|
318
- if percentage >= self.storage_error_at
319
- errors << "Mount %s at %d%% capacity" % [ path, percentage ]
320
- end
321
- end
322
132
 
323
- results[ :mounts ] = mounts
324
- results[ :error ] = errors.join( ', ' ) unless errors.empty?
325
-
326
- self.results[ host ] = results
327
- end
328
-
329
-
330
- ### Collect running processes on +host+ from an existing (and open)
331
- #### +snmp+ connection.
332
- ###
333
- def gather_processlist( snmp, host )
334
- self.log.debug "Getting running process list for %s" % [ host ]
335
- procs = []
336
-
337
- snmp.walk([ PROCESS[:list], PROCESS[:args] ]) do |list|
338
- process = list[0].value.to_s
339
- args = list[1].value.to_s
340
- procs << "%s %s " % [ process, args ]
341
- end
342
-
343
- # Check against the running stuff, setting an error if
344
- # one isn't found.
345
- #
346
- errors = []
347
- Array( self.processes ).each do |process|
348
- process_r = Regexp.new( process )
349
- found = procs.find{|p| p.match(process_r) }
350
- errors << "Process '%s' is not running" % [ process, host ] unless found
351
- end
352
-
353
- self.log.debug " %d running processes" % [ procs.length ]
354
- if errors.empty?
355
- self.results[ host ] = {}
356
- else
357
- self.results[ host ] = { error: errors.join( ', ' ) }
358
- end
133
+ ensure
134
+ @identifiers = {}
135
+ @results = {}
359
136
  end
360
137
 
138
+ end # Arborist::Monitor::SNMP
361
139
 
362
- ### Given a SNMP object, return a hash of:
363
- ###
364
- ### device path => percentage full
365
- ###
366
- def get_disk_percentages( snmp )
367
-
368
- # Does this look like a windows system, or a net-snmp based one?
369
- system_type = snmp.get( SNMP::ObjectId.new( IDENTIFICATION_OID ) ).varbind_list.first.value
370
- disks = {}
371
-
372
- # Windows has it's own MIBs.
373
- #
374
- if system_type =~ /windows/i
375
- snmp.walk( STORAGE_WINDOWS ) do |list|
376
- next unless list[0].value.to_s == WINDOWS_DEVICE
377
- disks[ list[1].value.to_s ] = ( list[3].value.to_f / list[2].value.to_f ) * 100
378
- end
379
- return disks
380
- end
381
-
382
- # Everything else.
383
- #
384
- snmp.walk( STORAGE_NET_SNMP ) do |list|
385
- mount = list[0].value.to_s
386
- next if mount == 'noSuchInstance'
387
-
388
- next if list[2].value.to_s == 'noSuchInstance'
389
- used = list[2].value.to_i
390
-
391
- typeoid = list[1].value.join('.').to_s
392
- next if typeoid =~ STORAGE_IGNORE
393
- next if mount =~ /\/(?:dev|proc)$/
394
-
395
- self.log.debug " %s -> %s -> %s" % [ mount, typeoid, used ]
396
- disks[ mount ] = used
397
- end
398
-
399
- return disks
400
- end
401
- end # class Arborist::Monitor::SNMP
140
+ require 'arborist/monitor/snmp/cpu'
141
+ require 'arborist/monitor/snmp/disk'
142
+ require 'arborist/monitor/snmp/process'
143
+ require 'arborist/monitor/snmp/memory'
402
144
 
@@ -0,0 +1,140 @@
1
+ # -*- ruby -*-
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+
4
+ require 'arborist/monitor/snmp' unless defined?( Arborist::Monitor::SNMP )
5
+
6
+ # Machine load/cpu checks.
7
+ #
8
+ # Sets current 1, 5, and 15 minute loads under the 'load' attribute,
9
+ # and calculates/warns on cpu overutilization.
10
+ #
11
+ class Arborist::Monitor::SNMP::CPU
12
+ include Arborist::Monitor::SNMP
13
+
14
+ extend Configurability, Loggability
15
+ log_to :arborist_snmp
16
+
17
+ # OIDS for discovering system load.
18
+ #
19
+ OIDS = {
20
+ load: '1.3.6.1.4.1.2021.10.1.3',
21
+ cpu: '1.3.6.1.2.1.25.3.3.1.2'
22
+ }
23
+
24
+ # When walking load OIDS, the iterator count matches
25
+ # these labels.
26
+ #
27
+ LOADKEYS = {
28
+ 1 => :load1,
29
+ 2 => :load5,
30
+ 3 => :load15
31
+ }
32
+
33
+
34
+ # Global defaults for instances of this monitor
35
+ #
36
+ configurability( 'arborist.snmp.cpu' ) do
37
+ # What overutilization percentage qualifies as a warning
38
+ setting :warn_at, default: 80
39
+ end
40
+
41
+
42
+ ### Return the properties used by this monitor.
43
+ ###
44
+ def self::node_properties
45
+ return USED_PROPERTIES
46
+ end
47
+
48
+
49
+ ### Class #run creates a new instance and immediately runs it.
50
+ ###
51
+ def self::run( nodes )
52
+ return new.run( nodes )
53
+ end
54
+
55
+
56
+ ### Perform the monitoring checks.
57
+ ###
58
+ def run( nodes )
59
+ super do |host, snmp|
60
+ self.find_load( host, snmp )
61
+ end
62
+ end
63
+
64
+
65
+ #########
66
+ protected
67
+ #########
68
+
69
+ ### Return system CPU data.
70
+ ###
71
+ def cpu( snmp )
72
+ return snmp.walk( OIDS[:cpu] )
73
+ end
74
+
75
+
76
+ ### Find load data, add additional niceties for reporting.
77
+ ###
78
+ def format_load( snmp )
79
+ info = { cpu: {}, load: {} }
80
+ cpus = self.cpu( snmp )
81
+
82
+ info[ :cpu ][ :count ] = cpus.size
83
+
84
+ # Windows SNMP doesn't have a concept of "load" over time,
85
+ # so we have to just use the current averaged CPU usage.
86
+ #
87
+ # This means that windows machines will very likely want to
88
+ # adjust the default "overutilization" number, considering
89
+ # it's really just how much of the CPU is used at the time of
90
+ # the monitor run, along with liberal use of the Observer "only
91
+ # alert after X events" pragmas.
92
+ #
93
+ if self.system =~ /windows\s+/i
94
+ info[ :cpu ][ :usage ] = cpus.values.inject( :+ ).to_f / cpus.size
95
+ info[ :message ] = "System is %0.1f%% in use." % [ info[ :cpu ][ :usage ] ]
96
+
97
+ # UCDavis stuff is better for alerting only after there has been
98
+ # an extended load event. Use the 5 minute average to avoid
99
+ # state changes on transient spikes.
100
+ #
101
+ else
102
+ snmp.walk( OIDS[:load] ).each_with_index do |(_, value), idx|
103
+ next unless LOADKEYS[ idx + 1 ]
104
+ info[ :load ][ LOADKEYS[idx + 1] ] = value.to_f
105
+ end
106
+
107
+ percentage = (( ( info[:load][ :load5 ] / cpus.size ) - 1 ) * 100 ).round( 1 )
108
+
109
+ if percentage < 0
110
+ info[ :message ] = "System is %0.1f%% idle." % [ percentage.abs ]
111
+ info[ :cpu ][ :usage ] = percentage + 100
112
+ else
113
+ info[ :message ] = "System is %0.1f%% overloaded." % [ percentage ]
114
+ info[ :cpu ][ :usage ] = percentage
115
+ end
116
+ end
117
+
118
+ return info
119
+ end
120
+
121
+
122
+ ### Collect the load information for +host+ from an existing
123
+ ### (and open) +snmp+ connection.
124
+ ###
125
+ def find_load( host, snmp )
126
+ info = self.format_load( snmp )
127
+
128
+ config = identifiers[ host ].last || {}
129
+ warn_at = config[ 'warn_at' ] || self.class.warn_at
130
+ usage = info.dig( :cpu, :usage ) || 0
131
+
132
+ if usage >= warn_at
133
+ info[ :warning ] = "%0.1f utilization exceeds %0.1f percent" % [ usage, warn_at ]
134
+ end
135
+
136
+ self.results[ host ] = info
137
+ end
138
+
139
+ end # class Arborist::Monitor::SNMP::CPU
140
+
@@ -0,0 +1,183 @@
1
+ # -*- ruby -*-
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+
4
+ require 'arborist/monitor/snmp' unless defined?( Arborist::Monitor::SNMP )
5
+
6
+ # Disk capacity checks.
7
+ #
8
+ # Sets all configured mounts with their current usage percentage
9
+ # in an attribute named "mounts".
10
+ #
11
+ class Arborist::Monitor::SNMP::Disk
12
+ include Arborist::Monitor::SNMP
13
+
14
+ extend Configurability, Loggability
15
+ log_to :arborist_snmp
16
+
17
+ # OIDS required to pull disk information from net-snmp.
18
+ #
19
+ STORAGE_NET_SNMP = {
20
+ path: '1.3.6.1.4.1.2021.9.1.2',
21
+ percent: '1.3.6.1.4.1.2021.9.1.9',
22
+ type: '1.3.6.1.2.1.25.3.8.1.4'
23
+ }
24
+
25
+ # The OID that matches a local windows hard disk.
26
+ #
27
+ WINDOWS_DEVICES = [
28
+ '1.3.6.1.2.1.25.2.1.4', # local disk
29
+ '1.3.6.1.2.1.25.2.1.7' # removables, but we have to include them for iscsi mounts
30
+ ]
31
+
32
+ # OIDS required to pull disk information from Windows.
33
+ #
34
+ STORAGE_WINDOWS = {
35
+ type: '1.3.6.1.2.1.25.2.3.1.2',
36
+ path: '1.3.6.1.2.1.25.2.3.1.3',
37
+ total: '1.3.6.1.2.1.25.2.3.1.5',
38
+ used: '1.3.6.1.2.1.25.2.3.1.6'
39
+ }
40
+
41
+ # The fallback warning capacity.
42
+ WARN_AT = 90
43
+
44
+
45
+ # Configurability API
46
+ #
47
+ configurability( 'arborist.snmp.disk' ) do
48
+ # What percentage qualifies as a warning
49
+ setting :warn_at, default: WARN_AT
50
+
51
+ # If non-empty, only these paths are included in checks.
52
+ #
53
+ setting :include do |val|
54
+ if val
55
+ mounts = Array( val ).map{|m| Regexp.new(m) }
56
+ Regexp.union( mounts )
57
+ end
58
+ end
59
+
60
+ # Paths to exclude from checks
61
+ #
62
+ setting :exclude,
63
+ default: [ '^/dev(/.+)?$', '^/net(/.+)?$', '^/proc$', '^/run$', '^/sys/' ] do |val|
64
+ mounts = Array( val ).map{|m| Regexp.new(m) }
65
+ Regexp.union( mounts )
66
+ end
67
+ end
68
+
69
+
70
+ ### Return the properties used by this monitor.
71
+ ###
72
+ def self::node_properties
73
+ return USED_PROPERTIES
74
+ end
75
+
76
+
77
+ ### Class #run creates a new instance and immediately runs it.
78
+ ###
79
+ def self::run( nodes )
80
+ return new.run( nodes )
81
+ end
82
+
83
+
84
+ ### Perform the monitoring checks.
85
+ ###
86
+ def run( nodes )
87
+ super do |host, snmp|
88
+ self.gather_disks( host, snmp )
89
+ end
90
+ end
91
+
92
+
93
+ #########
94
+ protected
95
+ #########
96
+
97
+ ### Collect mount point usage for +host+ from an existing (and open)
98
+ ### +snmp+ connection.
99
+ ###
100
+ def gather_disks( host, snmp )
101
+ mounts = self.system =~ /windows\s+/i ? self.windows_disks( snmp ) : self.unix_disks( snmp )
102
+ config = self.identifiers[ host ].last || {}
103
+ warn_at = config[ 'warn_at' ] || self.class.warn_at
104
+
105
+ includes = self.format_mounts( config, 'include' ) || self.class.include
106
+ excludes = self.format_mounts( config, 'exclude' ) || self.class.exclude
107
+
108
+ mounts.reject! do |path, percentage|
109
+ excludes.match( path ) || ( includes && ! includes.match( path ) )
110
+ end
111
+
112
+ errors = []
113
+ warnings = []
114
+ mounts.each_pair do |path, percentage|
115
+
116
+ warn = begin
117
+ if warn_at.is_a?( Hash )
118
+ warn_at[ path ] || WARN_AT
119
+ else
120
+ warn_at
121
+ end
122
+ end
123
+
124
+ self.log.debug "%s:%s -> at %d, warn at %d" % [ host, path, percentage, warn ]
125
+
126
+ if percentage >= warn.to_i
127
+ if percentage >= 100
128
+ errors << "%s at %d%% capacity" % [ path, percentage ]
129
+ else
130
+ warnings << "%s at %d%% capacity" % [ path, percentage ]
131
+ end
132
+ end
133
+ end
134
+
135
+ self.results[ host ] = { mounts: mounts }
136
+ self.results[ host ][ :error ] = errors.join(', ') unless errors.empty?
137
+ self.results[ host ][ :warning ] = warnings.join(', ') unless warnings.empty?
138
+ end
139
+
140
+
141
+ ### Return a single regexp for the 'include' or 'exclude' section of
142
+ ### resource node's +config+, or nil if nonexistent.
143
+ ###
144
+ def format_mounts( config, section )
145
+ list = config[ section ] || return
146
+ mounts = Array( list ).map{|m| Regexp.new(m) }
147
+ return Regexp.union( mounts )
148
+ end
149
+
150
+
151
+ ### Fetch information for Windows systems.
152
+ ###
153
+ def windows_disks( snmp )
154
+ raw = snmp.get_bulk([
155
+ STORAGE_WINDOWS[:path],
156
+ STORAGE_WINDOWS[:type],
157
+ STORAGE_WINDOWS[:total],
158
+ STORAGE_WINDOWS[:used]
159
+ ]).varbinds.map( &:value )
160
+
161
+ disks = {}
162
+ raw.each_slice( 4 ) do |device|
163
+ next unless device[1].respond_to?( :oid ) && WINDOWS_DEVICES.include?( device[1].oid )
164
+ next if device[2].zero?
165
+ disks[ device[0] ] = (( device[3].to_f / device[2] ) * 100).round( 1 )
166
+ end
167
+
168
+ return disks
169
+ end
170
+
171
+
172
+ ### Fetch information for Unix/MacOS systems.
173
+ ###
174
+ def unix_disks( snmp )
175
+ raw = snmp.get_bulk([
176
+ STORAGE_NET_SNMP[:path],
177
+ STORAGE_NET_SNMP[:percent] ]).varbinds.map( &:value )
178
+
179
+ return Hash[ *raw ]
180
+ end
181
+
182
+ end # class Arborist::Monitor::SNMP::Disk
183
+
@@ -0,0 +1,180 @@
1
+ # -*- ruby -*-
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+
4
+ require 'arborist/monitor/snmp' unless defined?( Arborist::Monitor::SNMP )
5
+
6
+ # SNMP memory and swap utilization checks.
7
+ #
8
+ # Set 'usage' and 'available' keys as properties, in percentage/GBs,
9
+ # respectively.
10
+ #
11
+ # By default, doesn't warn on memory usage, only swap, since
12
+ # that's more indicitive of a problem. You can still set the
13
+ # 'physical_warn_at' key to force warnings on ram usage, for embedded
14
+ # systems or other similar things without virtual memory.
15
+ #
16
+ class Arborist::Monitor::SNMP::Memory
17
+ include Arborist::Monitor::SNMP
18
+
19
+ extend Configurability, Loggability
20
+ log_to :arborist_snmp
21
+
22
+ # OIDS for discovering memory usage.
23
+ #
24
+ MEMORY = {
25
+ total: '1.3.6.1.4.1.2021.4.5.0',
26
+ avail: '1.3.6.1.4.1.2021.4.6.0',
27
+ windows: {
28
+ label: '1.3.6.1.2.1.25.2.3.1.3',
29
+ units: '1.3.6.1.2.1.25.2.3.1.4',
30
+ total: '1.3.6.1.2.1.25.2.3.1.5',
31
+ used: '1.3.6.1.2.1.25.2.3.1.6'
32
+ }
33
+ }
34
+
35
+ # OIDS for discovering swap usage.
36
+ #
37
+ SWAP = {
38
+ total: '1.3.6.1.4.1.2021.4.3.0',
39
+ avail: '1.3.6.1.4.1.2021.4.4.0'
40
+ }
41
+
42
+ # Global defaults for instances of this monitor
43
+ #
44
+ configurability( 'arborist.snmp.memory' ) do
45
+ # What memory usage percentage qualifies as a warning
46
+ setting :physical_warn_at, default: nil
47
+
48
+ # What swap usage percentage qualifies as a warning
49
+ setting :swap_warn_at, default: 60
50
+ end
51
+
52
+
53
+ ### Return the properties used by this monitor.
54
+ ###
55
+ def self::node_properties
56
+ return USED_PROPERTIES
57
+ end
58
+
59
+
60
+
61
+ ### Class #run creates a new instance and immediately runs it.
62
+ ###
63
+ def self::run( nodes )
64
+ return new.run( nodes )
65
+ end
66
+
67
+
68
+ ### Perform the monitoring checks.
69
+ ###
70
+ def run( nodes )
71
+ super do |host, snmp|
72
+ self.gather_memory( host, snmp )
73
+ end
74
+ end
75
+
76
+
77
+ #########
78
+ protected
79
+ #########
80
+
81
+ ### Collect available memory information for +host+ from an existing
82
+ ### (and open) +snmp+ connection.
83
+ ###
84
+ def gather_memory( host, snmp )
85
+ info = self.system =~ /windows\s+/i ? self.get_windows( snmp ) : self.get_mem( snmp )
86
+
87
+ config = identifiers[ host ].last || {}
88
+ physical_warn_at = config[ 'physical_warn_at' ] || self.class.physical_warn_at
89
+ swap_warn_at = config[ 'swap_warn_at' ] || self.class.swap_warn_at
90
+
91
+ self.log.debug "Memory data on %s: %p" % [ host, info ]
92
+ memory, swap = info[:memory], info[:swap]
93
+ self.results[ host ] = { memory: memory, swap: swap }
94
+
95
+ memusage = memory[ :usage ].to_i
96
+ if physical_warn_at && memusage >= physical_warn_at
97
+ self.results[ host ][ :warning ] = "%0.1f memory utilization exceeds %0.1f percent" % [
98
+ memusage,
99
+ physical_warn_at
100
+ ]
101
+ end
102
+
103
+ swapusage = swap[ :usage ].to_i
104
+ if swapusage >= swap_warn_at
105
+ self.results[ host ][ :warning ] = "%0.1f swap utilization exceeds %0.1f percent" % [
106
+ swapusage,
107
+ swap_warn_at
108
+ ]
109
+ end
110
+ end
111
+
112
+
113
+ ### Return a hash of usage percentage in use, and free mem in
114
+ ### megs.
115
+ ###
116
+ def get_mem( snmp )
117
+ info = {}
118
+ info[ :memory ] = self.calc_memory( snmp, MEMORY )
119
+ info[ :swap ] = self.calc_memory( snmp, SWAP )
120
+
121
+ return info
122
+ end
123
+
124
+
125
+ ### Windows appends virtual and physical memory onto the last two items
126
+ ### of the storage iterator, because that made sense in someone's mind.
127
+ ### Walk the whole oid tree, and get the values we're after, return
128
+ ### a hash of usage percentage in use and free mem in megs.
129
+ ###
130
+ def get_windows( snmp )
131
+ info = { memory: {}, swap: {} }
132
+ mem_idx, swap_idx = nil
133
+
134
+ snmp.walk( MEMORY[:windows][:label] ).each_with_index do |(_, val), i|
135
+ mem_idx = i + 1 if val =~ /physical memory/i
136
+ swap_idx = i + 1 if val =~ /virtual memory/i
137
+ end
138
+ return info unless mem_idx
139
+
140
+ info[ :memory ] = self.calc_windows_memory( snmp, mem_idx )
141
+ info[ :swap ] = self.calc_windows_memory( snmp, swap_idx )
142
+
143
+ return info
144
+ end
145
+
146
+
147
+ ### Format usage and available amount, given an OID hash.
148
+ ###
149
+ def calc_memory( snmp, oids )
150
+ info = { usage: 0, available: 0 }
151
+ avail = snmp.get( oids[:avail] ).varbinds.first.value.to_f
152
+ total = snmp.get( oids[:total] ).varbinds.first.value.to_f
153
+ used = total - avail
154
+
155
+ return info if avail.zero?
156
+
157
+ info[ :usage ] = (( used / total ) * 100 ).round( 2 )
158
+ info[ :available ] = (( total - used ) / 1024 ).round( 2 )
159
+ return info
160
+ end
161
+
162
+
163
+ ### Format usage and available amount for windows.
164
+ ###
165
+ def calc_windows_memory( snmp, idx)
166
+ info = { usage: 0, available: 0 }
167
+ return info unless idx
168
+
169
+ units = snmp.get( MEMORY[:windows][:units] + ".#{idx}" ).varbinds.first.value
170
+ total = snmp.get( MEMORY[:windows][:total] + ".#{idx}" ).varbinds.first.value.to_f * units
171
+ used = snmp.get( MEMORY[:windows][:used] + ".#{idx}" ).varbinds.first.value.to_f * units
172
+
173
+ info[ :usage ] = (( used / total ) * 100 ).round( 2 )
174
+ info[ :available ] = (( total - used ) / 1024 / 1024 ).round( 2 )
175
+ return info
176
+ end
177
+
178
+
179
+ end # class Arborist::Monitor::SNMP::Memory
180
+
@@ -0,0 +1,123 @@
1
+ # -*- ruby -*-
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+
4
+ require 'arborist/monitor/snmp' unless defined?( Arborist::Monitor::SNMP )
5
+
6
+ # SNMP running process checks.
7
+ #
8
+ # This only checks running userland processes.
9
+ #
10
+ class Arborist::Monitor::SNMP::Process
11
+ include Arborist::Monitor::SNMP
12
+
13
+ extend Configurability, Loggability
14
+ log_to :arborist_snmp
15
+
16
+
17
+ # OIDS for discovering running processes.
18
+ # Of course, Windows does it slightly differently.
19
+ #
20
+ PROCESS = {
21
+ netsnmp: {
22
+ list: '1.3.6.1.2.1.25.4.2.1.4',
23
+ args: '1.3.6.1.2.1.25.4.2.1.5'
24
+ },
25
+ windows: {
26
+ list: '1.3.6.1.2.1.25.4.2.1.2',
27
+ path: '1.3.6.1.2.1.25.4.2.1.4',
28
+ args: '1.3.6.1.2.1.25.4.2.1.5'
29
+ }
30
+ }
31
+
32
+
33
+ # Global defaults for instances of this monitor
34
+ #
35
+ configurability( 'arborist.snmp.processes' ) do
36
+ # Default list of processes to check for
37
+ setting :check, default: [] do |val|
38
+ Array( val )
39
+ end
40
+ end
41
+
42
+
43
+ ### Return the properties used by this monitor.
44
+ ###
45
+ def self::node_properties
46
+ return USED_PROPERTIES
47
+ end
48
+
49
+
50
+ ### Class #run creates a new instance and immediately runs it.
51
+ ###
52
+ def self::run( nodes )
53
+ return new.run( nodes )
54
+ end
55
+
56
+
57
+ ### Perform the monitoring checks.
58
+ ###
59
+ def run( nodes )
60
+ super do |host, snmp|
61
+ self.gather_processlist( host, snmp )
62
+ end
63
+ end
64
+
65
+
66
+ #########
67
+ protected
68
+ #########
69
+
70
+ ### Collect running processes on +host+ from an existing (and open)
71
+ #### +snmp+ connection.
72
+ ###
73
+ def gather_processlist( host, snmp )
74
+ config = self.identifiers[ host ].last || {}
75
+ errors = []
76
+ procs = self.system =~ /windows\s+/i ? self.get_windows( snmp ) : self.get_procs( snmp )
77
+
78
+ self.log.debug "Running processes for host: %s: %p" % [ host, procs ]
79
+ self.results[ host ] = { count: procs.size }
80
+
81
+ # Check against what is running.
82
+ #
83
+ Array( config['processes'] || self.class.check ).each do |process|
84
+ process_r = Regexp.new( process )
85
+ found = procs.find{|p| p.match(process_r) }
86
+ errors << "'%s' is not running" % [ process ] unless found
87
+ end
88
+
89
+ self.results[ host ][ :error ] = errors.join( ', ' ) unless errors.empty?
90
+ end
91
+
92
+
93
+ ### Parse OIDS and return an Array of running processes.
94
+ ### Windows specific behaviors.
95
+ ###
96
+ def get_windows( snmp )
97
+ oids = [ PROCESS[:windows][:path], PROCESS[:windows][:list], PROCESS[:windows][:args] ]
98
+ return snmp.walk( oids ).each_slice( 3 ). each_with_object( [] ) do |vals, acc|
99
+ path, process, args = vals[0][1], vals[1][1], vals[2][1]
100
+ next if path.empty?
101
+
102
+ process = "%s%s" % [ path, process ]
103
+ process << " %s" % [ args ] unless args.empty?
104
+ acc << process
105
+ end
106
+ end
107
+
108
+
109
+ ### Parse OIDS and return an Array of running processes.
110
+ ###
111
+ def get_procs( snmp )
112
+ oids = [ PROCESS[:netsnmp][:list], PROCESS[:netsnmp][:args] ]
113
+ return snmp.walk( oids ).each_slice( 2 ).each_with_object( [] ) do |vals, acc|
114
+ process, args = vals[0][1], vals[1][1]
115
+ next if process.empty?
116
+
117
+ process << " %s" % [ args ] unless args.empty?
118
+ acc << process
119
+ end
120
+ end
121
+
122
+ end # class Arborist::Monitor::SNMP::Process
123
+
@@ -0,0 +1,35 @@
1
+ # -*- ruby -*-
2
+ #encoding: utf-8
3
+
4
+ require 'loggability'
5
+ require 'arborist'
6
+
7
+
8
+ # Various monitoring checks using SNMP, for the Arborist monitoring toolkit.
9
+ module Arborist::SNMP
10
+ extend Loggability
11
+
12
+ # Loggability API -- set up a log host for this library
13
+ log_as :arborist_snmp
14
+
15
+
16
+ # Package version
17
+ VERSION = '0.4.0'
18
+
19
+ # Version control revision
20
+ REVISION = %q$Revision: e0b7c95a154f $
21
+
22
+
23
+ ### Return the name of the library with the version, and optionally the build ID if
24
+ ### +include_build+ is true.
25
+ def self::version_string( include_build: false )
26
+ str = "%p v%s" % [ self, VERSION ]
27
+ str << ' (' << REVISION.strip << ')' if include_build
28
+ return str
29
+ end
30
+
31
+
32
+ require 'arborist/monitor/snmp'
33
+
34
+ end # module Arborist::SNMP
35
+
metadata CHANGED
@@ -1,11 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arborist-snmp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.pre20161005111600
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
- - Mahlon E. Smith
8
- - Michael Granger
7
+ - Mahlon E. Smith <mahlon@martini.nu>
8
+ - Michael Granger <ged@faeriemud.org>
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain:
@@ -13,7 +13,7 @@ cert_chain:
13
13
  -----BEGIN CERTIFICATE-----
14
14
  MIIDbDCCAlSgAwIBAgIBATANBgkqhkiG9w0BAQUFADA+MQ8wDQYDVQQDDAZtYWhs
15
15
  b24xFzAVBgoJkiaJk/IsZAEZFgdtYXJ0aW5pMRIwEAYKCZImiZPyLGQBGRYCbnUw
16
- HhcNMTYwNjI5MjMzMzI2WhcNMTcwNjI5MjMzMzI2WjA+MQ8wDQYDVQQDDAZtYWhs
16
+ HhcNMTcxMTIyMjIyMTAyWhcNMTgxMTIyMjIyMTAyWjA+MQ8wDQYDVQQDDAZtYWhs
17
17
  b24xFzAVBgoJkiaJk/IsZAEZFgdtYXJ0aW5pMRIwEAYKCZImiZPyLGQBGRYCbnUw
18
18
  ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDpXGN0YbMVpYv4EoiCxpQw
19
19
  sxKdyhlkvpvENUkpEhbpnEuMKXgUfRHO4T/vBZf0h8eYgwnrHCRhAeIqesFKfoj9
@@ -24,52 +24,55 @@ cert_chain:
24
24
  AgMBAAGjdTBzMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBRY8ea6
25
25
  +6kAaW7ukKph2/4MTAD8/TAcBgNVHREEFTATgRFtYWhsb25AbWFydGluaS5udTAc
26
26
  BgNVHRIEFTATgRFtYWhsb25AbWFydGluaS5udTANBgkqhkiG9w0BAQUFAAOCAQEA
27
- EU39m2ZKYqAAu71bwjWl5zEAk0aE4ojMLIMWpWE6IwCr9FZVpC1B+LyEboWFljId
28
- R0udISkfM+kQ3FRzmnwwQLaYJyhDPEWbQ1O6P6wHCaUQ23A1P++dZf8PWuZkS6Dn
29
- C1q7Zq4EAZEBLUSK69iPP4jCLjIp3YBQ88D1/egA+hkrR/19m236PvhhaM9FTgQv
30
- LtL61M3ZtlTanoXiNbXRXwRnODzvjFpQRiiBiazCDBYj8oYDsNj+qNw/iZlZlzw5
31
- F6uYXeS4YCZP453ZcpgZkXo3F5RheTrkdf04DMwUpQPMKog9QmRSTlCxzH69kivQ
32
- IfRp+58YwWwtAIQPZoY6Rg==
27
+ 00FljTeSNaYUqJ59yLRA+i43wVNiO4ETQQu6fYQCPns12Sm90spOJb3SmTDkJ7CY
28
+ dixOQg5A3Et1LVS+Z/YfH1TAbb50oTWbZbTW2JknHS0hohq3UF1pvbkk1niZ26er
29
+ skJ352MUfcyaUkQyMmCjL/BpkDutYH5OCGh+FmK8+mH7SoC9Nr48WwH2prVdHs3y
30
+ OMWFgB33sXdj1XqOd2Rw1WPgAeMeDqWeIrRMpUhNZOwroaA1MAr60f9NIYxua/vx
31
+ n0YyneERGuHPSRZFgo72tGOqLpAlWnhPxRNqnayZmsg3hPPI87B6MTUI2UQ7VUdh
32
+ UrSf3b+cPoC8PNfjp8zsdw==
33
33
  -----END CERTIFICATE-----
34
- date: 2016-10-05 00:00:00.000000000 Z
34
+ date: 2018-04-04 00:00:00.000000000 Z
35
35
  dependencies:
36
36
  - !ruby/object:Gem::Dependency
37
- name: snmp
37
+ name: arborist
38
38
  requirement: !ruby/object:Gem::Requirement
39
39
  requirements:
40
40
  - - "~>"
41
41
  - !ruby/object:Gem::Version
42
- version: '1.2'
42
+ version: '0.1'
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
46
46
  requirements:
47
47
  - - "~>"
48
48
  - !ruby/object:Gem::Version
49
- version: '1.2'
49
+ version: '0.1'
50
50
  - !ruby/object:Gem::Dependency
51
- name: arborist
51
+ name: net-snmp2
52
52
  requirement: !ruby/object:Gem::Requirement
53
53
  requirements:
54
54
  - - "~>"
55
55
  - !ruby/object:Gem::Version
56
- version: '0'
56
+ version: '0.3'
57
57
  type: :runtime
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
60
60
  requirements:
61
61
  - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: '0'
64
- description: "\tThis library adds common SNMP support to Arborist monitors.\n"
65
- email:
66
- - mahlon@martini.nu
67
- - ged@faeriemud.org
63
+ version: '0.3'
64
+ description: "\tThis library adds common SNMP resource support to Arborist monitors.\n"
65
+ email: mahlon@martini.nu
68
66
  executables: []
69
67
  extensions: []
70
68
  extra_rdoc_files: []
71
69
  files:
72
70
  - lib/arborist/monitor/snmp.rb
71
+ - lib/arborist/monitor/snmp/cpu.rb
72
+ - lib/arborist/monitor/snmp/disk.rb
73
+ - lib/arborist/monitor/snmp/memory.rb
74
+ - lib/arborist/monitor/snmp/process.rb
75
+ - lib/arborist/snmp.rb
73
76
  homepage: http://bitbucket.org/mahlon/Arborist-SNMP
74
77
  licenses:
75
78
  - BSD-3-Clause
@@ -85,13 +88,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
85
88
  version: '2'
86
89
  required_rubygems_version: !ruby/object:Gem::Requirement
87
90
  requirements:
88
- - - ">"
91
+ - - ">="
89
92
  - !ruby/object:Gem::Version
90
- version: 1.3.1
93
+ version: '0'
91
94
  requirements: []
92
95
  rubyforge_project:
93
96
  rubygems_version: 2.5.1
94
97
  signing_key:
95
98
  specification_version: 4
96
- summary: Common SNMP support for Arborist
99
+ summary: SNMP support for Arborist monitors
97
100
  test_files: []
checksums.yaml.gz.sig DELETED
@@ -1,4 +0,0 @@
1
- �d���v�b���0L�:!���������ܷ�6�
2
- \U`\0ףc� x��r�8Y=�1� �o���݋���G�D�����e�J"�%�ع��/
3
- s˲"��z�˴Ķ���JҺ�1pU�֬��� �_b#�V"A
4
- !€��S�y*�/|�m���`�겶�/#pR�x���[`�'�N�b�m��'X��2�Ju�:��I�G�OS�����XS��e��rgr�˻4�|�Q�n�[۬�ÑU���
data.tar.gz.sig DELETED
Binary file
metadata.gz.sig DELETED
@@ -1,2 +0,0 @@
1
- ��U�ޥ�w�����$A5��ڻ�^|�ܕ���bM�:�*��QmA���1-b��,T�Ԑt݃]ٖ<¨��L@�[���!���0�ݸ�*�M�e@"ǔ&m{�9N�4�?����ŗS~��+�}�t���*� l���k��\���s �qlʈ������35&�*U>o"������
2
- *ׇ����)d}�!J�0Oʼn;[ꓦk�{� E*qa\E�.��&���BC�*�}������$ʇ���h"l�gb@��r/v