vmpooler 0.12.0 → 0.14.0

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