diff -r 4548e58c8c66 -r e0b7c95a154f lib/arborist/monitor/snmp/process.rb --- a/lib/arborist/monitor/snmp/process.rb Wed Aug 30 13:55:02 2017 -0700 +++ b/lib/arborist/monitor/snmp/process.rb Wed Apr 04 11:00:35 2018 -0700 @@ -5,56 +5,60 @@ # SNMP running process checks. # +# This only checks running userland processes. +# class Arborist::Monitor::SNMP::Process include Arborist::Monitor::SNMP - extend Loggability - log_to :arborist + extend Configurability, Loggability + log_to :arborist_snmp + # OIDS for discovering running processes. + # Of course, Windows does it slightly differently. # PROCESS = { - list: '1.3.6.1.2.1.25.4.2.1.4', - args: '1.3.6.1.2.1.25.4.2.1.5' + netsnmp: { + list: '1.3.6.1.2.1.25.4.2.1.4', + args: '1.3.6.1.2.1.25.4.2.1.5' + }, + windows: { + list: '1.3.6.1.2.1.25.4.2.1.2', + path: '1.3.6.1.2.1.25.4.2.1.4', + args: '1.3.6.1.2.1.25.4.2.1.5' + } } + # Global defaults for instances of this monitor # - DEFAULT_OPTIONS = { - processes: [] # list of procs to match - } + configurability( 'arborist.snmp.processes' ) do + # Default list of processes to check for + setting :check, default: [] do |val| + Array( val ) + end + end - ### This monitor is complex enough to require creating an instance from the caller. - ### Provide a friendlier error message the class was provided to exec() directly. + ### Return the properties used by this monitor. + ### + def self::node_properties + return USED_PROPERTIES + end + + + ### Class #run creates a new instance and immediately runs it. ### def self::run( nodes ) return new.run( nodes ) end - ### Create a new instance of this monitor. - ### - def initialize( options=DEFAULT_OPTIONS ) - options = DEFAULT_OPTIONS.merge( options || {} ) - %i[ processes ].each do |opt| - options[ opt ] = Array( options[opt] ) - end - - options.each do |name, value| - self.public_send( "#{name.to_s}=", value ) - end - end - - # Set an error if processes in this array aren't running. - attr_accessor :processes - - ### Perform the monitoring checks. ### def run( nodes ) - super do |snmp, host| - self.gather_processlist( snmp, host ) + super do |host, snmp| + self.gather_processlist( host, snmp ) end end @@ -66,32 +70,52 @@ ### Collect running processes on +host+ from an existing (and open) #### +snmp+ connection. ### - def gather_processlist( snmp, host ) - self.log.debug "Getting running process list for %s" % [ host ] - config = @identifiers[ host ].last || {} - procs = [] + def gather_processlist( host, snmp ) + config = self.identifiers[ host ].last || {} errors = [] + procs = self.system =~ /windows\s+/i ? self.get_windows( snmp ) : self.get_procs( snmp ) - snmp.walk([ PROCESS[:list], PROCESS[:args] ]) do |list| - process = list[0].value.to_s - args = list[1].value.to_s - procs << "%s %s " % [ process, args ] + self.log.debug "Running processes for host: %s: %p" % [ host, procs ] + self.results[ host ] = { count: procs.size } + + # Check against what is running. + # + Array( config['processes'] || self.class.check ).each do |process| + process_r = Regexp.new( process ) + found = procs.find{|p| p.match(process_r) } + errors << "'%s' is not running" % [ process ] unless found end - # Check against the running stuff, setting an error if - # one isn't found. - # - Array( config['processes'] || self.processes ).each do |process| - process_r = Regexp.new( process ) - found = procs.find{|p| p.match(process_r) } - errors << "Process '%s' is not running" % [ process, host ] unless found + self.results[ host ][ :error ] = errors.join( ', ' ) unless errors.empty? + end + + + ### Parse OIDS and return an Array of running processes. + ### Windows specific behaviors. + ### + def get_windows( snmp ) + oids = [ PROCESS[:windows][:path], PROCESS[:windows][:list], PROCESS[:windows][:args] ] + return snmp.walk( oids ).each_slice( 3 ). each_with_object( [] ) do |vals, acc| + path, process, args = vals[0][1], vals[1][1], vals[2][1] + next if path.empty? + + process = "%s%s" % [ path, process ] + process << " %s" % [ args ] unless args.empty? + acc << process end + end - self.log.debug " %d running processes" % [ procs.length ] - if errors.empty? - @results[ host ] = {} - else - @results[ host ] = { error: errors.join( ', ' ) } + + ### Parse OIDS and return an Array of running processes. + ### + def get_procs( snmp ) + oids = [ PROCESS[:netsnmp][:list], PROCESS[:netsnmp][:args] ] + return snmp.walk( oids ).each_slice( 2 ).each_with_object( [] ) do |vals, acc| + process, args = vals[0][1], vals[1][1] + next if process.empty? + + process << " %s" % [ args ] unless args.empty? + acc << process end end