vmpooler 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/vmpooler +3 -1
- data/lib/vmpooler.rb +23 -1
- data/lib/vmpooler/api/helpers.rb +1 -1
- data/lib/vmpooler/api/v1.rb +285 -17
- data/lib/vmpooler/generic_connection_pool.rb +5 -23
- data/lib/vmpooler/pool_manager.rb +753 -389
- data/lib/vmpooler/providers/base.rb +2 -1
- data/lib/vmpooler/providers/dummy.rb +2 -2
- data/lib/vmpooler/providers/vsphere.rb +50 -30
- data/lib/vmpooler/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3b49b7d743482ad4e81ac6cae5e2d3b3d4c8a15e27024c41afc8938ae3374fe
|
4
|
+
data.tar.gz: 6eea091a71b41ac346fae2098633a0e448bcfbad8c357f84bbf17bf8716a16d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b47704b269da351657da3246a6c31be8ecb6aaa20e5a4ab40f69656cccdd7c1ee94bd8661eb159e5654c270cf990afd78ec6964f7efbadca362a325bad47e83
|
7
|
+
data.tar.gz: a9481fb784e7d2f525a6c7089d8699a89e2694d43e2952c152358de3d057c3ef174a157452556b01651d6dc631de8e67120b46116c78c692bfd718a8dc008ddb
|
data/bin/vmpooler
CHANGED
@@ -7,6 +7,8 @@ config = Vmpooler.config
|
|
7
7
|
redis_host = config[:redis]['server']
|
8
8
|
redis_port = config[:redis]['port']
|
9
9
|
redis_password = config[:redis]['password']
|
10
|
+
redis_connection_pool_size = config[:redis]['connection_pool_size']
|
11
|
+
redis_connection_pool_timeout = config[:redis]['connection_pool_timeout']
|
10
12
|
logger_file = config[:config]['logfile']
|
11
13
|
|
12
14
|
metrics = Vmpooler.new_metrics(config)
|
@@ -36,7 +38,7 @@ if torun.include? 'manager'
|
|
36
38
|
Vmpooler::PoolManager.new(
|
37
39
|
config,
|
38
40
|
Vmpooler.new_logger(logger_file),
|
39
|
-
Vmpooler.
|
41
|
+
Vmpooler.redis_connection_pool(redis_host, redis_port, redis_password, redis_connection_pool_size, redis_connection_pool_timeout, metrics),
|
40
42
|
metrics
|
41
43
|
).execute!
|
42
44
|
end
|
data/lib/vmpooler.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Vmpooler
|
4
|
+
require 'concurrent'
|
4
5
|
require 'date'
|
5
6
|
require 'json'
|
6
7
|
require 'net/ldap'
|
@@ -58,9 +59,14 @@ module Vmpooler
|
|
58
59
|
|
59
60
|
# Set some configuration defaults
|
60
61
|
parsed_config[:config]['task_limit'] = string_to_int(ENV['TASK_LIMIT']) || parsed_config[:config]['task_limit'] || 10
|
62
|
+
parsed_config[:config]['ondemand_clone_limit'] = string_to_int(ENV['ONDEMAND_CLONE_LIMIT']) || parsed_config[:config]['ondemand_clone_limit'] || 10
|
63
|
+
parsed_config[:config]['max_ondemand_instances_per_request'] = string_to_int(ENV['MAX_ONDEMAND_INSTANCES_PER_REQUEST']) || parsed_config[:config]['max_ondemand_instances_per_request'] || 10
|
61
64
|
parsed_config[:config]['migration_limit'] = string_to_int(ENV['MIGRATION_LIMIT']) if ENV['MIGRATION_LIMIT']
|
62
65
|
parsed_config[:config]['vm_checktime'] = string_to_int(ENV['VM_CHECKTIME']) || parsed_config[:config]['vm_checktime'] || 1
|
63
66
|
parsed_config[:config]['vm_lifetime'] = string_to_int(ENV['VM_LIFETIME']) || parsed_config[:config]['vm_lifetime'] || 24
|
67
|
+
parsed_config[:config]['max_lifetime_upper_limit'] = string_to_int(ENV['MAX_LIFETIME_UPPER_LIMIT']) || parsed_config[:config]['max_lifetime_upper_limit']
|
68
|
+
parsed_config[:config]['ready_ttl'] = string_to_int(ENV['READY_TTL']) || parsed_config[:config]['ready_ttl'] || 60
|
69
|
+
parsed_config[:config]['ondemand_request_ttl'] = string_to_int(ENV['ONDEMAND_REQUEST_TTL']) || parsed_config[:config]['ondemand_request_ttl'] || 5
|
64
70
|
parsed_config[:config]['prefix'] = ENV['PREFIX'] || parsed_config[:config]['prefix'] || ''
|
65
71
|
|
66
72
|
parsed_config[:config]['logfile'] = ENV['LOGFILE'] if ENV['LOGFILE']
|
@@ -72,7 +78,7 @@ module Vmpooler
|
|
72
78
|
parsed_config[:config]['vm_lifetime_auth'] = string_to_int(ENV['VM_LIFETIME_AUTH']) if ENV['VM_LIFETIME_AUTH']
|
73
79
|
parsed_config[:config]['max_tries'] = string_to_int(ENV['MAX_TRIES']) if ENV['MAX_TRIES']
|
74
80
|
parsed_config[:config]['retry_factor'] = string_to_int(ENV['RETRY_FACTOR']) if ENV['RETRY_FACTOR']
|
75
|
-
parsed_config[:config]['create_folders'] = ENV['CREATE_FOLDERS'] if ENV['CREATE_FOLDERS']
|
81
|
+
parsed_config[:config]['create_folders'] = true?(ENV['CREATE_FOLDERS']) if ENV['CREATE_FOLDERS']
|
76
82
|
parsed_config[:config]['create_template_delta_disks'] = ENV['CREATE_TEMPLATE_DELTA_DISKS'] if ENV['CREATE_TEMPLATE_DELTA_DISKS']
|
77
83
|
set_linked_clone(parsed_config)
|
78
84
|
parsed_config[:config]['experimental_features'] = ENV['EXPERIMENTAL_FEATURES'] if ENV['EXPERIMENTAL_FEATURES']
|
@@ -84,6 +90,8 @@ module Vmpooler
|
|
84
90
|
parsed_config[:redis]['port'] = string_to_int(ENV['REDIS_PORT']) if ENV['REDIS_PORT']
|
85
91
|
parsed_config[:redis]['password'] = ENV['REDIS_PASSWORD'] if ENV['REDIS_PASSWORD']
|
86
92
|
parsed_config[:redis]['data_ttl'] = string_to_int(ENV['REDIS_DATA_TTL']) || parsed_config[:redis]['data_ttl'] || 168
|
93
|
+
parsed_config[:redis]['connection_pool_size'] = string_to_int(ENV['REDIS_CONNECTION_POOL_SIZE']) || parsed_config[:redis]['connection_pool_size'] || 10
|
94
|
+
parsed_config[:redis]['connection_pool_timeout'] = string_to_int(ENV['REDIS_CONNECTION_POOL_TIMEOUT']) || parsed_config[:redis]['connection_pool_timeout'] || 5
|
87
95
|
|
88
96
|
parsed_config[:statsd] = parsed_config[:statsd] || {} if ENV['STATSD_SERVER']
|
89
97
|
parsed_config[:statsd]['server'] = ENV['STATSD_SERVER'] if ENV['STATSD_SERVER']
|
@@ -117,6 +125,7 @@ module Vmpooler
|
|
117
125
|
|
118
126
|
parsed_config[:pools].each do |pool|
|
119
127
|
parsed_config[:pool_names] << pool['name']
|
128
|
+
pool['ready_ttl'] ||= parsed_config[:config]['ready_ttl']
|
120
129
|
if pool['alias']
|
121
130
|
if pool['alias'].is_a?(Array)
|
122
131
|
pool['alias'].each do |pool_alias|
|
@@ -154,6 +163,19 @@ module Vmpooler
|
|
154
163
|
pools
|
155
164
|
end
|
156
165
|
|
166
|
+
def self.redis_connection_pool(host, port, password, size, timeout, metrics)
|
167
|
+
Vmpooler::PoolManager::GenericConnectionPool.new(
|
168
|
+
metrics: metrics,
|
169
|
+
metric_prefix: 'redis_connection_pool',
|
170
|
+
size: size,
|
171
|
+
timeout: timeout
|
172
|
+
) do
|
173
|
+
connection = Concurrent::Hash.new
|
174
|
+
redis = new_redis(host, port, password)
|
175
|
+
connection['connection'] = redis
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
157
179
|
def self.new_redis(host = 'localhost', port = nil, password = nil)
|
158
180
|
Redis.new(host: host, port: port, password: password)
|
159
181
|
end
|
data/lib/vmpooler/api/helpers.rb
CHANGED
@@ -238,7 +238,7 @@ module Vmpooler
|
|
238
238
|
queue[:running] = get_total_across_pools_redis_scard(pools, 'vmpooler__running__', backend)
|
239
239
|
queue[:completed] = get_total_across_pools_redis_scard(pools, 'vmpooler__completed__', backend)
|
240
240
|
|
241
|
-
queue[:cloning] = backend.get('vmpooler__tasks__clone').to_i
|
241
|
+
queue[:cloning] = backend.get('vmpooler__tasks__clone').to_i + backend.get('vmpooler__tasks__ondemandclone').to_i
|
242
242
|
queue[:booting] = queue[:pending].to_i - queue[:cloning].to_i
|
243
243
|
queue[:booting] = 0 if queue[:booting] < 0
|
244
244
|
queue[:total] = queue[:pending].to_i + queue[:ready].to_i + queue[:running].to_i + queue[:completed].to_i
|
data/lib/vmpooler/api/v1.rb
CHANGED
@@ -42,6 +42,68 @@ module Vmpooler
|
|
42
42
|
Vmpooler::API.settings.checkoutlock
|
43
43
|
end
|
44
44
|
|
45
|
+
def get_template_aliases(template)
|
46
|
+
result = []
|
47
|
+
aliases = Vmpooler::API.settings.config[:alias]
|
48
|
+
if aliases
|
49
|
+
result += aliases[template] if aliases[template].is_a?(Array)
|
50
|
+
template_backends << aliases[template] if aliases[template].is_a?(String)
|
51
|
+
end
|
52
|
+
result
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_pool_weights(template_backends)
|
56
|
+
pool_index = pool_index(pools)
|
57
|
+
weighted_pools = {}
|
58
|
+
template_backends.each do |t|
|
59
|
+
next unless pool_index.key? t
|
60
|
+
|
61
|
+
index = pool_index[t]
|
62
|
+
clone_target = pools[index]['clone_target'] || config['clone_target']
|
63
|
+
next unless config.key?('backend_weight')
|
64
|
+
|
65
|
+
weight = config['backend_weight'][clone_target]
|
66
|
+
if weight
|
67
|
+
weighted_pools[t] = weight
|
68
|
+
end
|
69
|
+
end
|
70
|
+
weighted_pools
|
71
|
+
end
|
72
|
+
|
73
|
+
def count_selection(selection)
|
74
|
+
result = {}
|
75
|
+
selection.uniq.each do |poolname|
|
76
|
+
result[poolname] = selection.count(poolname)
|
77
|
+
end
|
78
|
+
result
|
79
|
+
end
|
80
|
+
|
81
|
+
def evaluate_template_aliases(template, count)
|
82
|
+
template_backends = []
|
83
|
+
template_backends << template if backend.sismember('vmpooler__pools', template)
|
84
|
+
selection = []
|
85
|
+
aliases = get_template_aliases(template)
|
86
|
+
if aliases
|
87
|
+
template_backends += aliases
|
88
|
+
weighted_pools = get_pool_weights(template_backends)
|
89
|
+
|
90
|
+
pickup = Pickup.new(weighted_pools) if weighted_pools.count == template_backends.count
|
91
|
+
count.to_i.times do
|
92
|
+
if pickup
|
93
|
+
selection << pickup.pick
|
94
|
+
else
|
95
|
+
selection << template_backends.sample
|
96
|
+
end
|
97
|
+
end
|
98
|
+
else
|
99
|
+
count.to_i.times do
|
100
|
+
selection << template
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
count_selection(selection)
|
105
|
+
end
|
106
|
+
|
45
107
|
def fetch_single_vm(template)
|
46
108
|
template_backends = [template]
|
47
109
|
aliases = Vmpooler::API.settings.config[:alias]
|
@@ -245,11 +307,9 @@ module Vmpooler
|
|
245
307
|
pool_index = pool_index(pools)
|
246
308
|
template_configs = backend.hgetall('vmpooler__config__template')
|
247
309
|
template_configs&.each do |poolname, template|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
end
|
252
|
-
end
|
310
|
+
next unless pool_index.include? poolname
|
311
|
+
|
312
|
+
pools[pool_index[poolname]]['template'] = template
|
253
313
|
end
|
254
314
|
end
|
255
315
|
|
@@ -257,11 +317,9 @@ module Vmpooler
|
|
257
317
|
pool_index = pool_index(pools)
|
258
318
|
poolsize_configs = backend.hgetall('vmpooler__config__poolsize')
|
259
319
|
poolsize_configs&.each do |poolname, size|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
end
|
264
|
-
end
|
320
|
+
next unless pool_index.include? poolname
|
321
|
+
|
322
|
+
pools[pool_index[poolname]]['size'] = size.to_i
|
265
323
|
end
|
266
324
|
end
|
267
325
|
|
@@ -269,14 +327,69 @@ module Vmpooler
|
|
269
327
|
pool_index = pool_index(pools)
|
270
328
|
clone_target_configs = backend.hgetall('vmpooler__config__clone_target')
|
271
329
|
clone_target_configs&.each do |poolname, clone_target|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
end
|
276
|
-
end
|
330
|
+
next unless pool_index.include? poolname
|
331
|
+
|
332
|
+
pools[pool_index[poolname]]['clone_target'] = clone_target
|
277
333
|
end
|
278
334
|
end
|
279
335
|
|
336
|
+
def too_many_requested?(payload)
|
337
|
+
payload&.each do |_poolname, count|
|
338
|
+
next unless count.to_i > config['max_ondemand_instances_per_request']
|
339
|
+
|
340
|
+
return true
|
341
|
+
end
|
342
|
+
false
|
343
|
+
end
|
344
|
+
|
345
|
+
def generate_ondemand_request(payload)
|
346
|
+
result = { 'ok': false }
|
347
|
+
|
348
|
+
requested_instances = payload.reject { |k, _v| k == 'request_id' }
|
349
|
+
if too_many_requested?(requested_instances)
|
350
|
+
result['message'] = "requested amount of instances exceeds the maximum #{config['max_ondemand_instances_per_request']}"
|
351
|
+
status 403
|
352
|
+
return result
|
353
|
+
end
|
354
|
+
|
355
|
+
score = Time.now.to_i
|
356
|
+
request_id = payload['request_id']
|
357
|
+
request_id ||= generate_request_id
|
358
|
+
result['request_id'] = request_id
|
359
|
+
|
360
|
+
if backend.exists("vmpooler__odrequest__#{request_id}")
|
361
|
+
result['message'] = "request_id '#{request_id}' has already been created"
|
362
|
+
status 409
|
363
|
+
return result
|
364
|
+
end
|
365
|
+
|
366
|
+
status 201
|
367
|
+
|
368
|
+
platforms_with_aliases = []
|
369
|
+
requested_instances.each do |poolname, count|
|
370
|
+
selection = evaluate_template_aliases(poolname, count)
|
371
|
+
selection.map { |selected_pool, selected_pool_count| platforms_with_aliases << "#{poolname}:#{selected_pool}:#{selected_pool_count}" }
|
372
|
+
end
|
373
|
+
platforms_string = platforms_with_aliases.join(',')
|
374
|
+
|
375
|
+
return result unless backend.zadd('vmpooler__provisioning__request', score, request_id)
|
376
|
+
|
377
|
+
backend.hset("vmpooler__odrequest__#{request_id}", 'requested', platforms_string)
|
378
|
+
if Vmpooler::API.settings.config[:auth] and has_token?
|
379
|
+
backend.hset("vmpooler__odrequest__#{request_id}", 'token:token', request.env['HTTP_X_AUTH_TOKEN'])
|
380
|
+
backend.hset("vmpooler__odrequest__#{request_id}", 'token:user',
|
381
|
+
backend.hget('vmpooler__token__' + request.env['HTTP_X_AUTH_TOKEN'], 'user'))
|
382
|
+
end
|
383
|
+
|
384
|
+
result['domain'] = config['domain'] if config['domain']
|
385
|
+
result[:ok] = true
|
386
|
+
result
|
387
|
+
end
|
388
|
+
|
389
|
+
def generate_request_id
|
390
|
+
SecureRandom.uuid
|
391
|
+
end
|
392
|
+
|
280
393
|
get '/' do
|
281
394
|
sync_pool_sizes
|
282
395
|
redirect to('/dashboard/')
|
@@ -395,7 +508,7 @@ module Vmpooler
|
|
395
508
|
end
|
396
509
|
|
397
510
|
# for backwards compatibility, include separate "empty" stats in "status" block
|
398
|
-
if ready == 0
|
511
|
+
if ready == 0 && max != 0
|
399
512
|
result[:status][:empty] ||= []
|
400
513
|
result[:status][:empty].push(pool['name'])
|
401
514
|
|
@@ -689,6 +802,88 @@ module Vmpooler
|
|
689
802
|
JSON.pretty_generate(result)
|
690
803
|
end
|
691
804
|
|
805
|
+
post "#{api_prefix}/ondemandvm/?" do
|
806
|
+
content_type :json
|
807
|
+
|
808
|
+
need_token! if Vmpooler::API.settings.config[:auth]
|
809
|
+
|
810
|
+
result = { 'ok' => false }
|
811
|
+
|
812
|
+
begin
|
813
|
+
payload = JSON.parse(request.body.read)
|
814
|
+
|
815
|
+
if payload
|
816
|
+
invalid = invalid_templates(payload.reject { |k, _v| k == 'request_id' })
|
817
|
+
if invalid.empty?
|
818
|
+
result = generate_ondemand_request(payload)
|
819
|
+
else
|
820
|
+
result[:bad_templates] = invalid
|
821
|
+
invalid.each do |bad_template|
|
822
|
+
metrics.increment('ondemandrequest.invalid.' + bad_template)
|
823
|
+
end
|
824
|
+
status 404
|
825
|
+
end
|
826
|
+
else
|
827
|
+
metrics.increment('ondemandrequest.invalid.unknown')
|
828
|
+
status 404
|
829
|
+
end
|
830
|
+
rescue JSON::ParserError
|
831
|
+
status 400
|
832
|
+
result = {
|
833
|
+
'ok' => false,
|
834
|
+
'message' => 'JSON payload could not be parsed'
|
835
|
+
}
|
836
|
+
end
|
837
|
+
|
838
|
+
JSON.pretty_generate(result)
|
839
|
+
end
|
840
|
+
|
841
|
+
post "#{api_prefix}/ondemandvm/:template/?" do
|
842
|
+
content_type :json
|
843
|
+
result = { 'ok' => false }
|
844
|
+
|
845
|
+
need_token! if Vmpooler::API.settings.config[:auth]
|
846
|
+
|
847
|
+
payload = extract_templates_from_query_params(params[:template])
|
848
|
+
|
849
|
+
if payload
|
850
|
+
invalid = invalid_templates(payload.reject { |k, _v| k == 'request_id' })
|
851
|
+
if invalid.empty?
|
852
|
+
result = generate_ondemand_request(payload)
|
853
|
+
else
|
854
|
+
result[:bad_templates] = invalid
|
855
|
+
invalid.each do |bad_template|
|
856
|
+
metrics.increment('ondemandrequest.invalid.' + bad_template)
|
857
|
+
end
|
858
|
+
status 404
|
859
|
+
end
|
860
|
+
else
|
861
|
+
metrics.increment('ondemandrequest.invalid.unknown')
|
862
|
+
status 404
|
863
|
+
end
|
864
|
+
|
865
|
+
JSON.pretty_generate(result)
|
866
|
+
end
|
867
|
+
|
868
|
+
get "#{api_prefix}/ondemandvm/:requestid/?" do
|
869
|
+
content_type :json
|
870
|
+
|
871
|
+
status 404
|
872
|
+
result = check_ondemand_request(params[:requestid])
|
873
|
+
|
874
|
+
JSON.pretty_generate(result)
|
875
|
+
end
|
876
|
+
|
877
|
+
delete "#{api_prefix}/ondemandvm/:requestid/?" do
|
878
|
+
content_type :json
|
879
|
+
need_token! if Vmpooler::API.settings.config[:auth]
|
880
|
+
|
881
|
+
status 404
|
882
|
+
result = delete_ondemand_request(params[:requestid])
|
883
|
+
|
884
|
+
JSON.pretty_generate(result)
|
885
|
+
end
|
886
|
+
|
692
887
|
post "#{api_prefix}/vm/?" do
|
693
888
|
content_type :json
|
694
889
|
result = { 'ok' => false }
|
@@ -764,6 +959,78 @@ module Vmpooler
|
|
764
959
|
invalid
|
765
960
|
end
|
766
961
|
|
962
|
+
def check_ondemand_request(request_id)
|
963
|
+
result = { 'ok' => false }
|
964
|
+
request_hash = backend.hgetall("vmpooler__odrequest__#{request_id}")
|
965
|
+
if request_hash.empty?
|
966
|
+
result['message'] = "no request found for request_id '#{request_id}'"
|
967
|
+
return result
|
968
|
+
end
|
969
|
+
|
970
|
+
result['request_id'] = request_id
|
971
|
+
result['ready'] = false
|
972
|
+
result['ok'] = true
|
973
|
+
status 202
|
974
|
+
|
975
|
+
if request_hash['status'] == 'ready'
|
976
|
+
result['ready'] = true
|
977
|
+
platform_parts = request_hash['requested'].split(',')
|
978
|
+
platform_parts.each do |platform|
|
979
|
+
pool_alias, pool, _count = platform.split(':')
|
980
|
+
instances = backend.smembers("vmpooler__#{request_id}__#{pool_alias}__#{pool}")
|
981
|
+
result[pool_alias] = { 'hostname': instances }
|
982
|
+
end
|
983
|
+
result['domain'] = config['domain'] if config['domain']
|
984
|
+
status 200
|
985
|
+
elsif request_hash['status'] == 'failed'
|
986
|
+
result['message'] = "The request failed to provision instances within the configured ondemand_request_ttl '#{config['ondemand_request_ttl']}'"
|
987
|
+
status 200
|
988
|
+
elsif request_hash['status'] == 'deleted'
|
989
|
+
result['message'] = 'The request has been deleted'
|
990
|
+
status 200
|
991
|
+
else
|
992
|
+
platform_parts = request_hash['requested'].split(',')
|
993
|
+
platform_parts.each do |platform|
|
994
|
+
pool_alias, pool, count = platform.split(':')
|
995
|
+
instance_count = backend.scard("vmpooler__#{request_id}__#{pool_alias}__#{pool}")
|
996
|
+
result[pool_alias] = {
|
997
|
+
'ready': instance_count.to_s,
|
998
|
+
'pending': (count.to_i - instance_count.to_i).to_s
|
999
|
+
}
|
1000
|
+
end
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
result
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
def delete_ondemand_request(request_id)
|
1007
|
+
result = { 'ok' => false }
|
1008
|
+
|
1009
|
+
platforms = backend.hget("vmpooler__odrequest__#{request_id}", 'requested')
|
1010
|
+
unless platforms
|
1011
|
+
result['message'] = "no request found for request_id '#{request_id}'"
|
1012
|
+
return result
|
1013
|
+
end
|
1014
|
+
|
1015
|
+
if backend.hget("vmpooler__odrequest__#{request_id}", 'status') == 'deleted'
|
1016
|
+
result['message'] = 'the request has already been deleted'
|
1017
|
+
else
|
1018
|
+
backend.hset("vmpooler__odrequest__#{request_id}", 'status', 'deleted')
|
1019
|
+
|
1020
|
+
platforms.split(',').each do |platform|
|
1021
|
+
pool_alias, pool, _count = platform.split(':')
|
1022
|
+
backend.smembers("vmpooler__#{request_id}__#{pool_alias}__#{pool}")&.each do |vm|
|
1023
|
+
backend.smove("vmpooler__running__#{pool}", "vmpooler__completed__#{pool}", vm)
|
1024
|
+
end
|
1025
|
+
backend.del("vmpooler__#{request_id}__#{pool_alias}__#{pool}")
|
1026
|
+
end
|
1027
|
+
backend.expire("vmpooler__odrequest__#{request_id}", 129_600_0)
|
1028
|
+
end
|
1029
|
+
status 200
|
1030
|
+
result['ok'] = true
|
1031
|
+
result
|
1032
|
+
end
|
1033
|
+
|
767
1034
|
post "#{api_prefix}/vm/:template/?" do
|
768
1035
|
content_type :json
|
769
1036
|
result = { 'ok' => false }
|
@@ -923,6 +1190,7 @@ module Vmpooler
|
|
923
1190
|
unless arg.to_i > 0
|
924
1191
|
failure.push("You provided a lifetime (#{arg}) but you must provide a positive number.")
|
925
1192
|
end
|
1193
|
+
|
926
1194
|
when 'tags'
|
927
1195
|
unless arg.is_a?(Hash)
|
928
1196
|
failure.push("You provided tags (#{arg}) as something other than a hash.")
|
@@ -1047,7 +1315,7 @@ module Vmpooler
|
|
1047
1315
|
invalid.each do |bad_template|
|
1048
1316
|
metrics.increment("config.invalid.#{bad_template}")
|
1049
1317
|
end
|
1050
|
-
result[:
|
1318
|
+
result[:not_configured] = invalid
|
1051
1319
|
status 400
|
1052
1320
|
end
|
1053
1321
|
else
|