From 18c73ab02135fdead2aa5a0b526e6b902979d4ab Mon Sep 17 00:00:00 2001 From: Chris Marchesi Date: Mon, 29 Jan 2018 15:44:03 -0800 Subject: [PATCH] r/virtual_machine: Fix template UUID lookup for older vSphere versions Historically, the FindByUuid method under SearchIndex did not support looking up VMs marked as template. However, at some point in time, this was changed (possibly vSphere 6.5, based on bug reports), and any mention of the UUID search not supporting templates was removed from the API documentation, without a mention of when support for templates was introduced. Since this is the method that we use to look up both VMs and templates, this means that templates have not been functional for vSphere <= 6.0 users since the resource was re-written in 1.0. There has been a workaround in that the source template is just converted back to virtual machine, but this update fixes it so that on versions of vSphere older than 6.5, we fall back to using ContainerView with a filter on the UUID. This works for all versions of vSphere. We keep FindByUuid for 6.5 as it may possibly be more direct path. --- .../virtualmachine/virtual_machine_helper.go | 87 +++++++++++++++++-- 1 file changed, 79 insertions(+), 8 deletions(-) diff --git a/vsphere/internal/helper/virtualmachine/virtual_machine_helper.go b/vsphere/internal/helper/virtualmachine/virtual_machine_helper.go index 9871a2e53..b04284b85 100644 --- a/vsphere/internal/helper/virtualmachine/virtual_machine_helper.go +++ b/vsphere/internal/helper/virtualmachine/virtual_machine_helper.go @@ -11,16 +11,26 @@ import ( "github.com/terraform-providers/terraform-provider-vsphere/vsphere/internal/helper/folder" "github.com/terraform-providers/terraform-provider-vsphere/vsphere/internal/helper/provider" "github.com/terraform-providers/terraform-provider-vsphere/vsphere/internal/helper/structure" + "github.com/terraform-providers/terraform-provider-vsphere/vsphere/internal/helper/viapi" "github.com/vmware/govmomi" "github.com/vmware/govmomi/find" "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/property" + "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" ) var errGuestShutdownTimeout = errors.New("the VM did not power off within the specified amount of time") +// vmUUIDSearchIndexVersion denotes the minimum version we use the SearchIndex +// VM UUID search for. All versions lower than this use ContainerView to find +// the VM. +var vmUUIDSearchUseContainerVersion = viapi.VSphereVersion{ + Major: 6, + Minor: 5, +} + // UUIDNotFoundError is an error type that is returned when a // virtual machine could not be found by UUID. type UUIDNotFoundError struct { @@ -43,17 +53,20 @@ func newUUIDNotFoundError(s string) *UUIDNotFoundError { // FromUUID locates a virtualMachine by its UUID. func FromUUID(client *govmomi.Client, uuid string) (*object.VirtualMachine, error) { log.Printf("[DEBUG] Locating virtual machine with UUID %q", uuid) - search := object.NewSearchIndex(client.Client) - ctx, cancel := context.WithTimeout(context.Background(), provider.DefaultAPITimeout) - defer cancel() - result, err := search.FindByUuid(ctx, nil, uuid, true, structure.BoolPtr(false)) - if err != nil { - return nil, err + var result object.Reference + var err error + version := viapi.ParseVersionFromClient(client) + expected := vmUUIDSearchUseContainerVersion + expected.Product = version.Product + if version.Older(expected) { + result, err = virtualMachineFromContainerView(client, uuid) + } else { + result, err = virtualMachineFromSearchIndex(client, uuid) } - if result == nil { - return nil, newUUIDNotFoundError(fmt.Sprintf("virtual machine with UUID %q not found", uuid)) + if err != nil { + return nil, err } // We need to filter our object through finder to ensure that the @@ -75,6 +88,64 @@ func FromUUID(client *govmomi.Client, uuid string) (*object.VirtualMachine, erro return vm.(*object.VirtualMachine), nil } +// virtualMachineFromSearchIndex gets the virtual machine reference via the +// SearchIndex MO and is the method used to fetch UUIDs on newer versions of +// vSphere. +func virtualMachineFromSearchIndex(client *govmomi.Client, uuid string) (object.Reference, error) { + log.Printf("[DEBUG] Using SearchIndex to look up UUID %q", uuid) + search := object.NewSearchIndex(client.Client) + ctx, cancel := context.WithTimeout(context.Background(), provider.DefaultAPITimeout) + defer cancel() + result, err := search.FindByUuid(ctx, nil, uuid, true, structure.BoolPtr(false)) + if err != nil { + return nil, err + } + + if result == nil { + return nil, newUUIDNotFoundError(fmt.Sprintf("virtual machine with UUID %q not found", uuid)) + } + + return result, nil +} + +// virtualMachineFromContainerView is a compatability method that is +// used when the version of vSphere is too old to support using SearchIndex's +// FindByUuid method correctly. This is mainly to facilitate the ability to use +// FromUUID to find both templates in addition to virtual machines, which +// historically was not supported by FindByUuid. +func virtualMachineFromContainerView(client *govmomi.Client, uuid string) (object.Reference, error) { + log.Printf("[DEBUG] Using ContainerView to look up UUID %q", uuid) + m := view.NewManager(client.Client) + + vctx, vcancel := context.WithTimeout(context.Background(), provider.DefaultAPITimeout) + defer vcancel() + v, err := m.CreateContainerView(vctx, client.ServiceContent.RootFolder, []string{"VirtualMachine"}, true) + if err != nil { + return nil, err + } + + defer func() { + dctx, dcancel := context.WithTimeout(context.Background(), provider.DefaultAPITimeout) + defer dcancel() + v.Destroy(dctx) + }() + + var vms []mo.VirtualMachine + err = v.RetrieveWithFilter(vctx, []string{"VirtualMachine"}, []string{"summary"}, &vms, property.Filter{"config.uuid": uuid}) + if err != nil { + return nil, err + } + + switch { + case len(vms) < 1: + return nil, newUUIDNotFoundError(fmt.Sprintf("virtual machine with UUID %q not found", uuid)) + case len(vms) > 1: + return nil, fmt.Errorf("multiple virtual machines with UUID %q found", uuid) + } + + return object.NewReference(client.Client, vms[0].Self), nil +} + // FromMOID locates a virtualMachine by its managed // object reference ID. func FromMOID(client *govmomi.Client, id string) (*object.VirtualMachine, error) {