arborist-snmp 0.4.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +1 -0
- data.tar.gz.sig +0 -0
- data/lib/arborist/monitor/snmp.rb +17 -21
- data/lib/arborist/monitor/snmp/cpu.rb +11 -19
- data/lib/arborist/monitor/snmp/disk.rb +80 -38
- data/lib/arborist/monitor/snmp/memory.rb +7 -7
- data/lib/arborist/monitor/snmp/process.rb +29 -13
- data/lib/arborist/monitor/snmp/ups.rb +10 -0
- data/lib/arborist/monitor/snmp/ups/battery.rb +136 -0
- data/lib/arborist/snmp.rb +3 -2
- metadata +32 -18
- metadata.gz.sig +1 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1249bdfdca3585ad83e5257085356af249e49ddd5691d1e5b8cd77f792c81c9a
|
4
|
+
data.tar.gz: 3b85bd63b62caa5554ee76f3737556b07e970999cc7ddf0153fd51169e4b7e54
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61a8a588eb87e99848c41c312b3eaa690a4c65ffb13d565266f89bdb850790822d5ea26132e69e26133f5bfae1eb5ed4e57c7bbd0f06c52d9d0dacc034c1d115
|
7
|
+
data.tar.gz: bfd1b4c4b1d7feb5c6b1726f4c73748d0e357a27983f69c8441a5882495d9d725dc04bab7550da22827fd1a7fe3316f703dc75e409fef7d048f5a9847aa243c3
|
checksums.yaml.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
)� ���1��v�Fe��W�o*���O�Q��{4��@��"ԅ�|���!Tڱ���t&@V0�?jm_�P�`����{[�Z��u�q/y��Y���=����K�N�L�J���5�]ː6�\Nω�Z�SNKkv�2d6���L-�+T?�^�z��_��o�ݏ��7�b�d)��k
|
data.tar.gz.sig
ADDED
Binary file
|
@@ -3,7 +3,7 @@
|
|
3
3
|
#encoding: utf-8
|
4
4
|
|
5
5
|
require 'arborist/monitor' unless defined?( Arborist::Monitor )
|
6
|
-
require '
|
6
|
+
require 'netsnmp'
|
7
7
|
|
8
8
|
# SNMP checks for Arborist. Requires an SNMP agent to be installed
|
9
9
|
# on target machine, and the various "pieces" enabled for your platform.
|
@@ -38,13 +38,6 @@ module Arborist::Monitor::SNMP
|
|
38
38
|
setting :batchsize, default: 25
|
39
39
|
end
|
40
40
|
|
41
|
-
# Indicate to FFI that we're using threads.
|
42
|
-
Net::SNMP.thread_safe = true
|
43
|
-
|
44
|
-
|
45
|
-
# The system type, as advertised.
|
46
|
-
attr_reader :system
|
47
|
-
|
48
41
|
# The mapping of addresses back to node identifiers.
|
49
42
|
attr_reader :identifiers
|
50
43
|
|
@@ -64,7 +57,7 @@ module Arborist::Monitor::SNMP
|
|
64
57
|
nodes.each_pair do |(identifier, props)|
|
65
58
|
next unless props.key?( 'addresses' )
|
66
59
|
address = props[ 'addresses' ].first
|
67
|
-
self.identifiers[ address ] = [ identifier, props
|
60
|
+
self.identifiers[ address ] = [ identifier, props ]
|
68
61
|
end
|
69
62
|
|
70
63
|
# Perform the work!
|
@@ -86,7 +79,7 @@ module Arborist::Monitor::SNMP
|
|
86
79
|
thr = Thread.new do
|
87
80
|
config = self.identifiers[ host ].last || {}
|
88
81
|
opts = {
|
89
|
-
|
82
|
+
host: host,
|
90
83
|
port: config[ 'port' ] || Arborist::Monitor::SNMP.port,
|
91
84
|
version: config[ 'version' ] || Arborist::Monitor::SNMP.version,
|
92
85
|
community: config[ 'community' ] || Arborist::Monitor::SNMP.community,
|
@@ -94,22 +87,17 @@ module Arborist::Monitor::SNMP
|
|
94
87
|
retries: config[ 'retries' ] || Arborist::Monitor::SNMP.retries
|
95
88
|
}
|
96
89
|
|
97
|
-
snmp = Net::SNMP::Session.open( opts )
|
98
90
|
begin
|
99
|
-
|
100
|
-
|
91
|
+
NETSNMP::Client.new( opts ) do |snmp|
|
92
|
+
Thread.current[ :system ] = snmp.get( oid: IDENTIFICATION_OID )
|
93
|
+
yield( host, snmp )
|
94
|
+
end
|
101
95
|
|
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
96
|
rescue => err
|
97
|
+
self.log.error "%s: %s\n%s" % [ host, err.message, err.backtrace.join("\n ") ]
|
108
98
|
self.results[ host ] = {
|
109
|
-
error: "
|
99
|
+
error: "Exception (%s: %s)" % [ err.class.name, err.message ]
|
110
100
|
}
|
111
|
-
ensure
|
112
|
-
snmp.close
|
113
101
|
end
|
114
102
|
end
|
115
103
|
|
@@ -135,10 +123,18 @@ module Arborist::Monitor::SNMP
|
|
135
123
|
@results = {}
|
136
124
|
end
|
137
125
|
|
126
|
+
|
127
|
+
### Return the current SNMP connection system string.
|
128
|
+
def system
|
129
|
+
return Thread.current[ :system ]
|
130
|
+
end
|
131
|
+
|
138
132
|
end # Arborist::Monitor::SNMP
|
139
133
|
|
140
134
|
require 'arborist/monitor/snmp/cpu'
|
141
135
|
require 'arborist/monitor/snmp/disk'
|
142
136
|
require 'arborist/monitor/snmp/process'
|
143
137
|
require 'arborist/monitor/snmp/memory'
|
138
|
+
require 'arborist/monitor/snmp/ups'
|
139
|
+
require 'arborist/monitor/snmp/ups/battery'
|
144
140
|
|
@@ -24,11 +24,7 @@ class Arborist::Monitor::SNMP::CPU
|
|
24
24
|
# When walking load OIDS, the iterator count matches
|
25
25
|
# these labels.
|
26
26
|
#
|
27
|
-
LOADKEYS =
|
28
|
-
1 => :load1,
|
29
|
-
2 => :load5,
|
30
|
-
3 => :load15
|
31
|
-
}
|
27
|
+
LOADKEYS = %i[ load1 load5 load15 ]
|
32
28
|
|
33
29
|
|
34
30
|
# Global defaults for instances of this monitor
|
@@ -66,18 +62,13 @@ class Arborist::Monitor::SNMP::CPU
|
|
66
62
|
protected
|
67
63
|
#########
|
68
64
|
|
69
|
-
### Return system CPU data.
|
70
|
-
###
|
71
|
-
def cpu( snmp )
|
72
|
-
return snmp.walk( OIDS[:cpu] )
|
73
|
-
end
|
74
|
-
|
75
|
-
|
76
65
|
### Find load data, add additional niceties for reporting.
|
77
66
|
###
|
78
67
|
def format_load( snmp )
|
79
68
|
info = { cpu: {}, load: {} }
|
80
|
-
cpus =
|
69
|
+
cpus = snmp.walk( oid: OIDS[:cpu] ).each_with_object( [] ) do |(_, value), acc|
|
70
|
+
acc << value
|
71
|
+
end
|
81
72
|
|
82
73
|
info[ :cpu ][ :count ] = cpus.size
|
83
74
|
|
@@ -91,20 +82,21 @@ class Arborist::Monitor::SNMP::CPU
|
|
91
82
|
# alert after X events" pragmas.
|
92
83
|
#
|
93
84
|
if self.system =~ /windows\s+/i
|
94
|
-
info[ :cpu ][ :usage ] = cpus.
|
85
|
+
info[ :cpu ][ :usage ] = cpus.inject( :+ ).to_f / cpus.size
|
95
86
|
info[ :message ] = "System is %0.1f%% in use." % [ info[ :cpu ][ :usage ] ]
|
96
87
|
|
88
|
+
|
97
89
|
# UCDavis stuff is better for alerting only after there has been
|
98
90
|
# an extended load event. Use the 5 minute average to avoid
|
99
91
|
# state changes on transient spikes.
|
100
92
|
#
|
101
93
|
else
|
102
|
-
snmp.walk( OIDS[:load] ).each_with_index do |(_, value), idx|
|
103
|
-
next unless LOADKEYS[ idx
|
104
|
-
info[ :load ][ LOADKEYS[idx
|
94
|
+
snmp.walk( oid: OIDS[:load] ).each_with_index do |(_, value), idx|
|
95
|
+
next unless LOADKEYS[ idx ]
|
96
|
+
info[ :load ][ LOADKEYS[idx] ] = value.to_f
|
105
97
|
end
|
106
98
|
|
107
|
-
percentage = (( ( info[:load][ :load5 ] / cpus.size
|
99
|
+
percentage = (( ( info[:load][ :load5 ] / cpus.size) - 1 ) * 100 ).round( 1 )
|
108
100
|
|
109
101
|
if percentage < 0
|
110
102
|
info[ :message ] = "System is %0.1f%% idle." % [ percentage.abs ]
|
@@ -125,7 +117,7 @@ class Arborist::Monitor::SNMP::CPU
|
|
125
117
|
def find_load( host, snmp )
|
126
118
|
info = self.format_load( snmp )
|
127
119
|
|
128
|
-
config = identifiers[ host ].last || {}
|
120
|
+
config = self.identifiers[ host ].last['config'] || {}
|
129
121
|
warn_at = config[ 'warn_at' ] || self.class.warn_at
|
130
122
|
usage = info.dig( :cpu, :usage ) || 0
|
131
123
|
|
@@ -17,9 +17,10 @@ class Arborist::Monitor::SNMP::Disk
|
|
17
17
|
# OIDS required to pull disk information from net-snmp.
|
18
18
|
#
|
19
19
|
STORAGE_NET_SNMP = {
|
20
|
-
path:
|
20
|
+
path: '1.3.6.1.4.1.2021.9.1.2',
|
21
21
|
percent: '1.3.6.1.4.1.2021.9.1.9',
|
22
|
-
type:
|
22
|
+
type: '1.3.6.1.2.1.25.3.8.1.4',
|
23
|
+
access: '1.3.6.1.2.1.25.3.8.1.5'
|
23
24
|
}
|
24
25
|
|
25
26
|
# The OID that matches a local windows hard disk.
|
@@ -32,15 +33,21 @@ class Arborist::Monitor::SNMP::Disk
|
|
32
33
|
# OIDS required to pull disk information from Windows.
|
33
34
|
#
|
34
35
|
STORAGE_WINDOWS = {
|
35
|
-
type:
|
36
|
-
path:
|
36
|
+
type: '1.3.6.1.2.1.25.2.3.1.2',
|
37
|
+
path: '1.3.6.1.2.1.25.2.3.1.3',
|
37
38
|
total: '1.3.6.1.2.1.25.2.3.1.5',
|
38
|
-
used:
|
39
|
+
used: '1.3.6.1.2.1.25.2.3.1.6'
|
39
40
|
}
|
40
41
|
|
41
42
|
# The fallback warning capacity.
|
42
43
|
WARN_AT = 90
|
43
44
|
|
45
|
+
# Don't alert if a mount is readonly by default.
|
46
|
+
ALERT_READONLY = false
|
47
|
+
|
48
|
+
# Access mode meanings
|
49
|
+
ACCESS_READWRITE = 1
|
50
|
+
ACCESS_READONLY = 2
|
44
51
|
|
45
52
|
# Configurability API
|
46
53
|
#
|
@@ -48,6 +55,9 @@ class Arborist::Monitor::SNMP::Disk
|
|
48
55
|
# What percentage qualifies as a warning
|
49
56
|
setting :warn_at, default: WARN_AT
|
50
57
|
|
58
|
+
# Set down if the mounts are readonly?
|
59
|
+
setting :alert_readonly, default: ALERT_READONLY
|
60
|
+
|
51
61
|
# If non-empty, only these paths are included in checks.
|
52
62
|
#
|
53
63
|
setting :include do |val|
|
@@ -60,7 +70,7 @@ class Arborist::Monitor::SNMP::Disk
|
|
60
70
|
# Paths to exclude from checks
|
61
71
|
#
|
62
72
|
setting :exclude,
|
63
|
-
default: [ '^/dev(/.+)?$', '^/net(/.+)?$', '
|
73
|
+
default: [ '^/dev(/.+)?$', '/dev$', '^/net(/.+)?$', '/proc$', '^/run$', '^/sys/', '/sys$' ] do |val|
|
64
74
|
mounts = Array( val ).map{|m| Regexp.new(m) }
|
65
75
|
Regexp.union( mounts )
|
66
76
|
end
|
@@ -70,7 +80,9 @@ class Arborist::Monitor::SNMP::Disk
|
|
70
80
|
### Return the properties used by this monitor.
|
71
81
|
###
|
72
82
|
def self::node_properties
|
73
|
-
|
83
|
+
used_properties = USED_PROPERTIES.dup
|
84
|
+
used_properties << :mounts
|
85
|
+
return used_properties
|
74
86
|
end
|
75
87
|
|
76
88
|
|
@@ -98,40 +110,54 @@ class Arborist::Monitor::SNMP::Disk
|
|
98
110
|
### +snmp+ connection.
|
99
111
|
###
|
100
112
|
def gather_disks( host, snmp )
|
101
|
-
|
102
|
-
config
|
103
|
-
warn_at
|
113
|
+
current_mounts = self.system =~ /windows\s+/i ? self.windows_disks( snmp ) : self.unix_disks( snmp )
|
114
|
+
config = self.identifiers[ host ].last['config'] || {}
|
115
|
+
warn_at = config[ 'warn_at' ] || self.class.warn_at
|
116
|
+
alert_readonly = config[ 'alert_readonly' ] || self.class.alert_readonly
|
117
|
+
|
118
|
+
self.log.debug self.identifiers[ host ]
|
104
119
|
|
105
120
|
includes = self.format_mounts( config, 'include' ) || self.class.include
|
106
121
|
excludes = self.format_mounts( config, 'exclude' ) || self.class.exclude
|
107
122
|
|
108
|
-
|
123
|
+
current_mounts.reject! do |path, data|
|
124
|
+
path = path.to_s
|
109
125
|
excludes.match( path ) || ( includes && ! includes.match( path ) )
|
110
126
|
end
|
111
127
|
|
112
128
|
errors = []
|
113
129
|
warnings = []
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
else
|
120
|
-
warn_at
|
121
|
-
end
|
130
|
+
current_mounts.each_pair do |path, data|
|
131
|
+
warn = if warn_at.is_a?( Hash )
|
132
|
+
warn_at[ path ] || self.class.warn_at
|
133
|
+
else
|
134
|
+
warn_at
|
122
135
|
end
|
123
136
|
|
124
|
-
|
137
|
+
readonly = alert_readonly.is_a?( Hash ) ? alert_readonly[ path ] : alert_readonly
|
138
|
+
|
139
|
+
self.log.debug "%s:%s -> %p, warn at %d" % [ host, path, data, warn ]
|
125
140
|
|
126
|
-
if
|
127
|
-
if
|
128
|
-
errors << "%s at %d%% capacity" % [ path,
|
141
|
+
if data[ :capacity ] >= warn.to_i
|
142
|
+
if data[ :capacity ] >= 100
|
143
|
+
errors << "%s at %d%% capacity" % [ path, data[ :capacity ] ]
|
129
144
|
else
|
130
|
-
warnings << "%s at %d%% capacity" % [ path,
|
145
|
+
warnings << "%s at %d%% capacity" % [ path, data[ :capacity ] ]
|
131
146
|
end
|
132
147
|
end
|
148
|
+
|
149
|
+
if readonly && data[ :accessmode ] == ACCESS_READONLY
|
150
|
+
errors << "%s is mounted read-only." % [ path ]
|
151
|
+
end
|
133
152
|
end
|
134
153
|
|
154
|
+
# Remove any past mounts that configuration exclusions should
|
155
|
+
# now omit.
|
156
|
+
mounts = self.identifiers[ host ].last[ 'mounts' ] || {}
|
157
|
+
mounts.keys.each{|k| mounts[k] = nil }
|
158
|
+
|
159
|
+
mounts.merge!( current_mounts )
|
160
|
+
|
135
161
|
self.results[ host ] = { mounts: mounts }
|
136
162
|
self.results[ host ][ :error ] = errors.join(', ') unless errors.empty?
|
137
163
|
self.results[ host ][ :warning ] = warnings.join(', ') unless warnings.empty?
|
@@ -151,18 +177,25 @@ class Arborist::Monitor::SNMP::Disk
|
|
151
177
|
### Fetch information for Windows systems.
|
152
178
|
###
|
153
179
|
def windows_disks( snmp )
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
180
|
+
paths = snmp.walk( oid: STORAGE_WINDOWS[:path] ).each_with_object( [] ) do |(_, value), acc|
|
181
|
+
acc << value
|
182
|
+
end
|
183
|
+
types = snmp.walk( oid: STORAGE_WINDOWS[:type] ).each_with_object( [] ) do |(_, value), acc|
|
184
|
+
acc << WINDOWS_DEVICES.include?( value )
|
185
|
+
end
|
186
|
+
totals = snmp.walk( oid: STORAGE_WINDOWS[:total] ).each_with_object( [] ) do |(_, value), acc|
|
187
|
+
acc << value
|
188
|
+
end
|
189
|
+
used = snmp.walk( oid: STORAGE_WINDOWS[:used] ).each_with_object( [] ) do |(_, value), acc|
|
190
|
+
acc << value
|
191
|
+
end
|
160
192
|
|
161
193
|
disks = {}
|
162
|
-
|
163
|
-
next
|
164
|
-
next
|
165
|
-
disks[
|
194
|
+
paths.each_with_index do |path, idx|
|
195
|
+
next if totals[ idx ].zero?
|
196
|
+
next unless types[ idx ]
|
197
|
+
disks[ path ] ||= {}
|
198
|
+
disks[ path ][ :capacity ] = (( used[idx].to_f / totals[idx] ) * 100).round( 1 )
|
166
199
|
end
|
167
200
|
|
168
201
|
return disks
|
@@ -172,11 +205,20 @@ class Arborist::Monitor::SNMP::Disk
|
|
172
205
|
### Fetch information for Unix/MacOS systems.
|
173
206
|
###
|
174
207
|
def unix_disks( snmp )
|
175
|
-
|
176
|
-
|
177
|
-
|
208
|
+
paths = snmp.walk( oid: STORAGE_NET_SNMP[:path] ).each_with_object( [] ) do |(_, value), acc|
|
209
|
+
acc << value
|
210
|
+
end
|
211
|
+
capacities = snmp.walk( oid: STORAGE_NET_SNMP[:percent] ).each_with_object( [] ) do |(_, value), acc|
|
212
|
+
acc << value
|
213
|
+
end
|
214
|
+
accessmodes = snmp.walk( oid: STORAGE_NET_SNMP[:access] ).each_with_object( [] ) do |(_, value), acc|
|
215
|
+
acc << value
|
216
|
+
end
|
178
217
|
|
179
|
-
|
218
|
+
pairs = paths.each_with_object( {} ).with_index do |(p, acc), idx|
|
219
|
+
acc[p] = { capacity: capacities[idx], accessmode: accessmodes[idx] }
|
220
|
+
end
|
221
|
+
return pairs
|
180
222
|
end
|
181
223
|
|
182
224
|
end # class Arborist::Monitor::SNMP::Disk
|
@@ -84,7 +84,7 @@ class Arborist::Monitor::SNMP::Memory
|
|
84
84
|
def gather_memory( host, snmp )
|
85
85
|
info = self.system =~ /windows\s+/i ? self.get_windows( snmp ) : self.get_mem( snmp )
|
86
86
|
|
87
|
-
config = identifiers[ host ].last || {}
|
87
|
+
config = self.identifiers[ host ].last['config'] || {}
|
88
88
|
physical_warn_at = config[ 'physical_warn_at' ] || self.class.physical_warn_at
|
89
89
|
swap_warn_at = config[ 'swap_warn_at' ] || self.class.swap_warn_at
|
90
90
|
|
@@ -131,7 +131,7 @@ class Arborist::Monitor::SNMP::Memory
|
|
131
131
|
info = { memory: {}, swap: {} }
|
132
132
|
mem_idx, swap_idx = nil
|
133
133
|
|
134
|
-
snmp.walk( MEMORY[:windows][:label] ).each_with_index do |(_, val), i|
|
134
|
+
snmp.walk( oid: MEMORY[:windows][:label] ).each_with_index do |(_, val), i|
|
135
135
|
mem_idx = i + 1 if val =~ /physical memory/i
|
136
136
|
swap_idx = i + 1 if val =~ /virtual memory/i
|
137
137
|
end
|
@@ -148,8 +148,8 @@ class Arborist::Monitor::SNMP::Memory
|
|
148
148
|
###
|
149
149
|
def calc_memory( snmp, oids )
|
150
150
|
info = { usage: 0, available: 0 }
|
151
|
-
avail = snmp.get( oids[:avail] ).
|
152
|
-
total = snmp.get( oids[:total] ).
|
151
|
+
avail = snmp.get( oid: oids[:avail] ).to_f
|
152
|
+
total = snmp.get( oid: oids[:total] ).to_f
|
153
153
|
used = total - avail
|
154
154
|
|
155
155
|
return info if avail.zero?
|
@@ -166,9 +166,9 @@ class Arborist::Monitor::SNMP::Memory
|
|
166
166
|
info = { usage: 0, available: 0 }
|
167
167
|
return info unless idx
|
168
168
|
|
169
|
-
units = snmp.get( MEMORY[:windows][:units] + ".#{idx}" )
|
170
|
-
total = snmp.get( MEMORY[:windows][:total] + ".#{idx}" ).
|
171
|
-
used = snmp.get( MEMORY[:windows][:used] + ".#{idx}" ).
|
169
|
+
units = snmp.get( oid: MEMORY[:windows][:units] + ".#{idx}" )
|
170
|
+
total = snmp.get( oid: MEMORY[:windows][:total] + ".#{idx}" ).to_f * units
|
171
|
+
used = snmp.get( oid: MEMORY[:windows][:used] + ".#{idx}" ).to_f * units
|
172
172
|
|
173
173
|
info[ :usage ] = (( used / total ) * 100 ).round( 2 )
|
174
174
|
info[ :available ] = (( total - used ) / 1024 / 1024 ).round( 2 )
|
@@ -71,7 +71,7 @@ class Arborist::Monitor::SNMP::Process
|
|
71
71
|
#### +snmp+ connection.
|
72
72
|
###
|
73
73
|
def gather_processlist( host, snmp )
|
74
|
-
config = self.identifiers[ host ].last || {}
|
74
|
+
config = self.identifiers[ host ].last['config'] || {}
|
75
75
|
errors = []
|
76
76
|
procs = self.system =~ /windows\s+/i ? self.get_windows( snmp ) : self.get_procs( snmp )
|
77
77
|
|
@@ -80,7 +80,7 @@ class Arborist::Monitor::SNMP::Process
|
|
80
80
|
|
81
81
|
# Check against what is running.
|
82
82
|
#
|
83
|
-
Array( config['
|
83
|
+
Array( config['check'] || self.class.check ).each do |process|
|
84
84
|
process_r = Regexp.new( process )
|
85
85
|
found = procs.find{|p| p.match(process_r) }
|
86
86
|
errors << "'%s' is not running" % [ process ] unless found
|
@@ -95,14 +95,24 @@ class Arborist::Monitor::SNMP::Process
|
|
95
95
|
###
|
96
96
|
def get_windows( snmp )
|
97
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
98
|
|
102
|
-
|
103
|
-
|
104
|
-
|
99
|
+
paths = snmp.walk( oid: oids[0] ).each_with_object( [] ) do |(_, value), acc|
|
100
|
+
acc << value
|
101
|
+
end
|
102
|
+
procs = snmp.walk( oid: oids[1] ).each_with_object( [] ) do |(_, value), acc|
|
103
|
+
acc << value
|
104
|
+
end
|
105
|
+
args = snmp.walk( oid: oids[2] ).each_with_object( [] ) do |(_, value), acc|
|
106
|
+
acc << value
|
105
107
|
end
|
108
|
+
|
109
|
+
return paths.zip( procs, args ).collect do |(path, process, arg)|
|
110
|
+
next unless path && process
|
111
|
+
next if path.empty?
|
112
|
+
path << process unless process.empty?
|
113
|
+
path << " %s" % [ arg.to_s ] if arg && ! arg.empty?
|
114
|
+
path
|
115
|
+
end.compact
|
106
116
|
end
|
107
117
|
|
108
118
|
|
@@ -110,13 +120,19 @@ class Arborist::Monitor::SNMP::Process
|
|
110
120
|
###
|
111
121
|
def get_procs( snmp )
|
112
122
|
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
123
|
|
117
|
-
|
118
|
-
acc <<
|
124
|
+
procs = snmp.walk( oid: oids.first ).each_with_object( [] ) do |(_, value), acc|
|
125
|
+
acc << value
|
126
|
+
end
|
127
|
+
args = snmp.walk( oid: oids.last ).each_with_object( [] ) do |(_, value), acc|
|
128
|
+
acc << value
|
119
129
|
end
|
130
|
+
|
131
|
+
return procs.zip( args ).collect do |(process, arg)|
|
132
|
+
next if process.empty?
|
133
|
+
process << " %s" % [ arg.to_s ] unless arg.empty?
|
134
|
+
process
|
135
|
+
end.compact
|
120
136
|
end
|
121
137
|
|
122
138
|
end # class Arborist::Monitor::SNMP::Process
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# vim: set noet nosta sw=4 ts=4 :
|
3
|
+
|
4
|
+
require 'arborist/monitor/snmp/ups' unless defined?( Arborist::Monitor::SNMP::UPS )
|
5
|
+
|
6
|
+
# Checks for UPS battery health.
|
7
|
+
#
|
8
|
+
# Checks the available battery percentage, if the UPS is on battery,
|
9
|
+
# and the temperature of the battery.
|
10
|
+
#
|
11
|
+
class Arborist::Monitor::SNMP::UPS::Battery
|
12
|
+
include Arborist::Monitor::SNMP
|
13
|
+
|
14
|
+
extend Configurability, Loggability
|
15
|
+
log_to :arborist_snmp
|
16
|
+
|
17
|
+
# OIDS for discovering ups status.
|
18
|
+
#
|
19
|
+
OIDS = {
|
20
|
+
battery_status: '.1.3.6.1.2.1.33.1.2.1.0', # 1 - unk, 2 - normal, 3 - low, 4 - depleted
|
21
|
+
seconds_on_battery: '.1.3.6.1.2.1.33.1.2.2.0',
|
22
|
+
est_minutes_remaining: '.1.3.6.1.2.1.33.1.2.3.0',
|
23
|
+
est_charge_remaining: '.1.3.6.1.2.1.33.1.2.4.0', # in percent
|
24
|
+
battery_voltage: '.1.3.6.1.2.1.33.1.2.5.0', # in 0.1v DC
|
25
|
+
battery_current: '.1.3.6.1.2.1.33.1.2.6.0', # in 0.1a DC
|
26
|
+
battery_temperature: '.1.3.6.1.2.1.33.1.2.7.0' # in Celcius
|
27
|
+
}
|
28
|
+
|
29
|
+
# Human-readable translations for battery status OID.
|
30
|
+
#
|
31
|
+
BATTERY_STATUS = {
|
32
|
+
1 => "Battery status is Unknown.",
|
33
|
+
2 => "Battery is OK.",
|
34
|
+
3 => "Battery is Low.",
|
35
|
+
4 => "Battery is Depleted."
|
36
|
+
}
|
37
|
+
|
38
|
+
# Global defaults for instances of this monitor
|
39
|
+
#
|
40
|
+
configurability( 'arborist.snmp.ups.battery' ) do
|
41
|
+
# What battery percentage qualifies as a warning
|
42
|
+
setting :capacity_warn_at, default: 60
|
43
|
+
|
44
|
+
# What battery temperature qualifies as a warning, in C
|
45
|
+
setting :temperature_warn_at, default: 50
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
### Return the properties used by this monitor.
|
50
|
+
###
|
51
|
+
def self::node_properties
|
52
|
+
return USED_PROPERTIES
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
### Class #run creates a new instance and immediately runs it.
|
57
|
+
###
|
58
|
+
def self::run( nodes )
|
59
|
+
return new.run( nodes )
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
### Perform the monitoring checks.
|
64
|
+
###
|
65
|
+
def run( nodes )
|
66
|
+
super do |host, snmp|
|
67
|
+
self.check_battery( host, snmp )
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
#########
|
73
|
+
protected
|
74
|
+
#########
|
75
|
+
|
76
|
+
### Query SNMP and format information into a hash.
|
77
|
+
###
|
78
|
+
def format_battery( snmp )
|
79
|
+
info = {}
|
80
|
+
|
81
|
+
# basic info that's always available
|
82
|
+
info[ :status ] = snmp.get( oid: OIDS[:battery_status] )
|
83
|
+
info[ :capacity ] = snmp.get( oid: OIDS[:est_charge_remaining] ) rescue nil
|
84
|
+
info[ :temperature ] = snmp.get( oid: OIDS[:battery_temperature] ) rescue nil
|
85
|
+
info[ :minutes_remaining ] = snmp.get( oid: OIDS[:est_minutes_remaining] ) rescue nil
|
86
|
+
|
87
|
+
# don't report voltage if the UPS doesn't
|
88
|
+
voltage = snmp.get( oid: OIDS[:battery_voltage] ) rescue nil
|
89
|
+
info[ :voltage ] = voltage / 10 if voltage
|
90
|
+
|
91
|
+
# don't report current if the UPS doesn't
|
92
|
+
current = snmp.get( oid: OIDS[:battery_current] ) rescue nil
|
93
|
+
info[ :current ] = current/10 if current
|
94
|
+
|
95
|
+
# see if we are on battery
|
96
|
+
info[ :seconds_on_battery ] = snmp.get( oid: OIDS[:seconds_on_battery] ) rescue 0
|
97
|
+
info[ :in_use ] = ( info[ :seconds_on_battery ] != 0 )
|
98
|
+
|
99
|
+
return { battery: info.compact }
|
100
|
+
end
|
101
|
+
|
102
|
+
### Parse SNMP-provided information and alert based on thresholds.
|
103
|
+
###
|
104
|
+
def check_battery( host, snmp )
|
105
|
+
info = self.format_battery( snmp )
|
106
|
+
|
107
|
+
config = self.identifiers[ host ].last['config'] || {}
|
108
|
+
cap_warn = config[ 'capacity_warn_at' ] || self.class.capacity_warn_at
|
109
|
+
temp_warn = config[ 'temperature_warn_at' ] || self.class.temperature_warn_at
|
110
|
+
|
111
|
+
in_use = info.dig( :battery, :in_use )
|
112
|
+
status = info.dig( :battery, :status )
|
113
|
+
capacity = info.dig( :battery, :capacity )
|
114
|
+
temperature = info.dig( :battery, :temperature )
|
115
|
+
warnings = []
|
116
|
+
|
117
|
+
if in_use
|
118
|
+
mins = info.dig( :battery, :minutes_remaining ) || "(unknown)"
|
119
|
+
warnings << "UPS on battery - %s minute(s) remaning." % [ mins ]
|
120
|
+
end
|
121
|
+
|
122
|
+
warnings << BATTERY_STATUS[ status ] if status != 2
|
123
|
+
|
124
|
+
warnings << "Battery remaining capacity %0.1f%% less than %0.1f percent" %
|
125
|
+
[ capacity, cap_warn ] if capacity && capacity <= cap_warn
|
126
|
+
|
127
|
+
warnings << "Battery temperature %dC greater than %dC" %
|
128
|
+
[ temperature, temp_warn ] if temperature && temperature >= temp_warn
|
129
|
+
|
130
|
+
info[ :warning ] = warnings.join( "\n" ) unless warnings.empty?
|
131
|
+
self.results[ host ] = info
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
end # class Arborist::Monitor::UPS::Battery
|
136
|
+
|
data/lib/arborist/snmp.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# -*- ruby -*-
|
2
2
|
#encoding: utf-8
|
3
|
+
# vim: set noet nosta sw=4 ts=4 :
|
3
4
|
|
4
5
|
require 'loggability'
|
5
6
|
require 'arborist'
|
@@ -14,10 +15,10 @@ module Arborist::SNMP
|
|
14
15
|
|
15
16
|
|
16
17
|
# Package version
|
17
|
-
VERSION = '0.
|
18
|
+
VERSION = '0.7.0'
|
18
19
|
|
19
20
|
# Version control revision
|
20
|
-
REVISION = %q$Revision
|
21
|
+
REVISION = %q$Revision$
|
21
22
|
|
22
23
|
|
23
24
|
### Return the name of the library with the version, and optionally the build ID if
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arborist-snmp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mahlon E. Smith <mahlon@martini.nu>
|
@@ -11,9 +11,9 @@ bindir: bin
|
|
11
11
|
cert_chain:
|
12
12
|
- |
|
13
13
|
-----BEGIN CERTIFICATE-----
|
14
|
-
|
14
|
+
MIIDMDCCAhigAwIBAgIBAjANBgkqhkiG9w0BAQsFADA+MQ8wDQYDVQQDDAZtYWhs
|
15
15
|
b24xFzAVBgoJkiaJk/IsZAEZFgdtYXJ0aW5pMRIwEAYKCZImiZPyLGQBGRYCbnUw
|
16
|
-
|
16
|
+
HhcNMjAwNzA5MjIxMzE4WhcNMjEwNzA5MjIxMzE4WjA+MQ8wDQYDVQQDDAZtYWhs
|
17
17
|
b24xFzAVBgoJkiaJk/IsZAEZFgdtYXJ0aW5pMRIwEAYKCZImiZPyLGQBGRYCbnUw
|
18
18
|
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDpXGN0YbMVpYv4EoiCxpQw
|
19
19
|
sxKdyhlkvpvENUkpEhbpnEuMKXgUfRHO4T/vBZf0h8eYgwnrHCRhAeIqesFKfoj9
|
@@ -21,17 +21,16 @@ cert_chain:
|
|
21
21
|
Fht9ppCuNmxJNd+L3zAX8lD01RUWNRC+8L5QLCjViJtjFDDCFfh9NCirs+XnTCzo
|
22
22
|
AJgFbsZIzFJtSiXUtFgscKr4Ik8ruhRbPbYbmx9rf6W74aTMPxggq/d3gj0Eh32y
|
23
23
|
WsXsQ5giVnmkbsRkBNu3QyZ8Xr5+7mvy5AWyqXKOrcW7lnYaob6Z9x/MGXGNeD6j
|
24
|
-
|
25
|
-
+6kAaW7ukKph2/4MTAD8/
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
UrSf3b+cPoC8PNfjp8zsdw==
|
24
|
+
AgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBRY8ea6
|
25
|
+
+6kAaW7ukKph2/4MTAD8/TANBgkqhkiG9w0BAQsFAAOCAQEAryZehbiEumHsUsce
|
26
|
+
FoBVuDoVdlJf8ae11G8IPXnEHCT8S5b+MBSYd55V3aGQm4bKoZA3GmmD8Y0a+oxt
|
27
|
+
l2kkTvE0r1bBb/qYQI97AjnqzHByseBRoaHtJ12JtrDEdi8y4jd5AJt4aW+G/roD
|
28
|
+
I2/ymUodKw9Cteom0RdJNzBUJ+Bq+qFOy7mVKNIfhXRRFYEy11y1712FsJXqUEku
|
29
|
+
qr3lnAEvEy9hQila4NoJT2+aQEKsjVON9D3a727naKDFUcKDg6P4KqS+yOKgR+QH
|
30
|
+
D8llK3JPaqKXuJkbd8jKchDk0Q+fA8Klan5SSnm7pMD51QM1mPsVPm5bEw5ib0bn
|
31
|
+
oR3hTQ==
|
33
32
|
-----END CERTIFICATE-----
|
34
|
-
date:
|
33
|
+
date: 2020-08-01 00:00:00.000000000 Z
|
35
34
|
dependencies:
|
36
35
|
- !ruby/object:Gem::Dependency
|
37
36
|
name: arborist
|
@@ -48,19 +47,33 @@ dependencies:
|
|
48
47
|
- !ruby/object:Gem::Version
|
49
48
|
version: '0.1'
|
50
49
|
- !ruby/object:Gem::Dependency
|
51
|
-
name:
|
50
|
+
name: netsnmp
|
52
51
|
requirement: !ruby/object:Gem::Requirement
|
53
52
|
requirements:
|
54
53
|
- - "~>"
|
55
54
|
- !ruby/object:Gem::Version
|
56
|
-
version: '0.
|
55
|
+
version: '0.1'
|
56
|
+
type: :runtime
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0.1'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: xorcist
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.1'
|
57
70
|
type: :runtime
|
58
71
|
prerelease: false
|
59
72
|
version_requirements: !ruby/object:Gem::Requirement
|
60
73
|
requirements:
|
61
74
|
- - "~>"
|
62
75
|
- !ruby/object:Gem::Version
|
63
|
-
version: '
|
76
|
+
version: '1.1'
|
64
77
|
description: "\tThis library adds common SNMP resource support to Arborist monitors.\n"
|
65
78
|
email: mahlon@martini.nu
|
66
79
|
executables: []
|
@@ -72,6 +85,8 @@ files:
|
|
72
85
|
- lib/arborist/monitor/snmp/disk.rb
|
73
86
|
- lib/arborist/monitor/snmp/memory.rb
|
74
87
|
- lib/arborist/monitor/snmp/process.rb
|
88
|
+
- lib/arborist/monitor/snmp/ups.rb
|
89
|
+
- lib/arborist/monitor/snmp/ups/battery.rb
|
75
90
|
- lib/arborist/snmp.rb
|
76
91
|
homepage: http://bitbucket.org/mahlon/Arborist-SNMP
|
77
92
|
licenses:
|
@@ -92,8 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
107
|
- !ruby/object:Gem::Version
|
93
108
|
version: '0'
|
94
109
|
requirements: []
|
95
|
-
|
96
|
-
rubygems_version: 2.5.1
|
110
|
+
rubygems_version: 3.1.2
|
97
111
|
signing_key:
|
98
112
|
specification_version: 4
|
99
113
|
summary: SNMP support for Arborist monitors
|
metadata.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
����ARlt�@�����"-Z��E6M�n�0�D�;�{�6��M�_O���
|