From 63712741a0a155250bf72a017f77f7da454066e6 Mon Sep 17 00:00:00 2001 From: "kirby@puppetlabs.com" Date: Mon, 6 Apr 2020 10:52:05 -0700 Subject: [PATCH] (POOLER-158) Getting started with provisioning on demand --- docker/Dockerfile | 4 +- lib/vmpooler/api/v1.rb | 87 ++++++++++++++++++++++++++++++++++++ lib/vmpooler/pool_manager.rb | 56 +++++++++++++++++++++++ 3 files changed, 145 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index eb2ae6b..196e94d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -8,9 +8,9 @@ # RUN: # docker run -e VMPOOLER_CONFIG -p 80:4567 -it vmpooler -FROM jruby:9.2.9-jdk +FROM jruby:9.2-jdk -ARG vmpooler_version=0.5.0 +ARG vmpooler_version=0.11.1 COPY docker/docker-entrypoint.sh /usr/local/bin/ diff --git a/lib/vmpooler/api/v1.rb b/lib/vmpooler/api/v1.rb index c588a4a..9b1cd66 100644 --- a/lib/vmpooler/api/v1.rb +++ b/lib/vmpooler/api/v1.rb @@ -277,6 +277,35 @@ module Vmpooler end end + def generate_ondemand_request(payload) + result = { 'ok': false } + + request_id = payload[:request_id] + request_id = generate_request_id if request_id.nil? + score = Time.now.to_i + + result['request_id'] = request_id + + if backend.exists("vmpooler__odrequest__#{request_id}") + result['message'] = "request_id '#{request_id}' has already been created" + return result + end + + return result unless backend.zadd('vmpooler__provisioning__request', score, request_id) + + status 201 + + requested = payload[:requested].map { |poolname, count| "#{poolname}:#{count}" }.join(',') + backend.hset("vmpooler__odrequest__#{request_id}", 'requested', requested) + + result['ok'] = true + result + end + + def generate_request_id + SecureRandom.uuid + end + get '/' do sync_pool_sizes redirect to('/dashboard/') @@ -689,6 +718,41 @@ module Vmpooler JSON.pretty_generate(result) end + post "#{api_prefix}/ondemandrequest/?" do + content_type :json + result = { 'ok' => false } + + payload = JSON.parse(request.body.read) + + if payload + invalid = invalid_templates(payload) + if invalid.empty? + result = generate_ondemand_request(payload) + else + invalid.each do |bad_template| + metrics.increment('ondemandrequest.invalid.' + bad_template) + end + status 404 + end + else + metrics.increment('ondemandrequest.invalid.unknown') + status 404 + end + + JSON.pretty_generate(result) + end + + get "#{api_prefix}/ondemandrequest/:requestid/?" do + content_type :json + + result = {'ok': false} + + status 404 + result = check_ondemand_request(payload) + + JSON.pretty_generate(result) + end + post "#{api_prefix}/vm/?" do content_type :json result = { 'ok' => false } @@ -764,6 +828,29 @@ module Vmpooler invalid end + def check_ondemand_request(payload) + result = {'ok': false} + request_id = payload[:request_id] + request_hash = backend.hgetall("vmpooler__odrequest__#{request_id}") + if request_hash.empty? + result['message'] = "no request found for request_id '#{request_id}'" + return result + end + + if request_hash['status'] == 'ready' + result['status'] = 'ready' + instances = {} + platforms = request_hash['requested'].split(',').map( { |r| r.split(':')[0] } ) + platforms.each do |platform| + instances[platform] = request_hash[platform].split(':') + end + result['instances'] = instances + status 200 + end + + result + end + post "#{api_prefix}/vm/:template/?" do content_type :json result = { 'ok' => false } diff --git a/lib/vmpooler/pool_manager.rb b/lib/vmpooler/pool_manager.rb index 65847e8..eaad5c0 100644 --- a/lib/vmpooler/pool_manager.rb +++ b/lib/vmpooler/pool_manager.rb @@ -1213,6 +1213,55 @@ module Vmpooler raise("Provider '#{provider_class}' is unknown for pool with provider name '#{provider_name}'") if provider.nil? end + def check_ondemand_requests(maxloop = 0, + loop_delay_min = CHECK_LOOP_DELAY_MIN_DEFAULT, + loop_delay_max = CHECK_LOOP_DELAY_MAX_DEFAULT, + loop_delay_decay = CHECK_LOOP_DELAY_DECAY_DEFAULT) + + # Use the pool setings if they exist + loop_delay_min = pool['check_loop_delay_min'] unless pool['check_loop_delay_min'].nil? + loop_delay_max = pool['check_loop_delay_max'] unless pool['check_loop_delay_max'].nil? + loop_delay_decay = pool['check_loop_delay_decay'] unless pool['check_loop_delay_decay'].nil? + + loop_delay_decay = 2.0 if loop_delay_decay <= 1.0 + loop_delay_max = loop_delay_min if loop_delay_max.nil? || loop_delay_max < loop_delay_min + + loop_count = 1 + loop_delay = loop_delay_min + + loop do + result = process_ondemand_requests + + loop_delay = (loop_delay * loop_delay_decay).to_i + loop_delay = loop_delay_min if result > 0 + loop_delay = loop_delay_max if loop_delay > loop_delay_max + sleep_with_wakeup_events(loop_delay, loop_delay_min, ondemand_request: true) + + unless maxloop == 0 + break if loop_count >= maxloop + + loop_count += 1 + end + end + end + + def process_ondemand_requests + requests = $redis.zrange('vmpooler__provisioning__requests', 0, -1) + return 0 if requests.empty? + + requests.each do |request_id| + create_ondemand_vms(request_id) + $redis.zrem('vmpooler__provisioning__requests', request_id) + end + + return requests.length + end + + def create_ondemand_vms(request_id) + requested = $redis.hget("vmpooler__odrequest__#{request_id}", 'requested') + requested = requested.split(',') + end + def execute!(maxloop = 0, loop_delay = 1) $logger.log('d', 'starting vmpooler') @@ -1303,6 +1352,13 @@ module Vmpooler end end + if !$threads['ondemand_provisioner'] + check_ondemand_requests + elsif !$threads['ondemand_provisioner'].alive? + $logger.log('d', '[!] [ondemand_provisioner] worker thread died, restarting') + check_ondemand_requests + end + sleep(loop_delay) unless maxloop == 0