rbvmomi2 3.0.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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +19 -0
  3. data/README.md +114 -0
  4. data/exe/rbvmomish +138 -0
  5. data/lib/rbvmomi/basic_types.rb +383 -0
  6. data/lib/rbvmomi/connection.rb +272 -0
  7. data/lib/rbvmomi/deserialization.rb +249 -0
  8. data/lib/rbvmomi/fault.rb +19 -0
  9. data/lib/rbvmomi/optimist.rb +72 -0
  10. data/lib/rbvmomi/pbm.rb +68 -0
  11. data/lib/rbvmomi/sms/SmsStorageManager.rb +10 -0
  12. data/lib/rbvmomi/sms.rb +63 -0
  13. data/lib/rbvmomi/sso.rb +313 -0
  14. data/lib/rbvmomi/trivial_soap.rb +122 -0
  15. data/lib/rbvmomi/type_loader.rb +138 -0
  16. data/lib/rbvmomi/utils/admission_control.rb +401 -0
  17. data/lib/rbvmomi/utils/deploy.rb +318 -0
  18. data/lib/rbvmomi/utils/leases.rb +145 -0
  19. data/lib/rbvmomi/utils/perfdump.rb +631 -0
  20. data/lib/rbvmomi/version.rb +6 -0
  21. data/lib/rbvmomi/vim/ComputeResource.rb +54 -0
  22. data/lib/rbvmomi/vim/Datacenter.rb +25 -0
  23. data/lib/rbvmomi/vim/Datastore.rb +72 -0
  24. data/lib/rbvmomi/vim/DynamicTypeMgrAllTypeInfo.rb +78 -0
  25. data/lib/rbvmomi/vim/DynamicTypeMgrDataTypeInfo.rb +23 -0
  26. data/lib/rbvmomi/vim/DynamicTypeMgrManagedTypeInfo.rb +54 -0
  27. data/lib/rbvmomi/vim/Folder.rb +214 -0
  28. data/lib/rbvmomi/vim/HostSystem.rb +177 -0
  29. data/lib/rbvmomi/vim/ManagedEntity.rb +60 -0
  30. data/lib/rbvmomi/vim/ManagedObject.rb +63 -0
  31. data/lib/rbvmomi/vim/ObjectContent.rb +26 -0
  32. data/lib/rbvmomi/vim/ObjectUpdate.rb +26 -0
  33. data/lib/rbvmomi/vim/OvfManager.rb +204 -0
  34. data/lib/rbvmomi/vim/PerfCounterInfo.rb +28 -0
  35. data/lib/rbvmomi/vim/PerformanceManager.rb +113 -0
  36. data/lib/rbvmomi/vim/PropertyCollector.rb +28 -0
  37. data/lib/rbvmomi/vim/ReflectManagedMethodExecuter.rb +33 -0
  38. data/lib/rbvmomi/vim/ResourcePool.rb +58 -0
  39. data/lib/rbvmomi/vim/ServiceInstance.rb +58 -0
  40. data/lib/rbvmomi/vim/Task.rb +68 -0
  41. data/lib/rbvmomi/vim/VirtualMachine.rb +75 -0
  42. data/lib/rbvmomi/vim.rb +157 -0
  43. data/lib/rbvmomi.rb +16 -0
  44. data/lib/rbvmomi2.rb +3 -0
  45. data/vmodl.db +0 -0
  46. metadata +214 -0
@@ -0,0 +1,631 @@
1
+ # Copyright (c) 2012-2017 VMware, Inc. All Rights Reserved.
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ require 'set'
5
+ require 'yaml'
6
+
7
+ # PerfAggregator is a class that, given connections to a list of vCenter
8
+ # Servers, will fetch the entire VM folder and ResourcePool hierarchies,
9
+ # including all VIM::VirtualMachine objects and aggregate VM stats along
10
+ # the tree hierarchies. The PerfAggregator class allows for users to
11
+ # perform post processing on the data returned by vCenter, e.g. to augment
12
+ # it with addtional data that was obtained using a combination of
13
+ # VM annotations (or custom values) and an external DB. Post processing
14
+ # can also define additional tree structures that may be completely
15
+ # independent of the VM folder and ResourcePool hirarchies provided by
16
+ # vCenter, e.g. one based on VMs used for testing of a set of source code
17
+ # branches.
18
+ class PerfAggregator
19
+ attr_accessor :path_types
20
+
21
+ def initialize logger = nil
22
+ @logger = logger
23
+ @path_types = Set.new
24
+ @path_types << 'rp'
25
+ @path_types << 'vmfolder'
26
+
27
+ # XXX: Rename this variable
28
+ @perf_metrics = {
29
+ 'virtualDisk.read' => :sum,
30
+ 'virtualDisk.write' => :sum,
31
+ 'virtualDisk.numberReadAveraged' => :sum,
32
+ 'virtualDisk.numberWriteAveraged' => :sum,
33
+ 'virtualDisk.totalReadLatency.avg' => :avg_ignore_zero,
34
+ 'virtualDisk.totalWriteLatency.avg' => :avg_ignore_zero,
35
+ 'virtualDisk.totalReadLatency.max' => :max,
36
+ 'virtualDisk.totalWriteLatency.max' => :max,
37
+ 'num.vm' => :sum,
38
+ 'num.poweredonvm' => :sum,
39
+ 'summary.quickStats.hostMemoryUsage' => :sum,
40
+ 'summary.quickStats.guestMemoryUsage' => :sum,
41
+ 'summary.quickStats.overallCpuUsage' => :sum,
42
+ 'summary.config.memorySizeMB' => :sum,
43
+ 'summary.config.numCpu' => :sum,
44
+ 'storage.space.committed' => :sum,
45
+ 'storage.space.uncommitted' => :sum,
46
+ 'storage.space.unshared' => :sum,
47
+ }
48
+ end
49
+
50
+ def log text
51
+ if @logger
52
+ @logger.info text
53
+ else
54
+ puts "#{Time.now}: #{text}"
55
+ end
56
+ end
57
+
58
+ def set_vm_processing_callback &block
59
+ @vm_processing_callback = block
60
+ end
61
+
62
+ def add_node_unless_exists inventory, id, props
63
+ if !inventory[id]
64
+ inventory[id] = props.merge({'children' => []})
65
+ end
66
+ end
67
+
68
+ # Method that extracts the entire VM folder and ResourcePool hierarchy
69
+ # from vCenter with a single API call. It generates a flat list of
70
+ # VIM objects which will include VIM::Folder, VIM::Datacenter,
71
+ # VIM::ClusterComputeResource, VIM::ResourcePool and VIM::VirtualMachine.
72
+ #
73
+ # Post processing is done (using helper methods) to populate full paths,
74
+ # lists of parents (ancestry) so that the tree structure can be understood.
75
+ # Information about two seperate sub-trees is gathered: The tree following
76
+ # the VM folders and one tree following the clusters and resource pools.
77
+ # In the vSphere Client there are called the "VM/Template View" and the
78
+ # "Host and Clusters View".
79
+ #
80
+ # @param rootFolder [VIM::Folder] Expected to be the rootFolder of the VC
81
+ # @param vm_prop_names [Array] List of VM properties to fetch
82
+ def all_inventory_flat rootFolder, vm_prop_names = ['name']
83
+ conn = rootFolder._connection
84
+ pc = conn.propertyCollector
85
+
86
+ filterSpec = RbVmomi::VIM.PropertyFilterSpec(
87
+ :objectSet => [
88
+ :obj => rootFolder,
89
+ :selectSet => [
90
+ RbVmomi::VIM.TraversalSpec(
91
+ :name => 'tsFolder',
92
+ :type => 'Folder',
93
+ :path => 'childEntity',
94
+ :skip => false,
95
+ :selectSet => [
96
+ RbVmomi::VIM.SelectionSpec(:name => 'tsFolder'),
97
+ RbVmomi::VIM.SelectionSpec(:name => 'tsDatacenterVmFolder'),
98
+ RbVmomi::VIM.SelectionSpec(:name => 'tsDatacenterHostFolder'),
99
+ RbVmomi::VIM.SelectionSpec(:name => 'tsClusterRP'),
100
+ RbVmomi::VIM.SelectionSpec(:name => 'tsClusterHost'),
101
+ ]
102
+ ),
103
+ RbVmomi::VIM.TraversalSpec(
104
+ :name => 'tsDatacenterVmFolder',
105
+ :type => 'Datacenter',
106
+ :path => 'vmFolder',
107
+ :skip => false,
108
+ :selectSet => [
109
+ RbVmomi::VIM.SelectionSpec(:name => 'tsFolder')
110
+ ]
111
+ ),
112
+ RbVmomi::VIM.TraversalSpec(
113
+ :name => 'tsDatacenterHostFolder',
114
+ :type => 'Datacenter',
115
+ :path => 'hostFolder',
116
+ :skip => false,
117
+ :selectSet => [
118
+ RbVmomi::VIM.SelectionSpec(:name => 'tsFolder')
119
+ ]
120
+ ),
121
+ RbVmomi::VIM.TraversalSpec(
122
+ :name => 'tsClusterRP',
123
+ :type => 'ClusterComputeResource',
124
+ :path => 'resourcePool',
125
+ :skip => false,
126
+ :selectSet => [
127
+ RbVmomi::VIM.SelectionSpec(:name => 'tsRP'),
128
+ ]
129
+ ),
130
+ RbVmomi::VIM.TraversalSpec(
131
+ :name => 'tsClusterHost',
132
+ :type => 'ClusterComputeResource',
133
+ :path => 'host',
134
+ :skip => false,
135
+ :selectSet => []
136
+ ),
137
+ RbVmomi::VIM.TraversalSpec(
138
+ :name => 'tsRP',
139
+ :type => 'ResourcePool',
140
+ :path => 'resourcePool',
141
+ :skip => false,
142
+ :selectSet => [
143
+ RbVmomi::VIM.SelectionSpec(:name => 'tsRP'),
144
+ ]
145
+ ),
146
+ ]
147
+ ],
148
+ :propSet => [
149
+ { :type => 'Folder', :pathSet => ['name', 'parent'] },
150
+ { :type => 'Datacenter', :pathSet => ['name', 'parent'] },
151
+ { :type => 'ClusterComputeResource',
152
+ :pathSet => ['name', 'parent', 'summary.effectiveCpu', 'summary.effectiveMemory']
153
+ },
154
+ { :type => 'ResourcePool', :pathSet => ['name', 'parent'] },
155
+ { :type => 'HostSystem', :pathSet => ['name', 'parent', 'runtime.connectionState'] },
156
+ { :type => 'VirtualMachine', :pathSet => vm_prop_names },
157
+ ]
158
+ )
159
+
160
+ result = pc.RetrieveProperties(:specSet => [filterSpec])
161
+ inventory = {}
162
+ vms = {}
163
+ result.each do |r|
164
+ if r.obj.is_a?(RbVmomi::VIM::VirtualMachine)
165
+ vms[r.obj] = r.to_hash
166
+ else
167
+ inventory[r.obj] = r.to_hash
168
+ end
169
+ end
170
+ inventory['root'] = {
171
+ 'name' => 'root',
172
+ 'path' => 'root',
173
+ 'parent' => nil,
174
+ 'parents' => [],
175
+ }
176
+ inventory[conn.host] = {
177
+ 'name' => conn.host,
178
+ 'path' => "root/#{conn.host}",
179
+ 'parent' => 'root',
180
+ 'parents' => ['root'],
181
+ }
182
+ _compute_vmfolders_and_rp_paths conn.host, inventory
183
+ _compute_parents_and_children inventory
184
+ [vms, inventory]
185
+ end
186
+
187
+ # Helper method that computes full paths and parent lists out of a
188
+ # flat list of objects. Operates recursively and doesn't yet split
189
+ # the paths into different tree types.
190
+ # @param obj [Hash] Property hash of current element
191
+ # @param objs [Array] Flat list of tree elements
192
+ def _compute_vmfolder_and_rp_path_and_parents vc, obj, objs
193
+ if obj['path']
194
+ return
195
+ end
196
+ if !obj['parent']
197
+ obj['parent'] = vc
198
+ obj['path'] = "root/#{vc}/#{obj['name']}"
199
+ obj['parents'] = ['root', vc]
200
+ return
201
+ end
202
+ parent = objs[obj['parent']]
203
+ _compute_vmfolder_and_rp_path_and_parents(vc, parent, objs)
204
+ obj['path'] = "%s/%s" % [parent['path'], obj['name']]
205
+ obj['parents'] = [obj['parent']] + parent['parents']
206
+ nil
207
+ end
208
+
209
+ # Helper method that computes full paths and parent lists out of a
210
+ # flat list of objects. Full paths are tracked seperately per type
211
+ # of tree, i.e. seperately for the ResourcePool tree and the VM folder
212
+ # tree.
213
+ # @param objs [Array] Flat list of tree elements
214
+ def _compute_vmfolders_and_rp_paths vc, objs
215
+ objs.each do |obj, props|
216
+ _compute_vmfolder_and_rp_path_and_parents(vc, props, objs)
217
+
218
+ props['paths'] = {}
219
+ obj_with_parents = [obj] + props['parents']
220
+ dc = obj_with_parents.find{|x| x.is_a?(RbVmomi::VIM::Datacenter)}
221
+ # Everything above and including a VIM::Datacenter is part of
222
+ # both the rp and vmfolder tree. Anything below depends on the
223
+ # folder of the datacenter it is under: The hostFolder is called
224
+ # "host" while the root vm folder is called "vm".
225
+ if !dc || obj.is_a?(RbVmomi::VIM::Datacenter)
226
+ props['paths']['rp'] = props['path']
227
+ props['paths']['vmfolder'] = props['path']
228
+ else
229
+ dc_index = obj_with_parents.index dc
230
+ folder = obj_with_parents[dc_index - 1]
231
+ if objs[folder]['name'] == 'host'
232
+ props['paths']['rp'] = props['path']
233
+ else
234
+ props['paths']['vmfolder'] = props['path']
235
+ end
236
+ end
237
+
238
+ props['children'] = []
239
+ end
240
+ end
241
+
242
+ # Helper method that computes children references and parent paths on
243
+ # all objects, if not computed yet. Assumes that full paths of each
244
+ # object have been calculated already.
245
+ # @param objs [Array] Flat list of tree elements
246
+ def _compute_parents_and_children objs
247
+ objs.each do |obj, props|
248
+ if props['parent_paths']
249
+ next
250
+ end
251
+ props['parent_paths'] = {}
252
+ if !props['parent']
253
+ next
254
+ end
255
+ parent = objs[props['parent']]
256
+ props['paths'].keys.each do |type|
257
+ props['parent_paths'][type] = parent['paths'][type]
258
+ end
259
+ parent['children'] << obj
260
+ end
261
+ end
262
+
263
+ def _aggregate_metrics vms_stats, perf_metrics
264
+ out = Hash[perf_metrics.keys.map{|x| [x, 0]}]
265
+ avg_counter = Hash[perf_metrics.keys.map{|x| [x, 0]}]
266
+
267
+ vms_stats.each do |vm_stats|
268
+ perf_metrics.each do |key, type|
269
+ values = vm_stats[key]
270
+ if !values.is_a?(Array)
271
+ values = [values]
272
+ end
273
+ values.compact.each do |val|
274
+ if type == :sum
275
+ out[key] += val
276
+ elsif type == :max
277
+ out[key] = [out[key], val].max
278
+ elsif type == :avg
279
+ out[key] += val.to_f
280
+ avg_counter[key] += 1
281
+ elsif type == :avg_ignore_zero
282
+ if val > 0
283
+ out[key] += val.to_f
284
+ avg_counter[key] += 1
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end
290
+
291
+ perf_metrics.each do |key, type|
292
+ if type == :avg_ignore_zero || type == :avg
293
+ if avg_counter[key] > 0
294
+ out[key] = out[key] / avg_counter[key]
295
+ end
296
+ end
297
+ end
298
+
299
+ out
300
+ end
301
+
302
+ def _collect_info_on_all_vms_single root_folder, opts = {}
303
+ prop_names = opts[:prop_names]
304
+ if !prop_names
305
+ prop_names = [
306
+ 'name',
307
+ 'config.template',
308
+ 'runtime.powerState', 'datastore', 'config.annotation',
309
+ 'parent', 'resourcePool', 'storage.perDatastoreUsage',
310
+ 'summary.config.memorySizeMB',
311
+ 'summary.config.numCpu',
312
+ 'summary.quickStats.hostMemoryUsage',
313
+ 'summary.quickStats.guestMemoryUsage',
314
+ 'summary.quickStats.overallCpuUsage',
315
+ 'runtime.connectionState',
316
+ 'config.instanceUuid',
317
+ 'customValue',
318
+ ]
319
+ end
320
+ perf_metrics = opts[:perf_metrics]
321
+ if !perf_metrics
322
+ perf_metrics = {
323
+ 'virtualDisk.read' => :avg,
324
+ 'virtualDisk.write' => :avg,
325
+ 'virtualDisk.numberReadAveraged' => :avg,
326
+ 'virtualDisk.numberWriteAveraged' => :avg,
327
+ 'virtualDisk.totalReadLatency' => :avg_ignore_zero,
328
+ 'virtualDisk.totalWriteLatency' => :avg_ignore_zero,
329
+ }
330
+ end
331
+ host_perf_metrics = opts[:host_perf_metrics]
332
+ if !host_perf_metrics
333
+ host_perf_metrics = {
334
+ 'cpu.usage' => :avg,
335
+ 'mem.usage' => :avg,
336
+ }
337
+ end
338
+
339
+ vms_props, inventory = all_inventory_flat root_folder, prop_names
340
+ vms = vms_props.keys
341
+
342
+ hosts_props = inventory.select{|k, v| k.is_a?(RbVmomi::VIM::HostSystem)}
343
+
344
+ conn = root_folder._connection
345
+ sc = conn.serviceContent
346
+ pc = sc.propertyCollector
347
+ pm = sc.perfManager
348
+ vc_uuid = conn.instanceUuid
349
+
350
+ connected_vms = vms_props.select do |vm, props|
351
+ is_connected = props['runtime.connectionState'] != "disconnected"
352
+ is_template = props['config.template']
353
+ is_connected && !is_template
354
+ end.keys
355
+
356
+ begin
357
+ # XXX: Need to find a good way to get the "right" samples
358
+ if connected_vms.length == 0
359
+ {}
360
+ else
361
+ vms_stats = pm.retrieve_stats(
362
+ connected_vms, perf_metrics.keys,
363
+ :max_samples => 3
364
+ )
365
+ end
366
+ rescue RbVmomi::Fault => ex
367
+ if ex.fault.is_a? RbVmomi::VIM::ManagedObjectNotFound
368
+ connected_vms -= [ex.fault.obj]
369
+ retry
370
+ end
371
+ raise
372
+ end
373
+
374
+ connected_hosts = hosts_props.select do |k,v|
375
+ v['runtime.connectionState'] != "disconnected"
376
+ end
377
+ if connected_hosts.length > 0
378
+ hosts_stats = pm.retrieve_stats(
379
+ connected_hosts.keys, host_perf_metrics.keys,
380
+ :max_samples => 3
381
+ )
382
+ end
383
+ hosts_props.each do |host, props|
384
+ if !connected_hosts[host]
385
+ next
386
+ end
387
+
388
+ stats = hosts_stats[host] || {}
389
+ stats = stats[:metrics] || {}
390
+ stats = _aggregate_metrics [stats], host_perf_metrics
391
+ props.merge!(stats)
392
+ end
393
+
394
+ vms_props.each do |vm, props|
395
+ if !connected_vms.member?(vm)
396
+ next
397
+ end
398
+ props['num.vm'] = 1
399
+ powered_on = (props['runtime.powerState'] == 'poweredOn')
400
+ props['num.poweredonvm'] = powered_on ? 1 : 0
401
+
402
+ stats = vms_stats[vm] || {}
403
+ stats = stats[:metrics] || {}
404
+ stats = _aggregate_metrics [stats], perf_metrics
405
+ props.merge!(stats)
406
+ props['virtualDisk.totalReadLatency.avg'] = props['virtualDisk.totalReadLatency']
407
+ props['virtualDisk.totalWriteLatency.avg'] = props['virtualDisk.totalWriteLatency']
408
+ props['virtualDisk.totalReadLatency.max'] = props['virtualDisk.totalReadLatency']
409
+ props['virtualDisk.totalWriteLatency.max'] = props['virtualDisk.totalWriteLatency']
410
+ props.delete('virtualDisk.totalReadLatency')
411
+ props.delete('virtualDisk.totalWriteLatency')
412
+
413
+ per_ds_usage = props['storage.perDatastoreUsage']
414
+ props['storage.space.committed'] = per_ds_usage.map{|x| x.committed}.inject(0, &:+)
415
+ props['storage.space.uncommitted'] = per_ds_usage.map{|x| x.uncommitted}.inject(0, &:+)
416
+ props['storage.space.unshared'] = per_ds_usage.map{|x| x.unshared}.inject(0, &:+)
417
+
418
+ props['parent_paths'] = {}
419
+ if inventory[props['parent']]
420
+ props['parent_paths']['vmfolder'] = inventory[props['parent']]['path']
421
+ end
422
+ if !props['config.template']
423
+ rp_props = inventory[props['resourcePool']]
424
+ props['parent_paths']['rp'] = rp_props['path']
425
+ end
426
+
427
+ props['annotation_yaml'] = YAML.load(props['config.annotation'] || '')
428
+ if !props['annotation_yaml'].is_a?(Hash)
429
+ props['annotation_yaml'] = {}
430
+ end
431
+
432
+ props['customValue'] = Hash[props['customValue'].map do |x|
433
+ [x.key, x.value]
434
+ end]
435
+
436
+ props['vc_uuid'] = vc_uuid
437
+ end
438
+
439
+ [vms_props, inventory, hosts_props]
440
+ end
441
+
442
+ def collect_info_on_all_vms root_folders, opts = {}
443
+ log "Fetching information from all VCs ..."
444
+ vms_props = {}
445
+ hosts_props = {}
446
+ inventory = {}
447
+ lock = Mutex.new
448
+ root_folders.map do |root_folder|
449
+ Thread.new do
450
+ begin
451
+ single_vms_props, single_inventory, single_hosts_props =
452
+ _collect_info_on_all_vms_single(root_folder, opts)
453
+
454
+ lock.synchronize do
455
+ vms_props.merge!(single_vms_props)
456
+ if inventory['root']
457
+ single_inventory['root']['children'] += inventory['root']['children']
458
+ end
459
+ inventory.merge!(single_inventory)
460
+ hosts_props.merge!(single_hosts_props)
461
+ end
462
+ rescue Exception => ex
463
+ log "#{ex.class}: #{ex.message}"
464
+ ex.backtrace.each do |line|
465
+ log line
466
+ end
467
+ raise
468
+ end
469
+ end
470
+ end.each{|t| t.join}
471
+
472
+ log "Make data marshal friendly ..."
473
+ inventory = _make_marshal_friendly(inventory)
474
+ vms_props = _make_marshal_friendly(vms_props)
475
+ hosts_props = _make_marshal_friendly(hosts_props)
476
+
477
+ log "Perform external post processing ..."
478
+ if @vm_processing_callback
479
+ @vm_processing_callback.call(self, vms_props, inventory)
480
+ end
481
+
482
+ log "Perform data aggregation ..."
483
+ # Processing the annotations may have added new nodes to the
484
+ # inventory list, hence we need to run _compute_parents_and_children
485
+ # again to calculate the parents and children for the newly
486
+ # added nodes.
487
+ _compute_parents_and_children inventory
488
+
489
+ # Now that we have all VMs and a proper inventory tree built, we can
490
+ # aggregate the VM stats along all trees and tree nodes. This
491
+ # de-normalizes the data heavily, but thats fine
492
+ path_types = opts[:path_types] || @path_types
493
+ inventory = _aggregate_vms path_types, vms_props, inventory
494
+
495
+ log "Done collecting and aggregating stats"
496
+
497
+ @inventory = inventory
498
+ @vms_props = vms_props
499
+
500
+ {
501
+ 'inventory' => inventory,
502
+ 'vms_props' => vms_props,
503
+ 'hosts_props' => hosts_props,
504
+ }
505
+ end
506
+
507
+ def _make_marshal_friendly hash
508
+ hash = Hash[hash.map do |k, v|
509
+ if v['parent']
510
+ v['parent'] = _mo2str(v['parent'])
511
+ end
512
+ if v['resourcePool']
513
+ v['resourcePool'] = _mo2str(v['resourcePool'])
514
+ end
515
+ if v['children']
516
+ v['children'] = v['children'].map{|x| _mo2str(x)}
517
+ end
518
+ if v['parents']
519
+ v['parents'] = v['parents'].map{|x| _mo2str(x)}
520
+ end
521
+ if v['datastore']
522
+ v['datastore'] = v['datastore'].map{|x| _mo2str(x)}
523
+ end
524
+ v['type'] = k.class.name
525
+ [_mo2str(k), v]
526
+ end]
527
+ # Marhsal hash to JSON and back. This is just debug code to ensure
528
+ # that all further processing can be done on a serialized dump of
529
+ # the data.
530
+ hash = JSON.load(JSON.dump(hash))
531
+ end
532
+
533
+ def _mo2str mo
534
+ if !mo.is_a?(RbVmomi::VIM::ManagedObject)
535
+ mo
536
+ else
537
+ "vim-#{mo._connection.instanceUuid}-#{mo._ref}"
538
+ end
539
+ end
540
+
541
+ # Helper method that aggregates the VM stats along all trees and
542
+ # tree nodes. This de-normalizes the data heavily, but thats fine.
543
+ def _aggregate_vms path_types, vms_props, inventory
544
+ # XXX: Opimtization:
545
+ # This function is currently quite wasteful. It computes all VMs
546
+ # at each level and then aggregates the VMs for each node individually
547
+ # Instead, the aggregation itself should explot the tree structure.
548
+ path_types.each do |path_type|
549
+ index = {}
550
+ reverse_index = {}
551
+ inventory.each do |k, v|
552
+ if v['paths'] && v['paths'][path_type]
553
+ path = v['paths'][path_type]
554
+ index[path] = v
555
+ reverse_index[path] = k
556
+ end
557
+ end
558
+
559
+ paths_vms = {}
560
+
561
+ vms_props.each do |vm, props|
562
+ if !props['parent_paths'] || !props['parent_paths'][path_type]
563
+ next
564
+ end
565
+ parent_path = props['parent_paths'][path_type]
566
+ while parent_path
567
+ parent = index[parent_path]
568
+ if !parent
569
+ puts "Parent is nil, so dumping some stuff"
570
+ puts path_type
571
+ puts "parent path: #{parent_path}"
572
+ pp index.keys
573
+ pp props
574
+ end
575
+ paths_vms[parent_path] ||= []
576
+ paths_vms[parent_path] << vm
577
+ parent_path = parent['parent_paths'][path_type]
578
+ end
579
+ end
580
+
581
+ paths_vms.each do |k, vms|
582
+ inventory[reverse_index[k]]['vms'] ||= {}
583
+ inventory[reverse_index[k]]['vms'][path_type] = vms
584
+ vms_stats = vms_props.select{|k, v| vms.member?(k)}.values
585
+ stats = _aggregate_metrics vms_stats, @perf_metrics
586
+ inventory[reverse_index[k]]['stats'] ||= {}
587
+ inventory[reverse_index[k]]['stats'][path_type] = stats
588
+ end
589
+
590
+ #pp paths_vms.map{|k, v| [k, reverse_index[k], v.length, index[k]['stats'][path_type].length]}
591
+ end
592
+
593
+ inventory
594
+ end
595
+
596
+ def visualize_vm_props
597
+ path_types_rows = construct_tree_rows_from_vm_props
598
+ path_types_rows.each do |path_type, rows|
599
+ puts "Path type #{path_type}:"
600
+ rows.each do |row|
601
+ indent, name, stats = row
602
+ puts "#{' ' * indent}#{name}: #{stats['num.vm']}"
603
+ end
604
+ puts ""
605
+ end
606
+ end
607
+
608
+ def construct_tree_rows_from_vm_props path_types = nil
609
+ path_types ||= @path_types
610
+ def visualize_node path_type, node, inventory, indent = 0
611
+ rows = []
612
+ if !node || !node['stats'] || !node['stats'][path_type]
613
+ stats = {}
614
+ return []
615
+ else
616
+ stats = node['stats'][path_type]
617
+ end
618
+ rows << [indent, node['name'], stats]
619
+ node['children'].each do |child|
620
+ rows += visualize_node path_type, inventory[child], inventory, indent + 1
621
+ end
622
+ rows
623
+ end
624
+
625
+ Hash[path_types.map do |path_type|
626
+ key, root = @inventory.find{|k, v| v['paths'][path_type] == 'root'}
627
+ rows = visualize_node path_type, root, @inventory
628
+ [path_type, rows]
629
+ end]
630
+ end
631
+ end
@@ -0,0 +1,6 @@
1
+ # Copyright (c) 2016-2020 VMware, Inc. All Rights Reserved.
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ module RbVmomi
5
+ VERSION = '3.0.0'.freeze
6
+ end
@@ -0,0 +1,54 @@
1
+ # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved.
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ class RbVmomi::VIM::ComputeResource
5
+ # Aggregate cluster information.
6
+ #
7
+ # @note Values are returned in a hash.
8
+ #
9
+ # @return [Mhz] totalCPU: Sum of the frequencies of each CPU in the cluster.
10
+ # @return [Mhz] usedCPU: CPU cycles used across the cluster.
11
+ # @return [MB] totalMem: Total RAM.
12
+ # @return [MB] usedMem: Used RAM.
13
+ def stats
14
+ filterSpec = RbVmomi::VIM.PropertyFilterSpec(
15
+ :objectSet => [{
16
+ :obj => self,
17
+ :selectSet => [
18
+ RbVmomi::VIM.TraversalSpec(
19
+ :name => 'tsHosts',
20
+ :type => 'ComputeResource',
21
+ :path => 'host',
22
+ :skip => false
23
+ )
24
+ ]
25
+ }],
26
+ :propSet => [{
27
+ :pathSet => %w(name overallStatus summary.hardware.cpuMhz
28
+ summary.hardware.numCpuCores summary.hardware.memorySize
29
+ summary.quickStats.overallCpuUsage
30
+ summary.quickStats.overallMemoryUsage),
31
+ :type => 'HostSystem'
32
+ }]
33
+ )
34
+
35
+ result = _connection.propertyCollector.RetrieveProperties(:specSet => [filterSpec])
36
+
37
+ stats = {
38
+ :totalCPU => 0,
39
+ :totalMem => 0,
40
+ :usedCPU => 0,
41
+ :usedMem => 0,
42
+ }
43
+
44
+ result.each do |x|
45
+ next if x['overallStatus'] == 'red'
46
+ stats[:totalCPU] += x['summary.hardware.cpuMhz'] * x['summary.hardware.numCpuCores']
47
+ stats[:totalMem] += x['summary.hardware.memorySize'] / (1024*1024)
48
+ stats[:usedCPU] += x['summary.quickStats.overallCpuUsage'] || 0
49
+ stats[:usedMem] += x['summary.quickStats.overallMemoryUsage'] || 0
50
+ end
51
+
52
+ stats
53
+ end
54
+ end