Files
mayfly-go/machine/machine.go

192 lines
3.9 KiB
Go
Raw Normal View History

2020-09-01 10:34:11 +08:00
package machine
import (
"errors"
"fmt"
"io"
2021-01-08 15:37:32 +08:00
"mayfly-go/base/model"
2020-09-01 10:34:11 +08:00
"mayfly-go/models"
"net"
"os"
"sync"
"time"
2021-01-08 15:37:32 +08:00
"github.com/pkg/sftp"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/terminal"
2020-09-01 10:34:11 +08:00
)
// 客户端信息
type Cli struct {
machine *models.Machine
// ssh客户端
client *ssh.Client
}
// 客户端缓存
var clientCache sync.Map
var mutex sync.Mutex
// 从缓存中获取客户端信息,不存在则查库,并新建
2021-01-08 15:37:32 +08:00
func GetCli(machineId uint64) (*Cli, error) {
2020-09-01 10:34:11 +08:00
mutex.Lock()
defer mutex.Unlock()
load, ok := clientCache.Load(machineId)
if ok {
2021-01-08 15:37:32 +08:00
return load.(*Cli), nil
2020-09-01 10:34:11 +08:00
}
cli, err := newClient(models.GetMachineById(machineId))
if err != nil {
2021-01-08 15:37:32 +08:00
return nil, err
2020-09-01 10:34:11 +08:00
}
clientCache.LoadOrStore(machineId, cli)
2021-01-08 15:37:32 +08:00
return cli, nil
2020-09-01 10:34:11 +08:00
}
//根据机器信息创建客户端对象
func newClient(machine *models.Machine) (*Cli, error) {
if machine == nil {
return nil, errors.New("机器不存在")
}
cli := new(Cli)
cli.machine = machine
err := cli.connect()
if err != nil {
2021-01-08 15:37:32 +08:00
return nil, err
2020-09-01 10:34:11 +08:00
}
return cli, nil
}
//连接
func (c *Cli) connect() error {
// 如果已经有client则直接返回
if c.client != nil {
return nil
}
m := c.machine
config := ssh.ClientConfig{
User: m.Username,
Auth: []ssh.AuthMethod{ssh.Password(m.Password)},
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
Timeout: 5 * time.Second,
}
addr := fmt.Sprintf("%s:%d", m.Ip, m.Port)
sshClient, err := ssh.Dial("tcp", addr, &config)
if err != nil {
return err
}
c.client = sshClient
return nil
}
// 测试连接
func TestConn(m *models.Machine) (*ssh.Client, error) {
config := ssh.ClientConfig{
User: m.Username,
Auth: []ssh.AuthMethod{ssh.Password(m.Password)},
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
Timeout: 5 * time.Second,
}
addr := fmt.Sprintf("%s:%d", m.Ip, m.Port)
sshClient, err := ssh.Dial("tcp", addr, &config)
if err != nil {
return nil, err
}
return sshClient, nil
}
// 关闭client和并从缓存中移除
func (c *Cli) Close() {
if c.client != nil {
c.client.Close()
}
if c.machine.Id > 0 {
clientCache.Delete(c.machine.Id)
}
}
// 获取sftp client
func (c *Cli) GetSftpCli() *sftp.Client {
if c.client == nil {
if err := c.connect(); err != nil {
2021-01-08 15:37:32 +08:00
panic(model.NewBizErr("连接ssh失败" + err.Error()))
2020-09-01 10:34:11 +08:00
}
}
client, serr := sftp.NewClient(c.client, sftp.MaxPacket(1<<15))
if serr != nil {
2021-01-08 15:37:32 +08:00
panic(model.NewBizErr("获取sftp client失败" + serr.Error()))
2020-09-01 10:34:11 +08:00
}
return client
}
// 获取session
func (c *Cli) GetSession() (*ssh.Session, error) {
if c.client == nil {
if err := c.connect(); err != nil {
return nil, err
}
}
return c.client.NewSession()
}
//执行shell
//@param shell shell脚本命令
2021-01-08 15:37:32 +08:00
func (c *Cli) Run(shell string) (*string, error) {
2020-09-01 10:34:11 +08:00
session, err := c.GetSession()
if err != nil {
2021-01-08 15:37:32 +08:00
c.Close()
return nil, err
2020-09-01 10:34:11 +08:00
}
defer session.Close()
buf, rerr := session.CombinedOutput(shell)
if rerr != nil {
2021-01-08 15:37:32 +08:00
return nil, rerr
2020-09-01 10:34:11 +08:00
}
2021-01-08 15:37:32 +08:00
res := string(buf)
return &res, nil
2020-09-01 10:34:11 +08:00
}
//执行带交互的命令
func (c *Cli) RunTerminal(shell string, stdout, stderr io.Writer) error {
session, err := c.GetSession()
if err != nil {
return err
}
//defer session.Close()
fd := int(os.Stdin.Fd())
oldState, err := terminal.MakeRaw(fd)
if err != nil {
panic(err)
}
defer terminal.Restore(fd, oldState)
session.Stdout = stdout
session.Stderr = stderr
session.Stdin = os.Stdin
termWidth, termHeight, err := terminal.GetSize(fd)
if err != nil {
panic(err)
}
// Set up terminal modes
modes := ssh.TerminalModes{
ssh.ECHO: 1, // enable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
// Request pseudo terminal
if err := session.RequestPty("xterm-256color", termHeight, termWidth, modes); err != nil {
return err
}
return session.Run(shell)
}