sensu-dashboard-sonian 0.9.3

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