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  }