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

Ongoing work on new attach route for a single VM + client side of things

parent b06696f3
Branches
No related tags found
No related merge requests found
......@@ -24,19 +24,20 @@
### VM management
| Route | Description | Method | Parameters | Req. user cap. | Op. | Req. VM access cap. |
|--- |--- |--- |--- |--- |--- |--- |
| `/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/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_ATTACH_ANY` | OR | `VM_ATTACH` |
| `/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/edit` | returns VMs that can be edited | GET | | `VM_EDIT_ANY` | OR | `VM_EDIT` |
| `/vms/editaccess` | returns VMs that can have their access changed | GET | | `VM_SET_ACCESS` | AND | `VM_SET_ACCESS` |
| `/vms/del` | returns VMs that can be deleted | GET | | `VM_DESTROY_ANY` | OR | `VM_DESTROY` |
| `/vms/exportdir` | returns VMs that can have a dir downloaded | GET | | `VM_READFS_ANY` | OR | `VM_READFS` |
| `/vms/importfiles` | returns VMs allowing files upload | GET | | `VM_WRITEFS_ANY` | OR | `VM_WRITEFS` |
| Route | Description | Method | Parameters | Req. user cap. | Op. | Req. VM access cap. |
|--- |--- |--- |--- |--- |--- |--- |
| `/vms` | returns VMs that can be listed | GET | | `VM_LIST_ANY` | OR | `VM_LIST` |
| `/vms/{id}` | returns the specified VM | GET | | `VM_LIST_ANY` | OR | `VM_LIST` |
| `/vms/start` | returns VMs that can be started | GET | | `VM_START_ANY` | OR | `VM_START` |
| `/vms/attach` | returns VM creds info for VMs that can be attached to | GET | | `VM_ATTACH_ANY` | OR | `VM_ATTACH` |
| `/vms/{id}/attach` | returns VM creds info for the specified VMs | GET | | `VM_ATTACH_ANY` | OR | `VM_ATTACH` |
| `/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/edit` | returns VMs that can be edited | GET | | `VM_EDIT_ANY` | OR | `VM_EDIT` |
| `/vms/editaccess` | returns VMs that can have their access changed | GET | | `VM_SET_ACCESS` | AND | `VM_SET_ACCESS` |
| `/vms/del` | returns VMs that can be deleted | GET | | `VM_DESTROY_ANY` | OR | `VM_DESTROY` |
| `/vms/exportdir` | returns VMs that can have a dir downloaded | GET | | `VM_READFS_ANY` | OR | `VM_READFS` |
| `/vms/importfiles` | returns VMs allowing files upload | GET | | `VM_WRITEFS_ANY` | OR | `VM_WRITEFS` |
| Route | Description | Method | Parameters | Req. user cap. | Op. | Req. VM access cap. |
|--- |--- |--- |--- |--- |--- |--- |
......
......@@ -145,7 +145,7 @@ func getFilteredVMCredentials(route string, patterns []string) ([]vm.VMCredentia
vmsList := []vm.VMCredentialsSerialized{}
if resp.IsSuccess() {
vms, err := deserializeVMCredentials(resp)
vms, err := deserializeVMsCredentials(resp)
if err != nil {
return nil, err
}
......@@ -189,15 +189,6 @@ func deserializeVMs(resp *resty.Response) ([]vm.VMNetworkSerialized, error) {
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.
func deserializeVM(resp *resty.Response) (*vm.VMNetworkSerialized, error) {
var vm *vm.VMNetworkSerialized = &vm.VMNetworkSerialized{}
......@@ -206,3 +197,22 @@ func deserializeVM(resp *resty.Response) (*vm.VMNetworkSerialized, error) {
}
return vm, nil
}
// Deserialize a list of VM credentials from an http response.
func deserializeVMsCredentials(resp *resty.Response) ([]vm.VMCredentialsSerialized, error) {
vmsCreds := []vm.VMCredentialsSerialized{}
if err := json.Unmarshal(resp.Body(), &vmsCreds); err != nil {
return nil, err
}
return vmsCreds, nil
}
// Deserialize a VM credentials from an http response.
func deserializeVMCredentials(resp *resty.Response) (*vm.VMCredentialsSerialized, error) {
var vmCreds vm.VMCredentialsSerialized
if err := json.Unmarshal(resp.Body(), &vmCreds); err != nil {
return nil, err
}
return &vmCreds, nil
}
......@@ -2,8 +2,8 @@ package cmdVM
import (
"fmt"
"nexus-client/exec"
"nexus-common/vm"
"nexus-client/exec"
u "nexus-client/utils"
g "nexus-client/globals"
)
......
package cmdVM
import (
"fmt"
"nexus-common/vm"
"nexus-client/exec"
u "nexus-client/utils"
g "nexus-client/globals"
)
type AttachAsyncSingle struct {
Name string
}
func (cmd *AttachAsyncSingle)GetName() string {
return cmd.Name
}
func (cmd *AttachAsyncSingle)GetDesc() []string {
return []string{
"Attach to a VM.",
"If not the VM's owner: requires VM_ATTACH VM access capability or VM_ATTACH_ANY user capability."}
}
func (cmd *AttachAsyncSingle)PrintUsage() {
for _, desc := range cmd.GetDesc() {
u.PrintlnErr(desc)
}
u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――")
u.PrintlnErr("USAGE: ",cmd.GetName(), " ID")
u.PrintlnErr("―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――")
}
func (cmd *AttachAsyncSingle)Run(args []string) int {
if err := exec.CheckRemoteViewer(); err != nil {
u.PrintlnErr(err.Error())
return 1
}
hostname := g.GetInstance().Hostname
cert := g.GetInstance().PubCert
client := g.GetInstance().Client
host := g.GetInstance().Host
argc := len(args)
if argc < 1 {
cmd.PrintUsage()
return 1
}
statusCode := 0
vmID := args[0]
resp, err := client.R().Get(host+"/vms/"+vmID+"/attach")
if err != nil {
u.PrintlnErr("Failed retrieving VM credentials for VM \""+vmID+"\": "+err.Error())
return 1
} else {
if resp.IsSuccess() {
myvm, err := deserializeVMCredentials(resp)
if err != nil {
u.PrintlnErr("Failed retrieving server's response: "+err.Error())
return 1
}
go func(v vm.VMCredentialsSerialized) {
stdoutStderr, err := exec.RunRemoteViewer(hostname, cert, v.Name, v.Port, v.Pwd, false)
if err != nil {
u.PrintlnErr("Failed attaching to VM ", v.ID, ": ", fmt.Sprintf("%s", stdoutStderr))
statusCode = 1
}
} (*myvm)
} else {
u.PrintlnErr("Failed retrieving VM credentials for VM \""+vmID+"\": "+resp.Status()+": "+resp.String())
statusCode = 1
}
}
return statusCode
}
......@@ -46,7 +46,6 @@ var cmdList = []cmd.Command {
&cmdMisc.HelpHeader{"!═════╡ TEMPLATE commands ╞═════════════════════════════════════════════════════════════════"},
&cmdTemplate.Create{"tplcreate"},
&cmdTemplate.Del{"tpldel"},
&cmdTemplate.Edit{"tpledit"},
&cmdTemplate.ExportDisk{"tplexportdisk"},
&cmdTemplate.List{"tpllist"},
// &cmdTemplate.ListSingle{"tpllistsingle"}, // for testing the route only
......@@ -54,6 +53,7 @@ var cmdList = []cmd.Command {
&cmdMisc.HelpHeader{"!═════╡ VM commands ╞═════════════════════════════════════════════════════════════════"},
&cmdVM.AddAccess{"vmaddaccess"},
&cmdVM.AttachAsync{"vmattach"},
&cmdVM.AttachAsyncSingle{"vmattachsingle"}, // for testing the route only
&cmdVM.Create{"vmcreate"},
&cmdVM.Creds2pdf{"vmcreds2pdf"},
&cmdVM.Creds2csv{"vmcreds2csv"},
......@@ -75,9 +75,6 @@ var prompt *liner.State = nil
var savedTermState *term.State = nil
func run() int {
cmdList[18].PrintUsage()
os.Exit(0)
var appname = path.Base(os.Args[0])
u.PrintlnErr(appname+" version "+version.Get().String())
......
......@@ -73,6 +73,7 @@ func (router *Router)Start(port int) {
vmsGroup.Use(middleware.JWTWithConfig(auth.GetTokenAccess()))
vmsGroup.GET("/:id", router.vms.GetListableVM)
vmsGroup.GET("", router.vms.GetListableVMs)
vmsGroup.GET("/:id/attach", router.vms.GetAttachableVM)
vmsGroup.GET("/attach", router.vms.GetAttachableVMs)
vmsGroup.GET("/del", router.vms.GetDeletableVMs)
vmsGroup.GET("/start", router.vms.GetStartableVMs)
......
......@@ -83,10 +83,10 @@ func (r *RouterVMs)GetListableVM(c echo.Context) error {
}
}
// Returns VMs that are running and that can be attached to.
// Returns VMs credentials for VMs that are running and that can be attached to.
// Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_ATTACH_ANY: returns all running VMs.
// VM access cap: CAP_VM_ATTACH: returns all running VMs with this cap for the logged user.
// User cap: CAP_VM_ATTACH_ANY: returns VMs credentials for all running VMs.
// VM access cap: CAP_VM_ATTACH: returns VMs credentials for all running VMs with this cap for the logged user.
// curl --cacert ca.pem -X GET https://localhost:1077/vms/attach -H "Authorization: Bearer <AccessToken>"
func (r *RouterVMs)GetAttachableVMs(c echo.Context) error {
return r.getFilteredVMCredentials(c, caps.CAP_VM_ATTACH_ANY, caps.CAP_VM_ATTACH, func(vm *vms.VM) bool {
......@@ -94,6 +94,50 @@ func (r *RouterVMs)GetAttachableVMs(c echo.Context) error {
})
}
// Returns VM credentials for a VM specified by its UUID.
// Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_ATTACH_ANY: returns VM credentials for the VM.
// VM access cap: CAP_VM_ATTACH: returns VM credentials for the VM with this cap for the logged user.
// curl --cacert ca.pem -X GET https://localhost:1077/vms/62ae8791-c108-4235-a7d6-074e9b6a9017/attach -H "Authorization: Bearer <AccessToken>"
func (r *RouterVMs)GetAttachableVM(c echo.Context) error {
// Retrieves logged user from context.
user, err := getLoggedUser(r.users, c)
if err != nil {
return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
}
// Retrieves the VM based on its UUID.
id, err := uuid.Parse(c.Param("id"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
vm, err := r.vms.GetVM(id)
if err != nil {
return echo.NewHTTPError(http.StatusNotFound, err.Error())
}
// If user has CAP_VM_ATTACH_ANY capability, returns the VM.
if user.HasCapability(caps.CAP_VM_ATTACH_ANY) {
return c.JSONPretty(http.StatusOK, vm.SerializeCredentials(), " ")
} else {
if vm.IsOwner(user.Email) {
return c.JSONPretty(http.StatusOK, vm.SerializeCredentials(), " ")
} else {
capabilities, exists := vm.GetAccess()[user.Email]
if exists {
_, found := capabilities[caps.CAP_VM_ATTACH]
if found {
return c.JSONPretty(http.StatusOK, vm.SerializeCredentials(), " ")
} else {
return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps)
}
} else {
return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps)
}
}
}
}
// Returns VMs that are stopped and that can be deleted.
// Requires to be the VM's owner, or either capability:
// User cap: CAP_VM_DESTROY_ANY: returns all VMs.
......
......@@ -32,12 +32,18 @@ func (user *User)HasCapability(capability string) bool {
}
func (user *User)GetVMAccessCapabilities() []string {
capabilities := []string{}
for cap, _ := range caps.VMAccessCaps {
_, exists := user.Caps[cap]
if exists {
capabilities = append(capabilities, cap)
}
caps := []string{}
for key, _ := range user.Caps {
caps = append(caps, key)
}
return capabilities
return caps
// capabilities := []string{}
// for cap, _ := range caps.GetVMAccessCapsNames() {
// _, exists := user.Caps[cap]
// if exists {
// capabilities = append(capabilities, cap)
// }
// }
// return capabilities
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment