arborist 0.2.0.pre20170519125456 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +670 -1
- data/History.md +67 -0
- data/Manifest.txt +9 -6
- data/README.md +1 -3
- data/Rakefile +39 -4
- data/TODO.md +22 -31
- data/lib/arborist.rb +9 -2
- data/lib/arborist/cli.rb +67 -85
- data/lib/arborist/client.rb +125 -59
- data/lib/arborist/command/ack.rb +86 -0
- data/lib/arborist/command/reset.rb +48 -0
- data/lib/arborist/command/start.rb +11 -1
- data/lib/arborist/command/summary.rb +173 -0
- data/lib/arborist/command/tree.rb +215 -0
- data/lib/arborist/command/watch.rb +22 -22
- data/lib/arborist/dependency.rb +24 -4
- data/lib/arborist/event.rb +18 -2
- data/lib/arborist/event/node.rb +6 -2
- data/lib/arborist/event/node_warn.rb +16 -0
- data/lib/arborist/manager.rb +179 -48
- data/lib/arborist/mixins.rb +11 -0
- data/lib/arborist/monitor.rb +29 -17
- data/lib/arborist/monitor/connection_batching.rb +293 -0
- data/lib/arborist/monitor/socket.rb +101 -167
- data/lib/arborist/monitor_runner.rb +101 -24
- data/lib/arborist/node.rb +297 -68
- data/lib/arborist/node/ack.rb +1 -1
- data/lib/arborist/node/host.rb +26 -5
- data/lib/arborist/node/resource.rb +14 -5
- data/lib/arborist/node/root.rb +12 -3
- data/lib/arborist/node/service.rb +29 -26
- data/lib/arborist/node_subscription.rb +65 -0
- data/lib/arborist/observer.rb +8 -0
- data/lib/arborist/observer/action.rb +6 -0
- data/lib/arborist/subscription.rb +22 -16
- data/lib/arborist/tree_api.rb +7 -2
- data/spec/arborist/client_spec.rb +157 -51
- data/spec/arborist/dependency_spec.rb +21 -0
- data/spec/arborist/event/node_spec.rb +5 -0
- data/spec/arborist/event_spec.rb +3 -3
- data/spec/arborist/manager_spec.rb +626 -347
- data/spec/arborist/mixins_spec.rb +19 -0
- data/spec/arborist/monitor/socket_spec.rb +1 -2
- data/spec/arborist/monitor_runner_spec.rb +81 -29
- data/spec/arborist/monitor_spec.rb +89 -14
- data/spec/arborist/node/host_spec.rb +68 -0
- data/spec/arborist/node/resource_spec.rb +2 -0
- data/spec/arborist/node/root_spec.rb +13 -0
- data/spec/arborist/node/service_spec.rb +9 -0
- data/spec/arborist/node_spec.rb +673 -111
- data/spec/arborist/node_subscription_spec.rb +54 -0
- data/spec/arborist/observer/action_spec.rb +6 -0
- data/spec/arborist/observer_runner_spec.rb +8 -1
- data/spec/arborist/tree_api_spec.rb +111 -8
- data/spec/data/monitors/pings.rb +0 -11
- data/spec/data/monitors/port_checks.rb +0 -9
- data/spec/data/nodes/sidonie.rb +1 -0
- data/spec/data/nodes/vhosts.rb +23 -0
- data/spec/data/nodes/yevaud.rb +4 -2
- data/spec/spec_helper.rb +71 -1
- metadata +91 -28
- metadata.gz.sig +0 -0
- data/Events.md +0 -35
- data/Monitors.md +0 -155
- data/Nodes.md +0 -70
- data/Observers.md +0 -72
- data/Protocol.md +0 -276
- data/Tutorial.md +0 -8
data/lib/arborist/dependency.rb
CHANGED
@@ -221,10 +221,22 @@ class Arborist::Dependency
|
|
221
221
|
### Return an English description of why this dependency is not met. If it is
|
222
222
|
### met, returns +nil+.
|
223
223
|
def down_reason
|
224
|
-
|
225
|
-
|
224
|
+
parts = [
|
225
|
+
self.down_primary_reasons,
|
226
|
+
self.down_secondary_reasons
|
227
|
+
].compact
|
228
|
+
|
229
|
+
return nil if parts.empty?
|
230
|
+
|
231
|
+
return parts.join( '; ' )
|
232
|
+
end
|
226
233
|
|
227
|
-
|
234
|
+
|
235
|
+
### Return an English description of why any first tier dependencies are not met,
|
236
|
+
### or +nil+ if there are none.
|
237
|
+
def down_primary_reasons
|
238
|
+
ids = self.down_identifiers
|
239
|
+
return nil if ids.empty?
|
228
240
|
|
229
241
|
msg = nil
|
230
242
|
case self.behavior
|
@@ -245,8 +257,16 @@ class Arborist::Dependency
|
|
245
257
|
else
|
246
258
|
raise "Don't know how to build a description of down behavior for %p" % [ self.behavior ]
|
247
259
|
end
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
### Return an English description of why any subdependencies are not met,
|
264
|
+
### or +nil+ if there are none.
|
265
|
+
def down_secondary_reasons
|
266
|
+
subdeps = self.down_subdeps
|
267
|
+
return nil if subdeps.empty?
|
248
268
|
|
249
|
-
return
|
269
|
+
return subdeps.map( &:down_reason ).join( ' and ' )
|
250
270
|
end
|
251
271
|
|
252
272
|
|
data/lib/arborist/event.rb
CHANGED
@@ -58,11 +58,27 @@ class Arborist::Event
|
|
58
58
|
### Return the event as a Hash.
|
59
59
|
def to_h
|
60
60
|
return {
|
61
|
-
|
62
|
-
|
61
|
+
type: self.type,
|
62
|
+
data: self.payload
|
63
63
|
}
|
64
64
|
end
|
65
65
|
|
66
|
+
|
67
|
+
### Return a string representation of the object suitable for debugging.
|
68
|
+
def inspect
|
69
|
+
return "#<%p:%#016x %s>" % [
|
70
|
+
self.class,
|
71
|
+
self.object_id * 2,
|
72
|
+
self.inspect_details,
|
73
|
+
]
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
### Return the detail portion of the #inspect string appropriate for this event type.
|
78
|
+
def inspect_details
|
79
|
+
return self.payload.inspect
|
80
|
+
end
|
81
|
+
|
66
82
|
end # class Arborist::Event
|
67
83
|
|
68
84
|
|
data/lib/arborist/event/node.rb
CHANGED
@@ -38,9 +38,13 @@ class Arborist::Event::Node < Arborist::Event
|
|
38
38
|
end
|
39
39
|
|
40
40
|
|
41
|
-
### Inject
|
41
|
+
### Inject useful node metadata into the generated hash.
|
42
42
|
def to_h
|
43
|
-
return super.merge(
|
43
|
+
return super.merge(
|
44
|
+
identifier: self.node.identifier,
|
45
|
+
parent: self.node.parent,
|
46
|
+
nodetype: self.node.type
|
47
|
+
)
|
44
48
|
end
|
45
49
|
|
46
50
|
end # module Arborist::Event::NodeMatching
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'arborist/event' unless defined?( Arborist::Event )
|
5
|
+
require 'arborist/event/node'
|
6
|
+
|
7
|
+
|
8
|
+
# An event generated when a monitor adds the first warning to a node.
|
9
|
+
class Arborist::Event::NodeWarn < Arborist::Event::Node
|
10
|
+
|
11
|
+
### Create a new NodeWarn event for the specified +node+.
|
12
|
+
def initialize( node )
|
13
|
+
super( node, node.warnings.to_h )
|
14
|
+
end
|
15
|
+
|
16
|
+
end # class Arborist::Event::NodeWarn
|
data/lib/arborist/manager.rb
CHANGED
@@ -22,7 +22,8 @@ class Arborist::Manager
|
|
22
22
|
extend Configurability,
|
23
23
|
Loggability,
|
24
24
|
Arborist::MethodUtilities
|
25
|
-
include CZTop::Reactor::SignalHandling
|
25
|
+
include CZTop::Reactor::SignalHandling,
|
26
|
+
Arborist::HashUtilities
|
26
27
|
|
27
28
|
|
28
29
|
# Signals the manager responds to
|
@@ -31,6 +32,22 @@ class Arborist::Manager
|
|
31
32
|
# :TODO: :QUIT, :WINCH, :USR2, :TTIN, :TTOU
|
32
33
|
] & Signal.list.keys.map( &:to_sym )
|
33
34
|
|
35
|
+
# Array of actions supported by the Tree API
|
36
|
+
VALID_TREEAPI_ACTIONS = %w[
|
37
|
+
ack
|
38
|
+
deps
|
39
|
+
fetch
|
40
|
+
graft
|
41
|
+
modify
|
42
|
+
prune
|
43
|
+
search
|
44
|
+
status
|
45
|
+
subscribe
|
46
|
+
unack
|
47
|
+
unsubscribe
|
48
|
+
update
|
49
|
+
]
|
50
|
+
|
34
51
|
|
35
52
|
# Use the Arborist logger
|
36
53
|
log_to :arborist
|
@@ -169,7 +186,6 @@ class Arborist::Manager
|
|
169
186
|
def run
|
170
187
|
self.log.info "Getting ready to start the manager."
|
171
188
|
self.setup_sockets
|
172
|
-
self.publish_system_event( 'startup', start_time: Time.now.to_s, version: Arborist::VERSION )
|
173
189
|
self.register_timers
|
174
190
|
self.with_signal_handler( reactor, *QUEUE_SIGS ) do
|
175
191
|
self.start_accepting_requests
|
@@ -244,6 +260,17 @@ class Arborist::Manager
|
|
244
260
|
end
|
245
261
|
|
246
262
|
|
263
|
+
### Return a human-readable representation of the Manager suitable for debugging.
|
264
|
+
def inspect
|
265
|
+
return "#<%p:%#x {runid: %s} %d nodes>" % [
|
266
|
+
self.class,
|
267
|
+
self.object_id * 2,
|
268
|
+
self.run_id,
|
269
|
+
self.nodes.length,
|
270
|
+
]
|
271
|
+
end
|
272
|
+
|
273
|
+
|
247
274
|
#
|
248
275
|
# :section: Node state saving/reloading
|
249
276
|
#
|
@@ -251,6 +278,7 @@ class Arborist::Manager
|
|
251
278
|
### Write out the state of all the manager's nodes to the state_file if one is
|
252
279
|
### configured.
|
253
280
|
def save_node_states
|
281
|
+
start_time = Time.now
|
254
282
|
path = self.class.state_file or return
|
255
283
|
self.log.info "Saving current node state to %s" % [ path ]
|
256
284
|
tmpfile = Tempfile.create(
|
@@ -262,6 +290,7 @@ class Arborist::Manager
|
|
262
290
|
tmpfile.close
|
263
291
|
|
264
292
|
File.rename( tmpfile.path, path.to_s )
|
293
|
+
self.log.debug "Saved state file in %0.1f seconds." % [ Time.now - start_time ]
|
265
294
|
|
266
295
|
rescue SystemCallError => err
|
267
296
|
self.log.error "%p while saving node state: %s" % [ err.class, err.message ]
|
@@ -323,8 +352,15 @@ class Arborist::Manager
|
|
323
352
|
### Register a periodic timer that will save a snapshot of the node tree's state to the state
|
324
353
|
### file on a configured interval if one is configured.
|
325
354
|
def register_checkpoint_timer
|
326
|
-
|
327
|
-
|
355
|
+
unless self.class.state_file
|
356
|
+
self.log.info "No state file configured; skipping checkpoint timer setup."
|
357
|
+
return nil
|
358
|
+
end
|
359
|
+
interval = self.class.checkpoint_frequency
|
360
|
+
unless interval && interval.nonzero?
|
361
|
+
self.log.info "Checkpoint frequency is %p; skipping checkpoint timer setup." % [ interval ]
|
362
|
+
return nil
|
363
|
+
end
|
328
364
|
|
329
365
|
self.log.info "Setting up node state checkpoint every %0.3fs" % [ interval ]
|
330
366
|
@checkpoint_timer = self.reactor.add_periodic_timer( interval ) do
|
@@ -446,10 +482,8 @@ class Arborist::Manager
|
|
446
482
|
def add_node( node )
|
447
483
|
identifier = node.identifier
|
448
484
|
|
449
|
-
|
450
|
-
|
451
|
-
self.nodes[ identifier ] = node
|
452
|
-
end
|
485
|
+
raise Arborist::NodeError, "Node %p already present." % [ identifier ] if self.nodes[ identifier ]
|
486
|
+
self.nodes[ identifier ] = node
|
453
487
|
|
454
488
|
if self.tree_built?
|
455
489
|
self.link_node( node )
|
@@ -491,26 +525,26 @@ class Arborist::Manager
|
|
491
525
|
|
492
526
|
### Update the node with the specified +identifier+ with the given +new_properties+
|
493
527
|
### and propagate any events generated by the update to the node and its ancestors.
|
494
|
-
def update_node( identifier, new_properties )
|
528
|
+
def update_node( identifier, new_properties, monitor_key='_' )
|
495
529
|
unless (( node = self.nodes[identifier] ))
|
496
530
|
self.log.warn "Update for non-existent node %p ignored." % [ identifier ]
|
497
531
|
return []
|
498
532
|
end
|
499
533
|
|
500
|
-
events = node.update( new_properties )
|
534
|
+
events = node.update( new_properties, monitor_key )
|
501
535
|
self.propagate_events( node, events )
|
502
536
|
end
|
503
537
|
|
504
538
|
|
505
|
-
### Traverse the node tree and
|
539
|
+
### Traverse the node tree and return the specified +return_values+ from any nodes which
|
506
540
|
### match the given +filter+, skipping downed nodes and all their children
|
507
|
-
###
|
541
|
+
### if +exclude_down+ is set. If +return_values+ is set to +nil+, then all
|
508
542
|
### values from the node will be returned.
|
509
|
-
def
|
510
|
-
nodes_iter = if
|
511
|
-
self.all_nodes
|
512
|
-
else
|
543
|
+
def find_matching_node_states( filter, return_values, exclude_down=false, negative_filter={} )
|
544
|
+
nodes_iter = if exclude_down
|
513
545
|
self.reachable_nodes
|
546
|
+
else
|
547
|
+
self.all_nodes
|
514
548
|
end
|
515
549
|
|
516
550
|
states = nodes_iter.
|
@@ -551,7 +585,7 @@ class Arborist::Manager
|
|
551
585
|
### Set up the ZeroMQ REP socket for the Tree API.
|
552
586
|
def setup_tree_socket
|
553
587
|
@tree_socket = CZTop::Socket::REP.new
|
554
|
-
self.log.
|
588
|
+
self.log.info " binding the tree API socket (%#0x) to %p" %
|
555
589
|
[ @tree_socket.object_id * 2, Arborist.tree_api_url ]
|
556
590
|
@tree_socket.options.linger = 0
|
557
591
|
@tree_socket.bind( Arborist.tree_api_url )
|
@@ -569,7 +603,7 @@ class Arborist::Manager
|
|
569
603
|
def on_tree_socket_event( event )
|
570
604
|
if event.readable?
|
571
605
|
request = event.socket.receive
|
572
|
-
msg = self.
|
606
|
+
msg = self.dispatch_request( request )
|
573
607
|
event.socket << msg
|
574
608
|
else
|
575
609
|
raise "Unsupported event %p on tree API socket!" % [ event ]
|
@@ -578,14 +612,11 @@ class Arborist::Manager
|
|
578
612
|
|
579
613
|
|
580
614
|
### Handle the specified +raw_request+ and return a response.
|
581
|
-
def
|
615
|
+
def dispatch_request( raw_request )
|
582
616
|
raise "Manager is shutting down" unless self.running?
|
583
617
|
|
584
618
|
header, body = Arborist::TreeAPI.decode( raw_request )
|
585
|
-
|
586
|
-
header.key?( 'action' )
|
587
|
-
handler = self.lookup_tree_request_action( header ) or
|
588
|
-
raise Arborist::MessageError, "No such action '%s'" % [ header['action'] ]
|
619
|
+
handler = self.lookup_tree_request_action( header )
|
589
620
|
|
590
621
|
return handler.call( header, body )
|
591
622
|
|
@@ -611,16 +642,19 @@ class Arborist::Manager
|
|
611
642
|
raise Arborist::MessageError, "unsupported version %d" % [ header['version'] ] unless
|
612
643
|
header['version'] == 1
|
613
644
|
|
614
|
-
|
615
|
-
|
645
|
+
action = header['action'] or
|
646
|
+
raise Arborist::MessageError, "missing required header 'action'"
|
647
|
+
raise Arborist::MessageError, "No such action '%s'" % [ action ] unless
|
648
|
+
VALID_TREEAPI_ACTIONS.include?( action )
|
616
649
|
|
650
|
+
handler_name = "handle_%s_request" % [ action ]
|
617
651
|
return self.method( handler_name )
|
618
652
|
end
|
619
653
|
|
620
654
|
|
621
655
|
### Return a response to the `status` action.
|
622
656
|
def handle_status_request( header, body )
|
623
|
-
self.log.
|
657
|
+
self.log.info "STATUS: %p" % [ header ]
|
624
658
|
return Arborist::TreeAPI.successful_response(
|
625
659
|
server_version: Arborist::VERSION,
|
626
660
|
state: self.running? ? 'running' : 'not running',
|
@@ -632,7 +666,7 @@ class Arborist::Manager
|
|
632
666
|
|
633
667
|
### Return a response to the `subscribe` action.
|
634
668
|
def handle_subscribe_request( header, body )
|
635
|
-
self.log.
|
669
|
+
self.log.info "SUBSCRIBE: %p" % [ header ]
|
636
670
|
event_type = header[ 'event_type' ]
|
637
671
|
node_identifier = header[ 'identifier' ]
|
638
672
|
|
@@ -642,7 +676,7 @@ class Arborist::Manager
|
|
642
676
|
|
643
677
|
subscription = self.create_subscription( node_identifier, event_type, positive, negative )
|
644
678
|
self.log.info "Subscription to %s events at or under %s: %p" %
|
645
|
-
[ event_type, node_identifier || 'the root node', subscription ]
|
679
|
+
[ event_type || 'all', node_identifier || 'the root node', subscription ]
|
646
680
|
|
647
681
|
return Arborist::TreeAPI.successful_response( id: subscription.id )
|
648
682
|
end
|
@@ -650,7 +684,7 @@ class Arborist::Manager
|
|
650
684
|
|
651
685
|
### Return a response to the `unsubscribe` action.
|
652
686
|
def handle_unsubscribe_request( header, body )
|
653
|
-
self.log.
|
687
|
+
self.log.info "UNSUBSCRIBE: %p" % [ header ]
|
654
688
|
subscription_id = header[ 'subscription_id' ] or
|
655
689
|
return Arborist::TreeAPI.error_response( 'client', 'No identifier specified for UNSUBSCRIBE.' )
|
656
690
|
subscription = self.remove_subscription( subscription_id ) or
|
@@ -664,20 +698,25 @@ class Arborist::Manager
|
|
664
698
|
end
|
665
699
|
|
666
700
|
|
667
|
-
### Return a repsonse to the `
|
668
|
-
def
|
669
|
-
self.log.
|
701
|
+
### Return a repsonse to the `fetch` action.
|
702
|
+
def handle_fetch_request( header, body )
|
703
|
+
self.log.info "FETCH: %p" % [ header ]
|
670
704
|
from = header['from'] || '_'
|
671
705
|
depth = header['depth']
|
706
|
+
tree = header['tree']
|
672
707
|
|
673
|
-
start_node = self.nodes[ from ]
|
708
|
+
start_node = self.nodes[ from ] or
|
709
|
+
return Arborist::TreeAPI.error_response( 'client', "No such node %s." % [from] )
|
674
710
|
self.log.debug " Listing nodes under %p" % [ start_node ]
|
675
|
-
|
711
|
+
|
712
|
+
if tree
|
713
|
+
iter = [ start_node.to_h(depth: (depth || -1)) ]
|
714
|
+
elsif depth
|
676
715
|
self.log.debug " depth limited to %d" % [ depth ]
|
677
|
-
self.depth_limited_enumerator_for( start_node, depth )
|
716
|
+
iter = self.depth_limited_enumerator_for( start_node, depth )
|
678
717
|
else
|
679
718
|
self.log.debug " no depth limit"
|
680
|
-
self.enumerator_for( start_node )
|
719
|
+
iter = self.enumerator_for( start_node )
|
681
720
|
end
|
682
721
|
data = iter.map( &:to_h )
|
683
722
|
self.log.debug " got data for %d nodes" % [ data.length ]
|
@@ -686,11 +725,45 @@ class Arborist::Manager
|
|
686
725
|
end
|
687
726
|
|
688
727
|
|
689
|
-
### Return a response to the
|
690
|
-
def
|
691
|
-
self.log.
|
728
|
+
### Return a response to the `deps` action.
|
729
|
+
def handle_deps_request( header, body )
|
730
|
+
self.log.info "DEPS: %p" % [ header ]
|
731
|
+
from = header['from'] || '_'
|
732
|
+
|
733
|
+
deps = self.merge_dependencies_from( from )
|
734
|
+
deps.delete( from )
|
692
735
|
|
693
|
-
|
736
|
+
return Arborist::TreeAPI.successful_response({ deps: deps.to_a })
|
737
|
+
|
738
|
+
rescue Arborist::ClientError => err
|
739
|
+
return Arborist::TreeAPI.error_response( 'client', err.message )
|
740
|
+
end
|
741
|
+
|
742
|
+
|
743
|
+
### Recurse into the children and secondary dependencies of the +from+ node and
|
744
|
+
### merge the identifiers of the traversed nodes into the +deps_set+.
|
745
|
+
def merge_dependencies_from( from, deps_set=Set.new )
|
746
|
+
return deps_set unless deps_set.add?( from )
|
747
|
+
|
748
|
+
start_node = self.nodes[ from ] or
|
749
|
+
raise Arborist::ClientError "No such node %s." % [ from ]
|
750
|
+
|
751
|
+
self.enumerator_for( start_node ).each do |subnode|
|
752
|
+
deps_set.add( subnode.identifier )
|
753
|
+
subnode.node_subscribers.each do |subdep|
|
754
|
+
self.merge_dependencies_from( subdep, deps_set )
|
755
|
+
end
|
756
|
+
end
|
757
|
+
|
758
|
+
return deps_set
|
759
|
+
end
|
760
|
+
|
761
|
+
|
762
|
+
### Return a response to the 'search' action.
|
763
|
+
def handle_search_request( header, body )
|
764
|
+
self.log.info "SEARCH: %p" % [ header ]
|
765
|
+
|
766
|
+
exclude_down = header['exclude_down']
|
694
767
|
values = if header.key?( 'return' )
|
695
768
|
header['return'] || []
|
696
769
|
else
|
@@ -700,7 +773,7 @@ class Arborist::Manager
|
|
700
773
|
body = [ body ] unless body.is_a?( Array )
|
701
774
|
positive = body.shift
|
702
775
|
negative = body.shift || {}
|
703
|
-
states = self.
|
776
|
+
states = self.find_matching_node_states( positive, values, exclude_down, negative )
|
704
777
|
|
705
778
|
return Arborist::TreeAPI.successful_response( states )
|
706
779
|
end
|
@@ -708,14 +781,15 @@ class Arborist::Manager
|
|
708
781
|
|
709
782
|
### Update nodes using the data from the update request's +body+.
|
710
783
|
def handle_update_request( header, body )
|
711
|
-
self.log.
|
784
|
+
self.log.info "UPDATE: %p" % [ header ]
|
712
785
|
|
713
786
|
unless body.respond_to?( :each )
|
714
787
|
return Arborist::TreeAPI.error_response( 'client', 'Malformed update: body does not respond to #each' )
|
715
788
|
end
|
716
789
|
|
790
|
+
monitor_key = header['monitor_key']
|
717
791
|
body.each do |identifier, properties|
|
718
|
-
self.update_node( identifier, properties )
|
792
|
+
self.update_node( identifier, properties, monitor_key )
|
719
793
|
end
|
720
794
|
|
721
795
|
return Arborist::TreeAPI.successful_response( nil )
|
@@ -724,7 +798,7 @@ class Arborist::Manager
|
|
724
798
|
|
725
799
|
### Remove a node and its children.
|
726
800
|
def handle_prune_request( header, body )
|
727
|
-
self.log.
|
801
|
+
self.log.info "PRUNE: %p" % [ header ]
|
728
802
|
|
729
803
|
identifier = header[ 'identifier' ] or
|
730
804
|
return Arborist::TreeAPI.error_response( 'client', 'No identifier specified for PRUNE.' )
|
@@ -736,10 +810,15 @@ class Arborist::Manager
|
|
736
810
|
|
737
811
|
### Add a node
|
738
812
|
def handle_graft_request( header, body )
|
739
|
-
self.log.
|
813
|
+
self.log.info "GRAFT: %p" % [ header ]
|
740
814
|
|
741
815
|
identifier = header[ 'identifier' ] or
|
742
816
|
return Arborist::TreeAPI.error_response( 'client', 'No identifier specified for GRAFT.' )
|
817
|
+
|
818
|
+
if self.nodes[ identifier ]
|
819
|
+
return Arborist::TreeAPI.error_response( 'client', "Node %p already exists." % [identifier] )
|
820
|
+
end
|
821
|
+
|
743
822
|
type = header[ 'type' ] or
|
744
823
|
return Arborist::TreeAPI.error_response( 'client', 'No type specified for GRAFT.' )
|
745
824
|
parent = header[ 'parent' ] || '_'
|
@@ -765,7 +844,7 @@ class Arborist::Manager
|
|
765
844
|
|
766
845
|
### Modify a node's operational attributes
|
767
846
|
def handle_modify_request( header, body )
|
768
|
-
self.log.
|
847
|
+
self.log.info "MODIFY: %p" % [ header ]
|
769
848
|
|
770
849
|
identifier = header[ 'identifier' ] or
|
771
850
|
return Arborist::TreeAPI.error_response( 'client', 'No identifier specified for MODIFY.' )
|
@@ -775,12 +854,56 @@ class Arborist::Manager
|
|
775
854
|
|
776
855
|
self.log.debug "Modifying operational attributes of the %s node: %p" % [ identifier, body ]
|
777
856
|
|
857
|
+
if new_parent_identifier = body.delete( 'parent' )
|
858
|
+
old_parent = self.nodes[ node.parent ]
|
859
|
+
new_parent = self.nodes[ new_parent_identifier ] or
|
860
|
+
return Arborist::TreeAPI.error_response( 'client', "No such parent node: %p" % [new_parent_identifier] )
|
861
|
+
node.reparent( old_parent, new_parent )
|
862
|
+
end
|
863
|
+
|
778
864
|
node.modify( body )
|
779
865
|
|
780
866
|
return Arborist::TreeAPI.successful_response( nil )
|
781
867
|
end
|
782
868
|
|
783
869
|
|
870
|
+
### Acknowledge a node
|
871
|
+
def handle_ack_request( header, body )
|
872
|
+
self.log.info "ACK: %p" % [ header ]
|
873
|
+
|
874
|
+
identifier = header[ 'identifier' ] or
|
875
|
+
return Arborist::TreeAPI.error_response( 'client', 'No identifier specified for ACK.' )
|
876
|
+
node = self.nodes[ identifier ] or
|
877
|
+
return Arborist::TreeAPI.error_response( 'client', "No such node %p" % [identifier] )
|
878
|
+
|
879
|
+
self.log.debug "Acking the %s node: %p" % [ identifier, body ]
|
880
|
+
|
881
|
+
body = symbolify_keys( body )
|
882
|
+
events = node.acknowledge( **body )
|
883
|
+
self.propagate_events( node, events )
|
884
|
+
|
885
|
+
return Arborist::TreeAPI.successful_response( nil )
|
886
|
+
end
|
887
|
+
|
888
|
+
|
889
|
+
### Un-acknowledge a node
|
890
|
+
def handle_unack_request( header, body )
|
891
|
+
self.log.info "UNACK: %p" % [ header ]
|
892
|
+
|
893
|
+
identifier = header[ 'identifier' ] or
|
894
|
+
return Arborist::TreeAPI.error_response( 'client', 'No identifier specified for UNACK.' )
|
895
|
+
node = self.nodes[ identifier ] or
|
896
|
+
return Arborist::TreeAPI.error_response( 'client', "No such node %p" % [identifier] )
|
897
|
+
|
898
|
+
self.log.debug "Unacking the %s node: %p" % [ identifier, body ]
|
899
|
+
|
900
|
+
events = node.unacknowledge
|
901
|
+
self.propagate_events( node, events )
|
902
|
+
|
903
|
+
return Arborist::TreeAPI.successful_response( nil )
|
904
|
+
end
|
905
|
+
|
906
|
+
|
784
907
|
### Return the current root node.
|
785
908
|
def root_node
|
786
909
|
return self.nodes[ '_' ]
|
@@ -859,7 +982,7 @@ class Arborist::Manager
|
|
859
982
|
### Set up the ZMQ PUB socket for published events.
|
860
983
|
def setup_event_socket
|
861
984
|
@event_socket = CZTop::Socket::PUB.new
|
862
|
-
self.log.
|
985
|
+
self.log.info " binding the event socket (%#0x) to %p" %
|
863
986
|
[ @event_socket.object_id * 2, Arborist.event_api_url ]
|
864
987
|
@event_socket.options.linger = ( self.linger * 1000 ).ceil
|
865
988
|
@event_socket.bind( Arborist.event_api_url )
|
@@ -893,6 +1016,7 @@ class Arborist::Manager
|
|
893
1016
|
|
894
1017
|
### Register the publisher with the reactor if it's not already.
|
895
1018
|
def register_event_socket
|
1019
|
+
self.log.debug "Registering event socket for write events."
|
896
1020
|
self.reactor.enable_events( self.event_socket, :write ) unless
|
897
1021
|
self.reactor.event_enabled?( self.event_socket, :write )
|
898
1022
|
end
|
@@ -900,6 +1024,7 @@ class Arborist::Manager
|
|
900
1024
|
|
901
1025
|
### Unregister the event publisher socket from the reactor if it's registered.
|
902
1026
|
def unregister_event_socket
|
1027
|
+
self.log.debug "Unregistering event socket for write events."
|
903
1028
|
self.reactor.disable_events( self.event_socket, :write ) if
|
904
1029
|
self.reactor.event_enabled?( self.event_socket, :write )
|
905
1030
|
end
|
@@ -909,6 +1034,7 @@ class Arborist::Manager
|
|
909
1034
|
def on_event_socket_event( event )
|
910
1035
|
if event.writable?
|
911
1036
|
if (( msg = self.event_queue.shift ))
|
1037
|
+
# self.log.debug "Publishing event %p" % [ msg ]
|
912
1038
|
event.socket << msg
|
913
1039
|
end
|
914
1040
|
else
|
@@ -921,7 +1047,12 @@ class Arborist::Manager
|
|
921
1047
|
|
922
1048
|
### Publish a system event that observers can watch for to detect restarts.
|
923
1049
|
def publish_heartbeat_event
|
924
|
-
|
1050
|
+
return unless self.start_time
|
1051
|
+
self.publish_system_event( 'heartbeat',
|
1052
|
+
run_id: self.run_id,
|
1053
|
+
start_time: self.start_time.iso8601,
|
1054
|
+
version: Arborist::VERSION
|
1055
|
+
)
|
925
1056
|
end
|
926
1057
|
|
927
1058
|
|