summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAlex Legler <alex@a3li.li>2013-11-11 11:33:12 +0100
committerAlex Legler <alex@a3li.li>2013-11-11 11:33:12 +0100
commitd733c0abf2d568319d3b19e8806816346760737e (patch)
tree446dcee6f606ecc95db2e66ad393925d89372622 /lib
parent.gitignore (diff)
downloadinfra-status-d733c0abf2d568319d3b19e8806816346760737e.tar.gz
infra-status-d733c0abf2d568319d3b19e8806816346760737e.tar.bz2
infra-status-d733c0abf2d568319d3b19e8806816346760737e.zip
v2
Diffstat (limited to 'lib')
-rw-r--r--lib/helpers.rb86
-rw-r--r--lib/notice_store.rb102
-rw-r--r--lib/service_registry.rb90
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