├── log └── .gitkeep ├── .rspec ├── lib ├── tasks │ └── .gitkeep ├── assets │ ├── .gitkeep │ ├── javascripts │ │ ├── mobileinit.js │ │ ├── clients.mobile.js.coffee │ │ ├── stashes.mobile.js.coffee │ │ ├── mobile.js │ │ └── events.mobile.js.coffee │ └── stylesheets │ │ ├── mobile.css.scss │ │ └── jquery.timepicker.css ├── utility.rb ├── templates │ └── haml │ │ └── scaffold │ │ └── _form.html.haml └── resting.rb ├── spec ├── unit │ ├── .gitkeep │ ├── downtime_spec.rb │ ├── user_spec.rb │ ├── downtime_client_spec.rb │ ├── role_spec.rb │ ├── aggregate_spec.rb │ ├── downtime_check_spec.rb │ ├── check_spec.rb │ ├── setting_spec.rb │ ├── client_spec.rb │ ├── api_spec.rb │ ├── stash_spec.rb │ └── event_spec.rb ├── features │ ├── .gitkeep │ ├── logs_features_spec.rb │ ├── aggregates_features_spec.rb │ ├── checks_features_spec.rb │ ├── application_features_spec.rb │ ├── client_features_spec.rb │ ├── stashes_features_spec.rb │ ├── downtimes_features_spec.rb │ └── stats_features_spec.rb ├── support │ ├── fake_sensu_macros.rb │ ├── devise.rb │ ├── shared_db_connection.rb │ ├── controller_macros.rb │ └── api_stubs.rb ├── controllers │ ├── logs_controller_spec.rb │ ├── stats_controller_spec.rb │ ├── aggregates_controller_spec.rb │ ├── stashes_controller_spec.rb │ ├── checks_controller_spec.rb │ ├── clients_controller_spec.rb │ ├── api_controller_spec.rb │ ├── settings_controller_spec.rb │ ├── events_controller_spec.rb │ ├── users_controller_spec.rb │ └── downtimes_controller_spec.rb ├── factories.rb └── spec_helper.rb ├── app ├── mailers │ └── .gitkeep ├── models │ ├── .gitkeep │ ├── role.rb │ ├── downtime_check.rb │ ├── downtime_client.rb │ ├── check.rb │ ├── api.rb │ ├── aggregate.rb │ ├── user.rb │ ├── client.rb │ ├── log.rb │ ├── ability.rb │ ├── setting.rb │ ├── stash.rb │ ├── event.rb │ └── downtime.rb ├── views │ ├── events │ │ ├── _output.html.haml │ │ ├── _issued.html.haml │ │ ├── index.html.haml │ │ ├── _status.html.haml │ │ ├── _table.html.haml │ │ ├── _modal_silence.html.haml │ │ ├── index.mobile.haml │ │ ├── _actions.html.haml │ │ ├── _modal_data.html.haml │ │ ├── _modals.html.haml │ │ └── _mobile_event.mobile.haml │ ├── api │ │ └── api │ │ │ ├── _time.html.haml │ │ │ ├── setup.html.haml │ │ │ └── _apistatus.html.haml │ ├── downtimes │ │ ├── edit.html.haml │ │ ├── new.html.haml │ │ └── old_downtimes.html.haml │ ├── users │ │ ├── show.html.haml │ │ ├── mailer │ │ │ ├── confirmation_instructions.html.erb │ │ │ ├── unlock_instructions.html.erb │ │ │ └── reset_password_instructions.html.erb │ │ ├── new.html.haml │ │ ├── unlocks │ │ │ └── new.html.erb │ │ ├── passwords │ │ │ ├── new.html.erb │ │ │ └── edit.html.erb │ │ ├── confirmations │ │ │ └── new.html.erb │ │ ├── sessions │ │ │ └── new.html.erb │ │ ├── registrations │ │ │ ├── new.html.erb │ │ │ └── edit.html.erb │ │ ├── edit.html.haml │ │ ├── shared │ │ │ └── _links.erb │ │ └── index.html.haml │ ├── layouts │ │ ├── _messages.html.haml │ │ ├── _navigation.mobile.haml │ │ ├── _navigation.html.haml │ │ ├── application.mobile.haml │ │ └── application.html.haml │ ├── checks │ │ ├── index.html.haml │ │ ├── _configure_server.html.haml │ │ └── _checks.html.haml │ ├── clients │ │ ├── _modal.html.haml │ │ ├── index.html.haml │ │ └── index.mobile.haml │ ├── devise │ │ ├── mailer │ │ │ ├── confirmation_instructions.html.erb │ │ │ ├── unlock_instructions.html.erb │ │ │ └── reset_password_instructions.html.erb │ │ ├── unlocks │ │ │ └── new.html.erb │ │ ├── passwords │ │ │ ├── new.html.erb │ │ │ └── edit.html.erb │ │ ├── confirmations │ │ │ └── new.html.erb │ │ ├── sessions │ │ │ ├── new.mobile.erb │ │ │ └── new.html.erb │ │ ├── registrations │ │ │ ├── new.html.erb │ │ │ └── edit.html.erb │ │ └── shared │ │ │ └── _links.erb │ ├── settings │ │ ├── missing.html.haml │ │ └── index.html.haml │ ├── stashes │ │ ├── _stash_row_legacy.html.haml │ │ ├── _stash_row.html.haml │ │ ├── index.mobile.haml │ │ ├── _stash_row.mobile.haml │ │ └── index.html.haml │ ├── logs │ │ └── index.html.haml │ ├── stats │ │ └── index.html.haml │ └── aggregates │ │ └── index.html.haml ├── helpers │ ├── api_helper.rb │ ├── logs_helper.rb │ ├── stats_helper.rb │ ├── checks_helper.rb │ ├── stashes_helper.rb │ ├── aggregates_helper.rb │ ├── downtimes_helper.rb │ ├── settings_helper.rb │ ├── clients_helper.rb │ ├── application_helper.rb │ └── events_helper.rb ├── assets │ ├── images │ │ ├── rails.png │ │ ├── sort_asc.png │ │ ├── sort_both.png │ │ ├── sort_desc.png │ │ ├── images │ │ │ ├── ajax-loader.gif │ │ │ ├── icons-18-black.png │ │ │ ├── icons-18-white.png │ │ │ ├── icons-36-black.png │ │ │ └── icons-36-white.png │ │ ├── sort_asc_disabled.png │ │ └── sort_desc_disabled.png │ ├── stylesheets │ │ ├── api.css.scss │ │ ├── logs.css.scss │ │ ├── checks.css.scss │ │ ├── stats.css.scss │ │ ├── clients.css.scss │ │ ├── downtimes.css.scss │ │ ├── settings.css.scss │ │ ├── stashes.css.scss │ │ ├── aggregates.css.scss │ │ ├── events.css.scss │ │ ├── application.css.scss │ │ └── bootstrap_and_overrides.css.scss │ └── javascripts │ │ ├── settings.js.coffee │ │ ├── stats.js.coffee │ │ ├── logs.js.coffee │ │ ├── aggregates.js.coffee │ │ ├── checks.js.coffee │ │ ├── application.js │ │ ├── api.js.coffee │ │ ├── downtimes.js.coffee │ │ ├── clients.js.coffee │ │ └── stashes.js.coffee └── controllers │ ├── aggregates_controller.rb │ ├── logs_controller.rb │ ├── checks_controller.rb │ ├── api_controller.rb │ ├── stats_controller.rb │ ├── clients_controller.rb │ ├── settings_controller.rb │ ├── application_controller.rb │ ├── stashes_controller.rb │ ├── users_controller.rb │ └── downtimes_controller.rb ├── vendor └── assets │ ├── javascripts │ └── .gitkeep │ └── stylesheets │ └── .gitkeep ├── public ├── favicon.ico ├── favicon_114.png ├── favicon_144.png ├── favicon_57.png ├── favicon_64.png ├── favicon_72.png ├── robots.txt ├── 500.html ├── 422.html └── 404.html ├── config ├── initializers │ ├── load_utility.rb │ ├── redirect_mobile.rb │ ├── required_settings.rb │ ├── mime_types.rb │ ├── rolify.rb │ ├── backtrace_silencers.rb │ ├── session_store.rb │ ├── wrap_parameters.rb │ └── inflections.rb ├── environment.rb ├── boot.rb ├── schedule.rb ├── locales │ ├── en.yml │ └── devise.en.yml ├── database.yml ├── environments │ ├── test.rb │ ├── development.rb │ └── production.rb └── application.rb ├── config.ru ├── db ├── migrate │ ├── 20120819215920_add_deleted_at_to_user.rb │ ├── 20120807181524_create_users.rb │ ├── 20121105221805_create_settings.rb │ ├── 20120814222508_create_downtime_checks.rb │ ├── 20120814222404_create_downtime_clients.rb │ ├── 20120809204302_create_logs.rb │ ├── 20120814212846_create_downtimes.rb │ ├── 20120819231659_rolify_create_roles.rb │ └── 20120807181910_add_devise_to_users.rb ├── seeds.rb └── schema.rb ├── doc └── README_FOR_APP ├── Rakefile ├── script └── rails ├── .gitignore ├── MIT-LICENSE.txt ├── Gemfile └── README.rdoc /log/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/unit/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/mailers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/features/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/events/_output.html.haml: -------------------------------------------------------------------------------- 1 | = event.output 2 | -------------------------------------------------------------------------------- /app/helpers/api_helper.rb: -------------------------------------------------------------------------------- 1 | module ApiHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/logs_helper.rb: -------------------------------------------------------------------------------- 1 | module LogsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/stats_helper.rb: -------------------------------------------------------------------------------- 1 | module StatsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/checks_helper.rb: -------------------------------------------------------------------------------- 1 | module ChecksHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/stashes_helper.rb: -------------------------------------------------------------------------------- 1 | module StashesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/aggregates_helper.rb: -------------------------------------------------------------------------------- 1 | module AggregatesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/downtimes_helper.rb: -------------------------------------------------------------------------------- 1 | module DowntimesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/settings_helper.rb: -------------------------------------------------------------------------------- 1 | module SettingsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/views/api/api/_time.html.haml: -------------------------------------------------------------------------------- 1 | %div 2 | %p== Server Time: #{Time.zone.now} 3 | -------------------------------------------------------------------------------- /app/views/downtimes/edit.html.haml: -------------------------------------------------------------------------------- 1 | %h2 Edit Downtime 2 | 3 | = render 'form' 4 | -------------------------------------------------------------------------------- /app/views/events/_issued.html.haml: -------------------------------------------------------------------------------- 1 | = time_ago_in_words(Time.at(event.issued)) 2 | -------------------------------------------------------------------------------- /app/views/downtimes/new.html.haml: -------------------------------------------------------------------------------- 1 | %h1 Schedule Downtime 2 | 3 | = render 'form' 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon_114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/public/favicon_114.png -------------------------------------------------------------------------------- /public/favicon_144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/public/favicon_144.png -------------------------------------------------------------------------------- /public/favicon_57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/public/favicon_57.png -------------------------------------------------------------------------------- /public/favicon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/public/favicon_64.png -------------------------------------------------------------------------------- /public/favicon_72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/public/favicon_72.png -------------------------------------------------------------------------------- /app/assets/images/rails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/app/assets/images/rails.png -------------------------------------------------------------------------------- /config/initializers/load_utility.rb: -------------------------------------------------------------------------------- 1 | require 'utility' 2 | require 'resting' 3 | include UtilityMethods 4 | -------------------------------------------------------------------------------- /app/assets/images/sort_asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/app/assets/images/sort_asc.png -------------------------------------------------------------------------------- /app/assets/images/sort_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/app/assets/images/sort_both.png -------------------------------------------------------------------------------- /app/assets/images/sort_desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/app/assets/images/sort_desc.png -------------------------------------------------------------------------------- /config/initializers/redirect_mobile.rb: -------------------------------------------------------------------------------- 1 | ActionController::Responder.class_eval do 2 | alias :to_mobile :to_html 3 | end 4 | -------------------------------------------------------------------------------- /app/assets/images/images/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/app/assets/images/images/ajax-loader.gif -------------------------------------------------------------------------------- /app/assets/images/sort_asc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/app/assets/images/sort_asc_disabled.png -------------------------------------------------------------------------------- /app/assets/images/sort_desc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/app/assets/images/sort_desc_disabled.png -------------------------------------------------------------------------------- /app/assets/images/images/icons-18-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/app/assets/images/images/icons-18-black.png -------------------------------------------------------------------------------- /app/assets/images/images/icons-18-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/app/assets/images/images/icons-18-white.png -------------------------------------------------------------------------------- /app/assets/images/images/icons-36-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/app/assets/images/images/icons-36-black.png -------------------------------------------------------------------------------- /app/assets/images/images/icons-36-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sensu/sensu-admin/HEAD/app/assets/images/images/icons-36-white.png -------------------------------------------------------------------------------- /app/views/users/show.html.haml: -------------------------------------------------------------------------------- 1 | %h2== User #{@user.email} 2 | 3 | %p== Created At: #{@user.created_at} 4 | %p== Admin?: #{@user.admin?} 5 | -------------------------------------------------------------------------------- /spec/unit/downtime_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Downtime do 4 | 5 | it "should process downtimes" do 6 | end 7 | 8 | end 9 | -------------------------------------------------------------------------------- /app/views/events/index.html.haml: -------------------------------------------------------------------------------- 1 | %div.alert.alert-info.fade.in{:id => "updating_event_list"} 2 | %p Updating event list... 3 | 4 | = render "table" 5 | -------------------------------------------------------------------------------- /spec/unit/user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /app/controllers/aggregates_controller.rb: -------------------------------------------------------------------------------- 1 | class AggregatesController < ApplicationController 2 | def index 3 | @aggregates = Aggregate.full_hash 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /config/initializers/required_settings.rb: -------------------------------------------------------------------------------- 1 | REQUIRED_SETTINGS = ["use_environments", 2 | "api_server", 3 | "configure_server"] 4 | -------------------------------------------------------------------------------- /app/models/role.rb: -------------------------------------------------------------------------------- 1 | class Role < ActiveRecord::Base 2 | has_and_belongs_to_many :users, :join_table => :users_roles 3 | belongs_to :resource, :polymorphic => true 4 | end 5 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run SensuAdmin::Application 5 | -------------------------------------------------------------------------------- /db/migrate/20120819215920_add_deleted_at_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddDeletedAtToUser < ActiveRecord::Migration 2 | def change 3 | add_column :users, :deleted_at, :timestamp 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/unit/downtime_client_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | class DowntimeClientTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the rails application 5 | SensuAdmin::Application.initialize! 6 | -------------------------------------------------------------------------------- /db/migrate/20120807181524_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration 2 | def change 3 | create_table :users do |t| 4 | 5 | t.timestamps 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/assets/javascripts/mobileinit.js: -------------------------------------------------------------------------------- 1 | $(document).bind("mobileinit", function() { 2 | $.mobile.page.prototype.options.addBackBtn = true; 3 | $.mobile.page.prototype.options.backBtnTheme = 'a'; 4 | }); 5 | -------------------------------------------------------------------------------- /lib/utility.rb: -------------------------------------------------------------------------------- 1 | module UtilityMethods 2 | def increment(hash, value) 3 | if hash[value].nil? 4 | hash[value] = 1 5 | else 6 | hash[value] += 1 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/logs_controller.rb: -------------------------------------------------------------------------------- 1 | class LogsController < ApplicationController 2 | def index 3 | @logs = Log.paginate(:page => params[:page], :per_page => 20).order('created_at DESC') 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/unit/role_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Role do 4 | 5 | it "has a valid factory" do 6 | role = FactoryGirl.create(:role) 7 | role.should be_valid 8 | end 9 | 10 | end 11 | -------------------------------------------------------------------------------- /app/assets/stylesheets/api.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the api controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/logs.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the logs controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/checks.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the checks controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/stats.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Stats controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/clients.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the clients controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/downtimes.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the downtimes controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/settings.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Settings controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/stashes.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the stashes controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/aggregates.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the aggregates controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | # Set up gems listed in the Gemfile. 4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 5 | 6 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) 7 | 8 | -------------------------------------------------------------------------------- /doc/README_FOR_APP: -------------------------------------------------------------------------------- 1 | Use this README file to introduce your application and point to useful places in the API for learning more. 2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /spec/unit/aggregate_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Aggregate do 4 | 5 | it "should return a hash of all aggregates" do 6 | aggregates = Aggregate.all 7 | aggregates.should be_false 8 | end 9 | 10 | end 11 | -------------------------------------------------------------------------------- /app/models/downtime_check.rb: -------------------------------------------------------------------------------- 1 | class DowntimeCheck < ActiveRecord::Base 2 | attr_accessible :downtime_id, :name 3 | belongs_to :downtime 4 | 5 | validates_presence_of :name 6 | validates_uniqueness_of :name, :scope => :downtime_id 7 | end 8 | -------------------------------------------------------------------------------- /app/models/downtime_client.rb: -------------------------------------------------------------------------------- 1 | class DowntimeClient < ActiveRecord::Base 2 | attr_accessible :downtime_id, :name 3 | belongs_to :downtime 4 | 5 | validates_presence_of :name 6 | validates_uniqueness_of :name, :scope => :downtime_id 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20121105221805_create_settings.rb: -------------------------------------------------------------------------------- 1 | class CreateSettings < ActiveRecord::Migration 2 | def change 3 | create_table :settings do |t| 4 | t.string :name 5 | t.string :value 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/support/fake_sensu_macros.rb: -------------------------------------------------------------------------------- 1 | module FakeSensuMacros 2 | 3 | def reset_fake_sensu! 4 | api_setting = Setting.find_by_name("api_server") 5 | api_path = api_setting.value 6 | RestClient.get("#{api_path}/reset") 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /spec/unit/downtime_check_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe DowntimeCheck do 4 | 5 | it "has a valid factory" do 6 | downtime_check = FactoryGirl.create(:downtime_check) 7 | downtime_check.should be_valid 8 | end 9 | 10 | end 11 | -------------------------------------------------------------------------------- /app/helpers/clients_helper.rb: -------------------------------------------------------------------------------- 1 | module ClientsHelper 2 | def format_subscriptions(subscriptions) 3 | return "N/A" if subscriptions.blank? || subscriptions.nil? 4 | subs = subscriptions.join(",") 5 | subs.length > 50 ? "#{subs[0..50]}..." : subs 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/views/layouts/_messages.html.haml: -------------------------------------------------------------------------------- 1 | - flash.each do |name, msg| 2 | %div{:class => "alert alert-#{name == :notice ? "success" : "error"}"} 3 | %a.close{"data-dismiss" => "alert"} × 4 | = content_tag :div, msg, :id => "flash_#{name}" if msg.is_a?(String) 5 | -------------------------------------------------------------------------------- /app/assets/javascripts/settings.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/stats.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/views/checks/index.html.haml: -------------------------------------------------------------------------------- 1 | :css 2 | .modal { 3 | width: 80%; 4 | height: 30%; 5 | left: 8%; 6 | top: 15%; 7 | } 8 | 9 | 10 | - if configure_server? 11 | = render :partial => "configure_server" 12 | - else 13 | = render :partial => "checks" 14 | -------------------------------------------------------------------------------- /app/views/clients/_modal.html.haml: -------------------------------------------------------------------------------- 1 | %div.modal-header 2 | %h3== Client: #{client.name} 3 | %div.modal-body 4 | %table.table.table-striped.table-bordered 5 | %tbody 6 | - client.attributes.each do |k,v| 7 | %tr 8 | %td= k 9 | %td= v 10 | -------------------------------------------------------------------------------- /lib/templates/haml/scaffold/_form.html.haml: -------------------------------------------------------------------------------- 1 | = semantic_form_for @<%= singular_name %> do |f| 2 | = f.inputs do 3 | <%- attributes.each do |attribute| -%> 4 | = f.input :<%= attribute.name %> 5 | <%- end -%> 6 | 7 | = f.actions do 8 | = f.action :submit, :as => :input 9 | -------------------------------------------------------------------------------- /spec/unit/check_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Check do 4 | 5 | it "should submit a check" do 6 | check = Check.all.first 7 | check_name = check.name 8 | subscribers = check.subscribers 9 | reset_fake_sensu! 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20120814222508_create_downtime_checks.rb: -------------------------------------------------------------------------------- 1 | class CreateDowntimeChecks < ActiveRecord::Migration 2 | def change 3 | create_table :downtime_checks do |t| 4 | t.string :name 5 | t.integer :downtime_id 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20120814222404_create_downtime_clients.rb: -------------------------------------------------------------------------------- 1 | class CreateDowntimeClients < ActiveRecord::Migration 2 | def change 3 | create_table :downtime_clients do |t| 4 | t.string :name 5 | t.integer :downtime_id 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/controllers/logs_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe LogsController do 4 | login_user 5 | 6 | describe "GET 'index'" do 7 | it "returns http success" do 8 | get 'index' 9 | response.code.should eq "200" 10 | end 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /spec/controllers/stats_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe StatsController do 4 | login_user 5 | 6 | describe "GET 'index'" do 7 | it "returns http success" do 8 | get 'index' 9 | response.code.should eq "200" 10 | end 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require File.expand_path('../config/application', __FILE__) 6 | 7 | SensuAdmin::Application.load_tasks 8 | -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Welcome <%= @resource.email %>!

2 | 3 |

You can confirm your account email through the link below:

4 | 5 |

<%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>

6 | -------------------------------------------------------------------------------- /app/views/users/mailer/confirmation_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Welcome <%= @resource.email %>!

2 | 3 |

You can confirm your account email through the link below:

4 | 5 |

<%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>

6 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | Mime::Type.register_alias "text/html", :mobile 7 | -------------------------------------------------------------------------------- /app/views/settings/missing.html.haml: -------------------------------------------------------------------------------- 1 | %div.alert.alert-error 2 | %p Your missing setting(s) needed to run sensu-admin, please run (or have your admin run) rake db:seed, Your missing the following variables: 3 | - REQUIRED_SETTINGS.each do |setting| 4 | - unless Setting.find_by_name(setting) 5 | %p= setting 6 | -------------------------------------------------------------------------------- /spec/controllers/aggregates_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe AggregatesController do 4 | login_user 5 | 6 | describe "GET 'index'" do 7 | it "returns http success" do 8 | get 'index' 9 | response.code.should eq "200" 10 | end 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /app/models/check.rb: -------------------------------------------------------------------------------- 1 | class Check < Resting 2 | 3 | def self.submit_check(check, subscribers) 4 | poster = post("check/request", {:check => check, :subscribers => subscribers }) 5 | poster.code == 201 6 | end 7 | 8 | def method_missing(method, *args, &block) 9 | "None" 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/views/events/_status.html.haml: -------------------------------------------------------------------------------- 1 | - if event.status == 1 2 | %span.badge.badge-warning 3 | = format_status(event.status) 4 | - elsif event.status == 2 5 | %span.badge.badge-important 6 | = format_status(event.status) 7 | - else 8 | %span.badge.badge-inverse 9 | = format_status(event.status) 10 | -------------------------------------------------------------------------------- /script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /spec/controllers/stashes_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe StashesController do 4 | login_user 5 | 6 | describe "GET 'index'" do 7 | it "returns http success" do 8 | # todo 9 | # get 'index' 10 | # response.code.should eq "200" 11 | end 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /app/views/checks/_configure_server.html.haml: -------------------------------------------------------------------------------- 1 | = @server_json["checks"].each do |k,v| 2 | %p== Check #{k} 3 | = form_tag :checks, :url => {:controller => :checks, :action => :update}, :remote => true, :method => :put do 4 | = v.each do |attr,val| 5 | %p= attr 6 | = text_field_tag attr, val 7 | = submit_tag "Submit" 8 | -------------------------------------------------------------------------------- /app/views/users/new.html.haml: -------------------------------------------------------------------------------- 1 | %br 2 | %h3 New User: 3 | = semantic_form_for @user do |form| 4 | = form.inputs do 5 | = form.input :email 6 | = form.input :password, :as => :password 7 | = form.input :password_confirmation, :as => :password 8 | = form.input :roles, :as => :check_boxes, :collection => Role.all 9 | = form.actions 10 | -------------------------------------------------------------------------------- /app/models/api.rb: -------------------------------------------------------------------------------- 1 | class Api < Resting 2 | 3 | def self.status 4 | get("info") 5 | end 6 | 7 | def self.version 8 | self.status.sensu['version'] 9 | end 10 | 11 | def self.redis_health 12 | self.status.redis['connected'] 13 | end 14 | 15 | def self.rabbitmq_health 16 | self.status.rabbitmq['connected'] 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/features/logs_features_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Logs" do 4 | 5 | before :each do 6 | user = FactoryGirl.create(:user) 7 | user.add_role :admin 8 | sign_in_user(user) 9 | visit '/logs' 10 | end 11 | 12 | it "should show the logs page" do 13 | page.should have_content "Logs" 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /app/assets/javascripts/logs.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | $ -> 5 | $('td.moreinfo').click -> 6 | $($(this).closest('tr').attr("rel")).modal("show") 7 | -------------------------------------------------------------------------------- /app/views/devise/mailer/unlock_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Your account has been locked due to an excessive amount of unsuccessful sign in attempts.

4 | 5 |

Click the link below to unlock your account:

6 | 7 |

<%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %>

8 | -------------------------------------------------------------------------------- /app/views/users/mailer/unlock_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Your account has been locked due to an excessive amount of unsuccessful sign in attempts.

4 | 5 |

Click the link below to unlock your account:

6 | 7 |

<%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %>

8 | -------------------------------------------------------------------------------- /config/initializers/rolify.rb: -------------------------------------------------------------------------------- 1 | Rolify.configure do |config| 2 | # By default ORM adapter is ActiveRecord. uncomment to use mongoid 3 | # config.use_mongoid 4 | 5 | # Dynamic shortcuts for User class (user.is_admin? like methods). Default is: false 6 | # Enable this feature _after_ running rake db:migrate as it relies on the roles table 7 | # config.use_dynamic_shortcuts 8 | end -------------------------------------------------------------------------------- /spec/features/aggregates_features_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Aggregates" do 4 | 5 | before :each do 6 | user = FactoryGirl.create(:user) 7 | user.add_role :admin 8 | sign_in_user(user) 9 | visit '/aggregates' 10 | end 11 | 12 | it "should show the aggregates page" do 13 | page.should have_content "Aggregates" 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /app/assets/javascripts/aggregates.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | $ -> 5 | $('a.modal-for-aggregate-display').click -> 6 | $('#aggregate_modal_' + $(this).attr("misc")).modal("show") 7 | -------------------------------------------------------------------------------- /config/schedule.rb: -------------------------------------------------------------------------------- 1 | # Use this file to easily define all of your cron jobs. 2 | # 3 | # It's helpful, but not entirely necessary to understand cron before proceeding. 4 | # http://en.wikipedia.org/wiki/Cron 5 | 6 | every 5.minutes do 7 | runner "Downtime.process_downtimes" 8 | end 9 | 10 | every 20.minutes do 11 | runner "Stash.clear_expired_stashes" 12 | end 13 | 14 | # Learn more: http://github.com/javan/whenever 15 | -------------------------------------------------------------------------------- /spec/support/devise.rb: -------------------------------------------------------------------------------- 1 | module DeviseMacros 2 | def sign_in_user(user) 3 | visit '/users/sign_in' 4 | fill_in "Email", :with => user.email 5 | fill_in "Password", :with => user.password 6 | click_button "Sign in" 7 | end 8 | 9 | def set_api 10 | visit '/api/setup' 11 | fill_in "Sensu API Server url", :with => "http://foo:bar@33.33.33.11:4567" 12 | click_link_or_button "save_api_server" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/support/shared_db_connection.rb: -------------------------------------------------------------------------------- 1 | class ActiveRecord::Base 2 | mattr_accessor :shared_connection 3 | @@shared_connection = nil 4 | 5 | def self.connection 6 | @@shared_connection || retrieve_connection 7 | end 8 | end 9 | 10 | # Forces all threads to share the same connection. This works on 11 | # Capybara because it starts the web server in a thread. 12 | ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection 13 | -------------------------------------------------------------------------------- /app/views/devise/unlocks/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend unlock instructions

2 | 3 | <%= form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
<%= f.label :email %>
7 | <%= f.email_field :email %>
8 | 9 |
<%= f.submit "Resend unlock instructions" %>
10 | <% end %> 11 | 12 | <%= render "devise/shared/links" %> 13 | -------------------------------------------------------------------------------- /app/views/users/unlocks/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend unlock instructions

2 | 3 | <%= form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
<%= f.label :email %>
7 | <%= f.email_field :email %>
8 | 9 |
<%= f.submit "Resend unlock instructions" %>
10 | <% end %> 11 | 12 | <%= render "devise/shared/links" %> 13 | -------------------------------------------------------------------------------- /db/migrate/20120809204302_create_logs.rb: -------------------------------------------------------------------------------- 1 | class CreateLogs < ActiveRecord::Migration 2 | def change 3 | create_table :logs do |t| 4 | t.string :client 5 | t.string :check 6 | t.string :silence_type 7 | t.string :action_type 8 | t.string :environment 9 | t.text :output 10 | t.text :reason 11 | t.string :status 12 | t.integer :user_id 13 | 14 | t.timestamps 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20120814212846_create_downtimes.rb: -------------------------------------------------------------------------------- 1 | class CreateDowntimes < ActiveRecord::Migration 2 | def change 3 | create_table :downtimes do |t| 4 | t.string :name 5 | t.text :description 6 | t.datetime :start_time 7 | t.datetime :stop_time 8 | t.integer :user_id 9 | t.boolean :processed, :default => false 10 | t.boolean :completed, :default => false 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html.erb: -------------------------------------------------------------------------------- 1 |

Forgot your password?

2 | 3 | <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
<%= f.label :email %>
7 | <%= f.email_field :email %>
8 | 9 |
<%= f.submit "Send me reset password instructions" %>
10 | <% end %> 11 | 12 | <%= render "devise/shared/links" %> 13 | -------------------------------------------------------------------------------- /app/views/users/passwords/new.html.erb: -------------------------------------------------------------------------------- 1 |

Forgot your password?

2 | 3 | <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
<%= f.label :email %>
7 | <%= f.email_field :email %>
8 | 9 |
<%= f.submit "Send me reset password instructions" %>
10 | <% end %> 11 | 12 | <%= render "devise/shared/links" %> 13 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /app/views/devise/confirmations/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend confirmation instructions

2 | 3 | <%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
<%= f.label :email %>
7 | <%= f.email_field :email %>
8 | 9 |
<%= f.submit "Resend confirmation instructions" %>
10 | <% end %> 11 | 12 | <%= render "devise/shared/links" %> 13 | -------------------------------------------------------------------------------- /app/views/users/confirmations/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend confirmation instructions

2 | 3 | <%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
<%= f.label :email %>
7 | <%= f.email_field :email %>
8 | 9 |
<%= f.submit "Resend confirmation instructions" %>
10 | <% end %> 11 | 12 | <%= render "devise/shared/links" %> 13 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | SensuAdmin::Application.config.session_store :cookie_store, :key => '_sensu-admin_session' 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rails generate session_migration") 8 | # SensuAdmin::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /app/views/layouts/_navigation.mobile.haml: -------------------------------------------------------------------------------- 1 | %div{'data-role' => 'navbar'} 2 | %ul 3 | - unless params[:controller] == "events" 4 | %li 5 | %a{:href => "/events?mobile=1", 'data-theme' => 'a'} Events 6 | - unless params[:controller] == "stashes" 7 | %li 8 | %a{:href => "/stashes?mobile=1", 'data-theme' => 'a'} Stashes 9 | - unless params[:controller] == "clients" 10 | %li 11 | %a{:href => "/clients?mobile=1", 'data-theme' => 'a'} Clients 12 | -------------------------------------------------------------------------------- /app/views/stashes/_stash_row_legacy.html.haml: -------------------------------------------------------------------------------- 1 | %tr{:id => "stash-#{i}"} 2 | %td.span5= k 3 | %td.span4= v['description'] unless v['description'].nil? 4 | %td= v['owner'] 5 | - if v['expire_at'] 6 | %td= distance_of_time_in_words(Time.now, Time.parse(v['expire_at'])) 7 | - else 8 | %td Never 9 | %td= time_ago_in_words(Time.at(v['timestamp'].to_i)) + " ago" || '' 10 | %td 11 | %a.delete-stash{:key => k, :misc => "#stash-#{i}", :rel => "/stashes/delete_stash"} Delete 12 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Someone has requested a link to change your password, and you can do this through the link below.

4 | 5 |

<%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %>

6 | 7 |

If you didn't request this, please ignore this email.

8 |

Your password won't change until you access the link above and create a new one.

9 | -------------------------------------------------------------------------------- /app/views/users/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Someone has requested a link to change your password, and you can do this through the link below.

4 | 5 |

<%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %>

6 | 7 |

If you didn't request this, please ignore this email.

8 |

Your password won't change until you access the link above and create a new one.

9 | -------------------------------------------------------------------------------- /spec/unit/setting_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Setting do 4 | 5 | it "should return the api server" do 6 | api_server = Setting.api_server 7 | api_server.should_not be_nil 8 | api_server.should_not be_false 9 | Setting.api_server.should_not be_nil 10 | end 11 | 12 | it "should return true if environments are in use" do 13 | env_setting = Setting.find_by_name("use_environments") 14 | Setting.use_environments?.should be_false 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/checks_controller.rb: -------------------------------------------------------------------------------- 1 | class ChecksController < ApplicationController 2 | def index 3 | @checks = Check.all 4 | @server_json = JSON.parse(File.open("config/config.json").read) if File.exists?("config/config.json") 5 | end 6 | 7 | def submit_check 8 | resp = Check.submit_check(params[:check], params[:subscribers].split(",")) 9 | respond_to do |format| 10 | format.json { render :json => resp.to_s } 11 | end 12 | end 13 | 14 | def update 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/controllers/checks_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ChecksController do 4 | login_user 5 | 6 | describe "GET 'index'" do 7 | it "returns http success" do 8 | get 'index' 9 | response.code.should eq "200" 10 | end 11 | end 12 | 13 | describe "GET 'submit_check'" do 14 | it "returns http success" do 15 | # todo 16 | # get 'submit_check', {:check => 1} 17 | # response.code.should eq "200" 18 | end 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /spec/controllers/clients_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ClientsController do 4 | login_user 5 | 6 | describe "GET 'index'" do 7 | it "returns http success" do 8 | get 'index' 9 | response.code.should eq "200" 10 | end 11 | end 12 | 13 | describe "DELETE 'destroy'" do 14 | it "returns http success" do 15 | # todo: no template 16 | # delete 'destroy', {:id => 1} 17 | # response.code.should eq "202" 18 | end 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /spec/unit/client_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Client do 4 | 5 | it "should return all clients through cache" do 6 | Client.refresh_cache 7 | clients = Client.all_with_cache 8 | clients.count.should eq 2 9 | end 10 | 11 | it "should return all clients as a hash" do 12 | Client.refresh_cache 13 | clients_hash = Client.full_hash 14 | clients_hash.should be_a Hash 15 | clients_hash.should_not be_empty 16 | clients_hash.count.should eq 2 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /lib/assets/javascripts/clients.mobile.js.coffee: -------------------------------------------------------------------------------- 1 | $ -> 2 | $(document).on 'click', '.delete-client', -> 3 | self = $(this) 4 | $.ajax 5 | type: "DELETE" 6 | url: $(self).attr("rel") 7 | cache: false 8 | data: {} 9 | success: (data) -> 10 | $("#client_delete_" + $(self).attr("index")).toggle() 11 | $("#client_deleted_" + $(self).attr("index")).toggle() 12 | $("#client_" + $(self).attr("index")).toggle() 13 | error: (data) -> 14 | alert "Failed to perform action" 15 | -------------------------------------------------------------------------------- /spec/features/checks_features_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Checks" do 4 | 5 | before :each do 6 | user = FactoryGirl.create(:user) 7 | user.add_role :admin 8 | sign_in_user(user) 9 | visit '/checks' 10 | end 11 | 12 | after :each do 13 | reset_fake_sensu! 14 | end 15 | 16 | it "should show the checks page" do 17 | page.should have_content "Checks" 18 | end 19 | 20 | it "should show a list of checks" do 21 | page.should have_content "test" 22 | end 23 | 24 | end 25 | 26 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters :format => [:json] 9 | end 10 | 11 | # Disable root element in JSON by default. 12 | ActiveSupport.on_load(:active_record) do 13 | self.include_root_in_json = false 14 | end 15 | -------------------------------------------------------------------------------- /spec/support/controller_macros.rb: -------------------------------------------------------------------------------- 1 | module ControllerMacros 2 | def login_admin 3 | before(:each) do 4 | @request.env["devise.mapping"] = Devise.mappings[:admin] 5 | sign_in FactoryGirl.create(:admin) 6 | end 7 | end 8 | 9 | def login_user 10 | before :all do 11 | load "#{Rails.root}/db/seeds.rb" 12 | end 13 | 14 | before :each do 15 | @request.env["devise.mapping"] = Devise.mappings[:user] 16 | user = FactoryGirl.create(:user) 17 | user.add_role :admin 18 | sign_in user 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/features/application_features_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Applicaiton" do 4 | 5 | before :each do 6 | user = FactoryGirl.create(:user) 7 | user.add_role :admin 8 | sign_in_user(user) 9 | end 10 | 11 | pending "should redirect to the settings page if the api does not connect" do 12 | visit '/settings' 13 | fill_in "setting_value", :with => "::55467" 14 | within("#api_url") do 15 | click_on("Update Setting") 16 | end 17 | page.body.should include "Please enter in your api server" 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /app/views/users/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |

Sign in

2 | 3 | <%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %> 4 |
<%= f.label :email %>
5 | <%= f.email_field :email %>
6 | 7 |
<%= f.label :password %>
8 | <%= f.password_field :password %>
9 | 10 | <% if devise_mapping.rememberable? -%> 11 |
<%= f.check_box :remember_me %> <%= f.label :remember_me %>
12 | <% end -%> 13 | 14 |
<%= f.submit "Sign in" %>
15 | <% end %> 16 | 17 | <%= render "devise/shared/links" %> 18 | -------------------------------------------------------------------------------- /lib/assets/javascripts/stashes.mobile.js.coffee: -------------------------------------------------------------------------------- 1 | $ -> 2 | $(document).on 'click', '.delete-stash', -> 3 | self = $(this) 4 | $.ajax 5 | type: "POST" 6 | url: $(self).attr("rel") 7 | cache: false 8 | key: $(self).attr("key") 9 | data: {'key': $(self).attr("key")}, 10 | success: (data) -> 11 | $("#delete_" + $(self).attr("index")).toggle() 12 | $("#deleted_" + $(self).attr("index")).toggle() 13 | $("#stash_" + $(self).attr("index")).toggle() 14 | error: (data) -> 15 | alert "Failed to perform action" 16 | -------------------------------------------------------------------------------- /app/views/devise/sessions/new.mobile.erb: -------------------------------------------------------------------------------- 1 |

Sign in

2 | 3 | <%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %> 4 |
<%= f.label :email %>
5 | <%= f.email_field :email %>
6 | 7 |
<%= f.label :password %>
8 | <%= f.password_field :password %>
9 | 10 | <% if devise_mapping.rememberable? -%> 11 |
<%= f.check_box :remember_me %> <%= f.label :remember_me %>
12 | <% end -%> 13 | 14 |
<%= f.submit "Sign in" %>
15 | <% end %> 16 | 17 | <%= render "devise/shared/links" %> 18 | -------------------------------------------------------------------------------- /db/migrate/20120819231659_rolify_create_roles.rb: -------------------------------------------------------------------------------- 1 | class RolifyCreateRoles < ActiveRecord::Migration 2 | def change 3 | create_table(:roles) do |t| 4 | t.string :name 5 | t.references :resource, :polymorphic => true 6 | 7 | t.timestamps 8 | end 9 | 10 | create_table(:users_roles, :id => false) do |t| 11 | t.references :user 12 | t.references :role 13 | end 14 | 15 | add_index(:roles, :name) 16 | add_index(:roles, [ :name, :resource_type, :resource_id ]) 17 | add_index(:users_roles, [ :user_id, :role_id ]) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/models/aggregate.rb: -------------------------------------------------------------------------------- 1 | class Aggregate < Resting 2 | def self.full_hash 3 | begin 4 | resp_hash = {} 5 | Aggregate.all.each do |agg| 6 | resp_hash[agg.check] = {} 7 | agg.issued.each do |issue| 8 | resp_hash[agg.check][issue] = Aggregate.get("aggregates/#{agg.check}/#{issue}?summarize=output", true) 9 | end 10 | end 11 | resp_hash 12 | rescue NoMethodError 13 | # If API does not support Aggregates, this raises up to the view. We need a better way of handling feature updates. 14 | false 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile ~/.gitignore_global 6 | 7 | # Ignore bundler config 8 | /.bundle 9 | 10 | config.yml 11 | 12 | # Ignore the default SQLite database. 13 | /db/*.sqlite3 14 | 15 | # Ignore all logfiles and tempfiles. 16 | .DS_Store 17 | /log/*.log 18 | /tmp 19 | app/assets/stylesheets/.sass-cache/ 20 | .sass-cache/ 21 | *.swp 22 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new.html.erb: -------------------------------------------------------------------------------- 1 |

Sign up

2 | 3 | <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
<%= f.label :email %>
7 | <%= f.email_field :email %>
8 | 9 |
<%= f.label :password %>
10 | <%= f.password_field :password %>
11 | 12 |
<%= f.label :password_confirmation %>
13 | <%= f.password_field :password_confirmation %>
14 | 15 |
<%= f.submit "Sign up" %>
16 | <% end %> 17 | 18 | <%= render "devise/shared/links" %> 19 | -------------------------------------------------------------------------------- /app/views/users/registrations/new.html.erb: -------------------------------------------------------------------------------- 1 |

Sign up

2 | 3 | <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
<%= f.label :email %>
7 | <%= f.email_field :email %>
8 | 9 |
<%= f.label :password %>
10 | <%= f.password_field :password %>
11 | 12 |
<%= f.label :password_confirmation %>
13 | <%= f.password_field :password_confirmation %>
14 | 15 |
<%= f.submit "Sign up" %>
16 | <% end %> 17 | 18 | <%= render "devise/shared/links" %> 19 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end 11 | # 12 | # These inflection rules are supported but not enabled by default: 13 | # ActiveSupport::Inflector.inflections do |inflect| 14 | # inflect.acronym 'RESTful' 15 | # end 16 | -------------------------------------------------------------------------------- /app/views/devise/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

Sign in

3 | 4 | <%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %> 5 |
<%= f.label :email %>
6 | <%= f.email_field :email %>
7 | 8 |
<%= f.label :password %>
9 | <%= f.password_field :password %>
10 | 11 | <% if devise_mapping.rememberable? -%> 12 |
<%= f.check_box :remember_me %> <%= f.label :remember_me %>
13 | <% end -%> 14 | 15 |
<%= f.submit "Sign in" %>
16 | <% end %> 17 | <%= render "devise/shared/links" %> 18 |
19 | 20 | -------------------------------------------------------------------------------- /app/views/users/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Change your password

2 | 3 | <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | <%= f.hidden_field :reset_password_token %> 6 | 7 |
<%= f.label :password, "New password" %>
8 | <%= f.password_field :password %>
9 | 10 |
<%= f.label :password_confirmation, "Confirm new password" %>
11 | <%= f.password_field :password_confirmation %>
12 | 13 |
<%= f.submit "Change my password" %>
14 | <% end %> 15 | 16 | <%= render "devise/shared/links" %> 17 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Change your password

2 | 3 | <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | <%= f.hidden_field :reset_password_token %> 6 | 7 |
<%= f.label :password, "New password" %>
8 | <%= f.password_field :password %>
9 | 10 |
<%= f.label :password_confirmation, "Confirm new password" %>
11 | <%= f.password_field :password_confirmation %>
12 | 13 |
<%= f.submit "Change my password" %>
14 | <% end %> 15 | 16 | <%= render "devise/shared/links" %> 17 | -------------------------------------------------------------------------------- /app/views/events/_table.html.haml: -------------------------------------------------------------------------------- 1 | %div{:class => "modal large-modal hide", :id => "event-data-modal"} 2 | %div{:style => "display: none;", :id => "use_environments", :rel => "#{use_environments?}"} 3 | %table.span12.dataTable.events_table.table{'data-source' => '/events/events_table', :border => "0", :cellpadding => "0", :cellspacing => "0", :id => "primary_events_table", :style => "width: 100%;"} 4 | %thead 5 | %th 6 | %th.span1 Status 7 | - if use_environments? 8 | %th.span1 ENV 9 | %th.span2 Client 10 | %th.span1 Check 11 | %th.span4 Output 12 | %th.span1 Action 13 | %th.span1 Issued 14 | %th.span1 15 | 16 | %tbody 17 | -------------------------------------------------------------------------------- /lib/assets/stylesheets/mobile.css.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the top of the 9 | * compiled file, but it's generally better to create a new file per style scope. 10 | * 11 | *= require_self 12 | *= require sensu-mobile.min 13 | *= require jquery.mobile.min 14 | */ 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | datetime: 6 | distance_in_words: 7 | less_than_x_minutes: 8 | one: <1min 9 | x_minutes: 10 | one: 1min 11 | other: "%{count}mins" 12 | about_x_hours: 13 | one: 1hr 14 | other: "%{count}hrs" 15 | x_days: 16 | one: 1day 17 | other: "%{count}days" 18 | x_months: 19 | one: 1month 20 | other: "%{count}month(s)" 21 | 22 | -------------------------------------------------------------------------------- /app/views/stashes/_stash_row.html.haml: -------------------------------------------------------------------------------- 1 | %tr{:id => "#{path}"} 2 | %td.span5{:title => path}= truncate(path, :length => 50) 3 | %td.span4= content['description'] || '' 4 | %td= content['owner'] || '' 5 | - if content['expire_at'] 6 | %td= distance_of_time_in_words(Time.now, Time.parse(content['expire_at'])) 7 | - else 8 | %td Never 9 | %td 10 | - if content['timestamp'].to_i 11 | = time_ago_in_words(Time.at(content['timestamp'].to_i)) + " ago" 12 | - else 13 | = 'Not set' 14 | %td{:id => "delete-#{path}"} 15 | %a.delete-stash{:id => path.gsub(/\//, "-"), :misc => "#stash-#{path}", :rel => "/stashes/delete_stash", :key => "#{path}"} Delete 16 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | development: 7 | adapter: sqlite3 8 | database: db/development.sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | # Warning: The database defined as "test" will be erased and 13 | # re-generated from your development database when you run "rake". 14 | # Do not set this db to the same as development or production. 15 | test: 16 | adapter: sqlite3 17 | database: db/test.sqlite3 18 | pool: 5 19 | timeout: 5000 20 | 21 | production: 22 | adapter: sqlite3 23 | database: db/production.sqlite3 24 | pool: 5 25 | timeout: 5000 26 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | def format_status(status) 3 | case status 4 | when 2, "2" 5 | "Crit" 6 | when 1, "1" 7 | "Warn" 8 | else 9 | "NA" 10 | end 11 | end 12 | 13 | def format_output(output, maxlen = 50) 14 | return "" if output.nil? 15 | (output.length > maxlen) ? "#{output[0..(maxlen - 3)]}..." : output 16 | end 17 | 18 | def use_environments? 19 | Setting.use_environments? 20 | end 21 | 22 | def configure_server? 23 | Setting.configure_server? 24 | end 25 | 26 | def downtime_number 27 | count = Downtime.active.not_completed.count 28 | (count > 0) ? "(#{count})" : "" 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/unit/api_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Api do 4 | 5 | it "should return a status" do 6 | status = Api.status 7 | status.should be_a Api 8 | status.should_not be_nil 9 | status.should_not be_false 10 | end 11 | 12 | it "should return a version" do 13 | version = Api.version 14 | version.should_not be_false 15 | version.should_not be_nil 16 | end 17 | 18 | it "should return redis health" do 19 | redis_health = Api.redis_health 20 | redis_health.should_not be_false 21 | redis_health.should_not be_nil 22 | end 23 | 24 | it "should return rabbit health" do 25 | rabbit_health = Api.rabbitmq_health 26 | rabbit_health.should_not be_nil 27 | end 28 | 29 | end 30 | -------------------------------------------------------------------------------- /app/views/api/api/setup.html.haml: -------------------------------------------------------------------------------- 1 | %p Your sensu-api server is either down or not responding, or you have not configured it correctly or you are just setting up sensu-admin. 2 | 3 | %p Please enter in your api server using the following details then hit test, then save. 4 | %p (e.g. http://username:password@sensu.myorg.com:4567) 5 | %p No trailing /'s please. 6 | 7 | #sensu_api_setup 8 | = semantic_form_for @sensu_api_server do |f| 9 | = f.input :value, :label => "Sensu API Server url" 10 | %input.btn.btn-info{:id => "save_api_server", :style => "display: none;", :name => "commit", :type => "submit", :value => "Save Setting"} 11 | %button.btn.btn-primary{:id => "test_api_server"} Test API 12 | %br 13 | %p{:id => "test_api_status"} 14 | -------------------------------------------------------------------------------- /app/views/stashes/index.mobile.haml: -------------------------------------------------------------------------------- 1 | %div{'data-role' => 'header'} 2 | %h1 Sensu Stashes 3 | = render 'layouts/navigation' 4 | %div.ui-content{'data-role' => 'content', :role => "main"} 5 | %div.content-primary 6 | %ul{'data-theme' => 'a', 'data-count-theme' => 'a', 'data-role' => 'listview', 'data-inset' => 'true'} 7 | - if @stashes.empty? 8 | %button{'data-theme' => 'c'} No Stashes 9 | - if Gem::Version.new(@sensu_version) <= Gem::Version.new("0.9.11") 10 | - @stashes.each_with_index do |(k,v), i| 11 | = render "stash_row", :k => k, :v => v, :i => i 12 | - else 13 | - @stashes.each_with_index do |stash, i| 14 | = render "stash_row", :k => stash['path'], :v => stash['content'], :i => i 15 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /app/controllers/api_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::ApiController < ApplicationController 2 | def status 3 | render :json => { :data => render_to_string(:action => '_apistatus', :layout => false) } 4 | end 5 | 6 | def time 7 | render :json => { :data => render_to_string(:action => '_time', :layout => false) } 8 | end 9 | 10 | def setup 11 | @sensu_api_server = Setting.find_by_name("api_server") 12 | end 13 | 14 | def test_api 15 | begin 16 | resp = RestClient.get "#{params[:url]}/info" 17 | render :json => { :data => { :status => 'ok', :message => resp.body }}, :layout => false 18 | rescue Exception => e 19 | render :json => { :data => { :status => 'fail', :message => e.inspect.to_s }}, :layout => false 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/controllers/api_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Api::ApiController do 4 | login_user 5 | 6 | describe "GET 'status'" do 7 | it "returns http success" do 8 | get 'status' 9 | response.code.should eq "200" 10 | end 11 | end 12 | 13 | describe "GET 'time'" do 14 | it "returns http success" do 15 | get 'time' 16 | response.code.should eq "200" 17 | end 18 | end 19 | 20 | describe "GET 'setup'" do 21 | it "returns http success" do 22 | get 'setup' 23 | response.code.should eq "200" 24 | end 25 | end 26 | 27 | describe "GET 'test_api'" do 28 | it "returns http success" do 29 | get 'test_api' 30 | response.code.should eq "200" 31 | end 32 | end 33 | 34 | end 35 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

Maybe you tried to change something you didn't have access to.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /app/views/stashes/_stash_row.mobile.haml: -------------------------------------------------------------------------------- 1 | %li{'data-theme' => 'a', :id => "stash_#{i}"} 2 | %h2= k 3 | %p== Description: #{v['description']} 4 | %p== Owner: #{v['owner']} 5 | - if v['expire_at'] 6 | %p== Expires at: #{distance_of_time_in_words(Time.now, Time.parse(v['expire_at']))} 7 | - else 8 | %p Expires at: Never 9 | %p== Set at: #{time_ago_in_words(Time.at(v['timestamp'].to_i))} ago 10 | %ul 11 | %li 12 | %h2= k 13 | %p== Attributes: 14 | - v.each do |k,v| 15 | %p== #{k} -> #{v} 16 | %li{'data-theme' => 'b'} 17 | %div{:id => "delete_#{i}"} 18 | %button.delete-stash{:index => i, 'data-theme' => 'b', :key => k, :rel => "/stashes/delete_stash"} Delete Stash 19 | %div{:id => "deleted_#{i}", :style => "display: none;"} 20 | %button{'data-theme' => 'c'} Deleted! 21 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

You may have mistyped the address or the page may have moved.

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/assets/javascripts/mobile.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // the compiled file. 9 | // 10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD 11 | // GO AFTER THE REQUIRES BELOW. 12 | // 13 | //= require jquery 14 | //= require mobileinit 15 | //= require jquery_ujs 16 | //= require jquery.mobile 17 | //= require events.mobile 18 | //= require stashes.mobile 19 | //= require clients.mobile 20 | -------------------------------------------------------------------------------- /spec/support/api_stubs.rb: -------------------------------------------------------------------------------- 1 | module ApiStubs 2 | def mock_api(opts={}) 3 | # Continuning this when spies are added to rspec-mocks: https://github.com/rspec/rspec-mocks/pull/241 4 | redis_status = opts['redis_status'] || true 5 | rabbitmq_status = opts['rabbitmq_status'] || true 6 | status = JSON.parse("{\"sensu\":{\"version\":\"0.9.12.beta.6\"},\"rabbitmq\":{\"keepalives\":{\"messages\":null,\"consumers\":null},\"results\":{\"messages\":null,\"consumers\":null},\"connected\":#{rabbitmq_status}},\"redis\":{\"connected\":#{redis_status}}}") 7 | 8 | @api = double('Api') 9 | @api.stub(:status).with(status) 10 | @api.stub(:version).with(status['sensu']['version']) 11 | @api.stub(:redis_health).with(status['redis']['connected']) 12 | @api.stub(:rabbitmq_health).with(status['rabbitmq']['connected']) 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /app/controllers/stats_controller.rb: -------------------------------------------------------------------------------- 1 | class StatsController < ApplicationController 2 | def index 3 | @events = Event.all_with_cache 4 | @clients = Client.all_with_cache 5 | @downtimes = Downtime.all 6 | @stashes = Stash.all 7 | @clients_by_subscription = {} 8 | @clients_by_environment = {} 9 | @clients.each do |client| 10 | client.subscriptions.each do |subscription| 11 | increment(@clients_by_subscription, subscription) 12 | end 13 | increment(@clients_by_environment, client.environment) 14 | end 15 | @events_by_check = {} 16 | @events_by_environment = {} 17 | @events_per_client = {} 18 | @events.each do |event| 19 | increment(@events_per_client, event.client) 20 | increment(@events_by_check, event.check) 21 | increment(@events_by_environment, event.environment) 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/controllers/settings_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SettingsController do 4 | login_user 5 | 6 | describe "GET 'index'" do 7 | it "returns http success" do 8 | get 'index' 9 | response.code.should eq "200" 10 | end 11 | end 12 | 13 | describe "GET 'missing'" do 14 | it "returns http success" do 15 | get 'missing' 16 | response.code.should eq "200" 17 | end 18 | end 19 | 20 | describe "GET 'create'" do 21 | it "returns http success" do 22 | get 'create' 23 | response.code.should eq "302" 24 | end 25 | end 26 | 27 | describe "PUT 'update'" do 28 | it "returns http success" do 29 | setting = FactoryGirl.create(:setting) 30 | put 'update', {:id => 1, :setting => {:name => setting.name}} 31 | response.code.should eq "302" 32 | end 33 | end 34 | 35 | end 36 | -------------------------------------------------------------------------------- /spec/features/client_features_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Clients" do 4 | 5 | before :each do 6 | user = FactoryGirl.create(:user) 7 | user.add_role :admin 8 | Client.refresh_cache 9 | sign_in_user(user) 10 | visit '/clients' 11 | end 12 | 13 | after :each do 14 | reset_fake_sensu! 15 | end 16 | 17 | it "should show the clients page" do 18 | page.should have_content "Clients" 19 | end 20 | 21 | it "should a client name" do 22 | page.should have_content "i-424242" 23 | end 24 | 25 | it "should show a client address" do 26 | page.should have_content "127.0.0.1" 27 | end 28 | 29 | it "should show subscriptions" do 30 | page.should have_content "test" 31 | end 32 | 33 | it "should show a client time" do 34 | page.should have_content time_ago_in_words(Time.at(1377979075)) 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /app/assets/javascripts/checks.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | $ -> 5 | $('.collapse').on 'show', -> 6 | $('#icon_toggle_' + $(this).attr("rel")).attr('class', 'icon-minus') 7 | $('.collapse').on 'hide', -> 8 | $('#icon_toggle_' + $(this).attr("rel")).attr('class', 'icon-plus') 9 | $('a.modal-for-check-submit').click -> 10 | $('#check_modal_' + $(this).attr("misc")).modal("show") 11 | $('.submit-individual-check').click -> 12 | self = $(this) 13 | $.post $(this).attr("rel"), { 'subscribers': $('#subscribers_input_' + $(this).attr("misc")).val()}, 14 | (data) -> 15 | if data 16 | $('#check_modal_' + $(self).attr("misc")).modal("hide") 17 | -------------------------------------------------------------------------------- /app/views/users/edit.html.haml: -------------------------------------------------------------------------------- 1 | %h2 Edit User 2 | 3 | - unless @user.active? 4 | %br 5 | %div.alert.alert-error 6 | %p Note: This user is not active and has a deleted_at flag set on their account, they need to be activated first from the users list before modifying anything. 7 | %br 8 | = semantic_form_for @user do |f| 9 | = f.input :email 10 | - if current_user.has_role? :admin 11 | = f.input :roles, :as => :check_boxes, :collection => Role.all 12 | = f.actions 13 | 14 | %br 15 | %br 16 | %h2 Change Password 17 | = form_for(@user, :url => { :id => @user.id, :action => "update_password" } ) do |f| 18 | %div.field 19 | = f.label :password, "Password" 20 | %br 21 | = f.password_field :password, :autocomplete => "off" 22 | %div.field 23 | = f.label :password_confirmation 24 | %br 25 | = f.password_field :password_confirmation 26 | %div.action_container 27 | = f.submit 28 | -------------------------------------------------------------------------------- /app/controllers/clients_controller.rb: -------------------------------------------------------------------------------- 1 | class ClientsController < ApplicationController 2 | before_filter :prepare_for_mobile 3 | 4 | def index 5 | @clients = Client.all_with_cache 6 | end 7 | 8 | def modal_data 9 | client = Client.find(params[:client_query]) 10 | if client 11 | render :json => {:code => 0, :data => render_to_string(:action => "_modal", :layout => false, :locals => {:client => client})} 12 | else 13 | render :json => {:code => 1, :msg => "Could not find client #{params[:client_query]}"} 14 | end 15 | end 16 | 17 | def delete_client 18 | resp = Client.destroy(params[:key]) 19 | Client.refresh_cache 20 | Event.refresh_cache 21 | respond_to do |format| 22 | format.json { render :json => {:data => (resp.code == 202).to_s}.to_json } 23 | format.mobile { render :json => {:data => (resp.code == 202).to_s}.to_json } 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/controllers/settings_controller.rb: -------------------------------------------------------------------------------- 1 | class SettingsController < ApplicationController 2 | before_filter :authenticate_user! 3 | 4 | def index 5 | authorize! :manage, Setting.all 6 | @sensu_api_server = Setting.find_by_name("api_server") 7 | @use_environments = Setting.find_by_name("use_environments") 8 | @configure_server = Setting.find_by_name("configure_server") 9 | end 10 | 11 | def missing 12 | end 13 | 14 | def create 15 | @setting = Setting.new(:name => params[:name], :value => params[:value]) 16 | if @setting.save! 17 | redirect_to(settings_path, :notice => "Successfully created") 18 | end 19 | end 20 | 21 | def update 22 | @setting = Setting.find(params[:id]) 23 | @setting.value = params[:setting][:value] 24 | if @setting.save! 25 | Setting.flush_cache 26 | redirect_to(settings_path, :notice => "Successfully updated") 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // the compiled file. 9 | // 10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD 11 | // GO AFTER THE REQUIRES BELOW. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require bootstrap 16 | //= require dataTables/jquery.dataTables 17 | //= require dataTables/jquery.dataTables.bootstrap 18 | //= require dataTables/jquery.dataTables.api.fnReloadAjax 19 | //= require_tree . 20 | //= require datepair 21 | //= require jquery.timepicker 22 | //= require bootstrap-datepicker 23 | -------------------------------------------------------------------------------- /app/views/settings/index.html.haml: -------------------------------------------------------------------------------- 1 | %div 2 | = semantic_form_for @sensu_api_server do |f| 3 | .row 4 | %div.span8#api_url 5 | = f.input :value, :label => "Sensu API Server url" 6 | = f.actions 7 | %div.span4 8 | %p (e.g. http://username:password@sensu.myorg.com:4567) 9 | %p No trailing /'s please. 10 | %br 11 | %br 12 | = semantic_form_for @use_environments do |f| 13 | .row 14 | %div.span8 15 | = f.input :value, :label => "Show environment for Client/Event", :as => :select, :collection => ["true", "false"] 16 | = f.actions 17 | %div.span4 18 | %p Note: Clients must have environments set to filter into Client & Event. 19 | %br 20 | %br 21 | = semantic_form_for @configure_server do |f| 22 | .row 23 | %div.span8 24 | = f.input :value, :label => "Configure Server checks", :as => :select, :collection => ["true", "false"] 25 | = f.actions 26 | %div.span4 27 | %p Note: Configures check to run on clients 28 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | rolify 3 | scope :deactivated, where("deleted_at IS NOT NULL") 4 | scope :active, where("deleted_at IS NULL") 5 | # Include default devise modules. Others available are: 6 | # :token_authenticatable, :confirmable, 7 | # :lockable, :timeoutable and :omniauthable 8 | devise :database_authenticatable, :recoverable, :rememberable, :trackable, :validatable 9 | has_many :logs 10 | 11 | # Setup accessible (or protected) attributes for your model 12 | attr_accessible :email, :password, :password_confirmation, :remember_me, :roles, :role_ids 13 | # attr_accessible :title, :body 14 | # 15 | def attempt_set_password(params) 16 | update_attributes(:password => params[:password], :password_confirmation => params[:password_confirmation]) 17 | end 18 | 19 | def admin? 20 | self.has_role? :admin 21 | end 22 | 23 | def active? 24 | self.deleted_at.nil? 25 | end 26 | 27 | def active_for_authentication? 28 | super && !deleted_at 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/views/layouts/_navigation.html.haml: -------------------------------------------------------------------------------- 1 | = link_to "Sensu-Admin", root_path, :class => 'brand' 2 | %ul.nav 3 | - if user_signed_in? 4 | %li 5 | = link_to("Events", events_path) 6 | %li 7 | = link_to("Clients", clients_path) 8 | %li 9 | = link_to("Stashes", stashes_path) 10 | %li 11 | = link_to("Checks", checks_path) 12 | %li 13 | = link_to("Downtimes #{downtime_number()}", downtimes_path) 14 | %li 15 | = link_to("Aggregates", aggregates_path) 16 | %li 17 | = link_to("Logs", logs_path) 18 | %li 19 | = link_to("Stats", stats_path) 20 | %li 21 | = link_to("Account", edit_user_path(current_user)) 22 | - if can? :manage, @user 23 | %li 24 | = link_to('Users', users_path) 25 | - if can? :manage, @settings 26 | %li 27 | = link_to('Settings', settings_path) 28 | %li 29 | = link_to('Logout', destroy_user_session_path, :method=>'delete') 30 | 31 | - else 32 | %li 33 | = link_to('Login', new_user_session_path) 34 | -------------------------------------------------------------------------------- /app/models/client.rb: -------------------------------------------------------------------------------- 1 | class Client < Resting 2 | def self.all_with_cache 3 | clients = Rails.cache.read("clients") 4 | if clients.nil? 5 | clients = Client.all 6 | Rails.cache.write("clients", clients, :expires_in => 45.seconds, :race_condition_ttl => 10) 7 | clients.each do |client| 8 | client_data = JSON.parse(client.to_json) # We only want attributes 9 | Rails.cache.write(client.name, client_data, :expires_in => 5.minutes, :race_condition_ttl => 10) 10 | end 11 | end 12 | clients 13 | end 14 | 15 | def self.refresh_cache 16 | Rails.cache.write("clients", Client.all, :expires_in => 30.seconds, :race_condition_ttl => 10) 17 | end 18 | 19 | def self.full_hash 20 | clienthash = {} 21 | Client.all_with_cache.each{|c| clienthash[c.name] = { :address => c.address, :environment => c.environment}} 22 | clienthash 23 | end 24 | 25 | # Here so if Client is missing attributes that the view does not fail 26 | def method_missing(method, *args, &block) 27 | "None" 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /app/assets/stylesheets/events.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the events controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | 5 | 6 | .critical-event { 7 | color: black; 8 | background: #DED3D3; 9 | border: 3px solid #FF8282; 10 | padding: 2px; 11 | -moz-border-radius: 25px; 12 | -webkit-border-radius: 25px; 13 | -khtml-border-radius: 25px; 14 | border-radius: 7px; 15 | 16 | /* Firefox */ 17 | -moz-border-top-colors: #eee #ddd #ccc #bbb #aaa; 18 | -moz-border-left-colors: #111 #222 #333 #444 #555; 19 | -moz-border-bottom-colors: #666 #777 #888 #999; 20 | /* WebKit - not working */ 21 | -webkit-border-top-colors: #eee #ddd #ccc #bbb #aaa; 22 | -webkit-border-left-colors: #111 #222 #333 #444 #555; 23 | -webkit-border-bottom-colors: #666 #777 #888 #999; 24 | /* IE9, Opera 10.5+ - not working */ 25 | border-top-colors: #eee #ddd #ccc #bbb #aaa; 26 | border-left-colors: #111 #222 #333 #444 #555; 27 | border-bottom-colors: #666 #777 #888 #999; 28 | } 29 | -------------------------------------------------------------------------------- /app/models/log.rb: -------------------------------------------------------------------------------- 1 | class Log < ActiveRecord::Base 2 | attr_accessible :check, :client, :environment, :output, :silence_type, :status, :user_id, :action_type, :reason 3 | belongs_to :user 4 | 5 | def self.log(user, client_name, type, reason = nil, event = nil) 6 | client = Client.find(client_name) 7 | if event.nil? 8 | #Client only event 9 | user.logs.create!(:client => client.name, 10 | :silence_type => "client", 11 | :action_type => type, 12 | :reason => reason, 13 | :environment => client.environment) 14 | else 15 | user.logs.create!(:client => client.name, 16 | :check => event.check, 17 | :silence_type => "event", 18 | :action_type => type, 19 | :reason => reason, 20 | :environment => client.environment, 21 | :output => event.output, 22 | :status => event.status) 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/views/devise/registrations/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit <%= resource_name.to_s.humanize %>

2 | 3 | <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
<%= f.label :email %>
7 | <%= f.email_field :email %>
8 | 9 |
<%= f.label :password %> (leave blank if you don't want to change it)
10 | <%= f.password_field :password, :autocomplete => "off" %>
11 | 12 |
<%= f.label :password_confirmation %>
13 | <%= f.password_field :password_confirmation %>
14 | 15 |
<%= f.label :current_password %> (we need your current password to confirm your changes)
16 | <%= f.password_field :current_password %>
17 | 18 |
<%= f.submit "Update" %>
19 | <% end %> 20 | 21 |

Cancel my account

22 | 23 |

Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete %>.

24 | 25 | <%= link_to "Back", :back %> 26 | -------------------------------------------------------------------------------- /app/views/users/registrations/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit <%= resource_name.to_s.humanize %>

2 | 3 | <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
<%= f.label :email %>
7 | <%= f.email_field :email %>
8 | 9 |
<%= f.label :password %> (leave blank if you don't want to change it)
10 | <%= f.password_field :password, :autocomplete => "off" %>
11 | 12 |
<%= f.label :password_confirmation %>
13 | <%= f.password_field :password_confirmation %>
14 | 15 |
<%= f.label :current_password %> (we need your current password to confirm your changes)
16 | <%= f.password_field :current_password %>
17 | 18 |
<%= f.submit "Update" %>
19 | <% end %> 20 | 21 |

Cancel my account

22 | 23 |

Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete %>.

24 | 25 | <%= link_to "Back", :back %> 26 | -------------------------------------------------------------------------------- /app/models/ability.rb: -------------------------------------------------------------------------------- 1 | class Ability 2 | include CanCan::Ability 3 | 4 | def initialize(user) 5 | # Define abilities for the passed in user here. For example: 6 | # 7 | user ||= User.new # guest user (not logged in) 8 | if user.admin? 9 | can :manage, :all 10 | else 11 | can :read, :all 12 | end 13 | # 14 | # The first argument to `can` is the action you are giving the user permission to do. 15 | # If you pass :manage it will apply to every action. Other common actions here are 16 | # :read, :create, :update and :destroy. 17 | # 18 | # The second argument is the resource the user can perform the action on. If you pass 19 | # :all it will apply to every resource. Otherwise pass a Ruby class of the resource. 20 | # 21 | # The third argument is an optional hash of conditions to further filter the objects. 22 | # For example, here the user can only update published articles. 23 | # 24 | # can :update, Article, :published => true 25 | # 26 | # See the wiki for details: https://github.com/ryanb/cancan/wiki/Defining-Abilities 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/models/setting.rb: -------------------------------------------------------------------------------- 1 | class Setting < ActiveRecord::Base 2 | attr_accessible :name, :value 3 | validates_uniqueness_of :name 4 | 5 | def self.api_server 6 | Rails.cache.fetch("api_server", :expires_in => 15.minutes, :race_condition_ttl => 10) do 7 | find_by_name("api_server").value 8 | end 9 | end 10 | 11 | def self.use_environments? 12 | Rails.cache.fetch("use_environments", :expires_in => 15.minutes, :race_condition_ttl => 10) do 13 | find_by_name("use_environments").value == "true" 14 | end 15 | end 16 | 17 | def self.min_desc_length 18 | Rails.cache.fetch("min_desc_length", :expires_in => 15.minutes, :race_condition_ttl => 10) do 19 | find_by_name("min_desc_length").value 20 | end 21 | end 22 | 23 | def self.configure_server? 24 | Rails.cache.fetch("configure_server", :expires_in => 15.minutes, :race_condition_ttl => 10) do 25 | find_by_name("configure_server").value == "true" && File.exists?("config/config.json") 26 | end 27 | end 28 | 29 | def self.flush_cache 30 | REQUIRED_SETTINGS.each do |setting| 31 | Rails.cache.delete(setting) 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Sonian Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /spec/factories.rb: -------------------------------------------------------------------------------- 1 | require 'factory_girl' 2 | 3 | FactoryGirl.define do 4 | 5 | ## Users/Roles 6 | factory :user do 7 | sequence :email do |n| 8 | "test_user#{n}@example.com" 9 | end 10 | password '123456' 11 | password_confirmation '123456' 12 | 13 | factory :roles do 14 | name "admin" 15 | user 16 | end 17 | end 18 | 19 | factory :role do 20 | end 21 | 22 | ## Downtimes 23 | factory :downtime do 24 | name "maintenance" 25 | description "shutting it down for maintenance" 26 | start_time { Time.now + 1.hour } 27 | stop_time { Time.now + 2.hours } 28 | user_id 1 29 | end 30 | 31 | factory :downtime_client do 32 | name "test_downtime_client" 33 | downtime 34 | 35 | factory :downtime_in_past do 36 | end 37 | end 38 | 39 | factory :downtime_check do 40 | # belongs_to :downtime 41 | sequence :name do |n| 42 | "test_downtime_check_#{n}" 43 | end 44 | end 45 | 46 | factory :client do 47 | name "test_client" 48 | end 49 | 50 | ## Setting 51 | factory :setting do 52 | name "test_configure_server" 53 | value true 54 | end 55 | 56 | end 57 | -------------------------------------------------------------------------------- /spec/features/stashes_features_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Stashes" do 4 | 5 | before :each do 6 | user = FactoryGirl.create(:user) 7 | user.add_role :admin 8 | Stash.refresh_cache 9 | sign_in_user(user) 10 | visit '/stashes' 11 | end 12 | 13 | after :each do 14 | reset_fake_sensu! 15 | end 16 | 17 | it "should show the stashes page" do 18 | page.should have_content "Stashes" 19 | end 20 | 21 | it "should show the correct stash count" do 22 | page.should have_content "Stashes (#{Stash.all.count})" 23 | end 24 | 25 | it "should show stashes and correct data" do 26 | page.should have_content "tester" 27 | page.should have_content "Never" 28 | page.should have_content "43 years ago" 29 | end 30 | 31 | it "should have a delete link" do 32 | page.should have_button "Delete" 33 | end 34 | 35 | pending "should allow deletion of a stash", :js => true do 36 | page.should have_selector("#silence-i-424242-tokens", :text => "Delete") 37 | find("#silence-i-424242-tokens", :text => "Delete").click 38 | page.should_not have_selector("#silence-i-424242-tokens", :text => "Delete") 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /app/views/clients/index.html.haml: -------------------------------------------------------------------------------- 1 | %div{:class => "modal small-modal hide", :id => "client-data-modal"} 2 | %div{:style => "display: none;", :id => "use_environments", :rel => "#{use_environments?}"} 3 | 4 | %table.table-striped.dataTable.clients_table.table{:id => "clients_table"} 5 | %thead 6 | - if use_environments? 7 | %th ENV 8 | %th Name 9 | %th Address 10 | %th Subscriptions 11 | %th Role 12 | %th Last Checkin 13 | %th 14 | 15 | %tbody 16 | - @clients.sort_by(&:name).each do |client| 17 | %tr{:id => "client_row_#{client.name}", :rel => client.name} 18 | - if use_environments? 19 | %td.moreinfo= client.environment 20 | %td.moreinfo= client.name 21 | %td.moreinfo= client.address 22 | %td.subpopover{'data-content' => client.subscriptions.join(', ')} 23 | = format_subscriptions(client.subscriptions) 24 | %i.icon-chevron-right 25 | %td.moreinfo= client.role 26 | %td.moreinfo= time_ago_in_words(Time.at(client.timestamp)) 27 | %td.moreinfo 28 | %i.icon-zoom-in 29 | %td 30 | %a.delete-client{:key => client.name, :rel => "/clients/#{client.name}"} Delete 31 | -------------------------------------------------------------------------------- /app/views/users/shared/_links.erb: -------------------------------------------------------------------------------- 1 | <%- if controller_name != 'sessions' %> 2 | <%= link_to "Sign in", new_session_path(resource_name) %>
3 | <% end -%> 4 | 5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %> 6 | <%= link_to "Sign up", new_registration_path(resource_name) %>
7 | <% end -%> 8 | 9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' %> 10 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
11 | <% end -%> 12 | 13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> 14 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
15 | <% end -%> 16 | 17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> 18 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
19 | <% end -%> 20 | 21 | <%- if devise_mapping.omniauthable? %> 22 | <%- resource_class.omniauth_providers.each do |provider| %> 23 | <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %>
24 | <% end -%> 25 | <% end -%> -------------------------------------------------------------------------------- /app/views/devise/shared/_links.erb: -------------------------------------------------------------------------------- 1 | <%- if controller_name != 'sessions' %> 2 | <%= link_to "Sign in", new_session_path(resource_name) %>
3 | <% end -%> 4 | 5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %> 6 | <%= link_to "Sign up", new_registration_path(resource_name) %>
7 | <% end -%> 8 | 9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' %> 10 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
11 | <% end -%> 12 | 13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> 14 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
15 | <% end -%> 16 | 17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> 18 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
19 | <% end -%> 20 | 21 | <%- if devise_mapping.omniauthable? %> 22 | <%- resource_class.omniauth_providers.each do |provider| %> 23 | <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %>
24 | <% end -%> 25 | <% end -%> -------------------------------------------------------------------------------- /app/views/api/api/_apistatus.html.haml: -------------------------------------------------------------------------------- 1 | %div.row 2 | - api = Api.status 3 | - sensu_version = api.sensu['version'] 4 | - redis_health = api.redis['connected'] ? "ok" : "failed" 5 | - rabbitmq_health = api.rabbitmq['connected'] ? "ok" : "failed" 6 | %span.badge.badge-info{:style => "margin-right: 5px;"} 7 | == API Version: #{sensu_version} 8 | - if redis_health == "ok" 9 | %span.badge.badge-success{:style => "margin-right: 5px;"} 10 | == Redis: #{redis_health.upcase} 11 | - else 12 | %span.badge.badge-important{:style => "margin-right: 5px;"} 13 | == Redis: #{redis_health.upcase}. 14 | - if rabbitmq_health == "ok" 15 | %span.badge.badge-success{:style => "margin-right: 5px;"} 16 | == RabbitMQ: #{rabbitmq_health.upcase} 17 | - else 18 | %span.badge.badge-important{:style => "margin-right: 5px;"} 19 | == RabbitMQ: #{rabbitmq_health.upcase}. 20 | %span.badge.badge-info{:style => "margin-right: 5px;"} 21 | == Keep Alives: Messages - #{api.rabbitmq['keepalives']['messages']} | Consumers - #{api.rabbitmq['keepalives']['consumers']} 22 | %span.badge.badge-info{:style => "margin-right: 5px;"} 23 | == Results: Messages - #{api.rabbitmq['results']['messages']} | Consumers - #{api.rabbitmq['results']['consumers']} 24 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | before_filter :authenticate_user! 4 | before_filter :check_for_api, :except => [:setup, :test_api, :update, :new, :create] #Otherwise devise wont sign people in 5 | before_filter :check_for_settings, :except => :missing 6 | 7 | private 8 | 9 | def check_for_api 10 | begin 11 | RestClient.get "#{Setting.api_server}/info" 12 | rescue Exception => e 13 | redirect_to(api_setup_path, :alert => "API(#{Setting.api_server}) is down: #{e}") 14 | end 15 | end 16 | 17 | def check_for_settings 18 | REQUIRED_SETTINGS.each do |setting| 19 | unless Setting.find_by_name(setting) 20 | redirect_to(settings_missing_path, :notice => "Settings missing #{setting}") and return 21 | end 22 | end 23 | end 24 | 25 | def mobile_device? 26 | if session[:mobile_param] 27 | session[:mobile_param] == "1" 28 | else 29 | request.user_agent =~ /Mobile|webOS/ 30 | end 31 | end 32 | 33 | helper_method :mobile_device? 34 | 35 | def prepare_for_mobile 36 | session[:mobile_param] = params[:mobile] if params[:mobile] 37 | request.format = :mobile if mobile_device? 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /app/views/users/index.html.haml: -------------------------------------------------------------------------------- 1 | %h3 Active users: 2 | %table.table.table-striped.table-bordered 3 | %tbody 4 | %thead 5 | %th Email 6 | %th Created At 7 | %th Admin? 8 | %th 9 | %th 10 | %tbody 11 | - @active_users.each do |user| 12 | %tr 13 | %td= user.email 14 | %td= user.created_at 15 | %td= user.admin? 16 | %td= button_to "Deactivate", user, :method => :delete, :confirm => "You sure?" 17 | %td= link_to "Edit", edit_user_path(user) 18 | %br 19 | %br 20 | %h3 Deactivated users: 21 | %table.table.table-striped.table-bordered 22 | %tbody 23 | %thead 24 | %th Email 25 | %th Created At 26 | %th Admin? 27 | %th Deactivated At 28 | %th 29 | %th 30 | %tbody 31 | - @deactivated_users.each do |user| 32 | %tr 33 | %td= user.email 34 | %td= user.created_at 35 | %td= user.admin? 36 | %td= user.deleted_at 37 | %td= button_to "Activate", {:id => user.id, :controller => 'users', :action => "activate"}, :method => :put, :confirm => "You sure?" 38 | %td= link_to "Edit", edit_user_path(user) 39 | 40 | 41 | %br 42 | %br 43 | = link_to 'Create new user', new_user_path, :class => "btn btn-info" 44 | -------------------------------------------------------------------------------- /lib/assets/stylesheets/jquery.timepicker.css: -------------------------------------------------------------------------------- 1 | .ui-timepicker-list { 2 | overflow-y: auto; 3 | height: 150px; 4 | width: 6.5em; 5 | background: #fff; 6 | border: 1px solid #ddd; 7 | margin: 0; 8 | padding: 0; 9 | list-style: none; 10 | -webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2); 11 | -moz-box-shadow:0 5px 10px rgba(0,0,0,0.2); 12 | box-shadow:0 5px 10px rgba(0,0,0,0.2); 13 | outline: none; 14 | } 15 | 16 | .ui-timepicker-list.ui-timepicker-with-duration { 17 | width: 11em; 18 | } 19 | 20 | .ui-timepicker-duration { 21 | margin-left: 5px; color: #888; 22 | } 23 | 24 | .ui-timepicker-list:hover .ui-timepicker-duration { 25 | color: #888; 26 | } 27 | 28 | .ui-timepicker-list li { 29 | padding: 3px 0 3px 5px; 30 | cursor: pointer; 31 | white-space: nowrap; 32 | color: #000; 33 | list-style: none; 34 | margin: 0; 35 | } 36 | 37 | .ui-timepicker-list:hover .ui-timepicker-selected { 38 | background: #fff; color: #000; 39 | } 40 | 41 | li.ui-timepicker-selected, 42 | .ui-timepicker-list li:hover, 43 | .ui-timepicker-list:hover .ui-timepicker-selected:hover { 44 | background: #1980EC; color: #fff; 45 | } 46 | 47 | li.ui-timepicker-selected .ui-timepicker-duration, 48 | .ui-timepicker-list li:hover .ui-timepicker-duration { 49 | color: #ccc; 50 | } 51 | -------------------------------------------------------------------------------- /spec/features/downtimes_features_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Downtimes" do 4 | 5 | before :each do 6 | user = FactoryGirl.create(:user) 7 | user.add_role :admin 8 | sign_in_user(user) 9 | visit '/downtimes' 10 | end 11 | 12 | it "should show the downtimes page" do 13 | page.should have_content "Active downtimes" 14 | end 15 | 16 | it "should allow creation of a new downtime" do 17 | page.should have_link "Create Downtime" 18 | click_link "Create Downtime" 19 | time_now = Time.now.utc 20 | fill_in "downtime[name]", :with => "Test Downtime" 21 | fill_in "downtime[description]", :with => "This is a test description" 22 | fill_in "downtime_start_date", :with => (time_now + 12.hours).strftime("%d/%m/%Y") 23 | fill_in "downtime_end_date", :with => (time_now + 13.hours).strftime("%d/%m/%Y") 24 | t1 = Time.at(time_now.to_i/(15*60)*(15*60)) + 12.hours 25 | t2 = Time.at(time_now.to_i/(15*60)*(15*60)) + 13.hours 26 | fill_in "downtime[begin_time]", :with => t1.strftime("%-l:%M%P") 27 | fill_in "downtime[end_time]", :with => t2.strftime("%-l:%M%P") 28 | check "www.fqdn.com" 29 | click_button "Create Downtime" 30 | page.should have_content "Test Downtime" 31 | page.should have_content "This is a test description" 32 | end 33 | 34 | end 35 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | 9 | if User.all.count == 0 10 | u = User.create!({:email => "admin@example.com", :password => "secret", :password_confirmation => "secret" }) 11 | u.add_role :admin 12 | u.save! 13 | end 14 | 15 | unless Setting.find_by_name("api_server") 16 | # Sensu API supports http basic auth, so if your using that -- just do http://user:pass@localhost:4567, no trailing / 17 | if Rails.env == "test" 18 | Setting.create(:name => "api_server", :value => "http://localhost:9292") 19 | else 20 | Setting.create(:name => "api_server", :value => "http://localhost:4567") 21 | end 22 | end 23 | 24 | unless Setting.find_by_name("use_environments") 25 | Setting.create(:name => "use_environments", :value => "false") 26 | end 27 | 28 | unless Setting.find_by_name("configure_server") 29 | Setting.create(:name => "configure_server", :value => "true") 30 | end 31 | 32 | unless Setting.find_by_name("min_desc_length") 33 | Setting.create(:name => "min_desc_length", :value => "0") 34 | end 35 | -------------------------------------------------------------------------------- /app/assets/javascripts/api.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | $ -> 5 | updateApiStatus = ()-> 6 | $.ajax "/api/status", 7 | type: 'GET' 8 | error: (jqXHR, textStatus, errorThrown) -> 9 | $("#api_info_div").html("Failed to update API information!") 10 | success: (data, textStatus, jqXHR) -> 11 | $("#api_info_div").html(data['data']) 12 | updateApiStatus() 13 | if $('#sensu_api_setup').length > 0 14 | $(document).on 'click', '#test_api_server', -> 15 | $.post "/api/test_api", { 'url': $("#setting_value").val()}, 16 | (data) -> 17 | if data 18 | if data['data']['status'] == 'ok' 19 | $('#test_api_status').text("API Success! " + data['data']['message'] + " -- Please click Save Setting now to save your changes") 20 | $('#save_api_server').show() 21 | $('#test_api_server').hide() 22 | $('.alert').hide() 23 | else 24 | $('#test_api_status').text("API Failed! " + JSON.stringify(data['data']['message'])) 25 | else 26 | $('#test_api_status').text("Failed to contact API -- Possible app error") 27 | -------------------------------------------------------------------------------- /app/views/events/_modal_silence.html.haml: -------------------------------------------------------------------------------- 1 | - len = Setting.min_desc_length.to_i 2 | - if type.match("client") 3 | - header_title = "Silence client #{event.client}" 4 | - suffix = "#{event.client}" 5 | - else 6 | - header_title = "Silence check #{event.check} on client #{event.client}" 7 | - suffix = "#{event.client}_#{event.check}" 8 | %div.modal-header 9 | %h3= header_title 10 | %div.modal-body 11 | %div Note: Your event handler must use sensu-plugin or hit the API to make use of silencing. 12 | %div.silence-reason 13 | %h4 Enter reason 14 | %textarea.input-xlarge.silence-input{:id => "text_input_#{suffix}", :index_id => i, :data => {:min => len}, :misc => suffix, :rel => "Silence #{type}", :rows => "3", :type => "text", :placeholder => "Silence #{type} reason"} 15 | %div.silence-time 16 | %h4 Expire at (Optional): 17 | %p Date: 18 | %input.datepicker{:id => "silence_expire_at_date_#{suffix}"} 19 | %p Time: 20 | %input.timepicker{:id => "silence_expire_at_time_#{suffix}"} 21 | %div.modal-footer 22 | - if len > 0 23 | %a.silence-submit-event.btn.btn-inverse{:control => "silence_submit_#{suffix}", :index_id => i, :misc => suffix, :rel => "/events/#{suffix.sub("_","/")}/silence"} Submit 24 | - else 25 | %a.silence-submit-event.btn.btn-success{:control => "silence_submit_#{suffix}", :index_id => i, :misc => "#{suffix}", :rel => "/events/#{suffix.sub("_","/")}/silence"} Submit 26 | -------------------------------------------------------------------------------- /app/assets/javascripts/downtimes.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | $ -> 5 | $('#downtime_clients_table').tableFilter({ additionalFilterTriggers: [$('#downtime_client_search')]}) 6 | $('#downtime_checks_table').tableFilter({ additionalFilterTriggers: [$('#downtime_check_search')]}) 7 | $('.filters').hide() 8 | $('#select_all_clients').change -> 9 | $('tr[rel="downtime_client_table_row"][filtermatch!="false"]').find('input[type="checkbox"]').attr("checked", $(this).is(":checked")) 10 | $('#select_all_checks').change -> 11 | $('tr[rel="downtime_check_table_row"][filtermatch!="false"]').find('input[type="checkbox"]').attr("checked", $(this).is(":checked")) 12 | $('.toggle_box').click -> 13 | $(this).siblings('.check_box').find('input').trigger("click") 14 | 15 | if $("#api_time_div").length > 0 16 | updateServerTime = ()-> 17 | $.ajax "/api/time", 18 | type: 'GET' 19 | error: (jqXHR, textStatus, errorThrown) -> 20 | $("#api_time_div").html("Failed to update server time.") 21 | success: (data, textStatus, jqXHR) -> 22 | $("#api_time_div").html(data['data']) 23 | updateServerTime() 24 | setInterval () -> 25 | updateServerTime() 26 | , 60000 27 | -------------------------------------------------------------------------------- /app/helpers/events_helper.rb: -------------------------------------------------------------------------------- 1 | module EventsHelper 2 | 3 | def silenced_output(check, client) 4 | # TODO: This should be all haml_tag's 5 | output = "" 6 | output << "Check silenced by:
" unless check.nil? 7 | output << "#{check['content']['description']} - #{check['content']['owner']} - #{check['content']['timestamp']}
" unless check.nil? 8 | output << "Client silenced by:
" unless client.nil? 9 | output << "#{client['content']['description']} - #{client['content']['owner']} - #{client['content']['timestamp']}
" unless client.nil? 10 | output << "Not Silenced" if client.nil? && check.nil? 11 | output 12 | end 13 | 14 | def display_silenced(event, i, client_silenced, check_silenced) 15 | # If client_silenced is nil, that means its not silenced 16 | voldisp = ((client_silenced.nil? && check_silenced.nil?) ? "up" : "off") 17 | haml_tag(:i, {:class => "icon-volume-#{voldisp}", :event => "#{event.client}_#{event.check}", :rel => "icon_silenced_#{i}"}) 18 | end 19 | 20 | def is_nil?(obj) 21 | obj.nil? ? "true" : "false" 22 | end 23 | 24 | def not_nil?(obj) 25 | obj.nil? ? "false" : "true" 26 | end 27 | 28 | def is_url?(str) 29 | require 'uri' 30 | str =~ URI::ABS_URI ? true : false 31 | end 32 | 33 | def anchor_wrap_if_url(str) 34 | if is_url?(str) 35 | content_tag(:a, str) 36 | else 37 | str 38 | end 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /app/views/events/index.mobile.haml: -------------------------------------------------------------------------------- 1 | - criticals = @events.select{|event| event.status.to_i == 2} 2 | - warnings = @events.select{|event| event.status.to_i == 1} 3 | - unknowns = @events.select{|event| ![1,2].include?(event.status.to_i)} 4 | 5 | %div{'data-role' => 'header'} 6 | %h1 Sensu Events 7 | //render_to_string(:action => "_output", :formats => [:html], :layout => false, :locals => { :event => event }), 8 | = render 'layouts/navigation' 9 | %div.ui-content{'data-role' => 'content', :role => "main"} 10 | %div.content-primary 11 | %ul{'data-theme' => 'b', 'data-count-theme' => 'a', 'data-role' => 'listview', 'data-inset' => 'true'} 12 | %li{'data-theme' => 'b'} 13 | %h2 Critical Events 14 | %div.ui-li-count= criticals.count 15 | %ul 16 | = render :partial => "mobile_event", :locals => {:events => criticals, :color_code => 'b'} 17 | %li{'data-theme' => 'c'} 18 | %h2 Warning Events 19 | %div.ui-li-count= warnings.count 20 | %ul 21 | = render :partial => "mobile_event", :locals => {:events => warnings, :color_code => 'c'} 22 | %li{'data-theme' => 'e'} 23 | %h2 Unknown Events 24 | %div.ui-li-count= unknowns.count 25 | %ul 26 | = render :partial => "mobile_event", :locals => {:events => unknowns, :color_code => 'e'} 27 | 28 | %p Note: In this current iteration, events do NOT auto-update on any interval, please hit refresh if you want the latest events. 29 | -------------------------------------------------------------------------------- /app/assets/javascripts/clients.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | $ -> 5 | $('.subpopover').popover({'placement': 'bottom'}) 6 | 7 | $('td.moreinfo').click -> 8 | $(this).closest('tr').attr("rel") 9 | $.get "/clients/modal_data", { 'client_query': $(this).closest('tr').attr("rel") }, 10 | (data) -> 11 | if data.code == 0 12 | $('#client-data-modal').html(data['data']) 13 | $('#client-data-modal').modal("show") 14 | else 15 | alert(data.msg) 16 | 17 | $('a.delete-client').click -> 18 | self = $(this) 19 | if (confirm('Are you sure?')) 20 | $.post "/clients/delete_client", { 'key': $(this).attr('key') }, 21 | (data) -> 22 | if data 23 | $("#client_row_" + $(self).attr('key')).hide() 24 | else 25 | alert("Could not delete client") 26 | 27 | use_environments = $("#use_environments").attr("rel") 28 | if use_environments == "true" 29 | aocolumns = [null, null, null, null, null, null, {bSortable: false}, {bSortable: false}] 30 | else 31 | aocolumns = [null, null, null, null, null, {bSortable: false}, {bSortable: false}] 32 | 33 | dtable = $('#clients_table').dataTable 34 | bAutoWidth: false 35 | bSort: true 36 | aoColumns: aocolumns 37 | bPaginate: false 38 | -------------------------------------------------------------------------------- /app/controllers/stashes_controller.rb: -------------------------------------------------------------------------------- 1 | class StashesController < ApplicationController 2 | before_filter :prepare_for_mobile 3 | 4 | def index 5 | api = Api.status 6 | @sensu_version = api.sensu['version'] 7 | @stashes = Stash.stashes 8 | end 9 | 10 | def create_stash 11 | attributes = {:description => "Manual stash creation", :owner => current_user.email, :timestamp => Time.now.to_i } 12 | unless params[:date].empty? || params[:time].empty? 13 | attributes.merge!({:expire_at => Time.parse("#{params[:date]} #{params[:time]}")}) 14 | end 15 | if params[:description].empty? 16 | attributes.merge!({:description => "Manual stash creation"}) 17 | else 18 | attributes.merge!({:description => params[:description]}) 19 | end 20 | resp = Stash.create_stash(params[:key], attributes) 21 | Stash.refresh_cache 22 | respond_to do |format| 23 | format.json { render :json => (resp.code == 201).to_s } 24 | end 25 | end 26 | 27 | def delete_stash 28 | resp = Stash.delete_stash(params[:key]) 29 | Stash.refresh_cache 30 | respond_to do |format| 31 | format.json { render :json => (resp.code == 204).to_s } 32 | format.mobile { render :json => (resp.code == 204).to_s } 33 | end 34 | end 35 | 36 | def delete_all_stashes 37 | resp = Stash.delete_all_stashes 38 | Stash.refresh_cache 39 | respond_to do |format| 40 | format.json { render :json => resp.to_s } 41 | format.mobile { render :json => resp.to_s } 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/features/stats_features_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Stats" do 4 | 5 | before :each do 6 | user = FactoryGirl.create(:user) 7 | user.add_role :admin 8 | sign_in_user(user) 9 | visit '/stats' 10 | end 11 | 12 | after :each do 13 | reset_fake_sensu! 14 | end 15 | 16 | it "should show the stats page" do 17 | page.should have_content "Stats" 18 | end 19 | 20 | it "should show clients by subscriptions" do 21 | within("#clients-by-subscriptions") { find("td", :text => "test") } 22 | end 23 | 24 | it "should show clients by environment" do 25 | within("#clients-by-environment") { find("td", :text => "None") } 26 | end 27 | 28 | it "should show miscellaneous client stats" do 29 | within("#clients-misc tbody") do 30 | find("td", :text => "Total Clients") 31 | page.should have_content Client.all.count 32 | end 33 | end 34 | 35 | it "should show events by check" do 36 | within("#events-by-check") do 37 | page.should have_content "test" 38 | end 39 | end 40 | 41 | it "should show events by environment" do 42 | within("#events-by-environment") do 43 | page.should have_content "4" 44 | end 45 | end 46 | 47 | it "should show events per client" do 48 | within("#events-per-client") do 49 | page.should have_content "i-424242" 50 | end 51 | end 52 | 53 | it "should show miscellaneous event stats" do 54 | within("#events-misc") do 55 | page.should have_content "Total Events" 56 | end 57 | end 58 | 59 | end 60 | -------------------------------------------------------------------------------- /lib/assets/javascripts/events.mobile.js.coffee: -------------------------------------------------------------------------------- 1 | $ -> 2 | $(document).on 'click', '.resolve-submit', -> 3 | self = $(this) 4 | $.ajax 5 | type: "POST" 6 | url: $(this).attr("rel") 7 | cache: false 8 | success: (data) -> 9 | $("[rel=\"silence_" + $(self).attr("control") + "\"]").hide() 10 | $("[rel=\"unsilence_" + $(self).attr("control") + "\"]").hide() 11 | $("[control=\"resolve_" + $(self).attr("control") + "\"]").hide() 12 | $("[control=\"resolved_" + $(self).attr("control") + "\"]").show() 13 | $("#" + $(self).attr("type")).toggle() 14 | error: (data) -> 15 | alert "Failed to perform action" 16 | $(document).on 'click', '.silence-submit', -> 17 | self = $(this) 18 | if $(self).attr("description") 19 | description = $("#" + $(self).attr("description") + "_description").val() 20 | if description.length == 0 21 | description = "No reason given" 22 | else 23 | description = "" 24 | if $(self).attr("silence_type") 25 | silence_type = $(self).attr("silence_type") 26 | else 27 | silence_type = "" 28 | $.ajax 29 | type: "POST" 30 | url: $(this).attr("rel") 31 | cache: false 32 | data: { description: description } 33 | success: (data) -> 34 | $("[rel=\"silence_" + $(self).attr("control") + "\"]").toggle() 35 | $("[rel=\"unsilence_" + $(self).attr("control") + "\"]").toggle() 36 | $("[rel=\"silence_details_" + $(self).attr("control") + "\"]").text silence_type + description 37 | error: (data) -> 38 | alert "Failed to perform action" 39 | -------------------------------------------------------------------------------- /app/views/checks/_checks.html.haml: -------------------------------------------------------------------------------- 1 | %p Note: You can expand a check and submit it by expanding the check and clicking "Request Check" 2 | - @checks.sort_by(&:name).each do |check| 3 | %div.modal{:id => "check_modal_#{check.name}", :style => "display: none;"} 4 | %div.modal-header 5 | %h3== Submit #{check.name} 6 | %div.modal-body 7 | %p Input subscribers to run check against comma seperated (e.g. role1, role2, role3) 8 | %textarea.input-xlarge{:id => "subscribers_input_#{check.name}"} 9 | %div.modal-footer 10 | %a.submit-individual-check.btn.btn-success{:id => "submit_check_#{check.name}", :misc => "#{check.name}", :rel => "/checks/#{check.name}/submit", :href => "#"} Submit 11 | 12 | %div.accordion{:id => 'check_accordion'} 13 | - @checks.sort_by(&:name).each do |check| 14 | %div.row 15 | %div.span12 16 | %div.accordion-group 17 | %div.accordion-heading 18 | %a.accordion-toggle{:href => "#collapse_#{check.name}", 'data-toggle' => 'collapse', 'data-parent' => '#check_accordion'} 19 | = check.name 20 | %i.icon-plus{:id => "icon_toggle_#{check.name}"} 21 | %div.accordion-body.collapse{:rel => "#{check.name}", :id => "collapse_#{check.name}"} 22 | %div.accordion-inner 23 | %table.table.table-striped.table-bordered.check-table{:id => "table_for_#{check.name}"} 24 | %tbody 25 | - check.attributes.each do |k,v| 26 | %tr 27 | %td= k 28 | %td= v 29 | 30 | %a.btn.btn-primary.modal-for-check-submit{:misc => "#{check.name}"} Request Check 31 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | SensuAdmin::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Configure static asset server for tests with Cache-Control for performance 11 | config.serve_static_assets = true 12 | config.static_cache_control = "public, max-age=3600" 13 | 14 | # Log error messages when you accidentally call methods on nil 15 | config.whiny_nils = true 16 | 17 | # Show full error reports and disable caching 18 | config.consider_all_requests_local = true 19 | config.action_controller.perform_caching = false 20 | 21 | # Raise exceptions instead of rendering exception templates 22 | config.action_dispatch.show_exceptions = false 23 | 24 | # Disable request forgery protection in test environment 25 | config.action_controller.allow_forgery_protection = false 26 | 27 | # Tell Action Mailer not to deliver emails to the real world. 28 | # The :test delivery method accumulates sent emails in the 29 | # ActionMailer::Base.deliveries array. 30 | config.action_mailer.delivery_method = :test 31 | 32 | # Raise exception on mass assignment protection for Active Record models 33 | config.active_record.mass_assignment_sanitizer = :strict 34 | 35 | # Print deprecation notices to the stderr 36 | config.active_support.deprecation = :stderr 37 | end 38 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | SensuAdmin::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # config.middleware.use "Rack::Bug", :secret_key => "testing123456789" 10 | 11 | # Log error messages when you accidentally call methods on nil. 12 | config.whiny_nils = true 13 | 14 | # Show full error reports and disable caching 15 | config.consider_all_requests_local = true 16 | config.action_controller.perform_caching = false 17 | 18 | # Don't care if the mailer can't send 19 | config.action_mailer.raise_delivery_errors = false 20 | config.action_mailer.default_url_options = { :host => 'localhost:3000' } 21 | 22 | # Print deprecation notices to the Rails logger 23 | config.active_support.deprecation = :log 24 | 25 | # Only use best-standards-support built into browsers 26 | config.action_dispatch.best_standards_support = :builtin 27 | 28 | # Raise exception on mass assignment protection for Active Record models 29 | config.active_record.mass_assignment_sanitizer = :strict 30 | 31 | # Log the query plan for queries taking more than this (works 32 | # with SQLite, MySQL, and PostgreSQL) 33 | config.active_record.auto_explain_threshold_in_seconds = 0.5 34 | 35 | # Do not compress assets 36 | config.assets.compress = false 37 | 38 | # Expands the lines which load the assets 39 | config.assets.debug = true 40 | end 41 | -------------------------------------------------------------------------------- /app/views/clients/index.mobile.haml: -------------------------------------------------------------------------------- 1 | %div{'data-role' => 'header'} 2 | %h1 Sensu Clients 3 | = render 'layouts/navigation' 4 | %div.ui-content{'data-role' => 'content', :role => "main"} 5 | %div.content-primary 6 | %ul{'data-theme' => 'a', 'data-count-theme' => 'a', 'data-role' => 'listview', 'data-inset' => 'true'} 7 | - if @clients.empty? 8 | %button{'data-theme' => 'c'} No Clients 9 | - @clients.each_with_index do |client, i| 10 | %li{'data-theme' => 'a', :id => "client_#{i}"} 11 | %h2= client.name 12 | - if use_environments? 13 | %p== ENV: #{client.environment} 14 | %p== Address: #{client.address} 15 | %p== Subscriptions: #{client.subscriptions.join(', ')} 16 | %p== Last checkin: #{time_ago_in_words(Time.at(client.timestamp))} 17 | %ul 18 | %li 19 | %h2= client.name 20 | - if use_environments? 21 | %p== ENV: #{client.environment} 22 | %p== Address: #{client.address} 23 | %p== Subscriptions: #{client.subscriptions.join(', ')} 24 | %p== Last checkin: #{time_ago_in_words(Time.at(client.timestamp))} 25 | %br 26 | %br 27 | %h3 JSON Attributes: 28 | - client.attributes.each do |k,v| 29 | %p== #{k} -> #{v} 30 | %li{'data-theme' => 'b'} 31 | %div{:id => "client_delete_#{i}"} 32 | %button.delete-client{:index => i, 'data-theme' => 'b', :rel => "/clients/#{client.name}"} Delete Client 33 | %div{:id => "client_deleted_#{i}", :style => "display: none;"} 34 | %button{'data-theme' => 'c'} Deleted! 35 | -------------------------------------------------------------------------------- /app/views/events/_actions.html.haml: -------------------------------------------------------------------------------- 1 | %td{:rel => "#{event.client}_popup_info", :title => "Check Silence", 'data-content' => silenced_output(check_silenced, client_silenced)} 2 | %div.btn-group 3 | %button.btn.btn-info.dropdown-toggle{:id => "#{event.client}_#{event.check}", 'data-toggle' => 'dropdown'} 4 | %div{:style => "width: 25px"} 5 | %div{:style => "float: left;"} 6 | - display_silenced(event, i, client_silenced, check_silenced) 7 | %div{:style => "float: right;"} 8 | %span.caret 9 | %ul.dropdown-menu 10 | - if client_silenced.nil? 11 | %li 12 | %a.silence-client{:id => "silence_client_#{i}", :index_id => "#{i}", :misc => "#{event.client}_#{event.check}", :href => "#"} Silence Client 13 | - else 14 | %li 15 | %a.unsilence-submit-event{:id => "unsilence_client_#{i}", :index_id => "#{i}", :misc => "#{event.client}_#{event.check}", :rel => "/events/#{event.client}/unsilence", :href => "#"} UnSilence Client 16 | - if check_silenced.nil? 17 | %li 18 | %a.silence-check{:id => "silence_check_#{i}", :index_id => "#{i}", :misc => "#{event.client}_#{event.check}", :href => "#"} Silence Check 19 | - else 20 | %li 21 | %a.unsilence-submit-event{:id => "unsilence_check_#{i}", :index_id => "#{i}", :misc => "#{event.client}_#{event.check}", :rel => "/events/#{event.client}/#{event.check}/unsilence", :href => "#"} UnSilence Check 22 | %li 23 | %a.resolve-event{:misc => "#row-event-#{i}", :index_id => "#{i}", :rel => "/events/#{event.client}/#{event.check}/resolve"} Resolve 24 | %li 25 | %a.delete-client{:misc => "#row-event-#{i}", :index_id => "#{i}", :key => "#{event.client}"} Delete Client 26 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rails', '3.2.14' 4 | 5 | # Bundle edge Rails instead: 6 | # gem 'rails', :git => 'git://github.com/rails/rails.git' 7 | 8 | gem 'sqlite3' 9 | # bundle --without mysql if using sqlite 10 | gem 'mysql2', "~> 0.3.11", :group => :mysql 11 | 12 | #Authentication Gems 13 | gem 'devise' 14 | gem 'cancan' 15 | gem 'rolify', '= 3.1' 16 | 17 | #API Gems 18 | gem 'json' 19 | gem 'rest-client' 20 | 21 | #Design gems 22 | gem 'bootstrap-sass', '= 2.1.1.0' 23 | gem 'haml-rails' 24 | gem 'bootstrap-will_paginate' 25 | gem 'formtastic' 26 | 27 | # Jquery 28 | gem 'jquery-rails' 29 | gem 'jquery-datatables-rails' 30 | 31 | # Gems used only for assets and not required 32 | # in production environments by default. 33 | group :assets do 34 | gem 'sass-rails', '~> 3.2.3' 35 | gem 'coffee-rails', '~> 3.2.1' 36 | gem 'uglifier', '>= 1.0.3' 37 | gem 'font-awesome-sass-rails' 38 | end 39 | 40 | #JS 41 | gem 'therubyracer' 42 | 43 | #scheduling gems 44 | gem 'whenever', :require => false 45 | 46 | #Servers 47 | gem 'unicorn' 48 | gem 'thin', :group => :development 49 | 50 | #testing 51 | group :test, :development do 52 | gem "fake_sensu", "0.1.4" 53 | gem "rspec-rails" 54 | gem "factory_girl_rails" 55 | gem "capybara" 56 | gem "poltergeist" 57 | gem "launchy" 58 | end 59 | 60 | group :test do 61 | gem "sinatra" 62 | end 63 | 64 | #debug 65 | #gem 'rack-bug', :git => 'https://github.com/brynary/rack-bug.git', :branch => 'rails3', :group => :development 66 | 67 | # To use ActiveModel has_secure_password 68 | # gem 'bcrypt-ruby', '~> 3.0.0' 69 | 70 | # To use Jbuilder templates for JSON 71 | # gem 'jbuilder' 72 | 73 | 74 | # Deploy with Capistrano 75 | # gem 'capistrano' 76 | 77 | # To use debugger 78 | # gem 'debugger' 79 | -------------------------------------------------------------------------------- /spec/unit/stash_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Stash do 4 | 5 | before :each do 6 | Stash.refresh_cache 7 | end 8 | 9 | after :each do 10 | reset_fake_sensu! 11 | end 12 | 13 | it "should return all stashes raw" do 14 | stashes = Stash.all 15 | stashes.should be_a Array 16 | stashes.count.should eq 2 17 | end 18 | 19 | it "should return all stashes through cache" do 20 | stashes = Stash.all_with_cache 21 | stashes.should be_a Array 22 | stashes.count.should eq 2 23 | end 24 | 25 | it "should return a list of stashes when self.stashes is called" do 26 | stashes = Stash.stashes 27 | stashes.should be_a Array 28 | stashes.count.should eq 2 29 | end 30 | 31 | it "should create a stash" do 32 | stashes = Stash.stashes 33 | stashes.count.should eq 2 34 | user = User.first 35 | stash_key = "silence/test_stash/test" 36 | attributes = { 37 | :description => "This is a test description", 38 | :owner => user.email, 39 | :timestamp => Time.now.to_i 40 | } 41 | stash = Stash.create_stash(stash_key, attributes) 42 | stash.should_not be_false 43 | stashes = Stash.all 44 | stashes.count.should eq 3 45 | end 46 | 47 | it "should delete a stash" do 48 | stashes = Stash.all 49 | stashes.count.should eq 2 50 | stash = stashes.last 51 | key = stash["path"] 52 | Stash.delete_stash(key).should_not be_false 53 | stashes = Stash.all 54 | stashes.count.should eq 1 55 | end 56 | 57 | it "should delete all stashes" do 58 | stashes = Stash.all 59 | stashes.count.should eq 2 60 | Stash.delete_all_stashes 61 | stashes = Stash.all 62 | stashes.count.should eq 0 63 | end 64 | 65 | pending "should paginate stashes (see Stash.stashes)" do 66 | end 67 | 68 | end 69 | -------------------------------------------------------------------------------- /spec/controllers/events_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe EventsController do 4 | login_user 5 | 6 | describe "GET 'index'" do 7 | it "returns http success" do 8 | get 'index' 9 | response.code.should eq "200" 10 | end 11 | end 12 | 13 | describe "GET 'events_table'" do 14 | it "returns http success" do 15 | get 'events_table', {:client => 1} 16 | response.code.should eq "406" 17 | end 18 | end 19 | 20 | describe "GET 'modal_data'" do 21 | it "returns http success" do 22 | get 'modal_data', {:client => 1} 23 | response.code.should eq "200" 24 | end 25 | end 26 | 27 | describe "GET 'resolve'" do 28 | it "returns http success" do 29 | # todo 30 | # get 'resolve', {:client => 1, :check => 1} 31 | # response.code.should eq "200" 32 | end 33 | end 34 | 35 | describe "GET 'silence_client'" do 36 | it "returns http success" do 37 | # todo 38 | # get 'silence_client', {:client =>1} 39 | # response.code.should eq "200" 40 | end 41 | end 42 | 43 | describe "GET 'silence_check'" do 44 | it "returns http success" do 45 | # todo 46 | # get 'silence_check', {:client => 1, :check => 1, :description => "this is a test description", :user => User.first} 47 | # response.code.should eq "200" 48 | end 49 | end 50 | 51 | describe "GET 'unsilence_check'" do 52 | it "returns http success" do 53 | # todo 54 | # get 'unsilence_check', {:client => 1, :check => 1} 55 | # response.code.should eq "200" 56 | end 57 | end 58 | 59 | describe "GET 'unslience_client'" do 60 | it "returns http success" do 61 | # todo 62 | # get 'unslience_client', {:client => 1} 63 | # response.code.should eq "200" 64 | end 65 | end 66 | 67 | end 68 | -------------------------------------------------------------------------------- /spec/controllers/users_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe UsersController do 4 | login_user 5 | 6 | before :each do 7 | @user = FactoryGirl.create(:user) 8 | end 9 | 10 | describe "GET 'index'" do 11 | it "returns http success" do 12 | get 'index' 13 | response.code.should eq "200" 14 | end 15 | end 16 | 17 | describe "GET 'new'" do 18 | it "returns http success" do 19 | get 'new' 20 | response.code.should eq "200" 21 | end 22 | end 23 | 24 | describe "GET 'create'" do 25 | it "returns http success" do 26 | get 'create' 27 | response.code.should eq "200" 28 | end 29 | end 30 | 31 | describe "GET 'edit'" do 32 | it "returns http success" do 33 | get 'edit', :id => @user.id 34 | response.code.should eq "200" 35 | end 36 | end 37 | 38 | describe "GET 'update_password'" do 39 | it "returns http success" do 40 | # todo 41 | # get 'update_password', {:id => @user.id, :password => "123456", :password_confirmation => "123456"} 42 | # response.code.should eq "302" 43 | end 44 | end 45 | 46 | describe "GET 'show'" do 47 | it "returns http success" do 48 | get 'show', {:id => @user.id} 49 | response.code.should eq "200" 50 | end 51 | end 52 | 53 | describe "GET 'update'" do 54 | it "returns http success" do 55 | put 'update', {:id => @user.id, :user => {:role_ids => 1}} 56 | response.code.should eq "302" 57 | end 58 | end 59 | 60 | describe "GET 'destroy'" do 61 | it "returns http success" do 62 | # TODO 63 | # get 'destroy' 64 | # response.code.should eq "200" 65 | end 66 | end 67 | 68 | describe "GET 'activate'" do 69 | it "returns http success" do 70 | get 'activate', :id => 1 71 | response.code.should eq "302" 72 | end 73 | end 74 | 75 | end 76 | -------------------------------------------------------------------------------- /app/assets/javascripts/stashes.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | $ -> 5 | $('.timepicker').timepicker({ 'step': 15, 'showDuration': true, 'timeFormat': 'G:i', 'scrollDefaultNow': true }) 6 | $('.datepicker').datepicker({ 'autoclose': true, 'dateFormat': 'd/m/yy', 'format': 'dd/mm/yyyy' }) 7 | $('.create-custom-stash-working').hide() 8 | $('.show-custom-stash-modal').click -> 9 | $('#custom_stash_modal').modal("show") 10 | $('.stash-submit-event').click -> 11 | self = $(this) 12 | $.post "/stashes/create_stash", { 'description': $('#custom_stash_description').val(), 'key': $('#custom_stash_key_input').val(), 'date': $('#custom_stash_date_input').val(), 'time': $('#custom_stash_time_input').val()}, 13 | (data) -> 14 | if data 15 | $(self).hide() 16 | $('.create-custom-stash-working').show() 17 | location.reload() 18 | else 19 | alert("Stash creation failed") 20 | $('.delete-stash').click -> 21 | self = $(this) 22 | if (confirm('Are you sure?')) 23 | $.post $(this).attr("rel"), { 'key': $(self).attr("key")}, 24 | (data) -> 25 | if data 26 | $($(self).attr("misc")).remove() 27 | $("#stash_count").html($(".table tbody tr").length) 28 | else 29 | alert("Stash deletion failed") 30 | $('#delete-all-stashes').click -> 31 | if (confirm('Are you sure?')) 32 | $('#delete-all-stashes-button').text("Working...") 33 | self = $(this) 34 | $.post $(this).attr("rel"), 35 | (data) -> 36 | if data 37 | $('#delete-all-stashes-button').text("Deleted!") 38 | $('tr').hide() 39 | $('h2').text("Stashes (0)") 40 | else 41 | alert("Stash deletion failed") 42 | -------------------------------------------------------------------------------- /app/views/layouts/application.mobile.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %meta{:charset => "utf-8"} 5 | %meta{"http-equiv" => "X-UA-Compatible", :content => "IE=edge,chrome=1"} 6 | %meta{:name => "viewport", :content => "width=device-width, initial-scale=1, maximum-scale=1"} 7 | %title= content_for?(:title) ? yield(:title) : "Sensu Admin" 8 | %meta{:content => "Sensu-admin is an admin application for viewing, silencing and resolving events from the sensu-api. In addition to exposing many other api related things.", :name => "description"} 9 | %meta{:content => "Sonian DevOps Josh Pasqualetto ", :name => "author"} 10 | %meta{:name => 'viewport', :content => "width=device-width, initial-scale=1"} 11 | = stylesheet_link_tag "mobile", :media => "all" 12 | = javascript_include_tag "mobile" 13 | = csrf_meta_tags 14 | 15 | %link{:rel => "icon", :type => "image/png", :href => "/favicon_64.png"} 16 | 17 | %link{:rel => "apple-touch-icon", :type => "image/png", :href => "/favicon_57.png"} 18 | 19 | %link{:rel => "apple-touch-icon", :type => "image/png", :href => "/favicon_114.png", :sizes => "114x114"} 20 | 21 | %link{:rel => "apple-touch-icon", :type => "image/png", :href => "/favicon_72.png", :sizes => "72x72"} 22 | 23 | %link{:rel => "apple-touch-icon", :type => "image/png", :href => "/favicon_144.png", :sizes => "144x144"} 24 | 25 | %link{:rel => "shortcut icon", :href => "/favicon.ico"} 26 | = yield(:head) 27 | %body 28 | = yield 29 | %center 30 | %div 31 | - if mobile_device? 32 | %a{:href => "/?mobile=0", "data-ajax" => "false"} Full Site 33 | - else 34 | %a{:href => "/?mobile=1", "data-ajax" => "false"} Mobile Site 35 | -------------------------------------------------------------------------------- /app/views/logs/index.html.haml: -------------------------------------------------------------------------------- 1 | - @logs.each do |log| 2 | %div{:class => "modal hide", :id => "log_modal_#{log.id}"} 3 | %div.modal-header 4 | %h3== Log Attributes for log id: #{log.id} 5 | %div.modal-body 6 | %table.table.table-striped.table-bordered 7 | %tbody 8 | - log.attributes.each do |k,v| 9 | %tr 10 | - if k == "user_id" 11 | %td User 12 | %td== #{log.user.email} (ID: #{log.user.id}) 13 | - else 14 | %td= k 15 | %td= v 16 | 17 | %table.table.table-striped 18 | %thead 19 | %th Status 20 | - if use_environments? 21 | %th ENV 22 | %th Client 23 | %th Check 24 | %th Type 25 | %th Action 26 | %th Output 27 | %th Reason 28 | %th User 29 | %th Done At 30 | %th 31 | 32 | %tbody 33 | = will_paginate @logs 34 | - @logs.each do |log| 35 | %tr{:rel => "#log_modal_#{log.id}"} 36 | %td.moreinfo 37 | - if log.silence_type == "client" 38 | %span.badge.badge-info Client 39 | - else 40 | - if log.status == "2" 41 | %span.badge.badge-important 42 | = format_status(log.status) 43 | - elsif log.status == "1" 44 | %span.badge.badge-warning 45 | = format_status(log.status) 46 | - else 47 | %span.badge.badge-inverse 48 | = format_status(log.status) 49 | - if use_environments? 50 | %td.moreinfo= log.environment 51 | %td.moreinfo= format_output(log.client, 12) 52 | %td.moreinfo= log.check 53 | %td.moreinfo= log.silence_type 54 | %td.moreinfo= log.action_type 55 | %td= format_output(log.output, 40) 56 | %td.moreinfo= format_output(log.reason, 30) 57 | %td.moreinfo= log.user.email 58 | %td.moreinfo= time_ago_in_words(log.created_at) 59 | %td.moreinfo 60 | %i.icon-zoom-in 61 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | ATTENTION: The sensu-admin project is no longer maintained. Please consider using Uchiwa or Sensu Enterprise Dashboard instead. @amdprophet 2015-12-17 2 | 3 | == Sensu Admin 4 | 5 | Sensu-admin is a web application to interface with the sensu-api, it displays 6 | events and clients and can silence events etc. 7 | 8 | == Getting Started 9 | 10 | rake db:migrate 11 | rake db:seed 12 | 13 | Make sure your post-deploy task runs "whenever --update-crontab from the rails cwd" 14 | 15 | The seed file creates a user account named admin@example.com with a password of secret. 16 | 17 | == Database 18 | 19 | sensu-admin uses the database very lightly, so you can use sqlite instead of mysql. bundling --without mysql will not require the mysql libraries. 20 | The User, Downtime and Log models are all that use the database currently so unless you have very high throughput it should result in little usage. 21 | 22 | == TODO 23 | 24 | === High Priority 25 | * Fix cookbook for deployment 26 | * Settings displaying environments, api settings 27 | * Silence events en mass 28 | 29 | === Medium Priority 30 | * Lazy load events, clients, etc in mobile 31 | * Silence all 32 | * Use sensu config for production sensu-admin -- https://github.com/sensu/sensu/blob/master/lib/sensu/settings.rb 33 | * Do not render out all modals into the html, do like you do with the main modal on events and render via ajax 34 | * Make sure that downtimes and silence events have pre defined descriptions that you can set (dropdown) 35 | * Fix bug where if a check output has no spaces it can blow out the list (because it will not wrap) 36 | * Add keepalive into downtime checks 37 | 38 | === Low Priority 39 | * Make a downtime info page 40 | * Log Downtime schedule, execution, and completion 41 | * Silence based on an attribute (es, rs) 42 | * Make the JQuery mobile version auto-update events if possible 43 | * Move mobile and desktop assets into their own directories and cleanup 44 | * Notify the user if whenever cronjobs have not run recently, namely when scheduling a downtime. 45 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the top of the 9 | * compiled file, but it's generally better to create a new file per style scope. 10 | * 11 | *= require_self 12 | *= require_tree . 13 | *= require formtastic 14 | *= require jquery.timepicker 15 | *= require bootstrap-datepicker 16 | *= require dataTables/jquery.dataTables.bootstrap 17 | */ 18 | 19 | 20 | .content { 21 | background-color: #eee; 22 | padding: 20px; 23 | margin: 0 -20px; /* negative indent the amount of the padding to maintain the grid system */ 24 | -webkit-border-radius: 0 0 6px 6px; 25 | -moz-border-radius: 0 0 6px 6px; 26 | border-radius: 0 0 6px 6px; 27 | -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.15); 28 | -moz-box-shadow: 0 1px 2px rgba(0,0,0,.15); 29 | box-shadow: 0 1px 2px rgba(0,0,0,.15); 30 | } 31 | 32 | table.table thead .sorting, 33 | table.table thead .sorting_asc, 34 | table.table thead .sorting_desc, 35 | table.table thead .sorting_asc_disabled, 36 | table.table thead .sorting_desc_disabled { 37 | cursor: pointer; 38 | *cursor: hand; 39 | } 40 | 41 | table.table thead .sorting { background: image-url('sort_both.png') no-repeat center right; } 42 | table.table thead .sorting_asc { background: image-url('sort_asc.png') no-repeat center right; } 43 | table.table thead .sorting_desc { background: image-url('sort_desc.png') no-repeat center right; } 44 | 45 | table.table thead .sorting_asc_disabled { background: image-url('sort_asc_disabled.png') no-repeat center right; } 46 | table.table thead .sorting_desc_disabled { background: image-url('sort_desc_disabled.png') no-repeat center right; } 47 | -------------------------------------------------------------------------------- /db/migrate/20120807181910_add_devise_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddDeviseToUsers < ActiveRecord::Migration 2 | def self.up 3 | change_table(:users) do |t| 4 | ## Database authenticatable 5 | t.string :email, :null => false, :default => "" 6 | t.string :encrypted_password, :null => false, :default => "" 7 | 8 | ## Recoverable 9 | t.string :reset_password_token 10 | t.datetime :reset_password_sent_at 11 | 12 | ## Rememberable 13 | t.datetime :remember_created_at 14 | 15 | ## Trackable 16 | t.integer :sign_in_count, :default => 0 17 | t.datetime :current_sign_in_at 18 | t.datetime :last_sign_in_at 19 | t.string :current_sign_in_ip 20 | t.string :last_sign_in_ip 21 | 22 | ## Confirmable 23 | # t.string :confirmation_token 24 | # t.datetime :confirmed_at 25 | # t.datetime :confirmation_sent_at 26 | # t.string :unconfirmed_email # Only if using reconfirmable 27 | 28 | ## Lockable 29 | # t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts 30 | # t.string :unlock_token # Only if unlock strategy is :email or :both 31 | # t.datetime :locked_at 32 | 33 | ## Token authenticatable 34 | # t.string :authentication_token 35 | 36 | 37 | # Uncomment below if timestamps were not included in your original model. 38 | # t.timestamps 39 | end 40 | 41 | add_index :users, :email, :unique => true 42 | add_index :users, :reset_password_token, :unique => true 43 | # add_index :users, :confirmation_token, :unique => true 44 | # add_index :users, :unlock_token, :unique => true 45 | # add_index :users, :authentication_token, :unique => true 46 | end 47 | 48 | def self.down 49 | # By default, we don't want to make any assumption about how to roll back a migration when your 50 | # model already existed. Please edit below which fields you would like to remove in this migration. 51 | raise ActiveRecord::IrreversibleMigration 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /app/views/stashes/index.html.haml: -------------------------------------------------------------------------------- 1 | %div{:class => "small-modal modal hide", :id => "custom_stash_modal"} 2 | %div.modal-header 3 | %h3== Create stash 4 | %div.modal-body 5 | %br 6 | %h3 Enter key 7 | %input.input-xlarge{:id => "custom_stash_key_input", :placeholder => "Stash key"} 8 | %p Note: No spaces, etc e.g. "silence/client_name/check_name" 9 | %br 10 | %br 11 | %h3 Description 12 | %textarea.input-xlarge{:id => "custom_stash_description", :placeholder => "Stash description", :rows => "2"} 13 | %br 14 | %br 15 | %br 16 | %h3 Expire at (Optional): 17 | %div 18 | %div.pull-left 19 | %p Date: 20 | %input.datepicker{:id => "custom_stash_date_input"} 21 | %br 22 | %br 23 | %br 24 | %br 25 | %div.pull-left 26 | %p Time: 27 | %input.timepicker{:id => "custom_stash_time_input"} 28 | %div{:style => "height: 160px;"} 29 | 30 | %div.modal-footer 31 | %a.stash-submit-event.btn.btn-info Submit 32 | %button.btn.btn-success.create-custom-stash-working 33 | Working... 34 | 35 | %br 36 | %br 37 | %div 38 | %div.btn-group.pull-right 39 | %button.btn.btn-danger.dropdown-toggle{:id => "delete-all-stashes-button", 'data-toggle' => 'dropdown'} 40 | Delete All Stashes 41 | %span.caret 42 | %ul.dropdown-menu 43 | %li 44 | %a{:id => 'delete-all-stashes', :rel => "/stashes/delete_all_stashes", :href => "#"} Yes, Nuke it 45 | %div.btn-group.pull-right 46 | %button.btn.btn-info.show-custom-stash-modal{:style => "margin-right: 10px;"} 47 | Create Stash 48 | %br 49 | %br 50 | 51 | 52 | %h2== Stashes (#{@stashes.count}) 53 | %table.table.table-striped 54 | %thead 55 | %th.span5 Key 56 | %th.span4 Description 57 | %th Owner 58 | %th Expires in 59 | %th Set 60 | %th Action 61 | 62 | %tbody 63 | - if Gem::Version.new(@sensu_version) <= Gem::Version.new("0.9.11") 64 | - @stashes.each_with_index do |(k,v), i| 65 | = render "stash_row_legacy", :k => k, :v => v, :i => i 66 | - else 67 | - @stashes.each do |stash| 68 | = render "stash_row", :path => stash['path'], :content => stash['content'] 69 | -------------------------------------------------------------------------------- /spec/controllers/downtimes_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe DowntimesController do 4 | login_user 5 | 6 | before :all do 7 | @downtime = Downtime.create(:name => "test_downtime", :user_id => 1, :start_time => Time.now, :stop_time => Time.now + 1.hour) 8 | end 9 | 10 | describe "GET 'index'" do 11 | it "returns http success" do 12 | get 'index' 13 | response.code.should eq "200" 14 | end 15 | end 16 | 17 | describe "GET 'new'" do 18 | it "returns http success" do 19 | # get 'new' 20 | # response.code.should eq "200" 21 | end 22 | end 23 | 24 | describe "GET 'create'" do 25 | it "returns http success" do 26 | # get 'create' 27 | # response.code.should eq "200" 28 | end 29 | end 30 | 31 | describe "GET 'old_downtimes'" do 32 | it "returns http success" do 33 | get 'old_downtimes' 34 | response.code.should eq "200" 35 | end 36 | end 37 | 38 | describe "GET 'show'" do 39 | it "returns http success" do 40 | # get 'show' 41 | # response.code.should eq "200" 42 | end 43 | end 44 | 45 | describe "GET 'edit'" do 46 | it "returns http success" do 47 | get 'edit', {:id => 1} 48 | response.code.should eq "200" 49 | end 50 | end 51 | 52 | describe "GET 'update'" do 53 | it "returns http success" do 54 | downtime = FactoryGirl.create(:downtime) 55 | downtime_client = FactoryGirl.build(:downtime_client) 56 | downtime_client.downtime_id = downtime.id 57 | downtime_client.save 58 | setting = FactoryGirl.create(:setting) 59 | # get 'update', {:id => downtime.id} 60 | # response.code.should eq "200" 61 | end 62 | end 63 | 64 | describe "GET 'force_complete'" do 65 | it "returns http success" do 66 | # todo 67 | # get 'force_complete', {:id => 1, :downtime_id => 1} 68 | # response.code.should eq "200" 69 | end 70 | end 71 | 72 | describe "GET 'destroy'" do 73 | context "when admin is not logged in" do 74 | it "returns http success" do 75 | 76 | get 'destroy', {:id => 1, :downtime_id => 1} 77 | response.code.should eq "302" 78 | end 79 | end 80 | end 81 | 82 | end 83 | -------------------------------------------------------------------------------- /lib/resting.rb: -------------------------------------------------------------------------------- 1 | class Resting 2 | 3 | def initialize(json) 4 | json.each do |k, v| 5 | instance_variable_set("@#{k}", v) 6 | self.class.send(:attr_reader, k) 7 | end 8 | end 9 | 10 | def attributes 11 | attribs = {} 12 | self.instance_variables.each do |v| 13 | attribs.merge!({v.to_s[1..-1] => self.instance_variable_get(v)}) 14 | end 15 | attribs 16 | end 17 | 18 | def self.all 19 | get(collection_name) 20 | end 21 | 22 | def self.all_raw 23 | get(collection_name, true) 24 | end 25 | 26 | def self.find(id) 27 | get(collection_name + "/#{id}") 28 | end 29 | 30 | def self.create(path, attributes = {}) 31 | post(singular_name + "/#{path}", attributes) 32 | end 33 | 34 | # 35 | # For singular destruction where you want the path based off the class 36 | # 37 | def self.destroy(path) 38 | delete(singular_name + "/#{path}") 39 | end 40 | 41 | def delete(path) 42 | self.class.delete(path) 43 | end 44 | 45 | protected 46 | 47 | class << self 48 | def collection_name 49 | self.name.downcase.pluralize 50 | end 51 | 52 | def singular_name 53 | self.name.downcase 54 | end 55 | 56 | def delete(path) 57 | begin 58 | RestClient.delete "#{Setting.api_server}/#{path}" 59 | rescue RestClient::ResourceNotFound 60 | false 61 | end 62 | end 63 | 64 | def put(path, attributes = {}) 65 | begin 66 | RestClient.put "#{Setting.api_server}/#{path}", attributes.to_json 67 | rescue RestClient::ResourceNotFound 68 | false 69 | end 70 | end 71 | 72 | def post(path, attributes = {}) 73 | begin 74 | RestClient.post "#{Setting.api_server}/#{path}", attributes.to_json 75 | rescue RestClient::ResourceNotFound 76 | false 77 | end 78 | end 79 | 80 | def get(path, raw = false) 81 | begin 82 | data = JSON.parse(RestClient.get "#{Setting.api_server}/#{path}") 83 | if raw 84 | data 85 | else 86 | case data 87 | when Array 88 | data.collect{|j| self.new(j)} 89 | when Hash 90 | self.new(data) 91 | end 92 | end 93 | rescue RestClient::ResourceNotFound 94 | false 95 | end 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /app/models/stash.rb: -------------------------------------------------------------------------------- 1 | class Stash < Resting 2 | 3 | def self.all 4 | self.all_raw 5 | end 6 | 7 | def self.all_with_cache 8 | Rails.cache.fetch("stashes", :expires_in => 30.seconds, :race_condition_ttl => 10) do 9 | self.all 10 | end 11 | end 12 | 13 | def self.refresh_cache 14 | Rails.cache.write("stashes", self.all, :expires_in => 30.seconds, :race_condition_ttl => 10) 15 | Rails.cache.delete("all_stashes") 16 | end 17 | 18 | def self.stashes 19 | api = Api.status 20 | sensu_version = api.sensu['version'] 21 | Rails.cache.fetch("all_stashes", :expires_in => 30.seconds, :race_condition_ttl => 10) do 22 | stashes = Stash.all 23 | return [] if stashes.empty? 24 | # 25 | # API Blows up with a few thousand objects so lets chunk it 26 | # 27 | if Gem::Version.new(sensu_version) <= Gem::Version.new("0.9.11") 28 | stash_response = {} 29 | if stashes.count > 201 30 | while !stashes.empty? 31 | stash_post = stashes.slice!(0..200) 32 | stsh = post("stashes", stash_post) 33 | stash_response.merge!(JSON.parse(stsh.body).to_hash) 34 | end 35 | else 36 | stsh = post("stashes", stashes) 37 | stash_response = JSON.parse(stsh.body).to_hash 38 | end 39 | stash_response 40 | else 41 | stashes 42 | end 43 | end 44 | end 45 | 46 | def self.create_stash(key, attributes) 47 | api = Api.status 48 | sensu_version = api.sensu['version'] 49 | if Gem::Version.new(sensu_version) <= Gem::Version.new("0.9.11") 50 | post("stash/#{key}", attributes) 51 | else 52 | post("stashes", { 53 | :path => key, 54 | :content => attributes 55 | }) 56 | end 57 | end 58 | 59 | def self.delete_stash(key) 60 | destroy(key) 61 | end 62 | 63 | def self.delete_all_stashes 64 | Stash.all.each do |v| 65 | destroy(v['path']) 66 | end 67 | true 68 | end 69 | 70 | def self.clear_expired_stashes 71 | Stash.stashes.each do |v| 72 | unless v['content']['expire_at'].nil? 73 | if Time.parse(v['content']['expire_at']) < Time.now 74 | puts "Clearing stash #{v['path']} due to expiration." 75 | destroy(v['path']) 76 | end 77 | end 78 | end and true 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | ENV["RAILS_ENV"] ||= 'test' 3 | require File.expand_path("../../config/environment", __FILE__) 4 | require 'rspec/rails' 5 | require 'rspec/autorun' 6 | require 'capybara/rspec' 7 | require 'capybara/poltergeist' 8 | require 'fake_sensu' 9 | include ActionView::Helpers::DateHelper 10 | 11 | Capybara.configure do |config| 12 | config.ignore_hidden_elements = true 13 | end 14 | Capybara.javascript_driver = :poltergeist 15 | 16 | # Requires supporting ruby files with custom matchers and macros, etc, 17 | # in spec/support/ and its subdirectories. 18 | Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} 19 | 20 | # seed database 21 | 22 | RSpec.configure do |config| 23 | # ## Mock Framework 24 | # 25 | # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: 26 | # 27 | # config.mock_with :mocha 28 | # config.mock_with :flexmock 29 | # config.mock_with :rr 30 | 31 | RSpec.configure do |config| 32 | config.before :suite do 33 | FakeSensu.start! "0.10.2" 34 | end 35 | config.before :all do 36 | load "#{Rails.root}/db/seeds.rb" 37 | end 38 | end 39 | 40 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 41 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 42 | 43 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 44 | # examples within a transaction, remove the following line or assign false 45 | # instead of true. 46 | config.use_transactional_fixtures = true 47 | 48 | # If true, the base class of anonymous controllers will be inferred 49 | # automatically. This will be the default behavior in future versions of 50 | # rspec-rails. 51 | config.infer_base_class_for_anonymous_controllers = false 52 | 53 | # Run specs in random order to surface order dependencies. If you find an 54 | # order dependency and want to debug it, you can fix the order by providing 55 | # the seed, which is printed after each run. 56 | # --seed 1234 57 | config.order = "random" 58 | 59 | # include controller test helpers 60 | config.include FakeSensuMacros 61 | config.include Devise::TestHelpers, :type => :controller 62 | config.include DeviseMacros, :type => :feature 63 | config.extend ControllerMacros, :type => :controller 64 | 65 | end 66 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_filter :find_user, :except => [ :index, :new, :create, :update_password, :update] 3 | before_filter :authenticate_user! 4 | 5 | def index 6 | @active_users = User.active 7 | @deactivated_users = User.deactivated 8 | end 9 | 10 | def new 11 | authorize! :manage, @user 12 | @user = User.new 13 | end 14 | 15 | def create 16 | authorize! :manage, @user 17 | @user = User.new(params[:user]) 18 | if @user.save 19 | redirect_to(@user, :notice => "User successfully created") 20 | else 21 | render(:new) 22 | end 23 | end 24 | 25 | def edit 26 | if current_user.has_role? :admin 27 | @user = User.find(params[:id]) 28 | else 29 | @user = current_user 30 | end 31 | end 32 | 33 | def update_password 34 | if current_user.has_role? :admin 35 | @user = User.find(params[:id]) 36 | else 37 | @user = current_user 38 | end 39 | 40 | if @user.attempt_set_password(params[:user]) 41 | sign_in(current_user, :bypass => true) 42 | redirect_to edit_user_path(@user), :notice => "Password Updated!" 43 | else 44 | render :edit 45 | end 46 | end 47 | 48 | def show 49 | end 50 | 51 | def update 52 | if current_user.has_role? :admin 53 | @user = User.find(params[:id]) 54 | else 55 | @user = current_user 56 | end 57 | 58 | unless params[:user][:role_ids].nil? 59 | unless current_user.has_role? :admin 60 | params[:user].delete(:role_ids) 61 | end 62 | end 63 | 64 | if @user.update_attributes(params[:user]) 65 | sign_in(current_user, :bypass => true) 66 | redirect_to edit_user_path(@user), :notice => "Updated!" 67 | else 68 | redirect_to edit_user_path(@user), :alert => "Password could not be updated, do they match?" 69 | end 70 | end 71 | 72 | 73 | def destroy 74 | find_user 75 | @user.deleted_at = Time.now 76 | @user.save! 77 | redirect_to users_path, :notice => "User successfully marked as deactivated" 78 | end 79 | 80 | def activate 81 | @user.deleted_at = nil 82 | @user.save! 83 | redirect_to users_path, :notice => "User successfully marked as activated" 84 | end 85 | 86 | private 87 | def find_user 88 | @user = User.find(params[:id]) 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %meta{:charset => "utf-8"} 5 | %meta{"http-equiv" => "X-UA-Compatible", :content => "IE=edge,chrome=1"} 6 | %meta{:name => "viewport", :content => "width=device-width, initial-scale=1, maximum-scale=1"} 7 | %title= content_for?(:title) ? yield(:title) : "Sensu Admin" 8 | %meta{:content => "Sensu-admin is an admin application for viewing, silencing and resolving events from the sensu-api. In addition to exposing many other api related things.", :name => "description"} 9 | %meta{:content => "Sonian DevOps Josh Pasqualetto ", :name => "author"} 10 | = stylesheet_link_tag "application", :media => "all" 11 | = javascript_include_tag "application" 12 | = csrf_meta_tags 13 | 14 | %link{:rel => "icon", :type => "image/png", :href => "/favicon_64.png"} 15 | 16 | %link{:rel => "apple-touch-icon", :type => "image/png", :href => "/favicon_57.png"} 17 | 18 | %link{:rel => "apple-touch-icon", :type => "image/png", :href => "/favicon_114.png", :sizes => "114x114"} 19 | 20 | %link{:rel => "apple-touch-icon", :type => "image/png", :href => "/favicon_72.png", :sizes => "72x72"} 21 | 22 | %link{:rel => "apple-touch-icon", :type => "image/png", :href => "/favicon_144.png", :sizes => "144x144"} 23 | 24 | %link{:rel => "shortcut icon", :href => "/favicon.ico"} 25 | = yield(:head) 26 | %body 27 | %header.navbar.navbar-fixed-top 28 | %nav.navbar-inner 29 | .container 30 | = render 'layouts/navigation' 31 | #main{:role => "main"} 32 | .container 33 | .content 34 | .row 35 | .span12 36 | = render 'layouts/messages' 37 | = yield 38 | %footer 39 | %center 40 | - if user_signed_in? 41 | %div{:id => "api_info_div", :style => "height: 20px; margin-top: 10px"} 42 | %div 43 | - if mobile_device? 44 | %a{:href => "/?mobile=0", "data-ajax" => "false"} Full Site 45 | - else 46 | %a{:href => "/?mobile=1", "data-ajax" => "false"} Mobile Site 47 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | SensuAdmin::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # Code is not reloaded between requests 5 | config.cache_classes = true 6 | 7 | # Full error reports are disabled and caching is turned on 8 | config.consider_all_requests_local = false 9 | config.action_controller.perform_caching = true 10 | 11 | # Disable Rails's static asset server (Apache or nginx will already do this) 12 | config.serve_static_assets = true 13 | 14 | # Compress JavaScripts and CSS 15 | config.assets.compress = true 16 | 17 | # Don't fallback to assets pipeline if a precompiled asset is missed 18 | config.assets.compile = false 19 | 20 | # Generate digests for assets URLs 21 | config.assets.digest = true 22 | 23 | # Defaults to nil and saved in location specified by config.assets.prefix 24 | # config.assets.manifest = YOUR_PATH 25 | 26 | # Specifies the header that your server uses for sending files 27 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache 28 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx 29 | 30 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 31 | # config.force_ssl = true 32 | 33 | # See everything in the log (default is :info) 34 | # config.log_level = :debug 35 | 36 | # Prepend all log lines with the following tags 37 | # config.log_tags = [ :subdomain, :uuid ] 38 | 39 | # Use a different logger for distributed setups 40 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 41 | 42 | # Use a different cache store in production 43 | # config.cache_store = :mem_cache_store 44 | 45 | # Enable serving of images, stylesheets, and JavaScripts from an asset server 46 | # config.action_controller.asset_host = "http://assets.example.com" 47 | 48 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) 49 | # config.assets.precompile += %w( search.js ) 50 | 51 | # Disable delivery errors, bad email addresses will be ignored 52 | # config.action_mailer.raise_delivery_errors = false 53 | 54 | # Enable threaded mode 55 | # config.threadsafe! 56 | 57 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 58 | # the I18n.default_locale when a translation can not be found) 59 | config.i18n.fallbacks = true 60 | 61 | # Send deprecation notices to registered listeners 62 | config.active_support.deprecation = :notify 63 | 64 | # Log the query plan for queries taking more than this (works 65 | # with SQLite, MySQL, and PostgreSQL) 66 | # config.active_record.auto_explain_threshold_in_seconds = 0.5 67 | end 68 | -------------------------------------------------------------------------------- /app/views/events/_modal_data.html.haml: -------------------------------------------------------------------------------- 1 | %div.modal-header 2 | - if use_environments? 3 | %h3== Client: #{event.client} (#{event.client_attributes['address']}) in #{event.client_attributes['environment']} 4 | - else 5 | %h3== Client: #{event.client} (#{event.client_attributes['address']}) 6 | %h3== Check: #{event.check} 7 | %div.modal-body.modal-data 8 | %h3 Check Output 9 | %table.table.table-striped.table-bordered 10 | %tbody 11 | %tr 12 | %td Output 13 | %td= event.output 14 | %tr 15 | %td Occurrences 16 | %td= event.occurrences 17 | %tr 18 | %td Status 19 | %td= format_status(event.status) 20 | %tr 21 | %td Occured 22 | %td= Time.at(event.issued) 23 | %tr 24 | %td Flapping 25 | %td= event.flapping 26 | %tr 27 | %td Check Silenced 28 | %td{:rel => "#{event.client}_#{event.check}_column_silenced"}= ((event.check_silenced.nil?) ? "No" : Time.at(event.check_silenced["content"]["timestamp"])) 29 | %tr 30 | %td Client Silenced 31 | %td{:rel => "#{event.client}_column_silenced"}= ((event.client_silenced.nil?) ? "No" : Time.at(event.client_silenced["content"]["timestamp"])) 32 | %br 33 | %br 34 | %h3 Check Attributes 35 | %table.table.table-striped.table-bordered 36 | %tbody 37 | - event.check_attributes.sort.each do |k,v| 38 | %tr 39 | %td= k 40 | %td= anchor_wrap_if_url(v) 41 | %br 42 | %br 43 | %h3 Client Attributes 44 | %table.table.table-striped.table-bordered 45 | %tbody 46 | - event.client_attributes.sort.each do |k,v| 47 | %tr 48 | %td= k 49 | %td= k == "timestamp" ? Time.at(v) : v 50 | %div.modal-footer{:style => "width: 100%; height: 10%; margin-left: -3%;"} 51 | / 52 | / TODO: This needs to be moved into partials probably and remove a disturbing number of attribs 53 | / 54 | - if event.check_silenced.nil? 55 | %a.silence-check.btn.btn-primary{:control => "silence_#{event.client}_#{event.check}", :index_id => "#{i}", :misc => "#{event.client}_#{event.check}"} Silence Check 56 | - else 57 | %a.unsilence-submit-event.btn.btn-primary{:control => "unsilence_#{event.client}_#{event.check}", :index_id => "#{i}", :misc => "#{event.client}_#{event.check}", :rel => "/events/#{event.client}/#{event.check}/unsilence"} Unsilence Check 58 | - if event.client_silenced.nil? 59 | %a.silence-client.btn.btn-primary{:control => "silence_#{event.client}", :index_id => "#{i}", :misc => "#{event.client}_#{event.check}"} Silence Client 60 | - else 61 | %a.unsilence-submit-event.btn.btn-primary{:control => "unsilence_#{event.client}", :index_id => "#{i}", :misc => "#{event.client}", :rel => "/events/#{event.client}/unsilence"} Unsilence Client 62 | %a.resolve-event.btn.btn-success{:rel => "/events/#{event.client}/#{event.check}/resolve"} Resolve 63 | -------------------------------------------------------------------------------- /app/views/stats/index.html.haml: -------------------------------------------------------------------------------- 1 | %h1 Stats 2 | %br 3 | %br 4 | %h2 Clients 5 | .row 6 | .span3 7 | %p By Subscriptions: 8 | %table.table.table-striped.table-bordered#clients-by-subscriptions 9 | %thead 10 | %th Name 11 | %th Count 12 | %tbody 13 | - @clients_by_subscription.sort{|l,r| r[1] <=> l[1]}.each do |k,v| 14 | %tr 15 | %td= k 16 | %td= v 17 | .span3 18 | %p By Environment: 19 | %table.table.table-striped.table-bordered#clients-by-environment 20 | %thead 21 | %th Name 22 | %th Count 23 | %tbody 24 | - value_total = 0 25 | - @clients_by_environment.sort{|l,r| r[1] <=> l[1]}.each do |k,v| 26 | - value_total += v 27 | %tr 28 | %td= k 29 | %td= v 30 | %tr 31 | %td Total 32 | %td= value_total 33 | .span5 34 | %p Misc stats 35 | %table.table.table-striped.table-bordered#clients-misc 36 | %thead 37 | %th Name 38 | %th Count 39 | %tbody 40 | %tr 41 | %td Total Clients 42 | %td= @clients.count 43 | %tr 44 | %td Total Subscriptions 45 | %td= @clients_by_subscription.count 46 | %tr 47 | %td Total Environments 48 | %td= @clients_by_environment.count 49 | 50 | %br 51 | %br 52 | %br 53 | %h2 Events 54 | .row 55 | .span3 56 | %p By Check: 57 | %table.table.table-striped.table-bordered#events-by-check 58 | %thead 59 | %th Name 60 | %th Count 61 | %tbody 62 | - @events_by_check.sort{|l,r| r[1] <=> l[1]}.each do |k,v| 63 | %tr 64 | %td= k 65 | %td= v 66 | .span2 67 | %p By Environment: 68 | %table.table.table-striped.table-bordered#events-by-environment 69 | %thead 70 | %th Name 71 | %th Count 72 | %tbody 73 | - total_events = 0 74 | - @events_by_environment.sort{|l,r| r[1] <=> l[1]}.each do |k,v| 75 | - total_events += v 76 | %tr 77 | %td= k 78 | %td= v 79 | %tr 80 | %td Total 81 | %td= total_events 82 | .span4 83 | %p Events per client 84 | %table.table.table-striped.table-bordered#events-per-client 85 | %thead 86 | %th Client 87 | %th Count 88 | %tbody 89 | - @events_per_client.sort{|l,r| r[1] <=> l[1]}.each do |k,v| 90 | %tr 91 | %td= k 92 | %td= v 93 | .span3 94 | %p Misc stats 95 | %table.table.table-striped.table-bordered#events-misc 96 | %thead 97 | %th Name 98 | %th Count 99 | %tbody 100 | %tr 101 | %td Total Events 102 | %td= @events.count 103 | %tr 104 | %td Total Environments 105 | %td= @events_by_environment.count 106 | -------------------------------------------------------------------------------- /app/views/events/_modals.html.haml: -------------------------------------------------------------------------------- 1 | %div{:class => "modal large-modal hide", :id => "event-data-modal"} 2 | 3 | - @events.each_with_index do |event,i| 4 | - processed_clients = [] 5 | - len = Setting.min_desc_length.to_i 6 | %div{:class => "modal small-modal hide", :id => "modal_#{event.client}_#{event.check}"} 7 | %div.modal-header 8 | %h3== Silence check #{event.check} on client #{event.client} 9 | %div.modal-body 10 | %p.pull-left Note: Your event handler must use sensu-plugin or hit the API to make use of silencing. 11 | %br 12 | %h3 Enter reason 13 | %textarea.input-xlarge.silence-input{:id => "text_input_#{event.client}_#{event.check}", :index_id => "#{i}", :data => {:min => len}, :misc => "#{event.client}_#{event.check}", :rel => "Silence Check", :rows => "3", :type => "text", :placeholder => "Silence Check Reason"} 14 | %br 15 | %h3 Expire at (Optional): 16 | %div 17 | %p Date: 18 | %input.datepicker{:id => "silence_expire_at_date_#{event.client}_#{event.check}"} 19 | %div 20 | %p Time: 21 | %input.timepicker{:id => "silence_expire_at_time_#{event.client}_#{event.check}"} 22 | %div.modal-footer 23 | - if len > 0 24 | %a.silence-submit-event.btn.btn-inverse{:control => "silence_submit_#{event.client}_#{event.check}", :index_id => "#{i}", :misc => "#{event.client}_#{event.check}", :rel => "/events/#{event.client}/#{event.check}/silence"} Submit 25 | - else 26 | %a.silence-submit-event.btn.btn-success{:control => "silence_submit_#{event.client}_#{event.check}", :index_id => "#{i}", :misc => "#{event.client}_#{event.check}", :rel => "/events/#{event.client}/#{event.check}/silence"} Submit 27 | 28 | - unless processed_clients.include?(event.client) 29 | - processed_clients << event.client 30 | %div{:class => "small-modal modal hide", :id => "modal_#{event.client}"} 31 | %div.modal-header 32 | %h3== Silence Client #{event.client} 33 | %div.modal-body 34 | %p.pull-left Note: Your event handler must use sensu-plugin or hit the API to make use of silencing. 35 | %br 36 | %h3 Enter reason 37 | %textarea.input-xlarge.silence-input{:id => "text_input_#{event.client}", :index_id => "#{i}", :data => {:min => len}, :misc => "#{event.client}", :rel => "Silence Check", :rows => "3", :type => "text", :placeholder => "Silence Client Reason"} 38 | %br 39 | %h3 Expire at (Optional): 40 | %div 41 | %p Date: 42 | %input.datepicker{:id => "silence_expire_at_date_#{event.client}"} 43 | %div 44 | %p Time: 45 | %input.timepicker{:id => "silence_expire_at_time_#{event.client}"} 46 | %div.modal-footer 47 | %a.silence-submit-event.btn.btn-success{:control => "silence_submit_#{event.client}", :index_id => "#{i}", :misc => "#{event.client}", :rel => "/events/#{event.client}/silence"} Submit 48 | - if len > 0 49 | %a.silence-submit-event.btn.btn-inverse{:control => "silence_grey_submit_#{event.client}", :index_id => "#{i}", :misc => "#{event.client}"} Submit 50 | -------------------------------------------------------------------------------- /app/views/aggregates/index.html.haml: -------------------------------------------------------------------------------- 1 | - if @aggregates == false 2 | %p It does not appear that your sensu-api or sensu itself supports Aggregates. Please run sensu > 0.9.8.beta if you want Aggregate support. 3 | - elsif @aggregates.empty? 4 | %p No aggregates available for display. 5 | - else 6 | - @aggregates.each do |k,v| 7 | - v.each do |i,iv| 8 | %div.modal{:id => "aggregate_modal_#{k}_#{i}", :style => "display: none;"} 9 | %div.modal-header 10 | %h3== Aggregate Summary #{k} - #{i} 11 | %div.modal-body 12 | %p== Summary output for check #{k} issued at #{i} 13 | %br 14 | %table.table.table-striped.table-bordered.table-condensed 15 | %thead 16 | %th Issued 17 | %th Ok 18 | %th Warning 19 | %th Critical 20 | %th Unknown 21 | %th Total 22 | %th Timestamp 23 | %tbody 24 | %tr 25 | %td= i 26 | %td= iv['ok'] 27 | %td= iv['warning'] 28 | %td= iv['critical'] 29 | %td= iv['unknown'] 30 | %td= iv['total'] 31 | %td= Time.at(i.to_i) 32 | 33 | %br 34 | %br 35 | %h3 Output 36 | %table.table.table-condensed 37 | %thead 38 | %th # 39 | %th Result 40 | %tbody 41 | - output = iv['outputs'].sort{|a,b| b[1].to_i <=> a[1].to_i} 42 | - output.each do |ok, ov| 43 | %tr 44 | %td= ov 45 | %td= ok 46 | %div.accordion.span8{:id => "aggregate_accordion"} 47 | - @aggregates.each do |k,v| 48 | %div.row 49 | %div.span8 50 | %div.accordion-group 51 | %div.accordion-heading 52 | %a.accordion-toggle{:href => "#collapse_#{k}", 'data-toggle' => 'collapse', 'data-parent' => '#aggregate_accordion'} 53 | = k 54 | %i.icon-plus{:id => "icon_toggle_#{k}"} 55 | %div.accordion-body.collapse{:rel => "#{k}", :id => "collapse_#{k}"} 56 | %div.accordion-inner 57 | %table.table.table-striped.table-bordered.table-condensed 58 | %thead 59 | %th Issued 60 | %th Ok 61 | %th Warning 62 | %th Critical 63 | %th Unknown 64 | %th Total 65 | %th Timestamp 66 | %tbody 67 | - v.each do |i, iv| 68 | %tr 69 | %td 70 | %a.modal-for-aggregate-display{:href => "#show_aggregate_modal_#{k}_#{i}", :misc => "#{k}_#{i}"} 71 | = i 72 | %td= iv['ok'] 73 | %td= iv['warning'] 74 | %td= iv['critical'] 75 | %td= iv['unknown'] 76 | %td= iv['total'] 77 | %td= Time.at(i.to_i) 78 | %br 79 | %br 80 | %br 81 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | if defined?(Bundler) 6 | # If you precompile assets before deploying to production, use this line 7 | Bundler.require(*Rails.groups(:assets => %w(development test))) 8 | # If you want your assets lazily compiled in production, use this line 9 | # Bundler.require(:default, :assets, Rails.env) 10 | end 11 | 12 | module SensuAdmin 13 | class Application < Rails::Application 14 | # Settings in config/environments/* take precedence over those specified here. 15 | # Application configuration should go into files in config/initializers 16 | # -- all .rb files in that directory are automatically loaded. 17 | 18 | # Custom directories with classes and modules you want to be autoloadable. 19 | config.autoload_paths += %W(#{config.root}/lib) 20 | 21 | # Precompile mobile assets 22 | config.assets.precompile += ['mobile.css', 'mobile.js'] 23 | 24 | # Only load the plugins named here, in the order given (default is alphabetical). 25 | # :all can be used as a placeholder for all plugins not explicitly named. 26 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 27 | 28 | # Activate observers that should always be running. 29 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer 30 | 31 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 32 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 33 | # config.time_zone = 'Central Time (US & Canada)' 34 | 35 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 36 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 37 | # config.i18n.default_locale = :de 38 | 39 | # Configure the default encoding used in templates for Ruby 1.9. 40 | config.encoding = "utf-8" 41 | 42 | config.cache_store = :memory_store 43 | 44 | # Configure sensitive parameters which will be filtered from the log file. 45 | config.filter_parameters += [:password] 46 | 47 | # Enable escaping HTML in JSON. 48 | config.active_support.escape_html_entities_in_json = true 49 | 50 | # Use SQL instead of Active Record's schema dumper when creating the database. 51 | # This is necessary if your schema can't be completely dumped by the schema dumper, 52 | # like if you have constraints or database-specific column types 53 | # config.active_record.schema_format = :sql 54 | 55 | # Enforce whitelist mode for mass assignment. 56 | # This will create an empty whitelist of attributes available for mass-assignment for all models 57 | # in your app. As such, your models will need to explicitly whitelist or blacklist accessible 58 | # parameters by using an attr_accessible or attr_protected declaration. 59 | config.active_record.whitelist_attributes = true 60 | 61 | # Enable the asset pipeline 62 | config.assets.enabled = true 63 | 64 | # Version of your assets, change this if you want to expire all your assets 65 | config.assets.version = '1.0' 66 | 67 | config.generators do |g| 68 | g.test_framework nil 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /spec/unit/event_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Event do 4 | 5 | before :each do 6 | Event.refresh_cache 7 | end 8 | 9 | after :each do 10 | reset_fake_sensu! 11 | end 12 | 13 | it "should return all checks through cache" do 14 | events = Event.all_with_cache 15 | events.count.should eq 4 16 | end 17 | 18 | it "should resolve an event" do 19 | events = Event.all 20 | events.count.should eq 4 21 | event = events[rand(events.length)] 22 | event.resolve.should be_true 23 | # TODO: would be nice if fake_sensu would delete, then switch back after one 24 | # GET /events 25 | end 26 | 27 | it "should return a single event" do 28 | event = Event.all.first 29 | client = event.client 30 | check = event.check 31 | single_event = Event.single("#{client}_#{check}") 32 | single_event.should be_a Event 33 | end 34 | 35 | it "should identify when an event's client is silenced" do 36 | event = Event.all.last 37 | client = event.client 38 | check = event.check 39 | description = "This is a test description, it is long enough" 40 | event.check_silenced.should eq nil 41 | Event.silence_check(client, check, description, User.first, nil, false, nil) 42 | event.check_silenced.should eq nil 43 | end 44 | 45 | it "should allow manual resolution of an event" do 46 | event = Event.all.last 47 | client = event.client 48 | check = event.check 49 | user = User.last 50 | Event.manual_resolve(client, check, user).should be_a String 51 | end 52 | 53 | it "should allow a client to be silenced" do 54 | event = Event.all.last 55 | client = event.client 56 | description = "This is a test description, it is long enough" 57 | user = User.first 58 | silenced_client = Event.silence_client(client, description, user, nil, false, nil) 59 | silenced_client.should be_a String 60 | end 61 | 62 | it "should allow a check to be silenced" do 63 | event = Event.all.last 64 | client = event.client 65 | check = event.check 66 | description = "This is a test description, it is long enough" 67 | user = User.last 68 | silenced_check = Event.silence_check(client, check, description, user) 69 | silenced_check.should be_a String 70 | end 71 | 72 | it "should allow a client to be unsilenced" do 73 | client = Event.all.last.client 74 | user = User.last 75 | unsilenced_client = Event.unsilence_client(client, user) 76 | unsilenced_client.should be_a String 77 | end 78 | 79 | it "should return client attributes" do 80 | event = Event.all.first 81 | client_attributes = event.client_attributes 82 | client_attributes.should be_a Hash 83 | client_attributes.should_not be_empty 84 | end 85 | 86 | it "should return check attributes" do 87 | event = Event.all.first 88 | check_attributes = event.check_attributes 89 | check_attributes.should be_a Hash 90 | check_attributes.should_not be_empty 91 | end 92 | 93 | it "should get a client name" do 94 | event = Event.all.first 95 | event.client.should eq "i-424242" 96 | end 97 | 98 | it "should return an environment" do 99 | event = Event.all.first 100 | end 101 | 102 | end 103 | -------------------------------------------------------------------------------- /app/models/event.rb: -------------------------------------------------------------------------------- 1 | class Event < Resting 2 | attr_accessor :client_silenced, :check_silenced, :client_attributes 3 | 4 | def self.all_with_cache 5 | Rails.cache.fetch("events", :expires_in => 30.seconds, :race_condition_ttl => 10) do 6 | all 7 | end 8 | end 9 | 10 | def self.refresh_cache 11 | Rails.cache.write("events", Event.all, :expires_in => 30.seconds, :race_condition_ttl => 10) 12 | end 13 | 14 | def resolve 15 | self.delete("#{self.class.to_s.downcase}/#{self.client}/#{self.check}") 16 | end 17 | 18 | def self.single(query) 19 | Event.all_with_cache.select{|event| query == "#{event.client}_#{event.check}"}[0] 20 | end 21 | 22 | def client_silenced 23 | stashes = Stash.stashes 24 | stashes.detect {|stash| stash['path'] == "silence/#{self.client}"} 25 | end 26 | 27 | def check_silenced 28 | stashes = Stash.stashes 29 | stashes.detect {|stash| stash['path'] == "silence/#{self.client}/#{self.check}"} 30 | end 31 | 32 | def self.manual_resolve(client, check, user) 33 | event = Event.single("#{client}_#{check}") 34 | Log.log(user, client, "resolve", nil, event) 35 | self.delete("events/#{client}/#{check}") 36 | end 37 | 38 | def self.silence_client(client, description, user, expire_at = nil, log = true, downtime_id = nil) 39 | Log.log(user, client, "silence", description) if log 40 | post_data = {:description => description, :owner => user.email, :timestamp => Time.now.to_i } 41 | post_data[:expire_at] = expire_at unless expire_at.nil? 42 | post_data[:downtime_id] = downtime_id unless downtime_id.nil? 43 | Stash.create("silence/#{client}", post_data) 44 | end 45 | 46 | def self.silence_check(client, check, description, user, expire_at = nil, log = true, downtime_id = nil) 47 | event = Event.single("#{client}_#{check}") 48 | Log.log(user, client, "silence", description, event) if log 49 | post_data = {:description => description, :owner => user.email, :timestamp => Time.now.to_i } 50 | post_data[:expire_at] = expire_at unless expire_at.nil? 51 | post_data[:downtime_id] = downtime_id unless downtime_id.nil? 52 | Stash.create("silence/#{client}/#{check}", post_data) 53 | end 54 | 55 | def self.unsilence_client(client, user) 56 | Log.log(user, client, "unsilence") 57 | Stash.delete("stashes/silence/#{client}") 58 | end 59 | 60 | def self.unsilence_check(client, check, user) 61 | event = Event.single("#{client}_#{check}") 62 | Log.log(user, client, "unsilence", nil, event) 63 | Stash.delete("stashes/silence/#{client}/#{check}") 64 | end 65 | 66 | def sort_val 67 | # Could use a custom sorter here, as Critical is == 2 and Warning == 1 68 | case self.status 69 | when 2 70 | 1 71 | when 1 72 | 2 73 | else 74 | 3 75 | end 76 | end 77 | 78 | def client_attributes 79 | Rails.cache.fetch(self.client, :expires_in => 5.minutes, :race_condition_ttl => 10) do 80 | JSON.parse(Client.find(self.client).to_json) 81 | end 82 | end 83 | 84 | def check_attributes 85 | Rails.cache.fetch(self.check, :expires_in => 5.minutes, :race_condition_ttl => 10) do 86 | if Check.find(self.check) 87 | JSON.parse(Check.find(self.check).to_json) 88 | else 89 | {"error" => "could not find check"} 90 | end 91 | end 92 | end 93 | 94 | def client 95 | client_attributes['name'] 96 | end 97 | 98 | def environment 99 | client_attributes['environment'] 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /app/views/downtimes/old_downtimes.html.haml: -------------------------------------------------------------------------------- 1 | %h2 Past downtimes 2 | %br 3 | - @old_downtimes.each do |downtime| 4 | %div{:class => "modal hide", :id => "downtime_modal_#{downtime.id}"} 5 | %div.modal-header 6 | %h3== Downtime: #{downtime.name} 7 | %div.modal-body 8 | %table.table.table-striped.table-bordered 9 | %tbody 10 | %tr 11 | %td Name 12 | %td= downtime.name 13 | %tr 14 | %td Description 15 | %td= downtime.description 16 | %tr 17 | %td Start time 18 | - if Time.at(downtime.start_time) > Time.now 19 | %td== #{downtime.start_time} -- in about #{distance_of_time_in_words(Time.now, downtime.start_time)} 20 | - else 21 | %td== #{downtime.start_time} -- #{time_ago_in_words(downtime.start_time)} ago 22 | %tr 23 | %td Stop time 24 | - if Time.at(downtime.stop_time) > Time.now 25 | %td== #{downtime.stop_time} -- in about #{distance_of_time_in_words(Time.now, downtime.stop_time)} 26 | - else 27 | %td== #{downtime.stop_time} -- #{time_ago_in_words(downtime.stop_time)} ago 28 | %tr 29 | %td User 30 | %td= downtime.user.email 31 | %tr 32 | %td Processed 33 | %td= downtime.processed? 34 | %tr 35 | %td Completed 36 | %td= downtime.completed? 37 | %tr 38 | %td Created At 39 | %td= downtime.created_at 40 | %tr 41 | %td Updated at 42 | %td= downtime.updated_at 43 | %br 44 | %h3 Clients (#{downtime.downtime_clients.count}) 45 | %table.table.table-striped.table-bordered 46 | %thead 47 | %th Name 48 | %th Address 49 | - if use_environments? 50 | %th ENV 51 | %tbody 52 | - downtime.downtime_clients.each do |dclient| 53 | %tr 54 | - if @clients_hash[dclient.name].nil? 55 | %td= dclient.name 56 | %td Unknown 57 | %td Unknown 58 | - else 59 | %td= dclient.name 60 | %td= @clients_hash[dclient.name][:address] 61 | - if use_environments? 62 | %td= @clients_hash[dclient.name][:environment] 63 | - unless downtime.downtime_checks.empty? 64 | %h3 Checks (#{downtime.downtime_checks.count}) 65 | %table.table.table-striped.table-bordered 66 | %tbody 67 | - downtime.downtime_checks.each do |check| 68 | %tr 69 | %td= check.name 70 | %div.modal-footer 71 | %p Destroy 72 | %p Mark as completed 73 | 74 | %table.table.table-striped.table-bordered 75 | %thead 76 | %th Name 77 | %th Description 78 | %th Start Time 79 | %th Stop Time 80 | %th User 81 | %th Processed? 82 | %th Completed? 83 | %th Clients 84 | %th Checks 85 | %th Created At 86 | %th 87 | %tbody 88 | - @old_downtimes.each do |adowntime| 89 | %tr{:rel => "#downtime_modal_#{adowntime.id}"} 90 | %td.moreinfo= adowntime.name 91 | %td.moreinfo= adowntime.description 92 | %td.moreinfo= adowntime.start_time 93 | %td.moreinfo= adowntime.stop_time 94 | %td.moreinfo= adowntime.user.email 95 | %td.moreinfo= adowntime.processed 96 | %td.moreinfo= adowntime.completed 97 | %td.moreinfo= adowntime.downtime_clients.count 98 | %td.moreinfo= adowntime.downtime_checks.count 99 | %td.moreinfo= time_ago_in_words(adowntime.created_at) + " ago" 100 | %td.moreinfo 101 | %i.icon-zoom-in 102 | -------------------------------------------------------------------------------- /app/controllers/downtimes_controller.rb: -------------------------------------------------------------------------------- 1 | class DowntimesController < ApplicationController 2 | respond_to :html 3 | before_filter :find_downtime, :except => [:index, :new, :create, :old_downtimes, :force_complete] 4 | before_filter :find_clients, :except => [:index] 5 | before_filter :find_checks, :except => [:index] 6 | 7 | def index 8 | @active_downtime = Downtime.active.not_completed.all_processed 9 | @future_downtime = Downtime.not_completed.not_processed 10 | @clients_hash = Client.full_hash 11 | #@past_downtime = Downtime.past Would need to paginate this. For now its on its own page. 12 | end 13 | 14 | def new 15 | @downtime = Downtime.new 16 | end 17 | 18 | def create 19 | Downtime.transaction do 20 | dt = params['downtime'] 21 | @downtime = Downtime.new 22 | @downtime.assign_attributes({ 23 | :name => dt['name'], 24 | :description => dt['description'], 25 | :start_time => Time.parse("#{dt['begin_date']} #{dt['begin_time']}"), 26 | :stop_time => Time.parse("#{dt['end_date']} #{dt['end_time']}"), 27 | :user_id => current_user.id 28 | }) 29 | unless dt['client_ids'].nil? 30 | dt['client_ids'].each do |client_id| 31 | next if client_id.blank? 32 | @downtime.downtime_clients.build(:name => client_id) 33 | end 34 | end 35 | unless dt['check_ids'].nil? 36 | dt['check_ids'].each do |check_id| 37 | next if check_id.blank? 38 | @downtime.downtime_checks.build(:name => check_id) 39 | end 40 | end 41 | end 42 | if @downtime.save! 43 | redirect_to(downtimes_path(@downtime), :notice => "Successfully created") 44 | end 45 | rescue ActiveRecord::RecordInvalid 46 | render :action => "edit" 47 | end 48 | 49 | def old_downtimes 50 | @old_downtimes = Downtime.past 51 | @clients_hash = Client.full_hash 52 | end 53 | 54 | def show 55 | end 56 | 57 | def edit 58 | end 59 | 60 | def update 61 | Downtime.transaction do 62 | dt = params['downtime'] 63 | @downtime.assign_attributes({ 64 | :name => dt['name'], 65 | :description => dt['description'], 66 | :start_time => Time.parse("#{dt['begin_date']} #{dt['begin_time']}"), 67 | :stop_time => Time.parse("#{dt['end_date']} #{dt['end_time']}"), 68 | :user_id => current_user.id 69 | }) 70 | unless dt['client_ids'].nil? 71 | @downtime.downtime_clients.destroy_all 72 | dt['client_ids'].each do |client_id| 73 | next if client_id.blank? 74 | @downtime.downtime_clients.build(:name => client_id) 75 | end 76 | end 77 | unless dt['check_ids'].nil? 78 | @downtime.downtime_checks.destroy_all 79 | dt['check_ids'].each do |check_id| 80 | next if check_id.blank? 81 | @downtime.downtime_checks.build(:name => check_id) 82 | end 83 | end 84 | if @downtime.save! 85 | redirect_to(downtimes_path, :notice => "Downtime successfully updated.") 86 | end 87 | end 88 | rescue ActiveRecord::RecordInvalid 89 | render :action => "edit" 90 | end 91 | 92 | def force_complete 93 | Downtime.force_complete(params[:downtime_id]) 94 | redirect_to(downtimes_path, :notice => "Downtime successfully completed") 95 | end 96 | 97 | def destroy 98 | @downtime.destroy 99 | redirect_to(downtimes_path, :notice => "Downtime successfully deleted") 100 | end 101 | 102 | private 103 | 104 | def find_downtime 105 | @downtime = Downtime.find(params[:id]) 106 | end 107 | 108 | def find_clients 109 | @clients = Client.all_with_cache 110 | end 111 | 112 | def find_checks 113 | @checks = Check.all 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /config/locales/devise.en.yml: -------------------------------------------------------------------------------- 1 | # Additional translations at https://github.com/plataformatec/devise/wiki/I18n 2 | 3 | en: 4 | errors: 5 | messages: 6 | expired: "has expired, please request a new one" 7 | not_found: "not found" 8 | already_confirmed: "was already confirmed, please try signing in" 9 | not_locked: "was not locked" 10 | not_saved: 11 | one: "1 error prohibited this %{resource} from being saved:" 12 | other: "%{count} errors prohibited this %{resource} from being saved:" 13 | 14 | devise: 15 | failure: 16 | already_authenticated: 'You are already signed in.' 17 | unauthenticated: 'You need to sign in or sign up before continuing.' 18 | unconfirmed: 'You have to confirm your account before continuing.' 19 | locked: 'Your account is locked.' 20 | invalid: 'Invalid email or password.' 21 | invalid_token: 'Invalid authentication token.' 22 | timeout: 'Your session expired, please sign in again to continue.' 23 | inactive: 'Your account was not activated yet, or has been deactivated.' 24 | sessions: 25 | signed_in: 'Signed in successfully.' 26 | signed_out: 'Signed out successfully.' 27 | passwords: 28 | send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.' 29 | updated: 'Your password was changed successfully. You are now signed in.' 30 | updated_not_active: 'Your password was changed successfully.' 31 | send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." 32 | no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." 33 | confirmations: 34 | send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.' 35 | send_paranoid_instructions: 'If your email address exists in our database, you will receive an email with instructions about how to confirm your account in a few minutes.' 36 | confirmed: 'Your account was successfully confirmed. You are now signed in.' 37 | registrations: 38 | signed_up: 'Welcome! You have signed up successfully.' 39 | signed_up_but_unconfirmed: 'A message with a confirmation link has been sent to your email address. Please open the link to activate your account.' 40 | signed_up_but_inactive: 'You have signed up successfully. However, we could not sign you in because your account is not yet activated.' 41 | signed_up_but_locked: 'You have signed up successfully. However, we could not sign you in because your account is locked.' 42 | updated: 'You updated your account successfully.' 43 | update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and click on the confirm link to finalize confirming your new email address." 44 | destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.' 45 | unlocks: 46 | send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.' 47 | unlocked: 'Your account has been unlocked successfully. Please sign in to continue.' 48 | send_paranoid_instructions: 'If your account exists, you will receive an email with instructions about how to unlock it in a few minutes.' 49 | omniauth_callbacks: 50 | success: 'Successfully authenticated from %{kind} account.' 51 | failure: 'Could not authenticate you from %{kind} because "%{reason}".' 52 | mailer: 53 | confirmation_instructions: 54 | subject: 'Confirmation instructions' 55 | reset_password_instructions: 56 | subject: 'Reset password instructions' 57 | unlock_instructions: 58 | subject: 'Unlock Instructions' 59 | -------------------------------------------------------------------------------- /app/assets/stylesheets/bootstrap_and_overrides.css.scss: -------------------------------------------------------------------------------- 1 | @import "font-awesome"; 2 | $iconSpritePath: ''; 3 | $iconWhiteSpritePath: ''; 4 | @import "bootstrap"; 5 | @import "bootstrap-responsive"; 6 | 7 | body { 8 | padding-top: 60px; 9 | } 10 | 11 | .table tbody tr:hover td, 12 | .table tbody tr:hover th { 13 | background-color: inherit; 14 | } 15 | 16 | .modal { 17 | position: fixed; 18 | top: 3%; 19 | right: 3%; 20 | left: 3%; 21 | width: auto; 22 | margin: 0 auto; 23 | } 24 | 25 | .small-modal { 26 | width: 30%; 27 | left: 8%; 28 | top: 20%; 29 | } 30 | 31 | .large-modal { 32 | width: 50%; 33 | left: 8%; 34 | top: 5%; 35 | } 36 | 37 | .silence-reason { 38 | float: left; 39 | } 40 | 41 | .silence-time { 42 | float: left; 43 | padding-left: 50px; 44 | } 45 | 46 | div.modal-body { 47 | padding-bottom: 80px; 48 | max-height: 400px; 49 | max-width: 95%; 50 | } 51 | 52 | .modal-data { 53 | margin-bottom: 100px; 54 | padding-bottom: 80px; 55 | } 56 | 57 | div.modal-footer { 58 | position: absolute; 59 | bottom: 0px; 60 | right: 0px; 61 | width: 100%; 62 | } 63 | 64 | table.events_table { 65 | margin-left: -17px; 66 | } 67 | 68 | @media (max-width: 979px) { 69 | body { 70 | padding-top: 0px; 71 | } 72 | .container { 73 | width: 100%; 74 | } 75 | table.events_table { 76 | margin-left: 5px; 77 | } 78 | .content { 79 | width: 970px; 80 | } 81 | } 82 | 83 | @media 84 | only screen and (max-width: 480px) { 85 | 86 | .large-modal, .small-modal { 87 | height: 95%; 88 | width: 95%; 89 | left: 0%; 90 | top: 5%; 91 | } 92 | 93 | .small-modal div.modal-body { 94 | max-height: 50%; 95 | } 96 | 97 | .large-modal div.modal-body { 98 | max-height: 50%; 99 | } 100 | 101 | .modal-footer { 102 | min-height: 45px; 103 | } 104 | 105 | 106 | /* Force table to not be like tables anymore */ 107 | .events_table thead,.events_table tbody,.events_table th,.events_table td,.events_table tr { 108 | display: block; 109 | } 110 | 111 | table.events_table { 112 | padding-left: 5%; 113 | margin-left: 10px; 114 | display:block; 115 | } 116 | 117 | /* Hide table headers (but not display: none;, for accessibility) */ 118 | .events_table thead tr { 119 | position: absolute; 120 | top: -9999px; 121 | left: -9999px; 122 | } 123 | 124 | .events_table tr { 125 | padding-bottom: 5%; 126 | border: 1px solid white; 127 | } 128 | 129 | .events_table td { 130 | /* Behave like a "row" */ 131 | border: none; 132 | border-bottom: 1px solid #eee; 133 | position: relative; 134 | padding-left: 0%; 135 | min-height: 15px; 136 | } 137 | 138 | .margin-right { 139 | margin-right: 5px; 140 | } 141 | 142 | table.events_table td:before { 143 | /* Now like a table header */ 144 | position: absolute; 145 | /* Top/left values mimic padding */ 146 | top: 6px; 147 | left: -42px; 148 | width: 45%; 149 | padding-right: 30px; 150 | white-space: nowrap; 151 | } 152 | 153 | /* 154 | Label the data 155 | */ 156 | .events_table td:nth-of-type(1):before { content: "Stat"; } 157 | .events_table td:nth-of-type(2):before { content: "ENV"; } 158 | .events_table td:nth-of-type(3):before { content: "ID"; } 159 | .events_table td:nth-of-type(4):before { content: "Chk"; } 160 | .events_table td:nth-of-type(5):before { content: "Out"; } 161 | .events_table td:nth-of-type(6):before { content: "Act"; } 162 | .events_table td:nth-of-type(7):before { content: "Time"; } 163 | .events_table td:nth-of-type(8):before { content: "More"; } 164 | } 165 | a:hover { 166 | cursor:pointer; 167 | } 168 | 169 | .alert { 170 | left: 50%; 171 | height: 12px; 172 | z-index: 2; 173 | position: absolute; 174 | margin: auto auto auto -100px; 175 | width: 130px; 176 | padding: 8px; 177 | line-height: 10px; 178 | display: none; 179 | } 180 | -------------------------------------------------------------------------------- /app/models/downtime.rb: -------------------------------------------------------------------------------- 1 | class Downtime < ActiveRecord::Base 2 | attr_accessible :description, :name, :start_time, :stop_time, :user_id, :processed, :completed, :begin_date, :end_date, :begin_time, :end_time 3 | 4 | belongs_to :user 5 | 6 | has_many :downtime_checks 7 | has_many :downtime_clients 8 | 9 | validates_presence_of :name 10 | validates_presence_of :user_id 11 | # validates_presence_of :downtime_clients 12 | 13 | validate :validates_stop_time_of, :on => :create 14 | 15 | scope :active, lambda { where("? BETWEEN start_time AND stop_time", DateTime.now) } 16 | scope :past, lambda { where("? > stop_time", DateTime.now) } 17 | scope :future, lambda { where("start_time > ?", DateTime.now) } 18 | scope :all_processed, where(:processed => true) 19 | scope :not_processed, where(:processed => false) 20 | scope :all_completed, where(:completed => true) 21 | scope :not_completed, where(:completed => false) 22 | 23 | def self.process_downtimes 24 | run_expired_stashes = false 25 | 26 | Downtime.active.not_processed.each do |downtime| 27 | puts "Processing downtime #{downtime.id}" 28 | if downtime.downtime_checks.empty? 29 | downtime.downtime_clients.each do |client| 30 | puts "Silencing client #{client.name} for downtime #{downtime.id}" 31 | Event.silence_client(client.name, "Scheduled downtime(#{downtime.id},#{downtime.name}) until #{downtime.stop_time} by #{downtime.user.email}", downtime.user, downtime.stop_time, false, downtime.id) 32 | end 33 | else 34 | downtime.downtime_clients.each do |client| 35 | downtime.downtime_checks.each do |check| 36 | puts "Silencing check #{check.name} on #{client.name} for downtime #{downtime.id}" 37 | Event.silence_check(client.name, check.name, "Scheduled downtime(#{downtime.id},#{downtime.name}) until #{downtime.stop_time} by #{downtime.user.email}", downtime.user, downtime.stop_time, false, downtime.id) 38 | end 39 | end 40 | end 41 | puts "Marking #{downtime.name} as processed" 42 | downtime.process 43 | end 44 | 45 | Downtime.not_completed.past.each do |downtime| 46 | puts "Marking #{downtime.name} as completed" 47 | run_expired_stashes = true 48 | downtime.complete 49 | end 50 | 51 | if run_expired_stashes 52 | puts "Clearing stashes in process_downtimes" 53 | Stash.clear_expired_stashes 54 | end 55 | end 56 | 57 | def self.force_complete(id) 58 | downtime = Downtime.find(id) 59 | Stash.stashes.each do |v| 60 | unless v['downtime_id'].nil? 61 | if v['downtime_id'].to_i == id.to_i 62 | puts "Force completing downtime (#{downtime.name}, #{downtime.id}) - Deleting stash #{v['path']}" 63 | Stash.delete_stash(v['path']) 64 | end 65 | end 66 | end 67 | downtime.process 68 | downtime.complete 69 | end 70 | 71 | def active? 72 | Time.now > self.start_time && Time.now < self.stop_time 73 | end 74 | 75 | def processed? 76 | self.processed 77 | end 78 | 79 | def completed? 80 | self.completed 81 | end 82 | 83 | def process 84 | self.update_attributes(:processed => true) 85 | end 86 | 87 | def complete 88 | self.update_attributes(:completed => true) 89 | end 90 | 91 | def begin_date 92 | return "" if self.start_time.nil? 93 | self.start_time.strftime("%d/%m/%Y") 94 | end 95 | 96 | def end_date 97 | return "" if self.stop_time.nil? 98 | self.stop_time.strftime("%d/%m/%Y") 99 | end 100 | 101 | def begin_time 102 | return "" if self.start_time.nil? 103 | self.start_time.strftime("%l:%M%P") 104 | end 105 | 106 | def end_time 107 | return "" if self.stop_time.nil? 108 | self.stop_time.strftime("%l:%M%P") 109 | end 110 | 111 | private 112 | 113 | def validates_stop_time_of 114 | errors.add(:stop_time, "is not in the future.") if Time.now > self.stop_time 115 | end 116 | end 117 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended to check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(:version => 20121105221805) do 15 | 16 | create_table "downtime_checks", :force => true do |t| 17 | t.string "name" 18 | t.integer "downtime_id" 19 | t.datetime "created_at", :null => false 20 | t.datetime "updated_at", :null => false 21 | end 22 | 23 | create_table "downtime_clients", :force => true do |t| 24 | t.string "name" 25 | t.integer "downtime_id" 26 | t.datetime "created_at", :null => false 27 | t.datetime "updated_at", :null => false 28 | end 29 | 30 | create_table "downtimes", :force => true do |t| 31 | t.string "name" 32 | t.text "description" 33 | t.datetime "start_time" 34 | t.datetime "stop_time" 35 | t.integer "user_id" 36 | t.boolean "processed", :default => false 37 | t.boolean "completed", :default => false 38 | t.datetime "created_at", :null => false 39 | t.datetime "updated_at", :null => false 40 | end 41 | 42 | create_table "logs", :force => true do |t| 43 | t.string "client" 44 | t.string "check" 45 | t.string "silence_type" 46 | t.string "action_type" 47 | t.string "environment" 48 | t.text "output" 49 | t.text "reason" 50 | t.string "status" 51 | t.integer "user_id" 52 | t.datetime "created_at", :null => false 53 | t.datetime "updated_at", :null => false 54 | end 55 | 56 | create_table "roles", :force => true do |t| 57 | t.string "name" 58 | t.integer "resource_id" 59 | t.string "resource_type" 60 | t.datetime "created_at", :null => false 61 | t.datetime "updated_at", :null => false 62 | end 63 | 64 | add_index "roles", ["name", "resource_type", "resource_id"], :name => "index_roles_on_name_and_resource_type_and_resource_id" 65 | add_index "roles", ["name"], :name => "index_roles_on_name" 66 | 67 | create_table "settings", :force => true do |t| 68 | t.string "name" 69 | t.string "value" 70 | t.datetime "created_at", :null => false 71 | t.datetime "updated_at", :null => false 72 | end 73 | 74 | create_table "users", :force => true do |t| 75 | t.datetime "created_at", :null => false 76 | t.datetime "updated_at", :null => false 77 | t.string "email", :default => "", :null => false 78 | t.string "encrypted_password", :default => "", :null => false 79 | t.string "reset_password_token" 80 | t.datetime "reset_password_sent_at" 81 | t.datetime "remember_created_at" 82 | t.integer "sign_in_count", :default => 0 83 | t.datetime "current_sign_in_at" 84 | t.datetime "last_sign_in_at" 85 | t.string "current_sign_in_ip" 86 | t.string "last_sign_in_ip" 87 | t.datetime "deleted_at" 88 | end 89 | 90 | add_index "users", ["email"], :name => "index_users_on_email", :unique => true 91 | add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true 92 | 93 | create_table "users_roles", :id => false, :force => true do |t| 94 | t.integer "user_id" 95 | t.integer "role_id" 96 | end 97 | 98 | add_index "users_roles", ["user_id", "role_id"], :name => "index_users_roles_on_user_id_and_role_id" 99 | 100 | end 101 | -------------------------------------------------------------------------------- /app/views/events/_mobile_event.mobile.haml: -------------------------------------------------------------------------------- 1 | - events.each_with_index do |event, i| 2 | %li{'data-inset' => 'true', :id => "critical_#{i}", 'data-theme' => color_code} 3 | %h2== #{event.check} on #{event.client} 4 | %p== Summary: #{event.output[0..100]} 5 | - if use_environments? 6 | %p== ENV: #{event.environment} 7 | %p== Time ago: #{time_ago_in_words(Time.at(event.issued))} 8 | - if event.client_silenced.nil? 9 | %p{:rel => "silence_details_#{event.client}"}== Client not silenced 10 | - else 11 | %p{:rel => "silence_details_#{event.client}"}== Client silenced: #{event.client_silenced['description']} 12 | - if event.check_silenced.nil? 13 | %p{:rel => "silence_details_#{event.client}_#{event.check}"}== Check not silenced 14 | - else 15 | %p{:rel => "silence_details_#{event.client}_#{event.check}"}== Check silenced: #{event.check_silenced['description']} 16 | %ul{'data-theme' => 'a', 'data-count-theme' => 'a', 'data-role' => 'listview', 'data-inset' => 'true'} 17 | %li 18 | %p== Check: #{event.check} 19 | %p== Client: #{event.client} 20 | %p{:style => 'white-space: normal;'}== Output: #{event.output} 21 | - if use_environments? 22 | %p== ENV: #{event.environment} 23 | %p== Occured: #{time_ago_in_words(Time.at(event.issued))} Ago 24 | %br 25 | %br 26 | %li 27 | - silence_client_style = (event.client_silenced.nil?) ? "" : "display: none;" 28 | - unsilence_client_style = (event.client_silenced.nil?) ? "display: none;" : "" 29 | %div{:rel => "silence_#{event.client}", :style => silence_client_style} 30 | %div.ui-hide-label{'data-role' => 'fieldcontain'} 31 | %input{:type => "text", :name => "description", :id => "#{event.client}_#{i}_description", :value => "", :placeholder => "Silence Reason"} 32 | %div.ui-submit.ui-btn.ui-shadow{'data-corners' => 'true'} 33 | %button.silence-submit{:rel => "/events/#{event.client}/silence", :control => "#{event.client}", :silence_type => "Client silenced reason: ", :description => "#{event.client}_#{i}", 'data-theme' => 'd'} Silence Client 34 | %div{:rel => "unsilence_#{event.client}", :style => unsilence_client_style} 35 | %p{:rel => "silence_details_#{event.client}"}== Client Silenced Details: #{event.client_silenced.to_s} 36 | %button.silence-submit{:rel => "/events/#{event.client}/unsilence", :control => "#{event.client}", :silence_type => "Client not silenced"} Unsilence Client 37 | %br 38 | %br 39 | %li 40 | - silence_check_style = (event.check_silenced.nil?) ? "" : "display: none;" 41 | - unsilence_check_style = (event.check_silenced.nil?) ? "display: none;" : "" 42 | %div{:rel => "silence_#{event.client}_#{event.check}", :style => silence_check_style} 43 | %div.ui-hide-label{'data-role' => 'fieldcontain'} 44 | %input{:type => "text", :name => "description", :id => "#{event.client}_#{event.check}_#{i}_description", :value => "", :placeholder => "Silence Reason"} 45 | %div.ui-submit.ui-btn.ui-shadow{'data-corners' => 'true'} 46 | %button.silence-submit{:rel => "/events/#{event.client}/#{event.check}/silence", :control => "#{event.client}_#{event.check}", :silence_type => "Check silenced reason: ", :description => "#{event.client}_#{event.check}_#{i}", 'data-theme' => 'd'} Silence Check 47 | %div{:rel => "unsilence_#{event.client}_#{event.check}", :style => unsilence_check_style} 48 | %p{:rel => "silence_details_#{event.client}_#{event.check}"}== Check Silenced Details: #{event.check_silenced.to_s} 49 | %button.silence-submit{:rel => "/events/#{event.client}/#{event.check}/unsilence", :control => "#{event.client}_#{event.check}", :silence_type => "Check not silenced"} Unsilence Check 50 | %li 51 | %div{:control => "resolve_#{event.client}_#{event.check}"} 52 | %button.resolve-submit{:type => "critical_#{i}", :rel => "/events/#{event.client}/#{event.check}/resolve", :control => "#{event.client}_#{event.check}", 'data-theme' => 'd'} Resolve Event 53 | %div{:control => "resolved_#{event.client}_#{event.check}", :style => "display: none;"} 54 | %button{'data-theme' => 'b'} Resolved 55 | --------------------------------------------------------------------------------