vmpooler 2.1.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)