vmpooler 2.1.0 → 2.4.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|
@@ -118,7 +119,13 @@ module Vmpooler
118
119
  pool_alias = redis.hget("vmpooler__vm__#{vm}", 'pool_alias') if request_id
119
120
  redis.multi
120
121
  redis.smove("vmpooler__pending__#{pool}", "vmpooler__completed__#{pool}", vm)
121
- redis.zadd('vmpooler__odcreate__task', 1, "#{pool_alias}:#{pool}:1:#{request_id}") if request_id
122
+ if request_id
123
+ ondemandrequest_hash = redis.hgetall("vmpooler__odrequest__#{request_id}")
124
+ if ondemandrequest_hash && ondemandrequest_hash['status'] != 'failed' && ondemandrequest_hash['status'] != 'deleted'
125
+ # will retry a VM that did not come up as vm_ready? only if it has not been market failed or deleted
126
+ redis.zadd('vmpooler__odcreate__task', 1, "#{pool_alias}:#{pool}:1:#{request_id}")
127
+ end
128
+ end
122
129
  redis.exec
123
130
  $metrics.increment("errors.markedasfailed.#{pool}")
124
131
  $logger.log('d', "[!] [#{pool}] '#{vm}' marked as 'failed' after #{timeout} minutes")
@@ -148,15 +155,15 @@ module Vmpooler
148
155
  end
149
156
  pool_alias = redis.hget("vmpooler__vm__#{vm}", 'pool_alias')
150
157
 
151
- redis.pipelined do
152
- redis.hset("vmpooler__active__#{pool}", vm, Time.now)
153
- redis.hset("vmpooler__vm__#{vm}", 'checkout', Time.now)
158
+ redis.pipelined do |pipeline|
159
+ pipeline.hset("vmpooler__active__#{pool}", vm, Time.now)
160
+ pipeline.hset("vmpooler__vm__#{vm}", 'checkout', Time.now)
154
161
  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)
162
+ pipeline.hset("vmpooler__vm__#{vm}", 'token:token', ondemandrequest_hash['token:token'])
163
+ pipeline.hset("vmpooler__vm__#{vm}", 'token:user', ondemandrequest_hash['token:user'])
164
+ pipeline.hset("vmpooler__vm__#{vm}", 'lifetime', $config[:config]['vm_lifetime_auth'].to_i)
158
165
  end
159
- redis.sadd("vmpooler__#{request_id}__#{pool_alias}__#{pool}", vm)
166
+ pipeline.sadd("vmpooler__#{request_id}__#{pool_alias}__#{pool}", vm)
160
167
  end
161
168
  move_vm_queue(pool, vm, 'pending', 'running', redis)
162
169
  check_ondemand_request_ready(request_id, redis)
@@ -164,12 +171,12 @@ module Vmpooler
164
171
  redis.smove("vmpooler__pending__#{pool}", "vmpooler__ready__#{pool}", vm)
165
172
  end
166
173
 
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)
174
+ redis.pipelined do |pipeline|
175
+ pipeline.hset("vmpooler__boot__#{Date.today}", "#{pool}:#{vm}", finish) # maybe remove as this is never used by vmpooler itself?
176
+ pipeline.hset("vmpooler__vm__#{vm}", 'ready', Time.now)
170
177
 
171
178
  # last boot time is displayed in API, and used by alarming script
172
- redis.hset('vmpooler__lastboot', pool, Time.now)
179
+ pipeline.hset('vmpooler__lastboot', pool, Time.now)
173
180
  end
174
181
 
175
182
  $metrics.timing("time_to_ready_state.#{pool}", finish)
@@ -361,35 +368,47 @@ module Vmpooler
361
368
  max_hostname_retries = 3
362
369
  while hostname_retries < max_hostname_retries
363
370
  hostname, hostname_available = generate_and_check_hostname
364
- domain = $config[:config]['domain']
365
- dns_ip, dns_available = check_dns_available(hostname, domain)
371
+ domain = Parsing.get_domain_for_pool(config, pool_name)
372
+ if domain
373
+ fqdn = "#{hostname}.#{domain}"
374
+ else
375
+ fqdn = hostname
376
+ end
377
+
378
+ # skip dns check if the provider is set to skip_dns_check_before_creating_vm
379
+ provider = get_provider_for_pool(pool_name)
380
+ if provider && provider.provider_config['skip_dns_check_before_creating_vm']
381
+ dns_available = true
382
+ else
383
+ dns_ip, dns_available = check_dns_available(fqdn)
384
+ end
385
+
366
386
  break if hostname_available && dns_available
367
387
 
368
388
  hostname_retries += 1
369
389
 
370
390
  if !hostname_available
371
391
  $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})")
392
+ $logger.log('s', "[!] [#{pool_name}] Generated hostname #{fqdn} was not unique (attempt \##{hostname_retries} of #{max_hostname_retries})")
373
393
  elsif !dns_available
374
394
  $metrics.increment("errors.staledns.#{pool_name}")
375
- $logger.log('s', "[!] [#{pool_name}] Generated hostname #{hostname} already exists in DNS records (#{dns_ip}), stale DNS")
395
+ $logger.log('s', "[!] [#{pool_name}] Generated hostname #{fqdn} already exists in DNS records (#{dns_ip}), stale DNS")
376
396
  end
377
397
  end
378
398
 
379
- raise "Unable to generate a unique hostname after #{hostname_retries} attempts. The last hostname checked was #{hostname}" unless hostname_available && dns_available
399
+ raise "Unable to generate a unique hostname after #{hostname_retries} attempts. The last hostname checked was #{fqdn}" unless hostname_available && dns_available
380
400
 
381
401
  hostname
382
402
  end
383
403
 
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
404
+ # Query the DNS for the name we want to create and if it already exists, mark it unavailable
405
+ # This protects against stale DNS records
406
+ def check_dns_available(vm_name)
388
407
  begin
389
408
  dns_ip = Resolv.getaddress(vm_name)
390
409
  rescue Resolv::ResolvError
391
410
  # this is the expected case, swallow the error
392
- # eg "no address for blah-daisy"
411
+ # eg "no address for blah-daisy.example.com"
393
412
  return ['', true]
394
413
  end
395
414
  [dns_ip, false]
@@ -397,6 +416,7 @@ module Vmpooler
397
416
 
398
417
  def _clone_vm(pool_name, provider, request_id = nil, pool_alias = nil)
399
418
  new_vmname = find_unique_hostname(pool_name)
419
+ pool_domain = Parsing.get_domain_for_pool(config, pool_name)
400
420
  mutex = vm_mutex(new_vmname)
401
421
  mutex.synchronize do
402
422
  @redis.with_metrics do |redis|
@@ -406,6 +426,7 @@ module Vmpooler
406
426
  redis.hset("vmpooler__vm__#{new_vmname}", 'clone', Time.now)
407
427
  redis.hset("vmpooler__vm__#{new_vmname}", 'template', pool_name) # This value is used to represent the pool.
408
428
  redis.hset("vmpooler__vm__#{new_vmname}", 'pool', pool_name)
429
+ redis.hset("vmpooler__vm__#{new_vmname}", 'domain', pool_domain) if pool_domain
409
430
  redis.hset("vmpooler__vm__#{new_vmname}", 'request_id', request_id) if request_id
410
431
  redis.hset("vmpooler__vm__#{new_vmname}", 'pool_alias', pool_alias) if pool_alias
411
432
  redis.exec
@@ -418,9 +439,9 @@ module Vmpooler
418
439
  finish = format('%<time>.2f', time: Time.now - start)
419
440
 
420
441
  @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)
442
+ redis.pipelined do |pipeline|
443
+ pipeline.hset("vmpooler__clone__#{Date.today}", "#{pool_name}:#{new_vmname}", finish)
444
+ pipeline.hset("vmpooler__vm__#{new_vmname}", 'clone_time', finish)
424
445
  end
425
446
  end
426
447
  $logger.log('s', "[+] [#{pool_name}] '#{new_vmname}' cloned in #{finish} seconds")
@@ -428,10 +449,10 @@ module Vmpooler
428
449
  $metrics.timing("clone.#{pool_name}", finish)
429
450
  rescue StandardError
430
451
  @redis.with_metrics do |redis|
431
- redis.pipelined do
432
- redis.srem("vmpooler__pending__#{pool_name}", new_vmname)
452
+ redis.pipelined do |pipeline|
453
+ pipeline.srem("vmpooler__pending__#{pool_name}", new_vmname)
433
454
  expiration_ttl = $config[:redis]['data_ttl'].to_i * 60 * 60
434
- redis.expire("vmpooler__vm__#{new_vmname}", expiration_ttl)
455
+ pipeline.expire("vmpooler__vm__#{new_vmname}", expiration_ttl)
435
456
  end
436
457
  end
437
458
  raise
@@ -462,12 +483,12 @@ module Vmpooler
462
483
 
463
484
  mutex.synchronize do
464
485
  @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)
486
+ redis.pipelined do |pipeline|
487
+ pipeline.hdel("vmpooler__active__#{pool}", vm)
488
+ pipeline.hset("vmpooler__vm__#{vm}", 'destroy', Time.now)
468
489
 
469
490
  # Auto-expire metadata key
470
- redis.expire("vmpooler__vm__#{vm}", ($config[:redis]['data_ttl'].to_i * 60 * 60))
491
+ pipeline.expire("vmpooler__vm__#{vm}", ($config[:redis]['data_ttl'].to_i * 60 * 60))
471
492
  end
472
493
 
473
494
  start = Time.now
@@ -879,7 +900,7 @@ module Vmpooler
879
900
  loop_count = 1
880
901
  loop_delay = loop_delay_min
881
902
  provider = get_provider_for_pool(pool['name'])
882
- raise("Could not find provider '#{pool['provider']}") if provider.nil?
903
+ raise("Could not find provider '#{pool['provider']}'") if provider.nil?
883
904
 
884
905
  sync_pool_template(pool)
885
906
  loop do
@@ -1204,19 +1225,19 @@ module Vmpooler
1204
1225
  pool_check_response[:destroyed_vms] += 1
1205
1226
  destroy_vm(vm, pool_name, provider)
1206
1227
  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}")
1228
+ redis.pipelined do |pipeline|
1229
+ pipeline.srem("vmpooler__completed__#{pool_name}", vm)
1230
+ pipeline.hdel("vmpooler__active__#{pool_name}", vm)
1231
+ pipeline.del("vmpooler__vm__#{vm}")
1211
1232
  end
1212
1233
  $logger.log('d', "[!] [#{pool_name}] _check_pool failed with an error while evaluating completed VMs: #{e}")
1213
1234
  end
1214
1235
  else
1215
1236
  $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}")
1237
+ redis.pipelined do |pipeline|
1238
+ pipeline.srem("vmpooler__completed__#{pool_name}", vm)
1239
+ pipeline.hdel("vmpooler__active__#{pool_name}", vm)
1240
+ pipeline.del("vmpooler__vm__#{vm}")
1220
1241
  end
1221
1242
  end
1222
1243
  end
@@ -1366,7 +1387,7 @@ module Vmpooler
1366
1387
 
1367
1388
  return provider_klass.const_get(classname).new(config, logger, metrics, redis_connection_pool, provider_name, options)
1368
1389
  end
1369
- raise("Provider '#{provider_class}' is unknown for pool with provider name '#{provider_name}'") if provider.nil?
1390
+ raise("Provider '#{provider_class}' is unknown for pool with provider name '#{provider_name}'") if provider_klass.nil?
1370
1391
  end
1371
1392
 
1372
1393
  def check_ondemand_requests(maxloop = 0,
@@ -1432,12 +1453,12 @@ module Vmpooler
1432
1453
  score = redis.zscore('vmpooler__provisioning__request', request_id)
1433
1454
  requested = requested.split(',')
1434
1455
 
1435
- redis.pipelined do
1456
+ redis.pipelined do |pipeline|
1436
1457
  requested.each do |request|
1437
- redis.zadd('vmpooler__odcreate__task', Time.now.to_i, "#{request}:#{request_id}")
1458
+ pipeline.zadd('vmpooler__odcreate__task', Time.now.to_i, "#{request}:#{request_id}")
1438
1459
  end
1439
- redis.zrem('vmpooler__provisioning__request', request_id)
1440
- redis.zadd('vmpooler__provisioning__processing', score, request_id)
1460
+ pipeline.zrem('vmpooler__provisioning__request', request_id)
1461
+ pipeline.zadd('vmpooler__provisioning__processing', score, request_id)
1441
1462
  end
1442
1463
  end
1443
1464
 
@@ -1467,9 +1488,9 @@ module Vmpooler
1467
1488
  redis.incr('vmpooler__tasks__ondemandclone')
1468
1489
  clone_vm(pool, provider, request_id, pool_alias)
1469
1490
  end
1470
- redis.pipelined do
1471
- redis.zrem(queue_key, request)
1472
- redis.zadd(queue_key, score, "#{pool_alias}:#{pool}:#{remaining_count}:#{request_id}")
1491
+ redis.pipelined do |pipeline|
1492
+ pipeline.zrem(queue_key, request)
1493
+ pipeline.zadd(queue_key, score, "#{pool_alias}:#{pool}:#{remaining_count}:#{request_id}")
1473
1494
  end
1474
1495
  end
1475
1496
  end
@@ -1520,10 +1541,10 @@ module Vmpooler
1520
1541
 
1521
1542
  $logger.log('s', "Ondemand request for '#{request_id}' failed to provision all instances within the configured ttl '#{ondemand_request_ttl}'")
1522
1543
  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)
1544
+ redis.pipelined do |pipeline|
1545
+ pipeline.zrem('vmpooler__provisioning__processing', request_id)
1546
+ pipeline.hset("vmpooler__odrequest__#{request_id}", 'status', 'failed')
1547
+ pipeline.expire("vmpooler__odrequest__#{request_id}", expiration_ttl)
1527
1548
  end
1528
1549
  remove_vms_for_failed_request(request_id, expiration_ttl, redis)
1529
1550
  true
@@ -1533,11 +1554,11 @@ module Vmpooler
1533
1554
  request_hash = redis.hgetall("vmpooler__odrequest__#{request_id}")
1534
1555
  Parsing.get_platform_pool_count(request_hash['requested']) do |platform_alias, pool, _count|
1535
1556
  pools_filled = redis.smembers("vmpooler__#{request_id}__#{platform_alias}__#{pool}")
1536
- redis.pipelined do
1557
+ redis.pipelined do |pipeline|
1537
1558
  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")
1559
+ move_vm_queue(pool, vm, 'running', 'completed', pipeline, "moved to completed queue. '#{request_id}' could not be filled in time")
1539
1560
  end
1540
- redis.expire("vmpooler__#{request_id}__#{platform_alias}__#{pool}", expiration_ttl)
1561
+ pipeline.expire("vmpooler__#{request_id}__#{platform_alias}__#{pool}", expiration_ttl)
1541
1562
  end
1542
1563
  end
1543
1564
  end
@@ -1581,19 +1602,19 @@ module Vmpooler
1581
1602
  $config[:pools].each do |pool|
1582
1603
  provider_name = pool['provider']
1583
1604
  # The provider_class parameter can be defined in the provider's data eg
1584
- #:providers:
1585
- # :vsphere:
1586
- # provider_class: 'vsphere'
1587
- # :another-vsphere:
1588
- # provider_class: 'vsphere'
1605
+ # :providers:
1606
+ # :vsphere:
1607
+ # provider_class: 'vsphere'
1608
+ # :another-vsphere:
1609
+ # provider_class: 'vsphere'
1589
1610
  # the above would create two providers/vsphere.rb class objects named 'vsphere' and 'another-vsphere'
1590
1611
  # each pools would then define which provider definition to use: vsphere or another-vsphere
1591
1612
  #
1592
1613
  # if provider_class is not defined it will try to use the provider_name as the class, this is to be
1593
1614
  # backwards compatible for example when there is only one provider listed
1594
1615
  # :providers:
1595
- # :dummy:
1596
- # filename: 'db.txs'
1616
+ # :dummy:
1617
+ # filename: 'db.txs'
1597
1618
  # the above example would create an object based on the class providers/dummy.rb
1598
1619
  if $config[:providers].nil? || $config[:providers][provider_name.to_sym].nil? || $config[:providers][provider_name.to_sym]['provider_class'].nil?
1599
1620
  provider_class = provider_name
@@ -242,7 +242,7 @@ module Vmpooler
242
242
  # returns
243
243
  # nil when successful. Raises error when encountered
244
244
  def create_template_delta_disks(_pool)
245
- raise("#{self.class.name} does not implement create_template_delta_disks")
245
+ puts("#{self.class.name} does not implement create_template_delta_disks")
246
246
  end
247
247
 
248
248
  # inputs
@@ -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.1.0'
4
+ VERSION = '2.4.0'
5
5
  end
data/lib/vmpooler.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  module Vmpooler
4
4
  require 'concurrent'
5
5
  require 'date'
6
+ require 'deep_merge'
6
7
  require 'json'
7
8
  require 'net/ldap'
8
9
  require 'open-uri'
@@ -16,6 +17,7 @@ module Vmpooler
16
17
 
17
18
  # Dependencies for tracing
18
19
  require 'opentelemetry-instrumentation-concurrent_ruby'
20
+ require 'opentelemetry-instrumentation-http_client'
19
21
  require 'opentelemetry-instrumentation-redis'
20
22
  require 'opentelemetry-instrumentation-sinatra'
21
23
  require 'opentelemetry-sdk'
@@ -41,8 +43,9 @@ module Vmpooler
41
43
  if parsed_config[:config]['extra_config']
42
44
  extra_configs = parsed_config[:config]['extra_config'].split(',')
43
45
  extra_configs.each do |config|
46
+ puts "loading extra_config file #{config}"
44
47
  extra_config = YAML.load_file(config)
45
- parsed_config.merge!(extra_config)
48
+ parsed_config.deep_merge(extra_config)
46
49
  end
47
50
  end
48
51
  end
@@ -131,6 +134,7 @@ module Vmpooler
131
134
  # Create an index of pool aliases
132
135
  parsed_config[:pool_names] = Set.new
133
136
  unless parsed_config[:pools]
137
+ puts 'loading pools configuration from redis, since the config[:pools] is empty'
134
138
  redis = new_redis(parsed_config[:redis]['server'], parsed_config[:redis]['port'], parsed_config[:redis]['password'])
135
139
  parsed_config[:pools] = load_pools_from_redis(redis)
136
140
  end
@@ -264,6 +268,7 @@ module Vmpooler
264
268
  OpenTelemetry::SDK.configure do |c|
265
269
  c.use 'OpenTelemetry::Instrumentation::Sinatra'
266
270
  c.use 'OpenTelemetry::Instrumentation::ConcurrentRuby'
271
+ c.use 'OpenTelemetry::Instrumentation::HttpClient'
267
272
  c.use 'OpenTelemetry::Instrumentation::Redis'
268
273
 
269
274
  c.add_span_processor(span_processor)