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

server and client: created structures in common to store REST arguments

fixed a serious bug where ports used by VMs were not being marked as free again!
added more tests to validate script
bumped server version to 1.8.1
bumped client version to 1.8.1
parent 66ce54d9
No related branches found
No related tags found
No related merge requests found
Showing
with 189 additions and 132 deletions
package cmdTemplate package cmdTemplate
import ( import (
"nexus-common/params"
u "nexus-client/utils" u "nexus-client/utils"
g "nexus-client/globals" g "nexus-client/globals"
"github.com/google/uuid" "github.com/google/uuid"
...@@ -65,15 +66,8 @@ func (cmd *Create)makeRequestForID(vmID uuid.UUID, name, access string) (*resty. ...@@ -65,15 +66,8 @@ func (cmd *Create)makeRequestForID(vmID uuid.UUID, name, access string) (*resty.
client := g.GetInstance().Client client := g.GetInstance().Client
host := g.GetInstance().Host host := g.GetInstance().Host
type TemplateArgs struct { p := &params.TplCreate{ vmID, name, access }
VMID uuid.UUID resp, err := client.R().SetBody(p).Post(host+"/templates/vm")
Name string
Access string
}
templateArgs := &TemplateArgs { vmID, name, access }
resp, err := client.R().SetBody(templateArgs).Post(host+"/templates/vm")
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -3,6 +3,7 @@ package cmdTemplate ...@@ -3,6 +3,7 @@ package cmdTemplate
import ( import (
"errors" "errors"
"strings" "strings"
"nexus-common/params"
u "nexus-client/utils" u "nexus-client/utils"
g "nexus-client/globals" g "nexus-client/globals"
) )
...@@ -11,11 +12,6 @@ type Edit struct { ...@@ -11,11 +12,6 @@ type Edit struct {
Name string Name string
} }
type templateEditParameters struct {
Name string
Access string
}
func (cmd *Edit)GetName() string { func (cmd *Edit)GetName() string {
return cmd.Name return cmd.Name
} }
...@@ -49,13 +45,13 @@ func (cmd *Edit)Run(args []string) int { ...@@ -49,13 +45,13 @@ func (cmd *Edit)Run(args []string) int {
return 1 return 1
} }
templateParams, err := cmd.parseArgs(args[1:]) p, err := cmd.parseArgs(args[1:])
if err != nil { if err != nil {
u.PrintlnErr(err.Error()) u.PrintlnErr(err.Error())
return 1 return 1
} }
if templateParams == nil { if p == nil {
cmd.PrintUsage() cmd.PrintUsage()
return 1 return 1
} }
...@@ -63,7 +59,7 @@ func (cmd *Edit)Run(args []string) int { ...@@ -63,7 +59,7 @@ func (cmd *Edit)Run(args []string) int {
tplID := args[0] tplID := args[0]
statusCode := 0 statusCode := 0
resp, err := client.R().SetBody(templateParams).Put(host+"/templates/"+tplID) resp, err := client.R().SetBody(p).Put(host+"/templates/"+tplID)
if err != nil { if err != nil {
u.PrintlnErr("Failed editing template \""+tplID+"\": "+err.Error()) u.PrintlnErr("Failed editing template \""+tplID+"\": "+err.Error())
statusCode = 1 statusCode = 1
...@@ -79,8 +75,8 @@ func (cmd *Edit)Run(args []string) int { ...@@ -79,8 +75,8 @@ func (cmd *Edit)Run(args []string) int {
return statusCode return statusCode
} }
func (cmd *Edit)parseArgs(args []string) (*templateEditParameters, error) { func (cmd *Edit)parseArgs(args []string) (*params.TplCreate, error) {
templateParams := &templateEditParameters {} p := &params.TplCreate{}
atLeastOneArg := false atLeastOneArg := false
getStringVal := func(s string, prefix string) string { getStringVal := func(s string, prefix string) string {
...@@ -94,13 +90,13 @@ func (cmd *Edit)parseArgs(args []string) (*templateEditParameters, error) { ...@@ -94,13 +90,13 @@ func (cmd *Edit)parseArgs(args []string) (*templateEditParameters, error) {
for _, arg := range args { for _, arg := range args {
s := getStringVal(arg, "name=") s := getStringVal(arg, "name=")
if s != "" { if s != "" {
templateParams.Name = s p.Name = s
atLeastOneArg = true atLeastOneArg = true
continue continue
} }
s = getStringVal(arg, "access=") s = getStringVal(arg, "access=")
if s != "" { if s != "" {
templateParams.Access = s p.Access = s
atLeastOneArg = true atLeastOneArg = true
continue continue
} }
...@@ -108,7 +104,7 @@ func (cmd *Edit)parseArgs(args []string) (*templateEditParameters, error) { ...@@ -108,7 +104,7 @@ func (cmd *Edit)parseArgs(args []string) (*templateEditParameters, error) {
} }
if atLeastOneArg { if atLeastOneArg {
return templateParams, nil return p, nil
} else { } else {
return nil, nil return nil, nil
} }
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"errors" "errors"
"strings" "strings"
"encoding/csv" "encoding/csv"
"nexus-common/params"
u "nexus-client/utils" u "nexus-client/utils"
g "nexus-client/globals" g "nexus-client/globals"
) )
...@@ -38,10 +39,6 @@ func (cmd *SetCaps)PrintUsage() { ...@@ -38,10 +39,6 @@ func (cmd *SetCaps)PrintUsage() {
u.PrintlnErr(usage) u.PrintlnErr(usage)
} }
type capsArgs struct {
Caps map[string]int `json:"caps" validate:"required"`
}
func (cmd *SetCaps)Run(args []string) int { func (cmd *SetCaps)Run(args []string) int {
argc := len(args) argc := len(args)
if argc < 1 { if argc < 1 {
...@@ -88,7 +85,7 @@ func (cmd *SetCaps)Run(args []string) int { ...@@ -88,7 +85,7 @@ func (cmd *SetCaps)Run(args []string) int {
continue continue
} }
userCaps := &capsArgs { make(map[string]int) } userCaps := &params.UserSetCaps{ make(map[string]int) }
capsStr := strings.TrimSpace(columns[1]) capsStr := strings.TrimSpace(columns[1])
if len(capsStr) > 0 { if len(capsStr) > 0 {
...@@ -114,10 +111,9 @@ func (cmd *SetCaps)Run(args []string) int { ...@@ -114,10 +111,9 @@ func (cmd *SetCaps)Run(args []string) int {
} }
} else { } else {
// Argument is an email address // Argument is an email address
email := args[0] email := args[0]
userCaps := &capsArgs { make(map[string]int) } userCaps := &params.UserSetCaps{ make(map[string]int) }
for _, cap := range args[1:] { for _, cap := range args[1:] {
userCaps.Caps[cap] = 1 userCaps.Caps[cap] = 1
} }
...@@ -133,7 +129,7 @@ func (cmd *SetCaps)Run(args []string) int { ...@@ -133,7 +129,7 @@ func (cmd *SetCaps)Run(args []string) int {
return statusCode return statusCode
} }
func (cmd *SetCaps)runRequest(email string, userCaps *capsArgs) error { func (cmd *SetCaps)runRequest(email string, userCaps *params.UserSetCaps) error {
client := g.GetInstance().Client client := g.GetInstance().Client
host := g.GetInstance().Host host := g.GetInstance().Host
......
package cmdUser package cmdUser
import ( import (
"nexus-common/params"
u "nexus-client/utils" u "nexus-client/utils"
g "nexus-client/globals" g "nexus-client/globals"
) )
...@@ -64,13 +65,8 @@ func (cmd *UpdatePwd)Run(args []string) int { ...@@ -64,13 +65,8 @@ func (cmd *UpdatePwd)Run(args []string) int {
} }
pwdStr := string(newPwd) pwdStr := string(newPwd)
p := &params.UserSetPwd{pwdStr}
type PwdArgs struct { resp, err := client.R().SetBody(p).Put(host+"/users/pwd")
Pwd string
}
pwdArgs := &PwdArgs{pwdStr}
resp, err := client.R().SetBody(pwdArgs).Put(host+"/users/pwd")
if err != nil { if err != nil {
u.PrintlnErr("Error: "+err.Error()) u.PrintlnErr("Error: "+err.Error())
return 1 return 1
......
...@@ -4,6 +4,8 @@ go 1.18 ...@@ -4,6 +4,8 @@ go 1.18
replace nexus-common/caps => ../../common/caps replace nexus-common/caps => ../../common/caps
replace nexus-common/params => ../../common/params
replace nexus-common/vm => ../../common/vm replace nexus-common/vm => ../../common/vm
replace nexus-common/template => ../../common/template replace nexus-common/template => ../../common/template
...@@ -58,6 +60,7 @@ require ( ...@@ -58,6 +60,7 @@ require (
nexus-client/exec v0.0.0-00010101000000-000000000000 // indirect nexus-client/exec v0.0.0-00010101000000-000000000000 // indirect
nexus-client/version v0.0.0-00010101000000-000000000000 // indirect nexus-client/version v0.0.0-00010101000000-000000000000 // indirect
nexus-common/caps v0.0.0-00010101000000-000000000000 // indirect nexus-common/caps v0.0.0-00010101000000-000000000000 // indirect
nexus-common/params v0.0.0-00010101000000-000000000000 // indirect
nexus-common/template v0.0.0-00010101000000-000000000000 // indirect nexus-common/template v0.0.0-00010101000000-000000000000 // indirect
nexus-common/utils v0.0.0-00010101000000-000000000000 // indirect nexus-common/utils v0.0.0-00010101000000-000000000000 // indirect
nexus-common/vm v0.0.0-00010101000000-000000000000 // indirect nexus-common/vm v0.0.0-00010101000000-000000000000 // indirect
......
...@@ -6,11 +6,13 @@ nexus_cli="./nexus-cli" ...@@ -6,11 +6,13 @@ nexus_cli="./nexus-cli"
partial_name="exam 328a2d0eff08" partial_name="exam 328a2d0eff08"
full_name="live $partial_name" full_name="live $partial_name"
creds_file=creds.pdf creds_pdf_file=creds.pdf
creds_csv_file=creds.csv
del_gen_files () { del_gen_files () {
find . -name "$full_name *.tar.gz" -delete find . -name "$full_name *.tar.gz" -delete
rm -f $creds_file rm -f $creds_pdf_file
rm -f $creds_csv_file
} }
cleanup () { cleanup () {
...@@ -90,9 +92,14 @@ check "vmstart VMs" ...@@ -90,9 +92,14 @@ check "vmstart VMs"
sleep 3 sleep 3
OK OK
echo "Generate credentials pdf..." echo "Generate credentials to PDF..."
$nexus_cli vmcred2pdf "$full_name" $creds_file $nexus_cli vmcreds2pdf "$full_name" $creds_pdf_file
check "vmcred2pdf" check "vmcreds2pdf"
OK
echo "Generate credentials to CSV..."
$nexus_cli vmcreds2csv "$full_name" $creds_csv_file
check "vmcreds2csv"
OK OK
echo "Kill students VMs..." echo "Kill students VMs..."
...@@ -106,6 +113,18 @@ $nexus_cli vmexportdir "$full_name" /home ...@@ -106,6 +113,18 @@ $nexus_cli vmexportdir "$full_name" /home
check "vmexportdir" check "vmexportdir"
OK OK
echo "Re-start students VMs with previous credentials..."
$nexus_cli vmstartwithcreds $creds_csv_file
check "vmstartwithcreds VMs"
sleep 10
OK
echo "Kill students VMs..."
$nexus_cli vmkill "$full_name"
sleep 3
check "vmkill VMs"
OK
echo "Delete students VMs..." echo "Delete students VMs..."
$nexus_cli vmdel "$partial_name" $nexus_cli vmdel "$partial_name"
check "vmdel VMs" check "vmdel VMs"
......
...@@ -4,6 +4,8 @@ go 1.18 ...@@ -4,6 +4,8 @@ go 1.18
replace nexus-common/caps => ../../common/caps replace nexus-common/caps => ../../common/caps
replace nexus-common/params => ../../common/params
replace nexus-common/vm => ../../common/vm replace nexus-common/vm => ../../common/vm
replace nexus-common/template => ../../common/template replace nexus-common/template => ../../common/template
...@@ -60,6 +62,7 @@ require ( ...@@ -60,6 +62,7 @@ require (
nexus-client/utils v0.0.0-00010101000000-000000000000 // indirect nexus-client/utils v0.0.0-00010101000000-000000000000 // indirect
nexus-client/version v0.0.0-00010101000000-000000000000 // indirect nexus-client/version v0.0.0-00010101000000-000000000000 // indirect
nexus-common/caps v0.0.0-00010101000000-000000000000 // indirect nexus-common/caps v0.0.0-00010101000000-000000000000 // indirect
nexus-common/params v0.0.0-00010101000000-000000000000 // indirect
nexus-common/template v0.0.0-00010101000000-000000000000 // indirect nexus-common/template v0.0.0-00010101000000-000000000000 // indirect
nexus-common/utils v0.0.0-00010101000000-000000000000 // indirect nexus-common/utils v0.0.0-00010101000000-000000000000 // indirect
nexus-common/vm v0.0.0-00010101000000-000000000000 // indirect nexus-common/vm v0.0.0-00010101000000-000000000000 // indirect
......
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
const ( const (
major = 1 major = 1
minor = 8 minor = 8
bugfix = 0 bugfix = 1
) )
type Version struct { type Version struct {
......
module nexus-common/params
go 1.18
package params
import (
"github.com/google/uuid"
)
type TplCreate struct {
VMID uuid.UUID `json:"vmID" validate:"required"`
Name string `json:"name" validate:"required,min=2"`
Access string `json:"access" validate:"required,min=4"`
}
type TplEdit struct {
Name string `json:"name"`
Access string `json:"access"`
}
package params
import (
"nexus-common/caps"
)
type UserSetCaps struct {
Caps caps.Capabilities
}
type UserSetPwd struct {
Pwd string `json:"pwd" validate:"required,min=8"`
}
package params
import (
"nexus-common/vm"
"nexus-common/caps"
"github.com/google/uuid"
)
type VMCreate struct {
Name string `json:"name" validate:"required,min=4,max=256"`
Cpus int `json:"cpus" validate:"required,gte=1,lte=16"`
Ram int `json:"ram" validate:"required,gte=512,lte=32768"`
Nic vm.NicType `json:"nic" validate:"required`
UsbDevs []string `json:"usbDevs" validate:"required`
TemplateID uuid.UUID `json:"templateID" validate:"required"`
}
type VMStartWithCreds struct {
Port int `json:"port" validate:"required,gte=1100,lte=65535"`
Pwd string `json:"pwd" validate:"required,min=8,max=64"`
}
type VMEdit struct {
Name string `json:"name" validate:"required,min=4,max=256"`
Cpus int `json:"cpus" validate:"required,gte=1,lte=16"`
Ram int `json:"ram" validate:"required,gte=512,lte=32768"`
Nic vm.NicType `json:"nic" validate:"required`
UsbDevs []string `json:"usbDevs" validate:"required`
}
type VMAddAccess struct {
Access caps.Capabilities `json:"access" validate:"required"`
}
type VMExportDir struct {
Dir string `json:"dir"`
}
...@@ -6,6 +6,8 @@ replace nexus-common/template => ../common/template ...@@ -6,6 +6,8 @@ replace nexus-common/template => ../common/template
replace nexus-common/vm => ../common/vm replace nexus-common/vm => ../common/vm
replace nexus-common/params => ../common/params
replace nexus-common/caps => ../common/caps replace nexus-common/caps => ../common/caps
replace nexus-server/consts => ./consts replace nexus-server/consts => ./consts
...@@ -62,6 +64,7 @@ require ( ...@@ -62,6 +64,7 @@ require (
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
nexus-common/caps v0.0.0-00010101000000-000000000000 // indirect nexus-common/caps v0.0.0-00010101000000-000000000000 // indirect
nexus-common/params v0.0.0-00010101000000-000000000000 // indirect
nexus-common/template v0.0.0-00010101000000-000000000000 // indirect nexus-common/template v0.0.0-00010101000000-000000000000 // indirect
nexus-common/vm v0.0.0-00010101000000-000000000000 // indirect nexus-common/vm v0.0.0-00010101000000-000000000000 // indirect
nexus-server/exec v0.0.0-00010101000000-000000000000 // indirect nexus-server/exec v0.0.0-00010101000000-000000000000 // indirect
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"path/filepath" "path/filepath"
"nexus-common/caps" "nexus-common/caps"
"nexus-common/params"
"nexus-server/vms" "nexus-server/vms"
"nexus-server/paths" "nexus-server/paths"
"nexus-server/users" "nexus-server/users"
...@@ -66,13 +67,8 @@ func (r *RouterTemplates)CreateTemplateFromVM(c echo.Context) error { ...@@ -66,13 +67,8 @@ func (r *RouterTemplates)CreateTemplateFromVM(c echo.Context) error {
return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps)
} }
// Deserializes and validates the client's parameters. // Deserializes and validates client's parameters.
type Parameters struct { p := new(params.TplCreate)
VMID uuid.UUID `json:"vmID" validate:"required"`
Name string `json:"name" validate:"required,min=2"`
Access string `json:"access" validate:"required,min=4"`
}
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())
} }
...@@ -243,14 +239,10 @@ func (r *RouterTemplates)EditTemplateByID(c echo.Context) error { ...@@ -243,14 +239,10 @@ func (r *RouterTemplates)EditTemplateByID(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, err.Error()) return echo.NewHTTPError(http.StatusBadRequest, err.Error())
} }
// Deserializes and validates the client's parameters. // Deserializes and validates client's parameters.
// Given these parameters are optional, we can't use a validator on them. // Given these parameters are optional, we can't use a validator on them.
// Validation is performed in templates.EditTemplate() instead. // Validation is performed in templates.EditTemplate() instead.
type Parameters struct { p := new(params.TplEdit)
Name string `json:"name"`
Access string `json:"access"`
}
p := new(Parameters)
if user.HasCapability(caps.CAP_TPL_EDIT_ANY) { if user.HasCapability(caps.CAP_TPL_EDIT_ANY) {
if err := decodeJson(c, &p); err != nil { if err := decodeJson(c, &p); err != nil {
......
...@@ -3,6 +3,7 @@ package router ...@@ -3,6 +3,7 @@ package router
import ( import (
"net/http" "net/http"
"nexus-common/caps" "nexus-common/caps"
"nexus-common/params"
"nexus-server/users" "nexus-server/users"
"nexus-server/utils" "nexus-server/utils"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
...@@ -128,18 +129,14 @@ func (r *RouterUsers) SetUserCaps(c echo.Context) error { ...@@ -128,18 +129,14 @@ func (r *RouterUsers) SetUserCaps(c echo.Context) error {
return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps)
} }
// Deserializes and validates the client's parameters. // Deserializes and validates client's parameters.
type Parameters struct { p := new(params.UserSetCaps)
Caps caps.Capabilities if err := decodeJson(c, &p); err != nil {
}
params := &Parameters{make(caps.Capabilities)}
if err := decodeJson(c, &params); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error()) return echo.NewHTTPError(http.StatusBadRequest, err.Error())
} }
// Checks capabilities are valid. // Checks capabilities are valid.
if err := caps.ValidateUserCaps(params.Caps); err != nil { if err := caps.ValidateUserCaps(p.Caps); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error()) return echo.NewHTTPError(http.StatusBadRequest, err.Error())
} }
...@@ -150,7 +147,7 @@ func (r *RouterUsers) SetUserCaps(c echo.Context) error { ...@@ -150,7 +147,7 @@ func (r *RouterUsers) SetUserCaps(c echo.Context) error {
} }
// Updates the user's capabilities. // Updates the user's capabilities.
userToUpdate.Caps = params.Caps userToUpdate.Caps = p.Caps
// Updates user and saves the new user file. // Updates user and saves the new user file.
if err = r.users.UpdateUser(&userToUpdate); err != nil { if err = r.users.UpdateUser(&userToUpdate); err != nil {
...@@ -163,19 +160,14 @@ func (r *RouterUsers) SetUserCaps(c echo.Context) error { ...@@ -163,19 +160,14 @@ func (r *RouterUsers) SetUserCaps(c echo.Context) error {
// Update the logged user's password. // Update the logged user's password.
// curl --cacert ca.pem -X PUT http://localhost:1077/users/pwd -H 'Content-Type: application/json' -d '{"pwd": "new_password"}' -H "Authorization: Bearer <AccessToken>" // curl --cacert ca.pem -X PUT http://localhost:1077/users/pwd -H 'Content-Type: application/json' -d '{"pwd": "new_password"}' -H "Authorization: Bearer <AccessToken>"
func (r *RouterUsers) SetUserPwd(c echo.Context) error { func (r *RouterUsers) SetUserPwd(c echo.Context) error {
// Deserializes the client's parameters. // Deserializes and validates client's parameters.
type Parameters struct { p := new(params.UserSetPwd)
Pwd string `json:"pwd" validate:"required,min=8"` if err := decodeJson(c, &p); err != nil {
}
params := &Parameters{}
// Deserializes the JSON body and checks its validity.
if err := decodeJson(c, &params); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error()) return echo.NewHTTPError(http.StatusBadRequest, err.Error())
} }
// Checks the new password is valid. // Checks the new password is valid.
if err := validator.New().Struct(params); err != nil { if err := validator.New().Struct(p); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error()) return echo.NewHTTPError(http.StatusBadRequest, err.Error())
} }
...@@ -186,7 +178,7 @@ func (r *RouterUsers) SetUserPwd(c echo.Context) error { ...@@ -186,7 +178,7 @@ func (r *RouterUsers) SetUserPwd(c echo.Context) error {
} }
// Hashes and udpates the user's password. // Hashes and udpates the user's password.
user.Pwd = utils.HashPassword(params.Pwd) user.Pwd = utils.HashPassword(p.Pwd)
// Updates user and saves the new user file. // Updates user and saves the new user file.
if err = r.users.UpdateUser(user); err != nil { if err = r.users.UpdateUser(user); err != nil {
......
...@@ -6,7 +6,7 @@ import ( ...@@ -6,7 +6,7 @@ import (
"net/http" "net/http"
"path/filepath" "path/filepath"
"nexus-common/caps" "nexus-common/caps"
vmc "nexus-common/vm" "nexus-common/params"
"nexus-server/vms" "nexus-server/vms"
"nexus-server/users" "nexus-server/users"
"nexus-server/paths" "nexus-server/paths"
...@@ -179,15 +179,7 @@ func (r *RouterVMs)CreateVM(c echo.Context) error { ...@@ -179,15 +179,7 @@ func (r *RouterVMs)CreateVM(c echo.Context) error {
} }
// Deserializes and validates client's parameters. // Deserializes and validates client's parameters.
type Parameters struct { p := new(params.VMCreate)
Name string `json:"name" validate:"required,min=4,max=256"`
Cpus int `json:"cpus" validate:"required,gte=1,lte=16"`
Ram int `json:"ram" validate:"required,gte=512,lte=32768"`
Nic vmc.NicType `json:"nic" validate:"required`
UsbDevs []string `json:"usbDevs" validate:"required`
TemplateID uuid.UUID `json:"templateID" validate:"required"`
}
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())
} }
...@@ -243,16 +235,12 @@ func (r *RouterVMs)StartVMWithCreds(c echo.Context) error { ...@@ -243,16 +235,12 @@ func (r *RouterVMs)StartVMWithCreds(c echo.Context) error {
return r.performVMAction(c, caps.CAP_VM_START_ANY, caps.CAP_VM_START, func(c echo.Context, vm *vms.VM) error { return r.performVMAction(c, caps.CAP_VM_START_ANY, caps.CAP_VM_START, func(c echo.Context, vm *vms.VM) error {
// Deserializes and validates client's parameters. // Deserializes and validates client's parameters.
type Parameters struct { p := new(params.VMStartWithCreds)
Port int `json:"port" validate:"required,gte=1100,lte=65535"`
Pwd string `json:"pwd" validate:"required,min=8,max=64"`
}
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())
} }
if err := r.vms.StartVMWithCreds(vm.GetID(), p.Port, p.Pwd); err != nil { if err := r.vms.StartVMWithCreds(vm.GetID(), p.Port, true, p.Pwd); 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"), " ")
...@@ -308,17 +296,10 @@ func (r *RouterVMs)RebootVM(c echo.Context) error { ...@@ -308,17 +296,10 @@ func (r *RouterVMs)RebootVM(c echo.Context) error {
// curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59 -H 'Content-Type: application/json' -d '{"name":"Edited VM","cpus":1,"ram":2048,"nic":"user","usbDevs":["1307:0165","1234:abcd"]}' -H "Authorization: Bearer <AccessToken>" // curl --cacert ca.pem -X PUT https://localhost:1077/vms/e41f3556-ca24-4658-bd79-8c85bd6bff59 -H 'Content-Type: application/json' -d '{"name":"Edited VM","cpus":1,"ram":2048,"nic":"user","usbDevs":["1307:0165","1234:abcd"]}' -H "Authorization: Bearer <AccessToken>"
func (r *RouterVMs)EditVMByID(c echo.Context) error { func (r *RouterVMs)EditVMByID(c echo.Context) error {
return r.performVMAction(c, caps.CAP_VM_EDIT_ANY, caps.CAP_VM_EDIT, func(c echo.Context, vm *vms.VM) error { return r.performVMAction(c, caps.CAP_VM_EDIT_ANY, caps.CAP_VM_EDIT, func(c echo.Context, vm *vms.VM) error {
// Deserializes and validates the client's parameters. // Deserializes and validates client's parameters.
// Given these parameters are optional, we can't use a validator on them. // Given these parameters are optional, we can't use a validator on them.
// Validation is performed in vm.EditVM() instead. // Validation is performed in vm.EditVM() instead.
type Parameters struct { p := new(params.VMEdit)
Name string `json:"name" validate:"required,min=4,max=256"`
Cpus int `json:"cpus" validate:"required,gte=1,lte=16"`
Ram int `json:"ram" validate:"required,gte=512,lte=32768"`
Nic vmc.NicType `json:"nic" validate:"required`
UsbDevs []string `json:"usbDevs" validate:"required`
}
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())
} }
...@@ -368,19 +349,16 @@ func (r *RouterVMs)SetVMAccessForUser(c echo.Context) error { ...@@ -368,19 +349,16 @@ func (r *RouterVMs)SetVMAccessForUser(c echo.Context) error {
} }
} }
// Deserializes and validates the client's parameters. // Deserializes and validates client's parameters.
type Parameters struct { p := new(params.VMAddAccess)
Access caps.Capabilities `json:"access" validate:"required"` if err := decodeJson(c, &p); err != nil {
}
params := new(Parameters)
if err := decodeJson(c, &params); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error()) return echo.NewHTTPError(http.StatusBadRequest, err.Error())
} }
if err := validator.New().Struct(params); err != nil { if err := validator.New().Struct(p); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error()) return echo.NewHTTPError(http.StatusBadRequest, err.Error())
} }
if err = r.vms.SetVMAccess(vmID, user.Email, email, params.Access); err != nil { if err = r.vms.SetVMAccess(vmID, user.Email, email, p.Access); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error()) return echo.NewHTTPError(http.StatusBadRequest, err.Error())
} }
...@@ -438,10 +416,7 @@ func (r *RouterVMs)DeleteVMAccessForUser(c echo.Context) error { ...@@ -438,10 +416,7 @@ func (r *RouterVMs)DeleteVMAccessForUser(c echo.Context) error {
func (r *RouterVMs)ExportVMDir(c echo.Context) error { func (r *RouterVMs)ExportVMDir(c echo.Context) error {
return r.performVMAction(c, caps.CAP_VM_READFS_ANY, caps.CAP_VM_READFS, func(c echo.Context, vm *vms.VM) error { return r.performVMAction(c, caps.CAP_VM_READFS_ANY, caps.CAP_VM_READFS, func(c echo.Context, vm *vms.VM) error {
// Deserializes and validates the client's parameter. // Deserializes and validates the client's parameter.
type Parameters struct { p := new(params.VMExportDir)
Dir string `json:"dir"`
}
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())
} }
......
...@@ -7,7 +7,7 @@ import ( ...@@ -7,7 +7,7 @@ import (
const ( const (
major = 1 major = 1
minor = 8 minor = 8
bugfix = 0 bugfix = 1
) )
type Version struct { type Version struct {
......
...@@ -90,6 +90,7 @@ func InitVMs() error { ...@@ -90,6 +90,7 @@ func InitVMs() error {
// Returns the list of serialized VMs for which VMKeeperFn returns true. // Returns the list of serialized VMs for which VMKeeperFn returns true.
func (vms *VMs)GetNetworkSerializedVMs(keepFn VMKeeperFn) []vm.VMNetworkSerialized { func (vms *VMs)GetNetworkSerializedVMs(keepFn VMKeeperFn) []vm.VMNetworkSerialized {
vms.rwlock.RLock() vms.rwlock.RLock()
list := []vm.VMNetworkSerialized{} list := []vm.VMNetworkSerialized{}
for _, vm := range vms.m { for _, vm := range vms.m {
vm.mutex.Lock() vm.mutex.Lock()
...@@ -173,16 +174,12 @@ func (vms *VMs)AddVM(vm *VM) error { ...@@ -173,16 +174,12 @@ func (vms *VMs)AddVM(vm *VM) error {
} }
// Starts a VM by its ID, using the specified port and password. // Starts a VM by its ID, using the specified port and password.
func (vms *VMs)StartVMWithCreds(vmID uuid.UUID, port int, pwd string) error { // If checkPort is true, a check is performed on the specified port and if it is already
// in use, the function fails and returns a corresponding error.
func (vms *VMs)StartVMWithCreds(vmID uuid.UUID, port int, checkPort bool, 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
...@@ -195,8 +192,21 @@ func (vms *VMs)StartVMWithCreds(vmID uuid.UUID, port int, pwd string) error { ...@@ -195,8 +192,21 @@ func (vms *VMs)StartVMWithCreds(vmID uuid.UUID, port int, pwd string) error {
return errors.New("Failed starting VM: VM already running") return errors.New("Failed starting VM: VM already running")
} }
if checkPort {
if vms.usedPorts[port] {
return errors.New("Failed starting VM: port already in use")
} else if !utils.IsPortAvailable(port) {
return errors.New("Failed starting VM: port not available")
} else {
vms.usedPorts[port] = true
}
} else {
vms.usedPorts[port] = true
}
totalRAM, availRAM, err := utils.GetRAM() totalRAM, availRAM, err := utils.GetRAM()
if err != nil { if err != nil {
vms.usedPorts[port] = false
return errors.New("Failed starting VM: failed obtaining memory info: "+err.Error()) return errors.New("Failed starting VM: failed obtaining memory info: "+err.Error())
} }
...@@ -209,42 +219,51 @@ func (vms *VMs)StartVMWithCreds(vmID uuid.UUID, port int, pwd string) error { ...@@ -209,42 +219,51 @@ func (vms *VMs)StartVMWithCreds(vmID uuid.UUID, port int, pwd string) error {
// otherwise, refuses to run it in order to avoid RAM saturation. // otherwise, refuses to run it in order to avoid RAM saturation.
if availRAM - vms.usedRAM <= int(math.Round(float64(totalRAM)*(1.-c.RamUsageLimit))) { if availRAM - vms.usedRAM <= int(math.Round(float64(totalRAM)*(1.-c.RamUsageLimit))) {
vms.usedRAM -= estimatedVmRAM vms.usedRAM -= estimatedVmRAM
vms.usedPorts[port] = false
return errors.New("Failed starting VM: insufficient free RAM") return errors.New("Failed starting VM: insufficient free RAM")
} }
// This callback is called once the VM started with vm.start terminates. // This callback is called once the VM started with vm.start terminates.
endofExecFn := func (vm *VM) { endofExecFn := func (vm *VM) {
vms.rwlock.Lock()
vms.usedPorts[port] = false
vms.usedRAM -= estimatedVmRAM
vms.rwlock.Unlock()
vm.mutex.Lock() vm.mutex.Lock()
vm.removeSecretFile() vm.removeSecretFile()
vm.resetStates() vm.resetStates()
vm.mutex.Unlock() vm.mutex.Unlock()
vms.rwlock.Lock()
vms.usedPorts[vm.Run.Port] = false
vms.usedRAM -= estimatedVmRAM
vms.rwlock.Unlock()
} }
if err = vm.start(port, pwd, endofExecFn); err != nil { if err = vm.start(port, pwd, endofExecFn); err != nil {
vms.usedPorts[port] = false
return err return err
} }
return nil return nil
} }
// Starts a VM by its ID using randomly generated port number and password. // Allocates and returns a free port randomly chosen between VMSpiceMinPort and
// Returns the port on which the VM is running and the access password. // VMSpiceMaxPort (inclusive).
func (vms *VMs)StartVM(vmID uuid.UUID) (int, string, error) { // Beware: this function updates the vms map.
func (vms *VMs)allocateFreeRandomPort() int {
vms.rwlock.Lock()
defer vms.rwlock.Unlock()
// Locates a free port randomly chosen between VMSpiceMinPort and VMSpiceMaxPort (inclusive).
var port int
for { for {
port = utils.Rand(c.VMSpiceMinPort, c.VMSpiceMaxPort) port := utils.Rand(c.VMSpiceMinPort, c.VMSpiceMaxPort)
if !vms.usedPorts[port] { if !vms.usedPorts[port] {
if utils.IsPortAvailable(port) { if utils.IsPortAvailable(port) {
vms.usedPorts[port] = true vms.usedPorts[port] = true
break return port
} }
} }
} }
}
// Starts a VM by its ID using randomly generated port number and password.
// Returns the port on which the VM is running and the access password.
func (vms *VMs)StartVM(vmID uuid.UUID) (int, string, error) {
port := vms.allocateFreeRandomPort()
// Randomly generates a 8 characters long password with 4 digits, 0 symbols, // Randomly generates a 8 characters long password with 4 digits, 0 symbols,
// allowing upper and lower case letters, disallowing repeat characters. // allowing upper and lower case letters, disallowing repeat characters.
...@@ -254,7 +273,7 @@ func (vms *VMs)StartVM(vmID uuid.UUID) (int, string, error) { ...@@ -254,7 +273,7 @@ func (vms *VMs)StartVM(vmID uuid.UUID) (int, string, error) {
return -1, "", errors.New("Failed starting VM: password generation error: "+err.Error()) return -1, "", errors.New("Failed starting VM: password generation error: "+err.Error())
} }
if err = vms.StartVMWithCreds(vmID, port, pwd); err != nil { if err = vms.StartVMWithCreds(vmID, port, false, pwd); err != nil {
return -1, "", err return -1, "", err
} }
return port, pwd, nil return port, pwd, nil
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment