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 }