From 500c6cc03c3f55ddd30c0d641ddfe82eafc6a6de Mon Sep 17 00:00:00 2001 From: Rick Sherman Date: Tue, 10 May 2016 12:47:01 -0500 Subject: [PATCH] (RE-7014) Add support for statsd They way we were using graphite was incorrect for the type of data we were sending it. statsd is the appropriate mechanism for our needs. statsd and graphite are mutually exclusive and configuring statsd will take precendence over Graphite. Example of configuration in vmpooler.yaml.example --- Gemfile | 3 ++ lib/vmpooler.rb | 16 ++++++++ lib/vmpooler/pool_manager.rb | 15 ++++++-- lib/vmpooler/statsd.rb | 12 ++++++ spec/spec_helper.rb | 4 ++ spec/vmpooler/pool_manager_spec.rb | 62 +++++++++++++++++++++++++++++- vmpooler | 8 +++- vmpooler.yaml.example | 31 ++++++++++++++- 8 files changed, 144 insertions(+), 7 deletions(-) create mode 100644 lib/vmpooler/statsd.rb diff --git a/Gemfile b/Gemfile index 724b058..c985ad9 100644 --- a/Gemfile +++ b/Gemfile @@ -1,15 +1,18 @@ source ENV['GEM_SOURCE'] || 'https://rubygems.org' gem 'json', '>= 1.8' +gem 'net-ldap', '<= 0.11' gem 'rack', '>= 1.6' gem 'rake', '>= 10.4' gem 'rbvmomi', '>= 1.8' gem 'redis', '>= 3.2' gem 'sinatra', '>= 1.4' +gem 'statsd-ruby', '>= 1.3.0' # Test deps group :test do gem 'rack-test', '>= 0.6' gem 'rspec', '>= 3.2' + gem 'simplecov', '>= 0.11.2' gem 'yarjuf', '>= 2.0' end diff --git a/lib/vmpooler.rb b/lib/vmpooler.rb index 70bb819..0e3fffa 100644 --- a/lib/vmpooler.rb +++ b/lib/vmpooler.rb @@ -7,6 +7,7 @@ module Vmpooler require 'rbvmomi' require 'redis' require 'sinatra/base' + require "statsd-ruby" require 'time' require 'timeout' require 'yaml' @@ -52,6 +53,13 @@ module Vmpooler parsed_config[:graphite]['prefix'] ||= 'vmpooler' end + # statsd is an addition and my not be present in YAML configuration + if parsed_config[:statsd] + if parsed_config[:statsd]['server'] + parsed_config[:statsd]['prefix'] ||= 'vmpooler' + end + end + if parsed_config[:tagfilter] parsed_config[:tagfilter].keys.each do |tag| parsed_config[:tagfilter][tag] = Regexp.new(parsed_config[:tagfilter][tag]) @@ -79,6 +87,14 @@ module Vmpooler end end + def self.new_statsd(server, port) + if server.nil? or server.empty? or server.length == 0 + nil + else + Statsd.new server, port + end + end + def self.pools(conf) conf[:pools] end diff --git a/lib/vmpooler/pool_manager.rb b/lib/vmpooler/pool_manager.rb index 1a0454c..d309147 100644 --- a/lib/vmpooler/pool_manager.rb +++ b/lib/vmpooler/pool_manager.rb @@ -1,12 +1,17 @@ module Vmpooler class PoolManager - def initialize(config, logger, redis, graphite=nil) + def initialize(config, logger, redis, graphite=nil, statsd=nil) $config = config # Load logger library $logger = logger - unless graphite.nil? + # statsd and graphite are mutex in the context of vmpooler + unless statsd.nil? + $statsd = statsd + end + + unless graphite.nil? || !statsd.nil? $graphite = graphite end @@ -258,6 +263,7 @@ module Vmpooler $redis.decr('vmpooler__tasks__clone') begin + $statsd.timing($config[:statsd]['prefix'] + ".clone.#{vm['template']}", finish) if defined? $statsd $graphite.log($config[:graphite]['prefix'] + ".clone.#{vm['template']}", finish) if defined? $graphite rescue end @@ -565,7 +571,10 @@ module Vmpooler total = $redis.scard('vmpooler__pending__' + pool['name']) + ready begin - if defined? $graphite + if defined? $statsd + $statsd.increment($config[:statsd]['prefix'] + '.ready.' + pool['name'], $redis.scard('vmpooler__ready__' + pool['name'])) + $statsd.increment($config[:statsd]['prefix'] + '.running.' + pool['name'], $redis.scard('vmpooler__running__' + pool['name'])) + elsif defined? $graphite $graphite.log($config[:graphite]['prefix'] + '.ready.' + pool['name'], $redis.scard('vmpooler__ready__' + pool['name'])) $graphite.log($config[:graphite]['prefix'] + '.running.' + pool['name'], $redis.scard('vmpooler__running__' + pool['name'])) end diff --git a/lib/vmpooler/statsd.rb b/lib/vmpooler/statsd.rb new file mode 100644 index 0000000..7995fb5 --- /dev/null +++ b/lib/vmpooler/statsd.rb @@ -0,0 +1,12 @@ +require 'rubygems' unless defined?(Gem) + +module Vmpooler + class Statsd + def initialize( + s = 'statsd', + port = 8125 + ) + @server = Statsd.new s, port + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2186bdb..f4fbf55 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,7 @@ +require 'simplecov' +SimpleCov.start do + add_filter '/spec/' +end require 'helpers' require 'rbvmomi' require 'rspec' diff --git a/spec/vmpooler/pool_manager_spec.rb b/spec/vmpooler/pool_manager_spec.rb index a402bf4..b314ec3 100644 --- a/spec/vmpooler/pool_manager_spec.rb +++ b/spec/vmpooler/pool_manager_spec.rb @@ -5,13 +5,12 @@ describe 'Pool Manager' do let(:logger) { double('logger') } let(:redis) { double('redis') } let(:config) { {} } - let(:graphite) { nil } let(:pool) { 'pool1' } let(:vm) { 'vm1' } let(:timeout) { 5 } let(:host) { double('host') } - subject { Vmpooler::PoolManager.new(config, logger, redis, graphite) } + subject { Vmpooler::PoolManager.new(config, logger, redis) } describe '#_check_pending_vm' do let(:pool_helper) { double('pool') } @@ -252,6 +251,65 @@ describe 'Pool Manager' do end end + describe '#_stats_running_ready' do + let(:pool_helper) { double('pool') } + let(:vsphere) { {pool => pool_helper} } + let(:graphite) { double('graphite') } + let(:config) { { + config: { task_limit: 10 }, + pools: [ {'name' => 'pool1', 'size' => 5} ], + graphite: { 'prefix' => 'vmpooler' } + } } + + before do + expect(subject).not_to be_nil + $vsphere = vsphere + allow(logger).to receive(:log) + allow(pool_helper).to receive(:find_folder) + allow(redis).to receive(:smembers).and_return([]) + allow(redis).to receive(:set) + allow(redis).to receive(:get).with('vmpooler__tasks__clone').and_return(0) + allow(redis).to receive(:get).with('vmpooler__empty__pool1').and_return(nil) + end + + context 'graphite' do + let(:graphite) { double('graphite') } + subject { Vmpooler::PoolManager.new(config, logger, redis, graphite) } + + it 'increments graphite when enabled and statsd disabled' do + allow(redis).to receive(:scard).with('vmpooler__ready__pool1').and_return(1) + allow(redis).to receive(:scard).with('vmpooler__cloning__pool1').and_return(0) + allow(redis).to receive(:scard).with('vmpooler__pending__pool1').and_return(0) + allow(redis).to receive(:scard).with('vmpooler__running__pool1').and_return(5) + + expect(graphite).to receive(:log).with('vmpooler.ready.pool1', 1) + expect(graphite).to receive(:log).with('vmpooler.running.pool1', 5) + subject._check_pool(config[:pools][0]) + end + end + + context 'statsd' do + let(:statsd) { double('statsd') } + let(:config) { { + config: { task_limit: 10 }, + pools: [ {'name' => 'pool1', 'size' => 5} ], + statsd: { 'prefix' => 'vmpooler' } + } } + subject { Vmpooler::PoolManager.new(config, logger, redis, graphite, statsd) } + + it 'increments statsd when configured' do + allow(redis).to receive(:scard).with('vmpooler__ready__pool1').and_return(1) + allow(redis).to receive(:scard).with('vmpooler__cloning__pool1').and_return(0) + allow(redis).to receive(:scard).with('vmpooler__pending__pool1').and_return(0) + allow(redis).to receive(:scard).with('vmpooler__running__pool1').and_return(5) + + expect(statsd).to receive(:increment).with('vmpooler.ready.pool1', 1) + expect(statsd).to receive(:increment).with('vmpooler.running.pool1', 5) + subject._check_pool(config[:pools][0]) + end + end + end + describe '#_create_vm_snapshot' do let(:snapshot_manager) { 'snapshot_manager' } let(:pool_helper) { double('snapshot_manager') } diff --git a/vmpooler b/vmpooler index 5d0dd51..96b46a1 100755 --- a/vmpooler +++ b/vmpooler @@ -9,6 +9,11 @@ config = Vmpooler.config redis_host = config[:redis]['server'] logger_file = config[:config]['logfile'] graphite = config[:graphite]['server'] ? config[:graphite]['server'] : nil +# statsd is an addition and my not be present in YAML configuration +if config[:statsd] + statsd = config[:statsd]['server'] ? config[:statsd]['server'] : nil + statsd_port = config[:statsd]['port'] ? config[:statsd]['port'] : 8125 +end api = Thread.new { thr = Vmpooler::API.new @@ -21,7 +26,8 @@ manager = Thread.new { config, Vmpooler.new_logger(logger_file), Vmpooler.new_redis(redis_host), - Vmpooler.new_graphite(graphite) + Vmpooler.new_graphite(graphite), + Vmpooler.new_statsd(statsd, statsd_port) ).execute! } diff --git a/vmpooler.yaml.example b/vmpooler.yaml.example index 26f2c51..5476094 100644 --- a/vmpooler.yaml.example +++ b/vmpooler.yaml.example @@ -53,10 +53,39 @@ :redis: server: 'redis.company.com' + + # :statsd: + # + # This section contains the connection information required to store + # historical data via statsd. This is mutually exclusive with graphite + # and takes precedence. + # + # Available configuration parameters: + # + # - server + # The FQDN hostname of the statsd daemon. + # (optional) + # + # - prefix + # The prefix to use while storing statsd data. + # (optional; default: 'vmpooler') + # + # - port + # The UDP port to communicate with statsd daemon. + # (optional; default: 8125) + + # Example: + + :statsd: + server: 'statsd.company.com' + prefix: 'vmpooler' + port: 8125 + # :graphite: # # This section contains the connection information required to store -# historical data in an external Graphite database. +# historical data in an external Graphite database. This is mutually exclusive +# with statsd. # # Available configuration parameters: #