func queryVmfsDatastoreExtendOptions(ctx context.Context, s *object.HostDatastoreSystem, ds *object.Datastore, devicePath string, suppressExpandCandidates bool) ([]types.VmfsDatastoreOption, error) {
	req := types.QueryVmfsDatastoreExtendOptions{
		This:                     s.Reference(),
		Datastore:                ds.Reference(),
		DevicePath:               devicePath,
		SuppressExpandCandidates: &suppressExpandCandidates,
	}

	res, err := methods.QueryVmfsDatastoreExtendOptions(ctx, s.Client(), &req)
	if err != nil {
		return nil, err
	}

	return res.Returnval, nil
}

func queryVmfsDatastoreExtendOptions ( ctx context . Context , s * object . HostDatastoreSystem , ds * object . Datastore , devicePath string , suppressExpandCandidates bool ) ( [ ] types . VmfsDatastoreOption , error ) { req := types . QueryVmfsDatastoreExtendOptions { This : s . Reference ( ) , Datastore : ds . Reference ( ) , DevicePath : devicePath , SuppressExpandCandidates : & suppressExpandCandidates , } res , err := methods . QueryVmfsDatastoreExtendOptions ( ctx , s . Client ( ) , & req ) if err != nil { return nil , err } return res . Returnval , nil }

// queryVmfsDatastoreExtendOptions is a stop-gap method that implements
// QueryVmfsDatastoreExtendOptions. It will be removed once the higher level
// HostDatastoreSystem object supports this method.

queryVmfsDatastoreExtendOptions is a stop - gap method that implements QueryVmfsDatastoreExtendOptions . It will be removed once the higher level HostDatastoreSystem object supports this method .


func extendVmfsDatastore(ctx context.Context, s *object.HostDatastoreSystem, ds *object.Datastore, spec types.VmfsDatastoreExtendSpec) (*object.Datastore, error) {
	req := types.ExtendVmfsDatastore{
		This:      s.Reference(),
		Datastore: ds.Reference(),
		Spec:      spec,
	}

	res, err := methods.ExtendVmfsDatastore(ctx, s.Client(), &req)
	if err != nil {
		return nil, err
	}

	return object.NewDatastore(s.Client(), res.Returnval), nil
}

func extendVmfsDatastore ( ctx context . Context , s * object . HostDatastoreSystem , ds * object . Datastore , spec types . VmfsDatastoreExtendSpec ) ( * object . Datastore , error ) { req := types . ExtendVmfsDatastore { This : s . Reference ( ) , Datastore : ds . Reference ( ) , Spec : spec , } res , err := methods . ExtendVmfsDatastore ( ctx , s . Client ( ) , & req ) if err != nil { return nil , err } return object . NewDatastore ( s . Client ( ) , res . Returnval ) , nil }

// extendVmfsDatastore is a stop-gap method that implements
// ExtendVmfsDatastore. It will be removed once the higher level
// HostDatastoreSystem object supports this method.

extendVmfsDatastore is a stop - gap method that implements ExtendVmfsDatastore . It will be removed once the higher level HostDatastoreSystem object supports this method .


func resourceVSphereComputeClusterImportSetDefaults(d *schema.ResourceData) error {
	s := resourceVSphereComputeCluster().Schema
	return structure.SetBatch(d, map[string]interface{}{
		"ha_admission_control_performance_tolerance":       s["ha_admission_control_performance_tolerance"].Default,
		"ha_admission_control_resource_percentage_cpu":     s["ha_admission_control_resource_percentage_cpu"].Default,
		"ha_admission_control_resource_percentage_memory":  s["ha_admission_control_resource_percentage_memory"].Default,
		"ha_admission_control_slot_policy_explicit_cpu":    s["ha_admission_control_slot_policy_explicit_cpu"].Default,
		"ha_admission_control_slot_policy_explicit_memory": s["ha_admission_control_slot_policy_explicit_memory"].Default,
		"host_cluster_exit_timeout":                        s["host_cluster_exit_timeout"].Default,
	})
}

// resourceVSphereComputeClusterImportSetDefaults sets configuration defaults
// for resource attributes that are not managed by read. This gives
// completeness for import, in addition to reducing the number of post-import
// diffs that may need to happen.


func resourceVSphereComputeClusterApplyCreate(d *schema.ResourceData, meta interface{}) (*object.ClusterComputeResource, error) {
	log.Printf("[DEBUG] %s: Processing compute cluster creation", resourceVSphereComputeClusterIDString(d))
	client, err := resourceVSphereComputeClusterClient(meta)
	if 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.VSphereFolderTypeHost, dc)
	if err != nil {
		return nil, fmt.Errorf("cannot locate folder: %s", err)
	}

	// Create the cluster. We use an empty config spec so that we can move the
	// necessary hosts into the cluster *before* we send the full configuration,
	// ensuring that any host-dependent configuration does not break.
	cluster, err := clustercomputeresource.Create(f, d.Get("name").(string), types.ClusterConfigSpecEx{})
	if err != nil {
		return nil, fmt.Errorf("error creating cluster: %s", err)
	}

	// Set the ID now before proceeding any further. Any other operation past
	// this point is recoverable.
	d.SetId(cluster.Reference().Value)

	return cluster, nil
}

// resourceVSphereComputeClusterApplyCreate processes the creation part of
// resourceVSphereComputeClusterCreate.


func resourceVSphereComputeClusterApplyTags(d *schema.ResourceData, meta interface{}, cluster *object.ClusterComputeResource) 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", resourceVSphereComputeClusterIDString(d))
		return nil
	}

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

func
resourceVSphereComputeClusterApplyTags
(
d
*
schema
.
ResourceData
,
meta
interface
{
}
,
cluster
*
object
.
ClusterComputeResource
)
error
{
tagsClient
,
err
:=
tagsClientIfDefined
(
d
,
meta
)
if
err
!=
{
return
err
}
// Apply any pending tags now
if
tagsClient
==
{
log
.
Printf
(
"
"
,
resourceVSphereComputeClusterIDString
(
d
)
)
return
}
log
.
Printf
(
"
"
,
resourceVSphereComputeClusterIDString
(
d
)
)
return
processTagDiff
(
tagsClient
,
d
,
cluster
)
}

// resourceVSphereComputeClusterApplyTags processes the tags step for both
// create and update for vsphere_compute_cluster.

resourceVSphereComputeClusterApplyTags processes the tags step for both create and update for vsphere_compute_cluster .


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

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

// resourceVSphereComputeClusterReadTags reads the tags for
// vsphere_compute_cluster.

resourceVSphereComputeClusterReadTags reads the tags for vsphere_compute_cluster .


func resourceVSphereComputeClusterApplyCustomAttributes(
	d *schema.ResourceData,
	meta interface{},
	cluster *object.ClusterComputeResource,
) 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", resourceVSphereComputeClusterIDString(d))
		return nil
	}

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

// resourceVSphereComputeClusterApplyCustomAttributes processes the custom
// attributes step for both create and update for vsphere_compute_cluster.


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

	return nil
}

// resourceVSphereComputeClusterReadCustomAttributes reads the custom
// attributes for vsphere_compute_cluster.


func resourceVSphereComputeClusterGetCluster(
	d structure.ResourceIDStringer,
	meta interface{},
) (*object.ClusterComputeResource, error) {
	log.Printf("[DEBUG] %s: Fetching ComputeClusterResource object from resource ID", resourceVSphereComputeClusterIDString(d))
	client, err := resourceVSphereComputeClusterClient(meta)
	if err != nil {
		return nil, err
	}

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

func resourceVSphereComputeClusterGetCluster ( d structure . ResourceIDStringer , meta interface { } , ) ( * object . ClusterComputeResource , error ) { log . Printf ( " " , resourceVSphereComputeClusterIDString ( d ) ) client , err := resourceVSphereComputeClusterClient ( meta ) if err != nil { return nil , err } return clustercomputeresource . FromID ( client , d . Id ( ) ) }

// resourceVSphereComputeClusterGetCluster gets the ComputeClusterResource from the ID
// in the supplied ResourceData.

resourceVSphereComputeClusterGetCluster gets the ComputeClusterResource from the ID in the supplied ResourceData .


func resourceVSphereComputeClusterGetClusterFromPath(
	meta interface{},
	path string,
	dcID string,
) (*object.ClusterComputeResource, error) {
	client, err := resourceVSphereComputeClusterClient(meta)
	if err != nil {
		return nil, err
	}
	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 cluster %q in datacenter %q", path, dc.InventoryPath)
	} else {
		log.Printf("[DEBUG] Fetching cluster at path %q", path)
	}

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

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


func resourceVSphereComputeClusterSaveDatacenter(
	d *schema.ResourceData,
	meta interface{},
	cluster *object.ClusterComputeResource,
) error {
	log.Printf("[DEBUG] %s: Saving datacenter", resourceVSphereComputeClusterIDString(d))
	client, err := resourceVSphereComputeClusterClient(meta)
	if err != nil {
		return err
	}

	p, err := folder.RootPathParticleHost.SplitDatacenter(cluster.InventoryPath)
	if err != nil {
		return fmt.Errorf("error parsing datacenter path from cluster: %s", err)
	}

	dc, err := getDatacenter(client, p)
	if err != nil {
		return fmt.Errorf("error fetching datacenter for cluster: %s", err)
	}

	return d.Set("datacenter_id", dc.Reference().Value)
}

// resourceVSphereComputeClusterSaveDatacenter saves the datacenter that the
// cluster is a member of for correctness on imports.


func resourceVSphereComputeClusterSaveNameAndPath(d *schema.ResourceData, cluster *object.ClusterComputeResource) error {
	log.Printf(
		"[DEBUG] %s: Saving name and path data for cluster %q",
		resourceVSphereComputeClusterIDString(d),
		cluster.InventoryPath,
	)

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

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

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


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

	var changed bool
	var err error

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

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

	return cluster, nil
}

// resourceVSphereComputeClusterApplyNameChange applies any changes to a
// ClusterComputeResource's name.


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

	var changed bool
	var err error

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

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

	return cluster, nil
}

// resourceVSphereComputeClusterApplyFolderChange applies any changes to a
// ClusterComputeResource's folder location.


func resourceVSphereComputeClusterValidateEmptyCluster(
	d structure.ResourceIDStringer,
	cluster *object.ClusterComputeResource,
) error {
	log.Printf("[DEBUG] %s: Checking to ensure that cluster is empty", resourceVSphereComputeClusterIDString(d))
	ne, err := clustercomputeresource.HasChildren(cluster)
	if err != nil {
		return fmt.Errorf("error checking for cluster contents: %s", err)
	}
	if ne {
		return fmt.Errorf(
			"cluster %q still has hosts or virtual machines. Please move or remove all items before deleting",
			cluster.InventoryPath,
		)
	}
	return nil
}

// resourceVSphereComputeClusterValidateEmptyCluster validates that the cluster
// is empty. This is used to ensure a safe deletion of the cluster - we do not
// allow deletion of clusters that still virtual machines or hosts in them.


func resourceVSphereComputeClusterApplyDelete(d structure.ResourceIDStringer, cluster *object.ClusterComputeResource) error {
	log.Printf("[DEBUG] %s: Proceeding with cluster deletion", resourceVSphereComputeClusterIDString(d))
	if err := clustercomputeresource.Delete(cluster); err != nil {
		return err
	}
	return nil
}

// resourceVSphereComputeClusterApplyDelete process the removal of a
// cluster.


func resourceVSphereComputeClusterFlattenData(
	d *schema.ResourceData,
	meta interface{},
	cluster *object.ClusterComputeResource,
) error {
	log.Printf("[DEBUG] %s: Saving cluster attributes", resourceVSphereComputeClusterIDString(d))
	client, err := resourceVSphereComputeClusterClient(meta)
	if 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 := clustercomputeresource.Properties(cluster)
	if err != nil {
		return err
	}

	// Save the root resource pool ID so that it can be passed on to other
	// resources without having to resort to the data source.
	if err := d.Set("resource_pool_id", props.ResourcePool.Value); err != nil {
		return err
	}

	return flattenClusterConfigSpecEx(d, props.ConfigurationEx.(*types.ClusterConfigInfoEx), version)
}

// resourceVSphereComputeClusterFlattenData saves the configuration attributes
// from a ClusterComputeResource into the supplied ResourceData. It also saves
// the root resource pool for the cluster in resource_pool_id.
//
// Note that other functions handle other non-configuration related items, such
// as path, name, tags, and custom attributes.


func expandClusterConfigSpecEx(d *schema.ResourceData, version viapi.VSphereVersion) *types.ClusterConfigSpecEx {
	obj := &types.ClusterConfigSpecEx{
		DasConfig: expandClusterDasConfigInfo(d, version),
		DpmConfig: expandClusterDpmConfigInfo(d),
		DrsConfig: expandClusterDrsConfigInfo(d),
	}

	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6, Minor: 5}) {
		obj.InfraUpdateHaConfig = expandClusterInfraUpdateHaConfigInfo(d)
		obj.Orchestration = expandClusterOrchestrationInfo(d)
		obj.ProactiveDrsConfig = expandClusterProactiveDrsConfigInfo(d)
	}

	return obj
}

func expandClusterConfigSpecEx ( d * schema . ResourceData , version viapi . VSphereVersion ) * types . ClusterConfigSpecEx { obj := & types . ClusterConfigSpecEx { DasConfig : expandClusterDasConfigInfo ( d , version ) , DpmConfig : expandClusterDpmConfigInfo ( d ) , DrsConfig : expandClusterDrsConfigInfo ( d ) , } if version . Newer ( viapi . VSphereVersion { Product : version . Product , Major : 6 , Minor : 5 } ) { obj . InfraUpdateHaConfig = expandClusterInfraUpdateHaConfigInfo ( d ) obj . Orchestration = expandClusterOrchestrationInfo ( d ) obj . ProactiveDrsConfig = expandClusterProactiveDrsConfigInfo ( d ) } return obj }

// expandClusterConfigSpecEx reads certain ResourceData keys and returns a
// ClusterConfigSpecEx.

expandClusterConfigSpecEx reads certain ResourceData keys and returns a ClusterConfigSpecEx .


func flattenClusterConfigSpecEx(d *schema.ResourceData, obj *types.ClusterConfigInfoEx, version viapi.VSphereVersion) error {
	if err := flattenClusterDasConfigInfo(d, obj.DasConfig, version); err != nil {
		return err
	}
	if err := flattenClusterDpmConfigInfo(d, obj.DpmConfigInfo); err != nil {
		return err
	}
	if err := flattenClusterDrsConfigInfo(d, obj.DrsConfig); err != nil {
		return err
	}

	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6, Minor: 5}) {
		if err := flattenClusterInfraUpdateHaConfigInfo(d, obj.InfraUpdateHaConfig); err != nil {
			return err
		}
	}
	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6, Minor: 5}) {
		if err := flattenClusterOrchestrationInfo(d, obj.Orchestration); err != nil {
			return err
		}
	}
	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6, Minor: 5}) {
		return flattenClusterProactiveDrsConfigInfo(d, obj.ProactiveDrsConfig)
	}

	return nil
}

func flattenClusterConfigSpecEx ( d * schema . ResourceData , obj * types . ClusterConfigInfoEx , version viapi . VSphereVersion ) error { if err := flattenClusterDasConfigInfo ( d , obj . DasConfig , version ) ; err != nil { return err } if err := flattenClusterDpmConfigInfo ( d , obj . DpmConfigInfo ) ; err != nil { return err } if err := flattenClusterDrsConfigInfo ( d , obj . DrsConfig ) ; err != nil { return err } if version . Newer ( viapi . VSphereVersion { Product : version . Product , Major : 6 , Minor : 5 } ) { if err := flattenClusterInfraUpdateHaConfigInfo ( d , obj . InfraUpdateHaConfig ) ; err != nil { return err } } if version . Newer ( viapi . VSphereVersion { Product : version . Product , Major : 6 , Minor : 5 } ) { if err := flattenClusterOrchestrationInfo ( d , obj . Orchestration ) ; err != nil { return err } } if version . Newer ( viapi . VSphereVersion { Product : version . Product , Major : 6 , Minor : 5 } ) { return flattenClusterProactiveDrsConfigInfo ( d , obj . ProactiveDrsConfig ) } return nil }

// flattenClusterConfigSpecEx saves a ClusterConfigSpecEx into the supplied
// ResourceData.

flattenClusterConfigSpecEx saves a ClusterConfigSpecEx into the supplied ResourceData .


func expandClusterDasConfigInfo(d *schema.ResourceData, version viapi.VSphereVersion) *types.ClusterDasConfigInfo {
	obj := &types.ClusterDasConfigInfo{
		DefaultVmSettings:          expandClusterDasVMSettings(d, version),
		Enabled:                    structure.GetBool(d, "ha_enabled"),
		HBDatastoreCandidatePolicy: d.Get("ha_heartbeat_datastore_policy").(string),
		HostMonitoring:             d.Get("ha_host_monitoring").(string),
		Option:                     expandResourceVSphereComputeClusterDasAdvancedOptions(d),
		VmMonitoring:               d.Get("ha_vm_monitoring").(string),
		HeartbeatDatastore: structure.SliceInterfacesToManagedObjectReferences(
			d.Get("ha_heartbeat_datastore_ids").(*schema.Set).List(),
			"Datastore",
		),
	}

	policy := d.Get("ha_admission_control_policy").(string)
	if policy != clusterAdmissionControlTypeDisabled {
		obj.AdmissionControlEnabled = structure.BoolPtr(true)
	} else {
		obj.AdmissionControlEnabled = structure.BoolPtr(false)
	}
	obj.AdmissionControlPolicy = expandBaseClusterDasAdmissionControlPolicy(d, policy, version)

	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6}) {
		obj.VmComponentProtecting = d.Get("ha_vm_component_protection").(string)
	}

	return obj
}

// expandClusterDasConfigInfo reads certain ResourceData keys and returns a
// ClusterDasConfigInfo.

expandClusterDasConfigInfo reads certain ResourceData keys and returns a ClusterDasConfigInfo .


func flattenClusterDasConfigInfo(d *schema.ResourceData, obj types.ClusterDasConfigInfo, version viapi.VSphereVersion) error {
	var dsIDs []string
	for _, v := range obj.HeartbeatDatastore {
		dsIDs = append(dsIDs, v.Value)
	}

	err := structure.SetBatch(d, map[string]interface{}{
		"ha_enabled":                    obj.Enabled,
		"ha_heartbeat_datastore_policy": obj.HBDatastoreCandidatePolicy,
		"ha_host_monitoring":            obj.HostMonitoring,
		"ha_vm_monitoring":              obj.VmMonitoring,
		"ha_heartbeat_datastore_ids":    dsIDs,
	})
	if err != nil {
		return err
	}

	if err := flattenClusterDasVMSettings(d, obj.DefaultVmSettings, version); err != nil {
		return err
	}
	if err := flattenResourceVSphereComputeClusterDasAdvancedOptions(d, obj.Option); err != nil {
		return err
	}

	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6}) {
		if err := d.Set("ha_vm_component_protection", obj.VmComponentProtecting); err != nil {
			return err
		}
	}

	// If AdmissionControlEnabled is false, AdmissionControlPolicy is still
	// configured. Set ha_admission_control_policy to disabled before
	// flattenBaseClusterDasAdmissionControlPolicy, so AdmissionControlEnabled
	// can still be checked.
	if *obj.AdmissionControlEnabled == false {
		return d.Set("ha_admission_control_policy", clusterAdmissionControlTypeDisabled)
	}
	return flattenBaseClusterDasAdmissionControlPolicy(d, obj.AdmissionControlPolicy, version)
}

// flattenClusterDasConfigInfo saves a ClusterDasConfigInfo into the supplied
// ResourceData.

flattenClusterDasConfigInfo saves a ClusterDasConfigInfo into the supplied ResourceData .


func expandBaseClusterDasAdmissionControlPolicy(
	d *schema.ResourceData,
	policy string,
	version viapi.VSphereVersion,
) types.BaseClusterDasAdmissionControlPolicy {
	var obj types.BaseClusterDasAdmissionControlPolicy

	switch policy {
	case clusterAdmissionControlTypeResourcePercentage:
		obj = expandClusterFailoverResourcesAdmissionControlPolicy(d, version)
	case clusterAdmissionControlTypeSlotPolicy:
		obj = expandClusterFailoverLevelAdmissionControlPolicy(d)
	case clusterAdmissionControlTypeFailoverHosts:
		obj = expandClusterFailoverHostAdmissionControlPolicy(d, version)
	case clusterAdmissionControlTypeDisabled:
		return nil
	}

	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6, Minor: 5}) {
		obj.GetClusterDasAdmissionControlPolicy().ResourceReductionToToleratePercent = int32(d.Get("ha_admission_control_host_failure_tolerance").(int))
	}

	return obj
}

// expandBaseClusterDasAdmissionControlPolicy reads certain ResourceData keys
// and returns a BaseClusterDasAdmissionControlPolicy.


func flattenBaseClusterDasAdmissionControlPolicy(
	d *schema.ResourceData,
	obj types.BaseClusterDasAdmissionControlPolicy,
	version viapi.VSphereVersion,
) error {
	var policy string

	switch t := obj.(type) {
	case *types.ClusterFailoverResourcesAdmissionControlPolicy:
		if err := flattenClusterFailoverResourcesAdmissionControlPolicy(d, t, version); err != nil {
			return err
		}
		policy = clusterAdmissionControlTypeResourcePercentage
	case *types.ClusterFailoverLevelAdmissionControlPolicy:
		if err := flattenClusterFailoverLevelAdmissionControlPolicy(d, t); err != nil {
			return err
		}
		policy = clusterAdmissionControlTypeSlotPolicy
	case *types.ClusterFailoverHostAdmissionControlPolicy:
		if err := flattenClusterFailoverHostAdmissionControlPolicy(d, t, version); err != nil {
			return err
		}
		policy = clusterAdmissionControlTypeFailoverHosts
	default:
		policy = clusterAdmissionControlTypeDisabled
	}

	return d.Set("ha_admission_control_policy", policy)
}

// flattenBaseClusterDasAdmissionControlPolicy saves a
// BaseClusterDasAdmissionControlPolicy into the supplied ResourceData.


func expandClusterFailoverResourcesAdmissionControlPolicy(
	d *schema.ResourceData,
	version viapi.VSphereVersion,
) *types.ClusterFailoverResourcesAdmissionControlPolicy {
	obj := &types.ClusterFailoverResourcesAdmissionControlPolicy{
		CpuFailoverResourcesPercent:    int32(d.Get("ha_admission_control_resource_percentage_cpu").(int)),
		MemoryFailoverResourcesPercent: int32(d.Get("ha_admission_control_resource_percentage_memory").(int)),
	}

	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6, Minor: 5}) {
		obj.AutoComputePercentages = structure.GetBool(d, "ha_admission_control_resource_percentage_auto_compute")
		obj.FailoverLevel = int32(d.Get("ha_admission_control_host_failure_tolerance").(int))
	}

	return obj
}

// expandClusterFailoverResourcesAdmissionControlPolicy reads certain
// ResourceData keys and returns a
// ClusterFailoverResourcesAdmissionControlPolicy.


func flattenClusterFailoverResourcesAdmissionControlPolicy(
	d *schema.ResourceData,
	obj *types.ClusterFailoverResourcesAdmissionControlPolicy,
	version viapi.VSphereVersion,
) error {
	// AutoComputePercentages is a vSphere >= 6.5 feature, but when it's
	// enabled the admission control CPU/memory values will be auto-set and
	// caused spurious diffs, so do a nil check to see if we have the value or
	// if it's disabled before we set the values.
	if obj.AutoComputePercentages == nil || !*obj.AutoComputePercentages {
		err := structure.SetBatch(d, map[string]interface{}{
			"ha_admission_control_resource_percentage_cpu":    obj.CpuFailoverResourcesPercent,
			"ha_admission_control_resource_percentage_memory": obj.MemoryFailoverResourcesPercent,
		})
		if err != nil {
			return err
		}
	}

	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6, Minor: 5}) {
		return structure.SetBatch(d, map[string]interface{}{
			"ha_admission_control_resource_percentage_auto_compute": obj.AutoComputePercentages,
			"ha_admission_control_host_failure_tolerance":           obj.FailoverLevel,
		})
	}

	return nil
}

// flattenClusterFailoverResourcesAdmissionControlPolicy saves a
// ClusterFailoverResourcesAdmissionControlPolicy into the supplied
// ResourceData.


func expandClusterFailoverLevelAdmissionControlPolicy(d *schema.ResourceData) *types.ClusterFailoverLevelAdmissionControlPolicy {
	obj := &types.ClusterFailoverLevelAdmissionControlPolicy{
		FailoverLevel: int32(d.Get("ha_admission_control_host_failure_tolerance").(int)),
	}

	if d.Get("ha_admission_control_slot_policy_use_explicit_size").(bool) {
		obj.SlotPolicy = &types.ClusterFixedSizeSlotPolicy{
			Cpu:    int32(d.Get("ha_admission_control_resource_percentage_cpu").(int)),
			Memory: int32(d.Get("ha_admission_control_resource_percentage_memory").(int)),
		}
	}

	return obj
}

// expandClusterFailoverLevelAdmissionControlPolicy reads certain ResourceData
// keys and returns a ClusterFailoverLevelAdmissionControlPolicy.


func flattenClusterFailoverLevelAdmissionControlPolicy(
	d *schema.ResourceData,
	obj *types.ClusterFailoverLevelAdmissionControlPolicy,
) error {
	if err := d.Set("ha_admission_control_host_failure_tolerance", obj.FailoverLevel); err != nil {
		return err
	}

	if obj.SlotPolicy != nil {
		return structure.SetBatch(d, map[string]interface{}{
			"ha_admission_control_resource_percentage_cpu":    obj.SlotPolicy.(*types.ClusterFixedSizeSlotPolicy).Cpu,
			"ha_admission_control_resource_percentage_memory": obj.SlotPolicy.(*types.ClusterFixedSizeSlotPolicy).Memory,
		})
	}

	return nil
}

// flattenClusterFailoverLevelAdmissionControlPolicy saves a
// ClusterFailoverLevelAdmissionControlPolicy into the supplied ResourceData.


func expandClusterFailoverHostAdmissionControlPolicy(
	d *schema.ResourceData,
	version viapi.VSphereVersion,
) *types.ClusterFailoverHostAdmissionControlPolicy {
	obj := &types.ClusterFailoverHostAdmissionControlPolicy{
		FailoverHosts: structure.SliceInterfacesToManagedObjectReferences(
			d.Get("ha_admission_control_failover_host_system_ids").(*schema.Set).List(),
			"HostSystem",
		),
	}

	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6, Minor: 5}) {
		obj.FailoverLevel = int32(d.Get("ha_admission_control_host_failure_tolerance").(int))
	}

	return obj
}

// expandClusterFailoverHostAdmissionControlPolicy reads certain ResourceData
// keys and returns a ClusterFailoverHostAdmissionControlPolicy.


func flattenClusterFailoverHostAdmissionControlPolicy(
	d *schema.ResourceData,
	obj *types.ClusterFailoverHostAdmissionControlPolicy,
	version viapi.VSphereVersion,
) error {
	var hsIDs []string
	for _, v := range obj.FailoverHosts {
		hsIDs = append(hsIDs, v.Value)
	}

	if err := d.Set("ha_admission_control_failover_host_system_ids", hsIDs); err != nil {
		return err
	}

	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6, Minor: 5}) {
		return d.Set("ha_admission_control_host_failure_tolerance", obj.FailoverLevel)
	}

	return nil
}

// flattenClusterFailoverHostAdmissionControlPolicy saves a
// ClusterFailoverHostAdmissionControlPolicy into the supplied ResourceData.


func expandClusterDasVMSettings(d *schema.ResourceData, version viapi.VSphereVersion) *types.ClusterDasVmSettings {
	obj := &types.ClusterDasVmSettings{
		IsolationResponse:         d.Get("ha_host_isolation_response").(string),
		RestartPriority:           d.Get("ha_vm_restart_priority").(string),
		VmToolsMonitoringSettings: expandClusterVMToolsMonitoringSettings(d),
	}

	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6}) {
		obj.VmComponentProtectionSettings = expandClusterVMComponentProtectionSettings(d)
	}
	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6, Minor: 5}) {
		obj.RestartPriorityTimeout = int32(d.Get("ha_vm_restart_timeout").(int))
	}

	return obj
}

// expandClusterDasVMSettings reads certain ResourceData keys and returns a
// ClusterDasVmSettings.

expandClusterDasVMSettings reads certain ResourceData keys and returns a ClusterDasVmSettings .


func flattenClusterDasVMSettings(d *schema.ResourceData, obj *types.ClusterDasVmSettings, version viapi.VSphereVersion) error {
	err := structure.SetBatch(d, map[string]interface{}{
		"ha_host_isolation_response": obj.IsolationResponse,
		"ha_vm_restart_priority":     obj.RestartPriority,
	})
	if err != nil {
		return err
	}

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

	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6}) {
		if err := flattenClusterVMComponentProtectionSettings(d, obj.VmComponentProtectionSettings); err != nil {
			return err
		}
	}
	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6, Minor: 5}) {
		return d.Set("ha_vm_restart_timeout", obj.RestartPriorityTimeout)
	}

	return nil
}

// flattenClusterDasVMSettings saves a ClusterDasVmSettings into the supplied
// ResourceData.

flattenClusterDasVMSettings saves a ClusterDasVmSettings into the supplied ResourceData .


func expandClusterVMComponentProtectionSettings(d *schema.ResourceData) *types.ClusterVmComponentProtectionSettings {
	obj := &types.ClusterVmComponentProtectionSettings{
		VmReactionOnAPDCleared:    d.Get("ha_datastore_apd_recovery_action").(string),
		VmStorageProtectionForAPD: d.Get("ha_datastore_apd_response").(string),
		VmStorageProtectionForPDL: d.Get("ha_datastore_pdl_response").(string),
		VmTerminateDelayForAPDSec: int32(d.Get("ha_datastore_apd_response_delay").(int)),
	}

	if d.Get("ha_datastore_apd_response").(string) != string(types.ClusterVmComponentProtectionSettingsStorageVmReactionDisabled) {
		// Flag EnableAPDTimeoutForHosts to ensure that APD is enabled for all
		// hosts in the cluster and our other settings here will be effective. Note
		// that this setting is not persisted to state or the vSphere backend and
		// is actually a host operation, not a cluster operation. It's here to
		// ensure that the settings specified here are otherwise effective. We may
		// need to revisit this if we introduce more robust host management
		// capabilities in the provider.
		obj.EnableAPDTimeoutForHosts = structure.BoolPtr(true)
	}

	return obj
}

// expandClusterVMComponentProtectionSettings reads certain ResourceData keys and returns a
// ClusterVmComponentProtectionSettings.


func flattenClusterVMComponentProtectionSettings(d *schema.ResourceData, obj *types.ClusterVmComponentProtectionSettings) error {
	return structure.SetBatch(d, map[string]interface{}{
		"ha_datastore_apd_recovery_action": obj.VmReactionOnAPDCleared,
		"ha_datastore_apd_response":        obj.VmStorageProtectionForAPD,
		"ha_datastore_pdl_response":        obj.VmStorageProtectionForPDL,
		"ha_datastore_apd_response_delay":  obj.VmTerminateDelayForAPDSec,
	})
}

// flattenClusterVmComponentProtectionSettings saves a
// ClusterVmComponentProtectionSettings into the supplied ResourceData.


func expandClusterVMToolsMonitoringSettings(d *schema.ResourceData) *types.ClusterVmToolsMonitoringSettings {
	obj := &types.ClusterVmToolsMonitoringSettings{
		FailureInterval:  int32(d.Get("ha_vm_failure_interval").(int)),
		MaxFailures:      int32(d.Get("ha_vm_maximum_resets").(int)),
		MaxFailureWindow: int32(d.Get("ha_vm_maximum_failure_window").(int)),
		MinUpTime:        int32(d.Get("ha_vm_minimum_uptime").(int)),
		VmMonitoring:     d.Get("ha_vm_monitoring").(string),
	}

	return obj
}

func expandClusterVMToolsMonitoringSettings ( d * schema . ResourceData ) * types . ClusterVmToolsMonitoringSettings { obj := & types . ClusterVmToolsMonitoringSettings { FailureInterval : int32 ( d . Get ( " " ) . ( int ) ) , MaxFailures : int32 ( d . Get ( " " ) . ( int ) ) , MaxFailureWindow : int32 ( d . Get ( " " ) . ( int ) ) , MinUpTime : int32 ( d . Get ( " " ) . ( int ) ) , VmMonitoring : d . Get ( " " ) . ( string ) , } return obj }

// expandClusterVMToolsMonitoringSettings reads certain ResourceData keys and returns a
// ClusterVmToolsMonitoringSettings.

expandClusterVMToolsMonitoringSettings reads certain ResourceData keys and returns a ClusterVmToolsMonitoringSettings .


func flattenClusterVMToolsMonitoringSettings(d *schema.ResourceData, obj *types.ClusterVmToolsMonitoringSettings) error {
	return structure.SetBatch(d, map[string]interface{}{
		"ha_vm_failure_interval":       obj.FailureInterval,
		"ha_vm_maximum_resets":         obj.MaxFailures,
		"ha_vm_maximum_failure_window": obj.MaxFailureWindow,
		"ha_vm_minimum_uptime":         obj.MinUpTime,
		"ha_vm_monitoring":             obj.VmMonitoring,
	})
}

func flattenClusterVMToolsMonitoringSettings ( d * schema . ResourceData , obj * types . ClusterVmToolsMonitoringSettings ) error { return structure . SetBatch ( d , map [ string ] interface { } { " " : obj . FailureInterval , " " : obj . MaxFailures , " " : obj . MaxFailureWindow , " " : obj . MinUpTime , " " : obj . VmMonitoring , } ) }

// flattenClusterVmToolsMonitoringSettings saves a
// ClusterVmToolsMonitoringSettings into the supplied ResourceData.

flattenClusterVmToolsMonitoringSettings saves a ClusterVmToolsMonitoringSettings into the supplied ResourceData .


func expandClusterDpmConfigInfo(d *schema.ResourceData) *types.ClusterDpmConfigInfo {
	obj := &types.ClusterDpmConfigInfo{
		DefaultDpmBehavior:  types.DpmBehavior(d.Get("dpm_automation_level").(string)),
		Enabled:             structure.GetBool(d, "dpm_enabled"),
		HostPowerActionRate: int32(d.Get("dpm_threshold").(int)),
	}

	return obj
}

func expandClusterDpmConfigInfo ( d * schema . ResourceData ) * types . ClusterDpmConfigInfo { obj := & types . ClusterDpmConfigInfo { DefaultDpmBehavior : types . DpmBehavior ( d . Get ( " " ) . ( string ) ) , Enabled : structure . GetBool ( d , " " ) , HostPowerActionRate : int32 ( d . Get ( " " ) . ( int ) ) , } return obj }

// expandClusterDpmConfigInfo reads certain ResourceData keys and returns a
// ClusterDpmConfigInfo.

expandClusterDpmConfigInfo reads certain ResourceData keys and returns a ClusterDpmConfigInfo .


func flattenClusterDpmConfigInfo(d *schema.ResourceData, obj *types.ClusterDpmConfigInfo) error {
	return structure.SetBatch(d, map[string]interface{}{
		"dpm_automation_level": obj.DefaultDpmBehavior,
		"dpm_enabled":          obj.Enabled,
		"dpm_threshold":        obj.HostPowerActionRate,
	})
}

func flattenClusterDpmConfigInfo ( d * schema . ResourceData , obj * types . ClusterDpmConfigInfo ) error { return structure . SetBatch ( d , map [ string ] interface { } { " " : obj . DefaultDpmBehavior , " " : obj . Enabled , " " : obj . HostPowerActionRate , } ) }

// flattenClusterDpmConfigInfo saves a ClusterDpmConfigInfo into the supplied
// ResourceData.

flattenClusterDpmConfigInfo saves a ClusterDpmConfigInfo into the supplied ResourceData .


func expandClusterDrsConfigInfo(d *schema.ResourceData) *types.ClusterDrsConfigInfo {
	obj := &types.ClusterDrsConfigInfo{
		DefaultVmBehavior:         types.DrsBehavior(d.Get("drs_automation_level").(string)),
		Enabled:                   structure.GetBool(d, "drs_enabled"),
		EnableVmBehaviorOverrides: structure.GetBool(d, "drs_enable_vm_overrides"),
		VmotionRate:               int32(d.Get("drs_migration_threshold").(int)),
		Option:                    expandResourceVSphereComputeClusterDrsAdvancedOptions(d),
	}

	return obj
}

// expandClusterDrsConfigInfo reads certain ResourceData keys and returns a
// ClusterDrsConfigInfo.

expandClusterDrsConfigInfo reads certain ResourceData keys and returns a ClusterDrsConfigInfo .


func flattenClusterDrsConfigInfo(d *schema.ResourceData, obj types.ClusterDrsConfigInfo) error {
	err := structure.SetBatch(d, map[string]interface{}{
		"drs_automation_level":    obj.DefaultVmBehavior,
		"drs_enabled":             obj.Enabled,
		"drs_enable_vm_overrides": obj.EnableVmBehaviorOverrides,
		"drs_migration_threshold": obj.VmotionRate,
	})
	if err != nil {
		return err
	}

	return flattenResourceVSphereComputeClusterDrsAdvancedOptions(d, obj.Option)
}

// flattenClusterDrsConfigInfo saves a ClusterDrsConfigInfo into the supplied
// ResourceData.

flattenClusterDrsConfigInfo saves a ClusterDrsConfigInfo into the supplied ResourceData .


func expandResourceVSphereComputeClusterDrsAdvancedOptions(d *schema.ResourceData) []types.BaseOptionValue {
	var opts []types.BaseOptionValue

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

// expandResourceVSphereComputeClusterDrsAdvancedOptions reads certain
// ResourceData keys and returns a BaseOptionValue list designed for use as DRS
// advanced options.


func expandClusterInfraUpdateHaConfigInfo(d *schema.ResourceData) *types.ClusterInfraUpdateHaConfigInfo {
	obj := &types.ClusterInfraUpdateHaConfigInfo{
		Behavior:            d.Get("proactive_ha_automation_level").(string),
		Enabled:             structure.GetBool(d, "proactive_ha_enabled"),
		ModerateRemediation: d.Get("proactive_ha_moderate_remediation").(string),
		Providers:           structure.SliceInterfacesToStrings(d.Get("proactive_ha_provider_ids").(*schema.Set).List()),
		SevereRemediation:   d.Get("proactive_ha_severe_remediation").(string),
	}

	return obj
}

func expandClusterInfraUpdateHaConfigInfo ( d * schema . ResourceData ) * types . ClusterInfraUpdateHaConfigInfo { obj := & types . ClusterInfraUpdateHaConfigInfo { Behavior : d . Get ( " " ) . ( string ) , Enabled : structure . GetBool ( d , " " ) , ModerateRemediation : d . Get ( " " ) . ( string ) , Providers : structure . SliceInterfacesToStrings ( d . Get ( " " ) . ( * schema . Set ) . List ( ) ) , SevereRemediation : d . Get ( " " ) . ( string ) , } return obj }

// expandClusterInfraUpdateHaConfigInfo reads certain ResourceData keys and returns a
// ClusterInfraUpdateHaConfigInfo.

expandClusterInfraUpdateHaConfigInfo reads certain ResourceData keys and returns a ClusterInfraUpdateHaConfigInfo .


func flattenClusterInfraUpdateHaConfigInfo(d *schema.ResourceData, obj *types.ClusterInfraUpdateHaConfigInfo) error {
	return structure.SetBatch(d, map[string]interface{}{
		"proactive_ha_automation_level":     obj.Behavior,
		"proactive_ha_enabled":              obj.Enabled,
		"proactive_ha_moderate_remediation": obj.ModerateRemediation,
		"proactive_ha_provider_ids":         obj.Providers,
		"proactive_ha_severe_remediation":   obj.SevereRemediation,
	})
}

func flattenClusterInfraUpdateHaConfigInfo ( d * schema . ResourceData , obj * types . ClusterInfraUpdateHaConfigInfo ) error { return structure . SetBatch ( d , map [ string ] interface { } { " " : obj . Behavior , " " : obj . Enabled , " " : obj . ModerateRemediation , " " : obj . Providers , " " : obj . SevereRemediation , } ) }

// flattenClusterInfraUpdateHaConfigInfo saves a ClusterInfraUpdateHaConfigInfo into the
// supplied ResourceData.

flattenClusterInfraUpdateHaConfigInfo saves a ClusterInfraUpdateHaConfigInfo into the supplied ResourceData .


func expandClusterOrchestrationInfo(d *schema.ResourceData) *types.ClusterOrchestrationInfo {
	obj := &types.ClusterOrchestrationInfo{
		DefaultVmReadiness: &types.ClusterVmReadiness{
			PostReadyDelay: int32(d.Get("ha_vm_restart_additional_delay").(int)),
			ReadyCondition: d.Get("ha_vm_dependency_restart_condition").(string),
		},
	}

	return obj
}

func expandClusterOrchestrationInfo ( d * schema . ResourceData ) * types . ClusterOrchestrationInfo { obj := & types . ClusterOrchestrationInfo { DefaultVmReadiness : & types . ClusterVmReadiness { PostReadyDelay : int32 ( d . Get ( " " ) . ( int ) ) , ReadyCondition : d . Get ( " " ) . ( string ) , } , } return obj }

// expandClusterOrchestrationInfo reads certain ResourceData keys and returns a
// ClusterOrchestrationInfo.

expandClusterOrchestrationInfo reads certain ResourceData keys and returns a ClusterOrchestrationInfo .


func flattenClusterOrchestrationInfo(d *schema.ResourceData, obj *types.ClusterOrchestrationInfo) error {
	return structure.SetBatch(d, map[string]interface{}{
		"ha_vm_restart_additional_delay":     obj.DefaultVmReadiness.PostReadyDelay,
		"ha_vm_dependency_restart_condition": obj.DefaultVmReadiness.ReadyCondition,
	})
}

func flattenClusterOrchestrationInfo ( d * schema . ResourceData , obj * types . ClusterOrchestrationInfo ) error { return structure . SetBatch ( d , map [ string ] interface { } { " " : obj . DefaultVmReadiness . PostReadyDelay , " " : obj . DefaultVmReadiness . ReadyCondition , } ) }

// flattenClusterOrchestrationInfo saves a ClusterOrchestrationInfo into the
// supplied ResourceData.

flattenClusterOrchestrationInfo saves a ClusterOrchestrationInfo into the supplied ResourceData .


func expandClusterProactiveDrsConfigInfo(d *schema.ResourceData) *types.ClusterProactiveDrsConfigInfo {
	obj := &types.ClusterProactiveDrsConfigInfo{
		Enabled: structure.GetBool(d, "drs_enable_predictive_drs"),
	}

	return obj
}

func expandClusterProactiveDrsConfigInfo ( d * schema . ResourceData ) * types . ClusterProactiveDrsConfigInfo { obj := & types . ClusterProactiveDrsConfigInfo { Enabled : structure . GetBool ( d , " " ) , } return obj }

// expandClusterProactiveDrsConfigInfo reads certain ResourceData keys and returns a
// ClusterProactiveDrsConfigInfo.

expandClusterProactiveDrsConfigInfo reads certain ResourceData keys and returns a ClusterProactiveDrsConfigInfo .


func flattenClusterProactiveDrsConfigInfo(d *schema.ResourceData, obj *types.ClusterProactiveDrsConfigInfo) error {
	return structure.SetBatch(d, map[string]interface{}{
		"drs_enable_predictive_drs": obj.Enabled,
	})
}

func flattenClusterProactiveDrsConfigInfo ( d * schema . ResourceData , obj * types . ClusterProactiveDrsConfigInfo ) error { return structure . SetBatch ( d , map [ string ] interface { } { " " : obj . Enabled , } ) }

// flattenClusterProactiveDrsConfigInfo saves a ClusterProactiveDrsConfigInfo into the
// supplied ResourceData.

flattenClusterProactiveDrsConfigInfo saves a ClusterProactiveDrsConfigInfo into the supplied ResourceData .


func Properties(pod *object.StoragePod) (*mo.StoragePod, error) {
	ctx, cancel := context.WithTimeout(context.Background(), provider.DefaultAPITimeout)
	defer cancel()
	var props mo.StoragePod
	if err := pod.Properties(ctx, pod.Reference(), nil, &props); err != nil {
		return nil, err
	}
	return &props, nil
}

func Properties ( pod * object . StoragePod ) ( * mo . StoragePod , error ) { ctx , cancel := context . WithTimeout ( context . Background ( ) , provider . DefaultAPITimeout ) defer cancel ( ) var props mo . StoragePod if err := pod . Properties ( ctx , pod . Reference ( ) , nil , & props ) ; err != nil { return nil , err } return & props , nil }

// Properties is a convenience method that wraps fetching the
// StoragePod MO from its higher-level object.

Properties is a convenience method that wraps fetching the StoragePod MO from its higher - level object .


func Create(f *object.Folder, name string) (*object.StoragePod, error) {
	log.Printf("[DEBUG] Creating datastore cluster %q", fmt.Sprintf("%s/%s", f.InventoryPath, name))
	ctx, cancel := context.WithTimeout(context.Background(), provider.DefaultAPITimeout)
	defer cancel()
	pod, err := f.CreateStoragePod(ctx, name)
	if err != nil {
		return nil, err
	}
	return pod, nil
}

func Create ( f * object . Folder , name string ) ( * object . StoragePod , error ) { log . Printf ( " " , fmt . Sprintf ( " " , f . InventoryPath , name ) ) ctx , cancel := context . WithTimeout ( context . Background ( ) , provider . DefaultAPITimeout ) defer cancel ( ) pod , err := f . CreateStoragePod ( ctx , name ) if err != nil { return nil , err } return pod , nil }

// Create creates a StoragePod from a supplied folder. The resulting StoragePod
// is returned.

Create creates a StoragePod from a supplied folder . The resulting StoragePod is returned .


func ApplyDRSConfiguration(client *govmomi.Client, pod *object.StoragePod, spec types.StorageDrsConfigSpec) error {
	log.Printf("[DEBUG] Applying storage DRS configuration against datastore cluster %q", pod.InventoryPath)
	mgr := object.NewStorageResourceManager(client.Client)
	ctx, cancel := context.WithTimeout(context.Background(), provider.DefaultAPITimeout)
	defer cancel()
	task, err := mgr.ConfigureStorageDrsForPod(ctx, pod, spec, true)
	if err != nil {
		return err
	}
	return task.Wait(ctx)
}

func ApplyDRSConfiguration ( client * govmomi . Client , pod * object . StoragePod , spec types . StorageDrsConfigSpec ) error { log . Printf ( " " , pod . InventoryPath ) mgr := object . NewStorageResourceManager ( client . Client ) ctx , cancel := context . WithTimeout ( context . Background ( ) , provider . DefaultAPITimeout ) defer cancel ( ) task , err := mgr . ConfigureStorageDrsForPod ( ctx , pod , spec , true ) if err != nil { return err } return task . Wait ( ctx ) }

// ApplyDRSConfiguration takes a types.StorageDrsConfigSpec and applies it
// against the specified StoragePod.

ApplyDRSConfiguration takes a types . StorageDrsConfigSpec and applies it against the specified StoragePod .


func Rename(pod *object.StoragePod, name string) error {
	log.Printf("[DEBUG] Renaming storage pod %q to %s", pod.InventoryPath, name)
	ctx, cancel := context.WithTimeout(context.Background(), provider.DefaultAPITimeout)
	defer cancel()
	task, err := pod.Rename(ctx, name)
	if err != nil {
		return err
	}
	return task.Wait(ctx)
}

func Rename ( pod * object . StoragePod , name string ) error { log . Printf ( " " , pod . InventoryPath , name ) ctx , cancel := context . WithTimeout ( context . Background ( ) , provider . DefaultAPITimeout ) defer cancel ( ) task , err := pod . Rename ( ctx , name ) if err != nil { return err } return task . Wait ( ctx ) }


func MoveToFolder(client *govmomi.Client, pod *object.StoragePod, relative string) error {
	f, err := folder.DatastoreFolderFromObject(client, pod, relative)
	if err != nil {
		return err
	}
	return folder.MoveObjectTo(pod.Reference(), f)
}

func MoveToFolder ( client * govmomi . Client , pod * object . StoragePod , relative string ) error { f , err := folder . DatastoreFolderFromObject ( client , pod , relative ) if err != nil { return err } return folder . MoveObjectTo ( pod . Reference ( ) , f ) }

// MoveToFolder is a complex method that moves a StoragePod to a given relative
// datastore folder path. "Relative" here means relative to a datacenter, which
// is discovered from the current StoragePod path.

MoveToFolder is a complex method that moves a StoragePod to a given relative datastore folder path . Relative here means relative to a datacenter which is discovered from the current StoragePod path .


func StorageDRSEnabled(pod *object.StoragePod) (bool, error) {
	props, err := Properties(pod)
	if err != nil {
		return false, err
	}
	if props.PodStorageDrsEntry == nil {
		return false, nil
	}
	return props.PodStorageDrsEntry.StorageDrsConfig.PodConfig.Enabled, nil
}

func StorageDRSEnabled ( pod * object . StoragePod ) ( bool , error ) { props , err := Properties ( pod ) if err != nil { return false , err } if props . PodStorageDrsEntry == nil { return false , nil } return props . PodStorageDrsEntry . StorageDrsConfig . PodConfig . Enabled , nil }

// StorageDRSEnabled checks a StoragePod to see if Storage DRS is enabled.

StorageDRSEnabled checks a StoragePod to see if Storage DRS is enabled .


func CreateVM(
	client *govmomi.Client,
	fo *object.Folder,
	spec types.VirtualMachineConfigSpec,
	pool *object.ResourcePool,
	host *object.HostSystem,
	pod *object.StoragePod,
) (*object.VirtualMachine, error) {
	sdrsEnabled, err := StorageDRSEnabled(pod)
	if err != nil {
		return nil, err
	}
	if !sdrsEnabled {
		return nil, fmt.Errorf("storage DRS is not enabled on datastore cluster %q", pod.Name())
	}
	log.Printf(
		"[DEBUG] Creating virtual machine %q on datastore cluster %q",
		fmt.Sprintf("%s/%s", fo.InventoryPath, spec.Name),
		pod.Name(),
	)
	sps := types.StoragePlacementSpec{
		Type:         string(types.StoragePlacementSpecPlacementTypeCreate),
		ResourcePool: types.NewReference(pool.Reference()),
		PodSelectionSpec: types.StorageDrsPodSelectionSpec{
			StoragePod:      types.NewReference(pod.Reference()),
			InitialVmConfig: expandVMPodConfigForPlacement(spec.DeviceChange, pod),
		},
		ConfigSpec: &spec,
		Folder:     types.NewReference(fo.Reference()),
	}
	if host != nil {
		sps.Host = types.NewReference(host.Reference())
	}

	placement, err := recommendSDRS(client, sps, provider.DefaultAPITimeout)
	if err != nil {
		return nil, err
	}
	// If the parent resource pool is a vApp, we need to create the VM using the
	// CreateChildVM vApp function instead of directly using SDRS recommendations.
	if sps.ResourcePool != nil {
		vc, err := vappcontainer.FromID(client, sps.ResourcePool.Reference().Value)
		switch {
		case viapi.IsManagedObjectNotFoundError(err):
			// This isn't a vApp container, so continue with normal SDRS work flow.
		case err == nil:
			return createVAppVMFromSPS(client, placement, spec, sps, vc)
		default:
			return nil, err
		}
	}
	return applySDRS(client, placement, provider.DefaultAPITimeout)
}

// CreateVM creates a virtual machine on a datastore cluster via the
// StorageResourceManager API. It mimics our helper in the virtualmachine
// package in functionality, returning a VM helper object on success.

CreateVM creates a virtual machine on a datastore cluster via the StorageResourceManager API . It mimics our helper in the virtualmachine package in functionality returning a VM helper object on success .


func CloneVM(
	client *govmomi.Client,
	src *object.VirtualMachine,
	fo *object.Folder,
	name string,
	spec types.VirtualMachineCloneSpec,
	timeout int,
	pod *object.StoragePod,
) (*object.VirtualMachine, error) {
	sdrsEnabled, err := StorageDRSEnabled(pod)
	if err != nil {
		return nil, err
	}
	if !sdrsEnabled {
		return nil, fmt.Errorf("storage DRS is not enabled on datastore cluster %q", pod.Name())
	}
	log.Printf(
		"[DEBUG] Cloning virtual machine %q to %q on datastore cluster %q",
		src.InventoryPath,
		fmt.Sprintf("%s/%s", fo.InventoryPath, name),
		pod.Name(),
	)

	sps := types.StoragePlacementSpec{
		Folder:    types.NewReference(fo.Reference()),
		Vm:        types.NewReference(src.Reference()),
		CloneName: name,
		CloneSpec: &spec,
		PodSelectionSpec: types.StorageDrsPodSelectionSpec{
			StoragePod: types.NewReference(pod.Reference()),
		},
		Type: string(types.StoragePlacementSpecPlacementTypeClone),
	}

	return recommendAndApplySDRS(client, sps, time.Minute*time.Duration(timeout))
}

func CloneVM ( client * govmomi . Client , src * object . VirtualMachine , fo * object . Folder , name string , spec types . VirtualMachineCloneSpec , timeout int , pod * object . StoragePod , ) ( * object . VirtualMachine , error ) { sdrsEnabled , err := StorageDRSEnabled ( pod ) if err != nil { return nil , err } if ! sdrsEnabled { return nil , fmt . Errorf ( " " , pod . Name ( ) ) } log . Printf ( " " , src . InventoryPath , fmt . Sprintf ( " " , fo . InventoryPath , name ) , pod . Name ( ) , ) sps := types . StoragePlacementSpec { Folder : types . NewReference ( fo . Reference ( ) ) , Vm : types . NewReference ( src . Reference ( ) ) , CloneName : name , CloneSpec : & spec , PodSelectionSpec : types . StorageDrsPodSelectionSpec { StoragePod : types . NewReference ( pod . Reference ( ) ) , } , Type : string ( types . StoragePlacementSpecPlacementTypeClone ) , } return recommendAndApplySDRS ( client , sps , time . Minute * time . Duration ( timeout ) ) }

// CloneVM clones a virtual machine to a datastore cluster via the
// StorageResourceManager API. It mimics our helper in the virtualmachine
// package in functionality, returning a VM helper object on success.

CloneVM clones a virtual machine to a datastore cluster via the StorageResourceManager API . It mimics our helper in the virtualmachine package in functionality returning a VM helper object on success .


func ReconfigureVM(
	client *govmomi.Client,
	vm *object.VirtualMachine,
	spec types.VirtualMachineConfigSpec,
	pod *object.StoragePod,
) error {
	sdrsEnabled, err := StorageDRSEnabled(pod)
	if err != nil {
		return err
	}
	if !sdrsEnabled {
		return fmt.Errorf("storage DRS is not enabled on datastore cluster %q", pod.Name())
	}

	log.Printf(
		"[DEBUG] Reconfiguring virtual machine %q through Storage DRS API, on datastore cluster %q",
		vm.InventoryPath,
		pod.Name(),
	)

	sps := types.StoragePlacementSpec{
		Type: string(types.StoragePlacementSpecPlacementTypeReconfigure),
		PodSelectionSpec: types.StorageDrsPodSelectionSpec{
			InitialVmConfig: expandVMPodConfigForPlacement(spec.DeviceChange, pod),
		},
		Vm:         types.NewReference(vm.Reference()),
		ConfigSpec: &spec,
	}

	_, err = recommendAndApplySDRS(client, sps, provider.DefaultAPITimeout)
	return err
}

// ReconfigureVM reconfigures a virtual machine via the StorageResourceManager
// API, applying any disk modifications that will require going through Storage
// DRS. It mimics our helper in the virtualmachine package in functionality.
//
// Note that this function will fail if there are no new disks in the spec,
// check this first before using this function. If no disk create operations
// are necessary, use the regular Reconfigure function in the virtualmachine
// helper package.

ReconfigureVM reconfigures a virtual machine via the StorageResourceManager API applying any disk modifications that will require going through Storage DRS . It mimics our helper in the virtualmachine package in functionality . Note that this function will fail if there are no new disks in the spec check this first before using this function . If no disk create operations are necessary use the regular Reconfigure function in the virtualmachine helper package .


func RelocateVM(
	client *govmomi.Client,
	vm *object.VirtualMachine,
	spec types.VirtualMachineRelocateSpec,
	timeout int,
	pod *object.StoragePod,
) error {
	sdrsEnabled, err := StorageDRSEnabled(pod)
	if err != nil {
		return err
	}
	if !sdrsEnabled {
		return fmt.Errorf("storage DRS is not enabled on datastore cluster %q", pod.Name())
	}
	log.Printf(
		"[DEBUG] Relocating virtual machine %q to datastore cluster %q",
		vm.InventoryPath,
		pod.Name(),
	)

	sps := types.StoragePlacementSpec{
		Vm: types.NewReference(vm.Reference()),
		PodSelectionSpec: types.StorageDrsPodSelectionSpec{
			StoragePod: types.NewReference(pod.Reference()),
		},
		Priority:     types.VirtualMachineMovePriorityDefaultPriority,
		RelocateSpec: &spec,
		Type:         string(types.StoragePlacementSpecPlacementTypeRelocate),
	}

	_, err = recommendAndApplySDRS(client, sps, time.Minute*time.Duration(timeout))
	return err
}

// RelocateVM migrates a virtual machine to a datastore cluster via the
// StorageResourceManager API. It mimics our helper in the virtualmachine
// package in functionality.

RelocateVM migrates a virtual machine to a datastore cluster via the StorageResourceManager API . It mimics our helper in the virtualmachine package in functionality .


func HasDiskCreationOperations(dc []types.BaseVirtualDeviceConfigSpec) bool {
	for _, deviceConfigSpec := range dc {
		if _, ok := virtualDiskFromDeviceConfigSpecForPlacement(deviceConfigSpec); ok {
			return true
		}
	}

	return false
}

// HasDiskCreationOperations is an exported function that checks a list of
// device changes to see if there are any disk creation operations. This should
// be used to check if ReconfigureVM should be done through the Storage DRS
// API, as a Reconfig operation done through SDRS without new disk operations
// will fail.

HasDiskCreationOperations is an exported function that checks a list of device changes to see if there are any disk creation operations . This should be used to check if ReconfigureVM should be done through the Storage DRS API as a Reconfig operation done through SDRS without new disk operations will fail .


func IsMember(pod *object.StoragePod, ds *object.Datastore) (bool, error) {
	dprops, err := datastore.Properties(ds)
	if err != nil {
		return false, fmt.Errorf("error getting properties for datastore %q: %s", ds.Name(), err)
	}
	if dprops.Parent == nil {
		return false, nil
	}
	if *dprops.Parent != pod.Reference() {
		return false, nil
	}
	return true, nil
}

func IsMember ( pod * object . StoragePod , ds * object . Datastore ) ( bool , error ) { dprops , err := datastore . Properties ( ds ) if err != nil { return false , fmt . Errorf ( " " , ds . Name ( ) , err ) } if dprops . Parent == nil { return false , nil } if * dprops . Parent != pod . Reference ( ) { return false , nil } return true , nil }

// IsMember checks to see if a datastore is a member of the datastore cluster
// in question.
//
// This is a pretty basic operation that checks that the parent of the
// datastore is the StoragePod.

IsMember checks to see if a datastore is a member of the datastore cluster in question . This is a pretty basic operation that checks that the parent of the datastore is the StoragePod .


func NewConfig(d *schema.ResourceData) (*Config, error) {
	// Handle backcompat support for vcenter_server; once that is removed,
	// vsphere_server can just become a Required field that is referenced inline
	// in Config below.
	server := d.Get("vsphere_server").(string)

	if server == "" {
		server = d.Get("vcenter_server").(string)
	}

	if server == "" {
		return nil, fmt.Errorf("one of vsphere_server or [deprecated] vcenter_server must be provided")
	}

	c := &Config{
		User:            d.Get("user").(string),
		Password:        d.Get("password").(string),
		InsecureFlag:    d.Get("allow_unverified_ssl").(bool),
		VSphereServer:   server,
		Debug:           d.Get("client_debug").(bool),
		DebugPathRun:    d.Get("client_debug_path_run").(string),
		DebugPath:       d.Get("client_debug_path").(string),
		Persist:         d.Get("persist_session").(bool),
		VimSessionPath:  d.Get("vim_session_path").(string),
		RestSessionPath: d.Get("rest_session_path").(string),
	}

	return c, nil
}

// NewConfig returns a new Config from a supplied ResourceData.

NewConfig returns a new Config from a supplied ResourceData .


func (c *Config) Client() (*VSphereClient, error) {
	client := new(VSphereClient)

	u, err := c.vimURL()
	if err != nil {
		return nil, fmt.Errorf("Error generating SOAP endpoint url: %s", err)
	}

	err = c.EnableDebug()
	if err != nil {
		return nil, fmt.Errorf("Error setting up client debug: %s", err)
	}

	// Set up the VIM/govmomi client connection, or load a previous session
	client.vimClient, err = c.SavedVimSessionOrNew(u)
	if err != nil {
		return nil, err
	}

	log.Printf("[DEBUG] VMWare vSphere Client configured for URL: %s", c.VSphereServer)

	if isEligibleTagEndpoint(client.vimClient) {
		// Connect to the CIS REST endpoint for tagging, or load a previous session
		client.tagsClient, err = c.SavedRestSessionOrNew(u)
		if err != nil {
			return nil, err
		}
		log.Println("[DEBUG] CIS REST client configuration successful")
	} else {
		// Just print a log message so that we know that tags are not available on
		// this connection.
		log.Printf("[DEBUG] Connected endpoint does not support tags (%s)", viapi.ParseVersionFromClient(client.vimClient))
	}

	// Done, save sessions if we need to and return
	if err := c.SaveVimClient(client.vimClient); err != nil {
		return nil, fmt.Errorf("error persisting SOAP session to disk: %s", err)
	}
	if err := c.SaveRestClient(client.tagsClient); err != nil {
		return nil, fmt.Errorf("error persisting REST session to disk: %s", err)
	}

	return client, nil
}

// Client returns a new client for accessing VMWare vSphere.

Client returns a new client for accessing VMWare vSphere .


func (c *Config) EnableDebug() error {
	if !c.Debug {
		return nil
	}

	// Base path for storing debug logs.
	r := c.DebugPath
	if r == "" {
		r = filepath.Join(os.Getenv("HOME"), ".govmomi")
	}
	r = filepath.Join(r, "debug")

	// Path for this particular run.
	run := c.DebugPathRun
	if run == "" {
		now := time.Now().Format("2006-01-02T15-04-05.999999999")
		r = filepath.Join(r, now)
	} else {
		// reuse the same path
		r = filepath.Join(r, run)
		_ = os.RemoveAll(r)
	}

	err := os.MkdirAll(r, 0700)
	if err != nil {
		log.Printf("[ERROR] Client debug setup failed: %v", err)
		return err
	}

	p := debug.FileProvider{
		Path: r,
	}

	debug.SetProvider(&p)
	return nil
}

func
(
c
*
Config
)
EnableDebug
(
)
error
{
if
!
c
.
Debug
{
return
}
// Base path for storing debug logs.
r
:=
c
.
DebugPath
if
r
==
"
"
{
r
=
filepath
.
Join
(
os
.
Getenv
(
"
"
)
,
"
"
)
}
r
=
filepath
.
Join
(
r
,
"
"
)
// Path for this particular run.
run
:=
c
.
DebugPathRun
if
run
==
"
"
{
now
:=
time
.
Now
(
)
.
Format
(
"
"
)
r
=
filepath
.
Join
(
r
,
now
)
}
else
{
// reuse the same path
r
=
filepath
.
Join
(
r
,
run
)
_
=
os
.
RemoveAll
(
r
)
}
err
:=
os
.
MkdirAll
(
r
,
0700
)
if
err
!=
{
log
.
Printf
(
"
"
,
err
)
return
err
}
p
:=
debug
.
FileProvider
{
Path
:
r
,
}
debug
.
SetProvider
(
&
p
)
return
}

// EnableDebug turns on govmomi API operation logging, if appropriate settings
// are set on the provider.

EnableDebug turns on govmomi API operation logging if appropriate settings are set on the provider .


func (c *Config) sessionFile() (string, error) {
	u, err := c.vimURLWithoutPassword()
	if err != nil {
		return "", err
	}

	// Key session file off of full URI and insecure setting.
	// Hash key to get a predictable, canonical format.
	key := fmt.Sprintf("%s#insecure=%t", u.String(), c.InsecureFlag)
	name := fmt.Sprintf("%040x", sha1.Sum([]byte(key)))
	return name, nil
}

// sessionFile is a helper that generates a unique hash of the client's URL
// to use as the session file name.
//
// This is the same logic used as part of govmomi and is designed to be
// consistent so that sessions can be shared if possible between both tools.

sessionFile is a helper that generates a unique hash of the client s URL to use as the session file name . This is the same logic used as part of govmomi and is designed to be consistent so that sessions can be shared if possible between both tools .


func (c *Config) vimSessionFile() (string, error) {
	p, err := c.sessionFile()
	if err != nil {
		return "", err
	}
	return filepath.Join(c.VimSessionPath, p), nil
}

func ( c * Config ) vimSessionFile ( ) ( string , error ) { p , err := c . sessionFile ( ) if err != nil { return " " , err } return filepath . Join ( c . VimSessionPath , p ) , nil }

// vimSessionFile is takes the session file name generated by sessionFile and
// then prefixes the SOAP client session path to it.

vimSessionFile is takes the session file name generated by sessionFile and then prefixes the SOAP client session path to it .


func (c *Config) restSessionFile() (string, error) {
	p, err := c.sessionFile()
	if err != nil {
		return "", err
	}
	return filepath.Join(c.RestSessionPath, p), nil
}

func ( c * Config ) restSessionFile ( ) ( string , error ) { p , err := c . sessionFile ( ) if err != nil { return " " , err } return filepath . Join ( c . RestSessionPath , p ) , nil }

// restSessionFile is takes the session file name generated by sessionFile and
// then prefixes the REST client session path to it.

restSessionFile is takes the session file name generated by sessionFile and then prefixes the REST client session path to it .


func (c *Config) SaveVimClient(client *govmomi.Client) error {
	if !c.Persist {
		return nil
	}

	p, err := c.vimSessionFile()
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Will persist SOAP client session data to %q", p)
	err = os.MkdirAll(filepath.Dir(p), 0700)
	if err != nil {
		return err
	}

	f, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY, 0600)
	if err != nil {
		return err
	}

	defer func() {
		if err = f.Close(); err != nil {
			log.Printf("[DEBUG] Error closing SOAP client session file %q: %s", p, err)
		}
	}()

	err = json.NewEncoder(f).Encode(client.Client)
	if err != nil {
		return err
	}

	return nil
}

func ( c * Config ) SaveVimClient ( client * govmomi . Client ) error { if ! c . Persist { return nil } p , err := c . vimSessionFile ( ) if err != nil { return err } log . Printf ( " " , p ) err = os . MkdirAll ( filepath . Dir ( p ) , 0700 ) if err != nil { return err } f , err := os . OpenFile ( p , os . O_CREATE | os . O_WRONLY , 0600 ) if err != nil { return err } defer func ( ) { if err = f . Close ( ) ; err != nil { log . Printf ( " " , p , err ) } } ( ) err = json . NewEncoder ( f ) . Encode ( client . Client ) if err != nil { return err } return nil }

// SaveVimClient saves a client to the supplied path. This facilitates re-use of
// the session at a later date.
//
// Note the logic in this function has been largely adapted from govc and is
// designed to be compatible with it.

SaveVimClient saves a client to the supplied path . This facilitates re - use of the session at a later date . Note the logic in this function has been largely adapted from govc and is designed to be compatible with it .


func (c *Config) SaveRestClient(client *tags.RestClient) error {
	if !c.Persist {
		return nil
	}

	p, err := c.restSessionFile()
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Will persist REST client session data to %q", p)
	err = os.MkdirAll(filepath.Dir(p), 0700)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(p, []byte(client.SessionID()), 0600)
	if err != nil {
		return err
	}

	return nil
}

func ( c * Config ) SaveRestClient ( client * tags . RestClient ) error { if ! c . Persist { return nil } p , err := c . restSessionFile ( ) if err != nil { return err } log . Printf ( " " , p ) err = os . MkdirAll ( filepath . Dir ( p ) , 0700 ) if err != nil { return err } err = ioutil . WriteFile ( p , [ ] byte ( client . SessionID ( ) ) , 0600 ) if err != nil { return err } return nil }

// SaveRestClient saves the REST client session ID to the supplied path. This
// facilitates re-use of the session at a later date.

SaveRestClient saves the REST client session ID to the supplied path . This facilitates re - use of the session at a later date .


func (c *Config) restoreVimClient(client *vim25.Client) (bool, error) {
	if !c.Persist {
		return false, nil
	}

	p, err := c.vimSessionFile()
	if err != nil {
		return false, err
	}
	log.Printf("[DEBUG] Attempting to locate SOAP client session data in %q", p)
	f, err := os.Open(p)
	if err != nil {
		if os.IsNotExist(err) {
			log.Printf("[DEBUG] SOAP client session data not found in %q", p)
			return false, nil
		}

		return false, err
	}

	defer func() {
		if err = f.Close(); err != nil {
			log.Printf("[DEBUG] Error closing SOAP client session file %q: %s", p, err)
		}
	}()

	dec := json.NewDecoder(f)
	err = dec.Decode(client)
	if err != nil {
		return false, err
	}

	return true, nil
}

func ( c * Config ) restoreVimClient ( client * vim25 . Client ) ( bool , error ) { if ! c . Persist { return false , nil } p , err := c . vimSessionFile ( ) if err != nil { return false , err } log . Printf ( " " , p ) f , err := os . Open ( p ) if err != nil { if os . IsNotExist ( err ) { log . Printf ( " " , p ) return false , nil } return false , err } defer func ( ) { if err = f . Close ( ) ; err != nil { log . Printf ( " " , p , err ) } } ( ) dec := json . NewDecoder ( f ) err = dec . Decode ( client ) if err != nil { return false , err } return true , nil }

// restoreVimClient loads the saved session from disk. Note that this is a helper
// function to LoadVimClient and should not be called directly.

restoreVimClient loads the saved session from disk . Note that this is a helper function to LoadVimClient and should not be called directly .


func (c *Config) readRestSessionID() (string, error) {
	if !c.Persist {
		return "", nil
	}

	p, err := c.restSessionFile()
	if err != nil {
		return "", err
	}
	log.Printf("[DEBUG] Attempting to locate REST client session data in %q", p)
	id, err := ioutil.ReadFile(p)
	if err != nil {
		if os.IsNotExist(err) {
			log.Printf("[DEBUG] REST client session data not found in %q", p)
			return "", nil
		}

		return "", err
	}

	return string(id), nil
}

func ( c * Config ) readRestSessionID ( ) ( string , error ) { if ! c . Persist { return " " , nil } p , err := c . restSessionFile ( ) if err != nil { return " " , err } log . Printf ( " " , p ) id , err := ioutil . ReadFile ( p ) if err != nil { if os . IsNotExist ( err ) { log . Printf ( " " , p ) return " " , nil } return " " , err } return string ( id ) , nil }

// readRestSessionID reads a saved REST session ID and returns it. An empty
// string is returned if session does not exist.

readRestSessionID reads a saved REST session ID and returns it . An empty string is returned if session does not exist .


func (c *Config) LoadVimClient() (*govmomi.Client, error) {
	client := new(vim25.Client)
	ok, err := c.restoreVimClient(client)
	if err != nil {
		return nil, err
	}

	if !ok || !client.Valid() {
		log.Println("[DEBUG] Cached SOAP client session data not valid or persistence not enabled, new session necessary")
		return nil, nil
	}

	m := session.NewManager(client)
	u, err := m.UserSession(context.TODO())
	if err != nil {
		if soap.IsSoapFault(err) {
			fault := soap.ToSoapFault(err).VimFault()
			// If the PropertyCollector is not found, the saved session for this URL is not valid
			if _, ok := fault.(types.ManagedObjectNotFound); ok {
				log.Println("[DEBUG] Cached SOAP client session missing property collector, new session necessary")
				return nil, nil
			}
		}

		return nil, err
	}

	// If the session is nil, the client is not authenticated
	if u == nil {
		log.Println("[DEBUG] Unauthenticated session, new session necessary")
		return nil, nil
	}

	log.Println("[DEBUG] Cached SOAP client session loaded successfully")
	return &govmomi.Client{
		Client:         client,
		SessionManager: m,
	}, nil
}

// LoadVimClient loads a saved vSphere SOAP API session from disk, previously
// saved by SaveVimClient, checking it for validity before returning it. A nil
// client means that the session is no longer valid and should be created from
// scratch.
//
// Note the logic in this function has been largely adapted from govc and is
// designed to be compatible with it - if a session has already been saved with
// govc, Terraform will attempt to use that session first.

LoadVimClient loads a saved vSphere SOAP API session from disk previously saved by SaveVimClient checking it for validity before returning it . A nil client means that the session is no longer valid and should be created from scratch . Note the logic in this function has been largely adapted from govc and is designed to be compatible with it - if a session has already been saved with govc Terraform will attempt to use that session first .


func (c *Config) LoadRestClient(ctx context.Context, u *url.URL) (*tags.RestClient, bool, error) {
	id, err := c.readRestSessionID()
	if err != nil {
		return nil, false, err
	}

	client := tags.NewClientWithSessionID(u, c.InsecureFlag, "", id)

	if id == "" {
		log.Println("[DEBUG] No cached REST session data found or persistence not enabled, new session necessary")
		return client, false, nil
	}

	if !client.Valid(ctx) {
		log.Println("[DEBUG] Cached REST client session data not valid, new session necessary")
		return client, false, nil
	}

	log.Println("[DEBUG] Cached REST client session loaded successfully")
	return client, true, nil
}

func ( c * Config ) LoadRestClient ( ctx context . Context , u * url . URL ) ( * tags . RestClient , bool , error ) { id , err := c . readRestSessionID ( ) if err != nil { return nil , false , err } client := tags . NewClientWithSessionID ( u , c . InsecureFlag , " " , id ) if id == " " { log . Println ( " " ) return client , false , nil } if ! client . Valid ( ctx ) { log . Println ( " " ) return client , false , nil } log . Println ( " " ) return client , true , nil }

// LoadRestClient loads a saved vSphere REST API session from disk, previously
// saved by SaveRestClient, checking it for validity before returning it. If
// it's not valid, false is returned as the third return value, but the client
// can still be technically used for logging in by calling Login on the client.

LoadRestClient loads a saved vSphere REST API session from disk previously saved by SaveRestClient checking it for validity before returning it . If it s not valid false is returned as the third return value but the client can still be technically used for logging in by calling Login on the client .


func (c *Config) SavedVimSessionOrNew(u *url.URL) (*govmomi.Client, error) {
	ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout)
	defer cancel()

	client, err := c.LoadVimClient()
	if err != nil {
		return nil, fmt.Errorf("error trying to load vSphere SOAP session from disk: %s", err)
	}
	if client == nil {
		log.Printf("[DEBUG] Creating new SOAP API session on endpoint %s", c.VSphereServer)
		client, err = govmomi.NewClient(ctx, u, c.InsecureFlag)
		if err != nil {
			return nil, fmt.Errorf("error setting up new vSphere SOAP client: %s", err)
		}
		log.Println("[DEBUG] SOAP API session creation successful")
	}
	return client, nil
}

func ( c * Config ) SavedVimSessionOrNew ( u * url . URL ) ( * govmomi . Client , error ) { ctx , cancel := context . WithTimeout ( context . Background ( ) , defaultAPITimeout ) defer cancel ( ) client , err := c . LoadVimClient ( ) if err != nil { return nil , fmt . Errorf ( " " , err ) } if client == nil { log . Printf ( " " , c . VSphereServer ) client , err = govmomi . NewClient ( ctx , u , c . InsecureFlag ) if err != nil { return nil , fmt . Errorf ( " " , err ) } log . Println ( " " ) } return client , nil }

// SavedVimSessionOrNew either loads a saved SOAP session from disk, or creates
// a new one.

SavedVimSessionOrNew either loads a saved SOAP session from disk or creates a new one .


func (c *Config) SavedRestSessionOrNew(u *url.URL) (*tags.RestClient, error) {
	ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout)
	defer cancel()

	client, valid, err := c.LoadRestClient(ctx, u)
	if err != nil {
		return nil, fmt.Errorf("error trying to load vSphere REST session from disk: %s", err)
	}
	if !valid {
		log.Printf("[DEBUG] Creating new CIS REST API session on endpoint %s", c.VSphereServer)
		if err := client.Login(ctx); err != nil {
			return nil, fmt.Errorf("Error connecting to CIS REST endpoint: %s", err)
		}
		log.Println("[DEBUG] CIS REST API session creation successful")
	}
	return client, nil
}

func ( c * Config ) SavedRestSessionOrNew ( u * url . URL ) ( * tags . RestClient , error ) { ctx , cancel := context . WithTimeout ( context . Background ( ) , defaultAPITimeout ) defer cancel ( ) client , valid , err := c . LoadRestClient ( ctx , u ) if err != nil { return nil , fmt . Errorf ( " " , err ) } if ! valid { log . Printf ( " " , c . VSphereServer ) if err := client . Login ( ctx ) ; err != nil { return nil , fmt . Errorf ( " " , err ) } log . Println ( " " ) } return client , nil }

// SavedRestSessionOrNew either loads a saved REST session from disk, or creates
// a new one.

SavedRestSessionOrNew either loads a saved REST session from disk or creates a new one .


func (w *virtualMachineCustomizationWaiter) wait(client *govmomi.Client, vm *object.VirtualMachine, timeout int) error {
	// A timeout of less than 1 minute (zero or negative value) skips the waiter,
	// so we return immediately.
	if timeout < 1 {
		return nil
	}

	// Our listener loop callback.
	cbErr := make(chan error, 1)
	cb := func(obj types.ManagedObjectReference, page []types.BaseEvent) error {
		for _, be := range page {
			switch e := be.(type) {
			case types.BaseCustomizationFailed:
				cbErr <- errors.New(e.GetCustomizationFailed().GetEvent().FullFormattedMessage)
			case *types.CustomizationSucceeded:
				close(cbErr)
			}
		}
		return nil
	}

	mgr := event.NewManager(client.Client)
	mgrErr := make(chan error, 1)
	// Make a proper background context so that we can gracefully cancel the
	// subscriber when we are done with it. This eventually gets passed down to
	// the property collector SOAP calls.
	pctx, pcancel := context.WithCancel(context.Background())
	defer pcancel()
	go func() {
		mgrErr <- mgr.Events(pctx, []types.ManagedObjectReference{vm.Reference()}, 10, true, false, cb)
	}()

	// Wait for any error condition (including nil from the closure of the
	// callback error channel on success). We also use a different context so
	// that we can give a better error message on timeout without interfering
	// with the subscriber's context.
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Minute)
	defer cancel()
	var err error
	select {
	case <-ctx.Done():
		if ctx.Err() == context.DeadlineExceeded {
			err = fmt.Errorf("timeout waiting for customization to complete")
		}
	case err = <-mgrErr:
	case err = <-cbErr:
	}
	return err
}

// wait waits for the customization of a supplied VirtualMachine to complete,
// either due to success or error. It does this by watching specifically for
// CustomizationSucceeded and CustomizationFailed events. If the customization
// failed due to some sort of error, the full formatted message is returned as
// an error.

wait waits for the customization of a supplied VirtualMachine to complete either due to success or error . It does this by watching specifically for CustomizationSucceeded and CustomizationFailed events . If the customization failed due to some sort of error the full formatted message is returned as an error .


func selectEventsForReference(client *govmomi.Client, ref types.ManagedObjectReference, eventTypes []string) ([]types.BaseEvent, error) {
	ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout)
	defer cancel()
	filter := types.EventFilterSpec{
		Entity: &types.EventFilterSpecByEntity{
			Entity:    ref,
			Recursion: types.EventFilterSpecRecursionOptionAll,
		},
		EventTypeId: eventTypes,
	}
	mgr := event.NewManager(client.Client)
	return mgr.QueryEvents(ctx, filter)
}

func selectEventsForReference ( client * govmomi . Client , ref types . ManagedObjectReference , eventTypes [ ] string ) ( [ ] types . BaseEvent , error ) { ctx , cancel := context . WithTimeout ( context . Background ( ) , defaultAPITimeout ) defer cancel ( ) filter := types . EventFilterSpec { Entity : & types . EventFilterSpecByEntity { Entity : ref , Recursion : types . EventFilterSpecRecursionOptionAll , } , EventTypeId : eventTypes , } mgr := event . NewManager ( client . Client ) return mgr . QueryEvents ( ctx , filter ) }

// selectEventsForReference allows you to query events for a specific
// ManagedObjectReference.
//
// Event types can be supplied to this function via the eventTypes parameter.
// This is highly recommended when you expect the list of events to be large,
// as there is no limit on returned events.

selectEventsForReference allows you to query events for a specific ManagedObjectReference . Event types can be supplied to this function via the eventTypes parameter . This is highly recommended when you expect the list of events to be large as there is no limit on returned events .


func getDatastore(f *find.Finder, ds string) (*object.Datastore, error) {

	if ds != "" {
		dso, err := f.Datastore(context.TODO(), ds)
		return dso, err
	} else {
		dso, err := f.DefaultDatastore(context.TODO())
		return dso, err
	}
}

func getDatastore ( f * find . Finder , ds string ) ( * object . Datastore , error ) { if ds != " " { dso , err := f . Datastore ( context . TODO ( ) , ds ) return dso , err } else { dso , err := f . DefaultDatastore ( context . TODO ( ) ) return dso , err } }


func resourceVSphereVirtualMachineUpdateReconfigureWithSDRS(
	d *schema.ResourceData,
	meta interface{},
	vm *object.VirtualMachine,
	spec types.VirtualMachineConfigSpec,
) error {
	// Check to see if we have any disk creation operations first, as sending an
	// update through SDRS without any disk creation operations will fail.
	if !storagepod.HasDiskCreationOperations(spec.DeviceChange) {
		log.Printf("[DEBUG] No disk operations for reconfiguration of VM %q, deferring to standard API", vm.InventoryPath)
		return virtualmachine.Reconfigure(vm, spec)
	}

	client := meta.(*VSphereClient).vimClient
	if err := viapi.ValidateVirtualCenter(client); err != nil {
		return fmt.Errorf("connection ineligible to use datastore_cluster_id: %s", err)
	}

	log.Printf("[DEBUG] %s: Reconfiguring virtual machine through Storage DRS API", resourceVSphereVirtualMachineIDString(d))
	pod, err := storagepod.FromID(client, d.Get("datastore_cluster_id").(string))
	if err != nil {
		return fmt.Errorf("error getting datastore cluster: %s", err)
	}

	err = storagepod.ReconfigureVM(client, vm, spec, pod)
	if err != nil {
		return fmt.Errorf("error reconfiguring VM on datastore cluster %q: %s", pod.Name(), err)
	}
	return nil
}

// resourceVSphereVirtualMachineUpdateReconfigureWithSDRS runs the reconfigure
// part of resourceVSphereVirtualMachineUpdate through storage DRS. It's
// designed to be run when a storage cluster is specified, versus simply
// specifying datastores.


func resourceVSphereVirtualMachineCreateBareWithSDRS(
	d *schema.ResourceData,
	meta interface{},
	fo *object.Folder,
	spec types.VirtualMachineConfigSpec,
	pool *object.ResourcePool,
	hs *object.HostSystem,
) (*object.VirtualMachine, error) {
	client := meta.(*VSphereClient).vimClient
	if err := viapi.ValidateVirtualCenter(client); err != nil {
		return nil, fmt.Errorf("connection ineligible to use datastore_cluster_id: %s", err)
	}

	log.Printf("[DEBUG] %s: Creating virtual machine through Storage DRS API", resourceVSphereVirtualMachineIDString(d))
	pod, err := storagepod.FromID(client, d.Get("datastore_cluster_id").(string))
	if err != nil {
		return nil, fmt.Errorf("error getting datastore cluster: %s", err)
	}

	vm, err := storagepod.CreateVM(client, fo, spec, pool, hs, pod)
	if err != nil {
		return nil, fmt.Errorf("error creating virtual machine on datastore cluster %q: %s", pod.Name(), err)
	}

	return vm, nil
}

// resourceVSphereVirtualMachineCreateBareWithSDRS runs the creation part of
// resourceVSphereVirtualMachineCreateBare through storage DRS. It's designed
// to be run when a storage cluster is specified, versus simply specifying
// datastores.


func resourceVSphereVirtualMachineCreateBareStandard(
	d *schema.ResourceData,
	meta interface{},
	fo *object.Folder,
	spec types.VirtualMachineConfigSpec,
	pool *object.ResourcePool,
	hs *object.HostSystem,
) (*object.VirtualMachine, error) {
	client := meta.(*VSphereClient).vimClient

	// Set the datastore for the VM.
	ds, err := datastore.FromID(client, d.Get("datastore_id").(string))
	if err != nil {
		return nil, fmt.Errorf("error locating datastore for VM: %s", err)
	}
	spec.Files = &types.VirtualMachineFileInfo{
		VmPathName: fmt.Sprintf("[%s]", ds.Name()),
	}

	vm, err := virtualmachine.Create(client, fo, spec, pool, hs)
	if err != nil {
		return nil, fmt.Errorf("error creating virtual machine: %s", err)
	}
	return vm, nil
}

// resourceVSphereVirtualMachineCreateBareStandard performs the steps necessary
// during resourceVSphereVirtualMachineCreateBare to create a virtual machine
// when a datastore cluster is not supplied.


func resourceVSphereVirtualMachineCreateCloneWithSDRS(
	d *schema.ResourceData,
	meta interface{},
	srcVM *object.VirtualMachine,
	fo *object.Folder,
	name string,
	spec types.VirtualMachineCloneSpec,
	timeout int,
) (*object.VirtualMachine, error) {
	client := meta.(*VSphereClient).vimClient
	if err := viapi.ValidateVirtualCenter(client); err != nil {
		return nil, fmt.Errorf("connection ineligible to use datastore_cluster_id: %s", err)
	}

	log.Printf("[DEBUG] %s: Cloning virtual machine through Storage DRS API", resourceVSphereVirtualMachineIDString(d))
	pod, err := storagepod.FromID(client, d.Get("datastore_cluster_id").(string))
	if err != nil {
		return nil, fmt.Errorf("error getting datastore cluster: %s", err)
	}

	vm, err := storagepod.CloneVM(client, srcVM, fo, name, spec, timeout, pod)
	if err != nil {
		return nil, fmt.Errorf("error cloning on datastore cluster %q: %s", pod.Name(), err)
	}

	return vm, nil
}

// resourceVSphereVirtualMachineCreateCloneWithSDRS runs the clone part of
// resourceVSphereVirtualMachineCreateClone through storage DRS. It's designed
// to be run when a storage cluster is specified, versus simply specifying
// datastores.


func resourceVSphereVirtualMachineRollbackCreate(
	d *schema.ResourceData,
	meta interface{},
	vm *object.VirtualMachine,
	origErr error,
) error {
	defer d.SetId("")
	// Updates are largely atomic, so more than likely no disks with
	// keep_on_remove were attached, but just in case, we run this through delete
	// to make sure to safely remove any disk that may have been attached as part
	// of this process if it was flagged as such.
	if err := resourceVSphereVirtualMachineDelete(d, meta); err != nil {
		return fmt.Errorf(formatVirtualMachinePostCloneRollbackError, vm.InventoryPath, origErr, err)
	}
	return fmt.Errorf("error reconfiguring virtual machine: %s", origErr)
}

// resourceVSphereVirtualMachineRollbackCreate attempts to "roll back" a
// resource due to an error that happened post-create that will put the VM in a
// state where it cannot be worked with. This should only be done early on in
// the process, namely on clone operations between when the clone actually
// happens, and no later than after the initial post-clone update is complete.
//
// If the rollback fails, an error is displayed prompting the user to manually
// delete the virtual machine before trying again.


func resourceVSphereVirtualMachineUpdateLocationRelocateWithSDRS(
	d *schema.ResourceData,
	meta interface{},
	vm *object.VirtualMachine,
	spec types.VirtualMachineRelocateSpec,
	timeout int,
) error {
	client := meta.(*VSphereClient).vimClient
	if err := viapi.ValidateVirtualCenter(client); err != nil {
		return fmt.Errorf("connection ineligible to use datastore_cluster_id: %s", err)
	}

	log.Printf("[DEBUG] %s: Running virtual machine relocate Storage DRS API", resourceVSphereVirtualMachineIDString(d))
	pod, err := storagepod.FromID(client, d.Get("datastore_cluster_id").(string))
	if err != nil {
		return fmt.Errorf("error getting datastore cluster: %s", err)
	}

	err = storagepod.RelocateVM(client, vm, spec, timeout, pod)
	if err != nil {
		return fmt.Errorf("error running vMotion on datastore cluster %q: %s", pod.Name(), err)
	}
	return nil
}

// resourceVSphereVirtualMachineUpdateLocationRelocateWithSDRS runs the storage vMotion
// part of resourceVSphereVirtualMachineUpdateLocation through storage DRS.
// It's designed to be run when a storage cluster is specified, versus simply
// specifying datastores.


func applyVirtualDevices(d *schema.ResourceData, c *govmomi.Client, l object.VirtualDeviceList) ([]types.BaseVirtualDeviceConfigSpec, error) {
	// We filter this device list through each major device class' apply
	// operation. This will give us a final set of changes that will be our
	// deviceChange attribute.
	var spec, delta []types.BaseVirtualDeviceConfigSpec
	var err error
	// First check the state of our SCSI bus. Normalize it if we need to.
	l, delta, err = virtualdevice.NormalizeSCSIBus(l, d.Get("scsi_type").(string), d.Get("scsi_controller_count").(int), d.Get("scsi_bus_sharing").(string))
	if err != nil {
		return nil, err
	}
	if len(delta) > 0 {
		log.Printf("[DEBUG] %s: SCSI bus has changed and requires a VM restart", resourceVSphereVirtualMachineIDString(d))
		d.Set("reboot_required", true)
	}
	spec = virtualdevice.AppendDeviceChangeSpec(spec, delta...)
	// Disks
	l, delta, err = virtualdevice.DiskApplyOperation(d, c, l)
	if err != nil {
		return nil, err
	}
	spec = virtualdevice.AppendDeviceChangeSpec(spec, delta...)
	// Network devices
	l, delta, err = virtualdevice.NetworkInterfaceApplyOperation(d, c, l)
	if err != nil {
		return nil, err
	}
	spec = virtualdevice.AppendDeviceChangeSpec(spec, delta...)
	// CDROM
	l, delta, err = virtualdevice.CdromApplyOperation(d, c, l)
	if err != nil {
		return nil, err
	}
	spec = virtualdevice.AppendDeviceChangeSpec(spec, delta...)
	log.Printf("[DEBUG] %s: Final device list: %s", resourceVSphereVirtualMachineIDString(d), virtualdevice.DeviceListString(l))
	log.Printf("[DEBUG] %s: Final device change spec: %s", resourceVSphereVirtualMachineIDString(d), virtualdevice.DeviceChangeString(spec))
	return spec, nil
}

// applyVirtualDevices is used by Create and Update to build a list of virtual
// device changes.

applyVirtualDevices is used by Create and Update to build a list of virtual device changes .


func expandClusterDpmHostConfigInfo(d *schema.ResourceData, host *object.HostSystem) (*types.ClusterDpmHostConfigInfo, error) {
	obj := &types.ClusterDpmHostConfigInfo{
		Behavior: types.DpmBehavior(d.Get("dpm_automation_level").(string)),
		Enabled:  structure.GetBool(d, "dpm_enabled"),
		Key:      host.Reference(),
	}

	return obj, nil
}

func expandClusterDpmHostConfigInfo ( d * schema . ResourceData , host * object . HostSystem ) ( * types . ClusterDpmHostConfigInfo , error ) { obj := & types . ClusterDpmHostConfigInfo { Behavior : types . DpmBehavior ( d . Get ( " " ) . ( string ) ) , Enabled : structure . GetBool ( d , " " ) , Key : host . Reference ( ) , } return obj , nil }

// expandClusterDrsVMConfigInfo reads certain ResourceData keys and returns a
// ClusterDpmHostConfigInfo.

expandClusterDrsVMConfigInfo reads certain ResourceData keys and returns a ClusterDpmHostConfigInfo .


func flattenClusterDpmHostConfigInfo(d *schema.ResourceData, obj *types.ClusterDpmHostConfigInfo) error {
	return structure.SetBatch(d, map[string]interface{}{
		"dpm_automation_level": obj.Behavior,
		"dpm_enabled":          obj.Enabled,
	})
}

func flattenClusterDpmHostConfigInfo ( d * schema . ResourceData , obj * types . ClusterDpmHostConfigInfo ) error { return structure . SetBatch ( d , map [ string ] interface { } { " " : obj . Behavior , " " : obj . Enabled , } ) }

// flattenClusterDpmHostConfigInfo saves a ClusterDpmHostConfigInfo into the
// supplied ResourceData.

flattenClusterDpmHostConfigInfo saves a ClusterDpmHostConfigInfo into the supplied ResourceData .


func resourceVSphereDPMHostOverrideFlattenID(cluster *object.ClusterComputeResource, host *object.HostSystem) (string, error) {
	return strings.Join([]string{cluster.Reference().Value, host.Reference().Value}, ":"), nil
}

func resourceVSphereDPMHostOverrideFlattenID ( cluster * object . ClusterComputeResource , host * object . HostSystem ) ( string , error ) { return strings . Join ( [ ] string { cluster . Reference ( ) . Value , host . Reference ( ) . Value } , " " ) , nil }

// resourceVSphereDPMHostOverrideFlattenID makes an ID for the
// vsphere_dpm_host_override resource.

resourceVSphereDPMHostOverrideFlattenID makes an ID for the vsphere_dpm_host_override resource .


func resourceVSphereDPMHostOverrideFindEntry(
	cluster *object.ClusterComputeResource,
	host *object.HostSystem,
) (*types.ClusterDpmHostConfigInfo, error) {
	props, err := clustercomputeresource.Properties(cluster)
	if err != nil {
		return nil, fmt.Errorf("error fetching cluster properties: %s", err)
	}

	for _, info := range props.ConfigurationEx.(*types.ClusterConfigInfoEx).DpmHostConfig {
		if info.Key == host.Reference() {
			log.Printf("[DEBUG] Found DPM config info for host %q in cluster %q", host.Name(), cluster.Name())
			return &info, nil
		}
	}

	log.Printf("[DEBUG] No DPM config info found for host %q in cluster %q", host.Name(), cluster.Name())
	return nil, nil
}

func resourceVSphereDPMHostOverrideFindEntry ( cluster * object . ClusterComputeResource , host * object . HostSystem , ) ( * types . ClusterDpmHostConfigInfo , error ) { props , err := clustercomputeresource . Properties ( cluster ) if err != nil { return nil , fmt . Errorf ( " " , err ) } for _ , info := range props . ConfigurationEx . ( * types . ClusterConfigInfoEx ) . DpmHostConfig { if info . Key == host . Reference ( ) { log . Printf ( " " , host . Name ( ) , cluster . Name ( ) ) return & info , nil } } log . Printf ( " " , host . Name ( ) , cluster . Name ( ) ) return nil , nil }

// resourceVSphereDPMHostOverrideFindEntry attempts to locate an existing DRS VM
// config in a cluster's configuration. It's used by the resource's read
// functionality and tests. nil is returned if the entry cannot be found.

resourceVSphereDPMHostOverrideFindEntry attempts to locate an existing DRS VM config in a cluster s configuration . It s used by the resource s read functionality and tests . nil is returned if the entry cannot be found .


func resourceVSphereVirtualDisk() *schema.Resource {
	return &schema.Resource{
		Create: resourceVSphereVirtualDiskCreate,
		Read:   resourceVSphereVirtualDiskRead,
		Delete: resourceVSphereVirtualDiskDelete,

		Schema: map[string]*schema.Schema{
			// Size in GB
			"size": {
				Type:     schema.TypeInt,
				Required: true,
				ForceNew: true, //TODO Can this be optional (resize)?
			},

			// TODO:
			//
			// * Add extra lifecycles (move, rename, etc). May not be possible
			// without breaking other resources though.
			// * Add validation (make sure it ends in .vmdk)
			"vmdk_path": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"datastore": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"type": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Default:  "eagerZeroedThick",
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					value := v.(string)
					if value != "thin" && value != "eagerZeroedThick" && value != "lazy" {
						errors = append(errors, fmt.Errorf(
							"only 'thin', 'eagerZeroedThick', and 'lazy' are supported values for 'type'"))
					}
					return
				},
			},

			"adapter_type": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
				Default:  "lsiLogic",
				// TODO: Move this to removed after we remove the support to specify this in later versions
				Deprecated: "this attribute has no effect on controller types - please use scsi_type in vsphere_virtual_machine instead",
				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
					value := v.(string)
					if value != "ide" && value != "busLogic" && value != "lsiLogic" {
						errors = append(errors, fmt.Errorf(
							"only 'ide', 'busLogic', and 'lsiLogic' are supported values for 'adapter_type'"))
					}
					return
				},
			},

			"datacenter": {
				Type:     schema.TypeString,
				Optional: true,
				ForceNew: true,
			},

			"create_directories": {
				Type:     schema.TypeBool,
				Optional: true,
				ForceNew: true,
			},
		},
	}
}