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 }