【发布时间】:2016-02-19 19:11:32
【问题描述】:
我将以下代码拼凑在一起,可以让我向 Cisco 机器的 ssh 控制台发送读写操作,但我目前不确定如何在同一个会话中发送多个命令。
我将整个程序包括在内,以便将服务器作为其他人将来尝试相同事情的起点,我找不到专门针对 GolangCisco 的工作示例。
package main
import (
"fmt"
"io"
"io/ioutil"
"net"
"os"
//"strings"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)
type SSHCommand struct {
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
}
type SSHClient struct {
Config *ssh.ClientConfig
Host string
Port int
}
func (client *SSHClient) RunCommand(cmd *SSHCommand) error {
var (
session *ssh.Session
err error
)
if session, err = client.newSession(); err != nil {
return err
}
defer session.Close()
if err = client.prepareCommand(session, cmd); err != nil {
return err
}
err = session.Run("en\r")
return err
}
func (client *SSHClient) prepareCommand(session *ssh.Session, cmd *SSHCommand) error {
if cmd.Stdin != nil {
stdin, err := session.StdinPipe()
if err != nil {
return fmt.Errorf("Unable to setup stdin for session: %v", err)
}
go io.Copy(stdin, cmd.Stdin)
}
if cmd.Stdout != nil {
stdout, err := session.StdoutPipe()
if err != nil {
return fmt.Errorf("Unable to setup stdout for session: %v", err)
}
go io.Copy(cmd.Stdout, stdout)
}
if cmd.Stderr != nil {
stderr, err := session.StderrPipe()
if err != nil {
return fmt.Errorf("Unable to setup stderr for session: %v", err)
}
go io.Copy(cmd.Stderr, stderr)
}
return nil
}
func (client *SSHClient) newSession() (*ssh.Session, error) {
connection, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", client.Host, client.Port), client.Config)
if err != nil {
return nil, fmt.Errorf("Failed to dial: %s", err)
}
session, err := connection.NewSession()
if err != nil {
return nil, fmt.Errorf("Failed to create session: %s", err)
}
modes := ssh.TerminalModes{
// ssh.ECHO: 0, // disable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
if err := session.RequestPty("xterm", 0, 200, modes); err != nil {
session.Close()
return nil, fmt.Errorf("request for pseudo terminal failed: %s", err)
}
return session, nil
}
func SSHAgent() ssh.AuthMethod {
if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
return ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers)
}
return nil
}
func main() {
sshConfig := &ssh.ClientConfig{
User: "admin",
Auth: []ssh.AuthMethod{
ssh.Password("password"),
},
}
sshConfig.Ciphers = []string{"aes128-cbc"}
client := &SSHClient{
Config: sshConfig,
Host: "10.10.10.10",
Port: 22,
}
cmd := &SSHCommand{
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
}
fmt.Printf("Running commands\n")
if err := client.RunCommand(cmd); err != nil {
fmt.Fprintf(os.Stderr, "command run error: %s\n", err)
os.Exit(1)
}
}
如您所见,我在 session.Run() 中硬编码了“en”命令。 如果我通过简单地添加一个命令:
err = session.Run("en\r")
err = session.Run("password\r")
它被忽略了。阅读https://godoc.org/golang.org/x/crypto/ssh#Session.Shell 告诉我这是设计使然。所以我的问题是:如何设置一些东西,让我输入命令、读取输出、解析它并根据该输出输入更多命令?
一旦我解决了这个问题,我计划解析路由表并发出命令作为响应。 IE,如果缺少路由X,添加它。
【问题讨论】:
-
为什么不能打开多个会话,每个命令一个?
-
我的印象是新会话不会保持上下文。即,我需要在同一个“线程”中以特定顺序输入一组特定的命令,即:命令二取决于命令一。我将上下文等同于会话,这是不正确的吗?
-
一个 ssh 会话只能运行一个命令。这就是 ssh 协议的工作原理。保持“状态”的唯一方法是打开一个shell(这种情况下需要交互解析cli),或者执行一个脚本(只能在脚本执行过程中保持状态)。