arborist 0.0.1.pre20160106113421 → 0.0.1.pre20160128152542
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Manifest.txt +9 -3
- data/README.md +15 -6
- data/Rakefile +16 -2
- data/TODO.md +10 -1
- data/Tutorial.md +8 -0
- data/bin/arborist +8 -0
- data/lib/arborist.rb +12 -11
- data/lib/arborist/cli.rb +380 -0
- data/lib/arborist/client.rb +16 -0
- data/lib/arborist/command/client.rb +30 -0
- data/lib/arborist/command/config.rb +20 -0
- data/lib/arborist/command/start.rb +60 -0
- data/lib/arborist/command/watch.rb +88 -0
- data/lib/arborist/event/node_matching.rb +5 -0
- data/lib/arborist/loader.rb +43 -0
- data/lib/arborist/loader/file.rb +72 -0
- data/lib/arborist/manager.rb +4 -3
- data/lib/arborist/monitor.rb +8 -22
- data/lib/arborist/node.rb +57 -37
- data/lib/arborist/observer.rb +4 -18
- data/spec/arborist/client_spec.rb +1 -1
- data/spec/arborist/manager/tree_api_spec.rb +1 -0
- data/spec/arborist/manager_spec.rb +15 -4
- data/spec/arborist/monitor_spec.rb +13 -9
- data/spec/arborist/node_spec.rb +36 -6
- data/spec/arborist_spec.rb +36 -5
- data/spec/data/monitors/pings.rb +1 -0
- data/spec/data/monitors/port_checks.rb +0 -3
- data/spec/spec_helper.rb +4 -1
- metadata +18 -11
- data/bin/amanagerd +0 -10
- data/bin/amonitord +0 -12
- data/bin/aobserverd +0 -12
data/lib/arborist/client.rb
CHANGED
@@ -117,6 +117,22 @@ class Arborist::Client
|
|
117
117
|
end
|
118
118
|
|
119
119
|
|
120
|
+
### Remove a subscription
|
121
|
+
def unsubscribe( *args )
|
122
|
+
request = self.make_unsubscribe_request( *args )
|
123
|
+
response = self.send_tree_api_request( request )
|
124
|
+
return response
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
### Remove the subscription with the specified +subid+.
|
129
|
+
def make_unsubscribe_request( subid )
|
130
|
+
self.log.debug "Making unsubscribe request for subid: %s" % [ subid ]
|
131
|
+
|
132
|
+
return self.pack_message( :unsubscribe, subscription_id: subid )
|
133
|
+
end
|
134
|
+
|
135
|
+
|
120
136
|
### Send the packed +request+ via the Tree API socket, raise an error on
|
121
137
|
### unsuccessful response, and return the response body.
|
122
138
|
def send_tree_api_request( request )
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'arborist/cli' unless defined?( Arborist::CLI )
|
5
|
+
require 'arborist/client'
|
6
|
+
|
7
|
+
# Command to start an interactive client session.
|
8
|
+
module Arborist::CLI::Client
|
9
|
+
extend Arborist::CLI::Subcommand
|
10
|
+
|
11
|
+
desc 'Start an interactive client session'
|
12
|
+
long_desc <<-EOF
|
13
|
+
Starts a pry session in an Arborist::Client context.
|
14
|
+
EOF
|
15
|
+
|
16
|
+
command :client do |cmd|
|
17
|
+
cmd.action do |globals, options, args|
|
18
|
+
begin
|
19
|
+
require 'pry'
|
20
|
+
rescue LoadError
|
21
|
+
exit_now! "This command requires the 'pry' gem."
|
22
|
+
end
|
23
|
+
|
24
|
+
client = Arborist::Client.new
|
25
|
+
Pry.pry( client )
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end # module Arborist::CLI::Client
|
30
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'arborist/cli' unless defined?( Arborist::CLI )
|
5
|
+
|
6
|
+
|
7
|
+
# Command to dump a basic Arborist config file
|
8
|
+
module Arborist::CLI::Config
|
9
|
+
extend Arborist::CLI::Subcommand
|
10
|
+
|
11
|
+
desc 'Dump a default Arborist config file'
|
12
|
+
command :config do |cmd|
|
13
|
+
|
14
|
+
cmd.action do |globals, options, args|
|
15
|
+
$stdout.puts Configurability.default_config.dump
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end # module Arborist::CLI::Clone
|
20
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'arborist/cli' unless defined?( Arborist::CLI )
|
5
|
+
|
6
|
+
|
7
|
+
# Command to start a Arborist daemon
|
8
|
+
module Arborist::CLI::Start
|
9
|
+
extend Arborist::CLI::Subcommand
|
10
|
+
|
11
|
+
desc 'Start an Arborist daemon'
|
12
|
+
long_desc <<-EOF
|
13
|
+
Start the Arborist manager, observers, or monitors. The SOURCE is
|
14
|
+
passed to the loader to tell it where to load things from.
|
15
|
+
EOF
|
16
|
+
|
17
|
+
arg :DAEMON
|
18
|
+
arg :SOURCE
|
19
|
+
|
20
|
+
command :start do |cmd|
|
21
|
+
|
22
|
+
cmd.flag :loader, desc: "Specify a loader type to use.",
|
23
|
+
default_value: 'file'
|
24
|
+
|
25
|
+
cmd.action do |globals, options, args|
|
26
|
+
appname = args.shift
|
27
|
+
source = args.shift
|
28
|
+
|
29
|
+
loader = Arborist::Loader.create( options.loader, source )
|
30
|
+
runner = case appname
|
31
|
+
when 'manager'
|
32
|
+
Arborist.manager_for( loader )
|
33
|
+
when 'monitors'
|
34
|
+
Arborist.monitor_runner_for( loader )
|
35
|
+
when 'observers'
|
36
|
+
Arborist.observer_runner_for( loader )
|
37
|
+
else
|
38
|
+
raise "Don't know how to start %p" % [ appname ]
|
39
|
+
end
|
40
|
+
|
41
|
+
unless_dryrun( "starting #{appname}" ) do
|
42
|
+
start( runner )
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
###############
|
49
|
+
module_function
|
50
|
+
###############
|
51
|
+
|
52
|
+
### Start the specified +runner+ instance after setting up the environment for
|
53
|
+
### it.
|
54
|
+
def start( runner )
|
55
|
+
$0 = runner.class.name
|
56
|
+
runner.run
|
57
|
+
end
|
58
|
+
|
59
|
+
end # module Arborist::CLI::Start
|
60
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'msgpack'
|
5
|
+
|
6
|
+
require 'arborist/cli' unless defined?( Arborist::CLI )
|
7
|
+
require 'arborist/client'
|
8
|
+
|
9
|
+
# Command to watch events in an Arborist manager.
|
10
|
+
module Arborist::CLI::Watch
|
11
|
+
extend Arborist::CLI::Subcommand
|
12
|
+
|
13
|
+
desc 'Watch events in an Arborist manager'
|
14
|
+
|
15
|
+
command :watch do |cmd|
|
16
|
+
cmd.action do |globals, options, args|
|
17
|
+
client = Arborist::Client.new
|
18
|
+
subid = client.subscribe( identifier: '_' )
|
19
|
+
|
20
|
+
sock = client.event_api
|
21
|
+
sock.subscribe( subid )
|
22
|
+
prompt.say "Subscription %p" % [ subid ]
|
23
|
+
|
24
|
+
begin
|
25
|
+
prompt.say "Watching for events on manager at %s" % [ client.event_api_url ]
|
26
|
+
loop do
|
27
|
+
msgsubid = sock.recv
|
28
|
+
raise "Partial write?!" unless sock.rcvmore?
|
29
|
+
raw_event = sock.recv
|
30
|
+
|
31
|
+
event = MessagePack.unpack( raw_event )
|
32
|
+
prompt.say "[%s] %s\n" % [
|
33
|
+
hl(Time.now.strftime('%Y-%m-%d %H:%M:%S %Z')).color(:dark, :white),
|
34
|
+
hl(dump_event( event )).color( :dark, :white )
|
35
|
+
]
|
36
|
+
end
|
37
|
+
ensure
|
38
|
+
client.unsubscribe( subid )
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
###############
|
45
|
+
module_function
|
46
|
+
###############
|
47
|
+
|
48
|
+
### Return a String representation of the specified +event+.
|
49
|
+
def dump_event( event )
|
50
|
+
event_type = event['type']
|
51
|
+
id = event['identifier']
|
52
|
+
|
53
|
+
case event_type
|
54
|
+
when 'node.update'
|
55
|
+
type, status, error = event['data'].values_at( *%w'type status error' )
|
56
|
+
return "%s updated: %s is %s%s" % [
|
57
|
+
hl( id ).color( :cyan ),
|
58
|
+
type,
|
59
|
+
hl( status ).color( status.to_sym ),
|
60
|
+
error ? " (#{error})" : ''
|
61
|
+
]
|
62
|
+
when 'node.delta'
|
63
|
+
pairs = diff_pairs( event['data'] )
|
64
|
+
return "%s delta, changes: %s" % [ hl( id ).color( :cyan ), pairs ]
|
65
|
+
else
|
66
|
+
return "%s event: %p" % [ hl(event_type).color(:dark, :white), event ]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
### Return a string showing the differences in a delta event's change +data+.
|
72
|
+
def diff_pairs( data )
|
73
|
+
return data.collect do |key, pairs|
|
74
|
+
if pairs.is_a?( Hash )
|
75
|
+
diff_pairs( pairs )
|
76
|
+
else
|
77
|
+
val1, val2 = *pairs
|
78
|
+
"%s: %s -> %s" % [
|
79
|
+
hl( key ).color( :dark, :white ),
|
80
|
+
hl( val1 ).color( :yellow ),
|
81
|
+
hl( val2 ).color( :bold, :yellow )
|
82
|
+
]
|
83
|
+
end
|
84
|
+
end.join( hl(", ").color(:dark, :white) )
|
85
|
+
end
|
86
|
+
|
87
|
+
end # module Arborist::CLI::Watch
|
88
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'pluggability'
|
5
|
+
require 'arborist' unless defined?( Arborist )
|
6
|
+
|
7
|
+
|
8
|
+
# Abstract base class for Arborist loader strategies
|
9
|
+
class Arborist::Loader
|
10
|
+
extend Loggability,
|
11
|
+
Pluggability
|
12
|
+
|
13
|
+
|
14
|
+
# Loggability API -- use the Arborist logger
|
15
|
+
log_to :arborist
|
16
|
+
|
17
|
+
# Pluggability API -- search for loaders under the specified path
|
18
|
+
plugin_prefixes 'arborist/loader'
|
19
|
+
|
20
|
+
|
21
|
+
### Return an Enumerator that yields Arborist::Nodes loaded from the target
|
22
|
+
### directory.
|
23
|
+
def nodes
|
24
|
+
raise NotImplementedError, "%p needs to implement #nodes"
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
### Return an Enumerator that yields Arborist::Monitors loaded from the target
|
29
|
+
### directory.
|
30
|
+
def monitors
|
31
|
+
raise NotImplementedError, "%p needs to implement #monitors"
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
### Return an Enumerator that yields Arborist::Observers loaded from the target
|
36
|
+
### directory.
|
37
|
+
def observers
|
38
|
+
raise NotImplementedError, "%p needs to implement #observers"
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
end # class Arborist::Loader
|
43
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'arborist/loader' unless defined?( Arborist::Loader )
|
5
|
+
|
6
|
+
|
7
|
+
# A loader for Arborist that knows how to load stuff from files on disk.
|
8
|
+
class Arborist::Loader::File < Arborist::Loader
|
9
|
+
|
10
|
+
##
|
11
|
+
# The glob pattern to use for searching for files
|
12
|
+
FILE_PATTERN = '**/*.rb'
|
13
|
+
|
14
|
+
|
15
|
+
### Create a new loader that will read nodes from the specified +directory+.
|
16
|
+
def initialize( directory )
|
17
|
+
Arborist.load_all
|
18
|
+
@directory = directory
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# The directory to load files from
|
23
|
+
attr_reader :directory
|
24
|
+
|
25
|
+
|
26
|
+
### Return an Enumerator
|
27
|
+
def paths
|
28
|
+
path = Pathname( self.directory )
|
29
|
+
if path.directory?
|
30
|
+
return Pathname.glob( directory + FILE_PATTERN ).each
|
31
|
+
else
|
32
|
+
return [ path ].each
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
### Return an Enumerator that yields Arborist::Nodes loaded from the target
|
38
|
+
### directory.
|
39
|
+
def nodes
|
40
|
+
return self.enumerator_for( Arborist::Node )
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
### Return an Enumerator that yields Arborist::Monitors loaded from the target
|
45
|
+
### directory.
|
46
|
+
def monitors
|
47
|
+
return self.enumerator_for( Arborist::Monitor )
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
### Return an Enumerator that yields Arborist::Observers loaded from the target
|
52
|
+
### directory.
|
53
|
+
def observers
|
54
|
+
return self.enumerator_for( Arborist::Observer )
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
### Return an Enumerator that will instantiate and yield instances of the specified
|
59
|
+
### +arborist_class+ for each file path in the loader's directory.
|
60
|
+
def enumerator_for( arborist_class )
|
61
|
+
return Enumerator.new do |yielder|
|
62
|
+
self.paths.each do |file|
|
63
|
+
objects = arborist_class.load( file )
|
64
|
+
objects.each do |object|
|
65
|
+
object.source = "file://%s" % [ file.expand_path ]
|
66
|
+
yielder.yield( object )
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end # class Arborist::Loader::File
|
data/lib/arborist/manager.rb
CHANGED
@@ -243,6 +243,7 @@ class Arborist::Manager
|
|
243
243
|
end
|
244
244
|
end
|
245
245
|
|
246
|
+
|
246
247
|
### Handle any queued signals.
|
247
248
|
def process_signal_queue
|
248
249
|
# Look for any signals that arrived and handle them
|
@@ -288,7 +289,7 @@ class Arborist::Manager
|
|
288
289
|
end
|
289
290
|
|
290
291
|
|
291
|
-
### Handle a USR1 signal. Writes a message to the log
|
292
|
+
### Handle a USR1 signal. Writes a message to the log.
|
292
293
|
def on_user1_signal( signo )
|
293
294
|
self.log.info "Checkpoint: User signal."
|
294
295
|
end
|
@@ -436,7 +437,7 @@ class Arborist::Manager
|
|
436
437
|
### an Enumerator if no block is given.
|
437
438
|
def reachable_nodes( &block )
|
438
439
|
iter = self.enumerator_for( self.root ) do |node|
|
439
|
-
!node.down?
|
440
|
+
!(node.down? || node.disabled?)
|
440
441
|
end
|
441
442
|
return iter.each( &block ) if block
|
442
443
|
return iter
|
@@ -491,7 +492,7 @@ class Arborist::Manager
|
|
491
492
|
### Propagate one or more +events+ to the specified +node+ and its ancestors in the tree,
|
492
493
|
### publishing them to matching subscriptions belonging to the nodes along the way.
|
493
494
|
def propagate_events( node, *events )
|
494
|
-
self.log.
|
495
|
+
self.log.info "Propagating %d events to node %s" % [ events.length, node.identifier ]
|
495
496
|
node.publish_events( *events )
|
496
497
|
|
497
498
|
if node.parent
|
data/lib/arborist/monitor.rb
CHANGED
@@ -55,14 +55,14 @@ class Arborist::Monitor
|
|
55
55
|
def exec_input( nodes, io )
|
56
56
|
return if io.closed?
|
57
57
|
|
58
|
-
nodes.each do |
|
59
|
-
self.log.debug "Serializing node properties for %s" % [
|
60
|
-
prop_map =
|
58
|
+
nodes.each do |(identifier, data)|
|
59
|
+
self.log.debug "Serializing node properties for %s" % [ identifier ]
|
60
|
+
prop_map = data.collect do |key, val|
|
61
61
|
"%s=%s" % [key, Shellwords.escape(val)]
|
62
62
|
end
|
63
63
|
|
64
64
|
self.log.debug " writing %d properties to %p" % [ prop_map.size, io ]
|
65
|
-
io.puts "%s %s" % [
|
65
|
+
io.puts "%s %s" % [ identifier, prop_map.join(' ') ]
|
66
66
|
self.log.debug " wrote the node to FD %d" % [ io.fileno ]
|
67
67
|
end
|
68
68
|
|
@@ -134,24 +134,9 @@ class Arborist::Monitor
|
|
134
134
|
end
|
135
135
|
|
136
136
|
|
137
|
-
### Return an iterator for all the
|
138
|
-
def self::each_in(
|
139
|
-
|
140
|
-
paths = if path.directory?
|
141
|
-
Pathname.glob( directory + MONITOR_FILE_PATTERN )
|
142
|
-
else
|
143
|
-
[ path ]
|
144
|
-
end
|
145
|
-
|
146
|
-
return paths.flat_map do |file|
|
147
|
-
file_url = "file://%s" % [ file.expand_path ]
|
148
|
-
monitors = self.load( file )
|
149
|
-
self.log.debug "Loaded monitors %p..." % [ monitors ]
|
150
|
-
monitors.each do |monitor|
|
151
|
-
monitor.source = file_url
|
152
|
-
end
|
153
|
-
monitors
|
154
|
-
end
|
137
|
+
### Return an iterator for all the monitors supplied by the specified +loader+.
|
138
|
+
def self::each_in( loader )
|
139
|
+
return loader.monitors
|
155
140
|
end
|
156
141
|
|
157
142
|
|
@@ -371,6 +356,7 @@ class Arborist::Monitor
|
|
371
356
|
### Set the module to use for the callbacks when interacting with the executed
|
372
357
|
### external command.
|
373
358
|
def exec_callbacks( mod )
|
359
|
+
self.log.info "Setting exec callbacks handler to: %p" % [ mod.name ]
|
374
360
|
self.exec_callbacks_mod = mod
|
375
361
|
end
|
376
362
|
|