sensu 0.13.0.alpha.2-java

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Sonian Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,11 @@
1
+ ![sensu](https://raw.github.com/sensu/sensu/master/sensu-logo.png)
2
+
3
+ A monitoring framework that aims to be simple, malleable, and scalable.
4
+
5
+ [![Build Status](https://secure.travis-ci.org/sensu/sensu.png)](https://travis-ci.org/sensu/sensu)
6
+
7
+ ## Documentation
8
+ Please refer to the [Sensu Docs](http://docs.sensuapp.org/).
9
+
10
+ ## License
11
+ Sensu is released under the [MIT license](https://raw.github.com/sensu/sensu/master/MIT-LICENSE.txt).
data/bin/sensu-api ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ unless $:.include?(File.dirname(__FILE__) + '/../lib/')
4
+ $: << File.dirname(__FILE__) + '/../lib'
5
+ end
6
+
7
+ require 'sensu/api'
8
+
9
+ options = Sensu::CLI.read
10
+ Sensu::API.run(options)
data/bin/sensu-client ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ unless $:.include?(File.dirname(__FILE__) + '/../lib/')
4
+ $: << File.dirname(__FILE__) + '/../lib'
5
+ end
6
+
7
+ require 'sensu/client'
8
+
9
+ options = Sensu::CLI.read
10
+ Sensu::Client.run(options)
data/bin/sensu-server ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ unless $:.include?(File.dirname(__FILE__) + '/../lib/')
4
+ $: << File.dirname(__FILE__) + '/../lib'
5
+ end
6
+
7
+ require 'sensu/server'
8
+
9
+ options = Sensu::CLI.read
10
+ Sensu::Server.run(options)
data/lib/sensu.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Sensu
2
+ # A monitoring framework that aims to be simple, malleable, & scalable.
3
+ end
data/lib/sensu/api.rb ADDED
@@ -0,0 +1,674 @@
1
+ require 'sensu/daemon'
2
+
3
+ gem 'sinatra', '1.3.5'
4
+ gem 'async_sinatra', '1.0.0'
5
+
6
+ unless RUBY_PLATFORM =~ /java/
7
+ gem 'thin', '1.5.0'
8
+ require 'thin'
9
+ end
10
+
11
+ require 'sinatra/async'
12
+
13
+ module Sensu
14
+ class API < Sinatra::Base
15
+ register Sinatra::Async
16
+
17
+ class << self
18
+ include Daemon
19
+
20
+ def run(options={})
21
+ bootstrap(options)
22
+ setup_process(options)
23
+ EM::run do
24
+ start
25
+ setup_signal_traps
26
+ end
27
+ end
28
+
29
+ def on_reactor_run
30
+ EM::next_tick do
31
+ setup_redis
32
+ set :redis, @redis
33
+ setup_transport
34
+ set :transport, @transport
35
+ end
36
+ end
37
+
38
+ def bootstrap(options)
39
+ setup_logger(options)
40
+ set :logger, @logger
41
+ load_settings(options)
42
+ set :checks, @settings[:checks]
43
+ set :all_checks, @settings.checks
44
+ if @settings[:api][:user] && @settings[:api][:password]
45
+ use Rack::Auth::Basic do |user, password|
46
+ user == @settings[:api][:user] && password == @settings[:api][:password]
47
+ end
48
+ end
49
+ on_reactor_run
50
+ self
51
+ end
52
+
53
+ def start_server
54
+ Thin::Logging.silent = true
55
+ bind = @settings[:api][:bind] || '0.0.0.0'
56
+ @thin = Thin::Server.new(bind, @settings[:api][:port], self)
57
+ @thin.start
58
+ end
59
+
60
+ def stop_server(&block)
61
+ @thin.stop
62
+ retry_until_true do
63
+ unless @thin.running?
64
+ block.call
65
+ true
66
+ end
67
+ end
68
+ end
69
+
70
+ def start
71
+ start_server
72
+ super
73
+ end
74
+
75
+ def stop
76
+ @logger.warn('stopping')
77
+ stop_server do
78
+ @redis.close
79
+ @transport.close
80
+ super
81
+ end
82
+ end
83
+
84
+ def test(options={})
85
+ bootstrap(options)
86
+ start
87
+ end
88
+ end
89
+
90
+ configure do
91
+ disable :protection
92
+ disable :show_exceptions
93
+ end
94
+
95
+ not_found do
96
+ ''
97
+ end
98
+
99
+ error do
100
+ ''
101
+ end
102
+
103
+ helpers do
104
+ def request_log_line
105
+ settings.logger.info([env['REQUEST_METHOD'], env['REQUEST_PATH']].join(' '), {
106
+ :remote_address => env['REMOTE_ADDR'],
107
+ :user_agent => env['HTTP_USER_AGENT'],
108
+ :request_method => env['REQUEST_METHOD'],
109
+ :request_uri => env['REQUEST_URI'],
110
+ :request_body => env['rack.input'].read
111
+ })
112
+ env['rack.input'].rewind
113
+ end
114
+
115
+ def bad_request!
116
+ ahalt 400
117
+ end
118
+
119
+ def not_found!
120
+ ahalt 404
121
+ end
122
+
123
+ def unavailable!
124
+ ahalt 503
125
+ end
126
+
127
+ def created!(response)
128
+ status 201
129
+ body response
130
+ end
131
+
132
+ def accepted!(response)
133
+ status 202
134
+ body response
135
+ end
136
+
137
+ def issued!
138
+ accepted!(MultiJson.dump(:issued => Time.now.to_i))
139
+ end
140
+
141
+ def no_content!
142
+ status 204
143
+ body ''
144
+ end
145
+
146
+ def read_data(rules={}, &block)
147
+ begin
148
+ data = MultiJson.load(env['rack.input'].read)
149
+ valid = rules.all? do |key, rule|
150
+ data[key].is_a?(rule[:type]) || (rule[:nil_ok] && data[key].nil?)
151
+ end
152
+ if valid
153
+ block.call(data)
154
+ else
155
+ bad_request!
156
+ end
157
+ rescue MultiJson::ParseError
158
+ bad_request!
159
+ end
160
+ end
161
+
162
+ def integer_parameter(parameter)
163
+ parameter =~ /^[0-9]+$/ ? parameter.to_i : nil
164
+ end
165
+
166
+ def pagination(items)
167
+ limit = integer_parameter(params[:limit])
168
+ offset = integer_parameter(params[:offset]) || 0
169
+ unless limit.nil?
170
+ headers['X-Pagination'] = MultiJson.dump(
171
+ :limit => limit,
172
+ :offset => offset,
173
+ :total => items.size
174
+ )
175
+ paginated = items.slice(offset, limit)
176
+ Array(paginated)
177
+ else
178
+ items
179
+ end
180
+ end
181
+
182
+ def transport_info(&block)
183
+ info = {
184
+ :keepalives => {
185
+ :messages => nil,
186
+ :consumers => nil
187
+ },
188
+ :results => {
189
+ :messages => nil,
190
+ :consumers => nil
191
+ },
192
+ :connected => settings.transport.connected?
193
+ }
194
+ if settings.transport.connected?
195
+ settings.transport.stats('keepalives') do |stats|
196
+ info[:keepalives] = stats
197
+ settings.transport.stats('results') do |stats|
198
+ info[:results] = stats
199
+ block.call(info)
200
+ end
201
+ end
202
+ else
203
+ block.call(info)
204
+ end
205
+ end
206
+
207
+ def resolve_event(event_json)
208
+ event = MultiJson.load(event_json)
209
+ check = event[:check].merge(
210
+ :output => 'Resolving on request of the API',
211
+ :status => 0,
212
+ :issued => Time.now.to_i,
213
+ :executed => Time.now.to_i,
214
+ :force_resolve => true
215
+ )
216
+ check.delete(:history)
217
+ payload = {
218
+ :client => event[:client][:name],
219
+ :check => check
220
+ }
221
+ settings.logger.info('publishing check result', {
222
+ :payload => payload
223
+ })
224
+ settings.transport.publish(:direct, 'results', MultiJson.dump(payload)) do |info|
225
+ if info[:error]
226
+ settings.logger.error('failed to publish check result', {
227
+ :payload => payload,
228
+ :error => info[:error].to_s
229
+ })
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ before do
236
+ request_log_line
237
+ content_type 'application/json'
238
+ end
239
+
240
+ aget '/info/?' do
241
+ transport_info do |info|
242
+ response = {
243
+ :sensu => {
244
+ :version => VERSION
245
+ },
246
+ :transport => info,
247
+ :redis => {
248
+ :connected => settings.redis.connected?
249
+ }
250
+ }
251
+ body MultiJson.dump(response)
252
+ end
253
+ end
254
+
255
+ aget '/health/?' do
256
+ if settings.redis.connected? && settings.transport.connected?
257
+ healthy = Array.new
258
+ min_consumers = integer_parameter(params[:consumers])
259
+ max_messages = integer_parameter(params[:messages])
260
+ transport_info do |info|
261
+ if min_consumers
262
+ healthy << (info[:keepalives][:consumers] >= min_consumers)
263
+ healthy << (info[:results][:consumers] >= min_consumers)
264
+ end
265
+ if max_messages
266
+ healthy << (info[:keepalives][:messages] <= max_messages)
267
+ healthy << (info[:results][:messages] <= max_messages)
268
+ end
269
+ healthy.all? ? no_content! : unavailable!
270
+ end
271
+ else
272
+ unavailable!
273
+ end
274
+ end
275
+
276
+ aget '/clients/?' do
277
+ response = Array.new
278
+ settings.redis.smembers('clients') do |clients|
279
+ clients = pagination(clients)
280
+ unless clients.empty?
281
+ clients.each_with_index do |client_name, index|
282
+ settings.redis.get('client:' + client_name) do |client_json|
283
+ response << MultiJson.load(client_json)
284
+ if index == clients.size - 1
285
+ body MultiJson.dump(response)
286
+ end
287
+ end
288
+ end
289
+ else
290
+ body MultiJson.dump(response)
291
+ end
292
+ end
293
+ end
294
+
295
+ aget %r{/clients?/([\w\.-]+)/?$} do |client_name|
296
+ settings.redis.get('client:' + client_name) do |client_json|
297
+ unless client_json.nil?
298
+ body client_json
299
+ else
300
+ not_found!
301
+ end
302
+ end
303
+ end
304
+
305
+ aget %r{/clients/([\w\.-]+)/history/?$} do |client_name|
306
+ response = Array.new
307
+ settings.redis.smembers('history:' + client_name) do |checks|
308
+ unless checks.empty?
309
+ checks.each_with_index do |check_name, index|
310
+ history_key = 'history:' + client_name + ':' + check_name
311
+ settings.redis.lrange(history_key, -21, -1) do |history|
312
+ history.map! do |status|
313
+ status.to_i
314
+ end
315
+ execution_key = 'execution:' + client_name + ':' + check_name
316
+ settings.redis.get(execution_key) do |last_execution|
317
+ unless history.empty? || last_execution.nil?
318
+ item = {
319
+ :check => check_name,
320
+ :history => history,
321
+ :last_execution => last_execution.to_i,
322
+ :last_status => history.last
323
+ }
324
+ response << item
325
+ end
326
+ if index == checks.size - 1
327
+ body MultiJson.dump(response)
328
+ end
329
+ end
330
+ end
331
+ end
332
+ else
333
+ body MultiJson.dump(response)
334
+ end
335
+ end
336
+ end
337
+
338
+ adelete %r{/clients?/([\w\.-]+)/?$} do |client_name|
339
+ settings.redis.get('client:' + client_name) do |client_json|
340
+ unless client_json.nil?
341
+ settings.redis.hgetall('events:' + client_name) do |events|
342
+ events.each do |check_name, event_json|
343
+ resolve_event(event_json)
344
+ end
345
+ EM::Timer.new(5) do
346
+ client = MultiJson.load(client_json)
347
+ settings.logger.info('deleting client', {
348
+ :client => client
349
+ })
350
+ settings.redis.srem('clients', client_name) do
351
+ settings.redis.del('client:' + client_name)
352
+ settings.redis.del('events:' + client_name)
353
+ settings.redis.smembers('history:' + client_name) do |checks|
354
+ checks.each do |check_name|
355
+ settings.redis.del('history:' + client_name + ':' + check_name)
356
+ settings.redis.del('execution:' + client_name + ':' + check_name)
357
+ end
358
+ settings.redis.del('history:' + client_name)
359
+ end
360
+ end
361
+ end
362
+ issued!
363
+ end
364
+ else
365
+ not_found!
366
+ end
367
+ end
368
+ end
369
+
370
+ aget '/checks/?' do
371
+ body MultiJson.dump(settings.all_checks)
372
+ end
373
+
374
+ aget %r{/checks?/([\w\.-]+)/?$} do |check_name|
375
+ if settings.checks[check_name]
376
+ response = settings.checks[check_name].merge(:name => check_name)
377
+ body MultiJson.dump(response)
378
+ else
379
+ not_found!
380
+ end
381
+ end
382
+
383
+ apost '/request/?' do
384
+ rules = {
385
+ :check => {:type => String, :nil_ok => false},
386
+ :subscribers => {:type => Array, :nil_ok => true}
387
+ }
388
+ read_data(rules) do |data|
389
+ if settings.checks[data[:check]]
390
+ check = settings.checks[data[:check]]
391
+ subscribers = data[:subscribers] || check[:subscribers] || Array.new
392
+ payload = {
393
+ :name => data[:check],
394
+ :command => check[:command],
395
+ :issued => Time.now.to_i
396
+ }
397
+ settings.logger.info('publishing check request', {
398
+ :payload => payload,
399
+ :subscribers => subscribers
400
+ })
401
+ subscribers.uniq.each do |exchange_name|
402
+ settings.transport.publish(:fanout, exchange_name, MultiJson.dump(payload)) do |info|
403
+ if info[:error]
404
+ settings.logger.error('failed to publish check request', {
405
+ :exchange_name => exchange_name,
406
+ :payload => payload,
407
+ :error => info[:error].to_s
408
+ })
409
+ end
410
+ end
411
+ end
412
+ issued!
413
+ else
414
+ not_found!
415
+ end
416
+ end
417
+ end
418
+
419
+ aget '/events/?' do
420
+ response = Array.new
421
+ settings.redis.smembers('clients') do |clients|
422
+ unless clients.empty?
423
+ clients.each_with_index do |client_name, index|
424
+ settings.redis.hgetall('events:' + client_name) do |events|
425
+ events.each do |check_name, event_json|
426
+ response << MultiJson.load(event_json)
427
+ end
428
+ if index == clients.size - 1
429
+ body MultiJson.dump(response)
430
+ end
431
+ end
432
+ end
433
+ else
434
+ body MultiJson.dump(response)
435
+ end
436
+ end
437
+ end
438
+
439
+ aget %r{/events/([\w\.-]+)/?$} do |client_name|
440
+ response = Array.new
441
+ settings.redis.hgetall('events:' + client_name) do |events|
442
+ events.each do |check_name, event_json|
443
+ response << MultiJson.load(event_json)
444
+ end
445
+ body MultiJson.dump(response)
446
+ end
447
+ end
448
+
449
+ aget %r{/events?/([\w\.-]+)/([\w\.-]+)/?$} do |client_name, check_name|
450
+ settings.redis.hgetall('events:' + client_name) do |events|
451
+ event_json = events[check_name]
452
+ unless event_json.nil?
453
+ body event_json
454
+ else
455
+ not_found!
456
+ end
457
+ end
458
+ end
459
+
460
+ adelete %r{/events?/([\w\.-]+)/([\w\.-]+)/?$} do |client_name, check_name|
461
+ settings.redis.hgetall('events:' + client_name) do |events|
462
+ if events.include?(check_name)
463
+ resolve_event(events[check_name])
464
+ issued!
465
+ else
466
+ not_found!
467
+ end
468
+ end
469
+ end
470
+
471
+ apost '/resolve/?' do
472
+ rules = {
473
+ :client => {:type => String, :nil_ok => false},
474
+ :check => {:type => String, :nil_ok => false}
475
+ }
476
+ read_data(rules) do |data|
477
+ settings.redis.hgetall('events:' + data[:client]) do |events|
478
+ if events.include?(data[:check])
479
+ resolve_event(events[data[:check]])
480
+ issued!
481
+ else
482
+ not_found!
483
+ end
484
+ end
485
+ end
486
+ end
487
+
488
+ aget '/aggregates/?' do
489
+ response = Array.new
490
+ settings.redis.smembers('aggregates') do |checks|
491
+ unless checks.empty?
492
+ checks.each_with_index do |check_name, index|
493
+ settings.redis.smembers('aggregates:' + check_name) do |aggregates|
494
+ aggregates.reverse!
495
+ aggregates.map! do |issued|
496
+ issued.to_i
497
+ end
498
+ item = {
499
+ :check => check_name,
500
+ :issued => aggregates
501
+ }
502
+ response << item
503
+ if index == checks.size - 1
504
+ body MultiJson.dump(response)
505
+ end
506
+ end
507
+ end
508
+ else
509
+ body MultiJson.dump(response)
510
+ end
511
+ end
512
+ end
513
+
514
+ aget %r{/aggregates/([\w\.-]+)/?$} do |check_name|
515
+ settings.redis.smembers('aggregates:' + check_name) do |aggregates|
516
+ unless aggregates.empty?
517
+ aggregates.reverse!
518
+ aggregates.map! do |issued|
519
+ issued.to_i
520
+ end
521
+ age = integer_parameter(params[:age])
522
+ if age
523
+ timestamp = Time.now.to_i - age
524
+ aggregates.reject! do |issued|
525
+ issued > timestamp
526
+ end
527
+ end
528
+ body MultiJson.dump(pagination(aggregates))
529
+ else
530
+ not_found!
531
+ end
532
+ end
533
+ end
534
+
535
+ adelete %r{/aggregates/([\w\.-]+)/?$} do |check_name|
536
+ settings.redis.smembers('aggregates:' + check_name) do |aggregates|
537
+ unless aggregates.empty?
538
+ aggregates.each do |check_issued|
539
+ result_set = check_name + ':' + check_issued
540
+ settings.redis.del('aggregation:' + result_set)
541
+ settings.redis.del('aggregate:' + result_set)
542
+ end
543
+ settings.redis.del('aggregates:' + check_name) do
544
+ settings.redis.srem('aggregates', check_name) do
545
+ no_content!
546
+ end
547
+ end
548
+ else
549
+ not_found!
550
+ end
551
+ end
552
+ end
553
+
554
+ aget %r{/aggregates?/([\w\.-]+)/([\w\.-]+)/?$} do |check_name, check_issued|
555
+ result_set = check_name + ':' + check_issued
556
+ settings.redis.hgetall('aggregate:' + result_set) do |aggregate|
557
+ unless aggregate.empty?
558
+ response = aggregate.inject(Hash.new) do |totals, (status, count)|
559
+ totals[status] = Integer(count)
560
+ totals
561
+ end
562
+ settings.redis.hgetall('aggregation:' + result_set) do |results|
563
+ parsed_results = results.inject(Array.new) do |parsed, (client_name, check_json)|
564
+ check = MultiJson.load(check_json)
565
+ parsed << check.merge(:client => client_name)
566
+ end
567
+ if params[:summarize]
568
+ options = params[:summarize].split(',')
569
+ if options.include?('output')
570
+ outputs = Hash.new(0)
571
+ parsed_results.each do |result|
572
+ outputs[result[:output]] += 1
573
+ end
574
+ response[:outputs] = outputs
575
+ end
576
+ end
577
+ if params[:results]
578
+ response[:results] = parsed_results
579
+ end
580
+ body MultiJson.dump(response)
581
+ end
582
+ else
583
+ not_found!
584
+ end
585
+ end
586
+ end
587
+
588
+ apost %r{/stash(?:es)?/(.*)/?} do |path|
589
+ read_data do |data|
590
+ settings.redis.set('stash:' + path, MultiJson.dump(data)) do
591
+ settings.redis.sadd('stashes', path) do
592
+ created!(MultiJson.dump(:path => path))
593
+ end
594
+ end
595
+ end
596
+ end
597
+
598
+ aget %r{/stash(?:es)?/(.*)/?} do |path|
599
+ settings.redis.get('stash:' + path) do |stash_json|
600
+ unless stash_json.nil?
601
+ body stash_json
602
+ else
603
+ not_found!
604
+ end
605
+ end
606
+ end
607
+
608
+ adelete %r{/stash(?:es)?/(.*)/?} do |path|
609
+ settings.redis.exists('stash:' + path) do |stash_exists|
610
+ if stash_exists
611
+ settings.redis.srem('stashes', path) do
612
+ settings.redis.del('stash:' + path) do
613
+ no_content!
614
+ end
615
+ end
616
+ else
617
+ not_found!
618
+ end
619
+ end
620
+ end
621
+
622
+ aget '/stashes/?' do
623
+ response = Array.new
624
+ settings.redis.smembers('stashes') do |stashes|
625
+ unless stashes.empty?
626
+ stashes.each_with_index do |path, index|
627
+ settings.redis.get('stash:' + path) do |stash_json|
628
+ settings.redis.ttl('stash:' + path) do |ttl|
629
+ unless stash_json.nil?
630
+ item = {
631
+ :path => path,
632
+ :content => MultiJson.load(stash_json),
633
+ :expire => ttl
634
+ }
635
+ response << item
636
+ else
637
+ settings.redis.srem('stashes', path)
638
+ end
639
+ if index == stashes.size - 1
640
+ body MultiJson.dump(pagination(response))
641
+ end
642
+ end
643
+ end
644
+ end
645
+ else
646
+ body MultiJson.dump(response)
647
+ end
648
+ end
649
+ end
650
+
651
+ apost '/stashes/?' do
652
+ rules = {
653
+ :path => {:type => String, :nil_ok => false},
654
+ :content => {:type => Hash, :nil_ok => false},
655
+ :expire => {:type => Integer, :nil_ok => true}
656
+ }
657
+ read_data(rules) do |data|
658
+ stash_key = 'stash:' + data[:path]
659
+ settings.redis.set(stash_key, MultiJson.dump(data[:content])) do
660
+ settings.redis.sadd('stashes', data[:path]) do
661
+ response = MultiJson.dump(:path => data[:path])
662
+ if data[:expire]
663
+ settings.redis.expire(stash_key, data[:expire]) do
664
+ created!(response)
665
+ end
666
+ else
667
+ created!(response)
668
+ end
669
+ end
670
+ end
671
+ end
672
+ end
673
+ end
674
+ end