vmware-archive / rbvmomi

Ruby interface to the VMware vSphere API.
MIT License
302 stars 175 forks source link

Can't find a VM in a folder #117

Open swalberg opened 7 years ago

swalberg commented 7 years ago

I'm trying to use the index to find a VM by its name in vsphere (as opposed to anything reported by the agent) rather than the current method of recursively finding all VMs and comparing the name.

dc.vmFolder.find('foo') seems to work as long as the object is in the root and not a folder. However the other find methods don't seem to care.

(byebug) dc.vmFolder.findByIp('x.x.x.x').name
"myvm"
(byebug) dc.vmFolder.find('myvm')
nil

This object is in a Discovered virtual machine folder.

This is similar to #46. Doesn't seem to matter if I call dc.find_vm 'foo' or dc.vmFolder.find('foo').

jrgarcia commented 7 years ago

It looks like find makes a call to FindChild method on the SearchIndex managed object in the SOAP API. The FindChild method states that it

Finds a particular child based on a managed entity name. This only searches the immediate children of a managed entity.

If you're expecting find to be recursive (which honestly, I would have expected too), it's not. I'm not sure about find_vm, I'll have to look into that. At any rate, if these are intentionally not recursive or not easily changed to be, I'll look at adding methods that do support recursion because that's definitely something that's needed. Thanks for bring this to my attention!

swalberg commented 7 years ago

Hm, yea that is odd. Are there other APIs I could be missing? Such as to resolve a name to a UUID then do the recursive search on UUID?

sdorsett commented 7 years ago

this has been an issue for a while. I've seen other users of rbvmomi use helper methods like this: https://github.com/MarkArbogast/vsphere-helper/blob/master/lib/vsphere_helper/helpers.rb#L51-#L68 ...which would allow you do this:

credentials = { :host => "192.168.1.10", :user => "root", :password => "vmware", :insecure => true }
vim = RbVmomi::VIM.connect(credentials)
datacenter = vim.rootFolder.childEntity.first

recursive_find_vm(datacenter.vmFolder, "my_vm_name")
jrgarcia commented 7 years ago

@swalberg I will definitely be adding this functionality, but it probably won't be this week. If you need this functionality really soon, I'd suggest doing what @sdorsett mentioned ☝️ for now. At least until I can add this functionality. It's a bummer that this is the case, but it's the best I can offer at the moment.

sdorsett commented 7 years ago

the other way I've seen this done is to use a view to perform the search since it has a recursive flag:

[32] pry(main)> search_name = 'centos-7.3';
[33] pry(main)> vim.serviceContent.viewManager.CreateContainerView({ container: vim.rootFolder, type: ['VirtualMachine'], recursive: true }).view.select {|vm| vm.name == search_name }

=> [VirtualMachine("vm-870")]
[34] pry(main)>
swalberg commented 7 years ago

@sdorsett yea we're doing something close to that right now and the problem is that it's really slow. Can take a minute or more in one of our DCs vs seconds when we use the index.

I will look at that search that @sdorsett just mentioned...

jrgarcia commented 7 years ago

@swalberg Thanks for adding that that is slow. I'll make sure to consider that when evaluating possible solutions to this.

swalberg commented 7 years ago

Just did a quick test of the ContainerView and it's not any faster than what we have now. I think the problem is that the view returns all the objects for Ruby to search through and the find* do the filtering on the server.

sdorsett commented 7 years ago

I talked to a coworker that has done some tweaking of our rbvmomi based auditing scripts and he recommended maybe trying something like this to filter what is being returned with the vm objects:

require 'rbvmomi';
credentials = { :host => "192.168.1.10", :user => "administrator@vsphere.local", :password => "vmware", :insecure => true };
@vim = RbVmomi::VIM.connect(credentials);
pc = @vim.serviceInstance.content.propertyCollector;
viewmgr = @vim.serviceInstance.content.viewManager;
rootFolder = @vim.serviceInstance.content.rootFolder;
vmview = viewmgr.CreateContainerView({:container => rootFolder, :type => ['VirtualMachine'], :recursive => true});  
filterSpec = RbVmomi::VIM.PropertyFilterSpec(
            :objectSet => [
                :obj => vmview,
                :skip => true,
                :selectSet => [
                    RbVmomi::VIM.TraversalSpec(
                        :name => "traverseEntities",
                        :type => "ContainerView",
                        :path => "view",
                        :skip => false 
                    )]
            ], 
            :propSet => [
                { :type => 'VirtualMachine', :pathSet => ['name']}
            ]
        );
result = pc.RetrieveProperties(:specSet => [filterSpec])
sdorsett commented 7 years ago

if you look at the returned vm object it will only contain a 'name' property:

[11] pry(main)> result[0]
=> ObjectContent( missingSet: [], obj: VirtualMachine("vm-529"), propSet: [DynamicProperty( name: "name", val: "template-centos-68" )] )

...but you can still call any typical methods through .obj of this object:

[15] pry(main)> result[0].obj
=> VirtualMachine("vm-529")
[16] pry(main)> ls result[0].obj
RbVmomi::BasicTypes::ManagedObject#methods: ==  []  _call  _connection  _get_property  _ref  _set_property  collect  collect!  eql?  hash  pretty_print  to_s  wait_until
RbVmomi::VIM::ExtensibleManagedObject#methods: availableField  availableField=  setCustomValue  setCustomValue!  value  value=
RbVmomi::VIM::ManagedEntity#methods:
  Destroy_Task   Reload!       alarmActionsEnabled   configIssue=   customValue         declaredAlarmState=  effectiveRole   name=           parent   permission   recentTask   tag=
  Destroy_Task!  Rename_Task   alarmActionsEnabled=  configStatus   customValue=        disabledMethod       effectiveRole=  overallStatus   parent=  permission=  recentTask=  triggeredAlarmState
  Reload         Rename_Task!  configIssue           configStatus=  declaredAlarmState  disabledMethod=      name            overallStatus=  path     pretty_path  tag          triggeredAlarmState=
RbVmomi::VIM::VirtualMachine#methods:
  AcquireMksTicket           EnableSecondaryVM_Task                        QueryUnownedFiles              StopRecording_Task                 guestHeartbeatStatus
  AcquireMksTicket!          EnableSecondaryVM_Task!                       QueryUnownedFiles!             StopRecording_Task!                guestHeartbeatStatus=
  AcquireTicket              EstimateStorageForConsolidateSnapshots_Task   RebootGuest                    StopReplaying_Task                 guest_ip
  AcquireTicket!             EstimateStorageForConsolidateSnapshots_Task!  RebootGuest!                   StopReplaying_Task!                layout
  AnswerVM                   ExportVm                                      ReconfigVM_Task                SuspendVM_Task                     layout=
  AnswerVM!                  ExportVm!                                     ReconfigVM_Task!               SuspendVM_Task!                    layoutEx
  AttachDisk_Task            ExtractOvfEnvironment                         RefreshStorageInfo             TerminateFaultTolerantVM_Task      layoutEx=
  AttachDisk_Task!           ExtractOvfEnvironment!                        RefreshStorageInfo!            TerminateFaultTolerantVM_Task!     macs
  CheckCustomizationSpec     MakePrimaryVM_Task                            RelocateVM_Task                TerminateVM                        network
  CheckCustomizationSpec!    MakePrimaryVM_Task!                           RelocateVM_Task!               TerminateVM!                       network=
  CloneVM_Task               MarkAsTemplate                                RemoveAllSnapshots_Task        TurnOffFaultToleranceForVM_Task    parentVApp
  CloneVM_Task!              MarkAsTemplate!                               RemoveAllSnapshots_Task!       TurnOffFaultToleranceForVM_Task!   parentVApp=
  ConsolidateVMDisks_Task    MarkAsVirtualMachine                          ResetGuestInformation          UnmountToolsInstaller              reloadVirtualMachineFromPath_Task
  ConsolidateVMDisks_Task!   MarkAsVirtualMachine!                         ResetGuestInformation!         UnmountToolsInstaller!             reloadVirtualMachineFromPath_Task!
  CreateScreenshot_Task      MigrateVM_Task                                ResetVM_Task                   UnregisterVM                       resourceConfig
  CreateScreenshot_Task!     MigrateVM_Task!                               ResetVM_Task!                  UnregisterVM!                      resourceConfig=
  CreateSecondaryVMEx_Task   MountToolsInstaller                           RevertToCurrentSnapshot_Task   UpgradeTools_Task                  resourcePool
  CreateSecondaryVMEx_Task!  MountToolsInstaller!                          RevertToCurrentSnapshot_Task!  UpgradeTools_Task!                 resourcePool=
  CreateSecondaryVM_Task     PowerOffVM_Task                               SendNMI                        UpgradeVM_Task                     rootSnapshot
  CreateSecondaryVM_Task!    PowerOffVM_Task!                              SendNMI!                       UpgradeVM_Task!                    rootSnapshot=
  CreateSnapshotEx_Task      PowerOnVM_Task                                SetDisplayTopology             add_delta_disk_layer_on_all_disks  runtime
  CreateSnapshotEx_Task!     PowerOnVM_Task!                               SetDisplayTopology!            capability                         runtime=
  CreateSnapshot_Task        PromoteDisks_Task                             SetScreenResolution            capability=                        snapshot
  CreateSnapshot_Task!       PromoteDisks_Task!                            SetScreenResolution!           config                             snapshot=
  CustomizeVM_Task           PutUsbScanCodes                               ShutdownGuest                  config=                            storage
  CustomizeVM_Task!          PutUsbScanCodes!                              ShutdownGuest!                 datastore                          storage=
  DefragmentAllDisks         QueryChangedDiskAreas                         StandbyGuest                   datastore=                         summary
  DefragmentAllDisks!        QueryChangedDiskAreas!                        StandbyGuest!                  disks                              summary=
  DetachDisk_Task            QueryFaultToleranceCompatibility              StartRecording_Task            environmentBrowser                 update_spec_add_delta_disk_layer_on_all_disks
  DetachDisk_Task!           QueryFaultToleranceCompatibility!             StartRecording_Task!           environmentBrowser=
  DisableSecondaryVM_Task    QueryFaultToleranceCompatibilityEx            StartReplaying_Task            guest
  DisableSecondaryVM_Task!   QueryFaultToleranceCompatibilityEx!           StartReplaying_Task!           guest=
instance variables: @connection  @ref  @soap
[17] pry(main)>
swalberg commented 7 years ago

Sorry I dropped the ball on this one... Playing around with it now and filtering the properties doesn't seem to help at all.

Some co-workers who use powershell say that get-vm -name myvm is really fast, I wonder how they do it?

jrgarcia commented 7 years ago

Thanks for the update. I'll see if I can find someone on the PowerCLI team that may have a few pointers on possible issues.

solohan83 commented 6 years ago

Hi,

Do we know if we plan to have a fix for this?

I have tried to use the helper, but it is not returning the VM I am looking for.

swalberg commented 6 years ago

So far I seem to have something based on @sdorsett's work.

    pc = @vim.serviceInstance.content.propertyCollector
    viewmgr = @vim.serviceInstance.content.viewManager
    rootFolder = @vim.serviceInstance.content.rootFolder
    vmview = viewmgr.CreateContainerView(container: rootFolder,
                                         type: ['VirtualMachine'],
                                         recursive: true)

    filterSpec = RbVmomi::VIM.PropertyFilterSpec(
      objectSet: [
        obj: vmview,
        skip: true,
        selectSet: [
          RbVmomi::VIM.TraversalSpec(
            name: "traverseEntities",
            type: "ContainerView",
            path: "view",
            skip: false 
          )]
      ],
      propSet: [
        { type: 'VirtualMachine', pathSet: ['name']}
      ]
    )
    result = pc.RetrieveProperties(specSet: [filterSpec])
    vm = result.find { |r| r.propSet.find { |p| p.name == 'name'}.val == vmname }
    vm ? vm.obj : nil
  end

The key is not to look at result.obj until the very end -- it causes a load from the server. You want to look at the propSet for the name.

nimDevOps commented 6 years ago

Just want to share my result with latest rbvmomi 1.12.0, the issue is still there, vm template in folder cannot be found. I use vcenter kitchen driver and the only way to work is to place vm template in the root folder which is not allowed to have in most production vmware vcenter farms. If there are any plans to fix this issue?

jrgarcia commented 6 years ago

I'll look into this again and see if I can figure out what's going on.

swalberg commented 6 years ago

@nimDevOps if you downgrade to the older chef-provisioning-vsphere, it supports templates and VMs in subfolders for the kitchen driver.

nimDevOps commented 6 years ago

@jrgarcia Thanks! I will wait for your suggestions/findings.

nimDevOps commented 6 years ago

Do you have any updates on this ?