vmpooler 0.12.0 → 0.14.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.
@@ -14,10 +14,11 @@ module Vmpooler
14
14
  # Provider options passed in during initialization
15
15
  attr_reader :provider_options
16
16
 
17
- def initialize(config, logger, metrics, name, options)
17
+ def initialize(config, logger, metrics, redis_connection_pool, name, options)
18
18
  @config = config
19
19
  @logger = logger
20
20
  @metrics = metrics
21
+ @redis = redis_connection_pool
21
22
  @provider_name = name
22
23
 
23
24
  # Ensure that there is not a nil provider configuration
@@ -9,8 +9,8 @@ module Vmpooler
9
9
  class Dummy < Vmpooler::PoolManager::Provider::Base
10
10
  # Fake VM Provider for testing
11
11
 
12
- def initialize(config, logger, metrics, name, options)
13
- super(config, logger, metrics, name, options)
12
+ def initialize(config, logger, metrics, redis_connection_pool, name, options)
13
+ super(config, logger, metrics, redis_connection_pool, name, options)
14
14
  dummyfilename = provider_config['filename']
15
15
 
16
16
  # This initial_state option is only intended to be used by spec tests
@@ -29,7 +29,8 @@ module Vmpooler
29
29
  logger.log('d', "[#{name}] ConnPool - Creating a connection pool of size #{connpool_size} with timeout #{connpool_timeout}")
30
30
  @connection_pool = Vmpooler::PoolManager::GenericConnectionPool.new(
31
31
  metrics: metrics,
32
- metric_prefix: "#{name}_provider_connection_pool",
32
+ connpool_type: 'provider_connection_pool',
33
+ connpool_provider: name,
33
34
  size: connpool_size,
34
35
  timeout: connpool_timeout
35
36
  ) do
@@ -9,8 +9,8 @@ module Vmpooler
9
9
  # The connection_pool method is normally used only for testing
10
10
  attr_reader :connection_pool
11
11
 
12
- def initialize(config, logger, metrics, name, options)
13
- super(config, logger, metrics, name, options)
12
+ def initialize(config, logger, metrics, redis_connection_pool, name, options)
13
+ super(config, logger, metrics, redis_connection_pool, name, options)
14
14
 
15
15
  task_limit = global_config[:config].nil? || global_config[:config]['task_limit'].nil? ? 10 : global_config[:config]['task_limit'].to_i
16
16
  # The default connection pool size is:
@@ -25,7 +25,8 @@ module Vmpooler
25
25
  logger.log('d', "[#{name}] ConnPool - Creating a connection pool of size #{connpool_size} with timeout #{connpool_timeout}")
26
26
  @connection_pool = Vmpooler::PoolManager::GenericConnectionPool.new(
27
27
  metrics: metrics,
28
- metric_prefix: "#{name}_provider_connection_pool",
28
+ connpool_type: 'provider_connection_pool',
29
+ connpool_provider: name,
29
30
  size: connpool_size,
30
31
  timeout: connpool_timeout
31
32
  ) do
@@ -39,6 +40,7 @@ module Vmpooler
39
40
  end
40
41
  @provider_hosts = {}
41
42
  @provider_hosts_lock = Mutex.new
43
+ @redis = redis_connection_pool
42
44
  end
43
45
 
44
46
  # name of the provider class
@@ -59,12 +61,16 @@ module Vmpooler
59
61
  def destroy_vm_and_log(vm_name, vm_object, pool, data_ttl)
60
62
  try = 0 if try.nil?
61
63
  max_tries = 3
62
- $redis.srem("vmpooler__completed__#{pool}", vm_name)
63
- $redis.hdel("vmpooler__active__#{pool}", vm_name)
64
- $redis.hset("vmpooler__vm__#{vm_name}", 'destroy', Time.now)
65
-
66
- # Auto-expire metadata key
67
- $redis.expire('vmpooler__vm__' + vm_name, (data_ttl * 60 * 60))
64
+ @redis.with_metrics do |redis|
65
+ redis.multi
66
+ redis.srem("vmpooler__completed__#{pool}", vm_name)
67
+ redis.hdel("vmpooler__active__#{pool}", vm_name)
68
+ redis.hset("vmpooler__vm__#{vm_name}", 'destroy', Time.now)
69
+
70
+ # Auto-expire metadata key
71
+ redis.expire('vmpooler__vm__' + vm_name, (data_ttl * 60 * 60))
72
+ redis.exec
73
+ end
68
74
 
69
75
  start = Time.now
70
76
 
@@ -293,7 +299,6 @@ module Vmpooler
293
299
  template_path = pool['template']
294
300
  target_folder_path = pool['folder']
295
301
  target_datastore = pool['datastore']
296
- target_cluster_name = get_target_cluster_from_config(pool_name)
297
302
  target_datacenter_name = get_target_datacenter_from_config(pool_name)
298
303
 
299
304
  # Get the template VM object
@@ -315,35 +320,23 @@ module Vmpooler
315
320
  ]
316
321
  )
317
322
 
318
- # Put the VM in the specified folder and resource pool
319
- relocate_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(
320
- datastore: find_datastore(target_datastore, connection, target_datacenter_name),
321
- diskMoveType: get_disk_backing(pool)
322
- )
323
-
324
- manage_host_selection = @config[:config]['manage_host_selection'] if @config[:config].key?('manage_host_selection')
325
- if manage_host_selection
326
- run_select_hosts(pool_name, @provider_hosts)
327
- target_host = select_next_host(pool_name, @provider_hosts)
328
- host_object = find_host_by_dnsname(connection, target_host)
329
- relocate_spec.host = host_object
330
- else
331
- # Choose a cluster/host to place the new VM on
332
- target_cluster_object = find_cluster(target_cluster_name, connection, target_datacenter_name)
333
- relocate_spec.pool = target_cluster_object.resourcePool
323
+ # Check if alternate network configuration is specified and add configuration
324
+ if pool.key?('network')
325
+ template_vm_network_device = template_vm_object.config.hardware.device.grep(RbVmomi::VIM::VirtualEthernetCard).first
326
+ network_name = pool['network']
327
+ network_device = set_network_device(target_datacenter_name, template_vm_network_device, network_name, connection)
328
+ config_spec.deviceChange = [{ operation: 'edit', device: network_device }]
334
329
  end
335
330
 
331
+ # Put the VM in the specified folder and resource pool
332
+ relocate_spec = create_relocate_spec(target_datastore, target_datacenter_name, pool_name, connection)
333
+
336
334
  # Create a clone spec
337
- clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(
338
- location: relocate_spec,
339
- config: config_spec,
340
- powerOn: true,
341
- template: false
342
- )
335
+ clone_spec = create_clone_spec(relocate_spec, config_spec)
343
336
 
344
337
  begin
345
338
  vm_target_folder = find_vm_folder(pool_name, connection)
346
- vm_target_folder = create_folder(connection, target_folder_path, target_datacenter_name) if vm_target_folder.nil? && @config[:config].key?('create_folders') && (@config[:config]['create_folders'] == true)
339
+ vm_target_folder ||= create_folder(connection, target_folder_path, target_datacenter_name) if @config[:config].key?('create_folders') && (@config[:config]['create_folders'] == true)
347
340
  rescue StandardError
348
341
  if @config[:config].key?('create_folders') && (@config[:config]['create_folders'] == true)
349
342
  vm_target_folder = create_folder(connection, target_folder_path, target_datacenter_name)
@@ -351,6 +344,7 @@ module Vmpooler
351
344
  raise
352
345
  end
353
346
  end
347
+ raise ArgumentError, "Cannot find the configured folder for #{pool_name} #{target_folder_path}" unless vm_target_folder
354
348
 
355
349
  # Create the new VM
356
350
  new_vm_object = template_vm_object.CloneVM_Task(
@@ -364,6 +358,81 @@ module Vmpooler
364
358
  vm_hash
365
359
  end
366
360
 
361
+ def create_relocate_spec(target_datastore, target_datacenter_name, pool_name, connection)
362
+ pool = pool_config(pool_name)
363
+ target_cluster_name = get_target_cluster_from_config(pool_name)
364
+
365
+ relocate_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(
366
+ datastore: find_datastore(target_datastore, connection, target_datacenter_name),
367
+ diskMoveType: get_disk_backing(pool)
368
+ )
369
+ manage_host_selection = @config[:config]['manage_host_selection'] if @config[:config].key?('manage_host_selection')
370
+ if manage_host_selection
371
+ run_select_hosts(pool_name, @provider_hosts)
372
+ target_host = select_next_host(pool_name, @provider_hosts)
373
+ host_object = find_host_by_dnsname(connection, target_host)
374
+ relocate_spec.host = host_object
375
+ else
376
+ # Choose a cluster/host to place the new VM on
377
+ target_cluster_object = find_cluster(target_cluster_name, connection, target_datacenter_name)
378
+ relocate_spec.pool = target_cluster_object.resourcePool
379
+ end
380
+ relocate_spec
381
+ end
382
+
383
+ def create_clone_spec(relocate_spec, config_spec)
384
+ RbVmomi::VIM.VirtualMachineCloneSpec(
385
+ location: relocate_spec,
386
+ config: config_spec,
387
+ powerOn: true,
388
+ template: false
389
+ )
390
+ end
391
+
392
+ def set_network_device(datacenter_name, template_vm_network_device, network_name, connection)
393
+ # Retrieve network object
394
+ datacenter = connection.serviceInstance.find_datacenter(datacenter_name)
395
+ new_network = datacenter.network.find { |n| n.name == network_name }
396
+
397
+ raise("Cannot find network #{network_name} in datacenter #{datacenter_name}") unless new_network
398
+
399
+ # Determine network device type
400
+ # All possible device type options here: https://vdc-download.vmware.com/vmwb-repository/dcr-public/98d63b35-d822-47fe-a87a-ddefd469df06/2e3c7b58-f2bd-486e-8bb1-a75eb0640bee/doc/vim.vm.device.VirtualEthernetCard.html
401
+ network_device =
402
+ if template_vm_network_device.is_a? RbVmomi::VIM::VirtualVmxnet2
403
+ RbVmomi::VIM.VirtualVmxnet2
404
+ elsif template_vm_network_device.is_a? RbVmomi::VIM::VirtualVmxnet3
405
+ RbVmomi::VIM.VirtualVmxnet3
406
+ elsif template_vm_network_device.is_a? RbVmomi::VIM::VirtualE1000
407
+ RbVmomi::VIM.VirtualE1000
408
+ elsif template_vm_network_device.is_a? RbVmomi::VIM::VirtualE1000e
409
+ RbVmomi::VIM.VirtualE1000e
410
+ elsif template_vm_network_device.is_a? RbVmomi::VIM::VirtualSriovEthernetCard
411
+ RbVmomi::VIM.VirtualSriovEthernetCard
412
+ else
413
+ RbVmomi::VIM.VirtualPCNet32
414
+ end
415
+
416
+ # Set up new network device attributes
417
+ network_device.key = template_vm_network_device.key
418
+ network_device.deviceInfo = RbVmomi::VIM.Description(
419
+ label: template_vm_network_device.deviceInfo.label,
420
+ summary: network_name
421
+ )
422
+ network_device.backing = RbVmomi::VIM.VirtualEthernetCardNetworkBackingInfo(
423
+ deviceName: network_name,
424
+ network: new_network,
425
+ useAutoDetect: false
426
+ )
427
+ network_device.addressType = 'assigned'
428
+ network_device.connectable = RbVmomi::VIM.VirtualDeviceConnectInfo(
429
+ allowGuestControl: true,
430
+ startConnected: true,
431
+ connected: true
432
+ )
433
+ network_device
434
+ end
435
+
367
436
  def create_disk(pool_name, vm_name, disk_size)
368
437
  pool = pool_config(pool_name)
369
438
  raise("Pool #{pool_name} does not exist for the provider #{name}") if pool.nil?
@@ -968,22 +1037,24 @@ module Vmpooler
968
1037
  begin
969
1038
  connection = ensured_vsphere_connection(pool_object)
970
1039
  vm_hash = get_vm_details(pool_name, vm_name, connection)
971
- $redis.hset("vmpooler__vm__#{vm_name}", 'host', vm_hash['host_name'])
972
- migration_limit = @config[:config]['migration_limit'] if @config[:config].key?('migration_limit')
973
- migration_count = $redis.scard('vmpooler__migration')
974
- if migration_enabled? @config
975
- if migration_count >= migration_limit
976
- logger.log('s', "[ ] [#{pool_name}] '#{vm_name}' is running on #{vm_hash['host_name']}. No migration will be evaluated since the migration_limit has been reached")
977
- break
978
- end
979
- run_select_hosts(pool_name, @provider_hosts)
980
- if vm_in_target?(pool_name, vm_hash['host_name'], vm_hash['architecture'], @provider_hosts)
981
- logger.log('s', "[ ] [#{pool_name}] No migration required for '#{vm_name}' running on #{vm_hash['host_name']}")
1040
+ @redis.with_metrics do |redis|
1041
+ redis.hset("vmpooler__vm__#{vm_name}", 'host', vm_hash['host_name'])
1042
+ migration_count = redis.scard('vmpooler__migration')
1043
+ migration_limit = @config[:config]['migration_limit'] if @config[:config].key?('migration_limit')
1044
+ if migration_enabled? @config
1045
+ if migration_count >= migration_limit
1046
+ logger.log('s', "[ ] [#{pool_name}] '#{vm_name}' is running on #{vm_hash['host_name']}. No migration will be evaluated since the migration_limit has been reached")
1047
+ break
1048
+ end
1049
+ run_select_hosts(pool_name, @provider_hosts)
1050
+ if vm_in_target?(pool_name, vm_hash['host_name'], vm_hash['architecture'], @provider_hosts)
1051
+ logger.log('s', "[ ] [#{pool_name}] No migration required for '#{vm_name}' running on #{vm_hash['host_name']}")
1052
+ else
1053
+ migrate_vm_to_new_host(pool_name, vm_name, vm_hash, connection)
1054
+ end
982
1055
  else
983
- migrate_vm_to_new_host(pool_name, vm_name, vm_hash, connection)
1056
+ logger.log('s', "[ ] [#{pool_name}] '#{vm_name}' is running on #{vm_hash['host_name']}")
984
1057
  end
985
- else
986
- logger.log('s', "[ ] [#{pool_name}] '#{vm_name}' is running on #{vm_hash['host_name']}")
987
1058
  end
988
1059
  rescue StandardError
989
1060
  logger.log('s', "[!] [#{pool_name}] '#{vm_name}' is running on #{vm_hash['host_name']}")
@@ -993,15 +1064,23 @@ module Vmpooler
993
1064
  end
994
1065
 
995
1066
  def migrate_vm_to_new_host(pool_name, vm_name, vm_hash, connection)
996
- $redis.sadd('vmpooler__migration', vm_name)
1067
+ @redis.with_metrics do |redis|
1068
+ redis.sadd('vmpooler__migration', vm_name)
1069
+ end
997
1070
  target_host_name = select_next_host(pool_name, @provider_hosts, vm_hash['architecture'])
998
1071
  target_host_object = find_host_by_dnsname(connection, target_host_name)
999
1072
  finish = migrate_vm_and_record_timing(pool_name, vm_name, vm_hash, target_host_object, target_host_name)
1000
- $redis.hset("vmpooler__vm__#{vm_name}", 'host', target_host_name)
1001
- $redis.hset("vmpooler__vm__#{vm_name}", 'migrated', true)
1073
+ @redis.with_metrics do |redis|
1074
+ redis.multi
1075
+ redis.hset("vmpooler__vm__#{vm_name}", 'host', target_host_name)
1076
+ redis.hset("vmpooler__vm__#{vm_name}", 'migrated', true)
1077
+ redis.exec
1078
+ end
1002
1079
  logger.log('s', "[>] [#{pool_name}] '#{vm_name}' migrated from #{vm_hash['host_name']} to #{target_host_name} in #{finish} seconds")
1003
1080
  ensure
1004
- $redis.srem('vmpooler__migration', vm_name)
1081
+ @redis.with_metrics do |redis|
1082
+ redis.srem('vmpooler__migration', vm_name)
1083
+ end
1005
1084
  end
1006
1085
 
1007
1086
  def migrate_vm_and_record_timing(pool_name, vm_name, vm_hash, target_host_object, dest_host_name)
@@ -1011,9 +1090,13 @@ module Vmpooler
1011
1090
  metrics.timing("migrate.#{pool_name}", finish)
1012
1091
  metrics.increment("migrate_from.#{vm_hash['host_name']}")
1013
1092
  metrics.increment("migrate_to.#{dest_host_name}")
1014
- checkout_to_migration = format('%<time>.2f', time: Time.now - Time.parse($redis.hget("vmpooler__vm__#{vm_name}", 'checkout')))
1015
- $redis.hset("vmpooler__vm__#{vm_name}", 'migration_time', finish)
1016
- $redis.hset("vmpooler__vm__#{vm_name}", 'checkout_to_migration', checkout_to_migration)
1093
+ @redis.with_metrics do |redis|
1094
+ checkout_to_migration = format('%<time>.2f', time: Time.now - Time.parse(redis.hget("vmpooler__vm__#{vm_name}", 'checkout')))
1095
+ redis.multi
1096
+ redis.hset("vmpooler__vm__#{vm_name}", 'migration_time', finish)
1097
+ redis.hset("vmpooler__vm__#{vm_name}", 'checkout_to_migration', checkout_to_migration)
1098
+ redis.exec
1099
+ end
1017
1100
  finish
1018
1101
  end
1019
1102
 
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # utility class shared between apps
4
+ module Vmpooler
5
+ class Parsing
6
+ def self.get_platform_pool_count(requested, &_block)
7
+ requested_platforms = requested.split(',')
8
+ requested_platforms.each do |platform|
9
+ platform_alias, pool, count = platform.split(':')
10
+ raise ArgumentError if platform_alias.nil? || pool.nil? || count.nil?
11
+
12
+ yield platform_alias, pool, count
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vmpooler
4
- VERSION = '0.12.0'
4
+ VERSION = '0.14.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vmpooler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-28 00:00:00.000000000 Z
11
+ date: 2020-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pickup
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '2.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: prometheus-client
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '2.0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '2.0'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: net-ldap
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +164,20 @@ dependencies:
150
164
  - - "~>"
151
165
  - !ruby/object:Gem::Version
152
166
  version: '2.2'
167
+ - !ruby/object:Gem::Dependency
168
+ name: concurrent-ruby
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '1.1'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '1.1'
153
181
  - !ruby/object:Gem::Dependency
154
182
  name: nokogiri
155
183
  requirement: !ruby/object:Gem::Requirement
@@ -191,13 +219,18 @@ files:
191
219
  - lib/vmpooler/api.rb
192
220
  - lib/vmpooler/api/dashboard.rb
193
221
  - lib/vmpooler/api/helpers.rb
222
+ - lib/vmpooler/api/request_logger.rb
194
223
  - lib/vmpooler/api/reroute.rb
195
224
  - lib/vmpooler/api/v1.rb
196
225
  - lib/vmpooler/dashboard.rb
197
- - lib/vmpooler/dummy_statsd.rb
198
226
  - lib/vmpooler/generic_connection_pool.rb
199
- - lib/vmpooler/graphite.rb
200
227
  - lib/vmpooler/logger.rb
228
+ - lib/vmpooler/metrics.rb
229
+ - lib/vmpooler/metrics/dummy_statsd.rb
230
+ - lib/vmpooler/metrics/graphite.rb
231
+ - lib/vmpooler/metrics/promstats.rb
232
+ - lib/vmpooler/metrics/promstats/collector_middleware.rb
233
+ - lib/vmpooler/metrics/statsd.rb
201
234
  - lib/vmpooler/pool_manager.rb
202
235
  - lib/vmpooler/providers.rb
203
236
  - lib/vmpooler/providers/base.rb
@@ -214,7 +247,7 @@ files:
214
247
  - lib/vmpooler/public/lib/dashboard.js
215
248
  - lib/vmpooler/public/lib/jquery.min.js
216
249
  - lib/vmpooler/public/vmpooler.css
217
- - lib/vmpooler/statsd.rb
250
+ - lib/vmpooler/util/parsing.rb
218
251
  - lib/vmpooler/version.rb
219
252
  - lib/vmpooler/views/dashboard.erb
220
253
  - lib/vmpooler/views/layout.erb
@@ -237,7 +270,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
237
270
  - !ruby/object:Gem::Version
238
271
  version: '0'
239
272
  requirements: []
240
- rubygems_version: 3.0.6
273
+ rubygems_version: 3.0.8
241
274
  signing_key:
242
275
  specification_version: 4
243
276
  summary: vmpooler provides configurable pools of instantly-available (running) virtual
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Vmpooler
4
- class DummyStatsd
5
- attr_reader :server, :port, :prefix
6
-
7
- def initialize(*)
8
- end
9
-
10
- def increment(*)
11
- true
12
- end
13
-
14
- def gauge(*)
15
- true
16
- end
17
-
18
- def timing(*)
19
- true
20
- end
21
- end
22
- end