github.com/vmware/govmomi@v0.37.1/eam/simulator/agent.go (about)

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