Class: Arborist::Monitor
- Inherits:
-
Object
- Object
- Arborist::Monitor
- Extended by:
- MethodUtilities, Configurability, Loggability
- Defined in:
- lib/arborist/monitor.rb
Overview
A declaration of an action to run against Manager nodes to update their state.
Defined Under Namespace
Modules: ConnectionBatching, DefaultCallbacks, Socket Classes: RunContext
Constant Summary collapse
- LOADED_INSTANCE_KEY =
The key for the thread local that is used to track instances as they’re loaded.
:loaded_monitor_instances
- MONITOR_FILE_PATTERN =
The glob pattern to use for searching for monitors
'**/*.rb'
- DEFAULT_INTERVAL =
The default monitoring interval, in seconds
5.minutes
Instance Attribute Summary collapse
-
#description(new_value = nil) ⇒ Object
Get/set the description of the monitor.
-
#exec_block ⇒ Object
The callback to invoke when the monitor is run.
-
#exec_callbacks_mod ⇒ Object
The monitor’s execution callbacks contained in a Module.
-
#exec_command ⇒ Object
The shell command to exec when running the monitor (if any).
-
#interval ⇒ Object
writeonly
The interval between runs in seconds, as set by ‘every`.
-
#key(new_value = nil) ⇒ Object
Get/set the key used by the monitor.
-
#negative_criteria ⇒ Object
readonly
A Hash of criteria to pass to the Manager to filter out nodes to monitor.
-
#node_properties ⇒ Object
readonly
The list of node properties to include when running the monitor.
-
#positive_criteria ⇒ Object
readonly
A Hash of criteria to pass to the Manager when searching for nodes to monitor.
-
#source ⇒ Object
The path to the source this Monitor was loaded from, if applicable.
-
#splay(seconds = nil) ⇒ Object
Specify the number of seconds of interval splay that should be used when running the monitor.
Class Method Summary collapse
-
.add_loaded_instance(new_instance) ⇒ Object
Record a new loaded instance if the Thread-local variable is set up to track them.
-
.each_in(loader) ⇒ Object
Return an iterator for all the monitors supplied by the specified
loader
. -
.load(file) ⇒ Object
Load the specified
file
and return any new Nodes created as a result. -
.new ⇒ Object
Overridden to track instances of created nodes for the DSL.
Instance Method Summary collapse
-
#check_config ⇒ Object
Check the monitor for sanity, raising an Arborist::ConfigError if it isn’t.
-
#every(seconds = nil) ⇒ Object
(also: #interval)
Specify that the monitor should be run every
seconds
seconds. -
#exclude(criteria) ⇒ Object
Specify that the monitor should exclude nodes which match the specified
criteria
when searching for nodes it will run against. -
#exclude_down(flag = nil) ⇒ Object
Specify that the monitor should (or should not) include nodes which have been marked ‘down’.
-
#exec(*command, &block) ⇒ Object
Specify what should be run to do the actual monitoring.
-
#exec_arguments(&block) ⇒ Object
Declare an argument-building callback for the command run by ‘exec’.
-
#exec_callbacks(mod) ⇒ Object
Set the module to use for the callbacks when interacting with the executed external command.
-
#exec_input(&block) ⇒ Object
Declare an input-building callback for the command run by ‘exec’.
-
#handle_results(&block) ⇒ Object
Declare a results handler
block
that will be used to parse the results for external commands. -
#initialize(description = nil, key = nil, &block) ⇒ Monitor
constructor
Create a new Monitor with the specified
description
. -
#inspect ⇒ Object
Return a string representation of the object suitable for debugging.
-
#match(criteria) ⇒ Object
Specify that the monitor should include the specified
criteria
when searching for nodes it will run against. -
#run(nodes) ⇒ Object
Run the monitor.
-
#run_external_command(command, nodes) ⇒ Object
Run the external
command
against the specifiednodes
. -
#use(*properties) ⇒ Object
Specify properties from each node to provide to the monitor.
Methods included from MethodUtilities
attr_predicate, attr_predicate_accessor, dsl_accessor, singleton_attr_accessor, singleton_attr_reader, singleton_attr_writer, singleton_method_alias, singleton_predicate_accessor, singleton_predicate_reader
Constructor Details
#initialize(description = nil, key = nil, &block) ⇒ Monitor
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.
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/arborist/monitor.rb', line 155 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 |
Instance Attribute Details
#description(new_value = nil) ⇒ Object
Get/set the description of the monitor.
254 255 256 257 |
# File 'lib/arborist/monitor.rb', line 254 def description( new_value=nil ) self.description = new_value if new_value return @description end |
#exec_block ⇒ Object
The callback to invoke when the monitor is run.
223 224 225 |
# File 'lib/arborist/monitor.rb', line 223 def exec_block @exec_block end |
#exec_callbacks_mod ⇒ Object
The monitor’s execution callbacks contained in a Module
227 228 229 |
# File 'lib/arborist/monitor.rb', line 227 def exec_callbacks_mod @exec_callbacks_mod end |
#exec_command ⇒ Object
The shell command to exec when running the monitor (if any). This can be any valid arguments to the ‘Kernel.spawn` method.
219 220 221 |
# File 'lib/arborist/monitor.rb', line 219 def exec_command @exec_command end |
#interval=(value) ⇒ Object (writeonly)
The interval between runs in seconds, as set by ‘every`.
193 194 195 |
# File 'lib/arborist/monitor.rb', line 193 def interval=(value) @interval = value end |
#key(new_value = nil) ⇒ Object
Get/set the key used by the monitor.
261 262 263 264 |
# File 'lib/arborist/monitor.rb', line 261 def key( new_value=nil ) self.key = new_value if new_value return @key end |
#negative_criteria ⇒ Object (readonly)
A Hash of criteria to pass to the Manager to filter out nodes to monitor.
205 206 207 |
# File 'lib/arborist/monitor.rb', line 205 def negative_criteria @negative_criteria end |
#node_properties ⇒ Object (readonly)
The list of node properties to include when running the monitor.
214 215 216 |
# File 'lib/arborist/monitor.rb', line 214 def node_properties @node_properties end |
#positive_criteria ⇒ Object (readonly)
A Hash of criteria to pass to the Manager when searching for nodes to monitor.
201 202 203 |
# File 'lib/arborist/monitor.rb', line 201 def positive_criteria @positive_criteria end |
#source ⇒ Object
The path to the source this Monitor was loaded from, if applicable
231 232 233 |
# File 'lib/arborist/monitor.rb', line 231 def source @source end |
#splay(seconds = nil) ⇒ Object
Specify the number of seconds of interval splay that should be used when running the monitor.
332 333 334 335 |
# File 'lib/arborist/monitor.rb', line 332 def splay( seconds=nil ) @splay = seconds if seconds return @splay end |
Class Method Details
.add_loaded_instance(new_instance) ⇒ Object
Record a new loaded instance if the Thread-local variable is set up to track them.
129 130 131 132 |
# File 'lib/arborist/monitor.rb', line 129 def self::add_loaded_instance( new_instance ) instances = Thread.current[ LOADED_INSTANCE_KEY ] or return instances << new_instance end |
.each_in(loader) ⇒ Object
Return an iterator for all the monitors supplied by the specified loader
.
147 148 149 |
# File 'lib/arborist/monitor.rb', line 147 def self::each_in( loader ) return loader.monitors end |
.load(file) ⇒ Object
Load the specified file
and return any new Nodes created as a result.
136 137 138 139 140 141 142 143 |
# File 'lib/arborist/monitor.rb', line 136 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 |
.new ⇒ Object
Overridden to track instances of created nodes for the DSL.
120 121 122 123 124 |
# File 'lib/arborist/monitor.rb', line 120 def self::new( * ) new_instance = super Arborist::Monitor.add_loaded_instance( new_instance ) return new_instance end |
Instance Method Details
#check_config ⇒ Object
Check the monitor for sanity, raising an Arborist::ConfigError if it isn’t.
247 248 249 250 |
# File 'lib/arborist/monitor.rb', line 247 def check_config raise Arborist::ConfigError, "No description set" unless self.description raise Arborist::ConfigError, "No key set" unless self.key end |
#every(seconds = nil) ⇒ Object Also known as: interval
Specify that the monitor should be run every seconds
seconds.
323 324 325 326 |
# File 'lib/arborist/monitor.rb', line 323 def every( seconds=nil ) @interval = seconds if seconds return @interval end |
#exclude(criteria) ⇒ Object
Specify that the monitor should exclude nodes which match the specified criteria
when searching for nodes it will run against.
349 350 351 |
# File 'lib/arborist/monitor.rb', line 349 def exclude( criteria ) self.negative_criteria.merge!( criteria ) end |
#exclude_down(flag = nil) ⇒ Object
Specify that the monitor should (or should not) include nodes which have been marked ‘down’.
210 |
# File 'lib/arborist/monitor.rb', line 210 attr_predicate :exclude_down |
#exec(*command, &block) ⇒ Object
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.
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
# File 'lib/arborist/monitor.rb', line 370 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 |
#exec_arguments(&block) ⇒ Object
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.
390 391 392 393 394 |
# File 'lib/arborist/monitor.rb', line 390 def exec_arguments( &block ) self.exec_callbacks_mod.instance_exec( block ) do |method_body| define_method( :exec_arguments, &method_body ) end end |
#exec_callbacks(mod) ⇒ Object
Set the module to use for the callbacks when interacting with the executed external command.
420 421 422 423 424 |
# File 'lib/arborist/monitor.rb', line 420 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 |
#exec_input(&block) ⇒ Object
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.
400 401 402 403 404 |
# File 'lib/arborist/monitor.rb', line 400 def exec_input( &block ) self.exec_callbacks_mod.instance_exec( block ) do |method_body| define_method( :exec_input, &method_body ) end end |
#handle_results(&block) ⇒ Object
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.
411 412 413 414 415 |
# File 'lib/arborist/monitor.rb', line 411 def handle_results( &block ) self.exec_callbacks_mod.instance_exec( block ) do |method_body| define_method( :handle_results, &method_body ) end end |
#inspect ⇒ Object
Return a string representation of the object suitable for debugging.
235 236 237 238 239 240 241 242 243 |
# File 'lib/arborist/monitor.rb', line 235 def inspect return "#<%p:%#x %s (every %ds +-%ds)>" % [ self.class, self.object_id * 2, self.description || "(no description)", @interval, @splay, ] end |
#match(criteria) ⇒ Object
Specify that the monitor should include the specified criteria
when searching for nodes it will run against.
340 341 342 343 344 |
# File 'lib/arborist/monitor.rb', line 340 def match( criteria ) self.positive_criteria.merge!( criteria ) @exclude_down = self.exclude_down && Arborist::Node::UNREACHABLE_STATES.include?( self.positive_criteria[:status] ) end |
#run(nodes) ⇒ Object
Run the monitor
267 268 269 270 271 272 273 274 |
# File 'lib/arborist/monitor.rb', line 267 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_external_command(command, nodes) ⇒ Object
Run the external command
against the specified nodes
.
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/arborist/monitor.rb', line 278 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. ] 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 |
#use(*properties) ⇒ Object
Specify properties from each node to provide to the monitor.
363 364 365 |
# File 'lib/arborist/monitor.rb', line 363 def use( *properties ) @node_properties = properties end |