vmpooler 2.2.0 → 2.3.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.
@@ -60,6 +60,7 @@ module Vmpooler
60
60
  to_set[k] = pool[k]
61
61
  end
62
62
  to_set['alias'] = pool['alias'].join(',') if to_set.key?('alias')
63
+ to_set['domain'] = Parsing.get_domain_for_pool(config, pool['name'])
63
64
  redis.hmset("vmpooler__pool__#{pool['name']}", to_set.to_a.flatten) unless to_set.empty?
64
65
  end
65
66
  previously_configured_pools.each do |pool|
@@ -148,15 +149,15 @@ module Vmpooler
148
149
  end
149
150
  pool_alias = redis.hget("vmpooler__vm__#{vm}", 'pool_alias')
150
151
 
151
- redis.pipelined do
152
- redis.hset("vmpooler__active__#{pool}", vm, Time.now)
153
- redis.hset("vmpooler__vm__#{vm}", 'checkout', Time.now)
152
+ redis.pipelined do |pipeline|
153
+ pipeline.hset("vmpooler__active__#{pool}", vm, Time.now)
154
+ pipeline.hset("vmpooler__vm__#{vm}", 'checkout', Time.now)
154
155
  if ondemandrequest_hash['token:token']
155
- redis.hset("vmpooler__vm__#{vm}", 'token:token', ondemandrequest_hash['token:token'])
156
- redis.hset("vmpooler__vm__#{vm}", 'token:user', ondemandrequest_hash['token:user'])
157
- redis.hset("vmpooler__vm__#{vm}", 'lifetime', $config[:config]['vm_lifetime_auth'].to_i)
156
+ pipeline.hset("vmpooler__vm__#{vm}", 'token:token', ondemandrequest_hash['token:token'])
157
+ pipeline.hset("vmpooler__vm__#{vm}", 'token:user', ondemandrequest_hash['token:user'])
158
+ pipeline.hset("vmpooler__vm__#{vm}", 'lifetime', $config[:config]['vm_lifetime_auth'].to_i)
158
159
  end
159
- redis.sadd("vmpooler__#{request_id}__#{pool_alias}__#{pool}", vm)
160
+ pipeline.sadd("vmpooler__#{request_id}__#{pool_alias}__#{pool}", vm)
160
161
  end
161
162
  move_vm_queue(pool, vm, 'pending', 'running', redis)
162
163
  check_ondemand_request_ready(request_id, redis)
@@ -164,12 +165,12 @@ module Vmpooler
164
165
  redis.smove("vmpooler__pending__#{pool}", "vmpooler__ready__#{pool}", vm)
165
166
  end
166
167
 
167
- redis.pipelined do
168
- redis.hset("vmpooler__boot__#{Date.today}", "#{pool}:#{vm}", finish) # maybe remove as this is never used by vmpooler itself?
169
- redis.hset("vmpooler__vm__#{vm}", 'ready', Time.now)
168
+ redis.pipelined do |pipeline|
169
+ pipeline.hset("vmpooler__boot__#{Date.today}", "#{pool}:#{vm}", finish) # maybe remove as this is never used by vmpooler itself?
170
+ pipeline.hset("vmpooler__vm__#{vm}", 'ready', Time.now)
170
171
 
171
172
  # last boot time is displayed in API, and used by alarming script
172
- redis.hset('vmpooler__lastboot', pool, Time.now)
173
+ pipeline.hset('vmpooler__lastboot', pool, Time.now)
173
174
  end
174
175
 
175
176
  $metrics.timing("time_to_ready_state.#{pool}", finish)
@@ -361,35 +362,47 @@ module Vmpooler
361
362
  max_hostname_retries = 3
362
363
  while hostname_retries < max_hostname_retries
363
364
  hostname, hostname_available = generate_and_check_hostname
364
- domain = $config[:config]['domain']
365
- dns_ip, dns_available = check_dns_available(hostname, domain)
365
+ domain = Parsing.get_domain_for_pool(config, pool_name)
366
+ if domain
367
+ fqdn = "#{hostname}.#{domain}"
368
+ else
369
+ fqdn = hostname
370
+ end
371
+
372
+ # skip dns check if the provider is set to skip_dns_check_before_creating_vm
373
+ provider = get_provider_for_pool(pool_name)
374
+ if provider && provider.provider_config['skip_dns_check_before_creating_vm']
375
+ dns_available = true
376
+ else
377
+ dns_ip, dns_available = check_dns_available(fqdn)
378
+ end
379
+
366
380
  break if hostname_available && dns_available
367
381
 
368
382
  hostname_retries += 1
369
383
 
370
384
  if !hostname_available
371
385
  $metrics.increment("errors.duplicatehostname.#{pool_name}")
372
- $logger.log('s', "[!] [#{pool_name}] Generated hostname #{hostname} was not unique (attempt \##{hostname_retries} of #{max_hostname_retries})")
386
+ $logger.log('s', "[!] [#{pool_name}] Generated hostname #{fqdn} was not unique (attempt \##{hostname_retries} of #{max_hostname_retries})")
373
387
  elsif !dns_available
374
388
  $metrics.increment("errors.staledns.#{pool_name}")
375
- $logger.log('s', "[!] [#{pool_name}] Generated hostname #{hostname} already exists in DNS records (#{dns_ip}), stale DNS")
389
+ $logger.log('s', "[!] [#{pool_name}] Generated hostname #{fqdn} already exists in DNS records (#{dns_ip}), stale DNS")
376
390
  end
377
391
  end
378
392
 
379
- raise "Unable to generate a unique hostname after #{hostname_retries} attempts. The last hostname checked was #{hostname}" unless hostname_available && dns_available
393
+ raise "Unable to generate a unique hostname after #{hostname_retries} attempts. The last hostname checked was #{fqdn}" unless hostname_available && dns_available
380
394
 
381
395
  hostname
382
396
  end
383
397
 
384
- def check_dns_available(vm_name, domain = nil)
385
- # Query the DNS for the name we want to create and if it already exists, mark it unavailable
386
- # This protects against stale DNS records
387
- vm_name = "#{vm_name}.#{domain}" if domain
398
+ # Query the DNS for the name we want to create and if it already exists, mark it unavailable
399
+ # This protects against stale DNS records
400
+ def check_dns_available(vm_name)
388
401
  begin
389
402
  dns_ip = Resolv.getaddress(vm_name)
390
403
  rescue Resolv::ResolvError
391
404
  # this is the expected case, swallow the error
392
- # eg "no address for blah-daisy"
405
+ # eg "no address for blah-daisy.example.com"
393
406
  return ['', true]
394
407
  end
395
408
  [dns_ip, false]
@@ -397,6 +410,7 @@ module Vmpooler
397
410
 
398
411
  def _clone_vm(pool_name, provider, request_id = nil, pool_alias = nil)
399
412
  new_vmname = find_unique_hostname(pool_name)
413
+ pool_domain = Parsing.get_domain_for_pool(config, pool_name)
400
414
  mutex = vm_mutex(new_vmname)
401
415
  mutex.synchronize do
402
416
  @redis.with_metrics do |redis|
@@ -406,6 +420,7 @@ module Vmpooler
406
420
  redis.hset("vmpooler__vm__#{new_vmname}", 'clone', Time.now)
407
421
  redis.hset("vmpooler__vm__#{new_vmname}", 'template', pool_name) # This value is used to represent the pool.
408
422
  redis.hset("vmpooler__vm__#{new_vmname}", 'pool', pool_name)
423
+ redis.hset("vmpooler__vm__#{new_vmname}", 'domain', pool_domain) if pool_domain
409
424
  redis.hset("vmpooler__vm__#{new_vmname}", 'request_id', request_id) if request_id
410
425
  redis.hset("vmpooler__vm__#{new_vmname}", 'pool_alias', pool_alias) if pool_alias
411
426
  redis.exec
@@ -418,9 +433,9 @@ module Vmpooler
418
433
  finish = format('%<time>.2f', time: Time.now - start)
419
434
 
420
435
  @redis.with_metrics do |redis|
421
- redis.pipelined do
422
- redis.hset("vmpooler__clone__#{Date.today}", "#{pool_name}:#{new_vmname}", finish)
423
- redis.hset("vmpooler__vm__#{new_vmname}", 'clone_time', finish)
436
+ redis.pipelined do |pipeline|
437
+ pipeline.hset("vmpooler__clone__#{Date.today}", "#{pool_name}:#{new_vmname}", finish)
438
+ pipeline.hset("vmpooler__vm__#{new_vmname}", 'clone_time', finish)
424
439
  end
425
440
  end
426
441
  $logger.log('s', "[+] [#{pool_name}] '#{new_vmname}' cloned in #{finish} seconds")
@@ -428,10 +443,10 @@ module Vmpooler
428
443
  $metrics.timing("clone.#{pool_name}", finish)
429
444
  rescue StandardError
430
445
  @redis.with_metrics do |redis|
431
- redis.pipelined do
432
- redis.srem("vmpooler__pending__#{pool_name}", new_vmname)
446
+ redis.pipelined do |pipeline|
447
+ pipeline.srem("vmpooler__pending__#{pool_name}", new_vmname)
433
448
  expiration_ttl = $config[:redis]['data_ttl'].to_i * 60 * 60
434
- redis.expire("vmpooler__vm__#{new_vmname}", expiration_ttl)
449
+ pipeline.expire("vmpooler__vm__#{new_vmname}", expiration_ttl)
435
450
  end
436
451
  end
437
452
  raise
@@ -462,12 +477,12 @@ module Vmpooler
462
477
 
463
478
  mutex.synchronize do
464
479
  @redis.with_metrics do |redis|
465
- redis.pipelined do
466
- redis.hdel("vmpooler__active__#{pool}", vm)
467
- redis.hset("vmpooler__vm__#{vm}", 'destroy', Time.now)
480
+ redis.pipelined do |pipeline|
481
+ pipeline.hdel("vmpooler__active__#{pool}", vm)
482
+ pipeline.hset("vmpooler__vm__#{vm}", 'destroy', Time.now)
468
483
 
469
484
  # Auto-expire metadata key
470
- redis.expire("vmpooler__vm__#{vm}", ($config[:redis]['data_ttl'].to_i * 60 * 60))
485
+ pipeline.expire("vmpooler__vm__#{vm}", ($config[:redis]['data_ttl'].to_i * 60 * 60))
471
486
  end
472
487
 
473
488
  start = Time.now
@@ -879,7 +894,7 @@ module Vmpooler
879
894
  loop_count = 1
880
895
  loop_delay = loop_delay_min
881
896
  provider = get_provider_for_pool(pool['name'])
882
- raise("Could not find provider '#{pool['provider']}") if provider.nil?
897
+ raise("Could not find provider '#{pool['provider']}'") if provider.nil?
883
898
 
884
899
  sync_pool_template(pool)
885
900
  loop do
@@ -1204,19 +1219,19 @@ module Vmpooler
1204
1219
  pool_check_response[:destroyed_vms] += 1
1205
1220
  destroy_vm(vm, pool_name, provider)
1206
1221
  rescue StandardError => e
1207
- redis.pipelined do
1208
- redis.srem("vmpooler__completed__#{pool_name}", vm)
1209
- redis.hdel("vmpooler__active__#{pool_name}", vm)
1210
- redis.del("vmpooler__vm__#{vm}")
1222
+ redis.pipelined do |pipeline|
1223
+ pipeline.srem("vmpooler__completed__#{pool_name}", vm)
1224
+ pipeline.hdel("vmpooler__active__#{pool_name}", vm)
1225
+ pipeline.del("vmpooler__vm__#{vm}")
1211
1226
  end
1212
1227
  $logger.log('d', "[!] [#{pool_name}] _check_pool failed with an error while evaluating completed VMs: #{e}")
1213
1228
  end
1214
1229
  else
1215
1230
  $logger.log('s', "[!] [#{pool_name}] '#{vm}' not found in inventory, removed from 'completed' queue")
1216
- redis.pipelined do
1217
- redis.srem("vmpooler__completed__#{pool_name}", vm)
1218
- redis.hdel("vmpooler__active__#{pool_name}", vm)
1219
- redis.del("vmpooler__vm__#{vm}")
1231
+ redis.pipelined do |pipeline|
1232
+ pipeline.srem("vmpooler__completed__#{pool_name}", vm)
1233
+ pipeline.hdel("vmpooler__active__#{pool_name}", vm)
1234
+ pipeline.del("vmpooler__vm__#{vm}")
1220
1235
  end
1221
1236
  end
1222
1237
  end
@@ -1366,7 +1381,7 @@ module Vmpooler
1366
1381
 
1367
1382
  return provider_klass.const_get(classname).new(config, logger, metrics, redis_connection_pool, provider_name, options)
1368
1383
  end
1369
- raise("Provider '#{provider_class}' is unknown for pool with provider name '#{provider_name}'") if provider.nil?
1384
+ raise("Provider '#{provider_class}' is unknown for pool with provider name '#{provider_name}'") if provider_klass.nil?
1370
1385
  end
1371
1386
 
1372
1387
  def check_ondemand_requests(maxloop = 0,
@@ -1432,12 +1447,12 @@ module Vmpooler
1432
1447
  score = redis.zscore('vmpooler__provisioning__request', request_id)
1433
1448
  requested = requested.split(',')
1434
1449
 
1435
- redis.pipelined do
1450
+ redis.pipelined do |pipeline|
1436
1451
  requested.each do |request|
1437
- redis.zadd('vmpooler__odcreate__task', Time.now.to_i, "#{request}:#{request_id}")
1452
+ pipeline.zadd('vmpooler__odcreate__task', Time.now.to_i, "#{request}:#{request_id}")
1438
1453
  end
1439
- redis.zrem('vmpooler__provisioning__request', request_id)
1440
- redis.zadd('vmpooler__provisioning__processing', score, request_id)
1454
+ pipeline.zrem('vmpooler__provisioning__request', request_id)
1455
+ pipeline.zadd('vmpooler__provisioning__processing', score, request_id)
1441
1456
  end
1442
1457
  end
1443
1458
 
@@ -1467,9 +1482,9 @@ module Vmpooler
1467
1482
  redis.incr('vmpooler__tasks__ondemandclone')
1468
1483
  clone_vm(pool, provider, request_id, pool_alias)
1469
1484
  end
1470
- redis.pipelined do
1471
- redis.zrem(queue_key, request)
1472
- redis.zadd(queue_key, score, "#{pool_alias}:#{pool}:#{remaining_count}:#{request_id}")
1485
+ redis.pipelined do |pipeline|
1486
+ pipeline.zrem(queue_key, request)
1487
+ pipeline.zadd(queue_key, score, "#{pool_alias}:#{pool}:#{remaining_count}:#{request_id}")
1473
1488
  end
1474
1489
  end
1475
1490
  end
@@ -1520,10 +1535,10 @@ module Vmpooler
1520
1535
 
1521
1536
  $logger.log('s', "Ondemand request for '#{request_id}' failed to provision all instances within the configured ttl '#{ondemand_request_ttl}'")
1522
1537
  expiration_ttl = $config[:redis]['data_ttl'].to_i * 60 * 60
1523
- redis.pipelined do
1524
- redis.zrem('vmpooler__provisioning__processing', request_id)
1525
- redis.hset("vmpooler__odrequest__#{request_id}", 'status', 'failed')
1526
- redis.expire("vmpooler__odrequest__#{request_id}", expiration_ttl)
1538
+ redis.pipelined do |pipeline|
1539
+ pipeline.zrem('vmpooler__provisioning__processing', request_id)
1540
+ pipeline.hset("vmpooler__odrequest__#{request_id}", 'status', 'failed')
1541
+ pipeline.expire("vmpooler__odrequest__#{request_id}", expiration_ttl)
1527
1542
  end
1528
1543
  remove_vms_for_failed_request(request_id, expiration_ttl, redis)
1529
1544
  true
@@ -1533,11 +1548,11 @@ module Vmpooler
1533
1548
  request_hash = redis.hgetall("vmpooler__odrequest__#{request_id}")
1534
1549
  Parsing.get_platform_pool_count(request_hash['requested']) do |platform_alias, pool, _count|
1535
1550
  pools_filled = redis.smembers("vmpooler__#{request_id}__#{platform_alias}__#{pool}")
1536
- redis.pipelined do
1551
+ redis.pipelined do |pipeline|
1537
1552
  pools_filled&.each do |vm|
1538
- move_vm_queue(pool, vm, 'running', 'completed', redis, "moved to completed queue. '#{request_id}' could not be filled in time")
1553
+ move_vm_queue(pool, vm, 'running', 'completed', pipeline, "moved to completed queue. '#{request_id}' could not be filled in time")
1539
1554
  end
1540
- redis.expire("vmpooler__#{request_id}__#{platform_alias}__#{pool}", expiration_ttl)
1555
+ pipeline.expire("vmpooler__#{request_id}__#{platform_alias}__#{pool}", expiration_ttl)
1541
1556
  end
1542
1557
  end
1543
1558
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # utility class shared between apps
3
+ # utility class shared between apps api and pool_manager
4
4
  module Vmpooler
5
5
  class Parsing
6
6
  def self.get_platform_pool_count(requested, &_block)
@@ -12,5 +12,25 @@ module Vmpooler
12
12
  yield platform_alias, pool, count
13
13
  end
14
14
  end
15
+
16
+ # @param config [String] - the full config structure
17
+ # @param pool_name [String] - the name of the pool
18
+ # @return [String] - domain name for pool, if set in the provider for the pool or in the config block
19
+ def self.get_domain_for_pool(config, pool_name)
20
+ pool = config[:pools].find { |p| p['name'] == pool_name }
21
+ return nil unless pool
22
+
23
+ provider_name = pool.fetch('provider', 'vsphere') # see vmpooler.yaml.example where it states defaulting to vsphere
24
+
25
+ if config[:providers] && config[:providers][provider_name.to_sym] && config[:providers][provider_name.to_sym]['domain']
26
+ domain = config[:providers][provider_name.to_sym]['domain']
27
+ elsif config[:config] && config[:config]['domain']
28
+ domain = config[:config]['domain']
29
+ else
30
+ domain = nil
31
+ end
32
+
33
+ domain
34
+ end
15
35
  end
16
36
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vmpooler
4
- VERSION = '2.2.0'
4
+ VERSION = '2.3.0'
5
5
  end
data/lib/vmpooler.rb CHANGED
@@ -17,6 +17,7 @@ module Vmpooler
17
17
 
18
18
  # Dependencies for tracing
19
19
  require 'opentelemetry-instrumentation-concurrent_ruby'
20
+ require 'opentelemetry-instrumentation-http_client'
20
21
  require 'opentelemetry-instrumentation-redis'
21
22
  require 'opentelemetry-instrumentation-sinatra'
22
23
  require 'opentelemetry-sdk'
@@ -42,6 +43,7 @@ module Vmpooler
42
43
  if parsed_config[:config]['extra_config']
43
44
  extra_configs = parsed_config[:config]['extra_config'].split(',')
44
45
  extra_configs.each do |config|
46
+ puts "loading extra_config file #{config}"
45
47
  extra_config = YAML.load_file(config)
46
48
  parsed_config.deep_merge(extra_config)
47
49
  end
@@ -132,6 +134,7 @@ module Vmpooler
132
134
  # Create an index of pool aliases
133
135
  parsed_config[:pool_names] = Set.new
134
136
  unless parsed_config[:pools]
137
+ puts 'loading pools configuration from redis, since the config[:pools] is empty'
135
138
  redis = new_redis(parsed_config[:redis]['server'], parsed_config[:redis]['port'], parsed_config[:redis]['password'])
136
139
  parsed_config[:pools] = load_pools_from_redis(redis)
137
140
  end
@@ -265,6 +268,7 @@ module Vmpooler
265
268
  OpenTelemetry::SDK.configure do |c|
266
269
  c.use 'OpenTelemetry::Instrumentation::Sinatra'
267
270
  c.use 'OpenTelemetry::Instrumentation::ConcurrentRuby'
271
+ c.use 'OpenTelemetry::Instrumentation::HttpClient'
268
272
  c.use 'OpenTelemetry::Instrumentation::Redis'
269
273
 
270
274
  c.add_span_processor(span_processor)