Skip to content
Snippets Groups Projects
Commit dfc598c4 authored by Florent Gluck's avatar Florent Gluck
Browse files

Added VM_ATTACH and VM_ATTACH_ANY capabilities.

Previously, the VM_LIST VM capability allowed one to list the VM as well as attach to it. This capability was too coarse. Instead, VM_LIST should only allow one to list the VM and nothing more. The VM_ATTACH capability was added specifically to allow a user to attach to the VM.
parent 2abc8b28
No related branches found
No related tags found
No related merge requests found
Showing
with 194 additions and 105 deletions
...@@ -376,9 +376,9 @@ Delete VMs matching the "exam ISC_433 PCO" pattern: ...@@ -376,9 +376,9 @@ Delete VMs matching the "exam ISC_433 PCO" pattern:
vmdel "exam ISC_433 PCO" vmdel "exam ISC_433 PCO"
``` ```
Set the VM access for VM `89649fe3-4940-4b77-929e-50903789cd87` with: `VM_LIST` and `VM_DESTROY` for user `student@nexus.org`: Set the VM access for VM `89649fe3-4940-4b77-929e-50903789cd87` with: `VM_LIST` and `VM_ATTACH` for user `student@nexus.org`:
``` ```
vmaddaccess 89649fe3-4940-4b77-929e-50903789cd87 student@nexus.org VM_LIST VM_DESTROY vmaddaccess 89649fe3-4940-4b77-929e-50903789cd87 student@nexus.org VM_LIST VM_ATTACH
``` ```
Set VM access for VMs matching the "alpine" pattern with: `VM_START` and `VM_STOP` for user `student@nexus.org`: Set VM access for VMs matching the "alpine" pattern with: `VM_START` and `VM_STOP` for user `student@nexus.org`:
...@@ -675,8 +675,8 @@ source .env.nexus ...@@ -675,8 +675,8 @@ source .env.nexus
Access control is implemented through capabilities. Access control is implemented through capabilities.
Capabilities define what users can or cannot do. They are divided into two categories: Capabilities define what users can or cannot do. They are divided into two categories:
- **User capabilities**: define user access control; these are stored in the user metadata - **User capabilities**: define user access control; these are stored in the user config
- **VM access capabilities**: define access to VMs; these are stored in the VM metadata - **VM access capabilities**: define access to VMs; these are stored in the VM config
### User capabilities ### User capabilities
...@@ -697,6 +697,7 @@ The table below lists all potential capabilities associated to a user: ...@@ -697,6 +697,7 @@ The table below lists all potential capabilities associated to a user:
| VM_STOP_ANY | Can kill/shutdown **ANY** VM | | VM_STOP_ANY | Can kill/shutdown **ANY** VM |
| VM_REBOOT_ANY | Can Reboot **ANY** VM | | VM_REBOOT_ANY | Can Reboot **ANY** VM |
| VM_LIST_ANY | Can list **ANY** VM | | VM_LIST_ANY | Can list **ANY** VM |
| VM_ATTACH_ANY | Can attach to **ANY** VM |
| VM_READFS_ANY | Can export files from **ANY** VM | | VM_READFS_ANY | Can export files from **ANY** VM |
| VM_WRITEFS_ANY | Can import files into **ANY** VM | | VM_WRITEFS_ANY | Can import files into **ANY** VM |
| VM_SET_ACCESS | Can change (edit or delete) a VM's access | | VM_SET_ACCESS | Can change (edit or delete) a VM's access |
...@@ -720,16 +721,17 @@ These capabilities are called "VM access capabilities": ...@@ -720,16 +721,17 @@ These capabilities are called "VM access capabilities":
| Capability | Description | | Capability | Description |
|--- |--- | |--- |--- |
| VM_SET_ACCESS | User can add/change access to the VM | | VM_SET_ACCESS | User can add/change access to the (running or stopped) VM |
| | VM_SET_ACCESS **must also be present** in the user's capabilities! | | | VM_SET_ACCESS **must also be present** in the user's capabilities! |
| VM_DESTROY | User can destroy the VM | | VM_DESTROY | User can destroy the (stopped) VM |
| VM_EDIT | User can edit the VM | | VM_EDIT | User can edit the (running or stopped) VM |
| VM_START | User can start the VM | | VM_START | User can start the (stopped) VM |
| VM_STOP | User can kill/shutdown the VM | | VM_STOP | User can kill/shutdown the (running) VM |
| VM_REBOOT | User can reboot the VM | | VM_REBOOT | User can reboot the (running) VM |
| VM_LIST | User can list or attach to the VM | | VM_LIST | User can list the VM's meta-data |
| VM_READFS | User can export files from the VM | | VM_ATTACH | User can attach to the (running) VM |
| VM_WRITEFS | User can import files into the VM | | VM_READFS | User can export files from the (stopped) VM |
| VM_WRITEFS | User can import files into the (stopped) VM |
### IMPORTANT ### IMPORTANT
......
...@@ -192,12 +192,14 @@ Here is the content of `vm.json`: ...@@ -192,12 +192,14 @@ Here is the content of `vm.json`:
"access": { "access": {
"lukeskywalker@force.net" : { "lukeskywalker@force.net" : {
"VM_LIST":1, "VM_LIST":1,
"VM_ATTACH":1,
"VM_START":1, "VM_START":1,
"VM_STOP":1, "VM_STOP":1,
"VM_REBOOT":1 "VM_REBOOT":1
}, },
"student@nexus.org" : { "student@nexus.org" : {
"VM_LIST":1 "VM_LIST":1,
"VM_ATTACH":1
} }
} }
} }
...@@ -269,6 +271,7 @@ The table below lists all potential capabilities associated to a user: ...@@ -269,6 +271,7 @@ The table below lists all potential capabilities associated to a user:
| VM_STOP_ANY | Can kill/shutdown **ANY** VM | | VM_STOP_ANY | Can kill/shutdown **ANY** VM |
| VM_REBOOT_ANY | Can Reboot **ANY** VM | | VM_REBOOT_ANY | Can Reboot **ANY** VM |
| VM_LIST_ANY | Can list **ANY** VM | | VM_LIST_ANY | Can list **ANY** VM |
| VM_ATTACH_ANY | Can attach to **ANY** VM |
| VM_READFS_ANY | Can export files from **ANY** VM | | VM_READFS_ANY | Can export files from **ANY** VM |
| VM_WRITEFS_ANY | Can import files into **ANY** VM | | VM_WRITEFS_ANY | Can import files into **ANY** VM |
| VM_SET_ACCESS | Can change (edit or delete) a VM's access | | VM_SET_ACCESS | Can change (edit or delete) a VM's access |
...@@ -292,16 +295,17 @@ These capabilities are called "VM access capabilities": ...@@ -292,16 +295,17 @@ These capabilities are called "VM access capabilities":
| Capability | Description | | Capability | Description |
|--- |--- | |--- |--- |
| VM_SET_ACCESS | User can add/change access to the VM | | VM_SET_ACCESS | User can add/change access to the (running or stopped) VM |
| | VM_SET_ACCESS **must also be present** in the user's capabilities! | | | VM_SET_ACCESS **must also be present** in the user's capabilities! |
| VM_DESTROY | User can destroy the VM | | VM_DESTROY | User can destroy the (stopped) VM |
| VM_EDIT | User can edit the VM | | VM_EDIT | User can edit the (running or stopped) VM |
| VM_START | User can start the VM | | VM_START | User can start the (stopped) VM |
| VM_STOP | User can kill/shutdown the VM | | VM_STOP | User can kill/shutdown the (running) VM |
| VM_REBOOT | User can reboot the VM | | VM_REBOOT | User can reboot the (running) VM |
| VM_LIST | User can list or attach to the VM | | VM_LIST | User can list the VM's meta-data |
| VM_READFS | User can export files from the VM | | VM_ATTACH | User can attach to the (running) VM |
| VM_WRITEFS | User can import files into the VM | | VM_READFS | User can export files from the (stopped) VM |
| VM_WRITEFS | User can import files into the (stopped) VM |
### IMPORTANT ### IMPORTANT
...@@ -340,7 +344,7 @@ These capabilities are called "VM access capabilities": ...@@ -340,7 +344,7 @@ These capabilities are called "VM access capabilities":
| `/vms` | returns VMs that can be listed | GET | | `VM_LIST_ANY` | OR | `VM_LIST` | | `/vms` | returns VMs that can be listed | GET | | `VM_LIST_ANY` | OR | `VM_LIST` |
| `/vms/{id}` | returns a VM | GET | | `VM_LIST_ANY` | OR | `VM_LIST` | | `/vms/{id}` | returns a VM | GET | | `VM_LIST_ANY` | OR | `VM_LIST` |
| `/vms/start` | returns VMs that can be started | GET | | `VM_START_ANY` | OR | `VM_START` | | `/vms/start` | returns VMs that can be started | GET | | `VM_START_ANY` | OR | `VM_START` |
| `/vms/attach` | returns VMs that can be attached to | GET | | `VM_LIST_ANY` | OR | `VM_LIST` | | `/vms/attach` | returns VMs that can be attached to | GET | | `VM_ATTACH_ANY` | OR | `VM_ATTACH` |
| `/vms/stop` | returns VMs that can be killed/shutdown | GET | | `VM_STOP_ANY` | OR | `VM_STOP` | | `/vms/stop` | returns VMs that can be killed/shutdown | GET | | `VM_STOP_ANY` | OR | `VM_STOP` |
| `/vms/reboot` | returns VMs that can be rebooted | GET | | `VM_REBOOT_ANY` | OR | `VM_REBOOT` | | `/vms/reboot` | returns VMs that can be rebooted | GET | | `VM_REBOOT_ANY` | OR | `VM_REBOOT` |
| `/vms/edit` | returns VMs that can be edited | GET | | `VM_EDIT_ANY` | OR | `VM_EDIT` | | `/vms/edit` | returns VMs that can be edited | GET | | `VM_EDIT_ANY` | OR | `VM_EDIT` |
......
package cmdVM package cmdVM
import ( import (
"fmt"
"regexp" "regexp"
"errors" "errors"
"strings" "strings"
...@@ -33,69 +32,81 @@ Regex examples: ...@@ -33,69 +32,81 @@ Regex examples:
u.PrintlnErr(usage) u.PrintlnErr(usage)
} }
// Prints a list of filtered VMs for a given route. // Returns a list of filtered VMs for a given route.
// Return 0 if everything went well or 1 in case of failure. // Filters are based on patterns describing IDs or regexes.
func printFilteredVMs(c cmd.Command, args []string, route string) int { // Patterns can either be:
if len(args) < 1 { // - any number of "VM IDs" (UUID)
c.PrintUsage() // - any number of "VM names"
return 1 // - any combination of "VM IDs" and "VM names"
// - any regular expression (it applies to the VM's name only)
// Remark: the matching is case-insensitive
// Regular expression examples:
// "." -> matches everything
// "bla" -> matches any VM name containing "bla"
func getFilteredVMs(route string, patterns []string) ([]vm.VMNetworkSerialized, error) {
if len(patterns) < 1 {
return nil, errors.New("At least one ID or regex must be specified")
} }
// Check if a "-l" argument is specified client := g.GetInstance().Client
foundLongOutputFlag := -1 host := g.GetInstance().Host
for idx, arg := range args {
if arg == "-l" { var ids []string
foundLongOutputFlag = idx var regexes []string
for _, pattern := range patterns {
_, err := uuid.Parse(pattern)
if err != nil {
regexes = append(regexes, pattern)
} else {
ids = append(ids, pattern)
} }
} }
if foundLongOutputFlag >= 0 { resp, err := client.R().Get(host+route)
args = u.RemoveArgAtIndex(args, foundLongOutputFlag) if err != nil {
return nil, err
} }
vms, err := getFilteredVMs(route, args) vmsList := []vm.VMNetworkSerialized{}
if resp.IsSuccess() {
vms, err := deserializeVMs(resp)
if err != nil { if err != nil {
u.PrintlnErr("Error: "+err.Error()) return nil, err
return 1
} }
if foundLongOutputFlag >= 0 {
for _, vm := range vms { for _, vm := range vms {
str, err := vm.String() found := false
if err != nil { for _, id := range ids {
u.PrintlnErr("Failed decoding VM "+vm.ID.String()+" to string. Skipped.") if id == vm.ID.String() {
} else { vmsList = append(vmsList, vm)
u.Println(str) found = true
break
} }
} }
if found {
continue
}
for _, regex := range regexes {
match, err := regexp.MatchString(strings.ToLower(regex), strings.ToLower(vm.Name))
if err != nil {
return nil, errors.New("Error matching \""+regex+"\": "+err.Error())
} else { } else {
// Compute the length of the longest name and state (used for padding columns) if match {
nameLen := 0 vmsList = append(vmsList, vm)
idLen := 0 break
stateLen := 0
for _, vm := range vms {
l := len(vm.Name)
if l > nameLen {
nameLen = l
} }
idLen = len(vm.ID.String()) // ID is a UUID of constant length
l = len(vm.State)
if l > stateLen {
stateLen = l
} }
} }
paddedFmt := fmt.Sprintf("%%-%ds | %%-%ds | %%-%ds\n",nameLen, idLen, stateLen)
for _, vm := range vms {
fmt.Printf(paddedFmt, vm.Name, vm.ID, vm.State)
} }
return vmsList, nil
} else {
return nil, errors.New("Error: "+resp.Status()+": "+resp.String())
} }
return 0
} }
// Returns a list of filtered VMs for a given route. // Returns a list of filtered VM credentials for a given route.
// Filters are based on patterns describing IDs or regexes. // Filters are based on patterns describing IDs or regexes.
// Patterns can either be: // Patterns can either be:
// - any number of "VM IDs" (UUID) // - any number of "VM IDs" (UUID)
...@@ -106,7 +117,7 @@ func printFilteredVMs(c cmd.Command, args []string, route string) int { ...@@ -106,7 +117,7 @@ func printFilteredVMs(c cmd.Command, args []string, route string) int {
// Regular expression examples: // Regular expression examples:
// "." -> matches everything // "." -> matches everything
// "bla" -> matches any VM name containing "bla" // "bla" -> matches any VM name containing "bla"
func getFilteredVMs(route string, patterns []string) ([]vm.VMNetworkSerialized, error) { func getFilteredVMCredentials(route string, patterns []string) ([]vm.VMCredentialsSerialized, error) {
if len(patterns) < 1 { if len(patterns) < 1 {
return nil, errors.New("At least one ID or regex must be specified") return nil, errors.New("At least one ID or regex must be specified")
} }
...@@ -131,10 +142,10 @@ func getFilteredVMs(route string, patterns []string) ([]vm.VMNetworkSerialized, ...@@ -131,10 +142,10 @@ func getFilteredVMs(route string, patterns []string) ([]vm.VMNetworkSerialized,
return nil, err return nil, err
} }
vmsList := []vm.VMNetworkSerialized{} vmsList := []vm.VMCredentialsSerialized{}
if resp.IsSuccess() { if resp.IsSuccess() {
vms, err := deserializeVMs(resp) vms, err := deserializeVMCredentials(resp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -169,7 +180,7 @@ func getFilteredVMs(route string, patterns []string) ([]vm.VMNetworkSerialized, ...@@ -169,7 +180,7 @@ func getFilteredVMs(route string, patterns []string) ([]vm.VMNetworkSerialized,
} }
} }
// Deserialize a list of VMs from an http response (no filtering). // Deserialize a list of VMs from an http response.
func deserializeVMs(resp *resty.Response) ([]vm.VMNetworkSerialized, error) { func deserializeVMs(resp *resty.Response) ([]vm.VMNetworkSerialized, error) {
vms := []vm.VMNetworkSerialized{} vms := []vm.VMNetworkSerialized{}
if err := json.Unmarshal(resp.Body(), &vms); err != nil { if err := json.Unmarshal(resp.Body(), &vms); err != nil {
...@@ -178,6 +189,15 @@ func deserializeVMs(resp *resty.Response) ([]vm.VMNetworkSerialized, error) { ...@@ -178,6 +189,15 @@ func deserializeVMs(resp *resty.Response) ([]vm.VMNetworkSerialized, error) {
return vms, nil return vms, nil
} }
// Deserialize a list of VM credentials from an http response.
func deserializeVMCredentials(resp *resty.Response) ([]vm.VMCredentialsSerialized, error) {
vms := []vm.VMCredentialsSerialized{}
if err := json.Unmarshal(resp.Body(), &vms); err != nil {
return nil, err
}
return vms, nil
}
// Deserialize a single VM from an http response. // Deserialize a single VM from an http response.
func deserializeVM(resp *resty.Response) (*vm.VMNetworkSerialized, error) { func deserializeVM(resp *resty.Response) (*vm.VMNetworkSerialized, error) {
var vm *vm.VMNetworkSerialized = &vm.VMNetworkSerialized{} var vm *vm.VMNetworkSerialized = &vm.VMNetworkSerialized{}
......
...@@ -22,7 +22,7 @@ func (cmd *AddAccess)GetName() string { ...@@ -22,7 +22,7 @@ func (cmd *AddAccess)GetName() string {
func (cmd *AddAccess)GetDesc() []string { func (cmd *AddAccess)GetDesc() []string {
return []string{ return []string{
"Adds a user's VM access in one or more VMs.", "Adds a user's VM access in one or more VMs.",
"Requires VM_SET_ACCESS user capability and VM_SET_ACCESS VM access capability."} "If not the VM's owner: requires VM_SET_ACCESS user capability and VM_SET_ACCESS VM access capability."}
} }
func (cmd *AddAccess)PrintUsage() { func (cmd *AddAccess)PrintUsage() {
......
...@@ -19,7 +19,7 @@ func (cmd *AttachAsync)GetName() string { ...@@ -19,7 +19,7 @@ func (cmd *AttachAsync)GetName() string {
func (cmd *AttachAsync)GetDesc() []string { func (cmd *AttachAsync)GetDesc() []string {
return []string{ return []string{
"Attaches to one or more VMs in order to use their desktop environment.", "Attaches to one or more VMs in order to use their desktop environment.",
"Requires VM_LIST VM access capability or VM_LIST_ANY user capability."} "If not the VM's owner: requires VM_ATTACH VM access capability or VM_ATTACH_ANY user capability."}
} }
func (cmd *AttachAsync)PrintUsage() { func (cmd *AttachAsync)PrintUsage() {
...@@ -42,7 +42,7 @@ func (cmd *AttachAsync)Run(args []string) int { ...@@ -42,7 +42,7 @@ func (cmd *AttachAsync)Run(args []string) int {
return 1 return 1
} }
vms, err := getFilteredVMs("/vms/attach", args) vms, err := getFilteredVMCredentials("/vms/attach", args)
if err != nil { if err != nil {
u.PrintlnErr(err.Error()) u.PrintlnErr(err.Error())
return 1 return 1
...@@ -56,7 +56,7 @@ func (cmd *AttachAsync)Run(args []string) int { ...@@ -56,7 +56,7 @@ func (cmd *AttachAsync)Run(args []string) int {
statusCode := 0 statusCode := 0
for _, v := range(vms) { for _, v := range(vms) {
go func(v vm.VMNetworkSerialized) { go func(v vm.VMCredentialsSerialized) {
stdoutStderr, err := exec.RunRemoteViewer(hostname, cert, v.Name, v.Port, v.Pwd, false) stdoutStderr, err := exec.RunRemoteViewer(hostname, cert, v.Name, v.Port, v.Pwd, false)
if err != nil { if err != nil {
u.PrintlnErr("Failed attaching to VM ", v.ID, ": ", fmt.Sprintf("%s", stdoutStderr)) u.PrintlnErr("Failed attaching to VM ", v.ID, ": ", fmt.Sprintf("%s", stdoutStderr))
......
...@@ -20,7 +20,7 @@ func (cmd *AttachSync)GetName() string { ...@@ -20,7 +20,7 @@ func (cmd *AttachSync)GetName() string {
func (cmd *AttachSync)GetDesc() []string { func (cmd *AttachSync)GetDesc() []string {
return []string{ return []string{
"Attaches to one or more VMs in order to use their desktop environment.", "Attaches to one or more VMs in order to use their desktop environment.",
"Requires VM_LIST VM access capability or VM_LIST_ANY user capability."} "If not the VM's owner: requires VM_LIST VM access capability or VM_LIST_ANY user capability."}
} }
func (cmd *AttachSync)PrintUsage() { func (cmd *AttachSync)PrintUsage() {
...@@ -43,7 +43,7 @@ func (cmd *AttachSync)Run(args []string) int { ...@@ -43,7 +43,7 @@ func (cmd *AttachSync)Run(args []string) int {
return 1 return 1
} }
vms, err := getFilteredVMs("/vms/attach", args) vms, err := getFilteredVMCredentials("/vms/attach", args)
if err != nil { if err != nil {
u.PrintlnErr(err.Error()) u.PrintlnErr(err.Error())
return 1 return 1
...@@ -61,7 +61,7 @@ func (cmd *AttachSync)Run(args []string) int { ...@@ -61,7 +61,7 @@ func (cmd *AttachSync)Run(args []string) int {
statusCode := 0 statusCode := 0
for _, v := range(vms) { for _, v := range(vms) {
go func(v vm.VMNetworkSerialized) { go func(v vm.VMCredentialsSerialized) {
stdoutStderr, err := exec.RunRemoteViewer(hostname, cert, v.Name, v.Port, v.Pwd, false) stdoutStderr, err := exec.RunRemoteViewer(hostname, cert, v.Name, v.Port, v.Pwd, false)
if err != nil { if err != nil {
u.PrintlnErr("Failed attaching to VM ", v.ID, ": ", fmt.Sprintf("%s", stdoutStderr)) u.PrintlnErr("Failed attaching to VM ", v.ID, ": ", fmt.Sprintf("%s", stdoutStderr))
......
...@@ -20,7 +20,7 @@ func (cmd *Creds2csv)GetDesc() []string { ...@@ -20,7 +20,7 @@ func (cmd *Creds2csv)GetDesc() []string {
return []string{ return []string{
"Creates a CSV file with the credentials required to attach to running VMs.", "Creates a CSV file with the credentials required to attach to running VMs.",
"The written CSV file contains 4 columns: VM ID;VM name;port;password", "The written CSV file contains 4 columns: VM ID;VM name;port;password",
"Requires VM_LIST VM access capability or VM_LIST_ANY user capability."} "If not the VM's owner: requires VM_ATTACH VM access capability or VM_ATTACH_ANY user capability."}
} }
func (cmd *Creds2csv)PrintUsage() { func (cmd *Creds2csv)PrintUsage() {
...@@ -42,7 +42,7 @@ func (cmd *Creds2csv)Run(args []string) int { ...@@ -42,7 +42,7 @@ func (cmd *Creds2csv)Run(args []string) int {
csvFile := args[argc-1] csvFile := args[argc-1]
vms, err := getFilteredVMs("/vms/attach", args[:argc-1]) vms, err := getFilteredVMCredentials("/vms/attach", args[:argc-1])
if err != nil { if err != nil {
u.PrintlnErr("Error: "+err.Error()) u.PrintlnErr("Error: "+err.Error())
return 1 return 1
......
...@@ -21,7 +21,7 @@ func (cmd *Creds2pdf)GetName() string { ...@@ -21,7 +21,7 @@ func (cmd *Creds2pdf)GetName() string {
func (cmd *Creds2pdf)GetDesc() []string { func (cmd *Creds2pdf)GetDesc() []string {
return []string{ return []string{
"Creates a PDF with the credentials required to attach to running VMs.", "Creates a PDF with the credentials required to attach to running VMs.",
"Requires VM_LIST VM access capability or VM_LIST_ANY user capability."} "If not the VM's owner: requires VM_ATTACH VM access capability or VM_ATTACH_ANY user capability."}
} }
func (cmd *Creds2pdf)PrintUsage() { func (cmd *Creds2pdf)PrintUsage() {
...@@ -43,7 +43,7 @@ func (cmd *Creds2pdf)Run(args []string) int { ...@@ -43,7 +43,7 @@ func (cmd *Creds2pdf)Run(args []string) int {
pdfFile := args[argc-1] pdfFile := args[argc-1]
vms, err := getFilteredVMs("/vms/attach", args[:argc-1]) vms, err := getFilteredVMCredentials("/vms/attach", args[:argc-1])
if err != nil { if err != nil {
u.PrintlnErr("Error: "+err.Error()) u.PrintlnErr("Error: "+err.Error())
return 1 return 1
......
...@@ -16,7 +16,7 @@ func (cmd *Del)GetName() string { ...@@ -16,7 +16,7 @@ func (cmd *Del)GetName() string {
func (cmd *Del)GetDesc() []string { func (cmd *Del)GetDesc() []string {
return []string{ return []string{
"Deletes one or more VMs.", "Deletes one or more VMs.",
"Requires VM_DESTROY VM access capability or VM_DESTROY_ANY user capability."} "If not the VM's owner: requires VM_DESTROY VM access capability or VM_DESTROY_ANY user capability."}
} }
func (cmd *Del)PrintUsage() { func (cmd *Del)PrintUsage() {
......
...@@ -21,7 +21,7 @@ func (cmd *DelAccess)GetName() string { ...@@ -21,7 +21,7 @@ func (cmd *DelAccess)GetName() string {
func (cmd *DelAccess)GetDesc() []string { func (cmd *DelAccess)GetDesc() []string {
return []string{ return []string{
"Removes a user's VM access in one or more VMs.", "Removes a user's VM access in one or more VMs.",
"Requires VM_SET_ACCESS user capability and VM_SET_ACCESS VM access capability."} "If not the VM's owner: requires VM_SET_ACCESS user capability and VM_SET_ACCESS VM access capability."}
} }
func (cmd *DelAccess)PrintUsage() { func (cmd *DelAccess)PrintUsage() {
......
...@@ -21,7 +21,7 @@ func (cmd *Edit)GetName() string { ...@@ -21,7 +21,7 @@ func (cmd *Edit)GetName() string {
func (cmd *Edit)GetDesc() []string { func (cmd *Edit)GetDesc() []string {
return []string{ return []string{
"Edits one or more VMs' properties: name, cpus, ram or nic.", "Edits one or more VMs' properties: name, cpus, ram or nic.",
"Requires VM_EDIT VM access capability or VM_EDIT_ANY user capability."} "If not the VM's owner: requires VM_EDIT VM access capability or VM_EDIT_ANY user capability."}
} }
func (cmd *Edit)PrintUsage() { func (cmd *Edit)PrintUsage() {
......
...@@ -17,7 +17,7 @@ func (cmd *ExportDir)GetName() string { ...@@ -17,7 +17,7 @@ func (cmd *ExportDir)GetName() string {
func (cmd *ExportDir)GetDesc() []string { func (cmd *ExportDir)GetDesc() []string {
return []string{ return []string{
"Exports one or more VMs' directory into one or more compressed archives.", "Exports one or more VMs' directory into one or more compressed archives.",
"Requires VM_READFS VM access capability or VM_READFS_ANY user capability."} "If not the VM's owner: requires VM_READFS VM access capability or VM_READFS_ANY user capability."}
} }
func (cmd *ExportDir)PrintUsage() { func (cmd *ExportDir)PrintUsage() {
......
...@@ -17,7 +17,7 @@ func (cmd *ImportDir)GetName() string { ...@@ -17,7 +17,7 @@ func (cmd *ImportDir)GetName() string {
func (cmd *ImportDir)GetDesc() []string { func (cmd *ImportDir)GetDesc() []string {
return []string{ return []string{
"Copies a local directory (or file) and all its content into one or more VMs.", "Copies a local directory (or file) and all its content into one or more VMs.",
"Requires VM_WRITEFS VM access capability or VM_WRITEFS_ANY user capability."} "If not the VM's owner: requires VM_WRITEFS VM access capability or VM_WRITEFS_ANY user capability."}
} }
func (cmd *ImportDir)PrintUsage() { func (cmd *ImportDir)PrintUsage() {
......
package cmdVM package cmdVM
import ( import (
"fmt"
u "nexus-client/utils" u "nexus-client/utils"
) )
...@@ -15,7 +16,7 @@ func (cmd *List)GetName() string { ...@@ -15,7 +16,7 @@ func (cmd *List)GetName() string {
func (cmd *List)GetDesc() []string { func (cmd *List)GetDesc() []string {
return []string{ return []string{
"Lists VMs.", "Lists VMs.",
"Requires VM_LIST VM access capability or VM_LIST_ANY user capability."} "If not the VM's owner: requires VM_LIST VM access capability or VM_LIST_ANY user capability."}
} }
func (cmd *List)PrintUsage() { func (cmd *List)PrintUsage() {
...@@ -30,5 +31,67 @@ func (cmd *List)PrintUsage() { ...@@ -30,5 +31,67 @@ func (cmd *List)PrintUsage() {
} }
func (cmd *List)Run(args []string) int { func (cmd *List)Run(args []string) int {
return printFilteredVMs(cmd, args, "/vms") return cmd.printFilteredVMs(args, "/vms")
}
// Prints a list of filtered VMs for a given route.
// Return 0 if everything went well or 1 in case of failure.
func (cmd *List)printFilteredVMs(args []string, route string) int {
if len(args) < 1 {
cmd.PrintUsage()
return 1
}
// Check if a "-l" argument is specified
foundLongOutputFlag := -1
for idx, arg := range args {
if arg == "-l" {
foundLongOutputFlag = idx
}
}
if foundLongOutputFlag >= 0 {
args = u.RemoveArgAtIndex(args, foundLongOutputFlag)
}
vms, err := getFilteredVMs(route, args)
if err != nil {
u.PrintlnErr("Error: "+err.Error())
return 1
}
if foundLongOutputFlag >= 0 {
for _, vm := range vms {
str, err := vm.String()
if err != nil {
u.PrintlnErr("Failed decoding VM "+vm.ID.String()+" to string. Skipped.")
} else {
u.Println(str)
}
}
} else {
// Compute the length of the longest name and state (used for padding columns)
nameLen := 0
idLen := 0
stateLen := 0
for _, vm := range vms {
l := len(vm.Name)
if l > nameLen {
nameLen = l
}
idLen = len(vm.ID.String()) // ID is a UUID of constant length
l = len(vm.State)
if l > stateLen {
stateLen = l
}
}
paddedFmt := fmt.Sprintf("%%-%ds | %%-%ds | %%-%ds\n",nameLen, idLen, stateLen)
for _, vm := range vms {
fmt.Printf(paddedFmt, vm.Name, vm.ID, vm.State)
}
}
return 0
} }
...@@ -16,7 +16,7 @@ func (cmd *ListSingle)GetName() string { ...@@ -16,7 +16,7 @@ func (cmd *ListSingle)GetName() string {
func (cmd *ListSingle)GetDesc() []string { func (cmd *ListSingle)GetDesc() []string {
return []string{ return []string{
"Lists a single VM.", "Lists a single VM.",
"Requires VM_LIST VM access capability or VM_LIST_ANY user capability."} "If not the VM's owner: requires VM_LIST VM access capability or VM_LIST_ANY user capability."}
} }
func (cmd *ListSingle)PrintUsage() { func (cmd *ListSingle)PrintUsage() {
...@@ -38,10 +38,10 @@ func (cmd *ListSingle)Run(args []string) int { ...@@ -38,10 +38,10 @@ func (cmd *ListSingle)Run(args []string) int {
return 1 return 1
} }
uuid := args[0] vmID := args[0]
resp, err := client.R().Get(host+"/vms/"+uuid) resp, err := client.R().Get(host+"/vms/"+vmID)
if err != nil { if err != nil {
u.PrintlnErr("Failed retrieving VM \""+uuid+"\": "+err.Error()) u.PrintlnErr("Failed retrieving VM \""+vmID+"\": "+err.Error())
return 1 return 1
} else { } else {
if resp.IsSuccess() { if resp.IsSuccess() {
...@@ -57,7 +57,7 @@ func (cmd *ListSingle)Run(args []string) int { ...@@ -57,7 +57,7 @@ func (cmd *ListSingle)Run(args []string) int {
u.Println(str) u.Println(str)
} }
} else { } else {
u.PrintlnErr("Failed retrieving VM \""+uuid+"\": "+resp.Status()+": "+resp.String()) u.PrintlnErr("Failed retrieving VM \""+vmID+"\": "+resp.Status()+": "+resp.String())
return 1 return 1
} }
} }
......
...@@ -16,7 +16,7 @@ func (cmd *Reboot)GetName() string { ...@@ -16,7 +16,7 @@ func (cmd *Reboot)GetName() string {
func (cmd *Reboot)GetDesc() []string { func (cmd *Reboot)GetDesc() []string {
return []string{ return []string{
"Gracefully reboots one or more VMs.", "Gracefully reboots one or more VMs.",
"Requires VM_REBOOT VM access capability or VM_REBOOT_ANY user capability."} "If not the VM's owner: requires VM_REBOOT VM access capability or VM_REBOOT_ANY user capability."}
} }
func (cmd *Reboot)PrintUsage() { func (cmd *Reboot)PrintUsage() {
......
...@@ -16,7 +16,7 @@ func (cmd *Shutdown)GetName() string { ...@@ -16,7 +16,7 @@ func (cmd *Shutdown)GetName() string {
func (cmd *Shutdown)GetDesc() []string { func (cmd *Shutdown)GetDesc() []string {
return []string{ return []string{
"Gracefully shutdowns one or more VMs.", "Gracefully shutdowns one or more VMs.",
"Requires VM_STOP VM access capability or VM_STOP_ANY user capability."} "If not the VM's owner: requires VM_STOP VM access capability or VM_STOP_ANY user capability."}
} }
func (cmd *Shutdown)PrintUsage() { func (cmd *Shutdown)PrintUsage() {
......
...@@ -16,7 +16,7 @@ func (cmd *Start)GetName() string { ...@@ -16,7 +16,7 @@ func (cmd *Start)GetName() string {
func (cmd *Start)GetDesc() []string { func (cmd *Start)GetDesc() []string {
return []string{ return []string{
"Starts one or more VMs.", "Starts one or more VMs.",
"Requires VM_START VM access capability or VM_START_ANY user capability."} "If not the VM's owner: requires VM_START VM access capability or VM_START_ANY user capability."}
} }
func (cmd *Start)PrintUsage() { func (cmd *Start)PrintUsage() {
......
...@@ -18,7 +18,7 @@ func (cmd *StartWithCreds)GetName() string { ...@@ -18,7 +18,7 @@ func (cmd *StartWithCreds)GetName() string {
func (cmd *StartWithCreds)GetDesc() []string { func (cmd *StartWithCreds)GetDesc() []string {
return []string{ return []string{
"Starts one or more VMs with user-defined credentials.", "Starts one or more VMs with user-defined credentials.",
"Requires VM_START VM access capability or VM_START_ANY user capability."} "If not the VM's owner: requires VM_START VM access capability or VM_START_ANY user capability."}
} }
func (cmd *StartWithCreds)PrintUsage() { func (cmd *StartWithCreds)PrintUsage() {
......
...@@ -16,7 +16,7 @@ func (cmd *Stop)GetName() string { ...@@ -16,7 +16,7 @@ func (cmd *Stop)GetName() string {
func (cmd *Stop)GetDesc() []string { func (cmd *Stop)GetDesc() []string {
return []string{ return []string{
"Kills one or more VMs.", "Kills one or more VMs.",
"Requires VM_STOP VM access capability or VM_STOP_ANY user capability."} "If not the VM's owner: requires VM_STOP VM access capability or VM_STOP_ANY user capability."}
} }
func (cmd *Stop)PrintUsage() { func (cmd *Stop)PrintUsage() {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment