A declaration of an action to run against Manager nodes to update their state.
The default monitoring interval, in seconds
The key for the thread local that is used to track instances as they're loaded.
The glob pattern to use for searching for monitors
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.
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
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
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
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