rbvmomi2 3.0.0

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