github.com/vmware/govmomi@v0.51.0/simulator/vm_compatibility_checker.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 "fmt" 9 "math/rand" 10 "slices" 11 12 "github.com/vmware/govmomi/vim25/methods" 13 "github.com/vmware/govmomi/vim25/mo" 14 "github.com/vmware/govmomi/vim25/soap" 15 "github.com/vmware/govmomi/vim25/types" 16 ) 17 18 type VmCompatibilityChecker struct { 19 mo.VirtualMachineCompatibilityChecker 20 } 21 22 func resolveHostsAndPool(ctx *Context, vm, host, pool *types.ManagedObjectReference) (*ResourcePool, []types.ManagedObjectReference) { 23 var vmMo *VirtualMachine 24 var poolMo *ResourcePool 25 26 switch { 27 case pool != nil: 28 poolMo = ctx.Map.Get(*pool).(*ResourcePool) 29 case vm != nil: 30 vmMo = ctx.Map.Get(*vm).(*VirtualMachine) 31 poolMo = ctx.Map.Get(*vmMo.ResourcePool).(*ResourcePool) 32 case host != nil: 33 h := ctx.Map.Get(*host).(*HostSystem) 34 parent := hostParent(ctx, &h.HostSystem).ResourcePool 35 poolMo = ctx.Map.Get(*parent).(*ResourcePool) 36 } 37 38 var hosts []types.ManagedObjectReference 39 40 switch { 41 case host != nil: 42 hosts = append(hosts, *host) 43 case pool != nil: 44 hosts = resourcePoolHosts(ctx, poolMo) 45 case vm != nil: 46 hosts = append(hosts, *vmMo.Runtime.Host) 47 } 48 49 return poolMo, hosts 50 } 51 52 func validateHostsAndPool(ctx *Context, pool *ResourcePool, hosts []types.ManagedObjectReference) *types.InvalidArgument { 53 allHosts := resourcePoolHosts(ctx, pool) 54 55 for _, host := range hosts { 56 if !slices.Contains(allHosts, host) { 57 return &types.InvalidArgument{ 58 InvalidProperty: "spec.pool", 59 } 60 } 61 } 62 63 return nil 64 } 65 66 func (c *VmCompatibilityChecker) checkVmConfigSpec(ctx *Context, check *types.CheckResult, spec types.VirtualMachineConfigSpec, hosts []types.ManagedObjectReference) { 67 if check.Host == nil { 68 // By default all hosts use the same HostSystem template, so we check against any. 69 // But we could choose a host based on the spec, e.g. record + playback of real hosts 70 check.Host = &hosts[rand.Intn(len(hosts))] 71 } 72 73 host := ctx.Map.Get(*check.Host).(*HostSystem) 74 75 mem := int32(spec.MemoryMB) 76 if mem > 0 { 77 min := int32(4) 78 max := host.Capability.MaxSupportedVmMemory 79 if mem > max || mem < min { 80 check.Warning = append(check.Warning, types.LocalizedMethodFault{ 81 Fault: &types.MemorySizeNotSupported{ 82 MemorySizeMB: mem, 83 MinMemorySizeMB: min, 84 MaxMemorySizeMB: max, 85 }, 86 LocalizedMessage: fmt.Sprintf("vm requires %d MB of memory, outside the range of %d to %d", mem, min, max), 87 }) 88 } 89 } 90 91 cpu := spec.NumCPUs 92 if cpu > 0 { 93 max := int32(host.Summary.Hardware.NumCpuCores) 94 if cpu > max { 95 check.Warning = append(check.Warning, types.LocalizedMethodFault{ 96 Fault: &types.NotEnoughCpus{ 97 NumCpuDest: max, 98 NumCpuVm: cpu, 99 }, 100 LocalizedMessage: fmt.Sprintf("vm requires %d CPUs, host has %d", cpu, max), 101 }) 102 } 103 } 104 105 if spec.GuestId != "" { 106 var guest types.VirtualMachineGuestOsIdentifier 107 if !slices.Contains(guest.Strings(), spec.GuestId) { 108 check.Warning = append(check.Warning, types.LocalizedMethodFault{ 109 Fault: &types.UnsupportedGuest{ 110 UnsupportedGuestOS: spec.GuestId, 111 }, 112 LocalizedMessage: fmt.Sprintf("vm guest os %s not supported", spec.GuestId), 113 }) 114 } 115 } 116 } 117 118 func (c *VmCompatibilityChecker) CheckVmConfigTask( 119 ctx *Context, 120 r *types.CheckVmConfig_Task) soap.HasFault { 121 122 task := CreateTask(c, "checkVmConfig", func(t *Task) (types.AnyType, types.BaseMethodFault) { 123 if r.Vm == nil && r.Host == nil && r.Pool == nil { 124 return nil, new(types.InvalidArgument) 125 } 126 127 poolMo, hosts := resolveHostsAndPool(ctx, r.Vm, r.Host, r.Pool) 128 if err := validateHostsAndPool(ctx, poolMo, hosts); err != nil { 129 return nil, err 130 } 131 132 check := types.CheckResult{ 133 Vm: r.Vm, 134 Host: r.Host, 135 } 136 137 c.checkVmConfigSpec(ctx, &check, r.Spec, hosts) 138 139 return types.ArrayOfCheckResult{ 140 CheckResult: []types.CheckResult{check}, 141 }, nil 142 }) 143 144 return &methods.CheckVmConfig_TaskBody{ 145 Res: &types.CheckVmConfig_TaskResponse{ 146 Returnval: task.Run(ctx), 147 }, 148 } 149 } 150 151 func (c *VmCompatibilityChecker) CheckCompatibilityTask( 152 ctx *Context, 153 r *types.CheckCompatibility_Task) soap.HasFault { 154 155 task := CreateTask(c, "checkCompatibility", func(t *Task) (types.AnyType, types.BaseMethodFault) { 156 poolMo, hosts := resolveHostsAndPool(ctx, &r.Vm, r.Host, r.Pool) 157 if err := validateHostsAndPool(ctx, poolMo, hosts); err != nil { 158 return nil, err 159 } 160 161 check := types.CheckResult{ 162 Vm: &r.Vm, 163 Host: r.Host, 164 } 165 166 return types.ArrayOfCheckResult{ 167 CheckResult: []types.CheckResult{check}, 168 }, nil 169 }) 170 171 return &methods.CheckCompatibility_TaskBody{ 172 Res: &types.CheckCompatibility_TaskResponse{ 173 Returnval: task.Run(ctx), 174 }, 175 } 176 }