mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
region: guests: sshable: add POST /servers/<server-id>/make-sshable
This commit is contained in:
@@ -36,3 +36,13 @@ type GuestSshableOutput struct {
|
||||
|
||||
MethodTried []GuestSshableMethodData
|
||||
}
|
||||
|
||||
type GuestMakeSshableInput struct {
|
||||
User string
|
||||
PrivateKey string
|
||||
Password string
|
||||
}
|
||||
|
||||
type GuestMakeSshableOutput struct {
|
||||
AnsiblePlaybookId string
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
@@ -30,12 +31,16 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
mcclient_modules "yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
cloudproxy_module "yunion.io/x/onecloud/pkg/mcclient/modules/cloudproxy"
|
||||
"yunion.io/x/onecloud/pkg/util/ansible"
|
||||
"yunion.io/x/onecloud/pkg/util/httputils"
|
||||
ssh_util "yunion.io/x/onecloud/pkg/util/ssh"
|
||||
)
|
||||
|
||||
type GuestSshableTryData struct {
|
||||
DryRun bool
|
||||
|
||||
User string
|
||||
Host string
|
||||
Port int
|
||||
@@ -320,6 +325,11 @@ func (guest *SGuest) sshableTry(
|
||||
tryData *GuestSshableTryData,
|
||||
methodData compute_api.GuestSshableMethodData,
|
||||
) bool {
|
||||
if tryData.DryRun {
|
||||
tryData.AddMethodTried(methodData)
|
||||
return true
|
||||
}
|
||||
|
||||
ctx, _ = context.WithTimeout(ctx, 7*time.Second)
|
||||
conf := ssh_util.ClientConfig{
|
||||
Username: tryData.User,
|
||||
@@ -338,3 +348,123 @@ func (guest *SGuest) sshableTry(
|
||||
tryData.AddMethodTried(methodData)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (guest *SGuest) AllowPerformMakeSshable(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject,
|
||||
) bool {
|
||||
return db.IsProjectAllowGetSpec(userCred, guest, "make-sshable")
|
||||
}
|
||||
|
||||
func (guest *SGuest) PerformMakeSshable(
|
||||
ctx context.Context,
|
||||
userCred mcclient.TokenCredential,
|
||||
query jsonutils.JSONObject,
|
||||
input compute_api.GuestMakeSshableInput,
|
||||
) (output compute_api.GuestMakeSshableOutput, err error) {
|
||||
if guest.Status != compute_api.VM_RUNNING {
|
||||
return output, httperrors.NewBadRequestError("make-sshable can only be performed when in running state")
|
||||
}
|
||||
|
||||
if input.User == "" {
|
||||
return output, httperrors.NewBadRequestError("missing username")
|
||||
}
|
||||
if input.PrivateKey == "" && input.Password == "" {
|
||||
return output, httperrors.NewBadRequestError("private_key and password cannot both be empty")
|
||||
}
|
||||
|
||||
_, projectPublicKey, err := sshkeys.GetSshProjectKeypair(ctx, guest.ProjectId)
|
||||
if err != nil {
|
||||
return output, httperrors.NewInternalServerError("fetch project public key: %v", err)
|
||||
}
|
||||
_, adminPublicKey, err := sshkeys.GetSshAdminKeypair(ctx)
|
||||
if err != nil {
|
||||
return output, httperrors.NewInternalServerError("fetch admin public key: %v", err)
|
||||
}
|
||||
|
||||
tryData := &GuestSshableTryData{
|
||||
DryRun: true,
|
||||
}
|
||||
if err := guest.sshableTryEach(ctx, userCred, tryData); err != nil {
|
||||
return output, httperrors.NewNotAcceptableError("searching for usable ssh address: %v", err)
|
||||
} else if len(tryData.MethodTried) == 0 {
|
||||
return output, httperrors.NewNotAcceptableError("no usable ssh address")
|
||||
}
|
||||
|
||||
host := ansible.Host{
|
||||
Name: guest.Name,
|
||||
}
|
||||
host.SetVar("ansible_user", input.User)
|
||||
host.SetVar("ansible_host", tryData.MethodTried[0].Host)
|
||||
host.SetVar("ansible_port", fmt.Sprintf("%d", tryData.MethodTried[0].Port))
|
||||
host.SetVar("ansible_become", "yes")
|
||||
pb := &ansible.Playbook{
|
||||
Inventory: ansible.Inventory{
|
||||
Hosts: []ansible.Host{host},
|
||||
},
|
||||
Modules: []ansible.Module{
|
||||
{
|
||||
Name: "group",
|
||||
Args: []string{
|
||||
"name=cloudroot",
|
||||
"state=present",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "user",
|
||||
Args: []string{
|
||||
"name=cloudroot",
|
||||
"state=present",
|
||||
"group=cloudroot",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "authorized_key",
|
||||
Args: []string{
|
||||
"user=cloudroot",
|
||||
"state=present",
|
||||
fmt.Sprintf("key=%q", adminPublicKey),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "authorized_key",
|
||||
Args: []string{
|
||||
"user=cloudroot",
|
||||
"state=present",
|
||||
fmt.Sprintf("key=%q", projectPublicKey),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "lineinfile",
|
||||
Args: []string{
|
||||
"dest=/etc/sudoers",
|
||||
"state=present",
|
||||
fmt.Sprintf("regexp=%q", "^cloudroot "),
|
||||
fmt.Sprintf("line=%q", "cloudroot ALL=(ALL) NOPASSWD: ALL"),
|
||||
fmt.Sprintf("validate=%q", "visudo -cf %s"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if input.PrivateKey != "" {
|
||||
pb.PrivateKey = []byte(input.PrivateKey)
|
||||
} else if input.Password != "" {
|
||||
host.SetVar("ansible_password", input.Password)
|
||||
}
|
||||
|
||||
cliSess := auth.GetSession(ctx, userCred, "", "")
|
||||
pbId := ""
|
||||
pbName := "make-sshable-" + guest.Name
|
||||
pbModel, err := mcclient_modules.AnsiblePlaybooks.UpdateOrCreatePbModel(
|
||||
ctx, cliSess, pbId, pbName, pb,
|
||||
)
|
||||
if err != nil {
|
||||
return output, httperrors.NewGeneralError(err)
|
||||
}
|
||||
|
||||
output = compute_api.GuestMakeSshableOutput{
|
||||
AnsiblePlaybookId: pbModel.Id,
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user