arborist 0.0.1.pre20160606141735 → 0.0.1.pre20160829140603
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ChangeLog +81 -2
- data/Events.md +11 -11
- data/Manifest.txt +2 -4
- data/TODO.md +9 -29
- data/lib/arborist.rb +6 -2
- data/lib/arborist/command/watch.rb +59 -8
- data/lib/arborist/loader/file.rb +1 -1
- data/lib/arborist/manager.rb +139 -50
- data/lib/arborist/manager/event_publisher.rb +29 -0
- data/lib/arborist/manager/tree_api.rb +16 -1
- data/lib/arborist/mixins.rb +36 -1
- data/lib/arborist/monitor.rb +12 -0
- data/lib/arborist/monitor/socket.rb +8 -9
- data/lib/arborist/monitor_runner.rb +2 -2
- data/lib/arborist/node.rb +16 -3
- data/lib/arborist/node/host.rb +25 -22
- data/lib/arborist/node/resource.rb +28 -14
- data/lib/arborist/node/service.rb +21 -2
- data/lib/arborist/observer_runner.rb +68 -7
- data/spec/arborist/client_spec.rb +3 -3
- data/spec/arborist/manager/event_publisher_spec.rb +0 -1
- data/spec/arborist/manager/tree_api_spec.rb +6 -5
- data/spec/arborist/manager_spec.rb +53 -24
- data/spec/arborist/node/resource_spec.rb +9 -0
- data/spec/arborist/node/service_spec.rb +16 -1
- data/spec/arborist/node_spec.rb +22 -12
- data/spec/arborist/observer_runner_spec.rb +58 -0
- data/spec/arborist/observer_spec.rb +15 -15
- data/spec/arborist/subscription_spec.rb +1 -1
- data/spec/data/nodes/{duir.rb → sub/duir.rb} +0 -0
- data/spec/data/observers/auditor.rb +2 -2
- data/spec/spec_helper.rb +1 -0
- metadata +30 -27
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -2
- data/lib/arborist/event/sys_node_added.rb +0 -10
- data/lib/arborist/event/sys_node_removed.rb +0 -10
- data/lib/arborist/event/sys_reloaded.rb +0 -15
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c13d6d8931471437945bbe16acd57fa7f17a01f8
|
4
|
+
data.tar.gz: 38d663d0f7fb9105727613bf61ff62a1496062f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 49431289b8d44fc52f40cdf60062905666a6f9de12c7c6ff8bbfb04d62e2f782f084fd824df08271ed264b513953b23baae0ccf9b4fd36b390a5d4f072c5b07f
|
7
|
+
data.tar.gz: 436d3fadba15758c0e9bd12656ada1ef3b7c5471c4b8ccaba431e02e338cae3576e3acbd579ea5f440937ff1af7b4717f118b6d4723691913f320c05afb82565
|
data/ChangeLog
CHANGED
@@ -1,3 +1,82 @@
|
|
1
|
+
2016-08-29 Michael Granger <ged@FaerieMUD.org>
|
2
|
+
|
3
|
+
* spec/data/nodes/duir.rb:
|
4
|
+
Merge with a5a6e5092024
|
5
|
+
[c961355d10c8] [tip]
|
6
|
+
|
7
|
+
2016-08-29 Mahlon E. Smith <mahlon@martini.nu>
|
8
|
+
|
9
|
+
* lib/arborist/loader/file.rb, spec/data/nodes/duir.rb,
|
10
|
+
spec/data/nodes/sub/duir.rb:
|
11
|
+
Fix the file loader so it correctly crawls subdirectories.
|
12
|
+
[a5a6e5092024]
|
13
|
+
|
14
|
+
2016-08-03 Michael Granger <ged@FaerieMUD.org>
|
15
|
+
|
16
|
+
* lib/arborist/event/sys_node_added.rb,
|
17
|
+
lib/arborist/event/sys_node_removed.rb,
|
18
|
+
lib/arborist/event/sys_reloaded.rb,
|
19
|
+
lib/arborist/event/sys_shutdown.rb:
|
20
|
+
Remove system event node classes
|
21
|
+
[a34a23ef095e]
|
22
|
+
|
23
|
+
2016-08-02 Michael Granger <ged@FaerieMUD.org>
|
24
|
+
|
25
|
+
* Manifest.txt, TODO.md, lib/arborist/mixins.rb,
|
26
|
+
lib/arborist/monitor.rb, lib/arborist/monitor_runner.rb,
|
27
|
+
lib/arborist/node.rb, lib/arborist/node/host.rb,
|
28
|
+
lib/arborist/node/service.rb, spec/arborist/node/service_spec.rb,
|
29
|
+
spec/arborist/node_spec.rb, spec/arborist/subscription_spec.rb:
|
30
|
+
- Update the TO-DO list
|
31
|
+
- Factor out some network functions into a common mixin
|
32
|
+
- Add Arborist::Monitor#inspect
|
33
|
+
- Restore ack+status of serialized nodes
|
34
|
+
- Allow services to set their own address when not bound to INADDR_ANY
|
35
|
+
- Finish separating node events from system events
|
36
|
+
[f853508dba8c] [github/master]
|
37
|
+
|
38
|
+
* lib/arborist/monitor/socket.rb:
|
39
|
+
Fix race to calculate select timeout in socket monitor
|
40
|
+
[56a6dd2a8e40]
|
41
|
+
|
42
|
+
2016-07-20 Michael Granger <ged@FaerieMUD.org>
|
43
|
+
|
44
|
+
* TODO.md:
|
45
|
+
Update the To-Do file
|
46
|
+
[53ca09b4d50e]
|
47
|
+
|
48
|
+
2016-07-20 Mahlon E. Smith <mahlon@martini.nu>
|
49
|
+
|
50
|
+
* lib/arborist/node/resource.rb, spec/arborist/node/resource_spec.rb:
|
51
|
+
Add a `category` attribute to the resource node type.
|
52
|
+
[ae3aa8435e15]
|
53
|
+
|
54
|
+
2016-07-20 Michael Granger <ged@FaerieMUD.org>
|
55
|
+
|
56
|
+
* .gems, .tm_properties, Events.md, TODO.md, arborist.gemspec,
|
57
|
+
lib/arborist.rb, lib/arborist/command/watch.rb,
|
58
|
+
lib/arborist/event/sys_shutdown.rb, lib/arborist/manager.rb,
|
59
|
+
lib/arborist/manager/event_publisher.rb,
|
60
|
+
lib/arborist/manager/tree_api.rb, lib/arborist/node.rb,
|
61
|
+
lib/arborist/observer_runner.rb, spec/arborist/client_spec.rb,
|
62
|
+
spec/arborist/manager/event_publisher_spec.rb,
|
63
|
+
spec/arborist/manager/tree_api_spec.rb,
|
64
|
+
spec/arborist/manager_spec.rb,
|
65
|
+
spec/arborist/observer_runner_spec.rb, spec/spec_helper.rb:
|
66
|
+
Send system events from the manager for status changes
|
67
|
+
[4cc9924e6b0c]
|
68
|
+
|
69
|
+
2016-06-15 Michael Granger <ged@FaerieMUD.org>
|
70
|
+
|
71
|
+
* spec/arborist/observer_spec.rb, spec/data/observers/auditor.rb:
|
72
|
+
Fix signature of observer actions in specs
|
73
|
+
[196443e52d87]
|
74
|
+
|
75
|
+
* lib/arborist/node.rb, lib/arborist/node/host.rb,
|
76
|
+
spec/arborist/node_spec.rb:
|
77
|
+
Add a config hash to node operational metadata
|
78
|
+
[196223da8ba4]
|
79
|
+
|
1
80
|
2016-05-19 Mahlon E. Smith <mahlon@martini.nu>
|
2
81
|
|
3
82
|
* lib/arborist/node/resource.rb, spec/arborist/node/resource_spec.rb:
|
@@ -9,7 +88,7 @@
|
|
9
88
|
isn't a service with a protocol and port, but a necessary piece of
|
10
89
|
information for overall host and service health, that should be
|
11
90
|
monitored (and potentially ack'ed/disabled) independently.
|
12
|
-
[b4324ab8853e]
|
91
|
+
[b4324ab8853e]
|
13
92
|
|
14
93
|
2016-05-18 Mahlon E. Smith <mahlon@laika.com>
|
15
94
|
|
@@ -23,7 +102,7 @@
|
|
23
102
|
lib/arborist/node.rb, spec/arborist/dependency_spec.rb,
|
24
103
|
spec/arborist/node_spec.rb:
|
25
104
|
Fix the way nodes' dependency state is restored
|
26
|
-
[325443abe6df]
|
105
|
+
[325443abe6df]
|
27
106
|
|
28
107
|
2016-04-27 Michael Granger <ged@FaerieMUD.org>
|
29
108
|
|
data/Events.md
CHANGED
@@ -2,19 +2,19 @@
|
|
2
2
|
|
3
3
|
## Event Types
|
4
4
|
|
5
|
-
«type».«subtype
|
5
|
+
«type».«subtype»
|
6
6
|
|
7
|
-
|
8
|
-
sys.shutdown
|
7
|
+
node.acked
|
9
8
|
node.delta
|
9
|
+
node.disabled
|
10
|
+
node.down
|
11
|
+
node.quieted
|
12
|
+
node.unknown
|
13
|
+
node.up
|
10
14
|
node.update
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
- update
|
16
|
-
- change (delta)
|
17
|
-
- reload
|
18
|
-
- shutdown / startup
|
15
|
+
sys.node_added
|
16
|
+
sys.node_removed
|
17
|
+
sys.reloaded
|
18
|
+
sys.hearbeat
|
19
19
|
|
20
20
|
|
data/Manifest.txt
CHANGED
@@ -31,9 +31,6 @@ lib/arborist/event/node_quieted.rb
|
|
31
31
|
lib/arborist/event/node_unknown.rb
|
32
32
|
lib/arborist/event/node_up.rb
|
33
33
|
lib/arborist/event/node_update.rb
|
34
|
-
lib/arborist/event/sys_node_added.rb
|
35
|
-
lib/arborist/event/sys_node_removed.rb
|
36
|
-
lib/arborist/event/sys_reloaded.rb
|
37
34
|
lib/arborist/exceptions.rb
|
38
35
|
lib/arborist/loader.rb
|
39
36
|
lib/arborist/loader/file.rb
|
@@ -77,6 +74,7 @@ spec/arborist/node/service_spec.rb
|
|
77
74
|
spec/arborist/node_spec.rb
|
78
75
|
spec/arborist/observer/action_spec.rb
|
79
76
|
spec/arborist/observer/summarize_spec.rb
|
77
|
+
spec/arborist/observer_runner_spec.rb
|
80
78
|
spec/arborist/observer_spec.rb
|
81
79
|
spec/arborist/subscription_spec.rb
|
82
80
|
spec/arborist_spec.rb
|
@@ -84,9 +82,9 @@ spec/data/monitors/pings.rb
|
|
84
82
|
spec/data/monitors/port_checks.rb
|
85
83
|
spec/data/monitors/system_resources.rb
|
86
84
|
spec/data/monitors/web_services.rb
|
87
|
-
spec/data/nodes/duir.rb
|
88
85
|
spec/data/nodes/localhost.rb
|
89
86
|
spec/data/nodes/sidonie.rb
|
87
|
+
spec/data/nodes/sub/duir.rb
|
90
88
|
spec/data/nodes/yevaud.rb
|
91
89
|
spec/data/observers/auditor.rb
|
92
90
|
spec/data/observers/webservices.rb
|
data/TODO.md
CHANGED
@@ -7,43 +7,23 @@
|
|
7
7
|
* Performance/profiling examination
|
8
8
|
|
9
9
|
|
10
|
-
### Manager
|
11
|
-
|
12
|
-
* Only restore timestamps from serialized node dependencies, not the deps themselves.
|
13
|
-
|
14
|
-
* Broadcast system events:
|
15
|
-
- `sys.node.added`
|
16
|
-
- `sys.node.removed`
|
17
|
-
- `sys.startup`
|
18
|
-
- `sys.shutdown`
|
19
|
-
|
20
|
-
|
21
10
|
### Observers
|
22
11
|
|
23
|
-
*
|
24
|
-
*
|
12
|
+
* Add `exclude` to observers DSL
|
13
|
+
* modify tree api to accept negative criteria to subscribe
|
14
|
+
* pass to manager's create_subscription()
|
15
|
+
* alter subscription to no-op if event matches negative stuff
|
25
16
|
|
26
17
|
|
27
|
-
### Nodes
|
28
|
-
|
29
|
-
* Allow a service node to not inherit all of its host's addresses (i.e., be bound to one address only or whatever)
|
30
|
-
* Resource nodes: disk, load, process checks, etc. Anything that might
|
31
|
-
be considered a problem, that you'd want to ack independantly of the
|
32
|
-
Host node they are attached to.
|
33
|
-
|
34
18
|
### Monitor
|
35
19
|
|
36
20
|
* Add some default monitor types and utilities
|
37
|
-
-
|
38
|
-
-
|
39
|
-
-
|
40
|
-
|
41
|
-
* Gems for monitor types that have external dependency
|
42
|
-
- SNMP
|
43
|
-
|
44
|
-
### Watch Command
|
21
|
+
- ftp
|
22
|
+
- imap
|
23
|
+
- pop
|
24
|
+
- smtp
|
45
25
|
|
46
|
-
*
|
26
|
+
* Write a gem for `fping` monitor
|
47
27
|
|
48
28
|
|
49
29
|
## Second Release (0.2)
|
data/lib/arborist.rb
CHANGED
@@ -17,7 +17,7 @@ module Arborist
|
|
17
17
|
VERSION = '0.0.1'
|
18
18
|
|
19
19
|
# Version control revision
|
20
|
-
REVISION = %q$Revision:
|
20
|
+
REVISION = %q$Revision: 4cc9924e6b0c $
|
21
21
|
|
22
22
|
|
23
23
|
# The name of the environment variable which can be used to set the config path
|
@@ -168,7 +168,11 @@ module Arborist
|
|
168
168
|
|
169
169
|
### Fetch the ZMQ context for Arborist.
|
170
170
|
def self::zmq_context
|
171
|
-
return @zmq_context ||=
|
171
|
+
return @zmq_context ||= begin
|
172
|
+
self.log.info "Using ZeroMQ %s/CZMQ %s" %
|
173
|
+
[ ZMQ.version.join('.'), ZMQ.czmq_version.join('.') ]
|
174
|
+
ZMQ::Context.new
|
175
|
+
end
|
172
176
|
end
|
173
177
|
|
174
178
|
|
@@ -10,31 +10,65 @@ require 'arborist/client'
|
|
10
10
|
module Arborist::CLI::Watch
|
11
11
|
extend Arborist::CLI::Subcommand
|
12
12
|
|
13
|
+
HEARTBEAT_CHARACTERS = %w[💓 💗]
|
14
|
+
|
13
15
|
desc 'Watch events in an Arborist manager'
|
14
16
|
|
15
17
|
command :watch do |cmd|
|
16
18
|
cmd.action do |globals, options, args|
|
17
19
|
client = Arborist::Client.new
|
18
|
-
subid = client.subscribe( identifier: '_' )
|
19
|
-
|
20
20
|
sock = client.event_api
|
21
|
-
|
21
|
+
|
22
|
+
subid = subscribe_to_node_events( client )
|
22
23
|
prompt.say "Subscription %p" % [ subid ]
|
23
24
|
|
25
|
+
# Watch for system events as well
|
26
|
+
prompt.say "Subscribing to manager heartbeat events."
|
27
|
+
sock.subscribe( 'sys.heartbeat' )
|
28
|
+
|
24
29
|
begin
|
30
|
+
last_runid = nil
|
25
31
|
prompt.say "Watching for events on manager at %s" % [ client.event_api_url ]
|
26
32
|
loop do
|
27
33
|
msgsubid = sock.recv
|
28
34
|
raise "Partial write?!" unless sock.rcvmore?
|
29
35
|
raw_event = sock.recv
|
30
|
-
|
31
36
|
event = MessagePack.unpack( raw_event )
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
37
|
+
|
38
|
+
case msgsubid
|
39
|
+
when 'sys.heartbeat'
|
40
|
+
this_runid = event['run_id']
|
41
|
+
|
42
|
+
if last_runid && last_runid != this_runid
|
43
|
+
self.log.warn "Manager restart: re-subscribing."
|
44
|
+
sock.unsubscribe( subid )
|
45
|
+
subid = subscribe_to_node_events( client )
|
46
|
+
prompt.say "New subscription %p" % [ subid ]
|
47
|
+
else
|
48
|
+
self.log.debug "Manager is alive (runid: %s)" % [ this_runid ]
|
49
|
+
end
|
50
|
+
|
51
|
+
$stderr.print( heartbeat() )
|
52
|
+
last_runid = this_runid
|
53
|
+
when 'sys.node_added'
|
54
|
+
prompt.say "[%s] «Node added» %s\n" % [
|
55
|
+
hl(Time.now.strftime('%Y-%m-%d %H:%M:%S %Z')).color(:dark, :white),
|
56
|
+
hl(event['node']).color( :bold, :cyan )
|
57
|
+
]
|
58
|
+
when 'sys.node_removed'
|
59
|
+
prompt.say "[%s] »Node removed« %s\n" % [
|
60
|
+
hl(Time.now.strftime('%Y-%m-%d %H:%M:%S %Z')).color(:dark, :white),
|
61
|
+
hl(event['node']).color( :dark, :cyan )
|
62
|
+
]
|
63
|
+
else
|
64
|
+
prompt.say "[%s] %s\n" % [
|
65
|
+
hl(Time.now.strftime('%Y-%m-%d %H:%M:%S %Z')).color(:dark, :white),
|
66
|
+
hl(dump_event( event )).color( :dark, :white )
|
67
|
+
]
|
68
|
+
end
|
36
69
|
end
|
37
70
|
ensure
|
71
|
+
self.log.info "Unsubscribing from subscription %p" % [ subid ]
|
38
72
|
client.unsubscribe( subid )
|
39
73
|
end
|
40
74
|
end
|
@@ -45,6 +79,16 @@ module Arborist::CLI::Watch
|
|
45
79
|
module_function
|
46
80
|
###############
|
47
81
|
|
82
|
+
### Establish a subscription to all node events via the specified +client+.
|
83
|
+
def subscribe_to_node_events( client )
|
84
|
+
subid = client.subscribe( identifier: '_' )
|
85
|
+
sock = client.event_api
|
86
|
+
|
87
|
+
sock.subscribe( subid )
|
88
|
+
return subid
|
89
|
+
end
|
90
|
+
|
91
|
+
|
48
92
|
### Return a String representation of the specified +event+.
|
49
93
|
def dump_event( event )
|
50
94
|
event_type = event['type']
|
@@ -84,5 +128,12 @@ module Arborist::CLI::Watch
|
|
84
128
|
end.join( hl(", ").color(:dark, :white) )
|
85
129
|
end
|
86
130
|
|
131
|
+
|
132
|
+
### Return a heartbeat string for the current time.
|
133
|
+
def heartbeat
|
134
|
+
idx = (Time.now.to_i % HEARTBEAT_CHARACTERS.length) - 1
|
135
|
+
return " " + HEARTBEAT_CHARACTERS[ idx ] + "\x08\x08"
|
136
|
+
end
|
137
|
+
|
87
138
|
end # module Arborist::CLI::Watch
|
88
139
|
|
data/lib/arborist/loader/file.rb
CHANGED
@@ -27,7 +27,7 @@ class Arborist::Loader::File < Arborist::Loader
|
|
27
27
|
def paths
|
28
28
|
path = Pathname( self.directory )
|
29
29
|
if path.directory?
|
30
|
-
return Pathname.glob(
|
30
|
+
return Pathname.glob( path + FILE_PATTERN ).each
|
31
31
|
else
|
32
32
|
return [ path ].each
|
33
33
|
end
|
data/lib/arborist/manager.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# -*- ruby -*-
|
2
2
|
#encoding: utf-8
|
3
3
|
|
4
|
+
require 'securerandom'
|
4
5
|
require 'pathname'
|
5
6
|
require 'tempfile'
|
6
7
|
require 'configurability'
|
@@ -30,7 +31,9 @@ class Arborist::Manager
|
|
30
31
|
# Configurability API -- set config defaults
|
31
32
|
CONFIG_DEFAULTS = {
|
32
33
|
state_file: nil,
|
33
|
-
checkpoint_frequency:
|
34
|
+
checkpoint_frequency: 30000,
|
35
|
+
heartbeat_frequency: 1000,
|
36
|
+
linger: 5000
|
34
37
|
}
|
35
38
|
|
36
39
|
|
@@ -46,16 +49,34 @@ class Arborist::Manager
|
|
46
49
|
singleton_attr_accessor :state_file
|
47
50
|
|
48
51
|
##
|
49
|
-
# The number of
|
52
|
+
# The number of milliseconds between automatic state checkpoints
|
50
53
|
singleton_attr_accessor :checkpoint_frequency
|
51
54
|
|
55
|
+
##
|
56
|
+
# The number of milliseconds between heartbeat events
|
57
|
+
singleton_attr_accessor :heartbeat_frequency
|
58
|
+
|
59
|
+
##
|
60
|
+
# The maximum amount of time to wait for pending events to be delivered during
|
61
|
+
# shutdown, in milliseconds.
|
62
|
+
singleton_attr_accessor :linger
|
63
|
+
|
52
64
|
|
53
65
|
### Configurability API -- configure the manager
|
54
66
|
def self::configure( config=nil )
|
55
67
|
config ||= {}
|
56
68
|
config = self.defaults.merge( config[:manager] || {} )
|
57
69
|
|
70
|
+
self.log.debug "Config is: %p" % [ config ]
|
71
|
+
|
58
72
|
self.state_file = config[:state_file] && Pathname( config[:state_file] )
|
73
|
+
self.linger = config[:linger].to_i
|
74
|
+
self.log.info "Linger configured to %p" % [ self.linger ]
|
75
|
+
|
76
|
+
self.heartbeat_frequency = config[:heartbeat_frequency].to_i ||
|
77
|
+
CONFIG_DEFAULTS[:heartbeat_frequency]
|
78
|
+
raise Arborist::ConfigError, "heartbeat frequency must be a positive non-zero integer" if
|
79
|
+
self.heartbeat_frequency <= 0
|
59
80
|
|
60
81
|
interval = config[:checkpoint_frequency].to_i
|
61
82
|
if interval && interval.nonzero?
|
@@ -72,10 +93,10 @@ class Arborist::Manager
|
|
72
93
|
|
73
94
|
### Create a new Arborist::Manager.
|
74
95
|
def initialize
|
96
|
+
@run_id = SecureRandom.hex( 16 )
|
75
97
|
@root = Arborist::Node.create( :root )
|
76
|
-
@nodes = {
|
77
|
-
|
78
|
-
}
|
98
|
+
@nodes = { '_' => @root }
|
99
|
+
|
79
100
|
@subscriptions = {}
|
80
101
|
@tree_built = false
|
81
102
|
|
@@ -83,12 +104,27 @@ class Arborist::Manager
|
|
83
104
|
@signal_timer = nil
|
84
105
|
@start_time = nil
|
85
106
|
|
86
|
-
Thread.main[:signal_queue] = []
|
87
|
-
@zmq_loop = nil
|
88
|
-
|
89
|
-
@api_handler = nil
|
90
|
-
@event_publisher = nil
|
91
107
|
@checkpoint_timer = nil
|
108
|
+
@linger = self.class.linger || Arborist::Manager::CONFIG_DEFAULTS[ :linger ]
|
109
|
+
self.log.info "Linger set to %p" % [ @linger ]
|
110
|
+
|
111
|
+
@zmq_loop = ZMQ::Loop.new
|
112
|
+
# @zmq_loop.verbose = true
|
113
|
+
@tree_sock = self.setup_tree_socket
|
114
|
+
@event_sock = self.setup_event_socket
|
115
|
+
|
116
|
+
@api_handler = Arborist::Manager::TreeAPI.new( @tree_sock, self )
|
117
|
+
@tree_sock.handler = @api_handler
|
118
|
+
@zmq_loop.register( @tree_sock )
|
119
|
+
|
120
|
+
@event_publisher = Arborist::Manager::EventPublisher.new( @event_sock, self, @zmq_loop )
|
121
|
+
@event_sock.handler = @event_publisher
|
122
|
+
@zmq_loop.register( @event_sock )
|
123
|
+
|
124
|
+
@heartbeat_timer = self.make_heartbeat_timer
|
125
|
+
@checkpoint_timer = self.make_checkpoint_timer
|
126
|
+
|
127
|
+
Thread.main[:signal_queue] = []
|
92
128
|
end
|
93
129
|
|
94
130
|
|
@@ -96,6 +132,10 @@ class Arborist::Manager
|
|
96
132
|
public
|
97
133
|
######
|
98
134
|
|
135
|
+
##
|
136
|
+
# A unique string used to identify different runs of the Manager
|
137
|
+
attr_reader :run_id
|
138
|
+
|
99
139
|
##
|
100
140
|
# The root node of the tree.
|
101
141
|
attr_accessor :root
|
@@ -128,6 +168,23 @@ class Arborist::Manager
|
|
128
168
|
# Flag for marking when the tree is built successfully the first time
|
129
169
|
attr_predicate_accessor :tree_built
|
130
170
|
|
171
|
+
##
|
172
|
+
# The maximum amount of time to wait for pending events to be delivered during
|
173
|
+
# shutdown, in milliseconds.
|
174
|
+
attr_reader :linger
|
175
|
+
|
176
|
+
##
|
177
|
+
# The ZMQ::Timer that processes signals
|
178
|
+
attr_reader :signal_timer
|
179
|
+
|
180
|
+
##
|
181
|
+
# The ZMQ::Timer that periodically checkpoints the manager's state (if it's configured to do so)
|
182
|
+
attr_reader :checkpoint_timer
|
183
|
+
|
184
|
+
##
|
185
|
+
# The ZMQ::Timer that periodically publishes a heartbeat event
|
186
|
+
attr_reader :heartbeat_timer
|
187
|
+
|
131
188
|
|
132
189
|
#
|
133
190
|
# :section: Startup/Shutdown
|
@@ -136,20 +193,21 @@ class Arborist::Manager
|
|
136
193
|
### Setup sockets and start the event loop.
|
137
194
|
def run
|
138
195
|
self.log.info "Getting ready to start the manager."
|
139
|
-
self.
|
196
|
+
self.publish_system_event( 'startup', start_time: Time.now.to_s, version: Arborist::VERSION )
|
197
|
+
self.register_timers
|
140
198
|
self.set_signal_handlers
|
141
199
|
self.start_accepting_requests
|
142
200
|
|
143
201
|
return self # For chaining
|
144
202
|
ensure
|
145
203
|
self.restore_signal_handlers
|
146
|
-
if
|
204
|
+
if self.zmq_loop
|
147
205
|
self.log.debug "Unregistering sockets."
|
148
|
-
|
206
|
+
self.zmq_loop.remove( @tree_sock )
|
149
207
|
@tree_sock.pollable.close
|
150
|
-
|
208
|
+
self.zmq_loop.remove( @event_sock )
|
151
209
|
@event_sock.pollable.close
|
152
|
-
|
210
|
+
self.zmq_loop.cancel_timer( @checkpoint_timer ) if @checkpoint_timer
|
153
211
|
end
|
154
212
|
|
155
213
|
self.save_node_states
|
@@ -161,7 +219,14 @@ class Arborist::Manager
|
|
161
219
|
|
162
220
|
### Returns true if the Manager is running.
|
163
221
|
def running?
|
164
|
-
return
|
222
|
+
return self.zmq_loop && self.zmq_loop.running?
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
### Register the Manager's timers.
|
227
|
+
def register_timers
|
228
|
+
self.zmq_loop.register_timer( self.heartbeat_timer )
|
229
|
+
self.zmq_loop.register_timer( self.checkpoint_timer ) if self.checkpoint_timer
|
165
230
|
end
|
166
231
|
|
167
232
|
|
@@ -169,32 +234,11 @@ class Arborist::Manager
|
|
169
234
|
def start_accepting_requests
|
170
235
|
self.log.debug "Starting the main loop"
|
171
236
|
|
172
|
-
@zmq_loop = ZMQ::Loop.new
|
173
|
-
|
174
|
-
@api_handler = Arborist::Manager::TreeAPI.new( @tree_sock, self )
|
175
|
-
@tree_sock.handler = @api_handler
|
176
|
-
@zmq_loop.register( @tree_sock )
|
177
|
-
|
178
|
-
@event_publisher = Arborist::Manager::EventPublisher.new( @event_sock, self, @zmq_loop )
|
179
|
-
@event_sock.handler = @event_publisher
|
180
|
-
@zmq_loop.register( @event_sock )
|
181
|
-
|
182
|
-
@checkpoint_timer = self.start_state_checkpointing
|
183
|
-
@zmq_loop.register_timer( @checkpoint_timer ) if @checkpoint_timer
|
184
|
-
|
185
237
|
self.setup_signal_timer
|
186
238
|
self.start_time = Time.now
|
187
239
|
|
188
240
|
self.log.debug "Manager running."
|
189
|
-
|
190
|
-
end
|
191
|
-
|
192
|
-
|
193
|
-
### Create the ZMQ API socket if necessary.
|
194
|
-
def setup_sockets
|
195
|
-
self.log.debug "Setting up sockets"
|
196
|
-
@tree_sock = self.setup_tree_socket
|
197
|
-
@event_sock = self.setup_event_socket
|
241
|
+
return self.zmq_loop.start
|
198
242
|
end
|
199
243
|
|
200
244
|
|
@@ -214,7 +258,7 @@ class Arborist::Manager
|
|
214
258
|
sock = Arborist.zmq_context.socket( :PUB )
|
215
259
|
self.log.debug " binding the event socket (%#0x) to %p" %
|
216
260
|
[ sock.object_id * 2, Arborist.event_api_url ]
|
217
|
-
sock.linger =
|
261
|
+
sock.linger = self.linger
|
218
262
|
sock.bind( Arborist.event_api_url )
|
219
263
|
return ZMQ::Pollitem.new( sock, ZMQ::POLLOUT )
|
220
264
|
end
|
@@ -231,7 +275,11 @@ class Arborist::Manager
|
|
231
275
|
self.log.info "Stopping the manager."
|
232
276
|
self.ignore_signals
|
233
277
|
self.cancel_signal_timer
|
234
|
-
|
278
|
+
|
279
|
+
@api_handler.shutdown
|
280
|
+
@event_publisher.shutdown
|
281
|
+
|
282
|
+
self.zmq_loop.stop
|
235
283
|
end
|
236
284
|
|
237
285
|
|
@@ -287,14 +335,26 @@ class Arborist::Manager
|
|
287
335
|
end
|
288
336
|
|
289
337
|
|
290
|
-
###
|
338
|
+
### Make a ZMQ::Timer that will publish a heartbeat event at a configurable interval.
|
339
|
+
def make_heartbeat_timer
|
340
|
+
interval = self.class.heartbeat_frequency || CONFIG_DEFAULTS[ :heartbeat_frequency ]
|
341
|
+
|
342
|
+
self.log.info "Setting up to heartbeat every %dms" % [ interval ]
|
343
|
+
heartbeat_timer = ZMQ::Timer.new( (interval/1000.0), 0 ) do
|
344
|
+
self.publish_heartbeat_event
|
345
|
+
end
|
346
|
+
return heartbeat_timer
|
347
|
+
end
|
348
|
+
|
349
|
+
|
350
|
+
### Make a ZMQ::Timer that will save a snapshot of the node tree's state to the state
|
291
351
|
### file on a configured interval if it's configured.
|
292
|
-
def
|
352
|
+
def make_checkpoint_timer
|
293
353
|
return nil unless self.class.state_file
|
294
354
|
interval = self.class.checkpoint_frequency or return nil
|
295
355
|
|
296
|
-
self.log.info "Setting up node state checkpoint every %
|
297
|
-
checkpoint_timer = ZMQ::Timer.new( interval, 0 ) do
|
356
|
+
self.log.info "Setting up node state checkpoint every %dms" % [ interval ]
|
357
|
+
checkpoint_timer = ZMQ::Timer.new( (interval/1000.0), 0 ) do
|
298
358
|
self.save_node_states
|
299
359
|
end
|
300
360
|
return checkpoint_timer
|
@@ -311,15 +371,15 @@ class Arborist::Manager
|
|
311
371
|
### Set up a periodic ZMQ timer to check for queued signals and handle them.
|
312
372
|
def setup_signal_timer
|
313
373
|
@signal_timer = ZMQ::Timer.new( SIGNAL_INTERVAL, 0, self.method(:process_signal_queue) )
|
314
|
-
|
374
|
+
self.zmq_loop.register_timer( @signal_timer )
|
315
375
|
end
|
316
376
|
|
317
377
|
|
318
378
|
### Disable the timer that checks for incoming signals
|
319
379
|
def cancel_signal_timer
|
320
|
-
if
|
321
|
-
|
322
|
-
|
380
|
+
if self.signal_timer
|
381
|
+
self.signal_timer.cancel
|
382
|
+
self.zmq_loop.cancel_timer( self.signal_timer )
|
323
383
|
end
|
324
384
|
end
|
325
385
|
|
@@ -403,6 +463,13 @@ class Arborist::Manager
|
|
403
463
|
end
|
404
464
|
|
405
465
|
|
466
|
+
### Simulate the receipt of the specified +signal+ (probably only useful
|
467
|
+
### in testing).
|
468
|
+
def simulate_signal( signal )
|
469
|
+
Thread.main[:signal_queue] << signal.to_sym
|
470
|
+
end
|
471
|
+
|
472
|
+
|
406
473
|
#
|
407
474
|
# :section: Tree API
|
408
475
|
#
|
@@ -461,7 +528,7 @@ class Arborist::Manager
|
|
461
528
|
|
462
529
|
if self.tree_built?
|
463
530
|
self.link_node( node )
|
464
|
-
|
531
|
+
self.publish_system_event( 'node_added', node: identifier )
|
465
532
|
end
|
466
533
|
end
|
467
534
|
|
@@ -484,7 +551,7 @@ class Arborist::Manager
|
|
484
551
|
raise "Can't remove an operational node" if node.operational?
|
485
552
|
|
486
553
|
self.log.info "Removing node %p" % [ node ]
|
487
|
-
|
554
|
+
self.publish_system_event( 'node_removed', node: node.identifier )
|
488
555
|
node.children.each do |identifier, child_node|
|
489
556
|
self.remove_node( child_node )
|
490
557
|
end
|
@@ -555,6 +622,13 @@ class Arborist::Manager
|
|
555
622
|
# Tree-traversal API
|
556
623
|
#
|
557
624
|
|
625
|
+
|
626
|
+
### Return the current root node.
|
627
|
+
def root_node
|
628
|
+
return self.nodes[ '_' ]
|
629
|
+
end
|
630
|
+
|
631
|
+
|
558
632
|
### Yield each node in a depth-first traversal of the manager's tree
|
559
633
|
### to the specified +block+, or return an Enumerator if no block is given.
|
560
634
|
def all_nodes( &block )
|
@@ -639,6 +713,21 @@ class Arborist::Manager
|
|
639
713
|
end
|
640
714
|
|
641
715
|
|
716
|
+
### Publish a system event that observers can watch for to detect restarts.
|
717
|
+
def publish_heartbeat_event
|
718
|
+
self.publish_system_event( 'heartbeat', run_id: self.run_id )
|
719
|
+
end
|
720
|
+
|
721
|
+
|
722
|
+
### Publish an event with the specified +eventname+ and +data+.
|
723
|
+
def publish_system_event( eventname, **data )
|
724
|
+
eventname = eventname.to_s
|
725
|
+
eventname = 'sys.' + eventname unless eventname.start_with?( 'sys.' )
|
726
|
+
self.log.debug "Publishing %s event: %p." % [ eventname, data ]
|
727
|
+
self.event_publisher.publish( eventname, data )
|
728
|
+
end
|
729
|
+
|
730
|
+
|
642
731
|
### Create a subscription that publishes to the Manager's event publisher for
|
643
732
|
### the node with the specified +identifier+ and +event_pattern+, using the
|
644
733
|
### given +criteria+ when considering an event.
|