vsphere/internal/virtualdevice/virtual_machine_disk_subresource.go
func DiskMigrateRelocateOperation(d *schema.ResourceData, c *govmomi.Client, l object.VirtualDeviceList) ([]types.VirtualMachineRelocateSpecDiskLocator, bool, error) {
	log.Printf("[DEBUG] DiskMigrateRelocateOperation: Generating any necessary disk relocate specs")
	ods, nds := d.GetChange(subresourceTypeDisk)

	var relocators []types.VirtualMachineRelocateSpecDiskLocator
	var relocateOK bool

	// We are only concerned with resources that would normally be updated, as
	// incoming or outgoing disks obviously won't need migrating. Hence, this is
	// a simplified subset of the normal apply logic.
	for ni, ne := range nds.([]interface{}) {
		nm := ne.(map[string]interface{})
		var name string
		var err error
		if name, err = diskLabelOrName(nm); err != nil {
			return nil, false, fmt.Errorf("disk.%d: %s", ni, err)
		}
		if name == diskDeletedName || name == diskDetachedName {
			continue
		}
		for _, oe := range ods.([]interface{}) {
			om := oe.(map[string]interface{})
			if nm["uuid"] == om["uuid"] {
				// No change in datastore is a no-op, unless we are changing default datastores
				if nm["datastore_id"] == om["datastore_id"] && !d.HasChange("datastore_id") {
					break
				}
				// If we got this far, some sort of datastore migration will be
				// necessary. Flag this now.
				relocateOK = true

				// A disk locator is only useful if a target datastore is available. If we
				// don't have a datastore specified (ie: when Storage DRS is in use), then
				// we just need to skip this disk. The disk will be migrated properly
				// through the SDRS API.
				if nm["datastore_id"] == "" || nm["datastore_id"] == diskDatastoreComputedName {
					break
				}
				r := NewDiskSubresource(c, d, nm, om, ni)
				relocator, err := r.Relocate(l, false)
				if err != nil {
					return nil, false, fmt.Errorf("%s: %s", r.Addr(), err)
				}
				if d.Get("datastore_id").(string) == relocator.Datastore.Value {
					log.Printf("[DEBUG] %s: Datastore in spec is same as default, dropping in favor of implicit relocation", r.Addr())
					break
				}
				relocators = append(relocators, relocator)
			}
		}
	}

	if !relocateOK {
		log.Printf("[DEBUG] DiskMigrateRelocateOperation: Disk relocation not necessary")
		return nil, false, nil
	}

	log.Printf("[DEBUG] DiskMigrateRelocateOperation: Disk relocator list: %s", diskRelocateListString(relocators))
	log.Printf("[DEBUG] DiskMigrateRelocateOperation: Disk relocator generation complete")
	return relocators, true, nil
}
func DiskMigrateRelocateOperation(d *schema.ResourceData, c *govmomi.Client, l object.VirtualDeviceList) ([]types.VirtualMachineRelocateSpecDiskLocator, bool, error) {
	log.Printf("[DEBUG] DiskMigrateRelocateOperation: Generating any necessary disk relocate specs")
	ods, nds := d.GetChange(subresourceTypeDisk)

	var relocators []types.VirtualMachineRelocateSpecDiskLocator
	var relocateOK bool

	// We are only concerned with resources that would normally be updated, as
	// incoming or outgoing disks obviously won't need migrating. Hence, this is
	// a simplified subset of the normal apply logic.
	for ni, ne := range nds.([]interface{}) {
		nm := ne.(map[string]interface{})
		var name string
		var err error
		if name, err = diskLabelOrName(nm); err != nil {
			return nil, false, fmt.Errorf("disk.%d: %s", ni, err)
		}
		if name == diskDeletedName || name == diskDetachedName {
			continue
		}
		for _, oe := range ods.([]interface{}) {
			om := oe.(map[string]interface{})
			if nm["uuid"] == om["uuid"] {
				// No change in datastore is a no-op, unless we are changing default datastores
				if nm["datastore_id"] == om["datastore_id"] && !d.HasChange("datastore_id") {
					break
				}
				// If we got this far, some sort of datastore migration will be
				// necessary. Flag this now.
				relocateOK = true

				// A disk locator is only useful if a target datastore is available. If we
				// don't have a datastore specified (ie: when Storage DRS is in use), then
				// we just need to skip this disk. The disk will be migrated properly
				// through the SDRS API.
				if nm["datastore_id"] == "" || nm["datastore_id"] == diskDatastoreComputedName {
					break
				}
				r := NewDiskSubresource(c, d, nm, om, ni)
				relocator, err := r.Relocate(l, false)
				if err != nil {
					return nil, false, fmt.Errorf("%s: %s", r.Addr(), err)
				}
				if d.Get("datastore_id").(string) == relocator.Datastore.Value {
					log.Printf("[DEBUG] %s: Datastore in spec is same as default, dropping in favor of implicit relocation", r.Addr())
					break
				}
				relocators = append(relocators, relocator)
			}
		}
	}

	if !relocateOK {
		log.Printf("[DEBUG] DiskMigrateRelocateOperation: Disk relocation not necessary")
		return nil, false, nil
	}

	log.Printf("[DEBUG] DiskMigrateRelocateOperation: Disk relocator list: %s", diskRelocateListString(relocators))
	log.Printf("[DEBUG] DiskMigrateRelocateOperation: Disk relocator generation complete")
	return relocators, true, nil
}
// DiskMigrateRelocateOperation assembles the
// VirtualMachineRelocateSpecDiskLocator slice for a virtual machine migration
// operation, otherwise known as storage vMotion.