climc: add server and host ssh terminal util

This commit is contained in:
Zexi Li
2020-03-10 18:24:37 +08:00
parent 9bad7f3485
commit ff27373bb2
4 changed files with 180 additions and 0 deletions
+42
View File
@@ -18,11 +18,13 @@ import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/mcclient/options"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/ssh"
)
func init() {
@@ -664,4 +666,44 @@ func init() {
printObject(result)
return nil
})
type HostSSHLoginOptions struct {
ID string `help:"ID or name of host"`
Port int `help:"SSH service port" default:"22"`
}
R(&HostSSHLoginOptions{}, "host-ssh", "SSH login of a host", func(s *mcclient.ClientSession, args *HostSSHLoginOptions) error {
srvid, e := modules.Hosts.GetId(s, args.ID, nil)
if e != nil {
return e
}
i, e := modules.Hosts.GetLoginInfo(s, srvid, nil)
if e != nil {
return e
}
host, err := i.GetString("ip")
if err != nil {
return err
}
user, err := i.GetString("username")
if err != nil {
return err
}
passwd, err := i.GetString("password")
if err != nil {
return err
}
port := 22
if args.Port != 22 {
port = args.Port
}
sshCli, err := ssh.NewClient(host, port, user, passwd, "")
if err != nil {
return err
}
log.Infof("ssh %s:%d", host, port)
if err := sshCli.RunTerminal(); err != nil {
return err
}
return nil
})
}
+74
View File
@@ -34,6 +34,7 @@ import (
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/mcclient/options"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/ssh"
)
func init() {
@@ -1218,4 +1219,77 @@ func init() {
}
return nil
})
R(&options.ServerSSHLoginOptions{}, "server-ssh", "Use SSH login a server", func(s *mcclient.ClientSession, opts *options.ServerSSHLoginOptions) error {
srv, err := modules.Servers.Get(s, opts.ID, nil)
if err != nil {
return err
}
srvid, err := srv.GetString("id")
if err != nil {
return err
}
address := make([]string, 0)
nics, err := srv.GetArray("nics")
if err != nil {
return err
}
for _, nic := range nics {
if addr, err := nic.GetString("ip_addr"); err == nil {
address = append(address, addr)
}
}
if len(address) == 0 {
return fmt.Errorf("Not found ip address from server %s", opts.ID)
}
params := jsonutils.NewDict()
if len(opts.Key) > 0 {
privateKey, e := ioutil.ReadFile(opts.Key)
if e != nil {
return e
}
params.Add(jsonutils.NewString(string(privateKey)), "private_key")
}
i, e := modules.Servers.GetLoginInfo(s, srvid, params)
if e != nil {
return e
}
passwd, err := i.GetString("password")
if err != nil {
return err
}
if opts.Password != "" {
passwd = opts.Password
}
user, err := i.GetString("username")
if err != nil {
return err
}
if opts.User != "" {
user = opts.User
}
host := address[0]
if opts.Host != "" {
host = opts.Host
}
port := 22
if opts.Port != 22 {
port = opts.Port
}
sshCli, err := ssh.NewClient(host, port, user, passwd, "")
if err != nil {
return err
}
log.Infof("ssh %s:%d", host, port)
if err := sshCli.RunTerminal(); err != nil {
return err
}
return nil
})
}
+8
View File
@@ -68,6 +68,14 @@ type ServerLoginInfoOptions struct {
Key string `help:"File name of private key, if password is encrypted by key"`
}
type ServerSSHLoginOptions struct {
ServerLoginInfoOptions
Host string `help:"IP address or hostname of the server"`
Port int `help:"SSH service port" default:"22"`
User string `help:"SSH login user"`
Password string `help:"SSH password"`
}
type ServerIdsOptions struct {
ID []string `help:"ID of servers to operate" metavar:"SERVER" json:"-"`
}
+56
View File
@@ -18,10 +18,12 @@ import (
"bytes"
"fmt"
"io"
"os"
"strings"
"time"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/terminal"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
@@ -168,3 +170,57 @@ func ParseOutput(output []byte) []string {
func (s *Client) Close() {
s.client.Close()
}
func (s *Client) RunTerminal() error {
defer s.Close()
session, err := s.client.NewSession()
if err != nil {
return errors.Wrap(err, "open new session")
}
defer session.Close()
fd := int(os.Stdin.Fd())
state, err := terminal.MakeRaw(fd)
if err != nil {
return errors.Wrap(err, "make raw terminal")
}
defer terminal.Restore(fd, state)
w, h, err := terminal.GetSize(fd)
if err != nil {
return errors.Wrap(err, "get terminal size")
}
modes := ssh.TerminalModes{
ssh.ECHO: 1,
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400,
}
term := os.Getenv("TERM")
if term == "" {
term = "xterm-256color"
}
if err := session.RequestPty(term, h, w, modes); err != nil {
return errors.Wrap(err, "session xterm")
}
session.Stdout = os.Stdout
session.Stderr = os.Stderr
session.Stdin = os.Stdin
if err := session.Shell(); err != nil {
return errors.Wrap(err, "session shell")
}
if err := session.Wait(); err != nil {
if e, ok := err.(*ssh.ExitError); ok {
switch e.ExitStatus() {
case 130:
return nil
}
}
return errors.Wrap(err, "ssh wait")
}
return nil
}