arborist 0.0.1.pre20160606141735 → 0.0.1.pre20160829140603
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/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.
|