From 9973ed878f41d0269a2be75196a8114484e99e79 Mon Sep 17 00:00:00 2001 From: "kirby@puppetlabs.com" Date: Fri, 24 Apr 2020 15:14:47 -0700 Subject: [PATCH] Implement redis connection pooling, multi, and pipelines. --- bin/vmpooler | 1 + docker/docker-compose.yml | 2 +- lib/vmpooler.rb | 6 + lib/vmpooler/api/v1.rb | 19 +- lib/vmpooler/pool_manager.rb | 406 +++++++++++++++--------------- lib/vmpooler/providers/vsphere.rb | 4 +- 6 files changed, 234 insertions(+), 204 deletions(-) diff --git a/bin/vmpooler b/bin/vmpooler index b3ce6d7..cd6074c 100755 --- a/bin/vmpooler +++ b/bin/vmpooler @@ -36,6 +36,7 @@ if torun.include? 'manager' Vmpooler::PoolManager.new( config, Vmpooler.new_logger(logger_file), + Vmpooler.redis_connection_pool(redis_host, redis_port, redis_password), metrics ).execute! end diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index a42fe09..253f395 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -20,7 +20,7 @@ services: - VMPOOLER_DEBUG=true # for use of dummy auth - VMPOOLER_CONFIG_FILE=/etc/vmpooler/vmpooler.yaml - REDIS_SERVER=redislocal - - LOGFILE=/dev/stdout + - LOGFILE=/dev/null - EXTRA_CONFIG=/etc/vmpooler/providers.yaml image: vmpooler-local depends_on: diff --git a/lib/vmpooler.rb b/lib/vmpooler.rb index 0bcbf91..cf5fd67 100644 --- a/lib/vmpooler.rb +++ b/lib/vmpooler.rb @@ -156,6 +156,12 @@ module Vmpooler pools end + def self.redis_connection_pool(host, port, password, size = 10) + ConnectionPool.new(size: size) { + redis_connection(host, port, password) + } + end + def self.redis_connection(host = 'localhost', port = nil, password = nil) Redis.new(host: host, port: port, password: password) end diff --git a/lib/vmpooler/api/v1.rb b/lib/vmpooler/api/v1.rb index de577cd..5a21ee2 100644 --- a/lib/vmpooler/api/v1.rb +++ b/lib/vmpooler/api/v1.rb @@ -79,7 +79,8 @@ module Vmpooler end def evaluate_template_aliases(template, count) - template_backends = [template] + template_backends = [] + template_backends << template if backend.sismember('vmpooler__pools', template) selection = [] aliases = get_template_aliases(template) if aliases @@ -346,7 +347,7 @@ module Vmpooler payload.delete('request_id') payload.each do |poolname, count| selection = evaluate_template_aliases(poolname, count) - selection.map { |alias_name, alias_count| platforms_with_aliases << "#{poolname}:#{alias_name}:#{alias_count}" } + selection.map { |selected_pool, selected_pool_count| platforms_with_aliases << "#{poolname}:#{selected_pool}:#{selected_pool_count}" } end platforms_string = platforms_with_aliases.join(',') @@ -904,9 +905,21 @@ module Vmpooler platform_parts = request_hash['requested'].split(',') platform_parts.each do |platform| pool_alias, pool, _count = platform.split(':') - result[pool_alias] = { 'hostname': request_hash[pool].split(':') } + instances = backend.smembers("vmpooler__#{request_id}__#{pool_alias}__#{pool}") + result[pool_alias] = { 'hostname': instances } + #backend.del("vmpooler__#{request_id}__#{pool_alias}__#{pool}") end status 200 + else + platform_parts = request_hash['requested'].split(',') + platform_parts.each do |platform| + pool_alias, pool, count = platform.split(':') + instance_count = backend.scard("vmpooler__#{request_id}__#{pool_alias}__#{pool}") + result[pool_alias] = { + 'ready': instance_count.to_s, + 'pending': (count.to_i - instance_count.to_i).to_s + } + end end result diff --git a/lib/vmpooler/pool_manager.rb b/lib/vmpooler/pool_manager.rb index 4e30368..5bb72f8 100644 --- a/lib/vmpooler/pool_manager.rb +++ b/lib/vmpooler/pool_manager.rb @@ -2,6 +2,7 @@ require 'vmpooler/providers' require 'spicy-proton' +require 'redis' module Vmpooler class PoolManager @@ -9,7 +10,7 @@ module Vmpooler CHECK_LOOP_DELAY_MAX_DEFAULT = 60 CHECK_LOOP_DELAY_DECAY_DEFAULT = 2.0 - def initialize(config, logger, metrics) + def initialize(config, logger, redis_connection_pool, metrics) $config = config # Load logger library @@ -19,24 +20,18 @@ module Vmpooler $metrics = metrics # Redis connection pool - @redis = ConnectionPool.new(size: 10) { - Vmpooler.redis_connection( - $config[:config][:redis]['server'], - $config[:config][:redis]['port'], - $config[:config][:redis]['password'] - ) - } + @redis = redis_connection_pool # VM Provider objects - $providers = {} + $providers = Concurrent::Hash.new # Our thread-tracker object - $threads = {} + $threads = Concurrent::Hash.new # Pool mutex - @reconfigure_pool = {} + @reconfigure_pool = Concurrent::Hash.new - @vm_mutex = {} + @vm_mutex = Concurrent::Hash.new # Name generator for generating host names @name_generator = Spicy::Proton.new @@ -85,7 +80,10 @@ module Vmpooler _check_pending_vm(vm, pool, timeout, provider) rescue StandardError => e $logger.log('s', "[!] [#{pool}] '#{vm}' #{timeout} #{provider} errored while checking a pending vm : #{e}") - fail_pending_vm(vm, pool, timeout) + @redis.with do |redis| + request_id = redis.hget("vmpooler__vm__#{vm}", 'request_id') + fail_pending_vm(vm, pool, timeout, redis, request_id=request_id) + end raise end end @@ -96,11 +94,13 @@ module Vmpooler return if mutex.locked? mutex.synchronize do - request_id = redis.hget("vmpooler__vm__#{vm}", 'request_id') - if provider.vm_ready?(pool, vm) - move_pending_vm_to_ready(vm, pool, request_id, redis) - else - fail_pending_vm(vm, pool, timeout, redis, request_id = request_id) + @redis.with do |redis| + request_id = redis.hget("vmpooler__vm__#{vm}", 'request_id') + if provider.vm_ready?(pool, vm) + move_pending_vm_to_ready(vm, pool, request_id, redis) + else + fail_pending_vm(vm, pool, timeout, redis, request_id = request_id) + end end end end @@ -114,10 +114,14 @@ module Vmpooler clone_stamp = redis.hget("vmpooler__vm__#{vm}", 'clone') return true unless clone_stamp + # if clone_stamp == 'QUEUED' + # $logger.log('s', "Waiting for clone_stamp. Got 'QUEUED'.") + # return true + # end time_since_clone = (Time.now - Time.parse(clone_stamp)) / 60 if time_since_clone > timeout if exists - pool_alias = get_alias_for_request(pool, request_id, redis) if $request_id + pool_alias = redis.hget("vmpooler__vm__#{vm}", 'pool_alias') if $request_id redis.multi redis.smove('vmpooler__pending__' + pool, 'vmpooler__completed__' + pool, vm) redis.zadd('vmpooler__odcreate__task', 1, "#{pool_alias}:#{pool_name}:1:#{request_id}") if request_id @@ -136,32 +140,36 @@ module Vmpooler def move_pending_vm_to_ready(vm, pool, request_id, redis) clone_time = redis.hget('vmpooler__vm__' + vm, 'clone') - finish = format('%