From 8b1ab19cb1487ed90c0d0032211c971a66702bce Mon Sep 17 00:00:00 2001 From: Sean Walberg Date: Mon, 19 Mar 2018 12:21:22 -0400 Subject: [PATCH] Improve the speed of finding an individual VM (#422) * Improve the speed of finding an individual VM This PR makes use of the [ContainerView](https://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.view.ViewManager.html) to retrieve a cut down list of servers from which the desired object can be found, after which the object itself can be downloaded. Previously, we'd download each VM (about 3.5K and several round trips to the server) to find the VM we want. From ``` sean~/play/knife-vsphere (find_speed %)$ time knife vsphere vm disk list myserver 0 Hard disk 1 nosnapshots_ds4 64.00 GiB real 1m56.153s user 0m9.536s sys 0m1.713s ``` to ``` sean~/play/knife-vsphere (find_speed *%)$ time be knife vsphere vm disk list myserver /Users/sean/.gem/ruby/2.4.1/gems/builder-3.2.2/lib/builder/xchar.rb:111: warning: constant ::Fixnum is deprecated 0 Hard disk 1 nosnapshots_ds4 64.00 GiB real 0m2.929s user 0m1.038s sys 0m0.236s ``` This'll have to be retrofitted to the other commands, which we can do over time. See https://github.com/vmware/rbvmomi/issues/117 for some more context * Use the new finding on property set * Add this to vm state, too * Add same thing to vm config * Add vm lookup to snapshot --- lib/chef/knife/search_helper.rb | 41 +++++++++++++++++++++++ lib/chef/knife/vsphere_vm_config.rb | 9 ++--- lib/chef/knife/vsphere_vm_disk_list.rb | 7 ++-- lib/chef/knife/vsphere_vm_property_set.rb | 7 ++-- lib/chef/knife/vsphere_vm_snapshot.rb | 15 +++------ lib/chef/knife/vsphere_vm_state.rb | 17 ++-------- 6 files changed, 58 insertions(+), 38 deletions(-) create mode 100644 lib/chef/knife/search_helper.rb diff --git a/lib/chef/knife/search_helper.rb b/lib/chef/knife/search_helper.rb new file mode 100644 index 00000000..970fed21 --- /dev/null +++ b/lib/chef/knife/search_helper.rb @@ -0,0 +1,41 @@ +# Some helpers for faster searching of the inventory +module SearchHelper + # Retrieves all the VM objects and returns their ObjectContents + # Note that since it's a ObjectContent coming back, the individual + # object's [] will return only the properties you asked for + # and `#obj` will return the actual object (but make a call to the server) + # param [Array] properties to retrieve + # @return [Array] + def get_all_vm_objects(properties = ['name']) + pc = vim_connection.serviceInstance.content.propertyCollector + viewmgr = vim_connection.serviceInstance.content.viewManager + root_folder = vim_connection.serviceInstance.content.rootFolder + vmview = viewmgr.CreateContainerView(container: root_folder, + type: ['VirtualMachine'], + recursive: true) + + filter_spec = RbVmomi::VIM.PropertyFilterSpec( + objectSet: [ + obj: vmview, + skip: true, + selectSet: [ + RbVmomi::VIM.TraversalSpec( + name: 'traverseEntities', + type: 'ContainerView', + path: 'view', + skip: false + ) + ] + ], + propSet: [ + { type: 'VirtualMachine', pathSet: properties } + ] + ) + pc.RetrieveProperties(specSet: [filter_spec]) + end + + def get_vm_by_name(vmname) + vm = get_all_vm_objects.detect { |r| r['name'] == vmname } + vm ? vm.obj : nil + end +end diff --git a/lib/chef/knife/vsphere_vm_config.rb b/lib/chef/knife/vsphere_vm_config.rb index 981e4edc..7cbae964 100644 --- a/lib/chef/knife/vsphere_vm_config.rb +++ b/lib/chef/knife/vsphere_vm_config.rb @@ -3,11 +3,11 @@ require 'chef/knife' require 'chef/knife/base_vsphere_command' -require 'rbvmomi' -require 'netaddr' +require 'chef/knife/search_helper' # VsphereVMconfig extends the BaseVspherecommand class Chef::Knife::VsphereVmConfig < Chef::Knife::BaseVsphereCommand + include SearchHelper banner "knife vsphere vm config VMNAME PROPERTY VALUE. See \"http://pubs.vmware.com/vi3/sdk/ReferenceGuide/vim.vm.ConfigSpec.html\" for allowed ATTRIBUTE values (any property of type xs:string is supported)." @@ -39,10 +39,7 @@ def run vim_connection - dc = datacenter - folder = find_folder(get_config(:folder)) || dc.vmFolder - - vm = traverse_folders_for_vm(folder, vmname) || abort("VM #{vmname} not found") + vm = get_vm_by_name(vmname) || fatal_exit("Could not find #{vmname}") properties = {} properties[property_name] = property_value diff --git a/lib/chef/knife/vsphere_vm_disk_list.rb b/lib/chef/knife/vsphere_vm_disk_list.rb index e03b456f..312ad5be 100644 --- a/lib/chef/knife/vsphere_vm_disk_list.rb +++ b/lib/chef/knife/vsphere_vm_disk_list.rb @@ -1,9 +1,12 @@ require 'chef/knife' require 'chef/knife/base_vsphere_command' +require 'chef/knife/search_helper' # List the disks attached to a VM # VsphereVmdisklist extends the BaseVspherecommand class Chef::Knife::VsphereVmDiskList < Chef::Knife::BaseVsphereCommand + include SearchHelper + banner 'knife vsphere vm disk list VMNAME' common_options @@ -18,9 +21,7 @@ def run fatal_exit 'You must specify a virtual machine name' end - vim_connection - vm = get_vm(vmname) - fatal_exit "Could not find #{vmname}" unless vm + vm = get_vm_by_name(vmname) || fatal_exit("Could not find #{vmname}") disks = vm.config.hardware.device.select do |device| device.is_a? RbVmomi::VIM::VirtualDisk diff --git a/lib/chef/knife/vsphere_vm_property_set.rb b/lib/chef/knife/vsphere_vm_property_set.rb index 56fd63c1..7baf97a5 100644 --- a/lib/chef/knife/vsphere_vm_property_set.rb +++ b/lib/chef/knife/vsphere_vm_property_set.rb @@ -3,11 +3,11 @@ require 'chef/knife' require 'chef/knife/base_vsphere_command' -require 'rbvmomi' -require 'netaddr' +require 'chef/knife/search_helper' # VsphereVMPropertySet extends Basevspherecommand class Chef::Knife::VsphereVmPropertySet < Chef::Knife::BaseVsphereCommand + include SearchHelper banner 'knife vsphere vm property set VMNAME PROPERTY VALUE. Sets a vApp Property on VMNAME.' common_options @@ -42,9 +42,8 @@ def run vim_connection dc = datacenter - folder = find_folder(get_config(:folder)) || dc.vmFolder - vm = find_in_folder(folder, RbVmomi::VIM::VirtualMachine, vmname) || abort("VM #{vmname} not found") + vm = get_vm_by_name(vmname) || fatal_exit("Could not find #{vmname}") if vm.config.vAppConfig && vm.config.vAppConfig.property existing_property = vm.config.vAppConfig.property.find { |p| p.props[:id] == property_name.to_s } diff --git a/lib/chef/knife/vsphere_vm_snapshot.rb b/lib/chef/knife/vsphere_vm_snapshot.rb index 97a37fd8..115980f5 100644 --- a/lib/chef/knife/vsphere_vm_snapshot.rb +++ b/lib/chef/knife/vsphere_vm_snapshot.rb @@ -4,11 +4,11 @@ require 'chef/knife' require 'chef/knife/base_vsphere_command' -require 'rbvmomi' -require 'netaddr' +require 'chef/knife/search_helper' # Manage snapshots of a virtual machine class Chef::Knife::VsphereVmSnapshot < Chef::Knife::BaseVsphereCommand + include SearchHelper banner 'knife vsphere vm snapshot VMNAME (options)' common_options @@ -44,7 +44,7 @@ class Chef::Knife::VsphereVmSnapshot < Chef::Knife::BaseVsphereCommand description: 'Indicates whether to wait for creation/removal to complete', boolean: false - option :find, + option :find, # imma deprecate this long: '--find', description: 'Finds the virtual machine by searching all folders' @@ -77,14 +77,7 @@ def run vim_connection - vm = if get_config(:find) - puts "No folder entered, searching for #{vmname}" - src_folder = find_folder(get_config(:folder)) - traverse_folders_for_vm(src_folder, vmname) - else - base_folder = find_folder get_config(:folder) - find_in_folder(base_folder, RbVmomi::VIM::VirtualMachine, vmname) || abort("VM #{vmname} not found") - end + vm = get_vm_by_name(vmname) || fatal_exit("Could not find #{vmname}") if vm.snapshot snapshot_list = vm.snapshot.rootSnapshotList diff --git a/lib/chef/knife/vsphere_vm_state.rb b/lib/chef/knife/vsphere_vm_state.rb index e39d602e..52d52fb6 100644 --- a/lib/chef/knife/vsphere_vm_state.rb +++ b/lib/chef/knife/vsphere_vm_state.rb @@ -5,12 +5,12 @@ require 'chef/knife' require 'chef/knife/base_vsphere_command' -require 'rbvmomi' -require 'netaddr' +require 'chef/knife/search_helper' # Manage power state of a virtual machine # VsphereVmState extends the BaseVspherecommand class Chef::Knife::VsphereVmState < Chef::Knife::BaseVsphereCommand + include SearchHelper # The Different power states that vSphere reports POWER_STATES = { PS_ON => 'powered on', @@ -56,18 +56,7 @@ def run vim_connection - if get_config(:recursive) - vms = get_vms(vmname) - if vms.length > 1 - abort "More than one VM with name #{vmname} found:\n" + vms.map { |vm| get_path_to_object(vm) }.join("\n") - end - abort "VM #{vmname} not found" if vms.length == 0 - vm = vms[0] - else - base_folder = find_folder(get_config(:folder)) - - vm = find_in_folder(base_folder, RbVmomi::VIM::VirtualMachine, vmname) || abort("VM #{vmname} not found") - end + vm = get_vm_by_name(vmname) || fatal_exit("Could not find #{vmname}") state = vm.runtime.powerState