class Interval
Public Instance Methods
get_prop(option_name)
click to toggle source
# File lib/interval.rb, line 184 def get_prop(option_name) prop_value = nil if @options[option_name] prop_value = @options[option_name] end logger.debug "Found value #{prop_value} for #{option_name.to_s}" prop_value end
my_fail(msg)
click to toggle source
Fail, exit with an error message
# File lib/interval.rb, line 152 def my_fail(msg) logger.fatal msg if logger STDERR.puts msg send_to_sensu(1, "Error running interval for #{@options[:proc_name]}, #{msg}") store_status 1 exit 1 end
parse_time(s)
click to toggle source
Parse a time expression
The argument should be in one of the following forms
<number> - parsed as seconds <number>s - parsed as seconds <number>m - parsed as minutes <number>h - parsed as hours
@param s [String] String to parse
return [Numeric] number of seconds time represents
# File lib/interval.rb, line 129 def parse_time(s) case s when /^\d+s?$/ s.to_i when /^\d+m$/ s.to_i * 60 when /^\d+h$/ s.to_i * 60 * 60 else -1 end end
read_options(argv)
click to toggle source
Read and parse command line options
# File lib/interval.rb, line 17 def read_options(argv) @options = {} optparse = OptionParser.new do|opts| opts.banner = "Usage: interval [options] -p <proc_name> -c <command_to_run>" @options[:proc_name] = nil opts.on( '-p', '--proc_name <name>', 'Process name to use' ) do |proc_name| @options[:proc_name] = proc_name end @options[:cmd] = nil opts.on( '-c', '--cmd <path>', 'Command to run' ) do |cmd| @options[:cmd] = cmd end opts.on( '-u', '--uri_cmd <path>', 'Command to run (URI-encoded)' ) do |ucmd| @options[:cmd] = URI.unescape(ucmd) end @options[:config_name] = nil opts.on( '-C', '--config_name <name>', 'Config name to use' ) do |config_name| @options[:config_name] = config_name end @options[:home] = nil opts.on( '-h', '--home <home>', 'Home directory of application' ) do |home| @options[:home] = home end @options[:log_file] = nil opts.on( '-l', '--logfile FILE', 'Write log to FILE, defaults to STDOUT' ) do |file| @options[:log_file] = file end @options[:log_level] = 'info' opts.on( '-L', '--log_level level', 'Set the log level (debug, info, warn, error, fatal)' ) do| level| @options[:log_level] = level end @options[:count] = nil opts.on( '-n', '--num <count>', 'Run the command <count> times and then exit (use for testing)' ) do |count| @options[:count] = count.to_i end @options[:fail_on_error] = false opts.on( '-f', '--fail_on_error', 'Abort if the program fails' ) do @options[:fail_on_error] = true end @options[:sensu_enabled] = false opts.on( '-z', '--sensu', 'Do report status to Sensu' ) do @options[:sensu_enabled] = true end @options[:sensu_port] = 3030 opts.on( '--sensu_port <port>', Integer, 'Port to talk to sensu on' ) do |port| @options[:sensu_port] = port end @options[:status_file] = nil opts.on( '-s', '--status_file <status>', 'File to write last run status to' ) do |status| @options[:status_file] = status end @options[:interval] = nil opts.on( '-i', '--interval <interval>', 'Time between runs of program (seconds if no unit is specified)' ) do |interval| @options[:interval] = interval end @options[:time_stamp] = nil opts.on( '-t', '--time <time>', 'Run command at a specific time each day, could be comma separated. Format HHMM. Example 1430,0230 will run the command at 14:30 and 02:30 each day' ) do |time| @options[:time_stamp] = time.split(",") end opts.on_tail("-h", "--help", "Show this message") do puts opts exit end end begin optparse.parse!(argv) if @options[:config_name].nil? @options[:config_name] = @options[:proc_name] end mandatory = [:proc_name, :cmd] missing = mandatory.select{ |param| @options[param].nil? } unless missing.empty? STDERR.puts "Missing options: #{missing.join(', ')}" STDERR.puts optparse exit 1 end rescue OptionParser::InvalidOption, OptionParser::MissingArgument # STDERR.puts $!.to_s # Friendly output when parsing fails STDERR.puts optparse # exit 1 # end end
run()
click to toggle source
# File lib/interval.rb, line 193 def run read_options(ARGV) # Check binary unless File.executable?(@options[:cmd].split[0]) my_fail "Specified binary '#{@options[:cmd]}' does not exist or is not executable" end set_log_level @options[:log_level] set_log_file @options[:log_file] if @options[:log_file] if @options[:home] Dir.chdir(@options[:home]) end # Get properties conf_name = @options[:config_name] ? @options[:config_name] : @options[:proc_name] begin interval_in = get_prop :interval if interval_in interval = parse_time interval_in logger.info "Using an run interval of #{interval} seconds" if interval < 1 my_fail "Illegal interval #{interval} given for property #{conf_name}.run.interval" end else time_stamp = get_prop :time_stamp raise "Could not found interval or time_stamp option, one of those is needed" unless time_stamp logger.info "Will run at time stamp #{time_stamp.join(",")}" end rescue StandardError => e puts e.message my_fail "Did not find config value for #{conf_name}" end logger.info "Starting interval #{@options.inspect}" store_status 0 count = 0 first = true loop do unless time_stamp && first count = count+1 logger.info "Launching[#{count}]: #{@options[:cmd]}" result, stdout, stderr = systemu @options[:cmd] store_status result.exitstatus if result != 0 logger.error "Failed to run #{@options[:cmd]} (exit code #{result})" logger.error "Stdout: #{stdout.strip}" if stdout.length > 0 logger.error "Stderr: #{stderr.strip}" if stderr.length > 0 send_to_sensu(1, "Result is #{result}. Stderr is: #{stderr}, stdout is #{stdout}") if @options[:fail_on_error] exit result end else logger.info "Command completed successfully" logger.info "Stdout: #{stdout.strip}" if stdout.length > 0 send_to_sensu(0, "") end end first = false if @options[:count] && count >= @options[:count] exit 0 end if time_stamp diff = [] time_stamp.each do |time| current = Time.now.strftime("%H%M%S") diff_time = ((time[0..1].to_i * 60 + time[2..3].to_i) - (current[0..1].to_i * 60 + current[2..3].to_i))*60 - current[4..5].to_i diff_time += 24*60*60 if diff_time <= 0 diff << diff_time end logger.debug "sleep for #{diff.min + 1} seconds" sleep diff.min + 1 else logger.debug "sleep for #{interval} seconds" sleep interval end end end
send_to_sensu(status, message)
click to toggle source
Send message to sensu
# File lib/interval.rb, line 163 def send_to_sensu(status, message) if @options[:sensu_enabled] report = { :name => "interval_error_#{@options[:proc_name]}", :output => "", :status => status } if status != 0 report[:output] = "Failed to run #{@options[:proc_name]} #{message}" end begin s = TCPSocket.open(SENSU_HOST, @options[:sensu_port]) s.print JSON.generate(report) s.close rescue Exception => e logger.error "Failed to send report to sensu: #{e.message}" end end end
store_status(exit_code)
click to toggle source
Store status on disk
# File lib/interval.rb, line 144 def store_status(exit_code) if @options[:status_file] File.open(@options[:status_file], 'w') do |f| f.puts("result=#{exit_code}") end end end