lib/flapjack/cli/notifier.rb in auxesis-flapjack-0.3.8 vs lib/flapjack/cli/notifier.rb in auxesis-flapjack-0.4.1

- old
+ new

@@ -3,10 +3,11 @@ require 'rubygems' require 'ostruct' require 'optparse' require 'log4r' require 'log4r/outputter/syslogoutputter' +require File.join(File.dirname(__FILE__), '..', 'database') module Flapjack class NotifierOptions def self.parse(args) options = OpenStruct.new @@ -19,10 +20,16 @@ options.port = port.to_i end opts.on('-r', '--recipients FILE', 'recipients file') do |recipients| options.recipients = recipients.to_s end + opts.on('-c', '--config FILE', 'config file') do |config| + options.config_filename = config.to_s + end + opts.on('-d', '--database-uri URI', 'location of the checks database') do |db| + options.database_uri = db.to_s + end opts.on_tail("-h", "--help", "Show this message") do puts opts exit end end @@ -41,14 +48,31 @@ options.host ||= 'localhost' options.port ||= 11300 @errors = [] # check that recipients file exists - unless File.exists?(options.recipients.to_s) - @errors << "The specified recipients file doesn't exist!" + if options.recipients + unless File.exists?(options.recipients.to_s) + @errors << "The specified recipients file doesn't exist!" + end + else + @errors << "You need to specify a recipients file with [-r|--recipients]." end - + + # check that config file exists + if options.config_filename + unless File.exists?(options.config_filename.to_s) + @errors << "The specified config file doesn't exist!" + end + else + options.config_filename ||= "/etc/flapjack/flapjack-notifier.yaml" + unless File.exists?(options.config_filename.to_s) + @errors << "The default config file (#{options.config_filename}) doesn't exist!" + @errors << "Set one up, or specify one with [-c|--config]." + end + end + # if there are errors, print them out and exit if @errors.size > 0 puts "Errors:" @errors.each do |error| puts " - #{error}" @@ -61,15 +85,17 @@ options end end class NotifierCLI - attr_accessor :log, :recipients, :notifier, :results_queue + attr_accessor :log, :recipients, :results_queue, :config + attr_accessor :notifier, :notifiers attr_accessor :condition - def initialize - @log = Log4r::Logger.new("notifier") + def initialize(opts={}) + @log = opts[:logger] + @log ||= Log4r::Logger.new("notifier") end def setup_loggers @log.add(Log4r::StdoutOutputter.new('notifier')) @log.add(Log4r::SyslogOutputter.new('notifier')) @@ -81,32 +107,126 @@ yaml = opts[:yaml] else opts[:filename] ||= File.join(Dir.pwd, "recipients.yaml") yaml = YAML::load(File.read(opts[:filename])) end - + + # FIXME: add error detection for passing in dumb things + @recipients = yaml.map do |r| OpenStruct.new(r) end end + + def setup_config(opts={}) + if opts[:yaml] + yaml = opts[:yaml] + else + opts[:filename] ||= File.join(Dir.pwd, "flapjack-notifier.yaml") + yaml = YAML::load(File.read(opts[:filename])) + end + + @config = OpenStruct.new(yaml) + end + + def setup_database(opts={}) + uri = (opts[:database_uri] || @config.database_uri) + raise ArgumentError, "database URI wasn't specified!" unless uri + DataMapper.setup(:default, uri) + validate_database + end + + def validate_database + begin + DataMapper.repository(:default).adapter.execute("SELECT 'id' FROM 'checks';") + rescue Sqlite3Error + @log.warning("The specified database doesn't appear to have any structure!") + @log.warning("You need to investigate this.") + end + # FIXME: add more rescues in here! + end + + def initialize_notifiers(opts={}) + notifiers_directory = opts[:notifiers_directory] + notifiers_directory ||= File.expand_path(File.join(File.dirname(__FILE__), '..', 'notifiers')) + + raise ArgumentError, "notifiers directory doesn't exist!" unless File.exists?(notifiers_directory) + + @notifiers = [] + + @config.notifiers.each_pair do |notifier, config| + filename = File.join(notifiers_directory, notifier.to_s, 'init') + if File.exists?(filename + '.rb') + @log.debug("Loading the #{notifier.to_s.capitalize} notifier") + require filename + @notifiers << Flapjack::Notifiers.const_get("#{notifier.to_s.capitalize}").new(config) + else + @log.warning("Flapjack::Notifiers::#{notifier.to_s.capitalize} doesn't exist!") + end + end + + @notifiers + end + + # Sets up notifier to do the grunt work of notifying people when checks + # return badly. + # + # Accepts a list of recipients (:recipients) and a logger (:logger) as + # arguments. If neither of these are specified, it will default to an + # existing list of recipients and the current logger. + # + # Sets up and returns @notifier, an instance of Flapjack::Notifier + def setup_notifier(opts={}) + recipients = (opts[:recipients] || @recipients) + log = (opts[:logger] || @log) + initialize_notifiers + notifiers = @notifiers # should we accept a list of notifiers? + @notifier = Flapjack::Notifier.new(:logger => log, + :notifiers => notifiers, + :recipients => recipients) + end def process_loop @log.info("Processing results...") loop do process_result end end - + + def save_result(result) + if check = Check.get(result.id) + check.status = result.retval + check.save + else + @log.error("Got result for check #{result.id}, but it doesn't exist!") + end + end + + def any_parents_warning_or_critical?(result) + if check = Check.get(result.id) + check.worst_parent_status > 0 ? true : false + else + @log.error("Got result for check #{result.id}, but it doesn't exist!") + end + end + def process_result @log.debug("Waiting for new result...") - result_job = @results_queue.reserve + result_job = @results_queue.reserve # blocks until a job is reserved result = Result.new(YAML::load(result_job.body)) @log.info("Processing result for check '#{result.id}'") if result.warning? || result.critical? - @log.info("Notifying on check '#{result.id}'") - @notifier.notify!(result) + if any_parents_warning_or_critical?(result) + @log.info("Not notifying on check '#{result.id}' as parent is failing.") + else + @log.info("Notifying on check '#{result.id}'") + @notifier.notify!(result) + end end + + @log.info("Storing status of check in database") + save_result(result) @log.debug("Deleting result for check '#{result.id}' from queue") result_job.delete end