class PingdomToGraphite::CLI

Public Instance Methods

advice() click to toggle source
# File lib/pingdom-to-graphite/cli.rb, line 78
def advice
  load_config!
  total_checks = @config["pingdom"]["checks"].count
  calls_per_check = 2 + (total_checks)
  puts "You have #{total_checks} monitored checks. Given a 48000/day API limit:"
  every_minutes = 5
  begin
    daily_calls = 60*24 / every_minutes * calls_per_check
    puts "Every #{every_minutes} Minutes: #{daily_calls}/day - #{daily_calls < 48000 ? "WORKS" : "won't work"}"
    every_minutes += 5
  end until (daily_calls < 48000)
end
backfill(check_id) click to toggle source
# File lib/pingdom-to-graphite/cli.rb, line 164
def backfill(check_id)
  load_config!
  load_state!
  # Check the state file
  if @state.has_key?(check_id) && @state[check_id].has_key?("earliest_ts")
    earliest_ts = @state[check_id.to_s]["earliest_ts"]
  else
    error("You can't backfill a check you've never run an update on.")
  end
  load_probe_list!
  load_check_list!
  datapull = get_datapull
  chunk = 10
  unless limit = options.limit
    limit = ask("You have #{datapull.effective_limit} API calls remaining. How many would you like to use?").to_i
  end
  created_ts = datapull.check(check_id).created

  # Keep within the API limits
  working_towards = (earliest_ts - created_ts) > 2678400 ? 31.days.ago.to_i : created_ts
  puts "Backfilling from #{Time.at(earliest_ts)} working towards #{Time.at(working_towards)}. Check began on #{Time.at(created_ts)}"
  # Break it into chunks
  additions = 0
  (limit.to_i.div(chunk)+1).times do
    batch_count = pull_and_push(check_id, working_towards, earliest_ts, chunk)
    puts "#{batch_count} metrics pushed in this batch." if options.verbose
    additions += batch_count
  end
  puts "#{additions} metrics sent to graphite for check #{check_id}."
end
init() click to toggle source
# File lib/pingdom-to-graphite/cli.rb, line 31
def init
  config_path = File.expand_path(options.config)

  if File.exists?(config_path)
    error("Config file already exists. (#{options.config})")
  else

    # Make sure we have a directory to put the config in
    unless File.directory?(File.dirname(config_path))
      FileUtils.mkdir_p(File.dirname(config_path), :mode => 0700)
    end

    # A nice little defaults file.
    settings = {
      "pingdom"   => {
        "username"  => "YOUR_USERNAME",
        "password"  => "YOUR_PASSWORD",
        "key"       => "YOUR_API_KEY",
        "checks"    => ["CHECK_ID_1","CHECK_ID_2"]
      },
      "graphite"  => {
        "host"  => "YOUR_SERVER",
        "port"    => "2003",
        "prefix"  => "pingdom"
      }
    }
    File.open(File.expand_path(options.config),"w",0600) do |f|
      f.write(JSON.pretty_generate(settings))
    end

  end

end
init_checks(check_regex=nil) click to toggle source
# File lib/pingdom-to-graphite/cli.rb, line 66
def init_checks(check_regex=nil)
  @check_regex = check_regex
  load_config!
  load_check_list!
  @config["pingdom"]["checks"] = @checks.keys
  File.open(File.expand_path(options.config),"w",0600) do |f|
    f.write(JSON.pretty_generate(@config))
  end
  puts "Added #{@checks.count} checks to #{options.config}"
end
list() click to toggle source
# File lib/pingdom-to-graphite/cli.rb, line 92
def list
  load_check_list!
  @checks.each do |check_id, check|
    puts "#{check.name} (#{check.id}) - #{check.status}"
  end
end
probes() click to toggle source
# File lib/pingdom-to-graphite/cli.rb, line 100
def probes
  load_probe_list!
  @probes.each do |probe_id, probe|
    puts "#{probe.countryiso} - #{probe.city}"
  end
end
results(check_id) click to toggle source
# File lib/pingdom-to-graphite/cli.rb, line 118
def results(check_id)
  load_config!
  load_probe_list!
  start_time = (options.start_time) ? DateTime.parse(options.start_time).to_i : Time.now.to_i - 3600
  end_time = (options.end_time) ? DateTime.parse(options.end_time).to_i : Time.now.to_i
  if start_time - end_time > 2764800
    error("Date range must be less then 32 days.")
  end
  datapull = get_datapull
  datapull.results(check_id, start_time, end_time).each do |result|
    #<Pingdom::Result probeid: 33 time: 1343945109 status: "up" responsetime: 1103 statusdesc: "OK" statusdesclong: "OK">
    puts "#{Time.at(result.time)}: #{result.status} - #{result.responsetime}ms (#{@probes[result.probeid].name})"
  end
  puts datapull.friendly_limit
end
update() click to toggle source
# File lib/pingdom-to-graphite/cli.rb, line 136
def update
  load_config!
  load_state!
  load_probe_list!
  load_check_list!
  datapull = get_datapull

  @config["pingdom"]["checks"].each do |check_id|
    puts "Check #{check_id}: " if options.verbose
    # Check the state file
    check_state = @state.has_key?(check_id.to_s) ? @state[check_id.to_s] : Hash.new
    latest_ts = check_state.has_key?("latest_ts") ? check_state["latest_ts"] : 1.hour.ago.to_i
    # API limits to 2764800 seconds, so we'll use that (minutes 30 seconds)
    limit_ts = 2764770.seconds.ago.to_i
    latest_ts = (latest_ts.to_i < limit_ts) ? limit_ts : latest_ts
    new_records = pull_and_push(check_id, latest_ts)
    puts "#{new_records} metrics sent to graphite for check #{check_id}."
  end
  puts datapull.friendly_limit
end

Private Instance Methods

error(message) click to toggle source
# File lib/pingdom-to-graphite/cli.rb, line 325
def error(message)
  STDERR.puts "ERROR: #{message}"
  exit 1
end
get_datapull() click to toggle source
# File lib/pingdom-to-graphite/cli.rb, line 197
def get_datapull
  if @datapull.nil?
    load_config!
    @datapull = PingdomToGraphite::DataPull.new(@config["pingdom"]["username"], @config["pingdom"]["password"], @config["pingdom"]["key"], log_level)
  end
  @datapull
end
get_datapush() click to toggle source
# File lib/pingdom-to-graphite/cli.rb, line 205
def get_datapush
  load_config!
  datapush = PingdomToGraphite::DataPush.new(@config["graphite"]["host"], @config["graphite"]["port"])
end
load_check_list!() click to toggle source

Store the check list in the object for reference (less api calls)

# File lib/pingdom-to-graphite/cli.rb, line 259
def load_check_list!
  load_config!
  datapull = get_datapull
  @checks = Hash.new
  datapull.checks.each do |check|
    # {"name"=>"Autocomplete", "id"=>259103, "type"=>"http", "lastresponsetime"=>203173,
    #  "status"=>"up", "lasttesttime"=>1298102416}
    if @check_regex
      if check.name =~ /#{@check_regex}/
        @checks[check.id] = check
      end
    else
      @checks[check.id] = check
    end
  end
end
load_config!() click to toggle source
# File lib/pingdom-to-graphite/cli.rb, line 210
def load_config!
  if @config.nil?
    config_file = File.expand_path(options.config)
    unless File.exists?(config_file)
      error("Missing config file (#{options.config})")
    end

    @config = JSON::parse(File.read(config_file));
  end

end
load_probe_list!() click to toggle source

Store the list in the object for reference (less api calls)

# File lib/pingdom-to-graphite/cli.rb, line 247
def load_probe_list!
  config_file = File.expand_path(options.config)
  datapull = get_datapull
  @probes = Hash.new
  datapull.probes.each do |probe|
    # {"city"=>"Manchester", "name"=>"Manchester, UK", "country"=>"United Kingdom",
    # "countryiso"=>"GB", "id"=>46, "ip"=>"212.84.74.156", "hostname"=>"s424.pingdom.com", "active"=>true}
    @probes[probe.id] = probe
  end
end
load_state!() click to toggle source
# File lib/pingdom-to-graphite/cli.rb, line 222
def load_state!
  state_file = File.expand_path(options.state)
  if File.exists?(state_file)
    @state = JSON::parse(File.read(state_file))
  else
    @state = Hash.new
  end
end
log_level() click to toggle source
# File lib/pingdom-to-graphite/cli.rb, line 330
def log_level
  options.verbose ? Logger::DEBUG : Logger::ERROR
end
parse_result(check_id, result) click to toggle source

Take a pingdom check, and return an Array of metrics to be passed to graphite

# File lib/pingdom-to-graphite/cli.rb, line 277
def parse_result(check_id, result)
  results = Array.new
  prefix = "#{@config["graphite"]["prefix"]}.#{@checks[check_id.to_i].class}."
  prefix += @checks[check_id.to_i].name.gsub(/ /,"_").gsub(/\./,"")
  check_status = result.status.eql?("up") ? 1 : 0
  check_time = Time.at(result.time).to_i
  check_city = @probes[result.probe_id].city.gsub(/ /,"_").gsub(/\./,"")
  results << GraphiteMetric::Plaintext.new("#{prefix}.status.#{@probes[result.probe_id].countryiso}.#{check_city}", check_status, check_time)
  results << GraphiteMetric::Plaintext.new("#{prefix}.responsetime.#{@probes[result.probe_id].countryiso}.#{check_city}", result.responsetime, check_time)
  results.each { |metric| puts metric } if options.verbose
  results
end
pull_and_push(check_id, latest_ts = nil, earlist_ts = nil, limit = nil) click to toggle source
# File lib/pingdom-to-graphite/cli.rb, line 290
def pull_and_push(check_id, latest_ts = nil, earlist_ts = nil, limit = nil)
  datapull = get_datapull
  datapush = get_datapush
  load_state!
  # Check the state file
  check_state = @state.has_key?(check_id.to_s) ? @state[check_id.to_s] : Hash.new
  latest_stored = check_state.has_key?("latest_ts") ? check_state["latest_ts"] : nil
  earliest_stored = check_state.has_key?("earliest_ts") ? check_state["earliest_ts"] : nil
  # Pull the data
  rec_count = 0
  result_list = Array.new
  begin
    datapull.full_results(check_id, latest_ts, earlist_ts, limit).each do |result|
      result_list += parse_result(check_id, result)
      latest_stored = result.time if latest_stored.nil? || result.time > latest_stored
      earliest_stored = result.time if earliest_stored.nil? || result.time < earliest_stored
      rec_count += 1
    end
  rescue Pingdom::Error => e
    error("Caught error from Pingdom: #{e}")
  end
  # Push to graphite
  begin
    datapush.to_graphite(result_list) unless result_list.empty?
  rescue Exception => e
    error("Failed to push to graphite: #{e}")
  end
  # Store the state
  @state[check_id] = Hash.new
  @state[check_id]["latest_ts"] = latest_stored
  @state[check_id]["earliest_ts"] = earliest_stored
  write_state!
  rec_count
end
write_state!() click to toggle source

Write the state to disk

# File lib/pingdom-to-graphite/cli.rb, line 232
def write_state!
  state_file = File.expand_path(options.state)

  # If the state dir doesn't exist create it first to prevent errors
  unless File.directory?(File.dirname(state_file))
    FileUtils.mkdir_p(File.dirname(state_file), :mode => 0700)
  end

  File.open(state_file,"w",0600) do |f|
    f.write(JSON.generate(@state))
  end
end