github.com/vmware/transport-go@v1.3.4/examples/need-refactoring/sample_vm_service.go (about) 1 // Copyright 2019-2020 VMware, Inc. 2 // SPDX-License-Identifier: BSD-2-Clause 3 4 package need_refactoring 5 6 import ( 7 "fmt" 8 "github.com/google/uuid" 9 "github.com/vmware/transport-go/model" 10 "github.com/vmware/transport-go/service" 11 "math/rand" 12 "reflect" 13 "strconv" 14 "sync" 15 ) 16 17 type VmRef struct { 18 VcGuid string `json:"vcGuid"` 19 VmId string `json:"vmId"` 20 } 21 22 const ( 23 powerState_poweredOff = "poweredOff" 24 powerState_poweredOn = "poweredOn" 25 powerState_suspended = "suspended" 26 27 diskFormat_native_512 = "native_512" 28 diskFormat_emulated_512 = "emulated_512" 29 diskFormat_native_4k = "native_4k" 30 31 powerOperation_powerOn = "powerOn" 32 powerOperation_powerOff = "powerOff" 33 powerOperation_reset = "reset" 34 powerOperation_suspend = "suspend" 35 ) 36 37 type RuntimeInfo struct { 38 Host string `json:"host"` 39 PowerState string `json:"powerState"` 40 } 41 42 type VirtualDisk struct { 43 Key int `json:"key"` 44 DeviceType string `json:"deviceType"` 45 DeviceName string `json:"deviceName"` 46 CapacityMB int64 `json:"capacityMB"` 47 DiskFormat string `json:"diskFormat"` 48 } 49 50 type VirtualUSB struct { 51 Key int `json:"key"` 52 DeviceType string `json:"deviceType"` 53 DeviceName string `json:"deviceName"` 54 Connected bool `json:"connected"` 55 Speed []string `json:"speed"` 56 } 57 58 type VirtualHardware struct { 59 MemoryMB int `json:"memoryMB"` 60 NumCPU int `json:"numCPU"` 61 Devices []interface{} `json:"devices"` 62 } 63 64 type VirtualMachine struct { 65 VmRef VmRef `json:"vmRef"` 66 Name string `json:"name"` 67 RuntimeInfo *RuntimeInfo `json:"runtimeInfo"` 68 Hardware *VirtualHardware `json:"hardware"` 69 } 70 71 type VmListResponse struct { 72 Error bool `json:"error"` 73 ErrorMessage string `json:"errorMessage"` 74 VirtualMachines []VirtualMachine `json:"virtualMachines"` 75 } 76 77 type VmCreateResponse struct { 78 Error bool `json:"error"` 79 ErrorMessage string `json:"errorMessage"` 80 Vm VirtualMachine `json:"vm"` 81 } 82 83 type BaseVmResponse struct { 84 Error bool `json:"error"` 85 ErrorMessage string `json:"errorMessage"` 86 } 87 88 type VmPowerOperationResponseItem struct { 89 VmRef VmRef `json:"vmRef"` 90 OperationResult bool `json:"operationResult"` 91 } 92 93 type VmPowerOperationResponse struct { 94 Error bool `json:"error"` 95 ErrorMessage string `json:"errorMessage"` 96 OpResults []VmPowerOperationResponseItem `json:"opResults"` 97 } 98 99 type VmDeleteRequest struct { 100 Vm VmRef `json:"vm"` 101 } 102 103 type VmPowerOperationRequest struct { 104 VmRefs []VmRef `json:"vmRefs"` 105 PowerOperation string `json:"powerOperation"` 106 } 107 108 type VmCreateRequest struct { 109 Name string `json:"name"` 110 VirtualHardware *VirtualHardware `json:"virtualHardware"` 111 } 112 113 type vmService struct { 114 vms []VirtualMachine 115 lock sync.Mutex 116 } 117 118 func (s *vmService) Init(core service.FabricServiceCore) error { 119 120 s.vms = []VirtualMachine{ 121 createVm("sample-go-vm1", &VirtualHardware{ 122 MemoryMB: 2048, 123 NumCPU: 4, 124 Devices: []interface{}{ 125 VirtualDisk{ 126 Key: 1, 127 DeviceType: "VirtualDisk", 128 DeviceName: "disk-1", 129 CapacityMB: 50000, 130 DiskFormat: diskFormat_emulated_512, 131 }, 132 VirtualDisk{ 133 Key: 2, 134 DeviceType: "VirtualDisk", 135 DeviceName: "disk-2", 136 CapacityMB: 150000, 137 DiskFormat: diskFormat_native_4k, 138 }, 139 }, 140 }), 141 142 createVm("sample-go-vm2", &VirtualHardware{ 143 MemoryMB: 8192, 144 NumCPU: 8, 145 Devices: []interface{}{ 146 VirtualDisk{ 147 Key: 1, 148 DeviceType: "VirtualDisk", 149 DeviceName: "disk-1", 150 CapacityMB: 50000, 151 DiskFormat: diskFormat_emulated_512, 152 }, 153 VirtualUSB{ 154 Key: 2, 155 DeviceType: "VirtualUSB", 156 DeviceName: "disk-2", 157 Connected: true, 158 Speed: []string{"superSpeed", "high"}, 159 }, 160 }, 161 }), 162 } 163 164 return nil 165 } 166 167 func (s *vmService) HandleServiceRequest( 168 request *model.Request, core service.FabricServiceCore) { 169 170 switch request.Request { 171 case "listVms": 172 core.SendResponse(request, &VmListResponse{ 173 VirtualMachines: s.vms, 174 }) 175 case "deleteVm": 176 s.deleteVm(request, core) 177 case "changeVmPowerState": 178 s.changePowerState(request, core) 179 case "createVm": 180 core.SendResponse(request, s.createVm(request)) 181 default: 182 core.HandleUnknownRequest(request) 183 } 184 } 185 186 func (s *vmService) deleteVm(request *model.Request, core service.FabricServiceCore) { 187 reqPayload, err := model.ConvertValueToType(request.Payload, reflect.TypeOf(&VmDeleteRequest{})) 188 if err != nil || reqPayload == nil { 189 core.SendResponse(request, &BaseVmResponse{ 190 Error: true, 191 ErrorMessage: "Request payload should be VmDeleteRequest!", 192 }) 193 return 194 } 195 196 deleteReq := reqPayload.(*VmDeleteRequest) 197 198 s.lock.Lock() 199 defer s.lock.Unlock() 200 201 for i, vm := range s.vms { 202 if deleteReq.Vm == vm.VmRef { 203 copy(s.vms[i:], s.vms[i+1:]) 204 s.vms = s.vms[:len(s.vms)-1] 205 core.SendResponse(request, &BaseVmResponse{}) 206 return 207 } 208 } 209 210 core.SendResponse(request, &BaseVmResponse{ 211 Error: true, 212 ErrorMessage: fmt.Sprint("Cannot find VM:", deleteReq.Vm), 213 }) 214 } 215 216 func (s *vmService) createVm(request *model.Request) *VmCreateResponse { 217 resp := &VmCreateResponse{} 218 219 reqPayload, err := model.ConvertValueToType(request.Payload, reflect.TypeOf(&VmCreateRequest{})) 220 if err != nil || reqPayload == nil { 221 resp.Error = true 222 resp.ErrorMessage = "Request payload should be VmCreateRequest!" 223 return resp 224 } 225 226 createReq := reqPayload.(*VmCreateRequest) 227 if createReq.Name == "" { 228 resp.Error = true 229 resp.ErrorMessage = "Invalid VmCreateRequest: null name!" 230 return resp 231 } 232 233 if createReq.VirtualHardware == nil { 234 resp.Error = true 235 resp.ErrorMessage = "Invalid VmCreateRequest: null virtualHardware!" 236 return resp 237 } 238 239 for i, device := range createReq.VirtualHardware.Devices { 240 241 deviceAsMap, ok := device.(map[string]interface{}) 242 if !ok || deviceAsMap == nil { 243 resp.Error = true 244 resp.ErrorMessage = "Invalid VmCreateRequest: invalid device" 245 return resp 246 } 247 248 var deviceType reflect.Type 249 switch deviceAsMap["deviceType"] { 250 case "VirtualDisk": 251 deviceType = reflect.TypeOf(VirtualDisk{}) 252 case "VirtualUSB": 253 deviceType = reflect.TypeOf(VirtualUSB{}) 254 default: 255 resp.Error = true 256 resp.ErrorMessage = "Invalid VmCreateRequest: unsupported device type " 257 return resp 258 } 259 260 createReq.VirtualHardware.Devices[i], err = model.ConvertValueToType(deviceAsMap, deviceType) 261 if err != nil { 262 resp.Error = true 263 resp.ErrorMessage = "Invalid VmCreateRequest: invalid device" 264 return resp 265 } 266 } 267 268 s.lock.Lock() 269 defer s.lock.Unlock() 270 271 vm := createVm(createReq.Name, createReq.VirtualHardware) 272 s.vms = append(s.vms, vm) 273 resp.Vm = vm 274 return resp 275 } 276 277 func createVm(name string, hardware *VirtualHardware) VirtualMachine { 278 return VirtualMachine{ 279 VmRef: VmRef{ 280 VcGuid: uuid.New().String(), 281 VmId: uuid.New().String(), 282 }, 283 Name: name, 284 RuntimeInfo: &RuntimeInfo{ 285 Host: "191.168.12." + strconv.Itoa(rand.Intn(255)), 286 PowerState: powerState_poweredOff, 287 }, 288 Hardware: hardware, 289 } 290 } 291 292 func (s *vmService) changePowerState(request *model.Request, core service.FabricServiceCore) { 293 reqPayload, err := model.ConvertValueToType( 294 request.Payload, reflect.TypeOf(&VmPowerOperationRequest{})) 295 if err != nil || reqPayload == nil { 296 core.SendResponse(request, &BaseVmResponse{ 297 Error: true, 298 ErrorMessage: "Request payload should be VmPowerOperationRequest!", 299 }) 300 return 301 } 302 303 s.lock.Lock() 304 defer s.lock.Unlock() 305 306 vmPowerOpReq := reqPayload.(*VmPowerOperationRequest) 307 result := &VmPowerOperationResponse{} 308 for _, vmRef := range vmPowerOpReq.VmRefs { 309 result.OpResults = append(result.OpResults, VmPowerOperationResponseItem{ 310 VmRef: vmRef, 311 OperationResult: s.changePowerStateOfVm(vmRef, vmPowerOpReq.PowerOperation), 312 }) 313 } 314 core.SendResponse(request, result) 315 } 316 317 func (s *vmService) changePowerStateOfVm(ref VmRef, newPowerState string) bool { 318 for _, vm := range s.vms { 319 if vm.VmRef == ref { 320 switch newPowerState { 321 case powerOperation_powerOff: 322 if vm.RuntimeInfo.PowerState == powerState_poweredOff { 323 // We cannot power off powered off VMs 324 return false 325 } 326 vm.RuntimeInfo.PowerState = powerState_poweredOff 327 return true 328 329 case powerOperation_reset: 330 if vm.RuntimeInfo.PowerState == powerState_poweredOff || 331 vm.RuntimeInfo.PowerState == powerState_suspended { 332 return false 333 } 334 vm.RuntimeInfo.PowerState = powerState_poweredOn 335 return true 336 337 case powerOperation_powerOn: 338 if vm.RuntimeInfo.PowerState == powerState_poweredOn { 339 // We cannot power on powered on VMs 340 return false 341 } 342 vm.RuntimeInfo.PowerState = powerState_poweredOn 343 return true 344 345 case powerOperation_suspend: 346 if vm.RuntimeInfo.PowerState == powerState_poweredOff || 347 vm.RuntimeInfo.PowerState == powerState_suspended { 348 return false 349 } 350 vm.RuntimeInfo.PowerState = powerState_suspended 351 return true 352 default: 353 return false 354 } 355 } 356 } 357 return false 358 }