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  }