diff --git a/src/client/cmdTemplate/templateCreate.go b/src/client/cmdTemplate/templateCreate.go index 5266b0dd2d204f79906b614313a1ae66f3cbbacc..74b681a3421e96729f0fb3e3ef9aef5801e17bf3 100644 --- a/src/client/cmdTemplate/templateCreate.go +++ b/src/client/cmdTemplate/templateCreate.go @@ -1,6 +1,7 @@ package cmdTemplate import ( + "nexus-common/params" u "nexus-client/utils" g "nexus-client/globals" "github.com/google/uuid" @@ -65,15 +66,8 @@ func (cmd *Create)makeRequestForID(vmID uuid.UUID, name, access string) (*resty. client := g.GetInstance().Client host := g.GetInstance().Host - type TemplateArgs struct { - VMID uuid.UUID - Name string - Access string - } - - templateArgs := &TemplateArgs { vmID, name, access } - - resp, err := client.R().SetBody(templateArgs).Post(host+"/templates/vm") + p := ¶ms.TplCreate{ vmID, name, access } + resp, err := client.R().SetBody(p).Post(host+"/templates/vm") if err != nil { return nil, err } diff --git a/src/client/cmdTemplate/templateEdit.go b/src/client/cmdTemplate/templateEdit.go index ddb58b7ef74142c9cbd6b63f144a6e6864c25827..da0fb4c7d98f194706c17d43536e60fc92acee29 100644 --- a/src/client/cmdTemplate/templateEdit.go +++ b/src/client/cmdTemplate/templateEdit.go @@ -3,6 +3,7 @@ package cmdTemplate import ( "errors" "strings" + "nexus-common/params" u "nexus-client/utils" g "nexus-client/globals" ) @@ -11,11 +12,6 @@ type Edit struct { Name string } -type templateEditParameters struct { - Name string - Access string -} - func (cmd *Edit)GetName() string { return cmd.Name } @@ -49,13 +45,13 @@ func (cmd *Edit)Run(args []string) int { return 1 } - templateParams, err := cmd.parseArgs(args[1:]) + p, err := cmd.parseArgs(args[1:]) if err != nil { u.PrintlnErr(err.Error()) return 1 } - if templateParams == nil { + if p == nil { cmd.PrintUsage() return 1 } @@ -63,7 +59,7 @@ func (cmd *Edit)Run(args []string) int { tplID := args[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 { u.PrintlnErr("Failed editing template \""+tplID+"\": "+err.Error()) statusCode = 1 @@ -79,8 +75,8 @@ func (cmd *Edit)Run(args []string) int { return statusCode } -func (cmd *Edit)parseArgs(args []string) (*templateEditParameters, error) { - templateParams := &templateEditParameters {} +func (cmd *Edit)parseArgs(args []string) (*params.TplCreate, error) { + p := ¶ms.TplCreate{} atLeastOneArg := false getStringVal := func(s string, prefix string) string { @@ -94,13 +90,13 @@ func (cmd *Edit)parseArgs(args []string) (*templateEditParameters, error) { for _, arg := range args { s := getStringVal(arg, "name=") if s != "" { - templateParams.Name = s + p.Name = s atLeastOneArg = true continue } s = getStringVal(arg, "access=") if s != "" { - templateParams.Access = s + p.Access = s atLeastOneArg = true continue } @@ -108,7 +104,7 @@ func (cmd *Edit)parseArgs(args []string) (*templateEditParameters, error) { } if atLeastOneArg { - return templateParams, nil + return p, nil } else { return nil, nil } diff --git a/src/client/cmdUser/userSetCaps.go b/src/client/cmdUser/userSetCaps.go index b27042097f52cc477134ddaab9a741906b965acf..0bae3d1f5ae1922795842b8ab681cc5f57129e65 100644 --- a/src/client/cmdUser/userSetCaps.go +++ b/src/client/cmdUser/userSetCaps.go @@ -6,6 +6,7 @@ import ( "errors" "strings" "encoding/csv" + "nexus-common/params" u "nexus-client/utils" g "nexus-client/globals" ) @@ -38,10 +39,6 @@ func (cmd *SetCaps)PrintUsage() { u.PrintlnErr(usage) } -type capsArgs struct { - Caps map[string]int `json:"caps" validate:"required"` -} - func (cmd *SetCaps)Run(args []string) int { argc := len(args) if argc < 1 { @@ -88,7 +85,7 @@ func (cmd *SetCaps)Run(args []string) int { continue } - userCaps := &capsArgs { make(map[string]int) } + userCaps := ¶ms.UserSetCaps{ make(map[string]int) } capsStr := strings.TrimSpace(columns[1]) if len(capsStr) > 0 { @@ -114,10 +111,9 @@ func (cmd *SetCaps)Run(args []string) int { } } else { // Argument is an email address - email := args[0] - userCaps := &capsArgs { make(map[string]int) } + userCaps := ¶ms.UserSetCaps{ make(map[string]int) } for _, cap := range args[1:] { userCaps.Caps[cap] = 1 } @@ -133,7 +129,7 @@ func (cmd *SetCaps)Run(args []string) int { 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 host := g.GetInstance().Host diff --git a/src/client/cmdUser/userUpdatePwd.go b/src/client/cmdUser/userUpdatePwd.go index dc0695c50039ebea5623044bc1e3bbdd16bfcf76..7d045e33a7a673572251d0248e9ae85e1880781d 100644 --- a/src/client/cmdUser/userUpdatePwd.go +++ b/src/client/cmdUser/userUpdatePwd.go @@ -1,6 +1,7 @@ package cmdUser import ( + "nexus-common/params" u "nexus-client/utils" g "nexus-client/globals" ) @@ -64,13 +65,8 @@ func (cmd *UpdatePwd)Run(args []string) int { } pwdStr := string(newPwd) - - type PwdArgs struct { - Pwd string - } - - pwdArgs := &PwdArgs{pwdStr} - resp, err := client.R().SetBody(pwdArgs).Put(host+"/users/pwd") + p := ¶ms.UserSetPwd{pwdStr} + resp, err := client.R().SetBody(p).Put(host+"/users/pwd") if err != nil { u.PrintlnErr("Error: "+err.Error()) return 1 diff --git a/src/client/nexus-cli/go.mod b/src/client/nexus-cli/go.mod index 89ae454c5bdd5dcd4084e77962c26af82c5757f1..903a49e47fd2271885a7957830a7e0519d4832a1 100644 --- a/src/client/nexus-cli/go.mod +++ b/src/client/nexus-cli/go.mod @@ -4,6 +4,8 @@ go 1.18 replace nexus-common/caps => ../../common/caps +replace nexus-common/params => ../../common/params + replace nexus-common/vm => ../../common/vm replace nexus-common/template => ../../common/template @@ -58,6 +60,7 @@ require ( nexus-client/exec 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/params 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/vm v0.0.0-00010101000000-000000000000 // indirect diff --git a/src/client/nexus-cli/validate b/src/client/nexus-cli/validate index aefcf26ab774d46667125793a65910b41e1ebd48..5c02d6ae5e375ed360326f57f367545c9d2ef5d4 100755 --- a/src/client/nexus-cli/validate +++ b/src/client/nexus-cli/validate @@ -6,11 +6,13 @@ nexus_cli="./nexus-cli" partial_name="exam 328a2d0eff08" full_name="live $partial_name" -creds_file=creds.pdf +creds_pdf_file=creds.pdf +creds_csv_file=creds.csv del_gen_files () { find . -name "$full_name *.tar.gz" -delete - rm -f $creds_file + rm -f $creds_pdf_file + rm -f $creds_csv_file } cleanup () { @@ -90,9 +92,14 @@ check "vmstart VMs" sleep 3 OK -echo "Generate credentials pdf..." -$nexus_cli vmcred2pdf "$full_name" $creds_file -check "vmcred2pdf" +echo "Generate credentials to PDF..." +$nexus_cli vmcreds2pdf "$full_name" $creds_pdf_file +check "vmcreds2pdf" +OK + +echo "Generate credentials to CSV..." +$nexus_cli vmcreds2csv "$full_name" $creds_csv_file +check "vmcreds2csv" OK echo "Kill students VMs..." @@ -106,6 +113,18 @@ $nexus_cli vmexportdir "$full_name" /home check "vmexportdir" 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..." $nexus_cli vmdel "$partial_name" check "vmdel VMs" diff --git a/src/client/nexush/go.mod b/src/client/nexush/go.mod index 2e81f76b12f43593a59853307caf87ab7229890b..4e87fa7b2ce314bc6a23bebd156be83be453de90 100644 --- a/src/client/nexush/go.mod +++ b/src/client/nexush/go.mod @@ -4,6 +4,8 @@ go 1.18 replace nexus-common/caps => ../../common/caps +replace nexus-common/params => ../../common/params + replace nexus-common/vm => ../../common/vm replace nexus-common/template => ../../common/template @@ -60,6 +62,7 @@ require ( nexus-client/utils 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/params 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/vm v0.0.0-00010101000000-000000000000 // indirect diff --git a/src/client/version/version.go b/src/client/version/version.go index 91f2cb535c52b8881263bc049ced2f53e1d8c42c..954a8d501a2b7b4b4cccc238d89106d6d90ac770 100644 --- a/src/client/version/version.go +++ b/src/client/version/version.go @@ -8,7 +8,7 @@ import ( const ( major = 1 minor = 8 - bugfix = 0 + bugfix = 1 ) type Version struct { diff --git a/src/common/params/go.mod b/src/common/params/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..9cb3705f5d86654e3aee6736257cff71642871b4 --- /dev/null +++ b/src/common/params/go.mod @@ -0,0 +1,3 @@ +module nexus-common/params + +go 1.18 diff --git a/src/common/params/templates.go b/src/common/params/templates.go new file mode 100644 index 0000000000000000000000000000000000000000..6bd01e8e7509730e05be801ca24e3d4ad458fdc9 --- /dev/null +++ b/src/common/params/templates.go @@ -0,0 +1,16 @@ +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"` +} diff --git a/src/common/params/users.go b/src/common/params/users.go new file mode 100644 index 0000000000000000000000000000000000000000..dcb13d200c5b299028db4ecad9c252a6256e343c --- /dev/null +++ b/src/common/params/users.go @@ -0,0 +1,13 @@ +package params + +import ( + "nexus-common/caps" +) + +type UserSetCaps struct { + Caps caps.Capabilities +} + +type UserSetPwd struct { + Pwd string `json:"pwd" validate:"required,min=8"` +} diff --git a/src/common/params/vms.go b/src/common/params/vms.go new file mode 100644 index 0000000000000000000000000000000000000000..d7c5256654244f49788fbeb1fd619efb0169333d --- /dev/null +++ b/src/common/params/vms.go @@ -0,0 +1,37 @@ +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"` +} diff --git a/src/server/go.mod b/src/server/go.mod index b7baa9dbcc89c88ab2a048330c733fc9246d0672..9a839d7bc4c62c00a9fc3f1b7e7d942ba2138ca8 100644 --- a/src/server/go.mod +++ b/src/server/go.mod @@ -6,6 +6,8 @@ replace nexus-common/template => ../common/template replace nexus-common/vm => ../common/vm +replace nexus-common/params => ../common/params + replace nexus-common/caps => ../common/caps replace nexus-server/consts => ./consts @@ -62,6 +64,7 @@ require ( golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // 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/vm v0.0.0-00010101000000-000000000000 // indirect nexus-server/exec v0.0.0-00010101000000-000000000000 // indirect diff --git a/src/server/router/routerTemplates.go b/src/server/router/routerTemplates.go index 78042919222bd19ee0a776b4b949e31571385c2c..2038ae72058721041d60f17f87b0c37e5dc5fb6d 100644 --- a/src/server/router/routerTemplates.go +++ b/src/server/router/routerTemplates.go @@ -6,6 +6,7 @@ import ( "net/http" "path/filepath" "nexus-common/caps" + "nexus-common/params" "nexus-server/vms" "nexus-server/paths" "nexus-server/users" @@ -66,13 +67,8 @@ func (r *RouterTemplates)CreateTemplateFromVM(c echo.Context) error { return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) } - // Deserializes and validates the client's parameters. - type Parameters struct { - 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) + // Deserializes and validates client's parameters. + p := new(params.TplCreate) if err := decodeJson(c, &p); err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } @@ -243,14 +239,10 @@ func (r *RouterTemplates)EditTemplateByID(c echo.Context) 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. // Validation is performed in templates.EditTemplate() instead. - type Parameters struct { - Name string `json:"name"` - Access string `json:"access"` - } - p := new(Parameters) + p := new(params.TplEdit) if user.HasCapability(caps.CAP_TPL_EDIT_ANY) { if err := decodeJson(c, &p); err != nil { diff --git a/src/server/router/routerUsers.go b/src/server/router/routerUsers.go index e68af79c85f969b49f25d0350c2eb2799cf4b5ad..a2f3c0866a033035b541e6593dac90832ddbb697 100644 --- a/src/server/router/routerUsers.go +++ b/src/server/router/routerUsers.go @@ -3,6 +3,7 @@ package router import ( "net/http" "nexus-common/caps" + "nexus-common/params" "nexus-server/users" "nexus-server/utils" "github.com/labstack/echo/v4" @@ -128,18 +129,14 @@ func (r *RouterUsers) SetUserCaps(c echo.Context) error { return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps) } - // Deserializes and validates the client's parameters. - type Parameters struct { - Caps caps.Capabilities - } - params := &Parameters{make(caps.Capabilities)} - - if err := decodeJson(c, ¶ms); err != nil { + // Deserializes and validates client's parameters. + p := new(params.UserSetCaps) + if err := decodeJson(c, &p); err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } // 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()) } @@ -150,7 +147,7 @@ func (r *RouterUsers) SetUserCaps(c echo.Context) error { } // Updates the user's capabilities. - userToUpdate.Caps = params.Caps + userToUpdate.Caps = p.Caps // Updates user and saves the new user file. if err = r.users.UpdateUser(&userToUpdate); err != nil { @@ -163,19 +160,14 @@ func (r *RouterUsers) SetUserCaps(c echo.Context) error { // 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>" func (r *RouterUsers) SetUserPwd(c echo.Context) error { - // Deserializes the client's parameters. - type Parameters struct { - Pwd string `json:"pwd" validate:"required,min=8"` - } - params := &Parameters{} - - // Deserializes the JSON body and checks its validity. - if err := decodeJson(c, ¶ms); err != nil { + // Deserializes and validates client's parameters. + p := new(params.UserSetPwd) + if err := decodeJson(c, &p); err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } // 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()) } @@ -186,7 +178,7 @@ func (r *RouterUsers) SetUserPwd(c echo.Context) error { } // 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. if err = r.users.UpdateUser(user); err != nil { diff --git a/src/server/router/routerVMs.go b/src/server/router/routerVMs.go index 06641342fda1c5a2780093f8bb09f83fed1f6980..018c56c4a9b7a2290b9a8cb61ba9063d4bf0c5b2 100644 --- a/src/server/router/routerVMs.go +++ b/src/server/router/routerVMs.go @@ -6,7 +6,7 @@ import ( "net/http" "path/filepath" "nexus-common/caps" - vmc "nexus-common/vm" + "nexus-common/params" "nexus-server/vms" "nexus-server/users" "nexus-server/paths" @@ -179,15 +179,7 @@ func (r *RouterVMs)CreateVM(c echo.Context) error { } // Deserializes and validates client's parameters. - type Parameters 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 vmc.NicType `json:"nic" validate:"required` - UsbDevs []string `json:"usbDevs" validate:"required` - TemplateID uuid.UUID `json:"templateID" validate:"required"` - } - p := new(Parameters) + p := new(params.VMCreate) if err := decodeJson(c, &p); err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.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 { // Deserializes and validates client's parameters. - type Parameters struct { - Port int `json:"port" validate:"required,gte=1100,lte=65535"` - Pwd string `json:"pwd" validate:"required,min=8,max=64"` - } - p := new(Parameters) + p := new(params.VMStartWithCreds) if err := decodeJson(c, &p); err != nil { 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 c.JSONPretty(http.StatusOK, jsonMsg("OK"), " ") @@ -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>" 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 { - // 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. // Validation is performed in vm.EditVM() instead. - type Parameters 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 vmc.NicType `json:"nic" validate:"required` - UsbDevs []string `json:"usbDevs" validate:"required` - } - p := new(Parameters) + p := new(params.VMEdit) if err := decodeJson(c, &p); err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } @@ -368,19 +349,16 @@ func (r *RouterVMs)SetVMAccessForUser(c echo.Context) error { } } - // Deserializes and validates the client's parameters. - type Parameters struct { - Access caps.Capabilities `json:"access" validate:"required"` - } - params := new(Parameters) - if err := decodeJson(c, ¶ms); err != nil { + // Deserializes and validates client's parameters. + p := new(params.VMAddAccess) + if err := decodeJson(c, &p); err != nil { 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()) } - 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()) } @@ -438,10 +416,7 @@ func (r *RouterVMs)DeleteVMAccessForUser(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 { // Deserializes and validates the client's parameter. - type Parameters struct { - Dir string `json:"dir"` - } - p := new(Parameters) + p := new(params.VMExportDir) if err := decodeJson(c, &p); err != nil { return echo.NewHTTPError(http.StatusBadRequest, err.Error()) } diff --git a/src/server/version/version.go b/src/server/version/version.go index 8ead9f3ce61a9819c169a0aecff61073339a34c1..4e448ea6c526b0fd90713186b54848bca27ce4b1 100644 --- a/src/server/version/version.go +++ b/src/server/version/version.go @@ -7,7 +7,7 @@ import ( const ( major = 1 minor = 8 - bugfix = 0 + bugfix = 1 ) type Version struct { diff --git a/src/server/vms/vms.go b/src/server/vms/vms.go index 15447add12a3da1a9972cebf8fb5c34b21412db6..438bf6c5c8d010cfa1c200d8c7fe831178a4a93f 100644 --- a/src/server/vms/vms.go +++ b/src/server/vms/vms.go @@ -90,6 +90,7 @@ func InitVMs() error { // Returns the list of serialized VMs for which VMKeeperFn returns true. func (vms *VMs)GetNetworkSerializedVMs(keepFn VMKeeperFn) []vm.VMNetworkSerialized { vms.rwlock.RLock() + list := []vm.VMNetworkSerialized{} for _, vm := range vms.m { vm.mutex.Lock() @@ -173,16 +174,12 @@ func (vms *VMs)AddVM(vm *VM) error { } // 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() 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) if err != nil { return err @@ -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") } + 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() if err != nil { + vms.usedPorts[port] = false 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 { // otherwise, refuses to run it in order to avoid RAM saturation. if availRAM - vms.usedRAM <= int(math.Round(float64(totalRAM)*(1.-c.RamUsageLimit))) { vms.usedRAM -= estimatedVmRAM + vms.usedPorts[port] = false return errors.New("Failed starting VM: insufficient free RAM") } // This callback is called once the VM started with vm.start terminates. endofExecFn := func (vm *VM) { + vms.rwlock.Lock() + vms.usedPorts[port] = false + vms.usedRAM -= estimatedVmRAM + vms.rwlock.Unlock() vm.mutex.Lock() vm.removeSecretFile() vm.resetStates() 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 { + vms.usedPorts[port] = false return err } return nil } -// 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) { +// Allocates and returns a free port randomly chosen between VMSpiceMinPort and +// VMSpiceMaxPort (inclusive). +// 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 { - port = utils.Rand(c.VMSpiceMinPort, c.VMSpiceMaxPort) + port := utils.Rand(c.VMSpiceMinPort, c.VMSpiceMaxPort) if !vms.usedPorts[port] { if utils.IsPortAvailable(port) { 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, // allowing upper and lower case letters, disallowing repeat characters. @@ -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()) } - if err = vms.StartVMWithCreds(vmID, port, pwd); err != nil { + if err = vms.StartVMWithCreds(vmID, port, false, pwd); err != nil { return -1, "", err } return port, pwd, nil