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

     1  /*
     2  Copyright (c) 2019-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  	"strings"
    21  
    22  	"github.com/vmware/govmomi/simulator/esx"
    23  	"github.com/vmware/govmomi/vim25/methods"
    24  	"github.com/vmware/govmomi/vim25/mo"
    25  	"github.com/vmware/govmomi/vim25/soap"
    26  	"github.com/vmware/govmomi/vim25/types"
    27  )
    28  
    29  type EnvironmentBrowser struct {
    30  	mo.EnvironmentBrowser
    31  
    32  	QueryConfigTargetResponse           types.QueryConfigTargetResponse
    33  	QueryConfigOptionResponse           types.QueryConfigOptionResponse
    34  	QueryConfigOptionDescriptorResponse types.QueryConfigOptionDescriptorResponse
    35  	QueryTargetCapabilitiesResponse     types.QueryTargetCapabilitiesResponse
    36  }
    37  
    38  func newEnvironmentBrowser(
    39  	ctx *Context,
    40  	hostRefs ...types.ManagedObjectReference) *types.ManagedObjectReference {
    41  
    42  	env := new(EnvironmentBrowser)
    43  	env.initDescriptorReturnVal(ctx, hostRefs...)
    44  	Map.Put(env)
    45  	return &env.Self
    46  }
    47  
    48  func (b *EnvironmentBrowser) addHost(
    49  	ctx *Context, hostRef types.ManagedObjectReference) {
    50  
    51  	// Get a set of unique hosts.
    52  	hostSet := map[types.ManagedObjectReference]struct{}{
    53  		hostRef: {},
    54  	}
    55  	for i := range b.QueryConfigOptionDescriptorResponse.Returnval {
    56  		cod := b.QueryConfigOptionDescriptorResponse.Returnval[i]
    57  		for j := range cod.Host {
    58  			if _, ok := hostSet[cod.Host[j]]; !ok {
    59  				hostSet[cod.Host[j]] = struct{}{}
    60  			}
    61  		}
    62  	}
    63  
    64  	// Get a list of unique hosts.
    65  	var hostRefs []types.ManagedObjectReference
    66  	for ref := range hostSet {
    67  		hostRefs = append(hostRefs, ref)
    68  	}
    69  
    70  	// Clear the descriptor's return val.
    71  	b.QueryConfigOptionDescriptorResponse.Returnval = nil
    72  
    73  	b.initDescriptorReturnVal(ctx, hostRefs...)
    74  }
    75  
    76  func (b *EnvironmentBrowser) initDescriptorReturnVal(
    77  	ctx *Context, hostRefs ...types.ManagedObjectReference) {
    78  
    79  	// Get the max supported hardware version for this list of hosts.
    80  	var maxHardwareVersion types.HardwareVersion
    81  	maxHardwareVersionForHost := map[types.ManagedObjectReference]types.HardwareVersion{}
    82  	for j := range hostRefs {
    83  		ref := hostRefs[j]
    84  		ctx.WithLock(ref, func() {
    85  			host := ctx.Map.Get(ref).(*HostSystem)
    86  			hostVersion := types.MustParseESXiVersion(host.Config.Product.Version)
    87  			hostHardwareVersion := hostVersion.HardwareVersion()
    88  			maxHardwareVersionForHost[ref] = hostHardwareVersion
    89  			if !maxHardwareVersion.IsValid() {
    90  				maxHardwareVersion = hostHardwareVersion
    91  				return
    92  			}
    93  			if hostHardwareVersion > maxHardwareVersion {
    94  				maxHardwareVersion = hostHardwareVersion
    95  			}
    96  		})
    97  	}
    98  
    99  	if !maxHardwareVersion.IsValid() {
   100  		return
   101  	}
   102  
   103  	hardwareVersions := types.GetHardwareVersions()
   104  	for i := range hardwareVersions {
   105  		hv := hardwareVersions[i]
   106  		dco := hv == maxHardwareVersion
   107  		cod := types.VirtualMachineConfigOptionDescriptor{
   108  			Key:                 hv.String(),
   109  			Description:         hv.String(),
   110  			DefaultConfigOption: types.NewBool(dco),
   111  			CreateSupported:     types.NewBool(true),
   112  			RunSupported:        types.NewBool(true),
   113  			UpgradeSupported:    types.NewBool(true),
   114  		}
   115  		for hostRef, hostVer := range maxHardwareVersionForHost {
   116  			if hostVer >= hv {
   117  				cod.Host = append(cod.Host, hostRef)
   118  			}
   119  		}
   120  
   121  		b.QueryConfigOptionDescriptorResponse.Returnval = append(
   122  			b.QueryConfigOptionDescriptorResponse.Returnval, cod)
   123  
   124  		if dco {
   125  			break
   126  		}
   127  	}
   128  }
   129  
   130  func (b *EnvironmentBrowser) hosts(ctx *Context) []types.ManagedObjectReference {
   131  	ctx.Map.m.Lock()
   132  	defer ctx.Map.m.Unlock()
   133  	for _, obj := range ctx.Map.objects {
   134  		switch e := obj.(type) {
   135  		case *mo.ComputeResource:
   136  			if b.Self == *e.EnvironmentBrowser {
   137  				return e.Host
   138  			}
   139  		case *ClusterComputeResource:
   140  			if b.Self == *e.EnvironmentBrowser {
   141  				return e.Host
   142  			}
   143  		}
   144  	}
   145  	return nil
   146  }
   147  
   148  func (b *EnvironmentBrowser) QueryConfigOption(req *types.QueryConfigOption) soap.HasFault {
   149  	body := new(methods.QueryConfigOptionBody)
   150  
   151  	opt := b.QueryConfigOptionResponse.Returnval
   152  	if opt == nil {
   153  		opt = &types.VirtualMachineConfigOption{
   154  			Version:       esx.HardwareVersion,
   155  			DefaultDevice: esx.VirtualDevice,
   156  		}
   157  	}
   158  
   159  	body.Res = &types.QueryConfigOptionResponse{
   160  		Returnval: opt,
   161  	}
   162  
   163  	return body
   164  }
   165  
   166  func guestFamily(id string) string {
   167  	// TODO: We could capture the entire GuestOsDescriptor list from EnvironmentBrowser,
   168  	// but it is a ton of data.. this should be good enough for now.
   169  	switch {
   170  	case strings.HasPrefix(id, "win"):
   171  		return string(types.VirtualMachineGuestOsFamilyWindowsGuest)
   172  	case strings.HasPrefix(id, "darwin"):
   173  		return string(types.VirtualMachineGuestOsFamilyDarwinGuestFamily)
   174  	default:
   175  		return string(types.VirtualMachineGuestOsFamilyLinuxGuest)
   176  	}
   177  }
   178  
   179  func (b *EnvironmentBrowser) QueryConfigOptionEx(req *types.QueryConfigOptionEx) soap.HasFault {
   180  	body := new(methods.QueryConfigOptionExBody)
   181  
   182  	opt := b.QueryConfigOptionResponse.Returnval
   183  	if opt == nil {
   184  		opt = &types.VirtualMachineConfigOption{
   185  			Version:       esx.HardwareVersion,
   186  			DefaultDevice: esx.VirtualDevice,
   187  		}
   188  	}
   189  
   190  	if req.Spec != nil {
   191  		// From the SDK QueryConfigOptionEx doc:
   192  		// "If guestId is nonempty, the guestOSDescriptor array of the config option is filtered to match against the guest IDs in the spec.
   193  		//  If there is no match, the whole list is returned."
   194  		for _, id := range req.Spec.GuestId {
   195  			for _, gid := range GuestID {
   196  				if string(gid) == id {
   197  					opt.GuestOSDescriptor = []types.GuestOsDescriptor{{
   198  						Id:     id,
   199  						Family: guestFamily(id),
   200  					}}
   201  
   202  					break
   203  				}
   204  			}
   205  		}
   206  	}
   207  
   208  	if len(opt.GuestOSDescriptor) == 0 {
   209  		for i := range GuestID {
   210  			id := string(GuestID[i])
   211  			opt.GuestOSDescriptor = append(opt.GuestOSDescriptor, types.GuestOsDescriptor{
   212  				Id:     id,
   213  				Family: guestFamily(id),
   214  			})
   215  		}
   216  	}
   217  
   218  	body.Res = &types.QueryConfigOptionExResponse{
   219  		Returnval: opt,
   220  	}
   221  
   222  	return body
   223  }
   224  
   225  func (b *EnvironmentBrowser) QueryConfigOptionDescriptor(ctx *Context, req *types.QueryConfigOptionDescriptor) soap.HasFault {
   226  	body := &methods.QueryConfigOptionDescriptorBody{
   227  		Res: &types.QueryConfigOptionDescriptorResponse{
   228  			Returnval: b.QueryConfigOptionDescriptorResponse.Returnval,
   229  		},
   230  	}
   231  
   232  	return body
   233  }
   234  
   235  func (b *EnvironmentBrowser) QueryConfigTarget(ctx *Context, req *types.QueryConfigTarget) soap.HasFault {
   236  	body := &methods.QueryConfigTargetBody{
   237  		Res: &types.QueryConfigTargetResponse{
   238  			Returnval: b.QueryConfigTargetResponse.Returnval,
   239  		},
   240  	}
   241  
   242  	if body.Res.Returnval != nil {
   243  		return body
   244  	}
   245  
   246  	target := &types.ConfigTarget{
   247  		SmcPresent: types.NewBool(false),
   248  	}
   249  	body.Res.Returnval = target
   250  
   251  	var hosts []types.ManagedObjectReference
   252  	if req.Host == nil {
   253  		hosts = b.hosts(ctx)
   254  	} else {
   255  		hosts = append(hosts, *req.Host)
   256  	}
   257  
   258  	seen := make(map[types.ManagedObjectReference]bool)
   259  
   260  	for i := range hosts {
   261  		host := ctx.Map.Get(hosts[i]).(*HostSystem)
   262  		target.NumCpus += int32(host.Summary.Hardware.NumCpuPkgs)
   263  		target.NumCpuCores += int32(host.Summary.Hardware.NumCpuCores)
   264  		target.NumNumaNodes++
   265  
   266  		for _, ref := range host.Datastore {
   267  			if seen[ref] {
   268  				continue
   269  			}
   270  			seen[ref] = true
   271  
   272  			ds := ctx.Map.Get(ref).(*Datastore)
   273  			target.Datastore = append(target.Datastore, types.VirtualMachineDatastoreInfo{
   274  				VirtualMachineTargetInfo: types.VirtualMachineTargetInfo{
   275  					Name: ds.Name,
   276  				},
   277  				Datastore:       ds.Summary,
   278  				Capability:      ds.Capability,
   279  				Mode:            string(types.HostMountModeReadWrite),
   280  				VStorageSupport: string(types.FileSystemMountInfoVStorageSupportStatusVStorageUnsupported),
   281  			})
   282  		}
   283  
   284  		for _, ref := range host.Network {
   285  			if seen[ref] {
   286  				continue
   287  			}
   288  			seen[ref] = true
   289  
   290  			switch n := ctx.Map.Get(ref).(type) {
   291  			case *mo.Network:
   292  				target.Network = append(target.Network, types.VirtualMachineNetworkInfo{
   293  					VirtualMachineTargetInfo: types.VirtualMachineTargetInfo{
   294  						Name: n.Name,
   295  					},
   296  					Network: n.Summary.GetNetworkSummary(),
   297  				})
   298  			case *DistributedVirtualPortgroup:
   299  				dvs := ctx.Map.Get(*n.Config.DistributedVirtualSwitch).(*DistributedVirtualSwitch)
   300  				target.DistributedVirtualPortgroup = append(target.DistributedVirtualPortgroup, types.DistributedVirtualPortgroupInfo{
   301  					SwitchName:                  dvs.Name,
   302  					SwitchUuid:                  dvs.Uuid,
   303  					PortgroupName:               n.Name,
   304  					PortgroupKey:                n.Key,
   305  					PortgroupType:               n.Config.Type,
   306  					UplinkPortgroup:             false,
   307  					Portgroup:                   n.Self,
   308  					NetworkReservationSupported: types.NewBool(false),
   309  				})
   310  			case *DistributedVirtualSwitch:
   311  				target.DistributedVirtualSwitch = append(target.DistributedVirtualSwitch, types.DistributedVirtualSwitchInfo{
   312  					SwitchName:                  n.Name,
   313  					SwitchUuid:                  n.Uuid,
   314  					DistributedVirtualSwitch:    n.Self,
   315  					NetworkReservationSupported: types.NewBool(false),
   316  				})
   317  			}
   318  		}
   319  	}
   320  
   321  	return body
   322  }
   323  
   324  func (b *EnvironmentBrowser) QueryTargetCapabilities(ctx *Context, req *types.QueryTargetCapabilities) soap.HasFault {
   325  	body := &methods.QueryTargetCapabilitiesBody{
   326  		Res: &types.QueryTargetCapabilitiesResponse{
   327  			Returnval: b.QueryTargetCapabilitiesResponse.Returnval,
   328  		},
   329  	}
   330  
   331  	if body.Res.Returnval != nil {
   332  		return body
   333  	}
   334  
   335  	body.Res.Returnval = &types.HostCapability{
   336  		VmotionSupported:         true,
   337  		MaintenanceModeSupported: true,
   338  	}
   339  
   340  	return body
   341  }