class Arborist::Monitor
A declaration of an action to run against Manager nodes to update their state.
Constants
- DEFAULT_INTERVAL
The default monitoring interval, in seconds
- LOADED_INSTANCE_KEY
The key for the thread local that is used to track instances as they're loaded.
- MONITOR_FILE_PATTERN
The glob pattern to use for searching for monitors
Attributes
The monitor's (human) description.
The callback to invoke when the monitor is run.
The monitor's execution callbacks contained in a Module
The shell command to exec when running the monitor (if any). This can be any valid arguments to the `Kernel.spawn` method.
The interval between runs in seconds, as set by `every`.
The monitor's key. This key should be shared between monitors that check the same resources.
A Hash of criteria to pass to the Manager to filter out nodes to monitor.
The list of node properties to include when running the monitor.
A Hash of criteria to pass to the Manager when searching for nodes to monitor.
The path to the source this Monitor
was loaded from, if applicable
The number of seconds of splay to use when running the monitor.
Public Class Methods
Record a new loaded instance if the Thread-local variable is set up to track them.
# File lib/arborist/monitor.rb, line 128 def self::add_loaded_instance( new_instance ) instances = Thread.current[ LOADED_INSTANCE_KEY ] or return instances << new_instance end
Return an iterator for all the monitors supplied by the specified loader
.
# File lib/arborist/monitor.rb, line 146 def self::each_in( loader ) return loader.monitors end
Load the specified file
and return any new Nodes created as a result.
# File lib/arborist/monitor.rb, line 135 def self::load( file ) self.log.info "Loading monitor file %s..." % [ file ] Thread.current[ LOADED_INSTANCE_KEY ] = [] Kernel.load( file ) return Thread.current[ LOADED_INSTANCE_KEY ] ensure Thread.current[ LOADED_INSTANCE_KEY ] = nil end
Overridden to track instances of created nodes for the DSL.
# File lib/arborist/monitor.rb, line 119 def self::new( * ) new_instance = super Arborist::Monitor.add_loaded_instance( new_instance ) return new_instance end
Create a new Monitor
with the specified description
. If the block
is given, it will be evaluated in the context of the new Monitor
before it's returned.
# File lib/arborist/monitor.rb, line 154 def initialize( description=nil, key=nil, &block ) @key = key @description = description || self.class.name @interval = DEFAULT_INTERVAL @splay = Arborist::Monitor.splay @positive_criteria = {} @negative_criteria = {} @exclude_down = false @node_properties = [] @exec_command = nil @exec_block = nil @exec_callbacks_mod = Module.new @source = nil self.instance_exec( &block ) if block self.check_config end
Public Instance Methods
Check the monitor for sanity, raising an Arborist::ConfigError
if it isn't.
# File lib/arborist/monitor.rb, line 246 def check_config raise Arborist::ConfigError, "No description set" unless self.description raise Arborist::ConfigError, "No key set" unless self.key end
Get/set the description of the monitor.
# File lib/arborist/monitor.rb, line 253 def description( new_value=nil ) self.description = new_value if new_value return @description end
Specify that the monitor should be run every seconds
seconds.
# File lib/arborist/monitor.rb, line 322 def every( seconds=nil ) @interval = seconds if seconds return @interval end
Specify that the monitor should exclude nodes which match the specified criteria
when searching for nodes it will run against.
# File lib/arborist/monitor.rb, line 348 def exclude( criteria ) self.negative_criteria.merge!( criteria ) end
Flag for whether the monitor will include downed hosts in its search. Defaults to false
.
# File lib/arborist/monitor.rb, line 209 attr_predicate :exclude_down
Specify what should be run to do the actual monitoring. Accepts an Array of strings (which are passed to `spawn`), a block, or an object that responds to the run
method.
# File lib/arborist/monitor.rb, line 369 def exec( *command, &block ) unless command.empty? self.log.warn "Ignored block with exec %s (%p)" % [ command.first, block ] if block if command.first.respond_to?( :run ) runner = command.first @exec_block = runner.method( :run ) @node_properties |= runner.node_properties if runner.respond_to?( :node_properties ) else @exec_command = command.map( &:to_s ) end return end @exec_block = block end
Declare an argument-building callback for the command run by 'exec'. The block
should accept an Array of nodes and return an Array of arguments for the command.
# File lib/arborist/monitor.rb, line 389 def exec_arguments( &block ) self.exec_callbacks_mod.instance_exec( block ) do |method_body| define_method( :exec_arguments, &method_body ) end end
Set the module to use for the callbacks when interacting with the executed external command.
# File lib/arborist/monitor.rb, line 419 def exec_callbacks( mod ) self.log.info "Setting exec callbacks handler to: %p" % [ mod.name ] @node_properties |= mod.node_properties if mod.respond_to?( :node_properties ) self.exec_callbacks_mod = mod end
Declare an input-building callback for the command run by 'exec'. The block
should accept an Array of nodes and a writable IO object, and should write out the necessary input to drive the command to the IO.
# File lib/arborist/monitor.rb, line 399 def exec_input( &block ) self.exec_callbacks_mod.instance_exec( block ) do |method_body| define_method( :exec_input, &method_body ) end end
Declare a results handler block
that will be used to parse the results for external commands. The block should accept 2 or 3 arguments: a PID, an IO that will be opened to the command's STDOUT, and optionally an IO that will be opened to the command's STDERR.
# File lib/arborist/monitor.rb, line 410 def handle_results( &block ) self.exec_callbacks_mod.instance_exec( block ) do |method_body| define_method( :handle_results, &method_body ) end end
Return a string representation of the object suitable for debugging.
# File lib/arborist/monitor.rb, line 234 def inspect return "#<%p:%#x %s (every %ds +-%ds)>" % [ self.class, self.object_id * 2, self.description || "(no description)", @interval, @splay, ] end
Get/set the key used by the monitor.
# File lib/arborist/monitor.rb, line 260 def key( new_value=nil ) self.key = new_value if new_value return @key end
Specify that the monitor should include the specified criteria
when searching for nodes it will run against.
# File lib/arborist/monitor.rb, line 339 def match( criteria ) self.positive_criteria.merge!( criteria ) @exclude_down = self.exclude_down && Arborist::Node::UNREACHABLE_STATES.include?( self.positive_criteria[:status] ) end
Run the monitor
# File lib/arborist/monitor.rb, line 266 def run( nodes ) if self.exec_block return self.exec_block.call( nodes ) elsif self.exec_command command = self.exec_command return self.run_external_command( command, nodes ) end end
Run the external command
against the specified nodes
.
# File lib/arborist/monitor.rb, line 277 def run_external_command( command, nodes ) self.log.debug "Running external command %p for %d nodes" % [ command, nodes.size ] context = Arborist::Monitor::RunContext.new context.extend( self.exec_callbacks_mod ) if self.exec_callbacks_mod arguments = Array( context.exec_arguments(nodes) ) command += arguments.flatten( 1 ) self.log.debug " command after adding arguments: %p" % [ command ] child_stdin, parent_writer = IO.pipe parent_reader, child_stdout = IO.pipe parent_err_reader, child_stderr = IO.pipe self.log.debug "Spawning command: %s" % [ Shellwords.join(command) ] pid = Process.spawn( *command, out: child_stdout, in: child_stdin, err: child_stderr ) child_stdout.close child_stdin.close child_stderr.close context.exec_input( nodes, parent_writer ) parent_writer.close return context.handle_results( pid, parent_reader, parent_err_reader ) rescue SystemCallError => err self.log.error "%p while running external monitor command `%s`: %s" % [ err.class, Shellwords.join( command ), err.message ] self.log.debug " %s" % [ err.backtrace.join("\n ") ] return {} ensure if pid begin Process.kill( 0, pid ) # waitpid if it's still alive Process.waitpid( pid ) rescue Errno::ESRCH end end end
Specify the number of seconds of interval splay that should be used when running the monitor.
# File lib/arborist/monitor.rb, line 331 def splay( seconds=nil ) @splay = seconds if seconds return @splay end
Specify properties from each node to provide to the monitor.
# File lib/arborist/monitor.rb, line 362 def use( *properties ) @node_properties = properties end