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/client.rb
CHANGED
@@ -18,13 +18,17 @@ class Arborist::Client
|
|
18
18
|
log_to :arborist
|
19
19
|
|
20
20
|
|
21
|
+
### Create and return a singleton instance with configured
|
22
|
+
### endpoints.
|
23
|
+
def self::instance
|
24
|
+
return @instance ||= new
|
25
|
+
end
|
26
|
+
|
27
|
+
|
21
28
|
### Create a new Client with the given API socket URIs.
|
22
29
|
def initialize( tree_api_url: nil, event_api_url: nil )
|
23
30
|
@tree_api_url = tree_api_url || Arborist.tree_api_url
|
24
31
|
@event_api_url = event_api_url || Arborist.event_api_url
|
25
|
-
|
26
|
-
@request_queue = nil
|
27
|
-
@event_subscriptions = nil
|
28
32
|
end
|
29
33
|
|
30
34
|
|
@@ -40,40 +44,35 @@ class Arborist::Client
|
|
40
44
|
|
41
45
|
|
42
46
|
#
|
43
|
-
#
|
47
|
+
# Convenience methods
|
44
48
|
#
|
45
49
|
|
46
|
-
### Mark a node as 'acknowledged' if it's down, or 'disabled' if
|
47
|
-
### it's up. (A pre-emptive acknowledgement.) Requires the node
|
48
|
-
### +identifier+, an acknowledgement +message+, and +sender+. You
|
49
|
-
### can optionally include a +via+ (source), and override the default
|
50
|
-
### +time+ of now.
|
51
|
-
def acknowledge( node, message, sender, via=nil, time=Time.now )
|
52
|
-
data = {
|
53
|
-
node => {
|
54
|
-
ack: {
|
55
|
-
message: message,
|
56
|
-
sender: sender,
|
57
|
-
via: via,
|
58
|
-
time: time.to_s
|
59
|
-
}
|
60
|
-
}
|
61
|
-
}
|
62
50
|
|
63
|
-
|
51
|
+
### Return dependencies of the given +identifier+ as an array.
|
52
|
+
def dependencies_of( identifier, partition: nil, properties: :all )
|
53
|
+
dependencies = self.deps( identifier: identifier )[ 'deps' ]
|
54
|
+
dependencies = self.search(
|
55
|
+
criteria: { identifier: dependencies },
|
56
|
+
options: { properties: properties }
|
57
|
+
)
|
58
|
+
|
59
|
+
if partition
|
60
|
+
partition = partition.to_s
|
61
|
+
dependencies.keys.each{|id| dependencies[id]['identifier'] = id }
|
62
|
+
dependencies = dependencies.values.group_by do |node|
|
63
|
+
node[ partition ]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
return dependencies
|
64
68
|
end
|
65
|
-
alias_method :ack, :acknowledge
|
66
69
|
|
67
70
|
|
68
|
-
###
|
69
|
-
def
|
70
|
-
|
71
|
-
|
72
|
-
self.send_tree_api_request( request )
|
73
|
-
return true
|
71
|
+
### Retreive a single node.
|
72
|
+
def fetch_node( identifier )
|
73
|
+
request = self.make_fetch_request( from: identifier, depth: 0 )
|
74
|
+
return self.send_tree_api_request( request ).first
|
74
75
|
end
|
75
|
-
alias_method :clear_ack, :clear_acknowledgement
|
76
|
-
|
77
76
|
|
78
77
|
|
79
78
|
#
|
@@ -87,44 +86,63 @@ class Arborist::Client
|
|
87
86
|
end
|
88
87
|
|
89
88
|
|
90
|
-
### Return
|
89
|
+
### Return a `status` request as a ZMQ message (a CZTop::Message).
|
91
90
|
def make_status_request
|
92
91
|
return Arborist::TreeAPI.request( :status )
|
93
92
|
end
|
94
93
|
|
95
94
|
|
96
|
-
###
|
97
|
-
def
|
98
|
-
request = self.
|
95
|
+
### Fetch the manager's current node tree.
|
96
|
+
def fetch( **args )
|
97
|
+
request = self.make_fetch_request( **args )
|
99
98
|
return self.send_tree_api_request( request )
|
100
99
|
end
|
101
100
|
|
102
101
|
|
103
|
-
### Return
|
104
|
-
|
102
|
+
### Return a `fetch` request as a ZMQ message (a CZTop::Message) with the given
|
103
|
+
### attributes.
|
104
|
+
def make_fetch_request( from: nil, depth: nil, tree: false )
|
105
105
|
header = {}
|
106
106
|
self.log.debug "From is: %p" % [ from ]
|
107
107
|
header[:from] = from if from
|
108
108
|
header[:depth] = depth if depth
|
109
|
+
header[:tree] = 'true' if tree
|
109
110
|
|
110
|
-
return Arborist::TreeAPI.request( :
|
111
|
+
return Arborist::TreeAPI.request( :fetch, header, nil )
|
111
112
|
end
|
112
113
|
|
113
114
|
|
114
115
|
### Return the manager's current node tree.
|
115
|
-
def
|
116
|
-
|
116
|
+
def search( criteria:{}, options:{}, **args )
|
117
|
+
criteria = args if criteria.empty?
|
118
|
+
request = self.make_search_request( criteria, **options )
|
117
119
|
return self.send_tree_api_request( request )
|
118
120
|
end
|
119
121
|
|
120
122
|
|
121
|
-
### Return
|
122
|
-
|
123
|
+
### Return a `search` request as a ZMQ message (a CZTop::Message) with the given
|
124
|
+
### attributes.
|
125
|
+
def make_search_request( criteria, exclude_down: false, properties: :all, exclude: {} )
|
123
126
|
header = {}
|
124
|
-
header[ :
|
127
|
+
header[ :exclude_down ] = true if exclude_down
|
125
128
|
header[ :return ] = properties if properties != :all
|
126
129
|
|
127
|
-
return Arborist::TreeAPI.request( :
|
130
|
+
return Arborist::TreeAPI.request( :search, header, [ criteria, exclude ] )
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
### Return the identifiers that have a dependency on the node with the
|
135
|
+
### specified +identifier+.
|
136
|
+
def deps( identifier: )
|
137
|
+
request = self.make_deps_request( identifier )
|
138
|
+
return self.send_tree_api_request( request )
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
### Return a `deps` request as a ZMQ message (a CZTop::Message) with the given
|
143
|
+
### +identifier+.
|
144
|
+
def make_deps_request( identifier )
|
145
|
+
return Arborist::TreeAPI.request( :deps, { from: identifier }, nil )
|
128
146
|
end
|
129
147
|
|
130
148
|
|
@@ -136,9 +154,10 @@ class Arborist::Client
|
|
136
154
|
end
|
137
155
|
|
138
156
|
|
139
|
-
###
|
140
|
-
|
141
|
-
|
157
|
+
### Return an `update` request as a zmq message (a CZTop::Message) with the given
|
158
|
+
### +data+.
|
159
|
+
def make_update_request( data, header={} )
|
160
|
+
return Arborist::TreeAPI.request( :update, header, data )
|
142
161
|
end
|
143
162
|
|
144
163
|
|
@@ -150,7 +169,8 @@ class Arborist::Client
|
|
150
169
|
end
|
151
170
|
|
152
171
|
|
153
|
-
###
|
172
|
+
### Return a `subscribe` request as a zmq message (a CZTop::Message) with the
|
173
|
+
### specified attributes.
|
154
174
|
def make_subscribe_request( criteria: {}, identifier: nil, event_type: nil, exclude: {} )
|
155
175
|
self.log.debug "Making subscription request for identifier: %p, event_type: %p, criteria: %p" %
|
156
176
|
[ identifier, event_type, criteria ]
|
@@ -170,11 +190,12 @@ class Arborist::Client
|
|
170
190
|
end
|
171
191
|
|
172
192
|
|
173
|
-
###
|
193
|
+
### Return an `unsubscribe` request as a zmq message (a CZTop::Message) with the
|
194
|
+
### specified +subid+.
|
174
195
|
def make_unsubscribe_request( subid )
|
175
196
|
self.log.debug "Making unsubscribe request for subid: %s" % [ subid ]
|
176
197
|
|
177
|
-
return Arborist::TreeAPI.request( :unsubscribe, subscription_id: subid )
|
198
|
+
return Arborist::TreeAPI.request( :unsubscribe, {subscription_id: subid}, nil )
|
178
199
|
end
|
179
200
|
|
180
201
|
|
@@ -186,11 +207,12 @@ class Arborist::Client
|
|
186
207
|
end
|
187
208
|
|
188
209
|
|
189
|
-
###
|
190
|
-
|
210
|
+
### Return a `prune` request as a zmq message (a CZTop::Message) with the
|
211
|
+
### specified +identifier+.
|
212
|
+
def make_prune_request( identifier: )
|
191
213
|
self.log.debug "Making prune request for identifier: %s" % [ identifier ]
|
192
214
|
|
193
|
-
return Arborist::TreeAPI.request( :prune, identifier: identifier )
|
215
|
+
return Arborist::TreeAPI.request( :prune, {identifier: identifier}, nil )
|
194
216
|
end
|
195
217
|
|
196
218
|
|
@@ -202,13 +224,11 @@ class Arborist::Client
|
|
202
224
|
end
|
203
225
|
|
204
226
|
|
205
|
-
###
|
206
|
-
|
227
|
+
### Return a `graft` request as a zmq message (a CZTop::Message) with the
|
228
|
+
### specified attributes.
|
229
|
+
def make_graft_request( identifier:, type:, parent: nil, attributes:{} )
|
207
230
|
self.log.debug "Making graft request for identifer: %s" % [ identifier ]
|
208
231
|
|
209
|
-
parent = attributes.delete( :parent )
|
210
|
-
type = attributes.delete( :type )
|
211
|
-
|
212
232
|
header = {
|
213
233
|
identifier: identifier,
|
214
234
|
parent: parent,
|
@@ -227,14 +247,60 @@ class Arborist::Client
|
|
227
247
|
end
|
228
248
|
|
229
249
|
|
230
|
-
###
|
231
|
-
|
250
|
+
### Return a `modify` request as a zmq message (a CZTop::Message) with the
|
251
|
+
### specified attributes.
|
252
|
+
def make_modify_request( identifier:, attributes: )
|
232
253
|
self.log.debug "Making modify request for identifer: %s" % [ identifier ]
|
233
254
|
|
234
255
|
return Arborist::TreeAPI.request( :modify, {identifier: identifier}, attributes )
|
235
256
|
end
|
236
257
|
|
237
258
|
|
259
|
+
### Mark a node as 'acknowledged' if it's down, or 'disabled' if
|
260
|
+
### it's up. (A pre-emptive acknowledgement.) Requires the node
|
261
|
+
### +identifier+, an acknowledgement +message+, and +sender+. You
|
262
|
+
### can optionally include a +via+ (source), and override the default
|
263
|
+
### +time+ of now.
|
264
|
+
def acknowledge( *args )
|
265
|
+
request = self.make_acknowledge_request( *args )
|
266
|
+
response = self.send_tree_api_request( request )
|
267
|
+
return true
|
268
|
+
end
|
269
|
+
alias_method :ack, :acknowledge
|
270
|
+
|
271
|
+
|
272
|
+
### Return an `ack` request as a zmq message (a CZTop::Message) with the specified
|
273
|
+
### attributes.
|
274
|
+
def make_acknowledge_request( identifier:, message:, sender:, via: nil, time: Time.now )
|
275
|
+
ack = {
|
276
|
+
message: message,
|
277
|
+
sender: sender,
|
278
|
+
via: via,
|
279
|
+
time: time.to_s
|
280
|
+
}
|
281
|
+
|
282
|
+
return Arborist::TreeAPI.request( :ack, {identifier: identifier}, ack )
|
283
|
+
end
|
284
|
+
|
285
|
+
|
286
|
+
### Clear the acknowledgement for a node.
|
287
|
+
def clear_acknowledgement( *args )
|
288
|
+
request = self.make_unack_request( *args )
|
289
|
+
response = self.send_tree_api_request( request )
|
290
|
+
return true
|
291
|
+
end
|
292
|
+
alias_method :unack, :clear_acknowledgement
|
293
|
+
alias_method :clear_ack, :clear_acknowledgement
|
294
|
+
alias_method :unacknowledge, :clear_acknowledgement
|
295
|
+
|
296
|
+
|
297
|
+
### Return an `unack` request as a zmq message (a CZTop::Message) with the specified
|
298
|
+
### attribute.
|
299
|
+
def make_unack_request( identifier: )
|
300
|
+
return Arborist::TreeAPI.request( :unack, {identifier: identifier}, nil )
|
301
|
+
end
|
302
|
+
|
303
|
+
|
238
304
|
### Send the packed +request+ via the Tree API socket, raise an error on
|
239
305
|
### unsuccessful response, and return the response body.
|
240
306
|
def send_tree_api_request( request )
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'etc'
|
5
|
+
|
6
|
+
require 'arborist/cli' unless defined?( Arborist::CLI )
|
7
|
+
require 'arborist/client'
|
8
|
+
|
9
|
+
# Command to ack a node
|
10
|
+
module Arborist::CLI::Ack
|
11
|
+
extend Arborist::CLI::Subcommand
|
12
|
+
|
13
|
+
desc 'Ack/disable one or more nodes in the Arborist tree'
|
14
|
+
|
15
|
+
arg :IDENTIFIER, optional: true, multiple: true
|
16
|
+
|
17
|
+
command :ack do |cmd|
|
18
|
+
|
19
|
+
cmd.switch :clear, default: false,
|
20
|
+
desc: "Clear the ack instead of setting it.",
|
21
|
+
negatable: false
|
22
|
+
|
23
|
+
cmd.flag [ :u, :user ],
|
24
|
+
desc: "The user to mark the nodes with."
|
25
|
+
cmd.flag [ :m, :message ],
|
26
|
+
desc: "The acknowledgement message."
|
27
|
+
|
28
|
+
cmd.action do |globals, options, args|
|
29
|
+
identifiers = get_identifiers( args )
|
30
|
+
help_now!( "No node identifiers supplied." ) if identifiers.empty?
|
31
|
+
|
32
|
+
client = Arborist::Client.new
|
33
|
+
res = {}
|
34
|
+
|
35
|
+
if options[ :clear ]
|
36
|
+
identifiers.each do |id|
|
37
|
+
res[ id ] = client.clear_ack( identifier: id )
|
38
|
+
end
|
39
|
+
|
40
|
+
else
|
41
|
+
message = options[ :message ] || prompt.ask( "Ack/disable message:" )
|
42
|
+
help_now!( "A acknowlegement/disable message is required." ) unless message
|
43
|
+
|
44
|
+
userid = options[ :user ] || prompt.ask( "Your name?", default: Etc.getpwuid.name )
|
45
|
+
help_now!( "Unable to determine ack user." ) unless userid
|
46
|
+
|
47
|
+
identifiers.each do |id|
|
48
|
+
res[ id ] = unless_dryrun( "Acking #{id}...", true ) do
|
49
|
+
client.ack(
|
50
|
+
identifier: id,
|
51
|
+
message: message,
|
52
|
+
sender: userid,
|
53
|
+
via: "command line"
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
res.each_pair do |identifier, result|
|
60
|
+
prompt.say "%s: %s" % [
|
61
|
+
hl.bold.bright_blue( identifier ),
|
62
|
+
result == true ? "Okay." : hl.red( res.to_s )
|
63
|
+
]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
###############
|
70
|
+
module_function
|
71
|
+
###############
|
72
|
+
|
73
|
+
### Parse a list of identifiers from the command line or from
|
74
|
+
### a multiline prompt.
|
75
|
+
def get_identifiers( args )
|
76
|
+
identifiers = args
|
77
|
+
if args.empty?
|
78
|
+
identifiers = prompt.multiline( "Enter node identifiers, separated with newlines or commas:" )
|
79
|
+
identifiers = identifiers.map( &:chomp ).map{|id| id.split(/,\s*/) }.flatten
|
80
|
+
end
|
81
|
+
|
82
|
+
return identifiers.uniq
|
83
|
+
end
|
84
|
+
|
85
|
+
end # module Arborist::CLI::Ack
|
86
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'arborist/cli' unless defined?( Arborist::CLI )
|
5
|
+
require 'arborist/node'
|
6
|
+
require 'arborist/manager'
|
7
|
+
|
8
|
+
|
9
|
+
# Command to reset node states while retaining acknowledgement state.
|
10
|
+
module Arborist::CLI::Reset
|
11
|
+
extend Arborist::CLI::Subcommand
|
12
|
+
|
13
|
+
desc 'Reset all state except for acknowledged/disabled'
|
14
|
+
long_desc <<-EOF
|
15
|
+
If you've rearranged the dependencies in the tree, existing states may no
|
16
|
+
longer be valid. This command forcefully resets all states to "unknown" so
|
17
|
+
they can be re-checked, but retains existing acknowledgements and intentionally
|
18
|
+
disabled nodes. (If you don't care about those you should instead simply delete
|
19
|
+
the state file.)
|
20
|
+
|
21
|
+
It's pointless to use this command while the Manager is running.
|
22
|
+
EOF
|
23
|
+
|
24
|
+
arg :SOURCE
|
25
|
+
|
26
|
+
command :reset do |cmd|
|
27
|
+
|
28
|
+
cmd.flag :loader, desc: "Specify a loader type to use.",
|
29
|
+
default_value: 'file'
|
30
|
+
|
31
|
+
cmd.action do |globals, options, args|
|
32
|
+
source = args.shift
|
33
|
+
loader = Arborist::Loader.create( options[:loader], source )
|
34
|
+
manager = Arborist.manager_for( loader )
|
35
|
+
|
36
|
+
manager.nodes.each_pair do |identifier, node|
|
37
|
+
next if node.ack || node.operational?
|
38
|
+
node.status = 'unknown'
|
39
|
+
end
|
40
|
+
|
41
|
+
unless_dryrun( "Resetting node states." ) do
|
42
|
+
manager.save_node_states
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end # module Arborist::CLI::Reset
|
48
|
+
|
@@ -8,6 +8,12 @@ require 'arborist/cli' unless defined?( Arborist::CLI )
|
|
8
8
|
module Arborist::CLI::Start
|
9
9
|
extend Arborist::CLI::Subcommand
|
10
10
|
|
11
|
+
VALID_DAEMONS = %w[
|
12
|
+
manager
|
13
|
+
monitors
|
14
|
+
observers
|
15
|
+
]
|
16
|
+
|
11
17
|
desc 'Start an Arborist daemon'
|
12
18
|
long_desc <<-EOF
|
13
19
|
Start the Arborist manager, observers, or monitors. The SOURCE is
|
@@ -30,6 +36,10 @@ module Arborist::CLI::Start
|
|
30
36
|
appname = args.shift
|
31
37
|
source = args.shift
|
32
38
|
|
39
|
+
unless VALID_DAEMONS.include?( appname )
|
40
|
+
raise "Unknown daemon component. Should be one of: %s" % [ VALID_DAEMONS.join(', ') ]
|
41
|
+
end
|
42
|
+
|
33
43
|
loader = Arborist::Loader.create( options[:loader], source )
|
34
44
|
runner = case appname
|
35
45
|
when 'manager'
|
@@ -42,7 +52,7 @@ module Arborist::CLI::Start
|
|
42
52
|
raise "Don't know how to start %p" % [ appname ]
|
43
53
|
end
|
44
54
|
|
45
|
-
unless_dryrun( "starting #{appname}" ) do
|
55
|
+
unless_dryrun( "starting #{appname} using #{runner.inspect}" ) do
|
46
56
|
start( runner, options[:p] )
|
47
57
|
end
|
48
58
|
end
|