sensu 0.13.0.alpha.2-java

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.
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