vsphere/internal/virtualdevice/virtual_machine_disk_subresource.go
func DiskImportOperation(d *schema.ResourceData, c *govmomi.Client, l object.VirtualDeviceList) error {
	log.Printf("[DEBUG] DiskImportOperation: Performing pre-read import and validation of virtual disks")
	devices := SelectDisks(l, d.Get("scsi_controller_count").(int))
	// Sort the device list, in case it's not sorted already.
	devSort := virtualDeviceListSorter{
		Sort:       devices,
		DeviceList: l,
	}
	log.Printf("[DEBUG] DiskImportOperation: Disk devices order before sort: %s", DeviceListString(devices))
	sort.Sort(devSort)
	devices = devSort.Sort
	log.Printf("[DEBUG] DiskImportOperation: Disk devices order after sort: %s", DeviceListString(devices))

	// Read in the disks. We don't do anything with the results here other than
	// validate that the disks are SCSI disks. The read operation validates the rest.
	var curSet []interface{}
	log.Printf("[DEBUG] DiskImportOperation: Validating disk type and saving ")
	for i, device := range devices {
		vd := device.GetVirtualDevice()
		ctlr := l.FindByKey(vd.ControllerKey)
		if ctlr == nil {
			return fmt.Errorf("could not find controller with key %d", vd.Key)
		}
		addr, err := computeDevAddr(vd, ctlr.(types.BaseVirtualController))
		if err != nil {
			return fmt.Errorf("error computing device address: %s", err)
		}
		ct, _, _, err := splitDevAddr(addr)
		if err != nil {
			return fmt.Errorf("disk.%d: error parsing device address %s: %s", i, addr, err)
		}
		if ct != SubresourceControllerTypeSCSI {
			return fmt.Errorf("disk.%d: unsupported controller type %s for disk %s. The VM resource supports SCSI disks only", i, ct, addr)
		}
		// As one final validation, as we are no longer reading here, validate that
		// this is a VMDK-backed virtual disk to make sure we aren't importing RDM
		// disks or what not. The device should have already been validated as a
		// virtual disk via SelectDisks.
		if _, ok := device.(*types.VirtualDisk).Backing.(*types.VirtualDiskFlatVer2BackingInfo); !ok {
			return fmt.Errorf(
				"disk.%d: unsupported disk type at %s (expected flat VMDK version 2, got %T)",
				i,
				addr,
				device.(*types.VirtualDisk).Backing,
			)
		}
		m := make(map[string]interface{})
		// Save information so that the next DiskRefreshOperation can pick this
		// disk up as if it was newly added and not attempt to try and line up
		// UUIDs. We use a negative key for this reason, in addition to assigning
		// the device address.
		m["key"] = (i + 1) * -1
		m["device_address"] = addr
		// Assign a computed label. This label *needs* be the label this disk is
		// assigned in config, or you risk service interruptions or data corruption.
		m["label"] = fmt.Sprintf("disk%d", i)
		// Set keep_on_remove to ensure that if labels are assigned incorrectly,
		// all that happens is that the disk is removed. The comments above
		// regarding the risk of incorrect label assignment are still true, but
		// this greatly reduces the risk of data loss.
		m["keep_on_remove"] = true

		curSet = append(curSet, m)
	}
	log.Printf("[DEBUG] DiskImportOperation: Discovered disks from import: %s", subresourceListString(curSet))
	if err := d.Set(subresourceTypeDisk, curSet); err != nil {
		return err
	}
	log.Printf("[DEBUG] DiskImportOperation: Pre-read import and validation complete")
	return nil
}
func DiskImportOperation(d *schema.ResourceData, c *govmomi.Client, l object.VirtualDeviceList) error {
	log.Printf("[DEBUG] DiskImportOperation: Performing pre-read import and validation of virtual disks")
	devices := SelectDisks(l, d.Get("scsi_controller_count").(int))
	// Sort the device list, in case it's not sorted already.
	devSort := virtualDeviceListSorter{
		Sort:       devices,
		DeviceList: l,
	}
	log.Printf("[DEBUG] DiskImportOperation: Disk devices order before sort: %s", DeviceListString(devices))
	sort.Sort(devSort)
	devices = devSort.Sort
	log.Printf("[DEBUG] DiskImportOperation: Disk devices order after sort: %s", DeviceListString(devices))

	// Read in the disks. We don't do anything with the results here other than
	// validate that the disks are SCSI disks. The read operation validates the rest.
	var curSet []interface{}
	log.Printf("[DEBUG] DiskImportOperation: Validating disk type and saving ")
	for i, device := range devices {
		vd := device.GetVirtualDevice()
		ctlr := l.FindByKey(vd.ControllerKey)
		if ctlr == nil {
			return fmt.Errorf("could not find controller with key %d", vd.Key)
		}
		addr, err := computeDevAddr(vd, ctlr.(types.BaseVirtualController))
		if err != nil {
			return fmt.Errorf("error computing device address: %s", err)
		}
		ct, _, _, err := splitDevAddr(addr)
		if err != nil {
			return fmt.Errorf("disk.%d: error parsing device address %s: %s", i, addr, err)
		}
		if ct != SubresourceControllerTypeSCSI {
			return fmt.Errorf("disk.%d: unsupported controller type %s for disk %s. The VM resource supports SCSI disks only", i, ct, addr)
		}
		// As one final validation, as we are no longer reading here, validate that
		// this is a VMDK-backed virtual disk to make sure we aren't importing RDM
		// disks or what not. The device should have already been validated as a
		// virtual disk via SelectDisks.
		if _, ok := device.(*types.VirtualDisk).Backing.(*types.VirtualDiskFlatVer2BackingInfo); !ok {
			return fmt.Errorf(
				"disk.%d: unsupported disk type at %s (expected flat VMDK version 2, got %T)",
				i,
				addr,
				device.(*types.VirtualDisk).Backing,
			)
		}
		m := make(map[string]interface{})
		// Save information so that the next DiskRefreshOperation can pick this
		// disk up as if it was newly added and not attempt to try and line up
		// UUIDs. We use a negative key for this reason, in addition to assigning
		// the device address.
		m["key"] = (i + 1) * -1
		m["device_address"] = addr
		// Assign a computed label. This label *needs* be the label this disk is
		// assigned in config, or you risk service interruptions or data corruption.
		m["label"] = fmt.Sprintf("disk%d", i)
		// Set keep_on_remove to ensure that if labels are assigned incorrectly,
		// all that happens is that the disk is removed. The comments above
		// regarding the risk of incorrect label assignment are still true, but
		// this greatly reduces the risk of data loss.
		m["keep_on_remove"] = true

		curSet = append(curSet, m)
	}
	log.Printf("[DEBUG] DiskImportOperation: Discovered disks from import: %s", subresourceListString(curSet))
	if err := d.Set(subresourceTypeDisk, curSet); err != nil {
		return err
	}
	log.Printf("[DEBUG] DiskImportOperation: Pre-read import and validation complete")
	return nil
}
// DiskImportOperation validates the disk configuration of the virtual
// machine's VirtualDeviceList to ensure it will be imported properly, and also
// saves device addresses into state for disks defined in config. Both the
// imported device list is sorted by the device's unit number on the SCSI bus.