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

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