diff --git a/src/cmdTemplate/templateCreate.go b/src/cmdTemplate/templateCreate.go index 7eea3025870504dfbdf93cd7da0ec1b0b7019ae4..44569d2222067945b433afa34731081d033bd009 100644 --- a/src/cmdTemplate/templateCreate.go +++ b/src/cmdTemplate/templateCreate.go @@ -3,6 +3,7 @@ package cmdTemplate import ( u "nexus-client/utils" g "nexus-client/globals" + "github.com/google/uuid" "github.com/go-resty/resty/v2" ) @@ -27,7 +28,7 @@ func (cmd *Create)PrintUsage() { u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") u.PrintlnErr("USAGE: "+cmd.GetName()+" ID name access") u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("ID ID of the VM used to create the template.") + u.PrintlnErr("vmID ID of the VM used to create the template.") u.PrintlnErr("name Name of the template to create.") u.PrintlnErr("access Access type, either \"public\" or \"private\"") } @@ -39,11 +40,14 @@ func (cmd *Create)Run(args []string) int { return 1 } - id := args[0] + vmID, err := uuid.Parse(args[0]) + if err != nil { + u.PrintlnErr(err) + } name := args[1] access := args[2] - resp, err := cmd.makeRequestForID(id, name, access) + resp, err := cmd.makeRequestForID(vmID, name, access) if err != nil { u.PrintlnErr("Error: "+err.Error()) return 1 @@ -57,12 +61,12 @@ func (cmd *Create)Run(args []string) int { } } -func (cmd *Create)makeRequestForID(vmID, name, access string) (*resty.Response, error) { +func (cmd *Create)makeRequestForID(vmID uuid.UUID, name, access string) (*resty.Response, error) { client := g.GetInstance().Client host := g.GetInstance().Host type TemplateArgs struct { - VMID string + VMID uuid.UUID Name string Access string } diff --git a/src/cmdVM/helper.go b/src/cmdVM/helper.go index c6e734e316b0f79464bfbfe59ad12ce07db9f087..f085cb530d16600f9292f576abde12fb7317ec7f 100644 --- a/src/cmdVM/helper.go +++ b/src/cmdVM/helper.go @@ -14,7 +14,7 @@ import ( ) // Converts a VM structure into a pretty string. -func (vm *VMSerialized)String() string { +func (vm *VMNetworkSerialized)String() string { output, err := json.MarshalIndent(vm, "", " ") if err != nil { return err.Error() @@ -114,7 +114,7 @@ func printFilteredVMs(c cmd.Command, args []string, route string) int { // Regular expression examples: // "." -> matches everything // "bla" -> matches any VM name containing "bla" -func getFilteredVMs(route string, patterns []string) ([]VMSerialized, error) { +func getFilteredVMs(route string, patterns []string) ([]VMNetworkSerialized, error) { if len(patterns) < 1 { return nil, errors.New("At least one ID or regex must be specified") } @@ -139,7 +139,7 @@ func getFilteredVMs(route string, patterns []string) ([]VMSerialized, error) { return nil, err } - vmsList := []VMSerialized{} + vmsList := []VMNetworkSerialized{} if resp.IsSuccess() { vms, err := getVMs(resp) @@ -178,8 +178,8 @@ func getFilteredVMs(route string, patterns []string) ([]VMSerialized, error) { } // Retrieves all VMs (no filtering). -func getVMs(resp *resty.Response) ([]VMSerialized, error) { - vms := []VMSerialized{} +func getVMs(resp *resty.Response) ([]VMNetworkSerialized, error) { + vms := []VMNetworkSerialized{} if err := json.Unmarshal(resp.Body(), &vms); err != nil { return nil, err } @@ -187,8 +187,8 @@ func getVMs(resp *resty.Response) ([]VMSerialized, error) { } // Retrieve a single VM. -func getVM(resp *resty.Response) (*VMSerialized, error) { - var vm *VMSerialized = &VMSerialized{} +func getVM(resp *resty.Response) (*VMNetworkSerialized, error) { + var vm *VMNetworkSerialized = &VMNetworkSerialized{} if err := json.Unmarshal(resp.Body(), vm); err != nil { return vm, err } diff --git a/src/cmdVM/vm.go b/src/cmdVM/vm.go index 8557cbfa3881e2c76e38a18f7f20d2b28f0156cf..b54daf2931586fa9abd5745768a0c3cd068adfe9 100644 --- a/src/cmdVM/vm.go +++ b/src/cmdVM/vm.go @@ -6,19 +6,20 @@ import ( // Make sure these types MATCH their counterparts in nexus-server codebase! type ( - VMSerialized struct { - ID uuid.UUID - Owner string - Name string - Cpus int - Ram int - Nic NicType - TemplateID uuid.UUID - Access map[string]Capabilities - State VMState - Port int - Pwd string - DiskBusy bool // If true the VM's disk is busy (cannot be modified or deleted) + VMNetworkSerialized struct { + ID uuid.UUID `json:"id"` + Owner string `json:"owner"` + Name string `json:"name"` + Cpus int `json:"cpus"` + Ram int `json:"ram"` + Nic NicType `json:"nic"` + UsbDevs []string `json:"usbDevs"` // vendorID:productID + TemplateID uuid.UUID `json:"templateID"` + Access map[string]Capabilities `json:"access"` + State VMState `json:"state"` + Port int `json:"port"` + Pwd string `json:"pwd"` + DiskBusy bool `json:"diskBusy"` } Capabilities map[string]int diff --git a/src/cmdVM/vmAttach.go b/src/cmdVM/vmAttach.go index d34fb9cab5326b0059ac5d98e3d5a386c2c506b7..d56af7a4d88ff0eacd56168b5544ffab4000e3dc 100644 --- a/src/cmdVM/vmAttach.go +++ b/src/cmdVM/vmAttach.go @@ -52,7 +52,7 @@ func (cmd *Attach)Run(args []string) int { // wg.Add(len(vms)) for _, vm := range(vms) { - go func(vm VMSerialized) { + go func(vm VMNetworkSerialized) { exec.RunRemoteViewer(hostname, cert, vm.Name, vm.Port, vm.Pwd, false) // wg.Done() } (vm) diff --git a/src/cmdVM/vmCreate.go b/src/cmdVM/vmCreate.go index fc0bf6109056cce489da78cd88a99a1611e9d527..e1c3f797684a78d135d12b37b57b83cc3aa8b6f7 100644 --- a/src/cmdVM/vmCreate.go +++ b/src/cmdVM/vmCreate.go @@ -2,9 +2,11 @@ package cmdVM import ( "fmt" + "strings" "strconv" u "nexus-client/utils" g "nexus-client/globals" + "github.com/google/uuid" ) type Create struct { @@ -26,13 +28,16 @@ func (cmd *Create)PrintUsage() { u.PrintlnErr(desc) } u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") - u.PrintlnErr("USAGE: "+cmd.GetName()+" name cpus ram nic template [count|file.csv]") + u.PrintlnErr("USAGE: "+cmd.GetName()+" name cpus ram nic usb template [count|file.csv]") u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――") const usage string = `name Name of the VM to create. cpus Number of CPUs, between 1 and 16. ram Amount of RAM in MB, between 512 and 32768. nic Network interface, either "none" (no network) or "user" (network access). -template ID of the template to VM will be based on. +usb List of USB devices exposed in the VM; either "none" or a list of comma separated + vendorID:productID (4-digit hex number), each specifying a USB device; example + exposing two USB devices: 1fc9:001d,067b:2303 +templateID ID of the template to VM will be based on. count Number of VMs to create (if not specified, one is created). If specified and > 1, each VM's name is postfixed with [n], where n ranges from 1..count. @@ -46,7 +51,7 @@ func (cmd *Create)Run(args []string) int { host := g.GetInstance().Host argc := len(args) - if argc < 5 || argc > 6 { + if argc < 6 || argc > 7 { cmd.PrintUsage() return 1 } @@ -62,17 +67,22 @@ func (cmd *Create)Run(args []string) int { u.PrintlnErr("Invalid amount of RAM") return 1 } - nic := args[3] - template := args[4] + nic := NicType(args[3]) + usbDevs := cmd.str2UsbDevices(args[4]) + templateID, err := uuid.Parse(args[5]) + if err != nil { + u.PrintlnErr(err) + } + count := 1 var csvEntries []string = nil // If there is a 6th argument, checks whether it's a number or a CSV file. - if argc == 6 { - count, err = strconv.Atoi(args[5]) + if argc == 7 { + count, err = strconv.Atoi(args[6]) if err != nil { // It's not a number, we assume it's a CSV file and parse it. - csvFile := args[5] + csvFile := args[6] csvEntries, err = u.ReadCSVColumn(csvFile, 0) if err != nil { u.PrintlnErr(err.Error()) @@ -95,8 +105,9 @@ func (cmd *Create)Run(args []string) int { Name string Cpus int Ram int - Nic string - TemplateID string + Nic NicType + UsbDevs []string + TemplateID uuid.UUID } vmArgs := &VMArgs { @@ -104,7 +115,8 @@ func (cmd *Create)Run(args []string) int { Cpus: ncpus, Ram: ram, Nic: nic, - TemplateID: template, + UsbDevs: usbDevs, + TemplateID: templateID, } statusCode := 0 @@ -141,3 +153,17 @@ func (cmd *Create)Run(args []string) int { return statusCode } + +// Convert a string of USB devices of the form "1fc9:001d,067b:2303" +// into a slice of string where each element is a string of the form "1fc9:001d". +// Returns an empty slice if the input string is "none". +func (cmd *Create)str2UsbDevices(s string) []string { + usbDevs := []string{} + if s != "none" { + devs := strings.Split(s, ",") // Extracts USB devices + for _, dev := range devs { + usbDevs = append(usbDevs, dev) + } + } + return usbDevs +} diff --git a/src/version/version.go b/src/version/version.go index 513ed595269292226bb8a787118bb80a018f0bac..21f4e7a2acc11caafd6715c53e99e27eaa84c6f0 100644 --- a/src/version/version.go +++ b/src/version/version.go @@ -7,8 +7,8 @@ import ( const ( major = 1 - minor = 5 - bugfix = 3 + minor = 6 + bugfix = 0 ) type Version struct {