3 |
3 |
4 require 'arborist/monitor/snmp' unless defined?( Arborist::Monitor::SNMP ) |
4 require 'arborist/monitor/snmp' unless defined?( Arborist::Monitor::SNMP ) |
5 |
5 |
6 # SNMP running process checks. |
6 # SNMP running process checks. |
7 # |
7 # |
|
8 # This only checks running userland processes. |
|
9 # |
8 class Arborist::Monitor::SNMP::Process |
10 class Arborist::Monitor::SNMP::Process |
9 include Arborist::Monitor::SNMP |
11 include Arborist::Monitor::SNMP |
10 |
12 |
11 extend Loggability |
13 extend Configurability, Loggability |
12 log_to :arborist |
14 log_to :arborist_snmp |
|
15 |
13 |
16 |
14 # OIDS for discovering running processes. |
17 # OIDS for discovering running processes. |
|
18 # Of course, Windows does it slightly differently. |
15 # |
19 # |
16 PROCESS = { |
20 PROCESS = { |
17 list: '1.3.6.1.2.1.25.4.2.1.4', |
21 netsnmp: { |
18 args: '1.3.6.1.2.1.25.4.2.1.5' |
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 } |
19 } |
30 } |
|
31 |
20 |
32 |
21 # Global defaults for instances of this monitor |
33 # Global defaults for instances of this monitor |
22 # |
34 # |
23 DEFAULT_OPTIONS = { |
35 configurability( 'arborist.snmp.processes' ) do |
24 processes: [] # list of procs to match |
36 # Default list of processes to check for |
25 } |
37 setting :check, default: [] do |val| |
|
38 Array( val ) |
|
39 end |
|
40 end |
26 |
41 |
27 |
42 |
28 ### This monitor is complex enough to require creating an instance from the caller. |
43 ### Return the properties used by this monitor. |
29 ### Provide a friendlier error message the class was provided to exec() directly. |
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. |
30 ### |
51 ### |
31 def self::run( nodes ) |
52 def self::run( nodes ) |
32 return new.run( nodes ) |
53 return new.run( nodes ) |
33 end |
54 end |
34 |
55 |
35 |
56 |
36 ### Create a new instance of this monitor. |
|
37 ### |
|
38 def initialize( options=DEFAULT_OPTIONS ) |
|
39 options = DEFAULT_OPTIONS.merge( options || {} ) |
|
40 %i[ processes ].each do |opt| |
|
41 options[ opt ] = Array( options[opt] ) |
|
42 end |
|
43 |
|
44 options.each do |name, value| |
|
45 self.public_send( "#{name.to_s}=", value ) |
|
46 end |
|
47 end |
|
48 |
|
49 # Set an error if processes in this array aren't running. |
|
50 attr_accessor :processes |
|
51 |
|
52 |
|
53 ### Perform the monitoring checks. |
57 ### Perform the monitoring checks. |
54 ### |
58 ### |
55 def run( nodes ) |
59 def run( nodes ) |
56 super do |snmp, host| |
60 super do |host, snmp| |
57 self.gather_processlist( snmp, host ) |
61 self.gather_processlist( host, snmp ) |
58 end |
62 end |
59 end |
63 end |
60 |
64 |
61 |
65 |
62 ######### |
66 ######### |
64 ######### |
68 ######### |
65 |
69 |
66 ### Collect running processes on +host+ from an existing (and open) |
70 ### Collect running processes on +host+ from an existing (and open) |
67 #### +snmp+ connection. |
71 #### +snmp+ connection. |
68 ### |
72 ### |
69 def gather_processlist( snmp, host ) |
73 def gather_processlist( host, snmp ) |
70 self.log.debug "Getting running process list for %s" % [ host ] |
74 config = self.identifiers[ host ].last || {} |
71 config = @identifiers[ host ].last || {} |
|
72 procs = [] |
|
73 errors = [] |
75 errors = [] |
|
76 procs = self.system =~ /windows\s+/i ? self.get_windows( snmp ) : self.get_procs( snmp ) |
74 |
77 |
75 snmp.walk([ PROCESS[:list], PROCESS[:args] ]) do |list| |
78 self.log.debug "Running processes for host: %s: %p" % [ host, procs ] |
76 process = list[0].value.to_s |
79 self.results[ host ] = { count: procs.size } |
77 args = list[1].value.to_s |
80 |
78 procs << "%s %s " % [ process, args ] |
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 |
79 end |
87 end |
80 |
88 |
81 # Check against the running stuff, setting an error if |
89 self.results[ host ][ :error ] = errors.join( ', ' ) unless errors.empty? |
82 # one isn't found. |
90 end |
83 # |
91 |
84 Array( config['processes'] || self.processes ).each do |process| |
92 |
85 process_r = Regexp.new( process ) |
93 ### Parse OIDS and return an Array of running processes. |
86 found = procs.find{|p| p.match(process_r) } |
94 ### Windows specific behaviors. |
87 errors << "Process '%s' is not running" % [ process, host ] unless found |
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 |
88 end |
105 end |
|
106 end |
89 |
107 |
90 self.log.debug " %d running processes" % [ procs.length ] |
108 |
91 if errors.empty? |
109 ### Parse OIDS and return an Array of running processes. |
92 @results[ host ] = {} |
110 ### |
93 else |
111 def get_procs( snmp ) |
94 @results[ host ] = { error: errors.join( ', ' ) } |
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 |
95 end |
119 end |
96 end |
120 end |
97 |
121 |
98 end # class Arborist::Monitor::SNMP::Process |
122 end # class Arborist::Monitor::SNMP::Process |
99 |
123 |