lib/arborist/monitor/snmp/process.rb
changeset 8 e0b7c95a154f
parent 4 e6eb11b1e00d
child 14 d5cb8bd33170
equal deleted inserted replaced
7:4548e58c8c66 8:e0b7c95a154f
     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