-
-
Save jessereynolds/6495377 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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