lib/sensu-dashboard/app.rb in sensu-dashboard-0.9.4 vs lib/sensu-dashboard/app.rb in sensu-dashboard-0.9.6.beta

- old
+ new

@@ -1,448 +1,464 @@ -require 'eventmachine' -require 'sinatra/async' +require 'sensu/config' require 'em-http-request' require 'em-websocket' +require 'sinatra/async' require 'sass' -require 'json' -require 'sensu/config' -options = Sensu::Config.read_arguments(ARGV) -config = Sensu::Config.new(options) -SETTINGS = config.settings +class Dashboard < Sinatra::Base + register Sinatra::Async -EventMachine.run do + def self.run(options={}) + EM::run do + self.setup(options) + self.websocket_server + self.run!(:port => $settings.dashboard.port) - class DashboardServer < Sinatra::Base - - register Sinatra::Async - set :root, File.dirname(__FILE__) - set :static, true - set :public_folder, Proc.new { File.join(root, "public") } - - api_server = 'http://' + SETTINGS['api']['host'] + ':' + SETTINGS['api']['port'].to_s - - use Rack::Auth::Basic do |user, password| - user == SETTINGS['dashboard']['user'] && password == SETTINGS['dashboard']['password'] + %w[INT TERM].each do |signal| + Signal.trap(signal) do + self.stop(signal) + end + end end + end - before do - content_type 'application/json' + def self.setup(options={}) + config = Sensu::Config.new(options) + $settings = config.settings + $logger = config.logger || config.open_log + unless $settings.key?('dashboard') + raise config.invalid_config('missing the following key: dashboard') end - - aget '/' do - content_type 'text/html' - @js = erb :event_templates, :layout => false - body erb :index + unless $settings.dashboard.port.is_a?(Integer) + raise config.invalid_config('dashboard must have a port') end - - aget '/clients' do - content_type 'text/html' - @js = erb :client_templates, :layout => false - body erb :clients + unless $settings.dashboard.user.is_a?(String) && $settings.dashboard.password.is_a?(String) + raise config.invalid_config('dashboard must have a user and password') end - - aget '/stashes' do - content_type 'text/html' - @js = erb :stash_templates, :layout => false - body erb :stashes + if options[:daemonize] + Process.daemonize end - - aget '/css/sonian.css' do - content_type 'text/css' - body sass :sonian + if options[:pid_file] + Process.write_pid(options[:pid_file]) end + $api_server = 'http://' + $settings.api.host + ':' + $settings.api.port.to_s + end - # api proxy - aget '/events.json' do - begin - http = EventMachine::HttpRequest.new("#{api_server}/events").get - rescue => e - puts e - status 404 - body '{"error":"could not retrieve events from the sensu api"}' + def self.websocket_server + $websocket_connections = [] + EM::WebSocket.start(:host => '0.0.0.0', :port => 9000) do |websocket| + websocket.onopen do + $logger.info('[websocket] -- client connected to websocket') + $websocket_connections.push(websocket) end - - http.errback do - status 404 - body '{"error":"could not retrieve events from the sensu api"}' + websocket.onclose do + $logger.info('[websocket] -- client disconnected from websocket') + $websocket_connections.delete(websocket) end + end + end - http.callback do - status http.response_header.status - body http.response + set :root, File.dirname(__FILE__) + set :static, true + set :public_folder, Proc.new { File.join(root, 'public') } + + use Rack::Auth::Basic do |user, password| + user == $settings.dashboard.user && password == $settings.dashboard.password + end + + before do + content_type 'application/json' + end + + aget '/' do + content_type 'text/html' + @js = erb :event_templates, :layout => false + body erb :index + end + + aget '/clients' do + content_type 'text/html' + @js = erb :client_templates, :layout => false + body erb :clients + end + + aget '/stashes' do + content_type 'text/html' + @js = erb :stash_templates, :layout => false + body erb :stashes + end + + aget '/css/sonian.css' do + content_type 'text/css' + body sass :sonian + end + + apost '/events.json' do + $logger.debug('[events] -- ' + request.ip + ' -- POST -- triggering dashboard refresh') + unless $websocket_connections.empty? + $websocket_connections.each do |websocket| + websocket.send '{"update":"true"}' end end + body '{"success":"triggered dashboard refresh"}' + end - # api proxy - aget '/autocomplete.json' do - multi = EventMachine::MultiRequest.new + aget '/autocomplete.json' do + multi = EM::MultiRequest.new - requests = [ - "#{api_server}/events", - "#{api_server}/clients" - ] + requests = [ + $api_server + '/events', + $api_server + '/clients' + ] - requests.each do |url| - multi.add EventMachine::HttpRequest.new(url).get - end + requests.each do |url| + multi.add EM::HttpRequest.new(url).get + end - multi.callback do - events = {} - clients = [] + multi.callback do + events = {} + clients = [] - multi.responses[:succeeded].each do |request| - body = JSON.parse(request.response) - case body - when Hash - events = body - when Array - clients = body - end + multi.responses[:succeeded].each do |request| + body = JSON.parse(request.response) + case body + when Hash + events = body + when Array + clients = body end + end - if events && clients - autocomplete = [] - statuses = {:warning => [], :critical => [], :unknown => []} - subscriptions = {} - checks = [] + if events && clients + autocomplete = [] + statuses = {:warning => [], :critical => [], :unknown => []} + subscriptions = {} + checks = [] - # searching by client - clients.each do |client| - client_name = client['name'] - if events.include?(client_name) - autocomplete.push({:value => [client_name], :type => 'client', :name => client_name}) - client['subscriptions'].each do |subscription| - subscriptions[subscription] ||= [] - subscriptions[subscription].push(client_name) + # searching by client + clients.each do |client| + client_name = client['name'] + if events.include?(client_name) + autocomplete.push({:value => [client_name], :type => 'client', :name => client_name}) + client['subscriptions'].each do |subscription| + subscriptions[subscription] ||= [] + subscriptions[subscription].push(client_name) + end + events[client_name].each do |check, event| + case event['status'] + when 1 + statuses[:warning].push(event['status']) + when 2 + statuses[:critical].push(event['status']) + else + statuses[:unknown].push(event['status']) end - events[client_name].each do |check, event| - - case event['status'] - when 1 - statuses[:warning].push(event['status']) - when 2 - statuses[:critical].push(event['status']) - else - statuses[:unknown].push(event['status']) - end - checks.push(check) - end + checks.push(check) end end + end - # searching by subscription - subscriptions.each do |k, v| - autocomplete.push({:value => v.uniq, :type => 'subscription', :name => k}) - end + # searching by subscription + subscriptions.each do |k, v| + autocomplete.push({:value => v.uniq, :type => 'subscription', :name => k}) + end - # searching by status - statuses.each do |k, v| - autocomplete.push({:value => v.uniq, :type => 'status', :name => k}) - end + # searching by status + statuses.each do |k, v| + autocomplete.push({:value => v.uniq, :type => 'status', :name => k}) + end - # searching by check - checks.uniq.each do |v| - autocomplete.push({:value => [v], :type => 'check', :name => v}) - end - - body autocomplete.to_json - else - status 404 - body '{"error":"could not retrieve events and/or clients from the sensu api"}' + # searching by check + checks.uniq.each do |v| + autocomplete.push({:value => [v], :type => 'check', :name => v}) end - end - end - aget '/clients.json' do - begin - http = EventMachine::HttpRequest.new("#{api_server}/clients").get - rescue => e - puts e + body autocomplete.to_json + else status 404 - body '{"error":"could not retrieve clients from the sensu api"}' + body '{"error":"could not retrieve events and/or clients from the sensu api"}' end + end + end - http.errback do - status 404 - body '{"error":"could not retrieve clients from the sensu api"}' - end + aget '/clients/autocomplete.json' do + multi = EM::MultiRequest.new - http.callback do - status http.response_header.status - body http.response - end + requests = [ + $api_server + '/clients' + ] + + requests.each do |url| + multi.add EM::HttpRequest.new(url).get end - # api proxy - aget '/clients/autocomplete.json' do - multi = EventMachine::MultiRequest.new + multi.callback do + events = {} + clients = [] - requests = [ - "#{api_server}/clients" - ] - - requests.each do |url| - multi.add EventMachine::HttpRequest.new(url).get + multi.responses[:succeeded].each do |request| + body = JSON.parse(request.response) + case body + when Array + clients = body + end end - multi.callback do - events = {} - clients = [] + if clients + autocomplete = [] + subscriptions = {} - multi.responses[:succeeded].each do |request| - body = JSON.parse(request.response) - case body - when Array - clients = body + # searching by client + clients.each do |client| + client_name = client['name'] + autocomplete.push({:value => [client_name], :type => 'client', :name => client_name}) + client['subscriptions'].each do |subscription| + subscriptions[subscription] ||= [] + subscriptions[subscription].push(client_name) end end - if clients - autocomplete = [] - subscriptions = {} - - # searching by client - clients.each do |client| - client_name = client['name'] - autocomplete.push({:value => [client_name], :type => 'client', :name => client_name}) - client['subscriptions'].each do |subscription| - subscriptions[subscription] ||= [] - subscriptions[subscription].push(client_name) - end - end - - # searching by subscription - subscriptions.each do |k, v| - autocomplete.push({:value => v.uniq, :type => 'subscription', :name => k}) - end - - body autocomplete.to_json - else - status 404 - body '{"error":"could not retrieve clients from the sensu api"}' + # searching by subscription + subscriptions.each do |k, v| + autocomplete.push({:value => v.uniq, :type => 'subscription', :name => k}) end - end - end - aget '/client/:id.json' do |id| - begin - http = EventMachine::HttpRequest.new("#{api_server}/client/#{id}").get - rescue => e - puts e + body autocomplete.to_json + else status 404 - body '{"error":"could not retrieve client from the sensu api"}' + body '{"error":"could not retrieve clients from the sensu api"}' end + end + end - http.errback do - status 404 - body '{"error":"could not retrieve client from the sensu api"}' - end + # + # API Proxy + # - http.callback do - status http.response_header.status - body http.response - end + aget '/events.json' do + begin + http = EM::HttpRequest.new($api_server + '/events').get + rescue => e + $logger.warning(e) + status 404 + body '{"error":"could not retrieve events from the sensu api"}' end - adelete '/client/:id.json' do |id| - begin - http = EventMachine::HttpRequest.new("#{api_server}/client/#{id}").delete - rescue => e - puts e - status 404 - body '{"error":"could not delete client from the sensu api"}' - end + http.errback do + status 404 + body '{"error":"could not retrieve events from the sensu api"}' + end - http.errback do - status 404 - body '{"error":"could not delete client from the sensu api"}' - end + http.callback do + status http.response_header.status + body http.response + end + end - http.callback do - status http.response_header.status - body http.response - end + aget '/clients.json' do + begin + http = EM::HttpRequest.new($api_server + '/clients').get + rescue => e + $logger.warning(e) + status 404 + body '{"error":"could not retrieve clients from the sensu api"}' end - aget '/stash/*.json' do |path| - begin - http = EventMachine::HttpRequest.new("#{api_server}/stash/#{path}").get - rescue => e - puts e - status 404 - body '{"error":"could not retrieve a stash from the sensu api"}' - end + http.errback do + status 404 + body '{"error":"could not retrieve clients from the sensu api"}' + end - http.errback do - status 404 - body '{"error":"could not retrieve a stash from the sensu api"}' - end + http.callback do + status http.response_header.status + body http.response + end + end - http.callback do - status http.response_header.status - body http.response - end + aget '/client/:id.json' do |id| + begin + http = EM::HttpRequest.new($api_server + '/client/' + id).get + rescue => e + $logger.warning(e) + status 404 + body '{"error":"could not retrieve client from the sensu api"}' end - apost '/stash/*.json' do |path| - begin - request_options = { - :body => {'timestamp' => Time.now.to_i}.to_json, - :head => { - 'content-type' => 'application/json' - } - } - http = EventMachine::HttpRequest.new("#{api_server}/stash/#{path}").post request_options - rescue => e - puts e - status 404 - body '{"error":"could not create a stash with the sensu api"}' - end + http.errback do + status 404 + body '{"error":"could not retrieve client from the sensu api"}' + end - http.errback do - status 404 - body '{"error":"could not create a stash with the sensu api"}' - end + http.callback do + status http.response_header.status + body http.response + end + end - http.callback do - status http.response_header.status - body http.response - end + adelete '/client/:id.json' do |id| + begin + http = EventMachine::HttpRequest.new($api_server + '/client/' + id).delete + rescue => e + $logger.warning(e) + status 404 + body '{"error":"could not delete client from the sensu api"}' end - adelete '/stash/*.json' do |path| - begin - http = EventMachine::HttpRequest.new("#{api_server}/stash/#{path}").delete - rescue => e - puts e - status 404 - body '{"error":"could not delete a stash with the sensu api"}' - end + http.errback do + status 404 + body '{"error":"could not delete client from the sensu api"}' + end - http.errback do - status 404 - body '{"error":"could not delete a stash with the sensu api"}' - end + http.callback do + status http.response_header.status + body http.response + end + end - http.callback do - status http.response_header.status - body http.response - end + aget '/stash/*.json' do |path| + begin + http = EM::HttpRequest.new($api_server + '/stash/' + path).get + rescue => e + $logger.warning(e) + status 404 + body '{"error":"could not retrieve a stash from the sensu api"}' end - apost '/event/resolve.json' do - begin - request_options = { - :body => request.body.read, - :head => { - 'content-type' => 'application/json' - } + http.errback do + status 404 + body '{"error":"could not retrieve a stash from the sensu api"}' + end + + http.callback do + status http.response_header.status + body http.response + end + end + + apost '/stash/*.json' do |path| + begin + request_options = { + :body => {'timestamp' => Time.now.to_i}.to_json, + :head => { + 'content-type' => 'application/json' } - http = EventMachine::HttpRequest.new("#{api_server}/event/resolve").post request_options - rescue => e - puts e - status 404 - body '{"error":"could not resolve an event with the sensu api"}' - end + } + http = EM::HttpRequest.new($api_server + '/stash/' + path).post request_options + rescue => e + $logger.warning(e) + status 404 + body '{"error":"could not create a stash with the sensu api"}' + end - http.errback do - status 404 - body '{"error":"could not resolve an event with the sensu api"}' - end + http.errback do + status 404 + body '{"error":"could not create a stash with the sensu api"}' + end - http.callback do - status http.response_header.status - body http.response - end + http.callback do + status http.response_header.status + body http.response end + end - aget '/stashes.json' do - begin - request_options = { -# :body => request.body.read, - :head => { - 'content-type' => 'application/json' - } - } - http = EventMachine::HttpRequest.new("#{api_server}/stashes").get request_options - rescue => e - puts e - status 404 - body '{"error":"could not retrieve a list of stashes from the sensu api"}' - end + adelete '/stash/*.json' do |path| + begin + http = EM::HttpRequest.new($api_server + '/stash/' + path).delete + rescue => e + $logger.warning(e) + status 404 + body '{"error":"could not delete a stash with the sensu api"}' + end - http.errback do - status 404 - body '{"error":"could not retrieve a list of stashes from the sensu api"}' - end + http.errback do + status 404 + body '{"error":"could not delete a stash with the sensu api"}' + end - http.callback do - resp = http.response - status http.response_header.status - body resp - end + http.callback do + status http.response_header.status + body http.response end + end - apost '/stashes.json' do - begin - request_options = { - :body => request.body.read, - :head => { - 'content-type' => 'application/json' - } + apost '/event/resolve.json' do + begin + request_options = { + :body => request.body.read, + :head => { + 'content-type' => 'application/json' } - http = EventMachine::HttpRequest.new("#{api_server}/stashes").post request_options - rescue => e - puts e - status 404 - body '{\"error\":\"could not retrieve a list of stashes from the sensu api\"}' - end + } + http = EM::HttpRequest.new($api_server + '/event/resolve').post request_options + rescue => e + $logger.warning(e) + status 404 + body '{"error":"could not resolve an event with the sensu api"}' + end - http.errback do - status 404 - body '{\"error\":\"could not retrieve a list of stashes from the sensu api\"}' - end + http.errback do + status 404 + body '{"error":"could not resolve an event with the sensu api"}' + end - http.callback do - resp = http.response - puts resp - status http.response_header.status - body resp - end + http.callback do + status http.response_header.status + body http.response end + end - websocket_connections = Array.new - EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 9000) do |websocket| - websocket.onopen do - websocket_connections.push websocket - puts 'client connected to websocket' - end - websocket.onclose do - websocket_connections.delete websocket - puts 'client disconnected from websocket' - end + aget '/stashes.json' do + begin + http = EM::HttpRequest.new($api_server + '/stashes').get + rescue => e + $logger.warning(e) + status 404 + body '{"error":"could not retrieve a list of stashes from the sensu api"}' end - apost '/events.json' do - unless websocket_connections.empty? - websocket_connections.each do |websocket| - websocket.send '{"update":"true"}' - end - end - body '{"success":"triggered dashboard refresh"}' + http.errback do + status 404 + body '{"error":"could not retrieve a list of stashes from the sensu api"}' end + + http.callback do + status http.response_header.status + body http.response + end end - DashboardServer.run!({:port => SETTINGS['dashboard']['port']}) + apost '/stashes.json' do + begin + request_options = { + :body => request.body.read, + :head => { + 'content-type' => 'application/json' + } + } + http = EM::HttpRequest.new($api_server + '/stashes').post request_options + rescue => e + $logger.warning(e) + status 404 + body '{"error":"could not retrieve a list of stashes from the sensu api"}' + end - # - # Recognize exit command - # - Signal.trap("INT") do - EM.stop + http.errback do + status 404 + body '{"error":"could not retrieve a list of stashes from the sensu api"}' + end + + http.callback do + status http.response_header.status + body http.response + end end - Signal.trap("TERM") do - EM.stop - end + def self.stop(signal) + $logger.warn('[stop] -- stopping sensu dashboard -- ' + signal) + EM::Timer.new(1) do + EM::stop_event_loop + end + end end + +options = Sensu::Config.read_arguments(ARGV) +Dashboard.run(options)