rbvmomi2 3.0.0 → 3.0.1

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -25
  3. data/exe/rbvmomish +50 -48
  4. data/lib/rbvmomi/basic_types.rb +318 -294
  5. data/lib/rbvmomi/connection.rb +221 -216
  6. data/lib/rbvmomi/deserialization.rb +201 -205
  7. data/lib/rbvmomi/fault.rb +10 -9
  8. data/lib/rbvmomi/optimist.rb +51 -50
  9. data/lib/rbvmomi/pbm.rb +52 -50
  10. data/lib/rbvmomi/sms/SmsStorageManager.rb +2 -1
  11. data/lib/rbvmomi/sms.rb +48 -46
  12. data/lib/rbvmomi/sso.rb +13 -18
  13. data/lib/rbvmomi/trivial_soap.rb +9 -8
  14. data/lib/rbvmomi/type_loader.rb +100 -101
  15. data/lib/rbvmomi/utils/admission_control.rb +90 -106
  16. data/lib/rbvmomi/utils/deploy.rb +77 -85
  17. data/lib/rbvmomi/utils/leases.rb +31 -33
  18. data/lib/rbvmomi/utils/perfdump.rb +177 -207
  19. data/lib/rbvmomi/version.rb +2 -1
  20. data/lib/rbvmomi/vim/ComputeResource.rb +17 -15
  21. data/lib/rbvmomi/vim/Datacenter.rb +1 -0
  22. data/lib/rbvmomi/vim/Datastore.rb +18 -15
  23. data/lib/rbvmomi/vim/DynamicTypeMgrAllTypeInfo.rb +7 -6
  24. data/lib/rbvmomi/vim/DynamicTypeMgrDataTypeInfo.rb +3 -2
  25. data/lib/rbvmomi/vim/DynamicTypeMgrManagedTypeInfo.rb +7 -6
  26. data/lib/rbvmomi/vim/Folder.rb +37 -33
  27. data/lib/rbvmomi/vim/HostSystem.rb +139 -136
  28. data/lib/rbvmomi/vim/ManagedEntity.rb +15 -14
  29. data/lib/rbvmomi/vim/ManagedObject.rb +11 -10
  30. data/lib/rbvmomi/vim/ObjectContent.rb +3 -1
  31. data/lib/rbvmomi/vim/ObjectUpdate.rb +3 -1
  32. data/lib/rbvmomi/vim/OvfManager.rb +50 -57
  33. data/lib/rbvmomi/vim/PerfCounterInfo.rb +4 -3
  34. data/lib/rbvmomi/vim/PerformanceManager.rb +28 -31
  35. data/lib/rbvmomi/vim/PropertyCollector.rb +8 -7
  36. data/lib/rbvmomi/vim/ReflectManagedMethodExecuter.rb +22 -21
  37. data/lib/rbvmomi/vim/ResourcePool.rb +19 -18
  38. data/lib/rbvmomi/vim/ServiceInstance.rb +8 -7
  39. data/lib/rbvmomi/vim/Task.rb +6 -5
  40. data/lib/rbvmomi/vim/VirtualMachine.rb +8 -7
  41. data/lib/rbvmomi/vim.rb +112 -129
  42. data/lib/rbvmomi.rb +1 -0
  43. metadata +54 -10
@@ -1,20 +1,21 @@
1
+ # frozen_string_literal: true
1
2
  # Copyright (c) 2012-2017 VMware, Inc. All Rights Reserved.
2
3
  # SPDX-License-Identifier: MIT
3
4
 
4
5
 
5
6
  # An admission controlled resource scheduler for large scale vSphere deployments
6
7
  #
7
- # While DRS (Dynamic Resource Scheduler) in vSphere handles CPU and Memory
8
- # allocations within a single vSphere cluster, larger deployments require
8
+ # While DRS (Dynamic Resource Scheduler) in vSphere handles CPU and Memory
9
+ # allocations within a single vSphere cluster, larger deployments require
9
10
  # another layer of scheduling to make the use of multiple clusters transparent.
10
- # So this class doesn't replace DRS, but in fact works on top of it.
11
+ # So this class doesn't replace DRS, but in fact works on top of it.
11
12
  #
12
13
  # The scheduler in this class performs admission control to make sure clusters
13
14
  # don't get overloaded. It does so by adding additional metrics to the already
14
- # existing CPU and Memory reservation system that DRS has. After admission
15
+ # existing CPU and Memory reservation system that DRS has. After admission
15
16
  # control it also performs very basic initial placement. Note that in-cluster
16
17
  # placement and load-balancing is left to DRS. Also note that no cross-cluster
17
- # load balancing is done.
18
+ # load balancing is done.
18
19
  #
19
20
  # This class uses the concept of a Pod: A set of clusters that share a set of
20
21
  # datastores. From a datastore perspective, we are free to place a VM on any
@@ -22,33 +23,33 @@
22
23
  # are automatically dicovered based on lists of clusters and datastores.
23
24
  #
24
25
  # Admission control covers the following metrics:
25
- # - Host availability: If no hosts are available within a cluster or pod,
26
+ # - Host availability: If no hosts are available within a cluster or pod,
26
27
  # admission is denied.
27
28
  # - Minimum free space: If a datastore falls below this free space percentage,
28
- # admission to it will be denied. Admission to a pod is granted as long at
29
+ # admission to it will be denied. Admission to a pod is granted as long at
29
30
  # least one datastore passes admission control.
30
31
  # - Maximum number of VMs: If a Pod exceeds a configured number of powered on
31
32
  # VMs, admission is denied. This is a crude but effective catch-all metric
32
- # in case users didn't set proper individual CPU or Memory reservations or
33
+ # in case users didn't set proper individual CPU or Memory reservations or
33
34
  # if the scalability limit doesn't originate from CPU or Memory.
34
35
  #
35
36
  # Placement after admission control:
36
37
  # - Cluster selection: A load metric based on a combination of CPU and Memory
37
38
  # load is used to always select the "least loaded" cluster. The metric is very
38
- # crude and only meant to do very rough load balancing. If DRS clusters are
39
- # large enough, this is good enough in most cases though.
40
- # - Datastore selection: Right now NO intelligence is implemented here.
39
+ # crude and only meant to do very rough load balancing. If DRS clusters are
40
+ # large enough, this is good enough in most cases though.
41
+ # - Datastore selection: Right now NO intelligence is implemented here.
41
42
  #
42
43
  # Usage:
43
44
  # Instantiate the class, call make_placement_decision and then use the exposed
44
- # computer (cluster), resource pool, vm_folder and datastore. Currently once
45
+ # computer (cluster), resource pool, vm_folder and datastore. Currently once
45
46
  # computed, a new updated placement can't be generated.
46
47
  class AdmissionControlledResourceScheduler
47
48
  attr_reader :rp
48
-
49
+
49
50
  def initialize vim, opts = {}
50
51
  @vim = vim
51
-
52
+
52
53
  @datacenter = opts[:datacenter]
53
54
  @datacenter_path = opts[:datacenter_path]
54
55
  @vm_folder = opts[:vm_folder]
@@ -58,17 +59,17 @@ class AdmissionControlledResourceScheduler
58
59
  @computer_names = opts[:computer_names]
59
60
  @datastores = opts[:datastores]
60
61
  @datastore_paths = opts[:datastore_paths]
61
-
62
+
62
63
  @max_vms_per_pod = opts[:max_vms_per_pod]
63
64
  @min_ds_free = opts[:min_ds_free]
64
65
  @service_docs_url = opts[:service_docs_url]
65
-
66
+
66
67
  @pc = @vim.serviceContent.propertyCollector
67
68
  @root_folder = @vim.serviceContent.rootFolder
68
-
69
+
69
70
  @logger = opts[:logger]
70
71
  end
71
-
72
+
72
73
  def log x
73
74
  if @logger
74
75
  @logger.info x
@@ -77,63 +78,56 @@ class AdmissionControlledResourceScheduler
77
78
  end
78
79
  end
79
80
 
80
- # Returns the used VM folder. If not set yet, uses the vm_folder_path to
81
+ # Returns the used VM folder. If not set yet, uses the vm_folder_path to
81
82
  # lookup the folder. If it doesn't exist, it is created. Collisions between
82
83
  # multiple clients concurrently creating the same folder are handled.
83
84
  # @return [RbVmomi::VIM::Folder] The VM folder
84
- def vm_folder
85
+ def vm_folder
85
86
  retries = 1
86
87
  begin
87
88
  @vm_folder ||= datacenter.vmFolder.traverse!(@vm_folder_path, RbVmomi::VIM::Folder)
88
- if !@vm_folder
89
- fail "VM folder #{@vm_folder_path} not found"
90
- end
89
+ raise "VM folder #{@vm_folder_path} not found" if !@vm_folder
91
90
  rescue RbVmomi::Fault => fault
92
91
  if !fault.fault.is_a?(RbVmomi::VIM::DuplicateName)
93
92
  raise
94
93
  else
95
94
  retries -= 1
96
- retry if retries >= 0
97
- end
95
+ retry if retries >= 0
96
+ end
98
97
  end
99
- @vm_folder
98
+ @vm_folder
100
99
  end
101
100
 
102
- # Returns the used Datacenter. If not set yet, uses the datacenter_path to
103
- # lookup the datacenter.
101
+ # Returns the used Datacenter. If not set yet, uses the datacenter_path to
102
+ # lookup the datacenter.
104
103
  # @return [RbVmomi::VIM::Datacenter] The datacenter
105
104
  def datacenter
106
105
  if !@datacenter
107
- @datacenter = @root_folder.traverse(@datacenter_path, RbVmomi::VIM::Datacenter)
108
- if !@datacenter
109
- fail "datacenter #{@datacenter_path} not found"
110
- end
106
+ @datacenter = @root_folder.traverse(@datacenter_path, RbVmomi::VIM::Datacenter)
107
+ raise "datacenter #{@datacenter_path} not found" if !@datacenter
111
108
  end
112
109
  @datacenter
113
110
  end
114
111
 
115
- # Returns the candidate datastores. If not set yet, uses the datastore_paths
112
+ # Returns the candidate datastores. If not set yet, uses the datastore_paths
116
113
  # to lookup the datastores under the datacenter.
117
- # As a side effect, also looks up properties about all the datastores
114
+ # As a side effect, also looks up properties about all the datastores
118
115
  # @return [Array] List of RbVmomi::VIM::Datastore
119
116
  def datastores
120
117
  if !@datastores
121
118
  @datastores = @datastore_paths.map do |path|
122
119
  ds = datacenter.datastoreFolder.traverse(path, RbVmomi::VIM::Datastore)
123
- if !ds
124
- fail "datastore #{path} not found"
125
- end
120
+ raise "datastore #{path} not found" if !ds
121
+
126
122
  ds
127
123
  end
128
124
  end
129
- if !@datastore_props
130
- @datastore_props = @pc.collectMultiple(@datastores, 'summary', 'name')
131
- end
125
+ @datastore_props = @pc.collectMultiple(@datastores, 'summary', 'name') if !@datastore_props
132
126
  @datastores
133
127
  end
134
128
 
135
- # Returns the candidate computers (aka clusters). If not set yet, uses the
136
- # computer_names to look them up.
129
+ # Returns the candidate computers (aka clusters). If not set yet, uses the
130
+ # computer_names to look them up.
137
131
  # @return [Array] List of [RbVmomi::VIM::ClusterComputeResource, Hash] tuples, where
138
132
  # the Hash is a list of stats about the computer
139
133
  def computers
@@ -146,26 +140,26 @@ class AdmissionControlledResourceScheduler
146
140
  @computers
147
141
  end
148
142
 
149
- # Returns the candidate pods. If not set, automatically computes the pods
150
- # based on the list of computers (aka clusters) and datastores.
143
+ # Returns the candidate pods. If not set, automatically computes the pods
144
+ # based on the list of computers (aka clusters) and datastores.
151
145
  # @return [Array] List of pods, where a pod is a list of RbVmomi::VIM::ClusterComputeResource
152
146
  def pods
153
147
  if !@pods
154
148
  # A pod is defined as a set of clusters (aka computers) that share the same
155
149
  # datastore accessibility. Computing pods is done automatically using simple
156
150
  # set theory math.
157
- computersProps = @pc.collectMultiple(computers.map{|x| x[0]}, 'datastore')
151
+ computersProps = @pc.collectMultiple(computers.map{ |x| x[0] }, 'datastore')
158
152
  @pods = computers.map do |computer, stats|
159
153
  computersProps[computer]['datastore'] & self.datastores
160
154
  end.uniq.map do |ds_list|
161
- computers.map{|x| x[0]}.select do |computer|
155
+ computers.map{ |x| x[0] }.select do |computer|
162
156
  (computer.datastore & self.datastores) == ds_list
163
157
  end
164
158
  end
165
159
  end
166
- @pods
160
+ @pods
167
161
  end
168
-
162
+
169
163
  # Returns all VMs residing with a pod. Doesn't account for templates. Does so
170
164
  # very efficiently using a single API query.
171
165
  # @return [Hash] Hash of VMs as keys and their properties as values.
@@ -201,35 +195,35 @@ class AdmissionControlledResourceScheduler
201
195
  { type: 'VirtualMachine', pathSet: %w(runtime.powerState) }
202
196
  ]
203
197
  )
204
-
198
+
205
199
  result = @vim.propertyCollector.RetrieveProperties(specSet: [filterSpec])
206
-
200
+
207
201
  out = result.map { |x| [x.obj, Hash[x.propSet.map { |y| [y.name, y.val] }]] }
208
- out.select{|obj, props| obj.is_a?(RbVmomi::VIM::VirtualMachine)}
202
+ out.select{ |obj, props| obj.is_a?(RbVmomi::VIM::VirtualMachine) }
209
203
  end
210
-
204
+
211
205
  # Returns all candidate datastores for a given pod.
212
206
  # @return [Array] List of RbVmomi::VIM::Datastore
213
207
  def pod_datastores pod
214
208
  pod.first.datastore & self.datastores
215
209
  end
216
-
210
+
217
211
  # Returns the list of pods that pass admission control. If not set yet, performs
218
- # admission control to compute the list. If no pods passed the admission
212
+ # admission control to compute the list. If no pods passed the admission
219
213
  # control, an exception is thrown.
220
214
  # @return [Array] List of pods, where a pod is a list of RbVmomi::VIM::ClusterComputeResource
221
215
  def filtered_pods
222
216
  # This function applies admission control and returns those pods that have
223
- # passed admission control. An exception is thrown if access was denied to
217
+ # passed admission control. An exception is thrown if access was denied to
224
218
  # all pods.
225
219
  if !@filtered_pods
226
- log "Performing admission control:"
220
+ log 'Performing admission control:'
227
221
  @filtered_pods = self.pods.select do |pod|
228
222
  # Gather some statistics about the pod ...
229
- on_vms = pod_vms(pod).select{|k,v| v['runtime.powerState'] == 'poweredOn'}
223
+ on_vms = pod_vms(pod).select{ |k, v| v['runtime.powerState'] == 'poweredOn' }
230
224
  num_pod_vms = on_vms.length
231
225
  pod_datastores = self.pod_datastores(pod)
232
- log "Pod: #{pod.map{|x| x.name}.join(', ')}"
226
+ log "Pod: #{pod.map{ |x| x.name }.join(', ')}"
233
227
  log " #{num_pod_vms} VMs"
234
228
  pod_datastores.each do |ds|
235
229
  ds_sum = @datastore_props[ds]['summary']
@@ -240,10 +234,10 @@ class AdmissionControlledResourceScheduler
240
234
  ds_name = ds_props['name']
241
235
  free = ds_props['free_percent']
242
236
  free_gb = ds_props['summary'].freeSpace.to_f / 1024**3
243
- free_str = "%.2f GB (%.2f%%)" % [free_gb, free]
237
+ free_str = '%.2f GB (%.2f%%)' % [free_gb, free]
244
238
  log " Datastore #{ds_name}: #{free_str} free"
245
239
  end
246
-
240
+
247
241
  # Admission check: VM limit
248
242
  denied = false
249
243
  max_vms = @max_vms_per_pod
@@ -253,7 +247,7 @@ class AdmissionControlledResourceScheduler
253
247
  denied = true
254
248
  end
255
249
  end
256
-
250
+
257
251
  # Admission check: Free space on datastores
258
252
  min_ds_free = @min_ds_free
259
253
  if min_ds_free && min_ds_free > 0
@@ -261,14 +255,14 @@ class AdmissionControlledResourceScheduler
261
255
  low_list = pod_datastores.select do |ds|
262
256
  @datastore_props[ds]['free_percent'] <= min_ds_free
263
257
  end
264
-
258
+
265
259
  if low_list.length == pod_datastores.length
266
- dsNames = low_list.map{|ds| @datastore_props[ds]['name']}.join(", ")
260
+ dsNames = low_list.map{ |ds| @datastore_props[ds]['name'] }.join(', ')
267
261
  err = "Datastores #{dsNames} below minimum free disk space (#{min_ds_free}%)"
268
262
  denied = true
269
263
  end
270
264
  end
271
-
265
+
272
266
  # Admission check: Hosts are available
273
267
  if !denied
274
268
  hosts_available = pod.any? do |computer|
@@ -276,26 +270,24 @@ class AdmissionControlledResourceScheduler
276
270
  stats[:totalCPU] > 0 && stats[:totalMem] > 0
277
271
  end
278
272
  if !hosts_available
279
- err = "No hosts are current available in this pod"
273
+ err = 'No hosts are current available in this pod'
280
274
  denied = true
281
275
  end
282
276
  end
283
-
284
- if denied
277
+
278
+ if denied
285
279
  log " Admission DENIED: #{err}"
286
280
  else
287
- log " Admission granted"
281
+ log ' Admission granted'
288
282
  end
289
-
283
+
290
284
  !denied
291
285
  end
292
286
  end
293
287
  if @filtered_pods.length == 0
294
288
  log "Couldn't find any Pod with enough resources."
295
- if @service_docs_url
296
- log "Check #{@service_docs_url} to see which other Pods you may be able to use"
297
- end
298
- fail "Admission denied"
289
+ log "Check #{@service_docs_url} to see which other Pods you may be able to use" if @service_docs_url
290
+ raise 'Admission denied'
299
291
  end
300
292
  @filtered_pods
301
293
  end
@@ -309,23 +301,20 @@ class AdmissionControlledResourceScheduler
309
301
  # Out of the pods to which we have been granted access, pick the cluster
310
302
  # (aka computer) with the lowest CPU/Mem utilization for load balancing
311
303
  available = self.filtered_pods.flatten
312
- eligible = self.computers.select do |computer,stats|
304
+ eligible = self.computers.select do |computer, stats|
313
305
  available.member?(computer) && stats[:totalCPU] > 0 and stats[:totalMem] > 0
314
306
  end
315
307
  computer = nil
316
308
  if placementhint
317
- if eligible.length > 0
318
- computer = eligible.map{|x| x[0]}[placementhint % eligible.length]
319
- end
309
+ computer = eligible.map{ |x| x[0] }[placementhint % eligible.length] if eligible.length > 0
320
310
  else
321
- computer, = eligible.min_by do |computer,stats|
311
+ computer, = eligible.min_by do |computer, stats|
322
312
  2**(stats[:usedCPU].to_f/stats[:totalCPU]) + (stats[:usedMem].to_f/stats[:totalMem])
323
313
  end
324
314
  end
325
-
326
- if !computer
327
- fail "No clusters available, should have been prevented by admission control"
328
- end
315
+
316
+ raise 'No clusters available, should have been prevented by admission control' if !computer
317
+
329
318
  @computer = computer
330
319
  end
331
320
  @computer
@@ -335,65 +324,60 @@ class AdmissionControlledResourceScheduler
335
324
  # datastore without much intelligence, as long as it passes admission control.
336
325
  # @return [RbVmomi::VIM::Datastore] Chosen datastore
337
326
  def datastore placementHint = nil
338
- if @datastore
339
- return @datastore
340
- end
341
-
327
+ return @datastore if @datastore
328
+
342
329
  pod_datastores = pick_computer.datastore & datastores
343
-
330
+
344
331
  eligible = pod_datastores.select do |ds|
345
332
  min_ds_free = @min_ds_free
346
333
  if min_ds_free && min_ds_free > 0
347
334
  ds_sum = @datastore_props[ds]['summary']
348
- free_percent = ds_sum.freeSpace.to_f * 100 / ds_sum.capacity
335
+ free_percent = ds_sum.freeSpace.to_f * 100 / ds_sum.capacity
349
336
  free_percent > min_ds_free
350
337
  else
351
338
  true
352
339
  end
353
340
  end
354
-
355
- if eligible.length == 0
356
- fail "Couldn't find any eligible datastore. Admission control should have prevented this"
357
- end
358
-
341
+
342
+ raise "Couldn't find any eligible datastore. Admission control should have prevented this" if eligible.length == 0
343
+
359
344
  if placementHint && placementHint > 0
360
345
  @datastore = eligible[placementHint % eligible.length]
361
346
  else
362
347
  @datastore = eligible.first
363
- end
348
+ end
364
349
  @datastore
365
350
  end
366
-
367
- # Runs the placement algorithm and populates all the various properties as
351
+
352
+ # Runs the placement algorithm and populates all the various properties as
368
353
  # a side effect. Run this first, before using the other functions of this
369
354
  # class.
370
355
  def make_placement_decision opts = {}
371
356
  self.filtered_pods
372
357
  self.pick_computer opts[:placementHint]
373
358
  log "Selected compute resource: #{@computer.name}"
374
-
359
+
375
360
  @rp = @computer.resourcePool.traverse(@rp_path)
376
- if !@rp
377
- fail "Resource pool #{@rp_path} not found"
378
- end
361
+ raise "Resource pool #{@rp_path} not found" if !@rp
362
+
379
363
  log "Resource pool: #{@rp.pretty_path}"
380
-
364
+
381
365
  stats = @computer.stats
382
366
  if stats[:totalMem] > 0 && stats[:totalCPU] > 0
383
367
  cpu_load = "#{(100*stats[:usedCPU])/stats[:totalCPU]}% cpu"
384
368
  mem_load = "#{(100*stats[:usedMem])/stats[:totalMem]}% mem"
385
369
  log "Cluster utilization: #{cpu_load}, #{mem_load}"
386
370
  end
387
-
388
- user_vms = vm_folder.inventory_flat('VirtualMachine' => %w(name storage)).select do |k, v|
371
+
372
+ user_vms = vm_folder.inventory_flat('VirtualMachine' => %w(name storage)).select do |k, v|
389
373
  k.is_a?(RbVmomi::VIM::VirtualMachine)
390
374
  end
391
375
  numVms = user_vms.length
392
- unshared = user_vms.map do |vm, info|
393
- info['storage'].perDatastoreUsage.map{|x| x.unshared}.inject(0, &:+)
376
+ unshared = user_vms.map do |vm, info|
377
+ info['storage'].perDatastoreUsage.map{ |x| x.unshared }.inject(0, &:+)
394
378
  end.inject(0, &:+)
395
379
  log "User stats: #{numVms} VMs using %.2fGB of storage" % [unshared.to_f / 1024**3]
396
-
380
+
397
381
  @placement_hint = opts[:placement_hint] || (rand(100) + 1)
398
382
  datastore = self.datastore @placement_hint
399
383
  log "Datastore: #{datastore.name}"