arborist 0.0.1.pre20160106113421 → 0.0.1.pre20160128152542
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|