sensu 0.9.7.beta.2 → 0.9.7.beta.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +8 -0
- data/lib/sensu/api.rb +179 -149
- data/lib/sensu/base.rb +0 -1
- data/lib/sensu/client.rb +37 -45
- data/lib/sensu/constants.rb +1 -1
- data/lib/sensu/logger.rb +1 -0
- data/lib/sensu/redis.rb +43 -17
- data/lib/sensu/server.rb +136 -145
- data/sensu.gemspec +2 -2
- metadata +11 -14
- data/lib/sensu/patches/amqp.rb +0 -5
data/CHANGELOG.md
CHANGED
@@ -15,8 +15,16 @@ AMQP handlers can no longer use `"send_only_check_output": true`, but
|
|
15
15
|
instead have access to the built-in mutators `"mutator": "only_check_output"` and
|
16
16
|
`"mutator": "only_check_output_split"`.
|
17
17
|
|
18
|
+
Ruby 1.8.7-p249 is no longer supported, as the AMQP library no longer
|
19
|
+
does. Please use the Sensu APT/YUM packages which contain an embedded
|
20
|
+
Ruby.
|
21
|
+
|
18
22
|
### Other
|
19
23
|
|
24
|
+
Improved RabbitMQ and Redis connection recovery.
|
25
|
+
|
26
|
+
Fixed API POST input validation.
|
27
|
+
|
20
28
|
Redis client connection heartbeat.
|
21
29
|
|
22
30
|
Improved graceful process termination.
|
data/lib/sensu/api.rb
CHANGED
@@ -8,57 +8,116 @@ module Sensu
|
|
8
8
|
class API < Sinatra::Base
|
9
9
|
register Sinatra::Async
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
class << self
|
12
|
+
def run(options={})
|
13
|
+
EM::run do
|
14
|
+
bootstrap(options)
|
15
|
+
start
|
16
|
+
trap_signals
|
17
|
+
end
|
18
|
+
end
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
-
|
20
|
+
def bootstrap(options={})
|
21
|
+
$logger = Cabin::Channel.get
|
22
|
+
base = Sensu::Base.new(options)
|
23
|
+
$settings = base.settings
|
24
|
+
if $settings[:api][:user] && $settings[:api][:password]
|
25
|
+
use Rack::Auth::Basic do |user, password|
|
26
|
+
user == $settings[:api][:user] && password == $settings[:api][:password]
|
21
27
|
end
|
22
28
|
end
|
23
29
|
end
|
24
|
-
end
|
25
30
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
:settings => $settings[:redis]
|
32
|
-
})
|
33
|
-
$redis = Sensu::Redis.connect($settings[:redis])
|
34
|
-
$redis.on_disconnect = Proc.new do
|
35
|
-
if $redis.connection_established?
|
36
|
-
$logger.warn('reconnecting to redis')
|
37
|
-
$redis.reconnect!
|
38
|
-
else
|
31
|
+
def setup_redis
|
32
|
+
$logger.debug('connecting to redis', {
|
33
|
+
:settings => $settings[:redis]
|
34
|
+
})
|
35
|
+
connection_failure = Proc.new do
|
39
36
|
$logger.fatal('cannot connect to redis', {
|
40
37
|
:settings => $settings[:redis]
|
41
38
|
})
|
42
39
|
$logger.fatal('SENSU NOT RUNNING!')
|
40
|
+
if $rabbitmq
|
41
|
+
$rabbitmq.close
|
42
|
+
end
|
43
43
|
exit 2
|
44
44
|
end
|
45
|
+
$redis = Sensu::Redis.connect($settings[:redis], :on_tcp_connection_failure => connection_failure)
|
46
|
+
$redis.on_tcp_connection_loss do |connection, settings|
|
47
|
+
$logger.warn('reconnecting to redis')
|
48
|
+
connection.reconnect(false, 10)
|
49
|
+
end
|
45
50
|
end
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
$rabbitmq = AMQP.connect($settings[:rabbitmq])
|
50
|
-
$rabbitmq.on_disconnect = Proc.new do
|
51
|
-
$logger.fatal('cannot connect to rabbitmq', {
|
51
|
+
|
52
|
+
def setup_rabbitmq
|
53
|
+
$logger.debug('connecting to rabbitmq', {
|
52
54
|
:settings => $settings[:rabbitmq]
|
53
55
|
})
|
54
|
-
|
56
|
+
connection_failure = Proc.new do
|
57
|
+
$logger.fatal('cannot connect to rabbitmq', {
|
58
|
+
:settings => $settings[:rabbitmq]
|
59
|
+
})
|
60
|
+
$logger.fatal('SENSU NOT RUNNING!')
|
61
|
+
$redis.close
|
62
|
+
exit 2
|
63
|
+
end
|
64
|
+
$rabbitmq = AMQP.connect($settings[:rabbitmq], :on_tcp_connection_failure => connection_failure)
|
65
|
+
$rabbitmq.on_tcp_connection_loss do |connection, settings|
|
66
|
+
$logger.warn('reconnecting to rabbitmq')
|
67
|
+
connection.reconnect(false, 10)
|
68
|
+
end
|
69
|
+
$amq = AMQP::Channel.new($rabbitmq)
|
70
|
+
$amq.auto_recovery = true
|
71
|
+
end
|
72
|
+
|
73
|
+
def start
|
74
|
+
setup_redis
|
75
|
+
setup_rabbitmq
|
76
|
+
Thin::Logging.silent = true
|
77
|
+
Thin::Server.start(self, $settings[:api][:port])
|
78
|
+
end
|
79
|
+
|
80
|
+
def stop
|
81
|
+
$logger.warn('stopping')
|
82
|
+
$rabbitmq.close
|
55
83
|
$redis.close
|
56
|
-
|
84
|
+
$logger.warn('stopping reactor')
|
85
|
+
EM::stop_event_loop
|
86
|
+
end
|
87
|
+
|
88
|
+
def trap_signals
|
89
|
+
%w[INT TERM].each do |signal|
|
90
|
+
Signal.trap(signal) do
|
91
|
+
$logger.warn('received signal', {
|
92
|
+
:signal => signal
|
93
|
+
})
|
94
|
+
stop
|
95
|
+
end
|
96
|
+
end
|
57
97
|
end
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
98
|
+
|
99
|
+
def run_test(options={}, &block)
|
100
|
+
bootstrap(options)
|
101
|
+
start
|
102
|
+
$settings[:client][:timestamp] = Time.now.to_i
|
103
|
+
$redis.set('client:' + $settings[:client][:name], $settings[:client].to_json).callback do
|
104
|
+
$redis.sadd('clients', $settings[:client][:name]).callback do
|
105
|
+
$redis.hset('events:' + $settings[:client][:name], 'test', {
|
106
|
+
:output => 'CRITICAL',
|
107
|
+
:status => 2,
|
108
|
+
:issued => Time.now.to_i,
|
109
|
+
:flapping => false,
|
110
|
+
:occurrences => 1
|
111
|
+
}.to_json).callback do
|
112
|
+
$redis.set('stash:test/test', {:key => 'value'}.to_json).callback do
|
113
|
+
$redis.sadd('stashes', 'test/test').callback do
|
114
|
+
EM::Timer.new(0.5) do
|
115
|
+
block.call
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
62
121
|
end
|
63
122
|
end
|
64
123
|
end
|
@@ -96,6 +155,31 @@ module Sensu
|
|
96
155
|
end
|
97
156
|
end
|
98
157
|
|
158
|
+
def bad_request!
|
159
|
+
status 400
|
160
|
+
body ''
|
161
|
+
end
|
162
|
+
|
163
|
+
def not_found!
|
164
|
+
status 404
|
165
|
+
body ''
|
166
|
+
end
|
167
|
+
|
168
|
+
def created!
|
169
|
+
status 201
|
170
|
+
body ''
|
171
|
+
end
|
172
|
+
|
173
|
+
def accepted!
|
174
|
+
status 202
|
175
|
+
body ''
|
176
|
+
end
|
177
|
+
|
178
|
+
def no_content!
|
179
|
+
status 204
|
180
|
+
body ''
|
181
|
+
end
|
182
|
+
|
99
183
|
def event_hash(event_json, client_name, check_name)
|
100
184
|
JSON.parse(event_json, :symbolize_names => true).merge(
|
101
185
|
:client => client_name,
|
@@ -163,8 +247,7 @@ module Sensu
|
|
163
247
|
unless client_json.nil?
|
164
248
|
body client_json
|
165
249
|
else
|
166
|
-
|
167
|
-
body ''
|
250
|
+
not_found!
|
168
251
|
end
|
169
252
|
end
|
170
253
|
end
|
@@ -191,12 +274,10 @@ module Sensu
|
|
191
274
|
$redis.del('history:' + client_name)
|
192
275
|
end
|
193
276
|
end
|
194
|
-
|
195
|
-
body ''
|
277
|
+
accepted!
|
196
278
|
end
|
197
279
|
else
|
198
|
-
|
199
|
-
body ''
|
280
|
+
not_found!
|
200
281
|
end
|
201
282
|
end
|
202
283
|
end
|
@@ -210,8 +291,7 @@ module Sensu
|
|
210
291
|
response = $settings[:checks][check_name].merge(:name => check_name)
|
211
292
|
body response.to_json
|
212
293
|
else
|
213
|
-
|
214
|
-
body ''
|
294
|
+
not_found!
|
215
295
|
end
|
216
296
|
end
|
217
297
|
|
@@ -220,27 +300,25 @@ module Sensu
|
|
220
300
|
post_body = JSON.parse(request.body.read, :symbolize_names => true)
|
221
301
|
check_name = post_body[:check]
|
222
302
|
subscribers = post_body[:subscribers]
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
303
|
+
if check_name.is_a?(String) && subscribers.is_a?(Array)
|
304
|
+
payload = {
|
305
|
+
:name => check_name,
|
306
|
+
:issued => Time.now.to_i
|
307
|
+
}
|
308
|
+
$logger.info('publishing check request', {
|
309
|
+
:payload => payload,
|
310
|
+
:subscribers => subscribers
|
311
|
+
})
|
312
|
+
subscribers.uniq.each do |exchange_name|
|
313
|
+
$amq.fanout(exchange_name).publish(payload.to_json)
|
314
|
+
end
|
315
|
+
created!
|
316
|
+
else
|
317
|
+
bad_request!
|
238
318
|
end
|
239
|
-
|
240
|
-
|
241
|
-
status 400
|
319
|
+
rescue JSON::ParserError, TypeError
|
320
|
+
bad_request!
|
242
321
|
end
|
243
|
-
body ''
|
244
322
|
end
|
245
323
|
|
246
324
|
aget '/events' do
|
@@ -279,8 +357,7 @@ module Sensu
|
|
279
357
|
unless event_json.nil?
|
280
358
|
body event_hash(event_json, client_name, check_name).to_json
|
281
359
|
else
|
282
|
-
|
283
|
-
body ''
|
360
|
+
not_found!
|
284
361
|
end
|
285
362
|
end
|
286
363
|
end
|
@@ -289,11 +366,10 @@ module Sensu
|
|
289
366
|
$redis.hgetall('events:' + client_name).callback do |events|
|
290
367
|
if events.include?(check_name)
|
291
368
|
resolve_event(client_name, check_name)
|
292
|
-
|
369
|
+
accepted!
|
293
370
|
else
|
294
|
-
|
371
|
+
not_found!
|
295
372
|
end
|
296
|
-
body ''
|
297
373
|
end
|
298
374
|
end
|
299
375
|
|
@@ -302,48 +378,42 @@ module Sensu
|
|
302
378
|
post_body = JSON.parse(request.body.read, :symbolize_names => true)
|
303
379
|
client_name = post_body[:client]
|
304
380
|
check_name = post_body[:check]
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
status 202
|
314
|
-
else
|
315
|
-
status 404
|
381
|
+
if client_name.is_a?(String) && check_name.is_a?(String)
|
382
|
+
$redis.hgetall('events:' + client_name).callback do |events|
|
383
|
+
if events.include?(check_name)
|
384
|
+
resolve_event(client_name, check_name)
|
385
|
+
accepted!
|
386
|
+
else
|
387
|
+
not_found!
|
388
|
+
end
|
316
389
|
end
|
317
|
-
|
390
|
+
else
|
391
|
+
bad_request!
|
318
392
|
end
|
319
|
-
|
320
|
-
|
321
|
-
body ''
|
393
|
+
rescue JSON::ParserError, TypeError
|
394
|
+
bad_request!
|
322
395
|
end
|
323
396
|
end
|
324
397
|
|
325
398
|
apost %r{/stash(?:es)?/(.*)} do |path|
|
326
399
|
begin
|
327
400
|
post_body = JSON.parse(request.body.read)
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
$redis.set('stash:' + path, post_body.to_json).callback do
|
333
|
-
$redis.sadd('stashes', path).callback do
|
334
|
-
status 201
|
335
|
-
body ''
|
401
|
+
$redis.set('stash:' + path, post_body.to_json).callback do
|
402
|
+
$redis.sadd('stashes', path).callback do
|
403
|
+
created!
|
404
|
+
end
|
336
405
|
end
|
406
|
+
rescue JSON::ParserError
|
407
|
+
bad_request!
|
337
408
|
end
|
338
409
|
end
|
339
410
|
|
340
411
|
aget %r{/stash(?:es)?/(.*)} do |path|
|
341
412
|
$redis.get('stash:' + path).callback do |stash_json|
|
342
|
-
|
343
|
-
status 404
|
344
|
-
body ''
|
345
|
-
else
|
413
|
+
unless stash_json.nil?
|
346
414
|
body stash_json
|
415
|
+
else
|
416
|
+
not_found!
|
347
417
|
end
|
348
418
|
end
|
349
419
|
end
|
@@ -353,13 +423,11 @@ module Sensu
|
|
353
423
|
if stash_exists
|
354
424
|
$redis.srem('stashes', path).callback do
|
355
425
|
$redis.del('stash:' + path).callback do
|
356
|
-
|
357
|
-
body ''
|
426
|
+
no_content!
|
358
427
|
end
|
359
428
|
end
|
360
429
|
else
|
361
|
-
|
362
|
-
body ''
|
430
|
+
not_found!
|
363
431
|
end
|
364
432
|
end
|
365
433
|
end
|
@@ -373,62 +441,24 @@ module Sensu
|
|
373
441
|
apost '/stashes' do
|
374
442
|
begin
|
375
443
|
post_body = JSON.parse(request.body.read)
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
response[path] = JSON.parse(stash_json)
|
386
|
-
end
|
387
|
-
if index == post_body.size - 1
|
388
|
-
body response.to_json
|
389
|
-
end
|
390
|
-
end
|
391
|
-
end
|
392
|
-
else
|
393
|
-
status 400
|
394
|
-
body ''
|
395
|
-
end
|
396
|
-
end
|
397
|
-
|
398
|
-
def self.run_test(options={}, &block)
|
399
|
-
self.setup(options)
|
400
|
-
$settings[:client][:timestamp] = Time.now.to_i
|
401
|
-
$redis.set('client:' + $settings[:client][:name], $settings[:client].to_json).callback do
|
402
|
-
$redis.sadd('clients', $settings[:client][:name]).callback do
|
403
|
-
$redis.hset('events:' + $settings[:client][:name], 'test', {
|
404
|
-
:output => 'CRITICAL',
|
405
|
-
:status => 2,
|
406
|
-
:issued => Time.now.to_i,
|
407
|
-
:flapping => false,
|
408
|
-
:occurrences => 1
|
409
|
-
}.to_json).callback do
|
410
|
-
$redis.set('stash:test/test', {:key => 'value'}.to_json).callback do
|
411
|
-
$redis.sadd('stashes', 'test/test').callback do
|
412
|
-
Thin::Logging.silent = true
|
413
|
-
Thin::Server.start(self, $settings[:api][:port])
|
414
|
-
EM::Timer.new(0.5) do
|
415
|
-
block.call
|
416
|
-
end
|
444
|
+
if post_body.is_a?(Array) && post_body.size > 0
|
445
|
+
response = Hash.new
|
446
|
+
post_body.each_with_index do |path, index|
|
447
|
+
$redis.get('stash:' + path).callback do |stash_json|
|
448
|
+
unless stash_json.nil?
|
449
|
+
response[path] = JSON.parse(stash_json)
|
450
|
+
end
|
451
|
+
if index == post_body.size - 1
|
452
|
+
body response.to_json
|
417
453
|
end
|
418
454
|
end
|
419
455
|
end
|
456
|
+
else
|
457
|
+
bad_request!
|
420
458
|
end
|
459
|
+
rescue JSON::ParserError
|
460
|
+
bad_request!
|
421
461
|
end
|
422
462
|
end
|
423
|
-
|
424
|
-
def self.stop(signal)
|
425
|
-
$logger.warn('received signal', {
|
426
|
-
:signal => signal
|
427
|
-
})
|
428
|
-
$logger.warn('stopping')
|
429
|
-
$redis.close
|
430
|
-
$logger.warn('stopping reactor')
|
431
|
-
EM::stop_event_loop
|
432
|
-
end
|
433
463
|
end
|
434
464
|
end
|
data/lib/sensu/base.rb
CHANGED
@@ -10,7 +10,6 @@ require 'cabin'
|
|
10
10
|
require 'amqp'
|
11
11
|
|
12
12
|
require File.join(File.dirname(__FILE__), 'patches', 'ruby')
|
13
|
-
require File.join(File.dirname(__FILE__), 'patches', 'amqp')
|
14
13
|
|
15
14
|
require File.join(File.dirname(__FILE__), 'constants')
|
16
15
|
require File.join(File.dirname(__FILE__), 'cli')
|
data/lib/sensu/client.rb
CHANGED
@@ -6,18 +6,8 @@ module Sensu
|
|
6
6
|
def self.run(options={})
|
7
7
|
client = self.new(options)
|
8
8
|
EM::run do
|
9
|
-
client.
|
10
|
-
client.
|
11
|
-
client.setup_subscriptions
|
12
|
-
client.setup_rabbitmq_monitor
|
13
|
-
client.setup_standalone
|
14
|
-
client.setup_sockets
|
15
|
-
|
16
|
-
%w[INT TERM].each do |signal|
|
17
|
-
Signal.trap(signal) do
|
18
|
-
client.stop(signal)
|
19
|
-
end
|
20
|
-
end
|
9
|
+
client.start
|
10
|
+
client.trap_signals
|
21
11
|
end
|
22
12
|
end
|
23
13
|
|
@@ -33,15 +23,20 @@ module Sensu
|
|
33
23
|
@logger.debug('connecting to rabbitmq', {
|
34
24
|
:settings => @settings[:rabbitmq]
|
35
25
|
})
|
36
|
-
|
37
|
-
@rabbitmq.on_disconnect = Proc.new do
|
26
|
+
connection_failure = Proc.new do
|
38
27
|
@logger.fatal('cannot connect to rabbitmq', {
|
39
28
|
:settings => @settings[:rabbitmq]
|
40
29
|
})
|
41
30
|
@logger.fatal('SENSU NOT RUNNING!')
|
42
31
|
exit 2
|
43
32
|
end
|
33
|
+
@rabbitmq = AMQP.connect(@settings[:rabbitmq], :on_tcp_connection_failure => connection_failure)
|
34
|
+
@rabbitmq.on_tcp_connection_loss do |connection, settings|
|
35
|
+
@logger.warn('reconnecting to rabbitmq')
|
36
|
+
connection.reconnect(false, 10)
|
37
|
+
end
|
44
38
|
@amq = AMQP::Channel.new(@rabbitmq)
|
39
|
+
@amq.auto_recovery = true
|
45
40
|
end
|
46
41
|
|
47
42
|
def publish_keepalive
|
@@ -186,20 +181,6 @@ module Sensu
|
|
186
181
|
end
|
187
182
|
end
|
188
183
|
|
189
|
-
def setup_rabbitmq_monitor
|
190
|
-
@logger.debug('monitoring rabbitmq connection')
|
191
|
-
@timers << EM::PeriodicTimer.new(5) do
|
192
|
-
if @rabbitmq.connected?
|
193
|
-
unless @check_request_queue.subscribed?
|
194
|
-
@logger.warn('re-subscribing to client subscriptions')
|
195
|
-
setup_subscriptions
|
196
|
-
end
|
197
|
-
else
|
198
|
-
@logger.warn('reconnecting to rabbitmq')
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
184
|
def setup_standalone
|
204
185
|
@logger.debug('scheduling standalone checks')
|
205
186
|
standalone_check_count = 0
|
@@ -238,45 +219,56 @@ module Sensu
|
|
238
219
|
end
|
239
220
|
|
240
221
|
def unsubscribe(&block)
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
end
|
245
|
-
if block
|
246
|
-
block.call
|
247
|
-
end
|
222
|
+
@logger.warn('unsubscribing from client subscriptions')
|
223
|
+
@check_request_queue.unsubscribe
|
224
|
+
block.call
|
248
225
|
end
|
249
226
|
|
250
227
|
def complete_checks_in_progress(&block)
|
251
228
|
@logger.info('completing checks in progress', {
|
252
229
|
:checks_in_progress => @checks_in_progress
|
253
230
|
})
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
true
|
259
|
-
end
|
231
|
+
retry_until_true do
|
232
|
+
if @checks_in_progress.empty?
|
233
|
+
block.call
|
234
|
+
true
|
260
235
|
end
|
261
236
|
end
|
262
237
|
end
|
263
238
|
|
264
|
-
def
|
265
|
-
|
266
|
-
|
267
|
-
|
239
|
+
def start
|
240
|
+
setup_rabbitmq
|
241
|
+
setup_keepalives
|
242
|
+
setup_subscriptions
|
243
|
+
setup_standalone
|
244
|
+
setup_sockets
|
245
|
+
end
|
246
|
+
|
247
|
+
def stop
|
268
248
|
@logger.warn('stopping')
|
269
249
|
@timers.each do |timer|
|
270
250
|
timer.cancel
|
271
251
|
end
|
272
252
|
unsubscribe do
|
273
253
|
complete_checks_in_progress do
|
254
|
+
@rabbitmq.close
|
274
255
|
@logger.warn('stopping reactor')
|
275
256
|
EM::stop_event_loop
|
276
257
|
end
|
277
258
|
end
|
278
259
|
end
|
279
260
|
|
261
|
+
def trap_signals
|
262
|
+
%w[INT TERM].each do |signal|
|
263
|
+
Signal.trap(signal) do
|
264
|
+
@logger.warn('received signal', {
|
265
|
+
:signal => signal
|
266
|
+
})
|
267
|
+
stop
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
280
272
|
private
|
281
273
|
|
282
274
|
def testing?
|
data/lib/sensu/constants.rb
CHANGED
data/lib/sensu/logger.rb
CHANGED
data/lib/sensu/redis.rb
CHANGED
@@ -2,13 +2,17 @@ require 'redis'
|
|
2
2
|
|
3
3
|
module Sensu
|
4
4
|
class Redis < Redis::Client
|
5
|
-
attr_accessor :
|
5
|
+
attr_accessor :settings, :on_tcp_connection_failure
|
6
|
+
|
7
|
+
alias :em_reconnect :reconnect
|
6
8
|
|
7
9
|
def initialize(*arguments)
|
8
10
|
super
|
9
11
|
@logger = Cabin::Channel.get
|
12
|
+
@settings = Hash.new
|
10
13
|
@connection_established = false
|
11
14
|
@connected = false
|
15
|
+
@reconnecting = false
|
12
16
|
@closing_connection = false
|
13
17
|
end
|
14
18
|
|
@@ -23,8 +27,9 @@ module Sensu
|
|
23
27
|
def connection_completed
|
24
28
|
@connection_established = true
|
25
29
|
@connected = true
|
26
|
-
|
27
|
-
|
30
|
+
@reconnecting = false
|
31
|
+
if @settings[:password]
|
32
|
+
auth(@settings[:password]).callback do |reply|
|
28
33
|
unless reply == 'OK'
|
29
34
|
@logger.fatal('redis authentication failed')
|
30
35
|
close_connection
|
@@ -41,9 +46,14 @@ module Sensu
|
|
41
46
|
setup_heartbeat
|
42
47
|
end
|
43
48
|
|
44
|
-
def reconnect
|
45
|
-
|
46
|
-
|
49
|
+
def reconnect(immediate=false, wait=10)
|
50
|
+
if @reconnecting && !immediate
|
51
|
+
EM::Timer.new(wait) do
|
52
|
+
em_reconnect(@settings[:host], @settings[:port])
|
53
|
+
end
|
54
|
+
else
|
55
|
+
@reconnecting = true
|
56
|
+
em_reconnect(@settings[:host], @settings[:port])
|
47
57
|
end
|
48
58
|
end
|
49
59
|
|
@@ -52,23 +62,33 @@ module Sensu
|
|
52
62
|
close_connection
|
53
63
|
end
|
54
64
|
|
65
|
+
def on_tcp_connection_loss(&block)
|
66
|
+
if block.respond_to?(:call)
|
67
|
+
@on_tcp_connection_loss = block
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
55
71
|
def unbind
|
56
72
|
@connected = false
|
57
73
|
super
|
58
|
-
|
59
|
-
@
|
74
|
+
unless @closing_connection
|
75
|
+
if @connection_established
|
76
|
+
if @on_tcp_connection_loss
|
77
|
+
@on_tcp_connection_loss.call(self, @settings)
|
78
|
+
end
|
79
|
+
else
|
80
|
+
if @on_tcp_connection_failure
|
81
|
+
@on_tcp_connection_failure.call(self, @settings)
|
82
|
+
end
|
83
|
+
end
|
60
84
|
end
|
61
85
|
end
|
62
86
|
|
63
|
-
def connection_established?
|
64
|
-
@connection_established
|
65
|
-
end
|
66
|
-
|
67
87
|
def connected?
|
68
88
|
@connected
|
69
89
|
end
|
70
90
|
|
71
|
-
def self.connect(options)
|
91
|
+
def self.connect(options, additional={})
|
72
92
|
options ||= Hash.new
|
73
93
|
if options.is_a?(String)
|
74
94
|
begin
|
@@ -86,11 +106,17 @@ module Sensu
|
|
86
106
|
port = options[:port] || 6379
|
87
107
|
password = options[:password]
|
88
108
|
end
|
89
|
-
EM::connect(host, port, self) do |redis|
|
90
|
-
redis.
|
91
|
-
|
92
|
-
|
109
|
+
connection = EM::connect(host, port, self) do |redis|
|
110
|
+
redis.settings = {
|
111
|
+
:host => host,
|
112
|
+
:port => port,
|
113
|
+
:password => password
|
114
|
+
}
|
115
|
+
end
|
116
|
+
if additional[:on_tcp_connection_failure].respond_to?(:call)
|
117
|
+
connection.on_tcp_connection_failure = additional[:on_tcp_connection_failure]
|
93
118
|
end
|
119
|
+
connection
|
94
120
|
end
|
95
121
|
end
|
96
122
|
end
|
data/lib/sensu/server.rb
CHANGED
@@ -8,18 +8,8 @@ module Sensu
|
|
8
8
|
def self.run(options={})
|
9
9
|
server = self.new(options)
|
10
10
|
EM::run do
|
11
|
-
server.
|
12
|
-
server.
|
13
|
-
server.setup_keepalives
|
14
|
-
server.setup_results
|
15
|
-
server.setup_master_monitor
|
16
|
-
server.setup_rabbitmq_monitor
|
17
|
-
|
18
|
-
%w[INT TERM].each do |signal|
|
19
|
-
Signal.trap(signal) do
|
20
|
-
server.stop(signal)
|
21
|
-
end
|
22
|
-
end
|
11
|
+
server.start
|
12
|
+
server.trap_signals
|
23
13
|
end
|
24
14
|
end
|
25
15
|
|
@@ -28,26 +18,30 @@ module Sensu
|
|
28
18
|
base = Sensu::Base.new(options)
|
29
19
|
@settings = base.settings
|
30
20
|
@timers = Array.new
|
21
|
+
@master_timers = Array.new
|
31
22
|
@handlers_in_progress_count = 0
|
23
|
+
@is_master = false
|
32
24
|
end
|
33
25
|
|
34
26
|
def setup_redis
|
35
27
|
@logger.debug('connecting to redis', {
|
36
28
|
:settings => @settings[:redis]
|
37
29
|
})
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
30
|
+
connection_failure = Proc.new do
|
31
|
+
@logger.fatal('cannot connect to redis', {
|
32
|
+
:settings => @settings[:redis]
|
33
|
+
})
|
34
|
+
@logger.fatal('SENSU NOT RUNNING!')
|
35
|
+
if @rabbitmq
|
36
|
+
@rabbitmq.close
|
37
|
+
end
|
38
|
+
exit 2
|
39
|
+
end
|
40
|
+
@redis = Sensu::Redis.connect(@settings[:redis], :on_tcp_connection_failure => connection_failure)
|
41
|
+
@redis.on_tcp_connection_loss do
|
42
|
+
unless testing?
|
43
|
+
@logger.fatal('redis connection closed')
|
44
|
+
stop
|
51
45
|
end
|
52
46
|
end
|
53
47
|
end
|
@@ -56,8 +50,7 @@ module Sensu
|
|
56
50
|
@logger.debug('connecting to rabbitmq', {
|
57
51
|
:settings => @settings[:rabbitmq]
|
58
52
|
})
|
59
|
-
|
60
|
-
@rabbitmq.on_disconnect = Proc.new do
|
53
|
+
connection_failure = Proc.new do
|
61
54
|
@logger.fatal('cannot connect to rabbitmq', {
|
62
55
|
:settings => @settings[:rabbitmq]
|
63
56
|
})
|
@@ -65,7 +58,15 @@ module Sensu
|
|
65
58
|
@redis.close
|
66
59
|
exit 2
|
67
60
|
end
|
61
|
+
@rabbitmq = AMQP.connect(@settings[:rabbitmq], :on_tcp_connection_failure => connection_failure)
|
62
|
+
@rabbitmq.on_tcp_connection_loss do |connection, settings|
|
63
|
+
@logger.warn('reconnecting to rabbitmq')
|
64
|
+
resign_as_master do
|
65
|
+
connection.reconnect(false, 10)
|
66
|
+
end
|
67
|
+
end
|
68
68
|
@amq = AMQP::Channel.new(@rabbitmq)
|
69
|
+
@amq.auto_recovery = true
|
69
70
|
end
|
70
71
|
|
71
72
|
def setup_keepalives
|
@@ -86,16 +87,16 @@ module Sensu
|
|
86
87
|
subdue = false
|
87
88
|
if check[:subdue].is_a?(Hash)
|
88
89
|
if check[:subdue].has_key?(:start) && check[:subdue].has_key?(:end)
|
89
|
-
|
90
|
-
|
91
|
-
if
|
92
|
-
if Time.now <
|
93
|
-
|
90
|
+
start_time = Time.parse(check[:subdue][:start])
|
91
|
+
end_time = Time.parse(check[:subdue][:end])
|
92
|
+
if end_time < start_time
|
93
|
+
if Time.now < end_time
|
94
|
+
start_time = Time.parse('12:00:00 AM')
|
94
95
|
else
|
95
|
-
|
96
|
+
end_time = Time.parse('11:59:59 PM')
|
96
97
|
end
|
97
98
|
end
|
98
|
-
if Time.now >=
|
99
|
+
if Time.now >= start_time && Time.now <= end_time
|
99
100
|
subdue = true
|
100
101
|
end
|
101
102
|
end
|
@@ -422,22 +423,20 @@ module Sensu
|
|
422
423
|
@settings.checks.each do |check|
|
423
424
|
unless check[:publish] == false || check[:standalone]
|
424
425
|
check_count += 1
|
425
|
-
@
|
426
|
+
@master_timers << EM::Timer.new(stagger * check_count) do
|
426
427
|
interval = testing? ? 0.5 : check[:interval]
|
427
|
-
@
|
428
|
+
@master_timers << EM::PeriodicTimer.new(interval) do
|
428
429
|
unless check_subdued?(check, :publisher)
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
@amq.fanout(exchange_name).publish(payload.to_json)
|
440
|
-
end
|
430
|
+
payload = {
|
431
|
+
:name => check[:name],
|
432
|
+
:issued => Time.now.to_i
|
433
|
+
}
|
434
|
+
@logger.info('publishing check request', {
|
435
|
+
:payload => payload,
|
436
|
+
:subscribers => check[:subscribers]
|
437
|
+
})
|
438
|
+
check[:subscribers].uniq.each do |exchange_name|
|
439
|
+
@amq.fanout(exchange_name).publish(payload.to_json)
|
441
440
|
end
|
442
441
|
end
|
443
442
|
end
|
@@ -459,34 +458,32 @@ module Sensu
|
|
459
458
|
|
460
459
|
def setup_keepalive_monitor
|
461
460
|
@logger.debug('monitoring client keepalives')
|
462
|
-
@
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
publish_result(client, check)
|
489
|
-
end
|
461
|
+
@master_timers << EM::PeriodicTimer.new(30) do
|
462
|
+
@logger.debug('checking for stale client info')
|
463
|
+
@redis.smembers('clients').callback do |clients|
|
464
|
+
clients.each do |client_name|
|
465
|
+
@redis.get('client:' + client_name).callback do |client_json|
|
466
|
+
client = JSON.parse(client_json, :symbolize_names => true)
|
467
|
+
check = {
|
468
|
+
:name => 'keepalive',
|
469
|
+
:issued => Time.now.to_i
|
470
|
+
}
|
471
|
+
time_since_last_keepalive = Time.now.to_i - client[:timestamp]
|
472
|
+
case
|
473
|
+
when time_since_last_keepalive >= 180
|
474
|
+
check[:output] = 'No keep-alive sent from client in over 180 seconds'
|
475
|
+
check[:status] = 2
|
476
|
+
publish_result(client, check)
|
477
|
+
when time_since_last_keepalive >= 120
|
478
|
+
check[:output] = 'No keep-alive sent from client in over 120 seconds'
|
479
|
+
check[:status] = 1
|
480
|
+
publish_result(client, check)
|
481
|
+
else
|
482
|
+
@redis.hexists('events:' + client[:name], 'keepalive').callback do |exists|
|
483
|
+
if exists
|
484
|
+
check[:output] = 'Keep-alive sent from client'
|
485
|
+
check[:status] = 0
|
486
|
+
publish_result(client, check)
|
490
487
|
end
|
491
488
|
end
|
492
489
|
end
|
@@ -502,7 +499,6 @@ module Sensu
|
|
502
499
|
end
|
503
500
|
|
504
501
|
def request_master_election
|
505
|
-
@is_master ||= false
|
506
502
|
@redis.setnx('lock:master', Time.now.to_i).callback do |created|
|
507
503
|
if created
|
508
504
|
@is_master = true
|
@@ -524,32 +520,6 @@ module Sensu
|
|
524
520
|
end
|
525
521
|
end
|
526
522
|
|
527
|
-
def resign_as_master(&block)
|
528
|
-
if @redis.connected? && @is_master
|
529
|
-
@redis.del('lock:master').callback do
|
530
|
-
@logger.warn('resigned as master')
|
531
|
-
@is_master = false
|
532
|
-
end
|
533
|
-
if block
|
534
|
-
timestamp = Time.now.to_i
|
535
|
-
retry_until_true do
|
536
|
-
if !@is_master
|
537
|
-
block.call
|
538
|
-
true
|
539
|
-
elsif Time.now.to_i - timestamp >= 5
|
540
|
-
@logger.warn('failed to resign as master')
|
541
|
-
block.call
|
542
|
-
true
|
543
|
-
end
|
544
|
-
end
|
545
|
-
end
|
546
|
-
else
|
547
|
-
if block
|
548
|
-
block.call
|
549
|
-
end
|
550
|
-
end
|
551
|
-
end
|
552
|
-
|
553
523
|
def setup_master_monitor
|
554
524
|
request_master_election
|
555
525
|
@timers << EM::PeriodicTimer.new(20) do
|
@@ -557,53 +527,59 @@ module Sensu
|
|
557
527
|
@redis.set('lock:master', Time.now.to_i).callback do
|
558
528
|
@logger.debug('updated master lock timestamp')
|
559
529
|
end
|
560
|
-
|
530
|
+
elsif @rabbitmq.connected?
|
561
531
|
request_master_election
|
562
532
|
end
|
563
533
|
end
|
564
534
|
end
|
565
535
|
|
566
|
-
def
|
567
|
-
@
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
536
|
+
def resign_as_master(&block)
|
537
|
+
if @is_master
|
538
|
+
@logger.warn('resigning as master')
|
539
|
+
@master_timers.each do |timer|
|
540
|
+
timer.cancel
|
541
|
+
end
|
542
|
+
@master_timers = Array.new
|
543
|
+
@redis.del('lock:master').callback do
|
544
|
+
@logger.info('removed master lock')
|
545
|
+
@is_master = false
|
546
|
+
end
|
547
|
+
timestamp = Time.now.to_i
|
548
|
+
retry_until_true do
|
549
|
+
if !@is_master
|
550
|
+
block.call
|
551
|
+
true
|
552
|
+
elsif !@redis.connected? || Time.now.to_i - timestamp >= 5
|
553
|
+
@logger.warn('failed to remove master lock')
|
554
|
+
@is_master = false
|
555
|
+
block.call
|
556
|
+
true
|
577
557
|
end
|
578
|
-
else
|
579
|
-
@logger.warn('reconnecting to rabbitmq')
|
580
558
|
end
|
559
|
+
else
|
560
|
+
@logger.debug('not currently master')
|
561
|
+
block.call
|
581
562
|
end
|
582
563
|
end
|
583
564
|
|
584
565
|
def unsubscribe(&block)
|
566
|
+
@logger.warn('unsubscribing from keepalive and result queues')
|
567
|
+
@keepalive_queue.unsubscribe
|
568
|
+
@result_queue.unsubscribe
|
585
569
|
if @rabbitmq.connected?
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
true
|
596
|
-
elsif Time.now.to_i - timestamp >= 5
|
597
|
-
@logger.warn('failed to unsubscribe from keepalives and results')
|
598
|
-
block.call
|
599
|
-
true
|
600
|
-
end
|
570
|
+
timestamp = Time.now.to_i
|
571
|
+
retry_until_true do
|
572
|
+
if !@keepalive_queue.subscribed? && !@result_queue.subscribed?
|
573
|
+
block.call
|
574
|
+
true
|
575
|
+
elsif Time.now.to_i - timestamp >= 5
|
576
|
+
@logger.warn('failed to unsubscribe from keepalive and result queues')
|
577
|
+
block.call
|
578
|
+
true
|
601
579
|
end
|
602
580
|
end
|
603
581
|
else
|
604
|
-
|
605
|
-
block.call
|
606
|
-
end
|
582
|
+
block.call
|
607
583
|
end
|
608
584
|
end
|
609
585
|
|
@@ -611,20 +587,23 @@ module Sensu
|
|
611
587
|
@logger.info('completing handlers in progress', {
|
612
588
|
:handlers_in_progress_count => @handlers_in_progress_count
|
613
589
|
})
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
true
|
619
|
-
end
|
590
|
+
retry_until_true do
|
591
|
+
if @handlers_in_progress_count == 0
|
592
|
+
block.call
|
593
|
+
true
|
620
594
|
end
|
621
595
|
end
|
622
596
|
end
|
623
597
|
|
624
|
-
def
|
625
|
-
|
626
|
-
|
627
|
-
|
598
|
+
def start
|
599
|
+
setup_redis
|
600
|
+
setup_rabbitmq
|
601
|
+
setup_keepalives
|
602
|
+
setup_results
|
603
|
+
setup_master_monitor
|
604
|
+
end
|
605
|
+
|
606
|
+
def stop
|
628
607
|
@logger.warn('stopping')
|
629
608
|
@timers.each do |timer|
|
630
609
|
timer.cancel
|
@@ -632,6 +611,7 @@ module Sensu
|
|
632
611
|
unsubscribe do
|
633
612
|
resign_as_master do
|
634
613
|
complete_handlers_in_progress do
|
614
|
+
@rabbitmq.close
|
635
615
|
@redis.close
|
636
616
|
@logger.warn('stopping reactor')
|
637
617
|
EM::stop_event_loop
|
@@ -640,6 +620,17 @@ module Sensu
|
|
640
620
|
end
|
641
621
|
end
|
642
622
|
|
623
|
+
def trap_signals
|
624
|
+
%w[INT TERM].each do |signal|
|
625
|
+
Signal.trap(signal) do
|
626
|
+
@logger.warn('received signal', {
|
627
|
+
:signal => signal
|
628
|
+
})
|
629
|
+
stop
|
630
|
+
end
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
643
634
|
private
|
644
635
|
|
645
636
|
def testing?
|
data/sensu.gemspec
CHANGED
@@ -13,8 +13,8 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.has_rdoc = false
|
14
14
|
|
15
15
|
s.add_dependency('eventmachine', '1.0.0.rc.4')
|
16
|
-
s.add_dependency('amqp', '0.7
|
17
|
-
s.add_dependency('json'
|
16
|
+
s.add_dependency('amqp', '0.9.7')
|
17
|
+
s.add_dependency('json')
|
18
18
|
s.add_dependency('cabin', '0.4.4')
|
19
19
|
s.add_dependency('ruby-redis', '0.0.2')
|
20
20
|
s.add_dependency('async_sinatra', '1.0.0')
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sensu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 62196237
|
5
5
|
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 9
|
9
9
|
- 7
|
10
10
|
- beta
|
11
|
-
-
|
12
|
-
version: 0.9.7.beta.
|
11
|
+
- 3
|
12
|
+
version: 0.9.7.beta.3
|
13
13
|
platform: ruby
|
14
14
|
authors:
|
15
15
|
- Sean Porter
|
@@ -18,7 +18,7 @@ autorequire:
|
|
18
18
|
bindir: bin
|
19
19
|
cert_chain: []
|
20
20
|
|
21
|
-
date: 2012-08-
|
21
|
+
date: 2012-08-25 00:00:00 -07:00
|
22
22
|
default_executable:
|
23
23
|
dependencies:
|
24
24
|
- !ruby/object:Gem::Dependency
|
@@ -47,12 +47,12 @@ dependencies:
|
|
47
47
|
requirements:
|
48
48
|
- - "="
|
49
49
|
- !ruby/object:Gem::Version
|
50
|
-
hash:
|
50
|
+
hash: 53
|
51
51
|
segments:
|
52
52
|
- 0
|
53
|
+
- 9
|
53
54
|
- 7
|
54
|
-
|
55
|
-
version: 0.7.4
|
55
|
+
version: 0.9.7
|
56
56
|
type: :runtime
|
57
57
|
version_requirements: *id002
|
58
58
|
- !ruby/object:Gem::Dependency
|
@@ -61,14 +61,12 @@ dependencies:
|
|
61
61
|
requirement: &id003 !ruby/object:Gem::Requirement
|
62
62
|
none: false
|
63
63
|
requirements:
|
64
|
-
- - "
|
64
|
+
- - ">="
|
65
65
|
- !ruby/object:Gem::Version
|
66
|
-
hash:
|
66
|
+
hash: 3
|
67
67
|
segments:
|
68
|
-
-
|
69
|
-
|
70
|
-
- 3
|
71
|
-
version: 1.7.3
|
68
|
+
- 0
|
69
|
+
version: "0"
|
72
70
|
type: :runtime
|
73
71
|
version_requirements: *id003
|
74
72
|
- !ruby/object:Gem::Dependency
|
@@ -199,7 +197,6 @@ files:
|
|
199
197
|
- lib/sensu/client.rb
|
200
198
|
- lib/sensu/constants.rb
|
201
199
|
- lib/sensu/logger.rb
|
202
|
-
- lib/sensu/patches/amqp.rb
|
203
200
|
- lib/sensu/patches/ruby.rb
|
204
201
|
- lib/sensu/process.rb
|
205
202
|
- lib/sensu/redis.rb
|