func splitDevAddr(id string) (string, int, int, error) {
	parts := strings.Split(id, ":")
	if len(parts) < 3 {
		return "", 0, 0, fmt.Errorf("invalid ID %q", id)
	}
	ct, cbs, dus := parts[0], parts[1], parts[2]
	cb, cbe := strconv.Atoi(cbs)
	du, due := strconv.Atoi(dus)
	var found bool
	for _, v := range subresourceIDControllerTypeAllowedValues {
		if v == ct {
			found = true
		}
	}
	if !found {
		return ct, cb, du, fmt.Errorf("invalid controller type %q found in ID", ct)
	}
	if cbe != nil {
		return ct, cb, du, fmt.Errorf("invalid bus number %q found in ID", cbs)
	}
	if due != nil {
		return ct, cb, du, fmt.Errorf("invalid disk unit number %q found in ID", dus)
	}
	return ct, cb, du, nil
}

// splitDevAddr splits an device addres into its inparticular parts and asserts
// that we have all the correct data.

splitDevAddr splits an device addres into its inparticular parts and asserts that we have all the correct data .


func findVirtualDeviceInListControllerSelectFunc(ct string, cb int) func(types.BaseVirtualDevice) bool {
	return func(device types.BaseVirtualDevice) bool {
		switch ct {
		case SubresourceControllerTypeIDE:
			if _, ok := device.(*types.VirtualIDEController); !ok {
				return false
			}
		case SubresourceControllerTypeSATA:
			if _, ok := device.(*types.VirtualAHCIController); !ok {
				return false
			}
		case SubresourceControllerTypeSCSI:
			if _, ok := device.(types.BaseVirtualSCSIController); !ok {
				return false
			}
		case SubresourceControllerTypePCI:
			if _, ok := device.(*types.VirtualPCIController); !ok {
				return false
			}
		}
		vc := device.(types.BaseVirtualController).GetVirtualController()
		if vc.BusNumber == int32(cb) {
			return true
		}
		return false
	}
}

// findVirtualDeviceInListControllerSelectFunc returns a function that can be
// used with VirtualDeviceList.Select to locate a controller device based on
// the criteria that we have laid out.


func findVirtualDeviceInListDeviceSelectFunc(ckey int32, du int) func(types.BaseVirtualDevice) bool {
	return func(d types.BaseVirtualDevice) bool {
		vd := d.GetVirtualDevice()
		if vd.ControllerKey == ckey && vd.UnitNumber != nil && *vd.UnitNumber == int32(du) {
			return true
		}
		return false
	}
}

func findVirtualDeviceInListDeviceSelectFunc ( ckey int32 , du int ) func ( types . BaseVirtualDevice ) bool { return func ( d types . BaseVirtualDevice ) bool { vd := d . GetVirtualDevice ( ) if vd . ControllerKey == ckey && vd . UnitNumber != nil && * vd . UnitNumber == int32 ( du ) { return true } return false } }

// findVirtualDeviceInListDeviceSelectFunc returns a function that can be used
// with VirtualDeviceList.Select to locate a virtual device based on its
// controller device key, and the unit number on the device.

findVirtualDeviceInListDeviceSelectFunc returns a function that can be used with VirtualDeviceList . Select to locate a virtual device based on its controller device key and the unit number on the device .


func findControllerForDevice(l object.VirtualDeviceList, bvd types.BaseVirtualDevice) (types.BaseVirtualController, error) {
	vd := bvd.GetVirtualDevice()
	ctlr := l.FindByKey(vd.ControllerKey)

	if ctlr == nil {
		return nil, fmt.Errorf("could not find controller key %d for device %d", vd.ControllerKey, vd.Key)
	}

	return ctlr.(types.BaseVirtualController), nil
}

func findControllerForDevice ( l object . VirtualDeviceList , bvd types . BaseVirtualDevice ) ( types . BaseVirtualController , error ) { vd := bvd . GetVirtualDevice ( ) ctlr := l . FindByKey ( vd . ControllerKey ) if ctlr == nil { return nil , fmt . Errorf ( " " , vd . ControllerKey , vd . Key ) } return ctlr . ( types . BaseVirtualController ) , nil }

// findControllerForDevice locates a controller via its virtual device.

findControllerForDevice locates a controller via its virtual device .


func (r *Subresource) FindVirtualDeviceByAddr(l object.VirtualDeviceList) (types.BaseVirtualDevice, error) {
	log.Printf("[DEBUG] FindVirtualDevice: Looking for device with address %s", r.DevAddr())
	ct, cb, du, err := splitDevAddr(r.DevAddr())
	if err != nil {
		return nil, err
	}

	// find the controller
	csf := findVirtualDeviceInListControllerSelectFunc(ct, cb)
	ctlrs := l.Select(csf)
	if len(ctlrs) != 1 {
		return nil, fmt.Errorf("invalid controller result - %d results returned (expected 1): type %q, bus number: %d", len(ctlrs), ct, cb)
	}
	ctlr := ctlrs[0]

	// find the device
	ckey := ctlr.GetVirtualDevice().Key
	dsf := findVirtualDeviceInListDeviceSelectFunc(ckey, du)
	devices := l.Select(dsf)
	if len(devices) != 1 {
		return nil, fmt.Errorf("invalid device result - %d results returned (expected 1): controller key %q, disk number: %d", len(devices), ckey, du)
	}
	device := devices[0]
	log.Printf("[DEBUG] FindVirtualDevice: Device found: %s", l.Name(device))
	return device, nil
}

// FindVirtualDeviceByAddr locates the subresource's virtual device in the
// supplied VirtualDeviceList by its device address.

FindVirtualDeviceByAddr locates the subresource s virtual device in the supplied VirtualDeviceList by its device address .


func (r *Subresource) FindVirtualDevice(l object.VirtualDeviceList) (types.BaseVirtualDevice, error) {
	if key := r.Get("key").(int); key > 0 {
		log.Printf("[DEBUG] FindVirtualDevice: Looking for device with key %d", key)
		if dev := l.FindByKey(int32(key)); dev != nil {
			log.Printf("[DEBUG] FindVirtualDevice: Device found: %s", l.Name(dev))
			return dev, nil
		}
		return nil, fmt.Errorf("could not find device with key %d", key)
	}
	return r.FindVirtualDeviceByAddr(l)
}

func ( r * Subresource ) FindVirtualDevice ( l object . VirtualDeviceList ) ( types . BaseVirtualDevice , error ) { if key := r . Get ( " " ) . ( int ) ; key > 0 { log . Printf ( " " , key ) if dev := l . FindByKey ( int32 ( key ) ) ; dev != nil { log . Printf ( " " , l . Name ( dev ) ) return dev , nil } return nil , fmt . Errorf ( " " , key ) } return r . FindVirtualDeviceByAddr ( l ) }

// FindVirtualDevice will attempt to find an address by its device key if it is
// > 0, otherwise it will attempt to locate it by its device address.

FindVirtualDevice will attempt to find an address by its device key if it is > 0 otherwise it will attempt to locate it by its device address .


func (r *Subresource) String() string {
	devaddr := r.Get("device_address").(string)
	if devaddr == "" {
		devaddr = "<new device>"
	}
	return fmt.Sprintf("%s (key %d at %s)", r.Addr(), r.Get("key").(int), devaddr)
}

func ( r * Subresource ) String ( ) string { devaddr := r . Get ( " " ) . ( string ) if devaddr == " " { devaddr = " " } return fmt . Sprintf ( " " , r . Addr ( ) , r . Get ( " " ) . ( int ) , devaddr ) }

// String prints out the device sub-resource's information including the ID at
// time of instantiation, the short name of the disk, and the current device
// key and address.

String prints out the device sub - resource s information including the ID at time of instantiation the short name of the disk and the current device key and address .


func swapSCSIDevice(l object.VirtualDeviceList, device types.BaseVirtualSCSIController, ct string, st string) ([]types.BaseVirtualDeviceConfigSpec, error) {
	log.Printf("[DEBUG] swapSCSIDevice: Swapping SCSI device for one of controller type %s: %s", ct, l.Name(device.(types.BaseVirtualDevice)))
	var spec []types.BaseVirtualDeviceConfigSpec
	bvd := device.(types.BaseVirtualDevice)
	cspec, err := object.VirtualDeviceList{bvd}.ConfigSpec(types.VirtualDeviceConfigSpecOperationRemove)
	if err != nil {
		return nil, err
	}
	spec = append(spec, cspec...)

	nsd, err := l.CreateSCSIController(ct)
	if err != nil {
		return nil, err
	}
	nsd.(types.BaseVirtualSCSIController).GetVirtualSCSIController().SharedBus = types.VirtualSCSISharing(st)
	nsd.(types.BaseVirtualSCSIController).GetVirtualSCSIController().BusNumber = device.GetVirtualSCSIController().BusNumber
	cspec, err = object.VirtualDeviceList{nsd}.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
	if err != nil {
		return nil, err
	}
	spec = append(spec, cspec...)
	ockey := device.GetVirtualSCSIController().Key
	nckey := nsd.GetVirtualDevice().Key
	for _, vd := range l {
		if vd.GetVirtualDevice().ControllerKey == ockey {
			vd.GetVirtualDevice().ControllerKey = nckey
			cspec, err := object.VirtualDeviceList{vd}.ConfigSpec(types.VirtualDeviceConfigSpecOperationEdit)
			if err != nil {
				return nil, err
			}
			if len(cspec) != 1 {
				return nil, fmt.Errorf("incorrect number of config spec items returned - expected 1, got %d", len(cspec))
			}
			// Clear the file operation
			cspec[0].GetVirtualDeviceConfigSpec().FileOperation = ""
			spec = append(spec, cspec...)
		}
	}
	log.Printf("[DEBUG] swapSCSIDevice: Outgoing device config spec: %s", DeviceChangeString(spec))
	return spec, nil
}

func
swapSCSIDevice
(
l
object
.
VirtualDeviceList
,
device
types
.
BaseVirtualSCSIController
,
ct
string
,
st
string
)
(
[
]
types
.
BaseVirtualDeviceConfigSpec
,
error
)
{
log
.
Printf
(
"
"
,
ct
,
l
.
Name
(
device
.
(
types
.
BaseVirtualDevice
)
)
)
var
spec
[
]
types
.
BaseVirtualDeviceConfigSpec
bvd
:=
device
.
(
types
.
BaseVirtualDevice
)
cspec
,
err
:=
object
.
VirtualDeviceList
{
bvd
}
.
ConfigSpec
(
types
.
VirtualDeviceConfigSpecOperationRemove
)
if
err
!=
{
return
,
err
}
spec
=
append
(
spec
,
cspec
...
)
nsd
,
err
:=
l
.
CreateSCSIController
(
ct
)
if
err
!=
{
return
,
err
}
nsd
.
(
types
.
BaseVirtualSCSIController
)
.
GetVirtualSCSIController
(
)
.
SharedBus
=
types
.
VirtualSCSISharing
(
st
)
nsd
.
(
types
.
BaseVirtualSCSIController
)
.
GetVirtualSCSIController
(
)
.
BusNumber
=
device
.
GetVirtualSCSIController
(
)
.
BusNumber
cspec
,
err
=
object
.
VirtualDeviceList
{
nsd
}
.
ConfigSpec
(
types
.
VirtualDeviceConfigSpecOperationAdd
)
if
err
!=
{
return
,
err
}
spec
=
append
(
spec
,
cspec
...
)
ockey
:=
device
.
GetVirtualSCSIController
(
)
.
Key
nckey
:=
nsd
.
GetVirtualDevice
(
)
.
Key
for
_
,
vd
:=
range
l
{
if
vd
.
GetVirtualDevice
(
)
.
ControllerKey
==
ockey
{
vd
.
GetVirtualDevice
(
)
.
ControllerKey
=
nckey
cspec
,
err
:=
object
.
VirtualDeviceList
{
vd
}
.
ConfigSpec
(
types
.
VirtualDeviceConfigSpecOperationEdit
)
if
err
!=
{
return
,
err
}
if
len
(
cspec
)
!=
1
{
return
,
fmt
.
Errorf
(
"
"
,
len
(
cspec
)
)
}
// Clear the file operation
cspec
[
0
]
.
GetVirtualDeviceConfigSpec
(
)
.
FileOperation
=
"
"
spec
=
append
(
spec
,
cspec
...
)
}
}
log
.
Printf
(
"
"
,
DeviceChangeString
(
spec
)
)
return
spec
,
}

// swapSCSIDevice swaps out the supplied controller for a new one of the
// supplied controller type. Any connected devices are re-connected at the same
// device units on the new device. A list of changes is returned.

swapSCSIDevice swaps out the supplied controller for a new one of the supplied controller type . Any connected devices are re - connected at the same device units on the new device . A list of changes is returned .


func NormalizeSCSIBus(l object.VirtualDeviceList, ct string, count int, st string) (object.VirtualDeviceList, []types.BaseVirtualDeviceConfigSpec, error) {
	log.Printf("[DEBUG] NormalizeSCSIBus: Normalizing first %d controllers on SCSI bus to device type %s", count, ct)
	var spec []types.BaseVirtualDeviceConfigSpec
	ctlrs := make([]types.BaseVirtualSCSIController, count)
	// Don't worry about doing any fancy select stuff here, just go thru the
	// VirtualDeviceList and populate the controllers.
	iCount := int32(count)
	for _, dev := range l {
		if sc, ok := dev.(types.BaseVirtualSCSIController); ok {
			if busNumber := sc.GetVirtualSCSIController().BusNumber; busNumber < iCount {
				ctlrs[busNumber] = sc
			}
		}
	}
	log.Printf("[DEBUG] NormalizeSCSIBus: Current SCSI bus contents: %s", scsiControllerListString(ctlrs))
	// Now iterate over the controllers
	for n, ctlr := range ctlrs {
		if ctlr == nil {
			log.Printf("[DEBUG] NormalizeSCSIBus: Creating SCSI controller of type %s at bus number %d", ct, n)
			cspec, err := createSCSIController(&l, ct, st)
			if err != nil {
				return nil, nil, err
			}
			spec = append(spec, cspec...)
			continue
		}
		if l.Type(ctlr.(types.BaseVirtualDevice)) == ct {
			cspec, err := setSCSIBusSharing(&l, ctlr, st)
			if err != nil {
				return nil, nil, err
			}
			spec = append(spec, cspec...)
			continue
		}
		cspec, err := swapSCSIDevice(l, ctlr, ct, st)
		if err != nil {
			return nil, nil, err
		}
		spec = append(spec, cspec...)
		l = applyDeviceChange(l, cspec)
		continue
	}
	log.Printf("[DEBUG] NormalizeSCSIBus: Outgoing device list: %s", DeviceListString(l))
	log.Printf("[DEBUG] NormalizeSCSIBus: Outgoing device config spec: %s", DeviceChangeString(spec))
	return l, spec, nil
}

// NormalizeSCSIBus checks the SCSI controllers on the virtual machine and
// either creates them if they don't exist, or migrates them to the specified
// controller type. Devices are migrated to the new controller appropriately. A
// spec slice is returned with the changes.
//
// The first number of slots specified by count are normalized by this
// function. Any others are left unchanged.

NormalizeSCSIBus checks the SCSI controllers on the virtual machine and either creates them if they don t exist or migrates them to the specified controller type . Devices are migrated to the new controller appropriately . A spec slice is returned with the changes . The first number of slots specified by count are normalized by this function . Any others are left unchanged .


func setSCSIBusSharing(l *object.VirtualDeviceList, ctlr types.BaseVirtualSCSIController, st string) ([]types.BaseVirtualDeviceConfigSpec, error) {
	var cspec []types.BaseVirtualDeviceConfigSpec
	if ctlr.GetVirtualSCSIController().SharedBus != types.VirtualSCSISharing(st) {
		ctlr.GetVirtualSCSIController().SharedBus = types.VirtualSCSISharing(st)
		var err error
		cspec, err = object.VirtualDeviceList{ctlr.(types.BaseVirtualDevice)}.ConfigSpec(types.VirtualDeviceConfigSpecOperationEdit)
		if err != nil {
			return nil, err
		}
		*l = applyDeviceChange(*l, cspec)
	}
	return cspec, nil
}

func setSCSIBusSharing ( l * object . VirtualDeviceList , ctlr types . BaseVirtualSCSIController , st string ) ( [ ] types . BaseVirtualDeviceConfigSpec , error ) { var cspec [ ] types . BaseVirtualDeviceConfigSpec if ctlr . GetVirtualSCSIController ( ) . SharedBus != types . VirtualSCSISharing ( st ) { ctlr . GetVirtualSCSIController ( ) . SharedBus = types . VirtualSCSISharing ( st ) var err error cspec , err = object . VirtualDeviceList { ctlr . ( types . BaseVirtualDevice ) } . ConfigSpec ( types . VirtualDeviceConfigSpecOperationEdit ) if err != nil { return nil , err } * l = applyDeviceChange ( * l , cspec ) } return cspec , nil }

// setSCSIBusSharing takes a BaseVirtualSCSIController, sets the sharing mode,
// and applies that change to the VirtualDeviceList.

setSCSIBusSharing takes a BaseVirtualSCSIController sets the sharing mode and applies that change to the VirtualDeviceList .


func createSCSIController(l *object.VirtualDeviceList, ct string, st string) ([]types.BaseVirtualDeviceConfigSpec, error) {
	nc, err := l.CreateSCSIController(ct)
	if err != nil {
		return nil, err
	}
	nc.(types.BaseVirtualSCSIController).GetVirtualSCSIController().SharedBus = types.VirtualSCSISharing(st)
	cspec, err := object.VirtualDeviceList{nc}.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
	*l = applyDeviceChange(*l, cspec)
	return cspec, err
}

func createSCSIController ( l * object . VirtualDeviceList , ct string , st string ) ( [ ] types . BaseVirtualDeviceConfigSpec , error ) { nc , err := l . CreateSCSIController ( ct ) if err != nil { return nil , err } nc . ( types . BaseVirtualSCSIController ) . GetVirtualSCSIController ( ) . SharedBus = types . VirtualSCSISharing ( st ) cspec , err := object . VirtualDeviceList { nc } . ConfigSpec ( types . VirtualDeviceConfigSpecOperationAdd ) * l = applyDeviceChange ( * l , cspec ) return cspec , err }

// createSCSIController creates a new SCSI controller of the specified type and
// sharing mode.

createSCSIController creates a new SCSI controller of the specified type and sharing mode .


func ReadSCSIBusType(l object.VirtualDeviceList, count int) string {
	ctlrs := make([]types.BaseVirtualSCSIController, count)
	for _, dev := range l {
		if sc, ok := dev.(types.BaseVirtualSCSIController); ok && sc.GetVirtualSCSIController().BusNumber < int32(count) {
			ctlrs[sc.GetVirtualSCSIController().BusNumber] = sc
		}
	}
	log.Printf("[DEBUG] ReadSCSIBusType: SCSI controller layout for first %d controllers: %s", count, scsiControllerListString(ctlrs))
	if ctlrs[0] == nil {
		return subresourceControllerTypeUnknown
	}
	last := l.Type(ctlrs[0].(types.BaseVirtualDevice))
	for _, ctlr := range ctlrs[1:] {
		if ctlr == nil || l.Type(ctlr.(types.BaseVirtualDevice)) != last {
			return subresourceControllerTypeMixed
		}
	}
	return last
}

func ReadSCSIBusType ( l object . VirtualDeviceList , count int ) string { ctlrs := make ( [ ] types . BaseVirtualSCSIController , count ) for _ , dev := range l { if sc , ok := dev . ( types . BaseVirtualSCSIController ) ; ok && sc . GetVirtualSCSIController ( ) . BusNumber < int32 ( count ) { ctlrs [ sc . GetVirtualSCSIController ( ) . BusNumber ] = sc } } log . Printf ( " " , count , scsiControllerListString ( ctlrs ) ) if ctlrs [ 0 ] == nil { return subresourceControllerTypeUnknown } last := l . Type ( ctlrs [ 0 ] . ( types . BaseVirtualDevice ) ) for _ , ctlr := range ctlrs [ 1 : ] { if ctlr == nil || l . Type ( ctlr . ( types . BaseVirtualDevice ) ) != last { return subresourceControllerTypeMixed } } return last }

// ReadSCSIBusType checks the SCSI bus state and returns a device type
// depending on if all controllers are one specific kind or not. Only the first
// number of controllers specified by count are checked.

ReadSCSIBusType checks the SCSI bus state and returns a device type depending on if all controllers are one specific kind or not . Only the first number of controllers specified by count are checked .


func ReadSCSIBusSharing(l object.VirtualDeviceList, count int) string {
	ctlrs := make([]types.BaseVirtualSCSIController, count)
	for _, dev := range l {
		if sc, ok := dev.(types.BaseVirtualSCSIController); ok && sc.GetVirtualSCSIController().BusNumber < int32(count) {
			ctlrs[sc.GetVirtualSCSIController().BusNumber] = sc
		}
	}
	log.Printf("[DEBUG] ReadSCSIBusSharing: SCSI controller layout for first %d controllers: %s", count, scsiControllerListString(ctlrs))
	if ctlrs[0] == nil {
		return subresourceControllerSharingUnknown
	}
	last := ctlrs[0].(types.BaseVirtualSCSIController).GetVirtualSCSIController().SharedBus
	for _, ctlr := range ctlrs[1:] {
		if ctlr == nil || ctlr.(types.BaseVirtualSCSIController).GetVirtualSCSIController().SharedBus != last {
			return subresourceControllerSharingMixed
		}
	}
	return string(last)
}

func ReadSCSIBusSharing ( l object . VirtualDeviceList , count int ) string { ctlrs := make ( [ ] types . BaseVirtualSCSIController , count ) for _ , dev := range l { if sc , ok := dev . ( types . BaseVirtualSCSIController ) ; ok && sc . GetVirtualSCSIController ( ) . BusNumber < int32 ( count ) { ctlrs [ sc . GetVirtualSCSIController ( ) . BusNumber ] = sc } } log . Printf ( " " , count , scsiControllerListString ( ctlrs ) ) if ctlrs [ 0 ] == nil { return subresourceControllerSharingUnknown } last := ctlrs [ 0 ] . ( types . BaseVirtualSCSIController ) . GetVirtualSCSIController ( ) . SharedBus for _ , ctlr := range ctlrs [ 1 : ] { if ctlr == nil || ctlr . ( types . BaseVirtualSCSIController ) . GetVirtualSCSIController ( ) . SharedBus != last { return subresourceControllerSharingMixed } } return string ( last ) }

// ReadSCSIBusSharing checks the SCSI bus sharing and returns a sharing type
// depending on if all controllers are one specific kind or not. Only the first
// number of controllers specified by count are checked.

ReadSCSIBusSharing checks the SCSI bus sharing and returns a sharing type depending on if all controllers are one specific kind or not . Only the first number of controllers specified by count are checked .


func pickSCSIController(l object.VirtualDeviceList, bus int) (types.BaseVirtualController, error) {
	log.Printf("[DEBUG] pickSCSIController: Looking for SCSI controller at bus number %d", bus)
	l = l.Select(func(device types.BaseVirtualDevice) bool {
		switch d := device.(type) {
		case types.BaseVirtualSCSIController:
			return d.GetVirtualSCSIController().BusNumber == int32(bus)
		}
		return false
	})

	if len(l) == 0 {
		return nil, fmt.Errorf("could not find scsi controller at bus number %d", bus)
	}

	log.Printf("[DEBUG] pickSCSIController: Found SCSI controller: %s", l.Name(l[0]))
	return l[0].(types.BaseVirtualController), nil
}

func pickSCSIController ( l object . VirtualDeviceList , bus int ) ( types . BaseVirtualController , error ) { log . Printf ( " " , bus ) l = l . Select ( func ( device types . BaseVirtualDevice ) bool { switch d := device . ( type ) { case types . BaseVirtualSCSIController : return d . GetVirtualSCSIController ( ) . BusNumber == int32 ( bus ) } return false } ) if len ( l ) == 0 { return nil , fmt . Errorf ( " " , bus ) } log . Printf ( " " , l . Name ( l [ 0 ] ) ) return l [ 0 ] . ( types . BaseVirtualController ) , nil }

// getSCSIController picks a SCSI controller at the specific bus number supplied.

getSCSIController picks a SCSI controller at the specific bus number supplied .


func (r *Subresource) ControllerForCreateUpdate(l object.VirtualDeviceList, ct string, bus int) (types.BaseVirtualController, error) {
	log.Printf("[DEBUG] ControllerForCreateUpdate: Looking for controller type %s", ct)
	var ctlr types.BaseVirtualController
	var err error
	switch ct {
	case SubresourceControllerTypeIDE:
		ctlr = l.PickController(&types.VirtualIDEController{})
	case SubresourceControllerTypeSATA:
		ctlr = l.PickController(&types.VirtualAHCIController{})
	case SubresourceControllerTypeSCSI:
		ctlr, err = pickSCSIController(l, bus)
	case SubresourceControllerTypePCI:
		ctlr = l.PickController(&types.VirtualPCIController{})
	default:
		return nil, fmt.Errorf("invalid controller type %T", ct)
	}
	if err != nil {
		return nil, err
	}
	if ctlr == nil {
		return nil, fmt.Errorf("could not find an available %s controller", ct)
	}

	// Assert that we are on bus 0 when we aren't looking for a SCSI controller.
	// We currently do not support attaching devices to multiple non-SCSI buses.
	if ctlr.GetVirtualController().BusNumber != 0 && ct != SubresourceControllerTypeSCSI {
		return nil, fmt.Errorf("there are no available slots on the primary %s controller", ct)
	}
	log.Printf("[DEBUG] ControllerForCreateUpdate: Found controller: %s", l.Name(ctlr.(types.BaseVirtualDevice)))

	return ctlr, nil
}

// ControllerForCreateUpdate wraps the controller selection logic to make it
// easier to use in create or update operations. If the controller type is a
// SCSI device, the bus number is searched as well.

ControllerForCreateUpdate wraps the controller selection logic to make it easier to use in create or update operations . If the controller type is a SCSI device the bus number is searched as well .


func applyDeviceChange(l object.VirtualDeviceList, cs []types.BaseVirtualDeviceConfigSpec) object.VirtualDeviceList {
	log.Printf("[DEBUG] applyDeviceChange: Applying changes: %s", DeviceChangeString(cs))
	log.Printf("[DEBUG] applyDeviceChange: Device list before changes: %s", DeviceListString(l))
	for _, s := range cs {
		spec := s.GetVirtualDeviceConfigSpec()
		switch spec.Operation {
		case types.VirtualDeviceConfigSpecOperationAdd:
			l = append(l, spec.Device)
		case types.VirtualDeviceConfigSpecOperationEdit:
			// Edit operations may not be 100% necessary to apply. This is because
			// more often than not, the device will probably be edited in place off
			// of the original reference, meaning that the slice should actually
			// point to the updated item. However, the safer of the two options is to
			// assume that this may *not* be happening as we are not enforcing that
			// in implementation anywhere.
			for n, dev := range l {
				if dev.GetVirtualDevice().Key == spec.Device.GetVirtualDevice().Key {
					l[n] = spec.Device
				}
			}
		case types.VirtualDeviceConfigSpecOperationRemove:
			for i := 0; i < len(l); i++ {
				dev := l[i]
				if dev.GetVirtualDevice().Key == spec.Device.GetVirtualDevice().Key {
					l = append(l[:i], l[i+1:]...)
					i--
				}
			}
		default:
			panic("unknown op")
		}
	}
	log.Printf("[DEBUG] applyDeviceChange: Device list after changes: %s", DeviceListString(l))
	return l
}

// applyDeviceChange applies a pending types.BaseVirtualDeviceConfigSpec to a
// working set to either add, remove, or update devices so that the working
// VirtualDeviceList is as up to date as possible.

applyDeviceChange applies a pending types . BaseVirtualDeviceConfigSpec to a working set to either add remove or update devices so that the working VirtualDeviceList is as up to date as possible .


func DeviceListString(l object.VirtualDeviceList) string {
	var names []string
	for _, d := range l {
		if d == nil {
			names = append(names, "<nil>")
		} else {
			names = append(names, l.Name(d))
		}
	}
	return strings.Join(names, ",")
}

func DeviceListString ( l object . VirtualDeviceList ) string { var names [ ] string for _ , d := range l { if d == nil { names = append ( names , " " ) } else { names = append ( names , l . Name ( d ) ) } } return strings . Join ( names , " " ) }

// DeviceListString pretty-prints each device in a virtual device list, used
// for logging purposes and what not.

DeviceListString pretty - prints each device in a virtual device list used for logging purposes and what not .


func DeviceChangeString(specs []types.BaseVirtualDeviceConfigSpec) string {
	var strs []string
	for _, v := range specs {
		spec := v.GetVirtualDeviceConfigSpec()
		strs = append(strs, fmt.Sprintf("(%s: %T at key %d)", string(spec.Operation), spec.Device, spec.Device.GetVirtualDevice().Key))
	}
	return strings.Join(strs, ",")
}

func DeviceChangeString ( specs [ ] types . BaseVirtualDeviceConfigSpec ) string { var strs [ ] string for _ , v := range specs { spec := v . GetVirtualDeviceConfigSpec ( ) strs = append ( strs , fmt . Sprintf ( " " , string ( spec . Operation ) , spec . Device , spec . Device . GetVirtualDevice ( ) . Key ) ) } return strings . Join ( strs , " " ) }

// DeviceChangeString pretty-prints a slice of VirtualDeviceConfigSpec.

DeviceChangeString pretty - prints a slice of VirtualDeviceConfigSpec .


func subresourceListString(data []interface{}) string {
	var strs []string
	for _, v := range data {
		if v == nil {
			strs = append(strs, "(<nil>)")
			continue
		}
		m := v.(map[string]interface{})
		devaddr := m["device_address"].(string)
		if devaddr == "" {
			devaddr = "<new device>"
		}
		strs = append(strs, fmt.Sprintf("(key %d at %s)", m["key"].(int), devaddr))
	}
	return strings.Join(strs, ",")
}

func subresourceListString ( data [ ] interface { } ) string { var strs [ ] string for _ , v := range data { if v == nil { strs = append ( strs , " " ) continue } m := v . ( map [ string ] interface { } ) devaddr := m [ " " ] . ( string ) if devaddr == " " { devaddr = " " } strs = append ( strs , fmt . Sprintf ( " " , m [ " " ] . ( int ) , devaddr ) ) } return strings . Join ( strs , " " ) }

// subresourceListString takes a list of sub-resources and pretty-prints the
// key and device address.

subresourceListString takes a list of sub - resources and pretty - prints the key and device address .


func scsiControllerListString(ctlrs []types.BaseVirtualSCSIController) string {
	var l object.VirtualDeviceList
	for _, ctlr := range ctlrs {
		if ctlr == nil {
			l = append(l, types.BaseVirtualDevice(nil))
		} else {
			l = append(l, ctlr.(types.BaseVirtualDevice))
		}
	}
	return DeviceListString(l)
}

func scsiControllerListString ( ctlrs [ ] types . BaseVirtualSCSIController ) string { var l object . VirtualDeviceList for _ , ctlr := range ctlrs { if ctlr == nil { l = append ( l , types . BaseVirtualDevice ( nil ) ) } else { l = append ( l , ctlr . ( types . BaseVirtualDevice ) ) } } return DeviceListString ( l ) }

// scsiControllerListString pretty-prints a slice of SCSI controllers.

scsiControllerListString pretty - prints a slice of SCSI controllers .


func AppendDeviceChangeSpec(
	spec []types.BaseVirtualDeviceConfigSpec,
	ops ...types.BaseVirtualDeviceConfigSpec,
) []types.BaseVirtualDeviceConfigSpec {
	for _, op := range ops {
		c := copystructure.Must(copystructure.Copy(op)).(types.BaseVirtualDeviceConfigSpec)
		spec = append(spec, c)
	}
	return spec
}

func AppendDeviceChangeSpec ( spec [ ] types . BaseVirtualDeviceConfigSpec , ops ... types . BaseVirtualDeviceConfigSpec , ) [ ] types . BaseVirtualDeviceConfigSpec { for _ , op := range ops { c := copystructure . Must ( copystructure . Copy ( op ) ) . ( types . BaseVirtualDeviceConfigSpec ) spec = append ( spec , c ) } return spec }

// AppendDeviceChangeSpec appends unique copies of the supplied device change
// operations and appends them to spec. The resulting list is returned.
//
// The object of this function is to provide deep copies of each virtual device
// to the spec as they looked like when the append operation was called,
// helping facilitate multiple update operations to the same device in a single
// reconfigure call.

AppendDeviceChangeSpec appends unique copies of the supplied device change operations and appends them to spec . The resulting list is returned . The object of this function is to provide deep copies of each virtual device to the spec as they looked like when the append operation was called helping facilitate multiple update operations to the same device in a single reconfigure call .


func expandClusterDependencyRuleInfo(d *schema.ResourceData) (*types.ClusterDependencyRuleInfo, error) {
	obj := &types.ClusterDependencyRuleInfo{
		ClusterRuleInfo: types.ClusterRuleInfo{
			Enabled:     structure.GetBool(d, "enabled"),
			Mandatory:   structure.GetBool(d, "mandatory"),
			Name:        d.Get("name").(string),
			UserCreated: structure.BoolPtr(true),
		},
		DependsOnVmGroup: d.Get("dependency_vm_group_name").(string),
		VmGroup:          d.Get("vm_group_name").(string),
	}
	return obj, nil
}

func expandClusterDependencyRuleInfo ( d * schema . ResourceData ) ( * types . ClusterDependencyRuleInfo , error ) { obj := & types . ClusterDependencyRuleInfo { ClusterRuleInfo : types . ClusterRuleInfo { Enabled : structure . GetBool ( d , " " ) , Mandatory : structure . GetBool ( d , " " ) , Name : d . Get ( " " ) . ( string ) , UserCreated : structure . BoolPtr ( true ) , } , DependsOnVmGroup : d . Get ( " " ) . ( string ) , VmGroup : d . Get ( " " ) . ( string ) , } return obj , nil }

// expandClusterDependencyRuleInfo reads certain ResourceData keys and returns a
// ClusterDependencyRuleInfo.

expandClusterDependencyRuleInfo reads certain ResourceData keys and returns a ClusterDependencyRuleInfo .


func flattenClusterDependencyRuleInfo(d *schema.ResourceData, obj *types.ClusterDependencyRuleInfo) error {
	return structure.SetBatch(d, map[string]interface{}{
		"enabled":                  obj.Enabled,
		"mandatory":                obj.Mandatory,
		"name":                     obj.Name,
		"dependency_vm_group_name": obj.DependsOnVmGroup,
		"vm_group_name":            obj.VmGroup,
	})
}

func flattenClusterDependencyRuleInfo ( d * schema . ResourceData , obj * types . ClusterDependencyRuleInfo ) error { return structure . SetBatch ( d , map [ string ] interface { } { " " : obj . Enabled , " " : obj . Mandatory , " " : obj . Name , " " : obj . DependsOnVmGroup , " " : obj . VmGroup , } ) }

// flattenClusterDependencyRuleInfo saves a ClusterDependencyRuleInfo into the supplied ResourceData.

flattenClusterDependencyRuleInfo saves a ClusterDependencyRuleInfo into the supplied ResourceData .


func resourceVSphereComputeClusterVMDependencyRuleParseID(id string) (string, int32, error) {
	parts := strings.SplitN(id, ":", 3)
	if len(parts) < 2 {
		return "", 0, fmt.Errorf("bad ID %q", id)
	}

	key, err := strconv.Atoi(parts[1])
	if err != nil {
		return "", 0, fmt.Errorf("bad key in ID %q: %s", parts[1], err)
	}

	return parts[0], int32(key), nil
}

// resourceVSphereComputeClusterVMDependencyRuleParseID parses an ID for the
// vsphere_compute_cluster_vm_dependency_rule and outputs its parts.


func resourceVSphereComputeClusterVMDependencyRuleFetchObjects(
	meta interface{},
	clusterID string,
	key int32,
) (*object.ClusterComputeResource, int32, error) {
	client, err := resourceVSphereComputeClusterVMDependencyRuleClient(meta)
	if err != nil {
		return nil, 0, err
	}

	cluster, err := clustercomputeresource.FromID(client, clusterID)
	if err != nil {
		return nil, 0, fmt.Errorf("cannot locate cluster: %s", err)
	}

	return cluster, key, nil
}

// resourceVSphereComputeClusterVMDependencyRuleFetchObjects fetches the "objects"
// for a cluster rule. This is currently just the cluster object as the rule
// key a static value and a pass-through - this is to keep its workflow
// consistent with other cluster-dependent resources that derive from
// ArrayUpdateSpec that have managed object as keys, such as VM and host
// overrides.


func resourceVSphereDatastoreClusterApplyCreate(d *schema.ResourceData, meta interface{}) (*object.StoragePod, error) {
	log.Printf("[DEBUG] %s: Processing datastore cluster creation", resourceVSphereDatastoreClusterIDString(d))
	client := meta.(*VSphereClient).vimClient
	if err := viapi.ValidateVirtualCenter(client); err != nil {
		return nil, err
	}

	dc, err := datacenterFromID(client, d.Get("datacenter_id").(string))
	if err != nil {
		return nil, fmt.Errorf("cannot locate datacenter: %s", err)
	}

	// Find the folder based off the path to the datacenter. This is where we
	// create the datastore cluster.
	f, err := folder.FromPath(client, d.Get("folder").(string), folder.VSphereFolderTypeDatastore, dc)
	if err != nil {
		return nil, fmt.Errorf("cannot locate folder: %s", err)
	}

	// Create the storage pod (datastore cluster).
	pod, err := storagepod.Create(f, d.Get("name").(string))
	if err != nil {
		return nil, fmt.Errorf("error creating datastore cluster: %s", err)
	}

	// Set the ID now before proceeding with tags, custom attributes, and DRS.
	// This ensures that we can recover from a problem with any of these
	// operations.
	d.SetId(pod.Reference().Value)

	return pod, nil
}

// resourceVSphereDatastoreClusterApplyCreate processes the creation part of
// resourceVSphereDatastoreClusterCreate.


func resourceVSphereDatastoreClusterApplyTags(d *schema.ResourceData, meta interface{}, pod *object.StoragePod) error {
	tagsClient, err := tagsClientIfDefined(d, meta)
	if err != nil {
		return err
	}

	// Apply any pending tags now
	if tagsClient == nil {
		log.Printf("[DEBUG] %s: Tags unsupported on this connection, skipping", resourceVSphereDatastoreClusterIDString(d))
		return nil
	}

	log.Printf("[DEBUG] %s: Applying any pending tags", resourceVSphereDatastoreClusterIDString(d))
	return processTagDiff(tagsClient, d, pod)
}

// resourceVSphereDatastoreClusterApplyTags processes the tags step for both
// create and update for vsphere_datastore_cluster.


func resourceVSphereDatastoreClusterReadTags(d *schema.ResourceData, meta interface{}, pod *object.StoragePod) error {
	if tagsClient, _ := meta.(*VSphereClient).TagsClient(); tagsClient != nil {
		log.Printf("[DEBUG] %s: Reading tags", resourceVSphereDatastoreClusterIDString(d))
		if err := readTagsForResource(tagsClient, pod, d); err != nil {
			return err
		}
	} else {
		log.Printf("[DEBUG] %s: Tags unsupported on this connection, skipping tag read", resourceVSphereDatastoreClusterIDString(d))
	}
	return nil
}

func resourceVSphereDatastoreClusterReadTags ( d * schema . ResourceData , meta interface { } , pod * object . StoragePod ) error { if tagsClient , _ := meta . ( * VSphereClient ) . TagsClient ( ) ; tagsClient != nil { log . Printf ( " " , resourceVSphereDatastoreClusterIDString ( d ) ) if err := readTagsForResource ( tagsClient , pod , d ) ; err != nil { return err } } else { log . Printf ( " " , resourceVSphereDatastoreClusterIDString ( d ) ) } return nil }

// resourceVSphereDatastoreClusterReadTags reads the tags for
// vsphere_datastore_cluster.

resourceVSphereDatastoreClusterReadTags reads the tags for vsphere_datastore_cluster .


func resourceVSphereDatastoreClusterApplyCustomAttributes(d *schema.ResourceData, meta interface{}, pod *object.StoragePod) error {
	client := meta.(*VSphereClient).vimClient
	// Verify a proper vCenter before proceeding if custom attributes are defined
	attrsProcessor, err := customattribute.GetDiffProcessorIfAttributesDefined(client, d)
	if err != nil {
		return err
	}

	if attrsProcessor == nil {
		log.Printf("[DEBUG] %s: Custom attributes unsupported on this connection, skipping", resourceVSphereDatastoreClusterIDString(d))
		return nil
	}

	log.Printf("[DEBUG] %s: Applying any pending custom attributes", resourceVSphereDatastoreClusterIDString(d))
	return attrsProcessor.ProcessDiff(pod)
}

// resourceVSphereDatastoreClusterApplyCustomAttributes processes the custom
// attributes step for both create and update for vsphere_datastore_cluster.


func resourceVSphereDatastoreClusterReadCustomAttributes(d *schema.ResourceData, meta interface{}, pod *object.StoragePod) error {
	client := meta.(*VSphereClient).vimClient
	// Read custom attributes
	if customattribute.IsSupported(client) {
		log.Printf("[DEBUG] %s: Reading custom attributes", resourceVSphereDatastoreClusterIDString(d))
		props, err := storagepod.Properties(pod)
		if err != nil {
			return err
		}
		customattribute.ReadFromResource(client, props.Entity(), d)
	} else {
		log.Printf("[DEBUG] %s: Custom attributes unsupported on this connection, skipping", resourceVSphereDatastoreClusterIDString(d))
	}

	return nil
}

// resourceVSphereDatastoreClusterReadCustomAttributes reads the custom
// attributes for vsphere_datastore_cluster.


func resourceVSphereDatastoreClusterApplySDRSConfig(d *schema.ResourceData, meta interface{}, pod *object.StoragePod) error {
	// This is a no-op if there is no SDRS config changed
	if !resourceVSphereDatastoreClusterHasSDRSConfigChange(d) {
		log.Printf("[DEBUG] %s: No SDRS configuration attributes have changed", resourceVSphereDatastoreClusterIDString(d))
		return nil
	}

	log.Printf("[DEBUG] %s: Applying SDRS configuration", resourceVSphereDatastoreClusterIDString(d))
	client := meta.(*VSphereClient).vimClient
	if err := viapi.ValidateVirtualCenter(client); err != nil {
		return err
	}

	// Get the version of the vSphere connection to help determine what
	// attributes we need to set
	version := viapi.ParseVersionFromClient(client)

	// Expand the SDRS configuration.
	spec := types.StorageDrsConfigSpec{
		PodConfigSpec: expandStorageDrsPodConfigSpec(d, version),
	}

	return storagepod.ApplyDRSConfiguration(client, pod, spec)
}

// resourceVSphereDatastoreClusterApplySDRSConfig applies the SDRS
// configuration to a datastore cluster.


func resourceVSphereDatastoreClusterHasSDRSConfigChange(d *schema.ResourceData) bool {
	for k := range resourceVSphereDatastoreCluster().Schema {
		switch {
		case resourceVSphereDatastoreClusterHasSDRSConfigChangeExcluded(k):
			continue
		case d.HasChange(k):
			return true
		}
	}

	return false
}

// resourceVSphereDatastoreClusterHasSDRSConfigChange checks all resource keys
// associated with storage DRS to see if there has been a change in the
// configuration of those keys. This helper is designed to detect no-ops in a
// SDRS configuration to see if we really need to send a configure API call to
// vSphere.


func resourceVSphereDatastoreClusterGetPod(d structure.ResourceIDStringer, meta interface{}) (*object.StoragePod, error) {
	log.Printf("[DEBUG] %s: Fetching StoragePod object from resource ID", resourceVSphereDatastoreClusterIDString(d))
	client := meta.(*VSphereClient).vimClient
	if err := viapi.ValidateVirtualCenter(client); err != nil {
		return nil, err
	}

	return storagepod.FromID(client, d.Id())
}

func resourceVSphereDatastoreClusterGetPod ( d structure . ResourceIDStringer , meta interface { } ) ( * object . StoragePod , error ) { log . Printf ( " " , resourceVSphereDatastoreClusterIDString ( d ) ) client := meta . ( * VSphereClient ) . vimClient if err := viapi . ValidateVirtualCenter ( client ) ; err != nil { return nil , err } return storagepod . FromID ( client , d . Id ( ) ) }

// resourceVSphereDatastoreClusterGetPod gets the StoragePod from the ID
// in the supplied ResourceData.

resourceVSphereDatastoreClusterGetPod gets the StoragePod from the ID in the supplied ResourceData .


func resourceVSphereDatastoreClusterGetPodFromPath(meta interface{}, path string, dcID string) (*object.StoragePod, error) {
	client := meta.(*VSphereClient).vimClient
	var dc *object.Datacenter
	if dcID != "" {
		var err error
		dc, err = datacenterFromID(client, dcID)
		if err != nil {
			return nil, fmt.Errorf("cannot locate datacenter: %s", err)
		}
		log.Printf("[DEBUG] Looking for datastore cluster %q in datacenter %q", path, dc.InventoryPath)
	} else {
		log.Printf("[DEBUG] Fetching datastore cluster at path %q", path)
	}
	if err := viapi.ValidateVirtualCenter(client); err != nil {
		return nil, err
	}

	return storagepod.FromPath(client, path, dc)
}

// resourceVSphereDatastoreClusterGetPodFromPath gets the StoragePod from a
// supplied path. If no datacenter is supplied, the path must be a full path.


func resourceVSphereDatastoreClusterSaveNameAndPath(d *schema.ResourceData, pod *object.StoragePod) error {
	log.Printf(
		"[DEBUG] %s: Saving name and path data for datastore cluster %q",
		resourceVSphereDatastoreClusterIDString(d),
		pod.InventoryPath,
	)

	if err := d.Set("name", pod.Name()); err != nil {
		return fmt.Errorf("error saving name: %s", err)
	}

	f, err := folder.RootPathParticleDatastore.SplitRelativeFolder(pod.InventoryPath)
	if err != nil {
		return fmt.Errorf("error parsing datastore cluster path %q: %s", pod.InventoryPath, err)
	}
	if err := d.Set("folder", folder.NormalizePath(f)); err != nil {
		return fmt.Errorf("error saving folder: %s", err)
	}
	return nil
}

// resourceVSphereDatastoreClusterSaveNameAndPath saves the name and path of a
// StoragePod into the supplied ResourceData.


func resourceVSphereDatastoreClusterApplyNameChange(
	d *schema.ResourceData,
	meta interface{},
	pod *object.StoragePod,
) (*object.StoragePod, error) {
	log.Printf(
		"[DEBUG] %s: Applying any name changes (old path = %q)",
		resourceVSphereDatastoreClusterIDString(d),
		pod.InventoryPath,
	)

	var changed bool
	var err error

	if d.HasChange("name") {
		if err = storagepod.Rename(pod, d.Get("name").(string)); err != nil {
			return nil, fmt.Errorf("error renaming datastore cluster: %s", err)
		}
		changed = true
	}

	if changed {
		// Update the pod so that we have the new inventory path for logging and
		// other things
		pod, err = resourceVSphereDatastoreClusterGetPod(d, meta)
		if err != nil {
			return nil, fmt.Errorf("error refreshing datastore cluster after name change: %s", err)
		}
		log.Printf(
			"[DEBUG] %s: Name changed, new path = %q",
			resourceVSphereDatastoreClusterIDString(d),
			pod.InventoryPath,
		)
	}

	return pod, nil
}

// resourceVSphereDatastoreClusterApplyNameChange applies any changes to a
// StoragePod's name.


func resourceVSphereDatastoreClusterApplyFolderChange(
	d *schema.ResourceData,
	meta interface{},
	pod *object.StoragePod,
) (*object.StoragePod, error) {
	log.Printf(
		"[DEBUG] %s: Applying any folder changes (old path = %q)",
		resourceVSphereDatastoreClusterIDString(d),
		pod.InventoryPath,
	)

	var changed bool
	var err error

	if d.HasChange("folder") {
		f := d.Get("folder").(string)
		client := meta.(*VSphereClient).vimClient
		if err = storagepod.MoveToFolder(client, pod, f); err != nil {
			return nil, fmt.Errorf("could not move datastore cluster to folder %q: %s", f, err)
		}
		changed = true
	}

	if changed {
		// Update the pod so that we have the new inventory path for logging and
		// other things
		pod, err = resourceVSphereDatastoreClusterGetPod(d, meta)
		if err != nil {
			return nil, fmt.Errorf("error refreshing pod after folder change: %s", err)
		}
		log.Printf(
			"[DEBUG] %s: Folder changed, new path = %q",
			resourceVSphereDatastoreClusterIDString(d),
			pod.InventoryPath,
		)
	}

	return pod, nil
}

// resourceVSphereDatastoreClusterApplyFolderChange applies any changes to a
// StoragePod's folder location.


func resourceVSphereDatastoreClusterValidateEmptyCluster(d structure.ResourceIDStringer, pod *object.StoragePod) error {
	log.Printf("[DEBUG] %s: Checking to ensure that datastore cluster is empty", resourceVSphereDatastoreClusterIDString(d))
	ne, err := storagepod.HasChildren(pod)
	if err != nil {
		return fmt.Errorf("error checking for datastore cluster contents: %s", err)
	}
	if ne {
		return fmt.Errorf(
			"datastore cluster %q still has datastores. Please move or remove all datastores before deleting",
			pod.InventoryPath,
		)
	}
	return nil
}

// resourceVSphereDatastoreClusterValidateEmptyCluster validates that the
// datastore cluster is empty. This is used to ensure a safe deletion of the
// cluster - we do not allow deletion of clusters that still have datastores in
// them.


func resourceVSphereDatastoreClusterApplyDelete(d *schema.ResourceData, pod *object.StoragePod) error {
	log.Printf("[DEBUG] %s: Proceeding with datastore cluster deletion", resourceVSphereDatastoreClusterIDString(d))
	if err := storagepod.Delete(pod); err != nil {
		return err
	}
	d.SetId("")
	return nil
}

// resourceVSphereDatastoreClusterApplyDelete process the removal of a
// datastore cluster.


func resourceVSphereDatastoreClusterFlattenSDRSData(d *schema.ResourceData, meta interface{}, pod *object.StoragePod) error {
	log.Printf("[DEBUG] %s: Saving datastore cluster attributes", resourceVSphereDatastoreClusterIDString(d))
	client := meta.(*VSphereClient).vimClient
	if err := viapi.ValidateVirtualCenter(client); err != nil {
		return err
	}

	// Get the version of the vSphere connection to help determine what
	// attributes we need to set
	version := viapi.ParseVersionFromClient(client)

	props, err := storagepod.Properties(pod)
	if err != nil {
		return err
	}

	return flattenStorageDrsPodConfigInfo(d, props.PodStorageDrsEntry.StorageDrsConfig.PodConfig, version)
}

// resourceVSphereDatastoreClusterFlattenSDRSData saves the DRS attributes from
// a StoragePod into the supplied ResourceData.
//
// Note that other functions handle non-SDRS related items, such as path, name,
// tags, and custom attributes.


func expandStorageDrsPodConfigSpec(d *schema.ResourceData, version viapi.VSphereVersion) *types.StorageDrsPodConfigSpec {
	obj := &types.StorageDrsPodConfigSpec{
		DefaultIntraVmAffinity: structure.GetBool(d, "sdrs_default_intra_vm_affinity"),
		DefaultVmBehavior:      d.Get("sdrs_automation_level").(string),
		Enabled:                structure.GetBool(d, "sdrs_enabled"),
		IoLoadBalanceConfig:    expandStorageDrsIoLoadBalanceConfig(d, version),
		IoLoadBalanceEnabled:   structure.GetBool(d, "sdrs_io_load_balance_enabled"),
		LoadBalanceInterval:    int32(d.Get("sdrs_load_balance_interval").(int)),
		SpaceLoadBalanceConfig: expandStorageDrsSpaceLoadBalanceConfig(d, version),
		Option:                 expandStorageDrsOptionSpec(d),
	}

	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6}) {
		obj.AutomationOverrides = expandStorageDrsAutomationConfig(d)
	}

	return obj
}

func expandStorageDrsPodConfigSpec ( d * schema . ResourceData , version viapi . VSphereVersion ) * types . StorageDrsPodConfigSpec { obj := & types . StorageDrsPodConfigSpec { DefaultIntraVmAffinity : structure . GetBool ( d , " " ) , DefaultVmBehavior : d . Get ( " " ) . ( string ) , Enabled : structure . GetBool ( d , " " ) , IoLoadBalanceConfig : expandStorageDrsIoLoadBalanceConfig ( d , version ) , IoLoadBalanceEnabled : structure . GetBool ( d , " " ) , LoadBalanceInterval : int32 ( d . Get ( " " ) . ( int ) ) , SpaceLoadBalanceConfig : expandStorageDrsSpaceLoadBalanceConfig ( d , version ) , Option : expandStorageDrsOptionSpec ( d ) , } if version . Newer ( viapi . VSphereVersion { Product : version . Product , Major : 6 } ) { obj . AutomationOverrides = expandStorageDrsAutomationConfig ( d ) } return obj }

// expandStorageDrsPodConfigSpec reads certain ResourceData keys and returns a
// StorageDrsPodConfigSpec.

expandStorageDrsPodConfigSpec reads certain ResourceData keys and returns a StorageDrsPodConfigSpec .


func flattenStorageDrsPodConfigInfo(d *schema.ResourceData, obj types.StorageDrsPodConfigInfo, version viapi.VSphereVersion) error {
	attrs := map[string]interface{}{
		"sdrs_default_intra_vm_affinity": obj.DefaultIntraVmAffinity,
		"sdrs_automation_level":          obj.DefaultVmBehavior,
		"sdrs_enabled":                   obj.Enabled,
		"sdrs_io_load_balance_enabled":   obj.IoLoadBalanceEnabled,
		"sdrs_load_balance_interval":     obj.LoadBalanceInterval,
	}
	for k, v := range attrs {
		if err := d.Set(k, v); err != nil {
			return fmt.Errorf("error setting attribute %q: %s", k, err)
		}
	}

	if err := flattenStorageDrsIoLoadBalanceConfig(d, obj.IoLoadBalanceConfig, version); err != nil {
		return err
	}

	if err := flattenStorageDrsSpaceLoadBalanceConfig(d, obj.SpaceLoadBalanceConfig, version); err != nil {
		return err
	}

	if err := flattenStorageDrsOptionSpec(d, obj.Option); err != nil {
		return err
	}

	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6}) {
		if err := flattenStorageDrsAutomationConfig(d, obj.AutomationOverrides); err != nil {
			return err
		}
	}

	return nil
}

func flattenStorageDrsPodConfigInfo ( d * schema . ResourceData , obj types . StorageDrsPodConfigInfo , version viapi . VSphereVersion ) error { attrs := map [ string ] interface { } { " " : obj . DefaultIntraVmAffinity , " " : obj . DefaultVmBehavior , " " : obj . Enabled , " " : obj . IoLoadBalanceEnabled , " " : obj . LoadBalanceInterval , } for k , v := range attrs { if err := d . Set ( k , v ) ; err != nil { return fmt . Errorf ( " " , k , err ) } } if err := flattenStorageDrsIoLoadBalanceConfig ( d , obj . IoLoadBalanceConfig , version ) ; err != nil { return err } if err := flattenStorageDrsSpaceLoadBalanceConfig ( d , obj . SpaceLoadBalanceConfig , version ) ; err != nil { return err } if err := flattenStorageDrsOptionSpec ( d , obj . Option ) ; err != nil { return err } if version . Newer ( viapi . VSphereVersion { Product : version . Product , Major : 6 } ) { if err := flattenStorageDrsAutomationConfig ( d , obj . AutomationOverrides ) ; err != nil { return err } } return nil }

// flattenStorageDrsPodConfigInfo saves a StorageDrsPodConfigInfo into the
// supplied ResourceData.

flattenStorageDrsPodConfigInfo saves a StorageDrsPodConfigInfo into the supplied ResourceData .


func expandStorageDrsAutomationConfig(d *schema.ResourceData) *types.StorageDrsAutomationConfig {
	obj := &types.StorageDrsAutomationConfig{
		IoLoadBalanceAutomationMode:     d.Get("sdrs_io_balance_automation_level").(string),
		PolicyEnforcementAutomationMode: d.Get("sdrs_policy_enforcement_automation_level").(string),
		RuleEnforcementAutomationMode:   d.Get("sdrs_rule_enforcement_automation_level").(string),
		SpaceLoadBalanceAutomationMode:  d.Get("sdrs_space_balance_automation_level").(string),
		VmEvacuationAutomationMode:      d.Get("sdrs_vm_evacuation_automation_level").(string),
	}
	return obj
}

func expandStorageDrsAutomationConfig ( d * schema . ResourceData ) * types . StorageDrsAutomationConfig { obj := & types . StorageDrsAutomationConfig { IoLoadBalanceAutomationMode : d . Get ( " " ) . ( string ) , PolicyEnforcementAutomationMode : d . Get ( " " ) . ( string ) , RuleEnforcementAutomationMode : d . Get ( " " ) . ( string ) , SpaceLoadBalanceAutomationMode : d . Get ( " " ) . ( string ) , VmEvacuationAutomationMode : d . Get ( " " ) . ( string ) , } return obj }

// expandStorageDrsAutomationConfig reads certain ResourceData keys and returns
// a StorageDrsAutomationConfig.

expandStorageDrsAutomationConfig reads certain ResourceData keys and returns a StorageDrsAutomationConfig .


func flattenStorageDrsAutomationConfig(d *schema.ResourceData, obj *types.StorageDrsAutomationConfig) error {
	attrs := map[string]interface{}{
		"sdrs_io_balance_automation_level":         obj.IoLoadBalanceAutomationMode,
		"sdrs_policy_enforcement_automation_level": obj.PolicyEnforcementAutomationMode,
		"sdrs_rule_enforcement_automation_level":   obj.RuleEnforcementAutomationMode,
		"sdrs_space_balance_automation_level":      obj.SpaceLoadBalanceAutomationMode,
		"sdrs_vm_evacuation_automation_level":      obj.VmEvacuationAutomationMode,
	}
	for k, v := range attrs {
		if err := d.Set(k, v); err != nil {
			return fmt.Errorf("error setting attribute %q: %s", k, err)
		}
	}
	return nil
}

func flattenStorageDrsAutomationConfig ( d * schema . ResourceData , obj * types . StorageDrsAutomationConfig ) error { attrs := map [ string ] interface { } { " " : obj . IoLoadBalanceAutomationMode , " " : obj . PolicyEnforcementAutomationMode , " " : obj . RuleEnforcementAutomationMode , " " : obj . SpaceLoadBalanceAutomationMode , " " : obj . VmEvacuationAutomationMode , } for k , v := range attrs { if err := d . Set ( k , v ) ; err != nil { return fmt . Errorf ( " " , k , err ) } } return nil }

// flattenStorageDrsAutomationConfig saves a StorageDrsAutomationConfig into
// the supplied ResourceData.

flattenStorageDrsAutomationConfig saves a StorageDrsAutomationConfig into the supplied ResourceData .


func expandStorageDrsIoLoadBalanceConfig(d *schema.ResourceData, version viapi.VSphereVersion) *types.StorageDrsIoLoadBalanceConfig {
	obj := &types.StorageDrsIoLoadBalanceConfig{
		IoLatencyThreshold:       int32(d.Get("sdrs_io_latency_threshold").(int)),
		IoLoadImbalanceThreshold: int32(d.Get("sdrs_io_load_imbalance_threshold").(int)),
	}

	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6}) {
		obj.ReservableIopsThreshold = int32(d.Get("sdrs_io_reservable_iops_threshold").(int))
		obj.ReservablePercentThreshold = int32(d.Get("sdrs_io_reservable_percent_threshold").(int))
		obj.ReservableThresholdMode = d.Get("sdrs_io_reservable_threshold_mode").(string)
	}

	return obj
}

func expandStorageDrsIoLoadBalanceConfig ( d * schema . ResourceData , version viapi . VSphereVersion ) * types . StorageDrsIoLoadBalanceConfig { obj := & types . StorageDrsIoLoadBalanceConfig { IoLatencyThreshold : int32 ( d . Get ( " " ) . ( int ) ) , IoLoadImbalanceThreshold : int32 ( d . Get ( " " ) . ( int ) ) , } if version . Newer ( viapi . VSphereVersion { Product : version . Product , Major : 6 } ) { obj . ReservableIopsThreshold = int32 ( d . Get ( " " ) . ( int ) ) obj . ReservablePercentThreshold = int32 ( d . Get ( " " ) . ( int ) ) obj . ReservableThresholdMode = d . Get ( " " ) . ( string ) } return obj }

// expandStorageDrsIoLoadBalanceConfig reads certain ResourceData keys and returns
// a StorageDrsIoLoadBalanceConfig.

expandStorageDrsIoLoadBalanceConfig reads certain ResourceData keys and returns a StorageDrsIoLoadBalanceConfig .


func flattenStorageDrsIoLoadBalanceConfig(
	d *schema.ResourceData,
	obj *types.StorageDrsIoLoadBalanceConfig,
	version viapi.VSphereVersion,
) error {
	attrs := map[string]interface{}{
		"sdrs_io_latency_threshold":        obj.IoLatencyThreshold,
		"sdrs_io_load_imbalance_threshold": obj.IoLoadImbalanceThreshold,
	}
	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6}) {
		attrs["sdrs_io_reservable_threshold_mode"] = obj.ReservableThresholdMode
		if obj.ReservableThresholdMode == string(types.StorageDrsPodConfigInfoBehaviorManual) {
			attrs["sdrs_io_reservable_iops_threshold"] = obj.ReservableIopsThreshold
		} else {
			attrs["sdrs_io_reservable_percent_threshold"] = obj.ReservablePercentThreshold
		}
	}

	for k, v := range attrs {
		if err := d.Set(k, v); err != nil {
			return fmt.Errorf("error setting attribute %q: %s", k, err)
		}
	}

	return nil
}

func flattenStorageDrsIoLoadBalanceConfig ( d * schema . ResourceData , obj * types . StorageDrsIoLoadBalanceConfig , version viapi . VSphereVersion , ) error { attrs := map [ string ] interface { } { " " : obj . IoLatencyThreshold , " " : obj . IoLoadImbalanceThreshold , } if version . Newer ( viapi . VSphereVersion { Product : version . Product , Major : 6 } ) { attrs [ " " ] = obj . ReservableThresholdMode if obj . ReservableThresholdMode == string ( types . StorageDrsPodConfigInfoBehaviorManual ) { attrs [ " " ] = obj . ReservableIopsThreshold } else { attrs [ " " ] = obj . ReservablePercentThreshold } } for k , v := range attrs { if err := d . Set ( k , v ) ; err != nil { return fmt . Errorf ( " " , k , err ) } } return nil }

// flattenStorageDrsIoLoadBalanceConfig saves a StorageDrsIoLoadBalanceConfig
// into the supplied ResourceData.

flattenStorageDrsIoLoadBalanceConfig saves a StorageDrsIoLoadBalanceConfig into the supplied ResourceData .


func expandStorageDrsSpaceLoadBalanceConfig(
	d *schema.ResourceData,
	version viapi.VSphereVersion,
) *types.StorageDrsSpaceLoadBalanceConfig {
	obj := &types.StorageDrsSpaceLoadBalanceConfig{
		MinSpaceUtilizationDifference: int32(d.Get("sdrs_free_space_utilization_difference").(int)),
		SpaceUtilizationThreshold:     int32(d.Get("sdrs_space_utilization_threshold").(int)),
	}

	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6}) {
		obj.FreeSpaceThresholdGB = int32(d.Get("sdrs_free_space_threshold").(int))
		obj.SpaceThresholdMode = d.Get("sdrs_free_space_threshold_mode").(string)
	}

	return obj
}

func expandStorageDrsSpaceLoadBalanceConfig ( d * schema . ResourceData , version viapi . VSphereVersion , ) * types . StorageDrsSpaceLoadBalanceConfig { obj := & types . StorageDrsSpaceLoadBalanceConfig { MinSpaceUtilizationDifference : int32 ( d . Get ( " " ) . ( int ) ) , SpaceUtilizationThreshold : int32 ( d . Get ( " " ) . ( int ) ) , } if version . Newer ( viapi . VSphereVersion { Product : version . Product , Major : 6 } ) { obj . FreeSpaceThresholdGB = int32 ( d . Get ( " " ) . ( int ) ) obj . SpaceThresholdMode = d . Get ( " " ) . ( string ) } return obj }

// expandStorageDrsSpaceLoadBalanceConfig reads certain ResourceData keys and returns
// a StorageDrsSpaceLoadBalanceConfig.

expandStorageDrsSpaceLoadBalanceConfig reads certain ResourceData keys and returns a StorageDrsSpaceLoadBalanceConfig .


func flattenStorageDrsSpaceLoadBalanceConfig(
	d *schema.ResourceData,
	obj *types.StorageDrsSpaceLoadBalanceConfig,
	version viapi.VSphereVersion,
) error {
	attrs := map[string]interface{}{
		"sdrs_free_space_utilization_difference": obj.MinSpaceUtilizationDifference,
		"sdrs_free_space_threshold_mode":         obj.SpaceThresholdMode,
	}

	freeSpaceSupported := version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6})
	if freeSpaceSupported && obj.SpaceThresholdMode == string(types.StorageDrsSpaceLoadBalanceConfigSpaceThresholdModeFreeSpace) {
		attrs["sdrs_free_space_threshold"] = obj.FreeSpaceThresholdGB
	} else {
		attrs["sdrs_space_utilization_threshold"] = obj.SpaceUtilizationThreshold
	}

	for k, v := range attrs {
		if err := d.Set(k, v); err != nil {
			return fmt.Errorf("error setting attribute %q: %s", k, err)
		}
	}

	return nil
}

// flattenStorageDrsSpaceLoadBalanceConfig saves a
// StorageDrsSpaceLoadBalanceConfig into the supplied ResourceData.

flattenStorageDrsSpaceLoadBalanceConfig saves a StorageDrsSpaceLoadBalanceConfig into the supplied ResourceData .


func expandStorageDrsOptionSpec(d *schema.ResourceData) []types.StorageDrsOptionSpec {
	var opts []types.StorageDrsOptionSpec

	m := d.Get("sdrs_advanced_options").(map[string]interface{})
	for k, v := range m {
		opts = append(opts, types.StorageDrsOptionSpec{
			Option: &types.OptionValue{
				Key:   k,
				Value: types.AnyType(v),
			},
		})
	}
	return opts
}

func expandStorageDrsOptionSpec ( d * schema . ResourceData ) [ ] types . StorageDrsOptionSpec { var opts [ ] types . StorageDrsOptionSpec m := d . Get ( " " ) . ( map [ string ] interface { } ) for k , v := range m { opts = append ( opts , types . StorageDrsOptionSpec { Option : & types . OptionValue { Key : k , Value : types . AnyType ( v ) , } , } ) } return opts }

// expandStorageDrsOptionSpec reads certain ResourceData keys and returns
// a StorageDrsOptionSpec.

expandStorageDrsOptionSpec reads certain ResourceData keys and returns a StorageDrsOptionSpec .


func flattenStorageDrsOptionSpec(d *schema.ResourceData, opts []types.BaseOptionValue) error {
	m := make(map[string]interface{})
	for _, opt := range opts {
		m[opt.GetOptionValue().Key] = opt.GetOptionValue().Value
	}

	return d.Set("sdrs_advanced_options", m)
}

func flattenStorageDrsOptionSpec ( d * schema . ResourceData , opts [ ] types . BaseOptionValue ) error { m := make ( map [ string ] interface { } ) for _ , opt := range opts { m [ opt . GetOptionValue ( ) . Key ] = opt . GetOptionValue ( ) . Value } return d . Set ( " " , m ) }

// flattenStorageDrsOptionSpec saves a StorageDrsOptionSpec into the supplied
// ResourceData.

flattenStorageDrsOptionSpec saves a StorageDrsOptionSpec into the supplied ResourceData .


func resourceVSphereFolderMigrateState(version int, os *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
	// Guard against a nil state.
	if os == nil {
		return nil, nil
	}

	// Guard against empty state, can't do anything with it
	if os.Empty() {
		return os, nil
	}

	var migrateFunc func(*terraform.InstanceState, interface{}) error
	switch version {
	case 0:
		log.Printf("[DEBUG] Migrating vsphere_folder state: old v%d state: %#v", version, os)
		migrateFunc = resourceVSphereFolderMigrateStateV1
	default:
		// Migration is complete
		log.Printf("[DEBUG] Migrating vsphere_folder state: completed v%d state: %#v", version, os)
		return os, nil
	}
	if err := migrateFunc(os, meta); err != nil {
		return nil, err
	}
	version++
	log.Printf("[DEBUG] Migrating vsphere_folder state: new v%d state: %#v", version, os)
	return resourceVSphereFolderMigrateState(version, os, meta)
}

// resourceVSphereFolderMigrateState is the master state migration function for
// the vsphere_folder resource.

resourceVSphereFolderMigrateState is the master state migration function for the vsphere_folder resource .


func resourceVSphereFolderMigrateStateV1(s *terraform.InstanceState, meta interface{}) error {
	// Our path for migration here is pretty much the same as our import path, so
	// we just leverage that functionality.
	//
	// We just need the path and the datacenter to proceed. We don't have an
	// analog in for existing_path in the new resource, so we just drop that on
	// the floor.
	dcp := folder.NormalizePath(s.Attributes["datacenter"])
	p := folder.NormalizePath(s.Attributes["path"])

	// Discover our datacenter first. This field can be empty, so we have to
	// search for it as we normally would.
	client := meta.(*VSphereClient).vimClient
	dc, err := getDatacenter(client, dcp)
	if err != nil {
		return err
	}

	// The old resource only supported VM folders, so this part is easy enough,
	// we can derive our full path by combining the VM path particle and our
	// relative path.
	fp := folder.RootPathParticleVM.PathFromDatacenter(dc, p)
	folder, err := folder.FromAbsolutePath(client, fp)
	if err != nil {
		return err
	}

	// We got our folder!
	//
	// Read will handle everything except for the ID, so just wipe attributes,
	// update the ID, and let read take care of the rest.
	s.Attributes = make(map[string]string)
	s.ID = folder.Reference().Value

	return nil
}

// resourceVSphereFolderMigrateStateV1 migrates the state of the vsphere_folder
// from version 0 to version 1.

resourceVSphereFolderMigrateStateV1 migrates the state of the vsphere_folder from version 0 to version 1 .


func getDatacenter(c *govmomi.Client, dc string) (*object.Datacenter, error) {
	finder := find.NewFinder(c.Client, true)
	t := c.ServiceContent.About.ApiType
	switch t {
	case "HostAgent":
		return finder.DefaultDatacenter(context.TODO())
	case "VirtualCenter":
		if dc != "" {
			return finder.Datacenter(context.TODO(), dc)
		}
		return finder.DefaultDatacenter(context.TODO())
	}
	return nil, fmt.Errorf("unsupported ApiType: %s", t)
}

func getDatacenter ( c * govmomi . Client , dc string ) ( * object . Datacenter , error ) { finder := find . NewFinder ( c . Client , true ) t := c . ServiceContent . About . ApiType switch t { case " " : return finder . DefaultDatacenter ( context . TODO ( ) ) case " " : if dc != " " { return finder . Datacenter ( context . TODO ( ) , dc ) } return finder . DefaultDatacenter ( context . TODO ( ) ) } return nil , fmt . Errorf ( " " , t ) }

// getDatacenter gets the higher-level datacenter object for the datacenter
// name supplied by dc.
//
// The default datacenter is denoted by using an empty string. When working
// with ESXi directly, the default datacenter is always selected.

getDatacenter gets the higher - level datacenter object for the datacenter name supplied by dc . The default datacenter is denoted by using an empty string . When working with ESXi directly the default datacenter is always selected .


func datacenterFromID(client *govmomi.Client, id string) (*object.Datacenter, error) {
	finder := find.NewFinder(client.Client, false)

	ref := types.ManagedObjectReference{
		Type:  "Datacenter",
		Value: id,
	}

	ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout)
	defer cancel()
	ds, err := finder.ObjectReference(ctx, ref)
	if err != nil {
		return nil, fmt.Errorf("could not find datacenter with id: %s: %s", id, err)
	}
	return ds.(*object.Datacenter), nil
}

func datacenterFromID ( client * govmomi . Client , id string ) ( * object . Datacenter , error ) { finder := find . NewFinder ( client . Client , false ) ref := types . ManagedObjectReference { Type : " " , Value : id , } ctx , cancel := context . WithTimeout ( context . Background ( ) , defaultAPITimeout ) defer cancel ( ) ds , err := finder . ObjectReference ( ctx , ref ) if err != nil { return nil , fmt . Errorf ( " " , id , err ) } return ds . ( * object . Datacenter ) , nil }

// datacenterFromID locates a Datacenter by its managed object reference ID.

datacenterFromID locates a Datacenter by its managed object reference ID .


func NewDiskSubresource(client *govmomi.Client, rdd resourceDataDiff, d, old map[string]interface{}, idx int) *DiskSubresource {
	sr := &DiskSubresource{
		Subresource: &Subresource{
			schema:  DiskSubresourceSchema(),
			client:  client,
			srtype:  subresourceTypeDisk,
			data:    d,
			olddata: old,
			rdd:     rdd,
		},
	}
	sr.Index = idx
	return sr
}

func NewDiskSubresource ( client * govmomi . Client , rdd resourceDataDiff , d , old map [ string ] interface { } , idx int ) * DiskSubresource { sr := & DiskSubresource { Subresource : & Subresource { schema : DiskSubresourceSchema ( ) , client : client , srtype : subresourceTypeDisk , data : d , olddata : old , rdd : rdd , } , } sr . Index = idx return sr }

// NewDiskSubresource returns a subresource populated with all of the necessary
// fields.

NewDiskSubresource returns a subresource populated with all of the necessary fields .


func DiskApplyOperation(d *schema.ResourceData, c *govmomi.Client, l object.VirtualDeviceList) (object.VirtualDeviceList, []types.BaseVirtualDeviceConfigSpec, error) {
	log.Printf("[DEBUG] DiskApplyOperation: Beginning apply operation")
	o, n := d.GetChange(subresourceTypeDisk)
	ods := o.([]interface{})
	nds := n.([]interface{})

	var spec []types.BaseVirtualDeviceConfigSpec

	// Our old and new sets now have an accurate description of devices that may
	// have been added, removed, or changed. Look for removed devices first.
	log.Printf("[DEBUG] DiskApplyOperation: Looking for resources to delete")
	for oi, oe := range ods {
		om := oe.(map[string]interface{})
		if err := diskApplyOperationDelete(oi, om, nds, c, d, &l, &spec); err != nil {
			return nil, nil, err
		}
	}

	// Now check for creates and updates.  The results of this operation are
	// committed to state after the operation completes, on top of the items that
	// have not changed.
	var updates []interface{}
	log.Printf("[DEBUG] DiskApplyOperation: Looking for resources to create or update")
	log.Printf("[DEBUG] DiskApplyOperation: Resources not being changed: %s", subresourceListString(updates))
	for ni, ne := range nds {
		nm := ne.(map[string]interface{})
		if err := diskApplyOperationCreateUpdate(ni, nm, ods, c, d, &l, &spec, &updates); err != nil {
			return nil, nil, err
		}
	}

	log.Printf("[DEBUG] DiskApplyOperation: Post-apply final resource list: %s", subresourceListString(updates))
	// We are now done! Return the updated device list and config spec. Save updates as well.
	if err := d.Set(subresourceTypeDisk, updates); err != nil {
		return nil, nil, err
	}
	log.Printf("[DEBUG] DiskApplyOperation: Device list at end of operation: %s", DeviceListString(l))
	log.Printf("[DEBUG] DiskApplyOperation: Device config operations from apply: %s", DeviceChangeString(spec))
	log.Printf("[DEBUG] DiskApplyOperation: Apply complete, returning updated spec")
	return l, spec, nil
}

// DiskApplyOperation processes an apply operation for all disks in the
// resource.
//
// The function takes the root resource's ResourceData, the provider
// connection, and the device list as known to vSphere at the start of this
// operation. All disk operations are carried out, with both the complete,
// updated, VirtualDeviceList, and the complete list of changes returned as a
// slice of BaseVirtualDeviceConfigSpec.

DiskApplyOperation processes an apply operation for all disks in the resource . The function takes the root resource s ResourceData the provider connection and the device list as known to vSphere at the start of this operation . All disk operations are carried out with both the complete updated VirtualDeviceList and the complete list of changes returned as a slice of BaseVirtualDeviceConfigSpec .


func diskApplyOperationDelete(
	index int,
	oldData map[string]interface{},
	newDataSet []interface{},
	c *govmomi.Client,
	d *schema.ResourceData,
	l *object.VirtualDeviceList,
	spec *[]types.BaseVirtualDeviceConfigSpec,
) error {
	didx := -1
	for ni, ne := range newDataSet {
		newData := ne.(map[string]interface{})
		var name string
		var err error
		if name, err = diskLabelOrName(newData); err != nil {
			return err
		}
		if (name == diskDeletedName || name == diskDetachedName) && oldData["uuid"] == newData["uuid"] {
			didx = ni
			break
		}
	}
	if didx < 0 {
		// Deleted entry not found
		return nil
	}
	r := NewDiskSubresource(c, d, oldData, nil, index)
	dspec, err := r.Delete(*l)
	if err != nil {
		return fmt.Errorf("%s: %s", r.Addr(), err)
	}
	*l = applyDeviceChange(*l, dspec)
	*spec = append(*spec, dspec...)
	return nil
}

func
diskApplyOperationDelete
(
index
int
,
oldData
map
[
string
]
interface
{
}
,
newDataSet
[
]
interface
{
}
,
c
*
govmomi
.
Client
,
d
*
schema
.
ResourceData
,
l
*
object
.
VirtualDeviceList
,
spec
*
[
]
types
.
BaseVirtualDeviceConfigSpec
,
)
error
{
didx
:=
1
for
ni
,
ne
:=
range
newDataSet
{
newData
:=
ne
.
(
map
[
string
]
interface
{
}
)
var
name
string
var
err
error
if
name
,
err
=
diskLabelOrName
(
newData
)
;
err
!=
{
return
err
}
if
(
name
==
diskDeletedName
||
name
==
diskDetachedName
)
&&
oldData
[
"
"
]
==
newData
[
"
"
]
{
didx
=
ni
break
}
}
if
didx
<
0
{
// Deleted entry not found
return
}
r
:=
NewDiskSubresource
(
c
,
d
,
oldData
,
,
index
)
dspec
,
err
:=
r
.
Delete
(
*
l
)
if
err
!=
{
return
fmt
.
Errorf
(
"
"
,
r
.
Addr
(
)
,
err
)
}
*
l
=
applyDeviceChange
(
*
l
,
dspec
)
*
spec
=
append
(
*
spec
,
dspec
...
)
return
}

// diskApplyOperationDelete is an inner-loop helper for disk deletion
// operations.

diskApplyOperationDelete is an inner - loop helper for disk deletion operations .


func diskApplyOperationCreateUpdate(
	index int,
	newData map[string]interface{},
	oldDataSet []interface{},
	c *govmomi.Client,
	d *schema.ResourceData,
	l *object.VirtualDeviceList,
	spec *[]types.BaseVirtualDeviceConfigSpec,
	updates *[]interface{},
) error {
	var name string
	var err error
	if name, err = diskLabelOrName(newData); err != nil {
		return err
	}
	if name == diskDeletedName || name == diskDetachedName {
		// This is a "dummy" deleted resource and should be skipped over
		return nil
	}
	for _, oe := range oldDataSet {
		oldData := oe.(map[string]interface{})
		if newData["uuid"] == oldData["uuid"] {
			// This is an update
			r := NewDiskSubresource(c, d, newData, oldData, index)
			// If the only thing changing here is the datastore, or keep_on_remove,
			// this is a no-op as far as a device change is concerned. Datastore
			// changes are handled during storage vMotion later on during the
			// update phase. keep_on_remove is a Terraform-only attribute and only
			// needs to be committed to state.
			omc, err := copystructure.Copy(oldData)
			if err != nil {
				return fmt.Errorf("%s: error generating copy of old disk data: %s", r.Addr(), err)
			}
			oldCopy := omc.(map[string]interface{})
			oldCopy["datastore_id"] = newData["datastore_id"]
			oldCopy["keep_on_remove"] = newData["keep_on_remove"]
			// TODO: Remove these in 2.0, when all attributes should bear a label and
			// name is gone, and we won't need to exempt transitions.
			oldCopy["label"] = newData["label"]
			oldCopy["name"] = newData["name"]
			if reflect.DeepEqual(oldCopy, newData) {
				*updates = append(*updates, r.Data())
				return nil
			}
			uspec, err := r.Update(*l)
			if err != nil {
				return fmt.Errorf("%s: %s", r.Addr(), err)
			}
			*l = applyDeviceChange(*l, uspec)
			*spec = append(*spec, uspec...)
			*updates = append(*updates, r.Data())
			return nil
		}
	}
	// New data was not found - this is a create operation
	r := NewDiskSubresource(c, d, newData, nil, index)
	cspec, err := r.Create(*l)
	if err != nil {
		return fmt.Errorf("%s: %s", r.Addr(), err)
	}
	*l = applyDeviceChange(*l, cspec)
	*spec = append(*spec, cspec...)
	*updates = append(*updates, r.Data())
	return nil
}

// diskApplyOperationCreateUpdate is an inner-loop helper for disk creation and
// update operations.

diskApplyOperationCreateUpdate is an inner - loop helper for disk creation and update operations .


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.

DiskMigrateRelocateOperation assembles the VirtualMachineRelocateSpecDiskLocator slice for a virtual machine migration operation otherwise known as storage vMotion .


func DiskCloneRelocateOperation(d *schema.ResourceData, c *govmomi.Client, l object.VirtualDeviceList) ([]types.VirtualMachineRelocateSpecDiskLocator, error) {
	log.Printf("[DEBUG] DiskCloneRelocateOperation: Generating full disk relocate spec list")
	devices := SelectDisks(l, d.Get("scsi_controller_count").(int))
	log.Printf("[DEBUG] DiskCloneRelocateOperation: 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] DiskCloneRelocateOperation: Disk devices order after sort: %s", DeviceListString(devices))
	// Do the same for our listed disks.
	curSet := d.Get(subresourceTypeDisk).([]interface{})
	log.Printf("[DEBUG] DiskCloneRelocateOperation: Current resource set: %s", subresourceListString(curSet))
	sort.Sort(virtualDiskSubresourceSorter(curSet))
	log.Printf("[DEBUG] DiskCloneRelocateOperation: Resource set order after sort: %s", subresourceListString(curSet))

	log.Printf("[DEBUG] DiskCloneRelocateOperation: Generating relocators for source disks")
	var relocators []types.VirtualMachineRelocateSpecDiskLocator
	for i, device := range devices {
		m := curSet[i].(map[string]interface{})
		vd := device.GetVirtualDevice()
		ctlr := l.FindByKey(vd.ControllerKey)
		if ctlr == nil {
			return nil, 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 nil, fmt.Errorf("error computing device address: %s", err)
		}
		r := NewDiskSubresource(c, d, m, nil, i)
		// 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 dsID := r.Get("datastore_id"); dsID == "" || dsID == diskDatastoreComputedName {
			continue
		}
		// Otherwise, proceed with generating and appending the locator.
		relocator, err := r.Relocate(l, true)
		if err != nil {
			return nil, fmt.Errorf("%s: %s", r.Addr(), err)
		}
		relocators = append(relocators, relocator)
	}

	log.Printf("[DEBUG] DiskCloneRelocateOperation: Disk relocator list: %s", diskRelocateListString(relocators))
	log.Printf("[DEBUG] DiskCloneRelocateOperation: Disk relocator generation complete")
	return relocators, nil
}

// DiskCloneRelocateOperation assembles the
// VirtualMachineRelocateSpecDiskLocator slice for a virtual machine clone
// operation.
//
// This differs from a regular storage vMotion in that we have no existing
// devices in the resource to work off of - the disks in the source virtual
// machine is our source of truth. These disks are assigned to our disk
// sub-resources in config and the relocate specs are generated off of the
// backing data defined in config, taking on these filenames when cloned. After
// the clone is complete, natural re-configuration happens to bring the disk
// configurations fully in sync with what is defined.

DiskCloneRelocateOperation assembles the VirtualMachineRelocateSpecDiskLocator slice for a virtual machine clone operation . This differs from a regular storage vMotion in that we have no existing devices in the resource to work off of - the disks in the source virtual machine is our source of truth . These disks are assigned to our disk sub - resources in config and the relocate specs are generated off of the backing data defined in config taking on these filenames when cloned . After the clone is complete natural re - configuration happens to bring the disk configurations fully in sync with what is defined .


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.

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 .


func ReadDiskAttrsForDataSource(l object.VirtualDeviceList, count int) ([]map[string]interface{}, error) {
	log.Printf("[DEBUG] ReadDiskAttrsForDataSource: Fetching select attributes for disks across %d SCSI controllers", count)
	devices := SelectDisks(l, count)
	log.Printf("[DEBUG] ReadDiskAttrsForDataSource: 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] ReadDiskAttrsForDataSource: Disk devices order after sort: %s", DeviceListString(devices))
	var out []map[string]interface{}
	for i, device := range devices {
		disk := device.(*types.VirtualDisk)
		backing, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo)
		if !ok {
			return nil, fmt.Errorf("disk number %d has an unsupported backing type (expected flat VMDK version 2, got %T)", i, disk.Backing)
		}
		m := make(map[string]interface{})
		var eager, thin bool
		if backing.EagerlyScrub != nil {
			eager = *backing.EagerlyScrub
		}
		if backing.ThinProvisioned != nil {
			thin = *backing.ThinProvisioned
		}
		m["size"] = diskCapacityInGiB(disk)
		m["eagerly_scrub"] = eager
		m["thin_provisioned"] = thin
		out = append(out, m)
	}
	log.Printf("[DEBUG] ReadDiskAttrsForDataSource: Attributes returned: %+v", out)
	return out, nil
}

// ReadDiskAttrsForDataSource returns select attributes from the list of disks
// on a virtual machine. This is used in the VM data source to discover
// specific options of all of the disks on the virtual machine sorted by the
// order that they would be added in if a clone were to be done.

ReadDiskAttrsForDataSource returns select attributes from the list of disks on a virtual machine . This is used in the VM data source to discover specific options of all of the disks on the virtual machine sorted by the order that they would be added in if a clone were to be done .


func (r *DiskSubresource) Create(l object.VirtualDeviceList) ([]types.BaseVirtualDeviceConfigSpec, error) {
	log.Printf("[DEBUG] %s: Creating disk", r)
	var spec []types.BaseVirtualDeviceConfigSpec

	disk, err := r.createDisk(l)
	if err != nil {
		return nil, fmt.Errorf("error creating disk: %s", err)
	}
	// We now have the controller on which we can create our device on.
	// Assign the disk to a controller.
	ctlr, err := r.assignDisk(l, disk)
	if err != nil {
		return nil, fmt.Errorf("cannot assign disk: %s", err)
	}

	if err := r.expandDiskSettings(disk); err != nil {
		return nil, err
	}

	// Done here. Save ID, push the device to the new device list and return.
	if err := r.SaveDevIDs(disk, ctlr); err != nil {
		return nil, err
	}
	dspec, err := object.VirtualDeviceList{disk}.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
	if err != nil {
		return nil, err
	}
	if len(dspec) != 1 {
		return nil, fmt.Errorf("incorrect number of config spec items returned - expected 1, got %d", len(dspec))
	}
	// Clear the file operation if we are attaching.
	if r.Get("attach").(bool) {
		dspec[0].GetVirtualDeviceConfigSpec().FileOperation = ""
	}
	spec = append(spec, dspec...)
	log.Printf("[DEBUG] %s: Device config operations from create: %s", r, DeviceChangeString(spec))
	log.Printf("[DEBUG] %s: Create finished", r)
	return spec, nil
}

// Create creates a vsphere_virtual_machine disk sub-resource.

Create creates a vsphere_virtual_machine disk sub - resource .


func (r *DiskSubresource) Read(l object.VirtualDeviceList) error {
	log.Printf("[DEBUG] %s: Reading state", r)
	disk, err := r.findVirtualDisk(l, true)
	if err != nil {
		return fmt.Errorf("cannot find disk device: %s", err)
	}
	unit, ctlr, err := r.findControllerInfo(l, disk)
	if err != nil {
		return err
	}
	r.Set("unit_number", unit)
	if err := r.SaveDevIDs(disk, ctlr); err != nil {
		return err
	}

	// Fetch disk attachment state in config
	var attach bool
	if r.Get("attach") != nil {
		attach = r.Get("attach").(bool)
	}
	// Save disk backing settings
	b, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo)
	if !ok {
		return fmt.Errorf("disk backing at %s is of an unsupported type (type %T)", r.Get("device_address").(string), disk.Backing)
	}
	r.Set("uuid", b.Uuid)
	r.Set("disk_mode", b.DiskMode)
	r.Set("write_through", b.WriteThrough)

	// Only use disk_sharing if we are on vSphere 6.0 and higher. In addition,
	// skip if the value is unset - this prevents spurious diffs during upgrade
	// situations where the VM hardware version does not actually allow disk
	// sharing. In this situation, the value will be blank, and setting it will
	// actually result in an error.
	version := viapi.ParseVersionFromClient(r.client)
	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6}) && b.Sharing != "" {
		r.Set("disk_sharing", b.Sharing)
	}

	if !attach {
		r.Set("thin_provisioned", b.ThinProvisioned)
		r.Set("eagerly_scrub", b.EagerlyScrub)
	}
	r.Set("datastore_id", b.Datastore.Value)

	// Disk settings
	if !attach {
		dp := &object.DatastorePath{}
		if ok := dp.FromString(b.FileName); !ok {
			return fmt.Errorf("could not parse path from filename: %s", b.FileName)
		}
		r.Set("path", dp.Path)
		r.Set("size", diskCapacityInGiB(disk))
	}

	if allocation := disk.StorageIOAllocation; allocation != nil {
		r.Set("io_limit", allocation.Limit)
		r.Set("io_reservation", allocation.Reservation)
		if shares := allocation.Shares; shares != nil {
			r.Set("io_share_level", string(shares.Level))
			r.Set("io_share_count", shares.Shares)
		}
	}
	log.Printf("[DEBUG] %s: Read finished (key and device address may have changed)", r)
	return nil
}

// Read reads a vsphere_virtual_machine disk sub-resource and commits the data
// to the newData layer.

Read reads a vsphere_virtual_machine disk sub - resource and commits the data to the newData layer .


func (r *DiskSubresource) Update(l object.VirtualDeviceList) ([]types.BaseVirtualDeviceConfigSpec, error) {
	log.Printf("[DEBUG] %s: Beginning update", r)
	disk, err := r.findVirtualDisk(l, false)
	if err != nil {
		return nil, fmt.Errorf("cannot find disk device: %s", err)
	}

	// Has the unit number changed?
	if r.HasChange("unit_number") {
		ctlr, err := r.assignDisk(l, disk)
		if err != nil {
			return nil, fmt.Errorf("cannot assign disk: %s", err)
		}
		r.SetRestart("unit_number")
		if err := r.SaveDevIDs(disk, ctlr); err != nil {
			return nil, fmt.Errorf("error saving device address: %s", err)
		}
		// A change in disk unit number forces a device key change after the
		// reconfigure. We need to keep the key in the device change spec we send
		// along, but we can reset it here safely. Set it to 0, which will send it
		// though the new device loop, but will distinguish it from newly-created
		// devices.
		r.Set("key", 0)
	}

	// We can now expand the rest of the settings.
	if err := r.expandDiskSettings(disk); err != nil {
		return nil, err
	}

	dspec, err := object.VirtualDeviceList{disk}.ConfigSpec(types.VirtualDeviceConfigSpecOperationEdit)
	if err != nil {
		return nil, err
	}
	if len(dspec) != 1 {
		return nil, fmt.Errorf("incorrect number of config spec items returned - expected 1, got %d", len(dspec))
	}
	// Clear file operation - VirtualDeviceList currently sets this to replace, which is invalid
	dspec[0].GetVirtualDeviceConfigSpec().FileOperation = ""
	log.Printf("[DEBUG] %s: Device config operations from update: %s", r, DeviceChangeString(dspec))
	log.Printf("[DEBUG] %s: Update complete", r)
	return dspec, nil
}

// Update updates a vsphere_virtual_machine disk sub-resource.

Update updates a vsphere_virtual_machine disk sub - resource .


func (r *DiskSubresource) Delete(l object.VirtualDeviceList) ([]types.BaseVirtualDeviceConfigSpec, error) {
	log.Printf("[DEBUG] %s: Beginning delete", r)
	disk, err := r.findVirtualDisk(l, false)
	if err != nil {
		return nil, fmt.Errorf("cannot find disk device: %s", err)
	}
	deleteSpec, err := object.VirtualDeviceList{disk}.ConfigSpec(types.VirtualDeviceConfigSpecOperationRemove)
	if err != nil {
		return nil, err
	}
	if len(deleteSpec) != 1 {
		return nil, fmt.Errorf("incorrect number of config spec items returned - expected 1, got %d", len(deleteSpec))
	}
	if r.Get("keep_on_remove").(bool) || r.Get("attach").(bool) {
		// Clear file operation so that the disk is kept on remove.
		deleteSpec[0].GetVirtualDeviceConfigSpec().FileOperation = ""
	}
	log.Printf("[DEBUG] %s: Device config operations from update: %s", r, DeviceChangeString(deleteSpec))
	log.Printf("[DEBUG] %s: Delete completed", r)
	return deleteSpec, nil
}

// Delete deletes a vsphere_virtual_machine disk sub-resource.

Delete deletes a vsphere_virtual_machine disk sub - resource .


func (r *DiskSubresource) DiffExisting() error {
	log.Printf("[DEBUG] %s: Beginning normalization of existing disk", r)
	name, err := diskLabelOrName(r.data)
	if err != nil {
		return err
	}
	// Prevent a backward migration of label -> name. TODO: Remove this after
	// 2.0.
	olabel, nlabel := r.GetChange("label")
	if olabel != "" && nlabel == "" {
		return errors.New("cannot migrate from label to name")
	}
	// Carry forward the name attribute like we used to if no label is defined.
	// TODO: Remove this after 2.0.
	if nlabel == "" {
		oname, _ := r.GetChange("name")
		r.Set("name", oname.(string))
	}

	// set some computed fields: key, device_address, and uuid will always be
	// non-populated, so copy those.
	okey, _ := r.GetChange("key")
	odaddr, _ := r.GetChange("device_address")
	ouuid, _ := r.GetChange("uuid")
	r.Set("key", okey)
	r.Set("device_address", odaddr)
	r.Set("uuid", ouuid)

	if !r.Get("attach").(bool) {
		// Carry forward path when attach is not set
		opath, _ := r.GetChange("path")
		r.Set("path", opath.(string))
	}

	// Set the datastore if it's missing as we infer this from the default
	// datastore in that case
	if r.Get("datastore_id") == "" {
		switch {
		case r.rdd.HasChange("datastore_id"):
			// If the default datastore is changing and we don't have a default
			// datastore here, we need to use the implicit setting here to indicate
			// that we may need to migrate. This allows us to differentiate between a
			// full storage vMotion no-op, an implicit migration, and a migration
			// where we will need to generate a relocate spec for the individual disk
			// to ensure it stays at a datastore it might be pinned on.
			dsID := r.rdd.Get("datastore_id").(string)
			if dsID == "" {
				r.Set("datastore_id", diskDatastoreComputedName)
			} else {
				r.Set("datastore_id", dsID)
			}
		default:
			if err = r.normalizeDiskDatastore(); err != nil {
				return err
			}
		}
	}

	// Preserve the share value if we don't have custom shares set
	osc, _ := r.GetChange("io_share_count")
	if r.Get("io_share_level").(string) != string(types.SharesLevelCustom) {
		r.Set("io_share_count", osc)
	}

	// Ensure that the user is not attempting to shrink the disk. If we do more
	// we might want to change the name of this method, but we want to check this
	// here as CustomizeDiff is meant for vetoing.
	osize, nsize := r.GetChange("size")
	if osize.(int) > nsize.(int) {
		return fmt.Errorf("virtual disk %q: virtual disks cannot be shrunk (old: %d new: %d)", name, osize.(int), nsize.(int))
	}

	// Ensure that there is no change in either eagerly_scrub or thin_provisioned
	// - these values cannot be changed once set.
	if _, err = r.GetWithVeto("eagerly_scrub"); err != nil {
		return fmt.Errorf("virtual disk %q: %s", name, err)
	}
	if _, err = r.GetWithVeto("thin_provisioned"); err != nil {
		return fmt.Errorf("virtual disk %q: %s", name, err)
	}
	// Same with attach
	if _, err = r.GetWithVeto("attach"); err != nil {
		return fmt.Errorf("virtual disk %q: %s", name, err)
	}

	// Validate storage vMotion if the datastore is changing
	if r.HasChange("datastore_id") {
		if err = r.validateStorageRelocateDiff(); err != nil {
			return err
		}
	}
	log.Printf("[DEBUG] %s: Normalization of existing disk diff complete", r)
	return nil
}

// DiffExisting validates and normalizes the fields for an existing disk
// sub-resource.  It handles carrying over existing values, so this should not
// be used on disks that have not been successfully matched up between current
// and old diffs.

DiffExisting validates and normalizes the fields for an existing disk sub - resource . It handles carrying over existing values so this should not be used on disks that have not been successfully matched up between current and old diffs .