github.com/vmware/govmomi@v0.51.0/eam/simulator/agent.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  	"log"
    10  	"time"
    11  
    12  	"github.com/google/uuid"
    13  
    14  	"github.com/vmware/govmomi/simulator"
    15  	"github.com/vmware/govmomi/vim25"
    16  	vimmethods "github.com/vmware/govmomi/vim25/methods"
    17  	"github.com/vmware/govmomi/vim25/soap"
    18  	vim "github.com/vmware/govmomi/vim25/types"
    19  
    20  	"github.com/vmware/govmomi/eam/internal"
    21  	"github.com/vmware/govmomi/eam/methods"
    22  	"github.com/vmware/govmomi/eam/mo"
    23  	"github.com/vmware/govmomi/eam/types"
    24  )
    25  
    26  // Agenct is the vSphere ESX Agent Manager managed object responsible
    27  // fordeploying an Agency on a single host. The Agent maintains the state
    28  // of the current deployment in its runtime information
    29  type Agent struct {
    30  	EamObject
    31  	mo.Agent
    32  }
    33  
    34  type AgentVMPlacementOptions struct {
    35  	computeResource vim.ManagedObjectReference
    36  	datacenter      vim.ManagedObjectReference
    37  	datastore       vim.ManagedObjectReference
    38  	folder          vim.ManagedObjectReference
    39  	host            vim.ManagedObjectReference
    40  	network         vim.ManagedObjectReference
    41  	pool            vim.ManagedObjectReference
    42  }
    43  
    44  // NewAgent returns a new Agent as if CreateAgency were called on the
    45  // EsxAgentManager object.
    46  func NewAgent(
    47  	ctx *simulator.Context,
    48  	agency vim.ManagedObjectReference,
    49  	config types.AgentConfigInfo,
    50  	vmName string,
    51  	vmPlacement AgentVMPlacementOptions) (*Agent, vim.BaseMethodFault) {
    52  	vimCtx := ctx.For(vim25.Path)
    53  	vimMap := vimCtx.Map
    54  
    55  	agent := &Agent{
    56  		EamObject: EamObject{
    57  			Self: vim.ManagedObjectReference{
    58  				Type:  internal.Agent,
    59  				Value: uuid.New().String(),
    60  			},
    61  		},
    62  		Agent: mo.Agent{
    63  			Config: config,
    64  			Runtime: types.AgentRuntimeInfo{
    65  				Agency:               &agency,
    66  				VmName:               vmName,
    67  				Host:                 &vmPlacement.host,
    68  				EsxAgentFolder:       &vmPlacement.folder,
    69  				EsxAgentResourcePool: &vmPlacement.pool,
    70  			},
    71  		},
    72  	}
    73  
    74  	// Register the agent with the registry in order for the agent to start
    75  	// receiving API calls from clients.
    76  	ctx.Map.Put(agent)
    77  
    78  	createVm := func() (vim.ManagedObjectReference, *vim.LocalizedMethodFault) {
    79  		var vmRef vim.ManagedObjectReference
    80  
    81  		// vmExtraConfig is used when creating the VM for this agent.
    82  		vmExtraConfig := []vim.BaseOptionValue{}
    83  
    84  		// If config.OvfPackageUrl is non-empty and does not appear to point to
    85  		// a local file or an HTTP URI, then assume it is a container.
    86  		if url := config.OvfPackageUrl; url != "" && !fsOrHTTPRx.MatchString(url) {
    87  			vmExtraConfig = append(
    88  				vmExtraConfig,
    89  				&vim.OptionValue{
    90  					Key:   "RUN.container",
    91  					Value: url,
    92  				})
    93  		}
    94  
    95  		// Copy the OVF environment properties into the VM's ExtraConfig property.
    96  		if ovfEnv := config.OvfEnvironment; ovfEnv != nil {
    97  			for _, ovfProp := range ovfEnv.OvfProperty {
    98  				vmExtraConfig = append(
    99  					vmExtraConfig,
   100  					&vim.OptionValue{
   101  						Key:   ovfProp.Key,
   102  						Value: ovfProp.Value,
   103  					})
   104  			}
   105  		}
   106  
   107  		datastore := vimMap.Get(vmPlacement.datastore).(*simulator.Datastore)
   108  		vmPathName := fmt.Sprintf("[%[1]s] %[2]s/%[2]s.vmx", datastore.Name, vmName)
   109  		vmConfigSpec := vim.VirtualMachineConfigSpec{
   110  			Name:        vmName,
   111  			ExtraConfig: vmExtraConfig,
   112  			Files: &vim.VirtualMachineFileInfo{
   113  				VmPathName: vmPathName,
   114  			},
   115  		}
   116  
   117  		// Create the VM for this agent.
   118  		vmFolder := vimMap.Get(vmPlacement.folder).(*simulator.Folder)
   119  		createVmTaskRef := vmFolder.CreateVMTask(vimCtx, &vim.CreateVM_Task{
   120  			This:   vmFolder.Self,
   121  			Config: vmConfigSpec,
   122  			Pool:   vmPlacement.pool,
   123  			Host:   &vmPlacement.host,
   124  		}).(*vimmethods.CreateVM_TaskBody).Res.Returnval
   125  		createVmTask := vimMap.Get(createVmTaskRef).(*simulator.Task)
   126  
   127  		// Wait for the task to complete and see if there is an error.
   128  		createVmTask.Wait()
   129  		if createVmTask.Info.Error != nil {
   130  			return vmRef, createVmTask.Info.Error
   131  		}
   132  
   133  		vmRef = createVmTask.Info.Result.(vim.ManagedObjectReference)
   134  		vm := vimMap.Get(vmRef).(*simulator.VirtualMachine)
   135  		log.Printf("created agent vm: MoRef=%v, Name=%s", vm.Self, vm.Name)
   136  
   137  		// Link the agent to this VM.
   138  		agent.Runtime.Vm = &vm.Self
   139  
   140  		return vm.Self, nil
   141  	}
   142  
   143  	vmRef, err := createVm()
   144  	if err != nil {
   145  		return nil, &vim.RuntimeFault{
   146  			MethodFault: vim.MethodFault{
   147  				FaultCause: err,
   148  			},
   149  		}
   150  	}
   151  
   152  	// Start watching this VM and updating the agent's information about the VM.
   153  	go func(ctx *simulator.Context, eamReg, vimReg *simulator.Registry) {
   154  		var (
   155  			ticker = time.NewTicker(1 * time.Second)
   156  			vmName string
   157  		)
   158  		for range ticker.C {
   159  			eamReg.WithLock(ctx, agent.Self, func() {
   160  				agentObj := eamReg.Get(agent.Self)
   161  				if agentObj == nil {
   162  					log.Printf("not found: %v", agent.Self)
   163  					// If the agent no longer exists then stop watching it.
   164  					ticker.Stop()
   165  					return
   166  				}
   167  
   168  				updateAgent := func(vm *simulator.VirtualMachine) {
   169  					if vmName == "" {
   170  						vmName = vm.Config.Name
   171  					}
   172  
   173  					// Update the agent's properties from the VM.
   174  					agent := agentObj.(*Agent)
   175  					agent.Runtime.VmPowerState = vm.Runtime.PowerState
   176  					if guest := vm.Summary.Guest; guest == nil {
   177  						agent.Runtime.VmIp = ""
   178  					} else {
   179  						agent.Runtime.VmIp = guest.IpAddress
   180  					}
   181  				}
   182  
   183  				vimReg.WithLock(ctx, vmRef, func() {
   184  					if vmObj := vimReg.Get(vmRef); vmObj != nil {
   185  						updateAgent(vmObj.(*simulator.VirtualMachine))
   186  					} else {
   187  						// If the VM no longer exists then create a new agent VM.
   188  						log.Printf(
   189  							"creating new agent vm: %v, %v, vmName=%s",
   190  							agent.Self, vmRef, vmName)
   191  
   192  						newVmRef, err := createVm()
   193  						if err != nil {
   194  							log.Printf(
   195  								"failed to create new agent vm: %v, %v, vmName=%s, err=%v",
   196  								agent.Self, vmRef, vmName, *err)
   197  							ticker.Stop()
   198  							return
   199  						}
   200  
   201  						// Make sure the vmRef variable is assigned to the new
   202  						// VM's reference for the next time through this loop.
   203  						vmRef = newVmRef
   204  
   205  						// Get a lock for the *new* VM.
   206  						vimReg.WithLock(ctx, vmRef, func() {
   207  							vmObj = vimReg.Get(vmRef)
   208  							if vmObj == nil {
   209  								log.Printf("not found: %v", vmRef)
   210  								ticker.Stop()
   211  								return
   212  							}
   213  							updateAgent(vmObj.(*simulator.VirtualMachine))
   214  						})
   215  					}
   216  
   217  				})
   218  			})
   219  		}
   220  	}(vimCtx, ctx.Map, vimMap)
   221  
   222  	return agent, nil
   223  }
   224  
   225  func (m *Agent) AgentQueryConfig(
   226  	ctx *simulator.Context,
   227  	req *types.AgentQueryConfig) soap.HasFault {
   228  
   229  	return &methods.AgentQueryConfigBody{
   230  		Res: &types.AgentQueryConfigResponse{
   231  			Returnval: m.Config,
   232  		},
   233  	}
   234  }
   235  
   236  func (m *Agent) AgentQueryRuntime(
   237  	ctx *simulator.Context,
   238  	req *types.AgentQueryRuntime) soap.HasFault {
   239  
   240  	return &methods.AgentQueryRuntimeBody{
   241  		Res: &types.AgentQueryRuntimeResponse{
   242  			Returnval: m.Runtime,
   243  		},
   244  	}
   245  }
   246  
   247  func (m *Agent) MarkAsAvailable(
   248  	ctx *simulator.Context,
   249  	req *types.MarkAsAvailable) soap.HasFault {
   250  
   251  	return &methods.MarkAsAvailableBody{
   252  		Res: &types.MarkAsAvailableResponse{},
   253  	}
   254  }