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