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

Added new command:

---------------------
vmstartwithcreds    Starts one or more VMs with user-defined credentials.
                    Requires VM_START VM access capability or VM_START_ANY user capability.
---------------------
client: fixed ReadCSVColumn which didn't work if separator char was not a comma
parent e2ae4057
No related branches found
No related tags found
No related merge requests found
...@@ -140,6 +140,14 @@ nexush> help ...@@ -140,6 +140,14 @@ nexush> help
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
ls List files in the specified dir or in the current dir if no argument is specified. ls List files in the specified dir or in the current dir if no argument is specified.
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
refresh Obtains a new access token.
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
version Display nexus server and client's versions.
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
whoami Displays the current user's details.
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
passwd Updates the current user's password.
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
userlist Lists users. userlist Lists users.
Requires USER_LIST user capability. Requires USER_LIST user capability.
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
...@@ -158,12 +166,16 @@ vmlist Lists VMs. ...@@ -158,12 +166,16 @@ vmlist Lists VMs.
vmcred2pdf Creates a PDF with the credentials required to attach to running VMs. vmcred2pdf Creates a PDF with the credentials required to attach to running VMs.
Requires VM_LIST VM access capability or VM_LIST_ANY user capability. Requires VM_LIST VM access capability or VM_LIST_ANY user capability.
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
vmcred2txt Creates a text file with the credentials required to attach to running VMs. vmcred2csv 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
Requires VM_LIST VM access capability or VM_LIST_ANY user capability. Requires VM_LIST VM access capability or VM_LIST_ANY user capability.
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
vmstart Starts one or more VMs. vmstart Starts one or more VMs.
Requires VM_START VM access capability or VM_START_ANY user capability. Requires VM_START VM access capability or VM_START_ANY user capability.
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
vmstartwithcreds Starts one or more VMs with user-defined credentials.
Requires VM_START VM access capability or VM_START_ANY user capability.
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
vmkill Kills one or more VMs. vmkill Kills one or more VMs.
Requires VM_STOP VM access capability or VM_STOP_ANY user capability. Requires VM_STOP VM access capability or VM_STOP_ANY user capability.
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
...@@ -191,13 +203,13 @@ vmaddaccess Adds a user's VM access in one or more VMs. ...@@ -191,13 +203,13 @@ vmaddaccess Adds a user's VM access in one or more VMs.
vmdelaccess Removes a user's VM access in one or more VMs. vmdelaccess Removes a user's VM access in one or more VMs.
Requires VM_SET_ACCESS user capability and VM_SET_ACCESS VM access capability. Requires VM_SET_ACCESS user capability and VM_SET_ACCESS VM access capability.
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
vmexportdir Exports one or more VMs' directory into one or more tar archives. vmexportdir Exports one or more VMs' directory into one or more compressed archives.
Requires VM_READFS VM access capability or VM_READFS_ANY user capability. Requires VM_READFS VM access capability or VM_READFS_ANY user capability.
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
vmimportdir Copies a local directory (or file) and all its content into one or more VMs. vmimportdir 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. Requires VM_WRITEFS VM access capability or VM_WRITEFS_ANY user capability.
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
tpllist Lists available templates. tpllist Lists templates.
Requires TPL_LIST or TPL_LIST_ANY user capability. Requires TPL_LIST or TPL_LIST_ANY user capability.
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
tplcreate Creates a template from an existing VM. tplcreate Creates a template from an existing VM.
......
package cmdVM
import (
"errors"
"strconv"
u "nexus-client/utils"
g "nexus-client/globals"
)
type StartWithCreds struct {
Name string
}
func (cmd *StartWithCreds)GetName() string {
return cmd.Name
}
func (cmd *StartWithCreds)GetDesc() []string {
return []string{
"Starts one or more VMs with user-defined credentials.",
"Requires VM_START VM access capability or VM_START_ANY user capability."}
}
func (cmd *StartWithCreds)PrintUsage() {
for _, desc := range cmd.GetDesc() {
u.PrintlnErr(desc)
}
u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――")
u.PrintlnErr("USAGE: "+cmd.GetName()+" file.csv")
u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――")
const usage string = `file.csv 4-column CSV file defining the VMs to start and their credentials.
Content of the 4 columns: VM ID;VM name;port;password.`
u.PrintlnErr(usage)
}
func (cmd *StartWithCreds)parseCSVFile(csvFile string) ([]string, []string, []string, error) {
// Column 0: VM IDs
vmIDs, err := u.ReadCSVColumn(csvFile, 0)
if err != nil {
return nil, nil, nil, err
}
// Column 2: ports
ports, err := u.ReadCSVColumn(csvFile, 2)
if err != nil {
return nil, nil, nil, err
}
// Column 3: passwords
pwds, err := u.ReadCSVColumn(csvFile, 3)
if err != nil {
return nil, nil, nil, err
}
count := len(vmIDs)
if (len(ports) != count) || (len(pwds) != count) {
return nil, nil, nil, errors.New("Invalid CSV file: all columns must have the same number of entries!")
}
return vmIDs, ports, pwds, nil
}
func (cmd *StartWithCreds)Run(args []string) int {
client := g.GetInstance().Client
host := g.GetInstance().Host
argc := len(args)
if argc != 1 {
cmd.PrintUsage()
return 1
}
vmIDs, ports, pwds, err := cmd.parseCSVFile(args[0])
if err != nil {
u.PrintlnErr(err.Error())
return 1
}
statusCode := 0
type VMArgs struct {
Port int
Pwd string
}
vmArgs := &VMArgs {}
for i, vmID := range(vmIDs) {
port, err := strconv.Atoi(ports[i])
if err != nil {
u.PrintlnErr("Line ",i,": invalid port number")
}
vmArgs.Port = port
vmArgs.Pwd = pwds[i]
resp, err := client.R().SetBody(vmArgs).Put(host+"/vms/"+vmID+"/startwithcreds")
if err != nil {
u.PrintlnErr("Failed starting VM \""+vmID+"\": "+err.Error())
statusCode = 1
} else {
if resp.IsSuccess() {
u.Println("Started VM \""+vmID+"\"")
} else {
u.PrintlnErr("Failed starting VM \""+vmID+"\": "+resp.Status()+": "+resp.String())
statusCode = 1
}
}
}
return statusCode
}
...@@ -38,6 +38,7 @@ var cmdList = []cmd.Command { ...@@ -38,6 +38,7 @@ var cmdList = []cmd.Command {
&cmdVM.Cred2pdf{"vmcred2pdf"}, &cmdVM.Cred2pdf{"vmcred2pdf"},
&cmdVM.Cred2csv{"vmcred2csv"}, &cmdVM.Cred2csv{"vmcred2csv"},
&cmdVM.Start{"vmstart"}, &cmdVM.Start{"vmstart"},
&cmdVM.StartWithCreds{"vmstartwithcreds"},
&cmdVM.Stop{"vmkill"}, &cmdVM.Stop{"vmkill"},
&cmdVM.Shutdown{"vmshutdown"}, &cmdVM.Shutdown{"vmshutdown"},
&cmdVM.Reboot{"vmreboot"}, &cmdVM.Reboot{"vmreboot"},
......
...@@ -45,6 +45,7 @@ var cmdList = []cmd.Command { ...@@ -45,6 +45,7 @@ var cmdList = []cmd.Command {
&cmdVM.Cred2pdf{"vmcred2pdf"}, &cmdVM.Cred2pdf{"vmcred2pdf"},
&cmdVM.Cred2csv{"vmcred2csv"}, &cmdVM.Cred2csv{"vmcred2csv"},
&cmdVM.Start{"vmstart"}, &cmdVM.Start{"vmstart"},
&cmdVM.StartWithCreds{"vmstartwithcreds"},
&cmdVM.Stop{"vmkill"}, &cmdVM.Stop{"vmkill"},
&cmdVM.Shutdown{"vmshutdown"}, &cmdVM.Shutdown{"vmshutdown"},
&cmdVM.Reboot{"vmreboot"}, &cmdVM.Reboot{"vmreboot"},
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"os" "os"
"io" "io"
"errors" "errors"
"strconv"
"encoding/csv" "encoding/csv"
) )
...@@ -16,6 +17,8 @@ func ReadCSVColumn(csvFile string, column int) ([]string, error) { ...@@ -16,6 +17,8 @@ func ReadCSVColumn(csvFile string, column int) ([]string, error) {
} }
defer file.Close() defer file.Close()
reader := csv.NewReader(file) reader := csv.NewReader(file)
reader.Comma = ';' // specify the column separator
reader.Comment = '#' // lines starting with this symbol are to be ignored
var entries []string var entries []string
for { for {
...@@ -26,8 +29,9 @@ func ReadCSVColumn(csvFile string, column int) ([]string, error) { ...@@ -26,8 +29,9 @@ func ReadCSVColumn(csvFile string, column int) ([]string, error) {
if err != nil { if err != nil {
return nil, errors.New("Failed reading "+err.Error()) return nil, errors.New("Failed reading "+err.Error())
} }
if column >= len(record) { colCount := len(record)
return nil, errors.New("Failed reading \""+csvFile+"\": column out of bound") if column >= colCount {
return nil, errors.New("Failed reading \""+csvFile+"\": column out of bound (detected "+strconv.Itoa(colCount)+" columns)")
} }
entries = append(entries, record[column]) entries = append(entries, record[column])
} }
......
...@@ -244,16 +244,15 @@ func (r *RouterVMs)StartVMWithCreds(c echo.Context) error { ...@@ -244,16 +244,15 @@ func (r *RouterVMs)StartVMWithCreds(c echo.Context) error {
// Deserializes and validates client's parameters. // Deserializes and validates client's parameters.
type Parameters struct { type Parameters struct {
port int `json:"port" validate:"required,gte=1100,lte=65535"` Port int `json:"port" validate:"required,gte=1100,lte=65535"`
pwd string `json:"pwd" validate:"required,min=8,max=64"` Pwd string `json:"pwd" validate:"required,min=8,max=64"`
} }
p := new(Parameters) p := new(Parameters)
if err := decodeJson(c, &p); err != nil { if err := decodeJson(c, &p); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error()) return echo.NewHTTPError(http.StatusBadRequest, err.Error())
} }
_, _, err := r.vms.StartVM(vm.GetID()) if err := r.vms.StartVMWithCreds(vm.GetID(), p.Port, p.Pwd); err != nil {
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error()) return echo.NewHTTPError(http.StatusBadRequest, err.Error())
} }
return c.JSONPretty(http.StatusOK, jsonMsg("OK"), " ") return c.JSONPretty(http.StatusOK, jsonMsg("OK"), " ")
......
...@@ -177,6 +177,12 @@ func (vms *VMs)StartVMWithCreds(vmID uuid.UUID, port int, pwd string) error { ...@@ -177,6 +177,12 @@ func (vms *VMs)StartVMWithCreds(vmID uuid.UUID, port int, pwd string) error {
vms.rwlock.Lock() vms.rwlock.Lock()
defer vms.rwlock.Unlock() defer vms.rwlock.Unlock()
if vms.usedPorts[port] || !utils.IsPortAvailable(port) {
return errors.New("Failed starting VM: port already in use")
} else {
vms.usedPorts[port] = true
}
vm, err := vms.getVMUnsafe(vmID) vm, err := vms.getVMUnsafe(vmID)
if err != nil { if err != nil {
return err return err
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment