#!/usr/bin/ruby require 'yaml' require 'time' require 'date' require 'optparse' # Copyright (C) 2009 John E. Vincent - http://dev.lusis.org/nagios/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Set some default options OPTIONS = { :config => "/etc/nagios/downtime.cfg" } OptionParser.new do |opts| script_name = File.basename($0) opts.banner = "Usage: #{script_name} [options]" opts.define_head "Ruby script to schedule reoccuring downtime. Configuration is defined in config.yaml. Schedules are defined in 'host_schedule.yaml' and 'service_schedule.yaml'" opts.on("-v", "--verbose", "Run Verbosely") do |v| OPTIONS[:verbose] = v end opts.on("-d", "--display", "Display current configuration and schedules") do |d| OPTIONS[:display] = d end opts.on("-c", "--config c", String, "Config file to use. Defaults /etc/nagios/downtime.cfg") do |c| OPTIONS[:config] = c end opts.on("-a", "--add", "Schedule a new downtime.") do |a| OPTIONS[:add] = a end opts.on("-r", "--remove", "Remove a downtime.") do |r| OPTIONS[:remove] = r end opts.on("-i", "--initialize", "Initialize a new configuration") do |i| OPTIONS[:initialize] = i end opts.on_tail("-h", "--help", "Show this message") do puts opts exit end end.parse! def parse_entry end def gen_svc_downtime(host,service,starttime,endtime,occurance,comment) starttime = gen_dstamp(starttime,occurance) endtime = gen_dstamp(endtime,occurance) return "[#{Time.now.to_i}] SCHEDULE_SVC_DOWNTIME;#{host};#{service};#{starttime};#{endtime};1;0;7200;Nagios System Account;#{comment}" end def gen_host_downtime end def schedule_downtime(nagcmdline) File.open(NAGIOS_CMD_FILE,'a') { |f| f.write("#{nagcmdline}\n") } puts "'#{nagcmdline}' written to #{NAGIOS_CMD_FILE}\n" if OPTIONS[:verbose] end def convert_occurance(occurance="daily", arr=[]) case occurance when 'tomorrow' dtdate = Date.today + 1 when 'daily' dtdate = Date.today when 'monthly' dtdate = Date.today >> 1 when 'annually' dtdate = Date.today >> 12 end return dtdate end def gen_dstamp(dttime,occurance) dtdate = convert_occurance(occurance) dstamp = Time.local(dtdate.year,dtdate.month,dtdate.day,dttime).to_i return dstamp end def display_config puts "Displaying the current configuration and schedules" puts "" puts "\t\e[4m\e[1mConfiguration Options\e[0m" puts "\t\t\e[1mConfiguration File\e[0m\t#{OPTIONS[:config]}" puts "\t\t\e[1mNagios Command File\e[0m\t#{NAGIOS_CMD_FILE}" puts "\t\t\e[1mService Schedule File\e[0m\t#{CONFIG['service_schedule_file']}" puts "" puts "\t\e[4m\e[1mDefined Schedules\e[0m" $svc_schedule.each_key do |host| puts "\t\t\e[1m#{host}\e[0m" $svc_schedule[host].each_key do |service| details = $svc_schedule[host][service] puts "\t\t\t\e[1m#{service}\e[0m" puts "\t\t\t\t\e[1mDowntime Start\e[0m\t\e[32m#{details['start']}\e[0m" puts "\t\t\t\t\e[1mDowntime End\e[0m\t\e[31m#{details['stop']}\e[0m" puts "\t\t\t\t\e[1mOccurance\e[0m\t\e[34m#{details['occurance']}\e[0m" puts "\t\t\t\t\e[1mComment\e[0m\t\t\e[33m#{details['comment']}\e[0m" puts "" end end exit end def add_downtime_interactive new_entry = Hash.new puts "Please answer the following questions accurately. There is currently no validation for new entries. If you mistype the hostname or service name, no downtime will be created" puts "\e[1mHostname?\e[0m" host = gets.chomp new_entry[host] = Hash.new puts "\e[1mService?\e[0m" service = gets.chomp puts "\e[1mStart Time?\e[0m" start = gets.chomp puts "\e[1mStop Time?\e[0m" stop = gets.chomp puts "\e[1mOccurance? (currently only 'daily' is honored)\e[0m" occurance = gets.chomp puts "\e[1mComment?\e[0m" comment = gets.chomp new_entry[host][service] = {"start"=>start,"stop"=>stop,"occurance"=>occurance,"comment"=>comment} add_downtime(new_entry) end def del_downtime_interactive hostsvcpair = Hash.new allhosts = enum_hosts($svc_schedule) allhosts.each do |host| hostservices = enum_services(host) hostservices.each do |service| hostsvcpair[allhosts.index(host)] = {"host"=>host,"service"=>service} puts "\e[1m#{allhosts.index(host)}\e[0m - #{host} - #{service}" end end puts "Please pick a service to delete by entering the ID of the service entry:" service_id = gets.chomp.to_i puts "You are requesting to delete the downtime ID #{service_id}: #{hostsvcpair[service_id]["host"]} - #{hostsvcpair[service_id]["service"]}. Is this correct? (Y/N)" answer = gets.chomp exit end def add_downtime(entry) if $svc_schedule.has_key?("#{entry.keys.to_s}") then host_schedule = $svc_schedule["#{entry.keys.to_s}"].merge(entry["#{entry.keys.to_s}"]) $svc_schedule["#{entry.keys.to_s}"] = host_schedule else $svc_schedule.merge!(entry) end open(CONFIG['service_schedule_file'], 'w') { |f| YAML.dump($svc_schedule, f) } exit end def delete_downtime(host,service) $svc_schedule.delete($svc_schedule["#{host}"]["#{service}"]) open(CONFIG['service_schedule_file'], 'w') { |f| YAML.dump(new_schedule, f) } exit end def validate_downtime end def enum_hosts(svchash) hosts = Array.new svchash.each_key do |host| hosts << host end return hosts end def enum_services(host) services = Array.new $svc_schedule[host].each_key do |service| services << service end return services end def main $svc_schedule.each_key do |host| puts "Host - #{host}\n" if OPTIONS[:verbose] $svc_schedule[host].each_key do |service| details = $svc_schedule[host][service] schedule_downtime(gen_svc_downtime(host,service,details['start'],details['stop'],details['occurance'],details['comment'])) end end end def setup puts "You are about to be asked a series of questions to help setup your downtime scheduling." puts "Please verify each entry before submitting as there is currently NO validation for user input." puts "Enter the full path and filename of the Nagios External Command File (e.g. /var/spool/nagios/rw/nagios.cmd):" nagios_command_file = gets.chomp puts "Enter the path for the Nagios configuration directory (usually /etc/nagios or /usr/local/etc/nagios):" config_path = gets.chomp puts "Three files will be created in the path you have entered: downtime.cfg, host_schedule.cfg and svc_schedule.cfg." puts "These are YAML files. Feel free to inspect them but be warned that if you break them, you will need to reinitialize and readd your services." puts "Do this at your own risk" config_file = config_path + "/downtime.cfg" service_file = config_path + "/svc_schedule.cfg" host_file = config_path + "/host_schedule.cfg" base_yaml = "--- #Empty schedule file" config = {"nagios_cmd_file"=>nagios_command_file, "service_schedule_file"=>service_file, "host_schedule_file"=>host_file} open(config_file, "w") { |f| YAML.dump(config, f) } File.open(service_file, 'w') {|f| f.write(base_yaml) } File.open(host_file, 'w') {|f| f.write(base_yaml) } puts "Config files created. Please run: 'downtime.rb -c #{config_file} -a' to add services" exit end setup if OPTIONS[:initialize] begin CONFIG = YAML::load(File.open(OPTIONS[:config])) rescue setup end begin $svc_schedule = YAML::load(File.open(CONFIG['service_schedule_file'])) if $svc_schedule.nil? then $svc_schedule=Hash.new end rescue $svc_schedule = Hash.new end NAGIOS_CMD_FILE = CONFIG['nagios_cmd_file'] # We're not doing host downtime right now #host_schedule = YAML::load(File.open(config['host_schedule_file'])) display_config if OPTIONS[:display] add_downtime_interactive if OPTIONS[:add] del_downtime_interactive if OPTIONS[:remove] main