├── 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 |
--------------------------------------------------------------------------------