From ab990e208122240bfb9ec0541957a0bcd69d0469 Mon Sep 17 00:00:00 2001 From: Colin Date: Wed, 15 Apr 2015 10:44:31 -0700 Subject: [PATCH] (QENG-2208) Move Sinatra Helpers to own file This moves the inline Helpers contained in V1.rb to their own file: helpers.rb. In making this change, any API.settings call was removed from the helper method itself and passed through from V1. This also adds tests for hostname shortener and validate date string. --- lib/vmpooler/api.rb | 2 +- lib/vmpooler/api/helpers.rb | 137 ++++++++++++++++++++++++++++ lib/vmpooler/api/v1.rb | 146 ++---------------------------- spec/vmpooler/api/helpers_spec.rb | 40 ++++++++ 4 files changed, 188 insertions(+), 137 deletions(-) create mode 100644 lib/vmpooler/api/helpers.rb create mode 100644 spec/vmpooler/api/helpers_spec.rb diff --git a/lib/vmpooler/api.rb b/lib/vmpooler/api.rb index 70bd319..ee51cbc 100644 --- a/lib/vmpooler/api.rb +++ b/lib/vmpooler/api.rb @@ -30,7 +30,7 @@ module Vmpooler use Vmpooler::Dashboard # Load API components - %w( dashboard reroute v1 ).each do |lib| + %w( helpers dashboard reroute v1 ).each do |lib| begin require "api/#{lib}" rescue LoadError diff --git a/lib/vmpooler/api/helpers.rb b/lib/vmpooler/api/helpers.rb new file mode 100644 index 0000000..5ce6d7e --- /dev/null +++ b/lib/vmpooler/api/helpers.rb @@ -0,0 +1,137 @@ +module Vmpooler + + class API + + module Helpers + + def mean(list) + s = list.map(&:to_f).reduce(:+).to_f + (s > 0 && list.length > 0) ? s / list.length.to_f : 0 + end + + def validate_date_str(date_str) + /^\d{4}-\d{2}-\d{2}$/ === date_str + end + + def hostname_shorten(hostname, domain=nil) + if domain && hostname =~ /^\w+\.#{domain}$/ + hostname = hostname[/[^\.]+/] + end + + hostname + end + + def get_task_times(redis, task, date_str) + redis.hvals("vmpooler__#{task}__" + date_str).map(&:to_f) + end + + def get_capacity_metrics(pools, redis) + capacity = { + current: 0, + total: 0, + percent: 0 + } + + pools.each do |pool| + pool['capacity'] = redis.scard('vmpooler__ready__' + pool['name']).to_i + + capacity[:current] += pool['capacity'] + capacity[:total] += pool['size'].to_i + end + + if capacity[:total] > 0 + capacity[:percent] = ((capacity[:current].to_f / capacity[:total].to_f) * 100.0).round(1) + end + + capacity + end + + def get_queue_metrics(pools, redis) + queue = { + pending: 0, + cloning: 0, + booting: 0, + ready: 0, + running: 0, + completed: 0, + total: 0 + } + + pools.each do |pool| + queue[:pending] += redis.scard('vmpooler__pending__' + pool['name']).to_i + queue[:ready] += redis.scard('vmpooler__ready__' + pool['name']).to_i + queue[:running] += redis.scard('vmpooler__running__' + pool['name']).to_i + queue[:completed] += redis.scard('vmpooler__completed__' + pool['name']).to_i + end + + queue[:cloning] = redis.get('vmpooler__tasks__clone').to_i + queue[:booting] = queue[:pending].to_i - queue[:cloning].to_i + queue[:booting] = 0 if queue[:booting] < 0 + queue[:total] = queue[:pending].to_i + queue[:ready].to_i + queue[:running].to_i + queue[:completed].to_i + + queue + end + + def get_task_metrics(redis, task_str, date_str, opts = {}) + opts = {:bypool => false}.merge(opts) + + task = { + duration: { + average: 0, + min: 0, + max: 0, + total: 0 + }, + count: { + total: 0 + } + } + + task[:count][:total] = redis.hlen('vmpooler__' + task_str + '__' + date_str).to_i + + if task[:count][:total] > 0 + if opts[:bypool] == true + task_times_bypool = {} + + task[:count][:pool] = {} + task[:duration][:pool] = {} + + redis.hgetall('vmpooler__' + task_str + '__' + date_str).each do |key, value| + pool = 'unknown' + hostname = 'unknown' + + if key =~ /\:/ + pool, hostname = key.split(':') + else + hostname = key + end + + task[:count][:pool][pool] ||= {} + task[:duration][:pool][pool] ||= {} + + task_times_bypool[pool] ||= [] + task_times_bypool[pool].push(value.to_f) + end + + task_times_bypool.each_key do |pool| + task[:count][:pool][pool][:total] = task_times_bypool[pool].length + + task[:duration][:pool][pool][:total] = task_times_bypool[pool].reduce(:+).to_f + task[:duration][:pool][pool][:average] = (task[:duration][:pool][pool][:total] / task[:count][:pool][pool][:total]).round(1) + task[:duration][:pool][pool][:min], task[:duration][:pool][pool][:max] = task_times_bypool[pool].minmax + end + end + + task_times = get_task_times(redis, task_str, date_str) + + task[:duration][:total] = task_times.reduce(:+).to_f + task[:duration][:average] = (task[:duration][:total] / task[:count][:total]).round(1) + task[:duration][:min], task[:duration][:max] = task_times.minmax + end + + task + end + + end + end +end diff --git a/lib/vmpooler/api/v1.rb b/lib/vmpooler/api/v1.rb index 0bab6bc..ae8ed8e 100644 --- a/lib/vmpooler/api/v1.rb +++ b/lib/vmpooler/api/v1.rb @@ -5,133 +5,7 @@ module Vmpooler api_prefix = "/api/v#{api_version}" helpers do - def get_capacity_metrics() - capacity = { - current: 0, - total: 0, - percent: 0 - } - - Vmpooler::API.settings.config[:pools].each do |pool| - pool['capacity'] = Vmpooler::API.settings.redis.scard('vmpooler__ready__' + pool['name']).to_i - - capacity[:current] += pool['capacity'] - capacity[:total] += pool['size'].to_i - end - - if capacity[:total] > 0 - capacity[:percent] = ((capacity[:current].to_f / capacity[:total].to_f) * 100.0).round(1) - end - - capacity - end - - def get_queue_metrics() - queue = { - pending: 0, - cloning: 0, - booting: 0, - ready: 0, - running: 0, - completed: 0, - total: 0 - } - - Vmpooler::API.settings.config[:pools].each do |pool| - queue[:pending] += Vmpooler::API.settings.redis.scard('vmpooler__pending__' + pool['name']).to_i - queue[:ready] += Vmpooler::API.settings.redis.scard('vmpooler__ready__' + pool['name']).to_i - queue[:running] += Vmpooler::API.settings.redis.scard('vmpooler__running__' + pool['name']).to_i - queue[:completed] += Vmpooler::API.settings.redis.scard('vmpooler__completed__' + pool['name']).to_i - end - - queue[:cloning] = Vmpooler::API.settings.redis.get('vmpooler__tasks__clone').to_i - queue[:booting] = queue[:pending].to_i - queue[:cloning].to_i - queue[:booting] = 0 if queue[:booting] < 0 - queue[:total] = queue[:pending].to_i + queue[:ready].to_i + queue[:running].to_i + queue[:completed].to_i - - queue - end - - def get_task_metrics(task_str, date_str, opts = {}) - opts = { :bypool => false }.merge(opts) - - task = { - duration: { - average: 0, - min: 0, - max: 0, - total: 0 - }, - count: { - total: 0 - } - } - - task[:count][:total] = Vmpooler::API.settings.redis.hlen('vmpooler__' + task_str + '__' + date_str).to_i - - if task[:count][:total] > 0 - if opts[:bypool] == true - task_times_bypool = {} - - task[:count][:pool] = {} - task[:duration][:pool] = {} - - Vmpooler::API.settings.redis.hgetall('vmpooler__' + task_str + '__' + date_str).each do |key, value| - pool = 'unknown' - hostname = 'unknown' - - if key =~ /\:/ - pool, hostname = key.split(':') - else - hostname = key - end - - task[:count][:pool][pool] ||= {} - task[:duration][:pool][pool] ||= {} - - task_times_bypool[pool] ||= [] - task_times_bypool[pool].push(value.to_f) - end - - task_times_bypool.each_key do |pool| - task[:count][:pool][pool][:total] = task_times_bypool[pool].length - - task[:duration][:pool][pool][:total] = task_times_bypool[pool].reduce(:+).to_f - task[:duration][:pool][pool][:average] = (task[:duration][:pool][pool][:total] / task[:count][:pool][pool][:total]).round(1) - task[:duration][:pool][pool][:min], task[:duration][:pool][pool][:max] = task_times_bypool[pool].minmax - end - end - - task_times = get_task_times(task_str, date_str) - - task[:duration][:total] = task_times.reduce(:+).to_f - task[:duration][:average] = (task[:duration][:total] / task[:count][:total]).round(1) - task[:duration][:min], task[:duration][:max] = task_times.minmax - end - - task - end - - def get_task_times(task, date_str) - Vmpooler::API.settings.redis.hvals("vmpooler__#{task}__" + date_str).map(&:to_f) - end - - def hostname_shorten(hostname) - if Vmpooler::API.settings.config[:config]['domain'] && hostname =~ /^\w+\.#{Vmpooler::API.settings.config[:config]['domain']}$/ - hostname = hostname[/[^\.]+/] - end - - hostname - end - - def mean(list) - s = list.map(&:to_f).reduce(:+).to_f - (s > 0 && list.length > 0) ? s / list.length.to_f : 0 - end - - def validate_date_str(date_str) - /^\d{4}-\d{2}-\d{2}$/ === date_str - end + include Vmpooler::API::Helpers end get "#{api_prefix}/status/?" do @@ -144,10 +18,10 @@ module Vmpooler } } - result[:capacity] = get_capacity_metrics() - result[:queue] = get_queue_metrics() - result[:clone] = get_task_metrics('clone', Date.today.to_s) - result[:boot] = get_task_metrics('boot', Date.today.to_s) + result[:capacity] = get_capacity_metrics(Vmpooler::API.settings.config[:pools], Vmpooler::API.settings.redis) + result[:queue] = get_queue_metrics(Vmpooler::API.settings.config[:pools], Vmpooler::API.settings.redis) + result[:clone] = get_task_metrics(Vmpooler::API.settings.redis, 'clone', Date.today.to_s) + result[:boot] = get_task_metrics(Vmpooler::API.settings.redis, 'boot', Date.today.to_s) # Check for empty pools Vmpooler::API.settings.config[:pools].each do |pool| @@ -225,8 +99,8 @@ module Vmpooler (from_date..to_date).each do |date| daily = { date: date.to_s, - boot: get_task_metrics('boot', date.to_s, :bypool => true), - clone: get_task_metrics('clone', date.to_s, :bypool => true) + boot: get_task_metrics(Vmpooler::API.settings.redis, 'boot', date.to_s, :bypool => true), + clone: get_task_metrics(Vmpooler::API.settings.redis, 'clone', date.to_s, :bypool => true) } result[:daily].push(daily) @@ -441,7 +315,7 @@ module Vmpooler status 404 result['ok'] = false - params[:hostname] = hostname_shorten(params[:hostname]) + params[:hostname] = hostname_shorten(params[:hostname], Vmpooler::API.settings.config[:config]['domain']) if Vmpooler::API.settings.redis.exists('vmpooler__vm__' + params[:hostname]) status 200 @@ -483,7 +357,7 @@ module Vmpooler status 404 result['ok'] = false - params[:hostname] = hostname_shorten(params[:hostname]) + params[:hostname] = hostname_shorten(params[:hostname], Vmpooler::API.settings.config[:config]['domain']) Vmpooler::API.settings.config[:pools].each do |pool| if Vmpooler::API.settings.redis.sismember('vmpooler__running__' + pool['name'], params[:hostname]) @@ -508,7 +382,7 @@ module Vmpooler status 404 result['ok'] = false - params[:hostname] = hostname_shorten(params[:hostname]) + params[:hostname] = hostname_shorten(params[:hostname], Vmpooler::API.settings.config[:config]['domain']) if Vmpooler::API.settings.redis.exists('vmpooler__vm__' + params[:hostname]) begin diff --git a/spec/vmpooler/api/helpers_spec.rb b/spec/vmpooler/api/helpers_spec.rb new file mode 100644 index 0000000..6c98640 --- /dev/null +++ b/spec/vmpooler/api/helpers_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +# A class for testing purposes that includes the Helpers. +# this is impersonating V1's `helpers do include Helpers end` +# +# This is the subject used throughout the test file. +# +class TestHelpers + include Vmpooler::API::Helpers +end + +describe Vmpooler::API::Helpers do + + subject { TestHelpers.new } + + describe '#hostname_shorten' do + [ + ['example.com', 'not-example.com', 'example.com'], + ['example.com', 'example.com', 'example.com'], + ['sub.example.com', 'example.com', 'sub'], + ['example.com', nil, 'example.com'] + ].each do |hostname, domain, expected| + it { expect(subject.hostname_shorten(hostname, domain)).to eq expected } + end + end + + describe '#validate_date_str' do + [ + ['2015-01-01', true], + [nil, false], + [false, false], + [true, false], + ['01-01-2015', false], + ['1/1/2015', false] + ].each do |date, expected| + it { expect(subject.validate_date_str(date)).to eq expected } + end + end + +end \ No newline at end of file