vsphere/internal/virtualdevice/virtual_machine_disk_subresource.go
func DiskCloneValidateOperation(d *schema.ResourceDiff, c *govmomi.Client, l object.VirtualDeviceList, linked bool) error {
	log.Printf("[DEBUG] DiskCloneValidateOperation: Checking existing virtual disk configuration")
	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] DiskCloneValidateOperation: Disk devices order before sort: %s", DeviceListString(devices))
	sort.Sort(devSort)
	devices = devSort.Sort
	log.Printf("[DEBUG] DiskCloneValidateOperation: Disk devices order after sort: %s", DeviceListString(devices))
	// Do the same for our listed disks.
	curSet := d.Get(subresourceTypeDisk).([]interface{})
	log.Printf("[DEBUG] DiskCloneValidateOperation: Current resource set: %s", subresourceListString(curSet))
	sort.Sort(virtualDiskSubresourceSorter(curSet))
	log.Printf("[DEBUG] DiskCloneValidateOperation: Resource set order after sort: %s", subresourceListString(curSet))

	// Quickly validate length. If there are more disks in the template than
	// there is in the configuration, kick out an error.
	if len(devices) > len(curSet) {
		return fmt.Errorf("not enough disks in configuration - you need at least %d to use this template (current: %d)", len(devices), len(curSet))
	}

	// Do test read operations on all disks.
	log.Printf("[DEBUG] DiskCloneValidateOperation: Running test read operations on all disks")
	for i, device := range devices {
		m := make(map[string]interface{})
		vd := device.GetVirtualDevice()
		ctlr := l.FindByKey(vd.ControllerKey)
		if ctlr == nil {
			return fmt.Errorf("could not find controller with key %d", vd.Key)
		}
		m["key"] = int(vd.Key)
		var err error
		m["device_address"], err = computeDevAddr(vd, ctlr.(types.BaseVirtualController))
		if err != nil {
			return fmt.Errorf("error computing device address: %s", err)
		}
		r := NewDiskSubresource(c, d, m, nil, i)
		if err := r.Read(l); err != nil {
			return fmt.Errorf("%s: validation failed (%s)", r.Addr(), err)
		}
		// Load the target resource to do a few comparisons for correctness in config.
		targetM := curSet[i].(map[string]interface{})
		tr := NewDiskSubresource(c, d, targetM, nil, i)

		// Do some pre-clone validation. This is mainly to make sure that the disks
		// clone in a way that is consistent with configuration.
		targetName, err := diskLabelOrName(tr.Data())
		if err != nil {
			return fmt.Errorf("%s: %s", tr.Addr(), err)
		}
		targetPath := r.Get("path").(string)
		sourceSize := r.Get("size").(int)
		targetSize := tr.Get("size").(int)
		targetThin := tr.Get("thin_provisioned").(bool)
		targetEager := tr.Get("eagerly_scrub").(bool)

		var sourceThin, sourceEager bool
		if b := r.Get("thin_provisioned"); b != nil {
			sourceThin = b.(bool)
		}
		if b := r.Get("eagerly_scrub"); b != nil {
			sourceEager = b.(bool)
		}

		switch {
		case linked:
			switch {
			case sourceSize != targetSize:
				return fmt.Errorf("%s: disk name %s must be the exact size of source when using linked_clone (expected: %d GiB)", tr.Addr(), targetName, sourceSize)
			case sourceThin != targetThin:
				return fmt.Errorf("%s: disk name %s must have same value for thin_provisioned as source when using linked_clone (expected: %t)", tr.Addr(), targetName, sourceThin)
			case sourceEager != targetEager:
				return fmt.Errorf("%s: disk name %s must have same value for eagerly_scrub as source when using linked_clone (expected: %t)", tr.Addr(), targetName, sourceEager)
			}
		default:
			if sourceSize > targetSize {
				return fmt.Errorf("%s: disk name %s must be at least the same size of source when cloning (expected: >= %d GiB)", tr.Addr(), targetName, sourceSize)
			}
		}

		// Finally, we don't support non-SCSI (ie: SATA, IDE, NVMe) disks, so kick
		// back an error if we see one of those.
		ct, _, _, err := splitDevAddr(r.DevAddr())
		if err != nil {
			return fmt.Errorf("%s: error parsing device address after reading disk %q: %s", tr.Addr(), targetPath, err)
		}
		if ct != SubresourceControllerTypeSCSI {
			return fmt.Errorf("%s: unsupported controller type %s for disk %q. Please use a template with SCSI disks only", tr.Addr(), ct, targetPath)
		}
	}
	log.Printf("[DEBUG] DiskCloneValidateOperation: All disks in source validated successfully")
	return nil
}
func DiskCloneValidateOperation(d *schema.ResourceDiff, c *govmomi.Client, l object.VirtualDeviceList, linked bool) error {
	log.Printf("[DEBUG] DiskCloneValidateOperation: Checking existing virtual disk configuration")
	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] DiskCloneValidateOperation: Disk devices order before sort: %s", DeviceListString(devices))
	sort.Sort(devSort)
	devices = devSort.Sort
	log.Printf("[DEBUG] DiskCloneValidateOperation: Disk devices order after sort: %s", DeviceListString(devices))
	// Do the same for our listed disks.
	curSet := d.Get(subresourceTypeDisk).([]interface{})
	log.Printf("[DEBUG] DiskCloneValidateOperation: Current resource set: %s", subresourceListString(curSet))
	sort.Sort(virtualDiskSubresourceSorter(curSet))
	log.Printf("[DEBUG] DiskCloneValidateOperation: Resource set order after sort: %s", subresourceListString(curSet))

	// Quickly validate length. If there are more disks in the template than
	// there is in the configuration, kick out an error.
	if len(devices) > len(curSet) {
		return fmt.Errorf("not enough disks in configuration - you need at least %d to use this template (current: %d)", len(devices), len(curSet))
	}

	// Do test read operations on all disks.
	log.Printf("[DEBUG] DiskCloneValidateOperation: Running test read operations on all disks")
	for i, device := range devices {
		m := make(map[string]interface{})
		vd := device.GetVirtualDevice()
		ctlr := l.FindByKey(vd.ControllerKey)
		if ctlr == nil {
			return fmt.Errorf("could not find controller with key %d", vd.Key)
		}
		m["key"] = int(vd.Key)
		var err error
		m["device_address"], err = computeDevAddr(vd, ctlr.(types.BaseVirtualController))
		if err != nil {
			return fmt.Errorf("error computing device address: %s", err)
		}
		r := NewDiskSubresource(c, d, m, nil, i)
		if err := r.Read(l); err != nil {
			return fmt.Errorf("%s: validation failed (%s)", r.Addr(), err)
		}
		// Load the target resource to do a few comparisons for correctness in config.
		targetM := curSet[i].(map[string]interface{})
		tr := NewDiskSubresource(c, d, targetM, nil, i)

		// Do some pre-clone validation. This is mainly to make sure that the disks
		// clone in a way that is consistent with configuration.
		targetName, err := diskLabelOrName(tr.Data())
		if err != nil {
			return fmt.Errorf("%s: %s", tr.Addr(), err)
		}
		targetPath := r.Get("path").(string)
		sourceSize := r.Get("size").(int)
		targetSize := tr.Get("size").(int)
		targetThin := tr.Get("thin_provisioned").(bool)
		targetEager := tr.Get("eagerly_scrub").(bool)

		var sourceThin, sourceEager bool
		if b := r.Get("thin_provisioned"); b != nil {
			sourceThin = b.(bool)
		}
		if b := r.Get("eagerly_scrub"); b != nil {
			sourceEager = b.(bool)
		}

		switch {
		case linked:
			switch {
			case sourceSize != targetSize:
				return fmt.Errorf("%s: disk name %s must be the exact size of source when using linked_clone (expected: %d GiB)", tr.Addr(), targetName, sourceSize)
			case sourceThin != targetThin:
				return fmt.Errorf("%s: disk name %s must have same value for thin_provisioned as source when using linked_clone (expected: %t)", tr.Addr(), targetName, sourceThin)
			case sourceEager != targetEager:
				return fmt.Errorf("%s: disk name %s must have same value for eagerly_scrub as source when using linked_clone (expected: %t)", tr.Addr(), targetName, sourceEager)
			}
		default:
			if sourceSize > targetSize {
				return fmt.Errorf("%s: disk name %s must be at least the same size of source when cloning (expected: >= %d GiB)", tr.Addr(), targetName, sourceSize)
			}
		}

		// Finally, we don't support non-SCSI (ie: SATA, IDE, NVMe) disks, so kick
		// back an error if we see one of those.
		ct, _, _, err := splitDevAddr(r.DevAddr())
		if err != nil {
			return fmt.Errorf("%s: error parsing device address after reading disk %q: %s", tr.Addr(), targetPath, err)
		}
		if ct != SubresourceControllerTypeSCSI {
			return fmt.Errorf("%s: unsupported controller type %s for disk %q. Please use a template with SCSI disks only", tr.Addr(), ct, targetPath)
		}
	}
	log.Printf("[DEBUG] DiskCloneValidateOperation: All disks in source validated successfully")
	return nil
}
func DiskCloneValidateOperation ( d * schema . ResourceDiff , c * govmomi . Client , l object . VirtualDeviceList , linked bool ) error { log . Printf ( " " ) devices := SelectDisks ( l , d . Get ( " " ) . ( int ) ) // Sort the device list, in case it's not sorted already. devSort := virtualDeviceListSorter { Sort : devices , DeviceList : l , } log . Printf ( " " , DeviceListString ( devices ) ) sort . Sort ( devSort ) devices = devSort . Sort log . Printf ( " " , DeviceListString ( devices ) ) // Do the same for our listed disks. curSet := d . Get ( subresourceTypeDisk ) . ( [ ] interface { } ) log . Printf ( " " , subresourceListString ( curSet ) ) sort . Sort ( virtualDiskSubresourceSorter ( curSet ) ) log . Printf ( " " , subresourceListString ( curSet ) ) // Quickly validate length. If there are more disks in the template than // there is in the configuration, kick out an error. if len ( devices ) > len ( curSet ) { return fmt . Errorf ( " " , len ( devices ) , len ( curSet ) ) } // Do test read operations on all disks. log . Printf ( " " ) for i , device := range devices { m := make ( map [ string ] interface { } ) vd := device . GetVirtualDevice ( ) ctlr := l . FindByKey ( vd . ControllerKey ) if ctlr == nil { return fmt . Errorf ( " " , vd . Key ) } m [ " " ] = int ( vd . Key ) var err error m [ " " ] , err = computeDevAddr ( vd , ctlr . ( types . BaseVirtualController ) ) if err != nil { return fmt . Errorf ( " " , err ) } r := NewDiskSubresource ( c , d , m , nil , i ) if err := r . Read ( l ) ; err != nil { return fmt . Errorf ( " " , r . Addr ( ) , err ) } // Load the target resource to do a few comparisons for correctness in config. targetM := curSet [ i ] . ( map [ string ] interface { } ) tr := NewDiskSubresource ( c , d , targetM , nil , i ) // Do some pre-clone validation. This is mainly to make sure that the disks // clone in a way that is consistent with configuration. targetName , err := diskLabelOrName ( tr . Data ( ) ) if err != nil { return fmt . Errorf ( " " , tr . Addr ( ) , err ) } targetPath := r . Get ( " " ) . ( string ) sourceSize := r . Get ( " " ) . ( int ) targetSize := tr . Get ( " " ) . ( int ) targetThin := tr . Get ( " " ) . ( bool ) targetEager := tr . Get ( " " ) . ( bool ) var sourceThin , sourceEager bool if b := r . Get ( " " ) ; b != nil { sourceThin = b . ( bool ) } if b := r . Get ( " " ) ; b != nil { sourceEager = b . ( bool ) } switch { case linked : switch { case sourceSize != targetSize : return fmt . Errorf ( " " , tr . Addr ( ) , targetName , sourceSize ) case sourceThin != targetThin : return fmt . Errorf ( " " , tr . Addr ( ) , targetName , sourceThin ) case sourceEager != targetEager : return fmt . Errorf ( " " , tr . Addr ( ) , targetName , sourceEager ) } default : if sourceSize > targetSize { return fmt . Errorf ( " " , tr . Addr ( ) , targetName , sourceSize ) } } // Finally, we don't support non-SCSI (ie: SATA, IDE, NVMe) disks, so kick // back an error if we see one of those. ct , _ , _ , err := splitDevAddr ( r . DevAddr ( ) ) if err != nil { return fmt . Errorf ( " " , tr . Addr ( ) , targetPath , err ) } if ct != SubresourceControllerTypeSCSI { return fmt . Errorf ( " " , tr . Addr ( ) , ct , targetPath ) } } log . Printf ( " " ) return nil }
// DiskCloneValidateOperation takes the VirtualDeviceList, which should come
// from a source VM or template, and validates the following:
//
// * There are at least as many disks defined in the configuration as there are
// in the source VM or template.
// * All disks survive a disk sub-resource read operation.
//
// This function is meant to be called during diff customization. It is a
// subset of the normal refresh behaviour as we don't worry about checking
// existing state.
DiskCloneValidateOperation takes the VirtualDeviceList which should come from a source VM or template and validates the following : * There are at least as many disks defined in the configuration as there are in the source VM or template . * All disks survive a disk sub - resource read operation . This function is meant to be called during diff customization . It is a subset of the normal refresh behaviour as we don t worry about checking existing state .