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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ChangeLog +670 -1
  5. data/History.md +67 -0
  6. data/Manifest.txt +9 -6
  7. data/README.md +1 -3
  8. data/Rakefile +39 -4
  9. data/TODO.md +22 -31
  10. data/lib/arborist.rb +9 -2
  11. data/lib/arborist/cli.rb +67 -85
  12. data/lib/arborist/client.rb +125 -59
  13. data/lib/arborist/command/ack.rb +86 -0
  14. data/lib/arborist/command/reset.rb +48 -0
  15. data/lib/arborist/command/start.rb +11 -1
  16. data/lib/arborist/command/summary.rb +173 -0
  17. data/lib/arborist/command/tree.rb +215 -0
  18. data/lib/arborist/command/watch.rb +22 -22
  19. data/lib/arborist/dependency.rb +24 -4
  20. data/lib/arborist/event.rb +18 -2
  21. data/lib/arborist/event/node.rb +6 -2
  22. data/lib/arborist/event/node_warn.rb +16 -0
  23. data/lib/arborist/manager.rb +179 -48
  24. data/lib/arborist/mixins.rb +11 -0
  25. data/lib/arborist/monitor.rb +29 -17
  26. data/lib/arborist/monitor/connection_batching.rb +293 -0
  27. data/lib/arborist/monitor/socket.rb +101 -167
  28. data/lib/arborist/monitor_runner.rb +101 -24
  29. data/lib/arborist/node.rb +297 -68
  30. data/lib/arborist/node/ack.rb +1 -1
  31. data/lib/arborist/node/host.rb +26 -5
  32. data/lib/arborist/node/resource.rb +14 -5
  33. data/lib/arborist/node/root.rb +12 -3
  34. data/lib/arborist/node/service.rb +29 -26
  35. data/lib/arborist/node_subscription.rb +65 -0
  36. data/lib/arborist/observer.rb +8 -0
  37. data/lib/arborist/observer/action.rb +6 -0
  38. data/lib/arborist/subscription.rb +22 -16
  39. data/lib/arborist/tree_api.rb +7 -2
  40. data/spec/arborist/client_spec.rb +157 -51
  41. data/spec/arborist/dependency_spec.rb +21 -0
  42. data/spec/arborist/event/node_spec.rb +5 -0
  43. data/spec/arborist/event_spec.rb +3 -3
  44. data/spec/arborist/manager_spec.rb +626 -347
  45. data/spec/arborist/mixins_spec.rb +19 -0
  46. data/spec/arborist/monitor/socket_spec.rb +1 -2
  47. data/spec/arborist/monitor_runner_spec.rb +81 -29
  48. data/spec/arborist/monitor_spec.rb +89 -14
  49. data/spec/arborist/node/host_spec.rb +68 -0
  50. data/spec/arborist/node/resource_spec.rb +2 -0
  51. data/spec/arborist/node/root_spec.rb +13 -0
  52. data/spec/arborist/node/service_spec.rb +9 -0
  53. data/spec/arborist/node_spec.rb +673 -111
  54. data/spec/arborist/node_subscription_spec.rb +54 -0
  55. data/spec/arborist/observer/action_spec.rb +6 -0
  56. data/spec/arborist/observer_runner_spec.rb +8 -1
  57. data/spec/arborist/tree_api_spec.rb +111 -8
  58. data/spec/data/monitors/pings.rb +0 -11
  59. data/spec/data/monitors/port_checks.rb +0 -9
  60. data/spec/data/nodes/sidonie.rb +1 -0
  61. data/spec/data/nodes/vhosts.rb +23 -0
  62. data/spec/data/nodes/yevaud.rb +4 -2
  63. data/spec/spec_helper.rb +71 -1
  64. metadata +91 -28
  65. metadata.gz.sig +0 -0
  66. data/Events.md +0 -35
  67. data/Monitors.md +0 -155
  68. data/Nodes.md +0 -70
  69. data/Observers.md +0 -72
  70. data/Protocol.md +0 -276
  71. data/Tutorial.md +0 -8
Binary file
data/Events.md DELETED
@@ -1,35 +0,0 @@
1
- # Events
2
-
3
- ## Event Types
4
-
5
- «type».«subtype»
6
-
7
- node.acked
8
- node.delta
9
- node.disabled
10
- node.down
11
- node.quieted
12
- node.unknown
13
- node.up
14
- node.update
15
- sys.node_added
16
- sys.node_removed
17
- sys.reloaded
18
- sys.hearbeat
19
-
20
-
21
- ## Event Movement
22
-
23
- Propagation
24
-
25
- events being sent up the tree to the root node
26
-
27
- Broadcast
28
-
29
- events being sent down to node children
30
-
31
- Publishing
32
-
33
- events being sent to subscriptions, including dependent nodes
34
- triggered via propagation and broadcasting
35
-
@@ -1,155 +0,0 @@
1
- # Monitors
2
-
3
- Monitors are loaded in a fashion similar to the way nodes describing the network topology are
4
- loaded: you provide a Enumerator that yields Arborist::Monitor objects to the #load_monitors method
5
- of an Arborist::MonitorRunner object. The `Arborist::Monitor.each_in` method, given a path to a directory containing `.rb` files that declare one or more monitors, will return such an Enumerator, but you could also use this to load monitor descriptions from any source you prefer, e.g., LDAP, a RDBMS, etc.
6
-
7
-
8
- ## Declaration DSL
9
-
10
- To facilitate describing monitors to run, Arborist::Monitor also provides a DSL-like syntax for constructing them.
11
-
12
- For example, this would declare two monitors, one which pings every 'host' node except those tagged as laptops in the network every 20 seconds, and the other which pings 'host' nodes tagged as laptops every 5 minutes.
13
-
14
- # monitors/pings.rb
15
- require 'arborist/monitor'
16
-
17
- Arborist::Monitor 'ping check' do
18
- key :pingcheck
19
- every 20.seconds
20
- match type: 'host'
21
- exclude tag: :laptop
22
- use :address
23
- exec 'fping'
24
- end
25
-
26
- Arborist::Monitor 'transient host pings' do
27
- key :pingcheck
28
- every 5.minutes
29
- match type: 'host', tag: 'laptop'
30
- use :address
31
- exec 'fping'
32
- end
33
-
34
- Each monitor is given a human-readable description for use in user interfaces, and one or more attributes that describe which nodes should be monitored, how they should be monitored, and how often the monitor should be run.
35
-
36
- ### Monitor Attributes
37
-
38
- #### key
39
-
40
- Declare a namespace for the monitor. The error status for a node is keyed by this value, so that monitors with different keys don't clear each other's errors.
41
-
42
- This attribute is mandatory.
43
-
44
- #### description
45
-
46
- Set a human-readable description for the monitor, for use in interfaces or logs.
47
-
48
- This attribute is mandatory.
49
-
50
- #### every( seconds )
51
-
52
- Declare the interval between runs of the monitor. The monitor will be skewed by a small amount from this value (unless you specify `splay 0`) to prevent many monitors from starting up simultaneously.
53
-
54
- #### splay( seconds )
55
-
56
- Manually set the amount of splay (random offset from the interval) the monitor should use. It defaults to `Math.logn( interval )`.
57
-
58
- #### exec( command )
59
- #### exec {|node_attributes| ... }
60
- #### exec( module )
61
-
62
- Specify what should be run to do the actual monitoring. The first form simply `spawn`s the specified command with its STDIN opened to a stream of serialized node data.
63
-
64
- By default, the format of the serialized nodes is one node per line, and each line looks like this:
65
-
66
- «identifier» «attribute1»=«attribute1 value» «attribute2»=«attribute2 value»
67
-
68
- Each line should use shell-escaping semantics, so that if an attribute value contains whitespace, it should be quoted, control characters need to be escaped, etc.
69
-
70
- For example, the ping checker might receive input like:
71
-
72
- duir address=192.168.16.3
73
- sidonie address="192.168.16.3"
74
- yevaud address="192.168.16.10"
75
-
76
- If the command you are running doesn't support this format, you can override this in one of two ways.
77
-
78
- If your command expects the node data as command-line arguments, you can provide a custom `exec_arguments` block. It will receive an Array of Arborist::Node objects and it should generate an Array of arguments to append to the command before `spawn`ing it.
79
-
80
- exec_arguments do |nodes|
81
- # Build an address -> node mapping for pairing the updates back up by address
82
- @node_map = nodes.each_with_object( {} ) do |node, hash|
83
- address = node.address
84
- hash[ address ] = node
85
- end
86
-
87
- @node_map.keys
88
- end
89
-
90
- If your command expects the node data via `STDIN`, but in a different format, you may declare an `exec_input` block. It will be called with the same node array, and additionally an IO open to the STDIN of the running command. This can be combined with the `exec_arguments` block, if you're dealing with something really weird.
91
-
92
- exec_input do |nodes, writer|
93
- # Build an address -> node mapping for pairing the updates back up by address
94
- @node_map = nodes.each_with_object( {} ) do |node, hash|
95
- address = node.address
96
- hash[ address ] = node
97
- end
98
-
99
- writer.puts( node_map.values )
100
- end
101
-
102
- The monitor must write results for any of the listed identifiers that require update in the same format to its STDOUT. For the ping check above, the results might look like:
103
-
104
- duir rtt=20ms
105
- sidonie rtt=103ms
106
- yevaud rtt= error=Host\ unreachable.
107
-
108
- If the program writes its output in some other format, you can provide a `handle_results` block. It will be called with the program's `STDOUT` if the block takes one argument, and if it takes an additional argument its `STDERR` as well. It should return a Hash of update Hashes, keyed by the node identifier it should be sent to.
109
-
110
- handle_results do |pid, out, err|
111
- updates = {}
112
-
113
- out.each_line do |line|
114
- address, status = line.split( /\s+:\s+/, 2 )
115
-
116
- # Use the @node_map we created up in the exec_arguments to map the output
117
- # back into identifiers. Error-checking omitted for brevity.
118
- identifier = @node_map[ address ].identifier
119
-
120
- # 127.0.0.1 is alive (0.12 ms)
121
- # 8.8.8.8 is alive (61.6 ms)
122
- # 192.168.16.16 is unreachable
123
- if status =~ /is alive \((\d+\.\d+ ms)\)/i
124
- updates[ identifier ] = { ping: { rtt: Float($1) } }
125
- else
126
- updates[ identifier ] = { error: status }
127
- end
128
- end
129
-
130
- updates
131
- end
132
-
133
- Unlisted attributes are unchanged. A listed attribute with an empty value is explicitly cleared. An identifier that isn't listed in the results means no update is necessary for that node.
134
-
135
- If you find yourself wanting to repeat one or more of the exec callbacks, you can also wrap them in a module and call `exec_callbacks` with it.
136
-
137
- The second and third forms can be used to implement a monitor in Ruby. In the second, the block is called with the Hash of node data, keyed by identifier, and it must return a Hash of updates keyed by identifier. The third form expects any object that responds to `#run`, which will be invoked the same way as the block.
138
-
139
-
140
- #### use( *properties )
141
-
142
- Specify the list of properties to provide to the monitor for each node. If this is unspecified, the input to the monitor will be just the list of identifiers.
143
-
144
-
145
-
146
- # Does everything in Ruby; gets the Array of Nodes as arguments to the block, expected to
147
- # return a Hash of updates keyed by node identifier
148
- exec do |nodes|
149
-
150
- end
151
-
152
-
153
- # Runs an external
154
- exec 'fping', '-e', '-t', '150'
155
-
data/Nodes.md DELETED
@@ -1,70 +0,0 @@
1
- # Nodes
2
-
3
-
4
-
5
- Arborist::Host 'sidonie' do
6
- parent 'duir'
7
- description "NAS and media server"
8
- address '192.168.16.3'
9
-
10
- tags :infrastructure,
11
- :storage,
12
- :media,
13
- :rip_status_check
14
-
15
- service 'ssh'
16
- service 'demon-http', port: 6666, protocol: 'http'
17
- service 'postgresql'
18
-
19
- service 'smtp'
20
-
21
- service 'http',
22
- depends_on: 'postgresql'
23
- service 'sabnzbd', port: 8080, protocol: 'http'
24
- service 'sickbeard', port: 8081, protocol: 'http'
25
- service 'pms', port: 32400, protocol: 'http'
26
- service 'couchpotato', port: 5050, protocol: 'http'
27
- end
28
-
29
-
30
- Arborist::Host 'jhereg' do
31
- parent 'duir'
32
- description "Directory server"
33
- address '192.168.16.7'
34
-
35
- service 'ldaps'
36
- end
37
-
38
-
39
- Arborist::Host 'webserver' do
40
- description "Public webserver"
41
- address '54.16.62.181'
42
-
43
- service 'http',
44
- depends_on: 'foo'
45
- depends_on: all_of( 'postgresql', 'daemon-http', on: 'sidonie' ),
46
- all_of( 'ldaps', on: 'jhereg' )
47
- end
48
-
49
-
50
- An application server depends on one each of the 'http' services and 'ldaps' services
51
- to be up.
52
-
53
- Arborist::Host 'appserver1' do
54
- description "Public application webserver"
55
- address '54.16.62.185'
56
- service 'http',
57
- depends_on: all_of(
58
- any_of( 'http', on: %w[service1 service2 service3] ),
59
- any_of( 'ldaps', on: %w[directory1 directory2] ),
60
- all_of( 'else', on: 'something' )
61
- )
62
- end
63
-
64
-
65
- [ :all_of,
66
- [ :any_of, 'service1-http', 'service2-http', 'service3-http' ],
67
- [ :any_of, 'directory1-ldaps', 'directory2-ldaps' ],
68
- 'something-else'
69
- ]
70
-
@@ -1,72 +0,0 @@
1
- # Observers
2
-
3
- subscription
4
- * Event to subscribe to
5
- * Node to attach subscription to. No node means 'root', which sees all subnode events.
6
- * One or more action blocks
7
-
8
- Actions have:
9
- * a block to execute
10
- * Zero or more time-periods, which are unioned together. No time periods means anytime.
11
-
12
- Pragmas:
13
- * Summarize:
14
- (send a single alert summarizing every event received over x period of time, or n events)
15
- * Squelch:
16
-
17
-
18
- :MAHLON:
19
- The manager should probably serialize subscriptions for its nodes. Otherwise the manager
20
- can restart and any running observers will never again receive events because the
21
- subscriptions will have disappeared.
22
-
23
-
24
-
25
- ## Examples
26
-
27
- # -*- ruby -*-
28
- #encoding: utf-8
29
-
30
- require 'arborist'
31
-
32
- WORK_HOURS = 'hour {8am-6pm}'
33
- OFF_HOURS = 'hour {6pm-8am}'
34
-
35
- Arborist::Observer "Webservers" do
36
- subscribe to: 'node.delta',
37
- where: {
38
- type: 'service',
39
- port: 80,
40
- delta: { status: ['up', 'down'] }
41
- }
42
-
43
- action( during: WORK_HOURS ) do |uuid, event|
44
- $stderr.puts "Webserver %s is DOWN (%p)" % [ event['data']['identifier'], event['data'] ]
45
- end
46
- summarize( every: 5.minutes, count: 5, during: OFF_HOURS ) do |*tuples|
47
- email to: 'ops@example.com', subject: ""
48
- end
49
-
50
- end
51
-
52
-
53
-
54
- ## Schedulability stuff
55
-
56
- schedule = Schedule.new
57
- schedule |= Period.time( '8AM' .. '8PM' )
58
- schedule |= Period.day( 'Mon' .. 'Fri' )
59
-
60
-
61
- # Thymelörde! - An Amazeballs Gem for Doing Stuff With Time™
62
-
63
-
64
- schedule = Thymelörde!.yes?( 'Tue-Thur {6am-9pm}' )
65
- schedule.ehh? #=> false
66
- schedule.yes? #=> false
67
-
68
-
69
- Legba -- the gatekeeper?
70
-
71
-
72
-
@@ -1,276 +0,0 @@
1
- # Monitors
2
-
3
- ## Basic Protocol
4
-
5
- ZMQ REQ socket, msgpack message consisting of an Array of two elements:
6
-
7
- [
8
- header,
9
- body
10
- ]
11
-
12
- Header is a Map of the form:
13
-
14
- {
15
- action: «verb», # required
16
- version: 1, # required
17
- [verb-specific attributes]
18
- }
19
-
20
- Body is either Nil, a Map of key-value pairs, or an Array of Maps appropriate to the `action`.
21
-
22
-
23
- ## Commands
24
-
25
-
26
- ### «commandname»
27
-
28
- «description»
29
-
30
- #### Header
31
-
32
- #### Body
33
-
34
- #### Return
35
-
36
- #### Examples
37
-
38
-
39
-
40
-
41
- ### status
42
-
43
- Fetch the status of the Manager.
44
-
45
- {
46
- action: status,
47
- version: 1,
48
- }
49
-
50
- Response:
51
-
52
- {
53
- success: true,
54
- version: 1
55
- },
56
- {
57
- server_version: 0.0.1,
58
- state: 'running',
59
- uptime: 17155,
60
- nodecount: 342
61
- }
62
-
63
-
64
- ### list
65
-
66
- Retrieve an Array of Maps that describes all or part of the node tree.
67
-
68
- #### Required
69
- from the node with the specified `identifier`, or the root node if no `identifier` is specified.
70
-
71
-
72
- Request:
73
-
74
- [
75
- {
76
- action: list,
77
- version: 1
78
- [from: «identifier»]
79
- [depth: «arg»]
80
- }
81
- ]
82
-
83
- Successful response:
84
-
85
- [
86
- {
87
- success: true,
88
- version: 1
89
- },
90
- [
91
- {
92
- identifier: 'foo',
93
- status: 'up',
94
- parent: '_',
95
- properties: {},
96
- },
97
- {
98
- identifier: 'bar',
99
- status: 'down',
100
- parent: 'foo',
101
- properties: {},
102
- }
103
- ]
104
- ]
105
-
106
- failure example:
107
-
108
- [
109
- {
110
- success: false,
111
- reason: "human readable exception message or whatever",
112
- category: either 'server' or 'client', meaning who is responsible for the error
113
- version: 1
114
- }
115
- ]
116
-
117
-
118
-
119
- ### fetch
120
-
121
- Fetch the `address`, `description`, and `status` of all nodes.
122
-
123
- [
124
- {
125
- action: fetch,
126
- version: 1,
127
- include_down: true,
128
- return: [address, description, status]
129
- },
130
-
131
- Response
132
-
133
- {
134
- 'theon' => {
135
- address: '10.2.10.4',
136
- description: 'no theon, reek',
137
- status: down,
138
- },
139
- 'thoros' => {
140
- address: '10.2.10.4',
141
- description: "The Red God's champion",
142
- status: up,
143
- }
144
- ]
145
- ]
146
-
147
-
148
- #### return
149
-
150
- - not specified : returns everything.
151
- - `Nil` : returns just identifiers
152
- - array of fields : returns the values of those fields
153
-
154
- Search for nodes that match the filter given in the request body, returning a serialized map of node identifiers to requested state.
155
-
156
-
157
- ### update
158
-
159
- [
160
- {
161
- action: update,
162
- version: 1
163
- },
164
- {
165
- duir: {
166
- pingtime: 0.02
167
- },
168
- sidonie: {
169
- pingtime: 0.28
170
- }
171
- }
172
- ]
173
-
174
- With a failure:
175
-
176
- [
177
- {
178
- action: update,
179
- version: 1
180
- },
181
- {
182
- duir: {
183
- pingtime: null,
184
- error: "Host unreachable."
185
- },
186
- sidonie: {
187
- pingtime: 0.28
188
- }
189
- }
190
- ]
191
-
192
-
193
- ### subscribe
194
-
195
- Get node change delta events for every 'host' type node.
196
-
197
- {
198
- action: subscribe,
199
- version: 1,
200
- event_type: node.delta
201
- },
202
- {
203
- type: 'host',
204
- }
205
-
206
- Get a snapshot of node state on every update for 'service' type nodes under
207
- the 'bennett' node.
208
-
209
- {
210
- action: subscribe,
211
- version: 1,
212
- event_type: node.update,
213
- identifier: 'bennett'
214
- },
215
- {
216
- type: 'service',
217
- }
218
-
219
- Get events of state changes to services running on port 80.
220
-
221
- {
222
- action: subscribe,
223
- version: 1,
224
- event_type: node.delta
225
- },
226
- {
227
- type: 'service',
228
- port: 80
229
- }
230
-
231
- Get notified of every system event (startup, shutdown, reload, etc.)
232
-
233
- {
234
- action: subscribe,
235
- version: 1,
236
- event_type: sys.*
237
- },
238
- Nil
239
-
240
-
241
- ### graft
242
-
243
- {
244
- action: graft,
245
- version: 1,
246
- type: 'host',
247
- identifier: 'joliet',
248
- parent: 'bennett' # defaults to root
249
- },
250
- {
251
- addresses: [],
252
- tags: []
253
- }
254
-
255
-
256
- ### prune
257
-
258
- {
259
- action: prune,
260
- version: 1,
261
- identifier: 'bennett'
262
- },
263
- Nil
264
-
265
-
266
- ### modify
267
-
268
- {
269
- action: modify,
270
- version: 1,
271
- identifier: 'bennett'
272
- },
273
- {
274
- addresses: ['10.13.0.22', '10.1.0.23']
275
- }
276
-