github.com/vmware/govmomi@v0.51.0/simulator/cluster_compute_resource.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package simulator
     6  
     7  import (
     8  	"log"
     9  	"math/rand"
    10  	"sync/atomic"
    11  	"time"
    12  
    13  	"github.com/google/uuid"
    14  
    15  	"github.com/vmware/govmomi/object"
    16  	"github.com/vmware/govmomi/simulator/esx"
    17  	"github.com/vmware/govmomi/vim25/methods"
    18  	"github.com/vmware/govmomi/vim25/mo"
    19  	"github.com/vmware/govmomi/vim25/soap"
    20  	"github.com/vmware/govmomi/vim25/types"
    21  )
    22  
    23  type ClusterComputeResource struct {
    24  	mo.ClusterComputeResource
    25  
    26  	ruleKey int32
    27  }
    28  
    29  func (c *ClusterComputeResource) RenameTask(ctx *Context, req *types.Rename_Task) soap.HasFault {
    30  	return RenameTask(ctx, c, req)
    31  }
    32  
    33  type addHost struct {
    34  	*ClusterComputeResource
    35  
    36  	req *types.AddHost_Task
    37  }
    38  
    39  func (add *addHost) Run(task *Task) (types.AnyType, types.BaseMethodFault) {
    40  	spec := add.req.Spec
    41  
    42  	if spec.HostName == "" {
    43  		return nil, &types.NoHost{}
    44  	}
    45  
    46  	cr := add.ClusterComputeResource
    47  	template := esx.HostSystem
    48  
    49  	if h := task.ctx.Map.FindByName(spec.UserName, cr.Host); h != nil {
    50  		// "clone" an existing host from the inventory
    51  		template = h.(*HostSystem).HostSystem
    52  		template.Vm = nil
    53  	} else {
    54  		template.Network = cr.Network[:1] // VM Network
    55  	}
    56  
    57  	host := NewHostSystem(task.ctx, template)
    58  	host.configure(task.ctx, spec, add.req.AsConnected)
    59  
    60  	task.ctx.Map.PutEntity(cr, task.ctx.Map.NewEntity(host))
    61  	host.Summary.Host = &host.Self
    62  	host.Config.Host = host.Self
    63  
    64  	task.ctx.Map.WithLock(task.ctx, *cr.EnvironmentBrowser, func() {
    65  		eb := task.ctx.Map.Get(*cr.EnvironmentBrowser).(*EnvironmentBrowser)
    66  		eb.addHost(task.ctx, host.Self)
    67  	})
    68  
    69  	cr.Host = append(cr.Host, host.Reference())
    70  	addComputeResource(cr.Summary.GetComputeResourceSummary(), host)
    71  
    72  	if cr.vsanIsEnabled() {
    73  		cr.addStorageHost(task.ctx, host.Self)
    74  	}
    75  
    76  	return host.Reference(), nil
    77  }
    78  
    79  func (c *ClusterComputeResource) AddHostTask(ctx *Context, add *types.AddHost_Task) soap.HasFault {
    80  	return &methods.AddHost_TaskBody{
    81  		Res: &types.AddHost_TaskResponse{
    82  			Returnval: NewTask(&addHost{c, add}).Run(ctx),
    83  		},
    84  	}
    85  }
    86  
    87  func (c *ClusterComputeResource) vsanIsEnabled() bool {
    88  	if cfg := c.ConfigurationEx.(*types.ClusterConfigInfoEx).VsanConfigInfo; cfg != nil {
    89  		return isTrue(cfg.Enabled)
    90  	}
    91  	return false
    92  }
    93  
    94  func (c *ClusterComputeResource) update(_ *Context, cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
    95  	if cspec.DasConfig != nil {
    96  		if val := cspec.DasConfig.Enabled; val != nil {
    97  			cfg.DasConfig.Enabled = val
    98  		}
    99  		if val := cspec.DasConfig.AdmissionControlEnabled; val != nil {
   100  			cfg.DasConfig.AdmissionControlEnabled = val
   101  		}
   102  	}
   103  	if cspec.DrsConfig != nil {
   104  		if val := cspec.DrsConfig.Enabled; val != nil {
   105  			cfg.DrsConfig.Enabled = val
   106  		}
   107  		if val := cspec.DrsConfig.DefaultVmBehavior; val != "" {
   108  			cfg.DrsConfig.DefaultVmBehavior = val
   109  		}
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  func (c *ClusterComputeResource) updateRules(_ *Context, cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
   116  	for _, spec := range cspec.RulesSpec {
   117  		var i int
   118  		exists := false
   119  
   120  		match := func(info types.BaseClusterRuleInfo) bool {
   121  			return info.GetClusterRuleInfo().Name == spec.Info.GetClusterRuleInfo().Name
   122  		}
   123  
   124  		if spec.Operation == types.ArrayUpdateOperationRemove {
   125  			match = func(rule types.BaseClusterRuleInfo) bool {
   126  				return rule.GetClusterRuleInfo().Key == spec.ArrayUpdateSpec.RemoveKey.(int32)
   127  			}
   128  		}
   129  
   130  		for i = range cfg.Rule {
   131  			if match(cfg.Rule[i].GetClusterRuleInfo()) {
   132  				exists = true
   133  				break
   134  			}
   135  		}
   136  
   137  		switch spec.Operation {
   138  		case types.ArrayUpdateOperationAdd:
   139  			if exists {
   140  				return new(types.InvalidArgument)
   141  			}
   142  			info := spec.Info.GetClusterRuleInfo()
   143  			info.Key = atomic.AddInt32(&c.ruleKey, 1)
   144  			info.RuleUuid = uuid.New().String()
   145  			cfg.Rule = append(cfg.Rule, spec.Info)
   146  		case types.ArrayUpdateOperationEdit:
   147  			if !exists {
   148  				return new(types.InvalidArgument)
   149  			}
   150  			cfg.Rule[i] = spec.Info
   151  		case types.ArrayUpdateOperationRemove:
   152  			if !exists {
   153  				return new(types.InvalidArgument)
   154  			}
   155  			cfg.Rule = append(cfg.Rule[:i], cfg.Rule[i+1:]...)
   156  		}
   157  	}
   158  
   159  	return nil
   160  }
   161  
   162  func (c *ClusterComputeResource) updateGroups(_ *Context, cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
   163  	for _, spec := range cspec.GroupSpec {
   164  		var i int
   165  		exists := false
   166  
   167  		match := func(info types.BaseClusterGroupInfo) bool {
   168  			return info.GetClusterGroupInfo().Name == spec.Info.GetClusterGroupInfo().Name
   169  		}
   170  
   171  		if spec.Operation == types.ArrayUpdateOperationRemove {
   172  			match = func(info types.BaseClusterGroupInfo) bool {
   173  				return info.GetClusterGroupInfo().Name == spec.ArrayUpdateSpec.RemoveKey.(string)
   174  			}
   175  		}
   176  
   177  		for i = range cfg.Group {
   178  			if match(cfg.Group[i].GetClusterGroupInfo()) {
   179  				exists = true
   180  				break
   181  			}
   182  		}
   183  
   184  		switch spec.Operation {
   185  		case types.ArrayUpdateOperationAdd:
   186  			if exists {
   187  				return new(types.InvalidArgument)
   188  			}
   189  			cfg.Group = append(cfg.Group, spec.Info)
   190  		case types.ArrayUpdateOperationEdit:
   191  			if !exists {
   192  				return new(types.InvalidArgument)
   193  			}
   194  			cfg.Group[i] = spec.Info
   195  		case types.ArrayUpdateOperationRemove:
   196  			if !exists {
   197  				return new(types.InvalidArgument)
   198  			}
   199  			cfg.Group = append(cfg.Group[:i], cfg.Group[i+1:]...)
   200  		}
   201  	}
   202  
   203  	return nil
   204  }
   205  
   206  func (c *ClusterComputeResource) updateOverridesDAS(_ *Context, cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
   207  	for _, spec := range cspec.DasVmConfigSpec {
   208  		var i int
   209  		var key types.ManagedObjectReference
   210  		exists := false
   211  
   212  		if spec.Operation == types.ArrayUpdateOperationRemove {
   213  			key = spec.RemoveKey.(types.ManagedObjectReference)
   214  		} else {
   215  			key = spec.Info.Key
   216  		}
   217  
   218  		for i = range cfg.DasVmConfig {
   219  			if cfg.DasVmConfig[i].Key == key {
   220  				exists = true
   221  				break
   222  			}
   223  		}
   224  
   225  		switch spec.Operation {
   226  		case types.ArrayUpdateOperationAdd:
   227  			if exists {
   228  				return new(types.InvalidArgument)
   229  			}
   230  			cfg.DasVmConfig = append(cfg.DasVmConfig, *spec.Info)
   231  		case types.ArrayUpdateOperationEdit:
   232  			if !exists {
   233  				return new(types.InvalidArgument)
   234  			}
   235  			src := spec.Info.DasSettings
   236  			if src == nil {
   237  				return new(types.InvalidArgument)
   238  			}
   239  			dst := cfg.DasVmConfig[i].DasSettings
   240  			if src.RestartPriority != "" {
   241  				dst.RestartPriority = src.RestartPriority
   242  			}
   243  			if src.RestartPriorityTimeout != 0 {
   244  				dst.RestartPriorityTimeout = src.RestartPriorityTimeout
   245  			}
   246  		case types.ArrayUpdateOperationRemove:
   247  			if !exists {
   248  				return new(types.InvalidArgument)
   249  			}
   250  			cfg.DasVmConfig = append(cfg.DasVmConfig[:i], cfg.DasVmConfig[i+1:]...)
   251  		}
   252  	}
   253  
   254  	return nil
   255  }
   256  
   257  func (c *ClusterComputeResource) updateOverridesDRS(_ *Context, cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
   258  	for _, spec := range cspec.DrsVmConfigSpec {
   259  		var i int
   260  		var key types.ManagedObjectReference
   261  		exists := false
   262  
   263  		if spec.Operation == types.ArrayUpdateOperationRemove {
   264  			key = spec.RemoveKey.(types.ManagedObjectReference)
   265  		} else {
   266  			key = spec.Info.Key
   267  		}
   268  
   269  		for i = range cfg.DrsVmConfig {
   270  			if cfg.DrsVmConfig[i].Key == key {
   271  				exists = true
   272  				break
   273  			}
   274  		}
   275  
   276  		switch spec.Operation {
   277  		case types.ArrayUpdateOperationAdd:
   278  			if exists {
   279  				return new(types.InvalidArgument)
   280  			}
   281  			cfg.DrsVmConfig = append(cfg.DrsVmConfig, *spec.Info)
   282  		case types.ArrayUpdateOperationEdit:
   283  			if !exists {
   284  				return new(types.InvalidArgument)
   285  			}
   286  			if spec.Info.Enabled != nil {
   287  				cfg.DrsVmConfig[i].Enabled = spec.Info.Enabled
   288  			}
   289  			if spec.Info.Behavior != "" {
   290  				cfg.DrsVmConfig[i].Behavior = spec.Info.Behavior
   291  			}
   292  		case types.ArrayUpdateOperationRemove:
   293  			if !exists {
   294  				return new(types.InvalidArgument)
   295  			}
   296  			cfg.DrsVmConfig = append(cfg.DrsVmConfig[:i], cfg.DrsVmConfig[i+1:]...)
   297  		}
   298  	}
   299  
   300  	return nil
   301  }
   302  
   303  func (c *ClusterComputeResource) updateOverridesVmOrchestration(_ *Context, cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
   304  	for _, spec := range cspec.VmOrchestrationSpec {
   305  		var i int
   306  		var key types.ManagedObjectReference
   307  		exists := false
   308  
   309  		if spec.Operation == types.ArrayUpdateOperationRemove {
   310  			key = spec.RemoveKey.(types.ManagedObjectReference)
   311  		} else {
   312  			key = spec.Info.Vm
   313  		}
   314  
   315  		for i = range cfg.VmOrchestration {
   316  			if cfg.VmOrchestration[i].Vm == key {
   317  				exists = true
   318  				break
   319  			}
   320  		}
   321  
   322  		switch spec.Operation {
   323  		case types.ArrayUpdateOperationAdd:
   324  			if exists {
   325  				return new(types.InvalidArgument)
   326  			}
   327  			cfg.VmOrchestration = append(cfg.VmOrchestration, *spec.Info)
   328  		case types.ArrayUpdateOperationEdit:
   329  			if !exists {
   330  				return new(types.InvalidArgument)
   331  			}
   332  			if spec.Info.VmReadiness.ReadyCondition != "" {
   333  				cfg.VmOrchestration[i].VmReadiness.ReadyCondition = spec.Info.VmReadiness.ReadyCondition
   334  			}
   335  			if spec.Info.VmReadiness.PostReadyDelay != 0 {
   336  				cfg.VmOrchestration[i].VmReadiness.PostReadyDelay = spec.Info.VmReadiness.PostReadyDelay
   337  			}
   338  		case types.ArrayUpdateOperationRemove:
   339  			if !exists {
   340  				return new(types.InvalidArgument)
   341  			}
   342  			cfg.VmOrchestration = append(cfg.VmOrchestration[:i], cfg.VmOrchestration[i+1:]...)
   343  		}
   344  	}
   345  
   346  	return nil
   347  }
   348  
   349  func (c *ClusterComputeResource) addStorageHost(ctx *Context, ref types.ManagedObjectReference) types.BaseMethodFault {
   350  	ds := ctx.Map.Get(ref).(*HostSystem).ConfigManager.DatastoreSystem
   351  	hds := ctx.Map.Get(*ds).(*HostDatastoreSystem)
   352  	return hds.createVsanDatastore(ctx)
   353  }
   354  
   355  func (c *ClusterComputeResource) updateVSAN(ctx *Context, cfg *types.ClusterConfigInfoEx, cspec *types.ClusterConfigSpecEx) types.BaseMethodFault {
   356  	if cspec.VsanConfig == nil {
   357  		return nil
   358  	}
   359  
   360  	if cfg.VsanConfigInfo == nil {
   361  		cfg.VsanConfigInfo = cspec.VsanConfig
   362  		if cfg.VsanConfigInfo.DefaultConfig == nil {
   363  			cfg.VsanConfigInfo.DefaultConfig = new(types.VsanClusterConfigInfoHostDefaultInfo)
   364  		}
   365  	} else {
   366  		if cspec.VsanConfig.Enabled != nil {
   367  			cfg.VsanConfigInfo.Enabled = cspec.VsanConfig.Enabled
   368  		}
   369  	}
   370  
   371  	if cfg.VsanConfigInfo.DefaultConfig.Uuid == "" {
   372  		cfg.VsanConfigInfo.DefaultConfig.Uuid = uuid.NewString()
   373  	}
   374  
   375  	if isTrue(cfg.VsanConfigInfo.Enabled) {
   376  		for _, ref := range c.Host {
   377  			if err := c.addStorageHost(ctx, ref); err != nil {
   378  				return err
   379  			}
   380  		}
   381  	}
   382  
   383  	return nil
   384  }
   385  
   386  func (c *ClusterComputeResource) ReconfigureComputeResourceTask(ctx *Context, req *types.ReconfigureComputeResource_Task) soap.HasFault {
   387  	task := CreateTask(c, "reconfigureCluster", func(*Task) (types.AnyType, types.BaseMethodFault) {
   388  		spec, ok := req.Spec.(*types.ClusterConfigSpecEx)
   389  		if !ok {
   390  			return nil, new(types.InvalidArgument)
   391  		}
   392  
   393  		updates := []func(*Context, *types.ClusterConfigInfoEx, *types.ClusterConfigSpecEx) types.BaseMethodFault{
   394  			c.update,
   395  			c.updateRules,
   396  			c.updateGroups,
   397  			c.updateOverridesDAS,
   398  			c.updateOverridesDRS,
   399  			c.updateOverridesVmOrchestration,
   400  			c.updateVSAN,
   401  		}
   402  
   403  		for _, update := range updates {
   404  			if err := update(ctx, c.ConfigurationEx.(*types.ClusterConfigInfoEx), spec); err != nil {
   405  				return nil, err
   406  			}
   407  		}
   408  
   409  		return nil, nil
   410  	})
   411  
   412  	return &methods.ReconfigureComputeResource_TaskBody{
   413  		Res: &types.ReconfigureComputeResource_TaskResponse{
   414  			Returnval: task.Run(ctx),
   415  		},
   416  	}
   417  }
   418  
   419  func (c *ClusterComputeResource) MoveIntoTask(ctx *Context, req *types.MoveInto_Task) soap.HasFault {
   420  	task := CreateTask(c, "moveInto", func(*Task) (types.AnyType, types.BaseMethodFault) {
   421  		for _, ref := range req.Host {
   422  			host := ctx.Map.Get(ref).(*HostSystem)
   423  
   424  			if *host.Parent == c.Self {
   425  				return nil, new(types.DuplicateName) // host already in this cluster
   426  			}
   427  
   428  			switch parent := ctx.Map.Get(*host.Parent).(type) {
   429  			case *ClusterComputeResource:
   430  				if !host.Runtime.InMaintenanceMode {
   431  					return nil, new(types.InvalidState)
   432  				}
   433  
   434  				RemoveReference(&parent.Host, ref)
   435  			case *mo.ComputeResource:
   436  				ctx.Map.Remove(ctx, parent.Self)
   437  			}
   438  
   439  			c.Host = append(c.Host, ref)
   440  			host.Parent = &c.Self
   441  		}
   442  
   443  		return nil, nil
   444  	})
   445  
   446  	return &methods.MoveInto_TaskBody{
   447  		Res: &types.MoveInto_TaskResponse{
   448  			Returnval: task.Run(ctx),
   449  		},
   450  	}
   451  }
   452  
   453  func (c *ClusterComputeResource) PlaceVm(ctx *Context, req *types.PlaceVm) soap.HasFault {
   454  	body := new(methods.PlaceVmBody)
   455  
   456  	if len(c.Host) == 0 {
   457  		body.Fault_ = Fault("", new(types.InvalidState))
   458  		return body
   459  	}
   460  
   461  	res := types.ClusterRecommendation{
   462  		Key:        "1",
   463  		Type:       "V1",
   464  		Time:       time.Now(),
   465  		Rating:     1,
   466  		Reason:     string(types.RecommendationReasonCodeXvmotionPlacement),
   467  		ReasonText: string(types.RecommendationReasonCodeXvmotionPlacement),
   468  		Target:     &c.Self,
   469  	}
   470  
   471  	hosts := req.PlacementSpec.Hosts
   472  	if len(hosts) == 0 {
   473  		hosts = c.Host
   474  	}
   475  
   476  	datastores := req.PlacementSpec.Datastores
   477  	if len(datastores) == 0 {
   478  		datastores = c.Datastore
   479  	}
   480  
   481  	switch types.PlacementSpecPlacementType(req.PlacementSpec.PlacementType) {
   482  	case types.PlacementSpecPlacementTypeClone, types.PlacementSpecPlacementTypeCreate:
   483  		spec := &types.VirtualMachineRelocateSpec{
   484  			Datastore: &datastores[rand.Intn(len(c.Datastore))],
   485  			Host:      &hosts[rand.Intn(len(c.Host))],
   486  			Pool:      c.ResourcePool,
   487  		}
   488  		res.Action = append(res.Action, &types.PlacementAction{
   489  			Vm:           req.PlacementSpec.Vm,
   490  			TargetHost:   spec.Host,
   491  			RelocateSpec: spec,
   492  		})
   493  	case types.PlacementSpecPlacementTypeReconfigure:
   494  		// Validate input.
   495  		if req.PlacementSpec.ConfigSpec == nil {
   496  			body.Fault_ = Fault("", &types.InvalidArgument{
   497  				InvalidProperty: "PlacementSpec.configSpec",
   498  			})
   499  			return body
   500  		}
   501  
   502  		// Update PlacementResult.
   503  		vmObj := ctx.Map.Get(*req.PlacementSpec.Vm).(*VirtualMachine)
   504  		spec := &types.VirtualMachineRelocateSpec{
   505  			Datastore:    &vmObj.Datastore[0],
   506  			Host:         vmObj.Runtime.Host,
   507  			Pool:         vmObj.ResourcePool,
   508  			DiskMoveType: string(types.VirtualMachineRelocateDiskMoveOptionsMoveAllDiskBackingsAndAllowSharing),
   509  		}
   510  		res.Action = append(res.Action, &types.PlacementAction{
   511  			Vm:           req.PlacementSpec.Vm,
   512  			TargetHost:   spec.Host,
   513  			RelocateSpec: spec,
   514  		})
   515  	case types.PlacementSpecPlacementTypeRelocate:
   516  		// Validate fields of req.PlacementSpec, if explicitly provided.
   517  		if !validatePlacementSpecForPlaceVmRelocate(ctx, req, body) {
   518  			return body
   519  		}
   520  
   521  		// After validating req.PlacementSpec, we must have a valid req.PlacementSpec.Vm.
   522  		vmObj := ctx.Map.Get(*req.PlacementSpec.Vm).(*VirtualMachine)
   523  
   524  		// Populate RelocateSpec's common fields, if not explicitly provided.
   525  		populateRelocateSpecForPlaceVmRelocate(&req.PlacementSpec.RelocateSpec, vmObj)
   526  
   527  		// Update PlacementResult.
   528  		res.Action = append(res.Action, &types.PlacementAction{
   529  			Vm:           req.PlacementSpec.Vm,
   530  			TargetHost:   req.PlacementSpec.RelocateSpec.Host,
   531  			RelocateSpec: req.PlacementSpec.RelocateSpec,
   532  		})
   533  	default:
   534  		log.Printf("unsupported placement type: %s", req.PlacementSpec.PlacementType)
   535  		body.Fault_ = Fault("", new(types.NotSupported))
   536  		return body
   537  	}
   538  
   539  	body.Res = &types.PlaceVmResponse{
   540  		Returnval: types.PlacementResult{
   541  			Recommendations: []types.ClusterRecommendation{res},
   542  		},
   543  	}
   544  
   545  	return body
   546  }
   547  
   548  // validatePlacementSpecForPlaceVmRelocate validates the fields of req.PlacementSpec for a relocate placement type.
   549  // Returns true if the fields are valid, false otherwise.
   550  func validatePlacementSpecForPlaceVmRelocate(ctx *Context, req *types.PlaceVm, body *methods.PlaceVmBody) bool {
   551  	if req.PlacementSpec.Vm == nil {
   552  		body.Fault_ = Fault("", &types.InvalidArgument{
   553  			InvalidProperty: "PlacementSpec",
   554  		})
   555  		return false
   556  	}
   557  
   558  	// Oddly when the VM is not found, PlaceVm complains about configSpec being invalid, despite this being
   559  	// a relocate placement type. Possibly due to treating the missing VM as a create placement type
   560  	// internally, which requires the configSpec to be present.
   561  	vmObj, exist := ctx.Map.Get(*req.PlacementSpec.Vm).(*VirtualMachine)
   562  	if !exist {
   563  		body.Fault_ = Fault("", &types.InvalidArgument{
   564  			InvalidProperty: "PlacementSpec.configSpec",
   565  		})
   566  		return false
   567  	}
   568  
   569  	return validateRelocateSpecForPlaceVmRelocate(ctx, req.PlacementSpec.RelocateSpec, body, vmObj)
   570  }
   571  
   572  // validateRelocateSpecForPlaceVmRelocate validates the fields of req.PlacementSpec.RelocateSpec for a relocate
   573  // placement type. Returns true if the fields are valid, false otherwise.
   574  func validateRelocateSpecForPlaceVmRelocate(ctx *Context, spec *types.VirtualMachineRelocateSpec, body *methods.PlaceVmBody, vmObj *VirtualMachine) bool {
   575  	if spec == nil {
   576  		// An empty relocate spec is valid, as it will be populated with default values.
   577  		return true
   578  	}
   579  
   580  	if spec.Host != nil {
   581  		if _, exist := ctx.Map.Get(*spec.Host).(*HostSystem); !exist {
   582  			body.Fault_ = Fault("", &types.ManagedObjectNotFound{
   583  				Obj: *spec.Host,
   584  			})
   585  			return false
   586  		}
   587  	}
   588  
   589  	if spec.Datastore != nil {
   590  		if _, exist := ctx.Map.Get(*spec.Datastore).(*Datastore); !exist {
   591  			body.Fault_ = Fault("", &types.ManagedObjectNotFound{
   592  				Obj: *spec.Datastore,
   593  			})
   594  			return false
   595  		}
   596  	}
   597  
   598  	if spec.Pool != nil {
   599  		if _, exist := ctx.Map.Get(*spec.Pool).(*ResourcePool); !exist {
   600  			body.Fault_ = Fault("", &types.ManagedObjectNotFound{
   601  				Obj: *spec.Pool,
   602  			})
   603  			return false
   604  		}
   605  	}
   606  
   607  	if spec.Disk != nil {
   608  		deviceList := object.VirtualDeviceList(vmObj.Config.Hardware.Device)
   609  		vdiskList := deviceList.SelectByType(&types.VirtualDisk{})
   610  		for _, disk := range spec.Disk {
   611  			var diskFound bool
   612  			for _, vdisk := range vdiskList {
   613  				if disk.DiskId == vdisk.GetVirtualDevice().Key {
   614  					diskFound = true
   615  					break
   616  				}
   617  			}
   618  			if !diskFound {
   619  				body.Fault_ = Fault("", &types.InvalidArgument{
   620  					InvalidProperty: "PlacementSpec.vm",
   621  				})
   622  				return false
   623  			}
   624  
   625  			// Unlike a non-existing spec.Datastore that throws ManagedObjectNotFound, a non-existing disk.Datastore
   626  			// throws InvalidArgument.
   627  			if _, exist := ctx.Map.Get(disk.Datastore).(*Datastore); !exist {
   628  				body.Fault_ = Fault("", &types.InvalidArgument{
   629  					InvalidProperty: "RelocateSpec",
   630  				})
   631  				return false
   632  			}
   633  		}
   634  	}
   635  
   636  	return true
   637  }
   638  
   639  // populateRelocateSpecForPlaceVmRelocate populates the fields of req.PlacementSpec.RelocateSpec for a relocate
   640  // placement type, if not explicitly provided.
   641  func populateRelocateSpecForPlaceVmRelocate(specPtr **types.VirtualMachineRelocateSpec, vmObj *VirtualMachine) {
   642  	if *specPtr == nil {
   643  		*specPtr = &types.VirtualMachineRelocateSpec{}
   644  	}
   645  
   646  	spec := *specPtr
   647  
   648  	if spec.DiskMoveType == "" {
   649  		spec.DiskMoveType = string(types.VirtualMachineRelocateDiskMoveOptionsMoveAllDiskBackingsAndDisallowSharing)
   650  	}
   651  
   652  	if spec.Datastore == nil {
   653  		spec.Datastore = &vmObj.Datastore[0]
   654  	}
   655  
   656  	if spec.Host == nil {
   657  		spec.Host = vmObj.Runtime.Host
   658  	}
   659  
   660  	if spec.Pool == nil {
   661  		spec.Pool = vmObj.ResourcePool
   662  	}
   663  
   664  	if spec.Disk == nil {
   665  		deviceList := object.VirtualDeviceList(vmObj.Config.Hardware.Device)
   666  		for _, vdisk := range deviceList.SelectByType(&types.VirtualDisk{}) {
   667  			spec.Disk = append(spec.Disk, types.VirtualMachineRelocateSpecDiskLocator{
   668  				DiskId:       vdisk.GetVirtualDevice().Key,
   669  				Datastore:    *spec.Datastore,
   670  				DiskMoveType: spec.DiskMoveType,
   671  			})
   672  		}
   673  	}
   674  }
   675  
   676  func CreateClusterComputeResource(ctx *Context, f *Folder, name string, spec types.ClusterConfigSpecEx) (*ClusterComputeResource, types.BaseMethodFault) {
   677  	if e := ctx.Map.FindByName(name, f.ChildEntity); e != nil {
   678  		return nil, &types.DuplicateName{
   679  			Name:   e.Entity().Name,
   680  			Object: e.Reference(),
   681  		}
   682  	}
   683  
   684  	cluster := &ClusterComputeResource{}
   685  	cluster.EnvironmentBrowser = newEnvironmentBrowser(ctx)
   686  	cluster.Name = name
   687  	cluster.Network = ctx.Map.getEntityDatacenter(f).defaultNetwork()
   688  	cluster.Summary = &types.ClusterComputeResourceSummary{
   689  		UsageSummary: new(types.ClusterUsageSummary),
   690  	}
   691  
   692  	config := &types.ClusterConfigInfoEx{}
   693  	cluster.ConfigurationEx = config
   694  
   695  	config.VmSwapPlacement = string(types.VirtualMachineConfigInfoSwapPlacementTypeVmDirectory)
   696  	config.DrsConfig.Enabled = types.NewBool(true)
   697  
   698  	pool := NewResourcePool(ctx)
   699  	ctx.Map.PutEntity(cluster, ctx.Map.NewEntity(pool))
   700  	cluster.ResourcePool = &pool.Self
   701  
   702  	folderPutChild(ctx, &f.Folder, cluster)
   703  	pool.Owner = cluster.Self
   704  
   705  	return cluster, nil
   706  }