Skip to content

Instantly share code, notes, and snippets.

@jessereynolds
Created September 9, 2013 13:10
Show Gist options
  • Save jessereynolds/6495377 to your computer and use it in GitHub Desktop.
Save jessereynolds/6495377 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'httparty'
require 'json/pure'
class VisageClient
include HTTParty
default_timeout 10
ENDPOINTS = [ 'stats-a', 'stats-b' ]
def self.acquire(opts)
raise ArgumentError "hostnames array is required" unless
opts[:hostnames] && opts[:hostnames].is_a?(Array) && ! opts[:hostnames].empty?
raise ArgumentError "plugins array is required" unless
opts[:plugins] && opts[:plugins].is_a?(Array) && ! opts[:plugins].empty?
raise ArgumentError "types array is required" unless
opts[:types] && opts[:types].is_a?(Array) && ! opts[:types].empty?
start_time = opts[:start_time]
endpoints = opts[:endpoints] || ENDPOINTS
hostnames = opts[:hostnames].join(',')
plugins = opts[:plugins].join(',')
types = opts[:types].join(',')
endpoints.each {|endpoint|
url = 'http://' + endpoint + '/data/' + hostnames + '/' + plugins + '/' + types
begin
data, meta = self.fetch(url, {:start_time => start_time, :max_tries => 1})
rescue StandardError, Timeout::Error
next
end
# lord forgive me for the sins I am committing here
if (data.keys & hostnames).empty?
next
end
data_ok = true
data.each_pair do |hostname, host_data|
plugins.each do |plugin|
types.each do |type|
unless host_data[plugin] && host_data[plugin][type] &&
host_data[plugin][type].is_a?(Hash) && host_data[plugin][type].length > 0
data_ok = false
end
if data_ok
host_data[plugin][type].each_pair do |series, series_data|
unless series_data['data'] && series_data['data'].is_a?(Array) &&
! series_data['data'].compact.empty?
data_ok = false
end
end
end
end
end
end
next unless data_ok
return data, meta
}
raise
end
def self.fetch(url, opts)
debug_output $stderr if opts[:debug]
start_time = opts[:start_time] || Time.now.to_i - 3600
max_tries = opts[:max_tries] || 2
url = url + "?start=#{start_time}"
start = Time.now
events = [{ :time => start, :summary => "doing get(#{url})" }]
tries = 0
begin
tries += 1
response = get(url)
rescue StandardError, Timeout::Error => e
if tries >= max_tries
raise e
else
puts "Error fetching #{url} (#{e}) waiting 3 seconds and retrying" if opts[:debug]
sleep 3
retry
end
end
finish = Time.now
time = (finish - start) * 1000
events << {
:time => finish,
:summary => "received response with status code: #{response.code} in #{time} milliseconds"
}
return([JSON.parse(response.body), {:events => events, :visage_time => time}])
end
# compact data series, adjust finish time accordingly
def self.trim(data)
metric, meta = self.analyse(data)
errors = metric[:errors]
events = meta[:events]
return {:errors => errors} unless errors.empty?
series = metric[:series]
orig_size = series.length
interval = metric[:interval]
start = metric[:start]
series.pop until series.empty? || series.last
size = series.length
duration = ((size - 1) * interval).to_i
finish = start + duration
trimmed = orig_size - size
events << {
:time => Time.now,
:summary => "trim: interval: #{interval}, start: #{start}, finish: #{finish}, " +
"duration: #{duration}, trimmed: #{trimmed}"
}
return [
{ :series => series,
:start => start,
:finish => finish,
:duration => duration,
:interval => interval,
:trimmed => trimmed,
:errors => errors },
{ :events => events }
]
end
def self.analyse(data)
errors = []
events = []
start = data['start'].to_i
finish = data['finish'].to_i
series = data['series']
duration = finish - start
errors << "analyse: series is nil" if series.nil?
errors << "analyse: series is empty" if series.empty?
return {:errors => errors} unless errors.empty?
errors << "analyse: series length (#{series.length}) is less than 2" if (series.length < 2)
return {:errors => errors} unless errors.empty?
size = series.length
interval = duration.to_f / (size.to_f - 1)
events << {
:time => Time.now,
:summary => "analyse: interval: #{interval}, start: #{start}, finish: #{finish}, duration: #{duration}"
}
return [
{ :series => series,
:start => start,
:finish => finish,
:duration => duration,
:interval => interval,
:errors => errors },
{ :events => events }
]
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment