vmpooler 0.11.2 → 0.13.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/vmpooler +3 -1
- data/lib/vmpooler.rb +23 -1
- data/lib/vmpooler/api/helpers.rb +16 -16
- data/lib/vmpooler/api/v1.rb +294 -19
- data/lib/vmpooler/generic_connection_pool.rb +5 -23
- data/lib/vmpooler/graphite.rb +2 -0
- 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: a200f3215b389575c605363ed55bb21b9a1779084cee39fef88fe28c2a51e9ab
|
4
|
+
data.tar.gz: fd48ee0ff0fd5ea1f19dbd0b19bce49d821b0a17ef6f62aca91804cbdebc7d88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63df277bab9ab5f049886d30166856ba5adfc85119e3408d91339827f6b05d3b12dde49c78c051c23d90bf123590e14d6cc7e41598cd315f2df9637a5c7c7042
|
7
|
+
data.tar.gz: 3f6ce744a9c792c12800d7da0b6738e995d8f9a0cbc86bd3635977b3c463765b371100f5ea3ad509a15b7bd83be5bf37e4ea72cf7004535a36afe263577ed356
|
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
@@ -84,29 +84,29 @@ module Vmpooler
|
|
84
84
|
when 'ldap'
|
85
85
|
ldap_base = auth[:ldap]['base']
|
86
86
|
ldap_port = auth[:ldap]['port'] || 389
|
87
|
+
ldap_user_obj = auth[:ldap]['user_object']
|
88
|
+
ldap_host = auth[:ldap]['host']
|
87
89
|
|
88
|
-
|
89
|
-
ldap_base
|
90
|
+
unless ldap_base.is_a? Array
|
91
|
+
ldap_base = ldap_base.split
|
92
|
+
end
|
93
|
+
|
94
|
+
unless ldap_user_obj.is_a? Array
|
95
|
+
ldap_user_obj = ldap_user_obj.split
|
96
|
+
end
|
97
|
+
|
98
|
+
ldap_base.each do |search_base|
|
99
|
+
ldap_user_obj.each do |search_user_obj|
|
90
100
|
result = authenticate_ldap(
|
91
101
|
ldap_port,
|
92
|
-
|
93
|
-
|
102
|
+
ldap_host,
|
103
|
+
search_user_obj,
|
94
104
|
search_base,
|
95
105
|
username_str,
|
96
106
|
password_str
|
97
107
|
)
|
98
|
-
return true if result
|
108
|
+
return true if result
|
99
109
|
end
|
100
|
-
else
|
101
|
-
result = authenticate_ldap(
|
102
|
-
ldap_port,
|
103
|
-
auth[:ldap]['host'],
|
104
|
-
auth[:ldap]['user_object'],
|
105
|
-
ldap_base,
|
106
|
-
username_str,
|
107
|
-
password_str
|
108
|
-
)
|
109
|
-
return result
|
110
110
|
end
|
111
111
|
|
112
112
|
return false
|
@@ -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]
|
@@ -83,8 +145,13 @@ module Vmpooler
|
|
83
145
|
vms.reverse.each do |vm|
|
84
146
|
ready = vm_ready?(vm, config['domain'])
|
85
147
|
if ready
|
86
|
-
backend.smove("vmpooler__ready__#{template_backend}", "vmpooler__running__#{template_backend}", vm)
|
87
|
-
|
148
|
+
smoved = backend.smove("vmpooler__ready__#{template_backend}", "vmpooler__running__#{template_backend}", vm)
|
149
|
+
if smoved
|
150
|
+
return [vm, template_backend, template]
|
151
|
+
else
|
152
|
+
metrics.increment("checkout.smove.failed.#{template_backend}")
|
153
|
+
return [nil, nil, nil]
|
154
|
+
end
|
88
155
|
else
|
89
156
|
backend.smove("vmpooler__ready__#{template_backend}", "vmpooler__completed__#{template_backend}", vm)
|
90
157
|
metrics.increment("checkout.nonresponsive.#{template_backend}")
|
@@ -240,11 +307,9 @@ module Vmpooler
|
|
240
307
|
pool_index = pool_index(pools)
|
241
308
|
template_configs = backend.hgetall('vmpooler__config__template')
|
242
309
|
template_configs&.each do |poolname, template|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
end
|
247
|
-
end
|
310
|
+
next unless pool_index.include? poolname
|
311
|
+
|
312
|
+
pools[pool_index[poolname]]['template'] = template
|
248
313
|
end
|
249
314
|
end
|
250
315
|
|
@@ -252,11 +317,9 @@ module Vmpooler
|
|
252
317
|
pool_index = pool_index(pools)
|
253
318
|
poolsize_configs = backend.hgetall('vmpooler__config__poolsize')
|
254
319
|
poolsize_configs&.each do |poolname, size|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
end
|
259
|
-
end
|
320
|
+
next unless pool_index.include? poolname
|
321
|
+
|
322
|
+
pools[pool_index[poolname]]['size'] = size.to_i
|
260
323
|
end
|
261
324
|
end
|
262
325
|
|
@@ -264,12 +327,67 @@ module Vmpooler
|
|
264
327
|
pool_index = pool_index(pools)
|
265
328
|
clone_target_configs = backend.hgetall('vmpooler__config__clone_target')
|
266
329
|
clone_target_configs&.each do |poolname, clone_target|
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
330
|
+
next unless pool_index.include? poolname
|
331
|
+
|
332
|
+
pools[pool_index[poolname]]['clone_target'] = clone_target
|
333
|
+
end
|
334
|
+
end
|
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'))
|
272
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
|
273
391
|
end
|
274
392
|
|
275
393
|
get '/' do
|
@@ -390,7 +508,7 @@ module Vmpooler
|
|
390
508
|
end
|
391
509
|
|
392
510
|
# for backwards compatibility, include separate "empty" stats in "status" block
|
393
|
-
if ready == 0
|
511
|
+
if ready == 0 && max != 0
|
394
512
|
result[:status][:empty] ||= []
|
395
513
|
result[:status][:empty].push(pool['name'])
|
396
514
|
|
@@ -684,6 +802,88 @@ module Vmpooler
|
|
684
802
|
JSON.pretty_generate(result)
|
685
803
|
end
|
686
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
|
+
|
687
887
|
post "#{api_prefix}/vm/?" do
|
688
888
|
content_type :json
|
689
889
|
result = { 'ok' => false }
|
@@ -759,6 +959,78 @@ module Vmpooler
|
|
759
959
|
invalid
|
760
960
|
end
|
761
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
|
+
|
762
1034
|
post "#{api_prefix}/vm/:template/?" do
|
763
1035
|
content_type :json
|
764
1036
|
result = { 'ok' => false }
|
@@ -874,6 +1146,8 @@ module Vmpooler
|
|
874
1146
|
|
875
1147
|
status 200
|
876
1148
|
result['ok'] = true
|
1149
|
+
else
|
1150
|
+
metrics.increment('delete.srem.failed')
|
877
1151
|
end
|
878
1152
|
end
|
879
1153
|
|
@@ -916,6 +1190,7 @@ module Vmpooler
|
|
916
1190
|
unless arg.to_i > 0
|
917
1191
|
failure.push("You provided a lifetime (#{arg}) but you must provide a positive number.")
|
918
1192
|
end
|
1193
|
+
|
919
1194
|
when 'tags'
|
920
1195
|
unless arg.is_a?(Hash)
|
921
1196
|
failure.push("You provided tags (#{arg}) as something other than a hash.")
|
@@ -1040,7 +1315,7 @@ module Vmpooler
|
|
1040
1315
|
invalid.each do |bad_template|
|
1041
1316
|
metrics.increment("config.invalid.#{bad_template}")
|
1042
1317
|
end
|
1043
|
-
result[:
|
1318
|
+
result[:not_configured] = invalid
|
1044
1319
|
status 400
|
1045
1320
|
end
|
1046
1321
|
else
|