vsphere/internal/virtualdevice/virtual_machine_disk_subresource.go
func DiskPostCloneOperation(d *schema.ResourceData, c *govmomi.Client, l object.VirtualDeviceList) (object.VirtualDeviceList, []types.BaseVirtualDeviceConfigSpec, error) {
	log.Printf("[DEBUG] DiskPostCloneOperation: Looking for disk device changes post-clone")
	devices := SelectDisks(l, d.Get("scsi_controller_count").(int))
	log.Printf("[DEBUG] DiskPostCloneOperation: Disk devices located: %s", DeviceListString(devices))
	// Sort the device list, in case it's not sorted already.
	devSort := virtualDeviceListSorter{
		Sort:       devices,
		DeviceList: l,
	}
	sort.Sort(devSort)
	devices = devSort.Sort
	log.Printf("[DEBUG] DiskPostCloneOperation: Disk devices order after sort: %s", DeviceListString(devices))
	// Do the same for our listed disks.
	curSet := d.Get(subresourceTypeDisk).([]interface{})
	log.Printf("[DEBUG] DiskPostCloneOperation: Current resource set: %s", subresourceListString(curSet))
	sort.Sort(virtualDiskSubresourceSorter(curSet))
	log.Printf("[DEBUG] DiskPostCloneOperation: Resource set order after sort: %s", subresourceListString(curSet))

	var spec []types.BaseVirtualDeviceConfigSpec
	var updates []interface{}

	log.Printf("[DEBUG] DiskPostCloneOperation: Looking for and applying device changes in source disks")
	for i, device := range devices {
		src := curSet[i].(map[string]interface{})
		vd := device.GetVirtualDevice()
		ctlr := l.FindByKey(vd.ControllerKey)
		if ctlr == nil {
			return nil, nil, fmt.Errorf("could not find controller with key %d", vd.Key)
		}
		src["key"] = int(vd.Key)
		var err error
		src["device_address"], err = computeDevAddr(vd, ctlr.(types.BaseVirtualController))
		if err != nil {
			return nil, nil, fmt.Errorf("error computing device address: %s", err)
		}
		// Copy the source set into old. This allows us to patch a copy of the
		// product of this set with the source, creating a diff.
		old, err := copystructure.Copy(src)
		if err != nil {
			return nil, nil, fmt.Errorf("error copying source set for disk at unit_number %d: %s", src["unit_number"].(int), err)
		}
		rOld := NewDiskSubresource(c, d, old.(map[string]interface{}), nil, i)
		if err := rOld.Read(l); err != nil {
			return nil, nil, fmt.Errorf("%s: %s", rOld.Addr(), err)
		}
		new, err := copystructure.Copy(rOld.Data())
		if err != nil {
			return nil, nil, fmt.Errorf("error copying current device state for disk at unit_number %d: %s", src["unit_number"].(int), err)
		}
		for k, v := range src {
			// Skip label, path (path will always be computed here as cloned disks
			// are not being attached externally), name, datastore_id, and uuid. Also
			// skip share_count if we the share level isn't custom.
			//
			// TODO: Remove "name" after 2.0.
			switch k {
			case "label", "path", "name", "datastore_id", "uuid":
				continue
			case "io_share_count":
				if src["io_share_level"] != string(types.SharesLevelCustom) {
					continue
				}
			}
			new.(map[string]interface{})[k] = v
		}
		rNew := NewDiskSubresource(c, d, new.(map[string]interface{}), rOld.Data(), i)
		if !reflect.DeepEqual(rNew.Data(), rOld.Data()) {
			uspec, err := rNew.Update(l)
			if err != nil {
				return nil, nil, fmt.Errorf("%s: %s", rNew.Addr(), err)
			}
			l = applyDeviceChange(l, uspec)
			spec = append(spec, uspec...)
		}
		updates = append(updates, rNew.Data())
	}

	// Any disk past the current device list is a new device. Create those now.
	for _, ni := range curSet[len(devices):] {
		r := NewDiskSubresource(c, d, ni.(map[string]interface{}), nil, len(updates))
		cspec, err := r.Create(l)
		if err != nil {
			return nil, nil, fmt.Errorf("%s: %s", r.Addr(), err)
		}
		l = applyDeviceChange(l, cspec)
		spec = append(spec, cspec...)
		updates = append(updates, r.Data())
	}

	log.Printf("[DEBUG] DiskPostCloneOperation: Post-clone final resource list: %s", subresourceListString(updates))
	if err := d.Set(subresourceTypeDisk, updates); err != nil {
		return nil, nil, err
	}
	log.Printf("[DEBUG] DiskPostCloneOperation: Device list at end of operation: %s", DeviceListString(l))
	log.Printf("[DEBUG] DiskPostCloneOperation: Device config operations from post-clone: %s", DeviceChangeString(spec))
	log.Printf("[DEBUG] DiskPostCloneOperation: Operation complete, returning updated spec")
	return l, spec, nil
}
func DiskPostCloneOperation(d *schema.ResourceData, c *govmomi.Client, l object.VirtualDeviceList) (object.VirtualDeviceList, []types.BaseVirtualDeviceConfigSpec, error) {
	log.Printf("[DEBUG] DiskPostCloneOperation: Looking for disk device changes post-clone")
	devices := SelectDisks(l, d.Get("scsi_controller_count").(int))
	log.Printf("[DEBUG] DiskPostCloneOperation: Disk devices located: %s", DeviceListString(devices))
	// Sort the device list, in case it's not sorted already.
	devSort := virtualDeviceListSorter{
		Sort:       devices,
		DeviceList: l,
	}
	sort.Sort(devSort)
	devices = devSort.Sort
	log.Printf("[DEBUG] DiskPostCloneOperation: Disk devices order after sort: %s", DeviceListString(devices))
	// Do the same for our listed disks.
	curSet := d.Get(subresourceTypeDisk).([]interface{})
	log.Printf("[DEBUG] DiskPostCloneOperation: Current resource set: %s", subresourceListString(curSet))
	sort.Sort(virtualDiskSubresourceSorter(curSet))
	log.Printf("[DEBUG] DiskPostCloneOperation: Resource set order after sort: %s", subresourceListString(curSet))

	var spec []types.BaseVirtualDeviceConfigSpec
	var updates []interface{}

	log.Printf("[DEBUG] DiskPostCloneOperation: Looking for and applying device changes in source disks")
	for i, device := range devices {
		src := curSet[i].(map[string]interface{})
		vd := device.GetVirtualDevice()
		ctlr := l.FindByKey(vd.ControllerKey)
		if ctlr == nil {
			return nil, nil, fmt.Errorf("could not find controller with key %d", vd.Key)
		}
		src["key"] = int(vd.Key)
		var err error
		src["device_address"], err = computeDevAddr(vd, ctlr.(types.BaseVirtualController))
		if err != nil {
			return nil, nil, fmt.Errorf("error computing device address: %s", err)
		}
		// Copy the source set into old. This allows us to patch a copy of the
		// product of this set with the source, creating a diff.
		old, err := copystructure.Copy(src)
		if err != nil {
			return nil, nil, fmt.Errorf("error copying source set for disk at unit_number %d: %s", src["unit_number"].(int), err)
		}
		rOld := NewDiskSubresource(c, d, old.(map[string]interface{}), nil, i)
		if err := rOld.Read(l); err != nil {
			return nil, nil, fmt.Errorf("%s: %s", rOld.Addr(), err)
		}
		new, err := copystructure.Copy(rOld.Data())
		if err != nil {
			return nil, nil, fmt.Errorf("error copying current device state for disk at unit_number %d: %s", src["unit_number"].(int), err)
		}
		for k, v := range src {
			// Skip label, path (path will always be computed here as cloned disks
			// are not being attached externally), name, datastore_id, and uuid. Also
			// skip share_count if we the share level isn't custom.
			//
			// TODO: Remove "name" after 2.0.
			switch k {
			case "label", "path", "name", "datastore_id", "uuid":
				continue
			case "io_share_count":
				if src["io_share_level"] != string(types.SharesLevelCustom) {
					continue
				}
			}
			new.(map[string]interface{})[k] = v
		}
		rNew := NewDiskSubresource(c, d, new.(map[string]interface{}), rOld.Data(), i)
		if !reflect.DeepEqual(rNew.Data(), rOld.Data()) {
			uspec, err := rNew.Update(l)
			if err != nil {
				return nil, nil, fmt.Errorf("%s: %s", rNew.Addr(), err)
			}
			l = applyDeviceChange(l, uspec)
			spec = append(spec, uspec...)
		}
		updates = append(updates, rNew.Data())
	}

	// Any disk past the current device list is a new device. Create those now.
	for _, ni := range curSet[len(devices):] {
		r := NewDiskSubresource(c, d, ni.(map[string]interface{}), nil, len(updates))
		cspec, err := r.Create(l)
		if err != nil {
			return nil, nil, fmt.Errorf("%s: %s", r.Addr(), err)
		}
		l = applyDeviceChange(l, cspec)
		spec = append(spec, cspec...)
		updates = append(updates, r.Data())
	}

	log.Printf("[DEBUG] DiskPostCloneOperation: Post-clone final resource list: %s", subresourceListString(updates))
	if err := d.Set(subresourceTypeDisk, updates); err != nil {
		return nil, nil, err
	}
	log.Printf("[DEBUG] DiskPostCloneOperation: Device list at end of operation: %s", DeviceListString(l))
	log.Printf("[DEBUG] DiskPostCloneOperation: Device config operations from post-clone: %s", DeviceChangeString(spec))
	log.Printf("[DEBUG] DiskPostCloneOperation: Operation complete, returning updated spec")
	return l, spec, nil
}
// DiskPostCloneOperation normalizes the virtual disks on a freshly-cloned
// virtual machine and outputs any necessary device change operations. It also
// sets the state in advance of the post-create read.
//
// This differs from a regular apply operation in that a configuration is
// already present, but we don't have any existing state, which the standard
// virtual device operations rely pretty heavily on.