diff options
author | 2013-11-11 11:33:12 +0100 | |
---|---|---|
committer | 2013-11-11 11:33:12 +0100 | |
commit | d733c0abf2d568319d3b19e8806816346760737e (patch) | |
tree | 446dcee6f606ecc95db2e66ad393925d89372622 /lib | |
parent | .gitignore (diff) | |
download | infra-status-d733c0abf2d568319d3b19e8806816346760737e.tar.gz infra-status-d733c0abf2d568319d3b19e8806816346760737e.tar.bz2 infra-status-d733c0abf2d568319d3b19e8806816346760737e.zip |
v2
Diffstat (limited to 'lib')
-rw-r--r-- | lib/helpers.rb | 86 | ||||
-rw-r--r-- | lib/notice_store.rb | 102 | ||||
-rw-r--r-- | lib/service_registry.rb | 90 |
3 files changed, 278 insertions, 0 deletions
diff --git a/lib/helpers.rb b/lib/helpers.rb new file mode 100644 index 0000000..2f613b1 --- /dev/null +++ b/lib/helpers.rb @@ -0,0 +1,86 @@ +helpers do + def h(text) + Rack::Utils.escape_html(text) + end + + def markdown(text) + markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, :autolink => true, :space_after_headers => true) + markdown.render(text) + end + + def get_forced_state(notices) + notices.each do |notice| + next unless notice.has_key? 'force_state' + + return notice['force_state'] + end + + nil + end + + def service_info(service) + content = '' + active_notices = NoticeStore.instance.active_notices_for(service) + + unless (forced_state = get_forced_state(active_notices)) == nil + content << status_icon(forced_state) + else + case ServiceRegistry.instance.services[service] + when State::UP + content << status_icon('up') + when State::WARNING + content << status_icon('warning') + when State::DOWN + content << status_icon('down') + else + content << status_icon('na') + end + end + + content << '<span class="badge" style="margin-right: 1em;" title="There are notices (%s) below regarding this service.">%s</span>' % [active_notices.count, active_notices.count] if active_notices.count > 0 + + content + end + + def panel_class(notice) + if notice['type'] == 'outage' + 'panel-danger' + elsif notice['type'] == 'information' + 'panel-info' + elsif notice['type'] == 'maintenance' + 'panel-warning' + else + 'panel-default' + end + end + + def status_icon(status) + case status.to_s + when 'up' + return '<img src="/icons/status_up.png" alt="The service is up and running." title="The service is up and running." class="pull-right" />' + when 'down' + return '<img src="/icons/status_down.png" alt="There are indications the service is down." title="There are indications the service is down." class="pull-right" />' + when 'warning' + return '<img src="/icons/status_warning.png" alt="There are issues with the service." title="There are issues with the service." class="pull-right" />' + when 'maintenance' + return '<img src="/icons/maintenance.png" alt="The service is undergoing scheduled maintenance." title="The service is undergoing scheduled maintenance." class="pull-right" />' + else + return '<img src="/icons/na.png" alt="No data available." title="No data available." class="pull-right" />' + end + end + + def item_icon(type) + case type.to_s + when 'maintenance' + return '<img src="/icons/maintenance.png" alt="Scheduled maintenance" title="Scheduled maintenance" style="vertical-align: text-top;" />' + when 'outage' + return '<img src="/icons/outage.png" alt="Unplanned outage" title="Unplanned outage" style="vertical-align: text-top;" />' + when 'information' + return '<img src="/icons/information.png" alt="General information" title="General information" style="vertical-align: text-top;" />' + end + end + + def date_format(date) + date.rfc2822 + end +end
\ No newline at end of file diff --git a/lib/notice_store.rb b/lib/notice_store.rb new file mode 100644 index 0000000..abb8fb3 --- /dev/null +++ b/lib/notice_store.rb @@ -0,0 +1,102 @@ +require 'yaml' +require 'date' +require 'singleton' + +# Stores notices and caches them in memory. +# Automatically refreshes the cache after CACHE_SECONDS seconds. +class NoticeStore + include Singleton + CACHE_SECONDS = 600 + + def initialize + update! + end + + def update! + @notices = [] + + Dir.glob('data/notices/*.txt') do |file| + begin + @notices << Notice.from_file(file) + rescue ArgumentError + $stderr.puts 'Invalid notice: %s' % file + end + end + + # Name your stuff with YYYYMMDD-foo to get the newest notices on top + @notices.reverse! + + @load_date = DateTime.now + end + + def notices + update? + @notices + end + + def active_notices + notices.select do |notice| + is_active = notice['active'] + is_active &= notice['expire_at'] >= DateTime.now if notice.has_key? 'expire_at' + is_active &= notice['created_at'] <= DateTime.now if notice.has_key? 'created_at' + + is_active + end + end + + def active_notices_for(service) + active_notices.select do |notice| + notice.has_key? 'affects' and notice['affects'].include? service + end + end + + def notice(id) + notices.each do |notice| + return notice if notice['id'] == id + end + + nil + end + + private + def update? + if ((DateTime.now - @load_date) * 60 * 60 * 24).to_i > CACHE_SECONDS + update! + end + end +end + +class Notice + def self.from_file(filename) + content = File.read(filename) + new(File.basename(filename, '.txt'), YAML.load(content), content.split('---')[2].strip) + end + + def [](what) + if @metadata.has_key? what + @metadata[what] + else + nil + end + end + + def has_key?(what) + @metadata.has_key? what + end + + def get_content + @content + end + + private + def initialize(id, metadata, content) + @metadata = metadata + + %w[created_at eta expire_at].each do |key| + @metadata[key] = DateTime.parse(@metadata[key]) if @metadata.has_key? key + end + + @metadata['id'] = id + @content = content + end +end
\ No newline at end of file diff --git a/lib/service_registry.rb b/lib/service_registry.rb new file mode 100644 index 0000000..61e51c5 --- /dev/null +++ b/lib/service_registry.rb @@ -0,0 +1,90 @@ +require 'singleton' +require 'json' + +# Defining state constants +module State + UP=1 + DOWN=2 + WARNING=3 +end + +module HelperMethods + # Checks if all hosts are up + def all_hosts_up?(hosts) + hosts.each do |host| + return false unless + status_data['hosts'].has_key?(host) and + status_data['hosts'][host]['current_state'] == 0 + end + + true + end + + def host_up?(host) + status_data['hosts'].has_key?(host) and + status_data['hosts'][host]['current_state'] == 0 + end + + def host_flapping?(host) + status_data['hosts'].has_key?(host) and + status_data['hosts'][host]['is_flapping'] != 0 + end + + # Checks if the service is up + def service_up?(host, service) + status_data['services'].has_key?(host) and + status_data['services'][host].has_key?(service) and + status_data['services'][host][service]['current_state'] == 0 + end + + def service_flapping?(host, service) + status_data['services'].has_key?(host) and + status_data['services'][host].has_key?(service) and + status_data['services'][host][service]['is_flapping'] != 0 + end + + def default(host, service = nil) + if service == nil + if host_flapping? host + State::WARNING + elsif host_up? host + State::UP + else + State::DOWN + end + else + if service_flapping? host, service + State::WARNING + elsif service_up? host, service + State::UP + else + State::DOWN + end + end + end +end + +class ServiceRegistry + StatusSource = File.join(File.dirname(__FILE__), '..', 'data', 'status.json') + + include Singleton + include HelperMethods + attr_reader :services, :status_data + + def initialize + end + + def service(name, &block) + @services[name] = block.call + end + + def update! + @services = {} + @status_data = JSON.parse(File.read(StatusSource)) + load(File.join(File.dirname(__FILE__), '..', 'data', 'services.rb')) + end +end + +def Services(&block) + ServiceRegistry.instance.instance_eval(&block) +end |