sensu-dashboard-sonian 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.gitignore +6 -0
  2. data/Gemfile +4 -0
  3. data/MIT-LICENSE.txt +20 -0
  4. data/README.org +4 -0
  5. data/Rakefile +1 -0
  6. data/bin/sensu-dashboard +9 -0
  7. data/lib/sensu-dashboard/app.rb +449 -0
  8. data/lib/sensu-dashboard/public/css/autoSuggest.css +217 -0
  9. data/lib/sensu-dashboard/public/css/style.css +177 -0
  10. data/lib/sensu-dashboard/public/img/cross.png +0 -0
  11. data/lib/sensu-dashboard/public/img/footer_bg.png +0 -0
  12. data/lib/sensu-dashboard/public/img/header_bg.png +0 -0
  13. data/lib/sensu-dashboard/public/img/loading_circle.gif +0 -0
  14. data/lib/sensu-dashboard/public/img/main_content_bg.png +0 -0
  15. data/lib/sensu-dashboard/public/img/megaphone_icon.png +0 -0
  16. data/lib/sensu-dashboard/public/img/megaphone_icon_off.png +0 -0
  17. data/lib/sensu-dashboard/public/js/FABridge.js +604 -0
  18. data/lib/sensu-dashboard/public/js/functions.js +379 -0
  19. data/lib/sensu-dashboard/public/js/jquery-1.5.1.min.js +16 -0
  20. data/lib/sensu-dashboard/public/js/jquery.autoSuggest.js +375 -0
  21. data/lib/sensu-dashboard/public/js/jquery.leanModal.min.js +1 -0
  22. data/lib/sensu-dashboard/public/js/jquery.sortElements.js +69 -0
  23. data/lib/sensu-dashboard/public/js/jquery.tmpl.min.js +1 -0
  24. data/lib/sensu-dashboard/public/js/jquery.zclip.min.js +12 -0
  25. data/lib/sensu-dashboard/public/js/modernizr-1.7.min.js +2 -0
  26. data/lib/sensu-dashboard/public/js/swfobject.js +4 -0
  27. data/lib/sensu-dashboard/public/js/web_socket.js +312 -0
  28. data/lib/sensu-dashboard/public/js/webtoolkit.sha1.js +174 -0
  29. data/lib/sensu-dashboard/public/swf/WebSocketMain.swf +0 -0
  30. data/lib/sensu-dashboard/public/swf/ZeroClipboard.swf +0 -0
  31. data/lib/sensu-dashboard/version.rb +5 -0
  32. data/lib/sensu-dashboard/views/client_templates.erb +68 -0
  33. data/lib/sensu-dashboard/views/clients.erb +37 -0
  34. data/lib/sensu-dashboard/views/event_templates.erb +190 -0
  35. data/lib/sensu-dashboard/views/index.erb +48 -0
  36. data/lib/sensu-dashboard/views/layout.erb +124 -0
  37. data/lib/sensu-dashboard/views/sonian.sass +213 -0
  38. data/lib/sensu-dashboard/views/stash_templates.erb +44 -0
  39. data/lib/sensu-dashboard/views/stashes.erb +32 -0
  40. data/sensu-dashboard.gemspec +22 -0
  41. metadata +166 -0
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ .DS_Store
2
+ .sass-cache/
3
+ .gem
4
+ .bundle
5
+ Gemfile.lock
6
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in sensu-dashboard.gemspec
4
+ gemspec
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.org ADDED
@@ -0,0 +1,4 @@
1
+ * Sonian Monitoring Dashboard
2
+ - Lists current alerts from the monitoring server
3
+ - Lists clients subscribed to AMQP queues
4
+ - Uses WebSockets to receive new information
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ begin
3
+ require 'sensu-dashboard/app.rb'
4
+ rescue LoadError => e
5
+ require 'rubygems'
6
+ path = File.expand_path '../../lib', __FILE__
7
+ $:.unshift(path) if File.directory?(path) && !$:.include?(path)
8
+ require 'sensu-dashboard/app.rb'
9
+ end
@@ -0,0 +1,449 @@
1
+ require 'eventmachine'
2
+ require 'sinatra/async'
3
+ require 'em-http-request'
4
+ require 'em-websocket'
5
+ require 'sass'
6
+ require 'json'
7
+ require 'sensu/config'
8
+
9
+ options = Sensu::Config.read_arguments(ARGV)
10
+ config = Sensu::Config.new(options)
11
+ SETTINGS = config.settings
12
+
13
+ EventMachine.run do
14
+
15
+ class DashboardServer < Sinatra::Base
16
+
17
+ register Sinatra::Async
18
+ set :root, File.dirname(__FILE__)
19
+ set :static, true
20
+ set :public_folder, Proc.new { File.join(root, "public") }
21
+
22
+ api_server = 'http://' + SETTINGS['api']['host'] + ':' + SETTINGS['api']['port'].to_s
23
+
24
+ use Rack::Auth::Basic do |user, password|
25
+ user == SETTINGS['dashboard']['user'] && password == SETTINGS['dashboard']['password']
26
+ end
27
+
28
+ before do
29
+ content_type 'application/json'
30
+ end
31
+
32
+ aget '/' do
33
+ content_type 'text/html'
34
+ @js = erb :event_templates, :layout => false
35
+ body erb :index
36
+ end
37
+
38
+ aget '/clients' do
39
+ content_type 'text/html'
40
+ @js = erb :client_templates, :layout => false
41
+ body erb :clients
42
+ end
43
+
44
+ aget '/stashes' do
45
+ content_type 'text/html'
46
+ @js = erb :stash_templates, :layout => false
47
+ body erb :stashes
48
+ end
49
+
50
+ aget '/css/sonian.css' do
51
+ content_type 'text/css'
52
+ body sass :sonian
53
+ end
54
+
55
+ # api proxy
56
+ aget '/events.json' do
57
+ begin
58
+ http = EventMachine::HttpRequest.new("#{api_server}/events").get
59
+ rescue => e
60
+ puts e
61
+ status 404
62
+ body '{"error":"could not retrieve events from the sensu api"}'
63
+ end
64
+
65
+ http.errback do
66
+ status 404
67
+ body '{"error":"could not retrieve events from the sensu api"}'
68
+ end
69
+
70
+ http.callback do
71
+ status http.response_header.status
72
+ body http.response
73
+ end
74
+ end
75
+
76
+ # api proxy
77
+ aget '/autocomplete.json' do
78
+ multi = EventMachine::MultiRequest.new
79
+
80
+ requests = [
81
+ "#{api_server}/events",
82
+ "#{api_server}/clients"
83
+ ]
84
+
85
+ requests.each do |url|
86
+ multi.add EventMachine::HttpRequest.new(url).get
87
+ end
88
+
89
+ multi.callback do
90
+ events = {}
91
+ clients = []
92
+
93
+ multi.responses[:succeeded].each do |request|
94
+ body = JSON.parse(request.response)
95
+ case body
96
+ when Hash
97
+ events = body
98
+ when Array
99
+ clients = body
100
+ end
101
+ end
102
+
103
+ if events && clients
104
+ autocomplete = []
105
+ statuses = {:warning => [], :critical => [], :unknown => []}
106
+ subscriptions = {}
107
+ checks = []
108
+
109
+ # searching by client
110
+ clients.each do |client|
111
+ client_name = client['name']
112
+ if events.include?(client_name)
113
+ autocomplete.push({:value => [client_name], :type => 'client', :name => client_name})
114
+ client['subscriptions'].each do |subscription|
115
+ subscriptions[subscription] ||= []
116
+ subscriptions[subscription].push(client_name)
117
+ end
118
+ events[client_name].each do |check, event|
119
+
120
+ case event['status']
121
+ when 1
122
+ statuses[:warning].push(event['status'])
123
+ when 2
124
+ statuses[:critical].push(event['status'])
125
+ else
126
+ statuses[:unknown].push(event['status'])
127
+ end
128
+ checks.push(check)
129
+ end
130
+ end
131
+ end
132
+
133
+ # searching by subscription
134
+ subscriptions.each do |k, v|
135
+ autocomplete.push({:value => v.uniq, :type => 'subscription', :name => k})
136
+ end
137
+
138
+ # searching by status
139
+ statuses.each do |k, v|
140
+ autocomplete.push({:value => v.uniq, :type => 'status', :name => k})
141
+ end
142
+
143
+ # searching by check
144
+ checks.uniq.each do |v|
145
+ autocomplete.push({:value => [v], :type => 'check', :name => v})
146
+ end
147
+
148
+ body autocomplete.to_json
149
+ else
150
+ status 404
151
+ body '{"error":"could not retrieve events and/or clients from the sensu api"}'
152
+ end
153
+ end
154
+ end
155
+
156
+ aget '/clients.json' do
157
+ begin
158
+ http = EventMachine::HttpRequest.new("#{api_server}/clients").get
159
+ rescue => e
160
+ puts e
161
+ status 404
162
+ body '{"error":"could not retrieve clients from the sensu api"}'
163
+ end
164
+
165
+ http.errback do
166
+ status 404
167
+ body '{"error":"could not retrieve clients from the sensu api"}'
168
+ end
169
+
170
+ http.callback do
171
+ status http.response_header.status
172
+ body http.response
173
+ end
174
+ end
175
+
176
+ # api proxy
177
+ aget '/clients/autocomplete.json' do
178
+ multi = EventMachine::MultiRequest.new
179
+
180
+ requests = [
181
+ "#{api_server}/clients"
182
+ ]
183
+
184
+ requests.each do |url|
185
+ multi.add EventMachine::HttpRequest.new(url).get
186
+ end
187
+
188
+ multi.callback do
189
+ events = {}
190
+ clients = []
191
+
192
+ multi.responses[:succeeded].each do |request|
193
+ body = JSON.parse(request.response)
194
+ case body
195
+ when Array
196
+ clients = body
197
+ end
198
+ end
199
+
200
+ if clients
201
+ autocomplete = []
202
+ subscriptions = {}
203
+
204
+ # searching by client
205
+ clients.each do |client|
206
+ client_name = client['name']
207
+ autocomplete.push({:value => [client_name], :type => 'client', :name => client_name})
208
+ client['subscriptions'].each do |subscription|
209
+ subscriptions[subscription] ||= []
210
+ subscriptions[subscription].push(client_name)
211
+ end
212
+ end
213
+
214
+ # searching by subscription
215
+ subscriptions.each do |k, v|
216
+ autocomplete.push({:value => v.uniq, :type => 'subscription', :name => k})
217
+ end
218
+
219
+ body autocomplete.to_json
220
+ else
221
+ status 404
222
+ body '{"error":"could not retrieve clients from the sensu api"}'
223
+ end
224
+ end
225
+ end
226
+
227
+ aget '/client/:id.json' do |id|
228
+ begin
229
+ http = EventMachine::HttpRequest.new("#{api_server}/client/#{id}").get
230
+ rescue => e
231
+ puts e
232
+ status 404
233
+ body '{"error":"could not retrieve client from the sensu api"}'
234
+ end
235
+
236
+ http.errback do
237
+ status 404
238
+ body '{"error":"could not retrieve client from the sensu api"}'
239
+ end
240
+
241
+ http.callback do
242
+ status http.response_header.status
243
+ body http.response
244
+ end
245
+ end
246
+
247
+ adelete '/client/:id.json' do |id|
248
+ begin
249
+ http = EventMachine::HttpRequest.new("#{api_server}/client/#{id}").delete
250
+ rescue => e
251
+ puts e
252
+ status 404
253
+ body '{"error":"could not delete client from the sensu api"}'
254
+ end
255
+
256
+ http.errback do
257
+ status 404
258
+ body '{"error":"could not delete client from the sensu api"}'
259
+ end
260
+
261
+ http.callback do
262
+ status http.response_header.status
263
+ body http.response
264
+ end
265
+ end
266
+
267
+ aget '/stash/*.json' do |path|
268
+ begin
269
+ http = EventMachine::HttpRequest.new("#{api_server}/stash/#{path}").get
270
+ rescue => e
271
+ puts e
272
+ status 404
273
+ body '{"error":"could not retrieve a stash from the sensu api"}'
274
+ end
275
+
276
+ http.errback do
277
+ status 404
278
+ body '{"error":"could not retrieve a stash from the sensu api"}'
279
+ end
280
+
281
+ http.callback do
282
+ status http.response_header.status
283
+ body http.response
284
+ end
285
+ end
286
+
287
+ apost '/stash/*.json' do |path|
288
+ begin
289
+ request_options = {
290
+ :body => {'timestamp' => Time.now.to_i}.to_json,
291
+ :head => {
292
+ 'content-type' => 'application/json'
293
+ }
294
+ }
295
+ http = EventMachine::HttpRequest.new("#{api_server}/stash/#{path}").post request_options
296
+ rescue => e
297
+ puts e
298
+ status 404
299
+ body '{"error":"could not create a stash with the sensu api"}'
300
+ end
301
+
302
+ http.errback do
303
+ status 404
304
+ body '{"error":"could not create a stash with the sensu api"}'
305
+ end
306
+
307
+ http.callback do
308
+ status http.response_header.status
309
+ body http.response
310
+ end
311
+ end
312
+
313
+ adelete '/stash/*.json' do |path|
314
+ begin
315
+ http = EventMachine::HttpRequest.new("#{api_server}/stash/#{path}").delete
316
+ rescue => e
317
+ puts e
318
+ status 404
319
+ body '{"error":"could not delete a stash with the sensu api"}'
320
+ end
321
+
322
+ http.errback do
323
+ status 404
324
+ body '{"error":"could not delete a stash with the sensu api"}'
325
+ end
326
+
327
+ http.callback do
328
+ status http.response_header.status
329
+ body http.response
330
+ end
331
+ end
332
+
333
+ apost '/event/resolve.json' do
334
+ begin
335
+ request_options = {
336
+ :body => request.body.read,
337
+ :head => {
338
+ 'content-type' => 'application/json'
339
+ }
340
+ }
341
+ http = EventMachine::HttpRequest.new("#{api_server}/event/resolve").post request_options
342
+ rescue => e
343
+ puts e
344
+ status 404
345
+ body '{"error":"could not resolve an event with the sensu api"}'
346
+ end
347
+
348
+ http.errback do
349
+ status 404
350
+ body '{"error":"could not resolve an event with the sensu api"}'
351
+ end
352
+
353
+ http.callback do
354
+ status http.response_header.status
355
+ body http.response
356
+ end
357
+ end
358
+
359
+ aget '/stashes.json' do
360
+ begin
361
+ request_options = {
362
+ # :body => request.body.read,
363
+ :head => {
364
+ 'content-type' => 'application/json'
365
+ }
366
+ }
367
+ http = EventMachine::HttpRequest.new("#{api_server}/stashes").get request_options
368
+ rescue => e
369
+ puts e
370
+ status 404
371
+ body '{"error":"could not retrieve a list of stashes from the sensu api"}'
372
+ end
373
+
374
+ http.errback do
375
+ status 404
376
+ body '{"error":"could not retrieve a list of stashes from the sensu api"}'
377
+ end
378
+
379
+ http.callback do
380
+ resp = http.response
381
+ puts resp
382
+ status http.response_header.status
383
+ body resp
384
+ end
385
+ end
386
+
387
+ apost '/stashes.json' do
388
+ begin
389
+ request_options = {
390
+ :body => request.body.read,
391
+ :head => {
392
+ 'content-type' => 'application/json'
393
+ }
394
+ }
395
+ http = EventMachine::HttpRequest.new("#{api_server}/stashes").post request_options
396
+ rescue => e
397
+ puts e
398
+ status 404
399
+ body '{\"error\":\"could not retrieve a list of stashes from the sensu api\"}'
400
+ end
401
+
402
+ http.errback do
403
+ status 404
404
+ body '{\"error\":\"could not retrieve a list of stashes from the sensu api\"}'
405
+ end
406
+
407
+ http.callback do
408
+ resp = http.response
409
+ puts resp
410
+ status http.response_header.status
411
+ body resp
412
+ end
413
+ end
414
+
415
+ websocket_connections = Array.new
416
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 9000) do |websocket|
417
+ websocket.onopen do
418
+ websocket_connections.push websocket
419
+ puts 'client connected to websocket'
420
+ end
421
+ websocket.onclose do
422
+ websocket_connections.delete websocket
423
+ puts 'client disconnected from websocket'
424
+ end
425
+ end
426
+
427
+ apost '/events.json' do
428
+ unless websocket_connections.empty?
429
+ websocket_connections.each do |websocket|
430
+ websocket.send '{"update":"true"}'
431
+ end
432
+ end
433
+ body '{"success":"triggered dashboard refresh"}'
434
+ end
435
+ end
436
+
437
+ DashboardServer.run!({:port => SETTINGS['dashboard']['port']})
438
+
439
+ #
440
+ # Recognize exit command
441
+ #
442
+ Signal.trap("INT") do
443
+ EM.stop
444
+ end
445
+ Signal.trap("TERM") do
446
+ EM.stop
447
+ end
448
+
449
+ end