Go 初始化/管理并发SSH连接

Go 初始化/管理并发SSH连接,go,struct,ssh,connection,Go,Struct,Ssh,Connection,我上下搜索过,我不确定是否使用了错误的关键字,但我无法理解这一点。我正在构建一个应用程序,它接收主机名列表并通过SSH连接到这些主机名。其目的是维护这些连接(并在断开连接时重新连接)。我的程序会周期性地接收指令,并对部分/所有这些主机执行命令 我现在的问题是,知道您不能初始化变量而不使用它,我必须为这些SSH连接动态创建变量,以便能够独立地监视/管理它们(读/写,必要时重新连接,等等)。由于我对go的了解有限,而且有一种意外地使事情过于复杂的倾向,到目前为止,我想到的最好的方法是为每个连接及其参

我上下搜索过,我不确定是否使用了错误的关键字,但我无法理解这一点。我正在构建一个应用程序,它接收主机名列表并通过SSH连接到这些主机名。其目的是维护这些连接(并在断开连接时重新连接)。我的程序会周期性地接收指令,并对部分/所有这些主机执行命令

我现在的问题是,知道您不能初始化变量而不使用它,我必须为这些SSH连接动态创建变量,以便能够独立地监视/管理它们(读/写,必要时重新连接,等等)。由于我对go的了解有限,而且有一种意外地使事情过于复杂的倾向,到目前为止,我想到的最好的方法是为每个连接及其参数(主机名、用户名、密码、SSH配置详细信息、日志文件位置等)使用一个结构和附加

目前,我的不可靠代码如下所示:

package main

import (
    "os"
    "fmt"
    "flag"
    "golang.org/x/crypto/ssh"
    "io"
    "log"
    "strings"
    "time"
    "encoding/xml"
    "bufio"
    "net"
)

// SSH connection struct
type SSHConnections struct {
    Host string
    User string
    Password string
    CLILogfile string
    SSHConn ssh.Client
    SSHConfig ssh.ClientConfig
}

func main() {
    //parse list of addresses
    var ipaddr = []string{"a.b.c.d","e.f.g.h"} //hard-coded for now

    //build out SSHConnections struct

    for i := 0; i < len(ipaddr); i++ {
        tempsshConfig := &ssh.ClientConfig{
            User:   "administrator",
            Auth: []ssh.AuthMethod{
                ssh.Password("Password!"),
            },
            HostKeyCallback: ssh.InsecureIgnoreHostKey(), 
        }
        tempsshConfig.Config.Ciphers = append(tempsshConfig.Config.Ciphers, "aes128-cbc")
        var newitem = SSHConnections{
            Host: ipaddr[i],
            User: "administrator",
            Password: "Password!",
            CLILogfile: ipaddr[1],
            SSHConn: ssh.Client,
            SSHConfig: *tempsshConfig,
        }

        SSHConnections = append(SSHConnections, newitem)
    }
for i := 0; i < len(ipaddr); i++ {
    // ...
    var newitem = SSHConnections{
        // ...
        SSHConn: ssh.Client,
        // ...
    }

    SSHConnections = append(SSHConnections, newitem)
}
var connections []SSHConnections
for i := 0; i < len(ipaddr); i++ {
    // ...
    var newitem = SSHConnections{
        // Everything that's there now except the SSHConn ...
    }
    connections = append(connections, newitem)    
}
在此之后,我还需要管理特定于连接的参数(日志文件声明、其他SSH参数、实际的SSH连接过程),可能与上面的结构相同。以上是我目前的绊脚石,我不知道如何解决这个问题,更不用说将下面的单一连接代码集成到上面了

//extra SSH parameters required before connecting
sshConfig.Config.Ciphers = append(sshConfig.Config.Ciphers, "aes128-cbc")
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
}

//prepare logfiles
f, ferr := os.OpenFile("outputfile.txt", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
if ferr != nil {
    panic(ferr)
}
defer f.Close() 

//SSH connection procedure
connection, err := ssh.Dial("tcp", hostname+":22", sshConfig)
if err != nil {
    log.Fatalf("Failed to dial: %s", err)
}
session, err := connection.NewSession()
handleError(err, true, "Failed to create session: %s")
sshOut, err := session.StdoutPipe()
handleError(err, true, "Unable to setup stdin for session: %v")
sshIn, err := session.StdinPipe()
handleError(err, true, "Unable to setup stdout for session: %v")
if err := session.RequestPty("xterm", 0, 200, modes); err != nil {
    session.Close()
    handleError(err, true, "request for pseudo terminal failed: %s")
}
if err := session.Shell(); err != nil {
    session.Close()
    handleError(err, true, "request for shell failed: %s")
}
为了让我的帖子更清晰,我删掉了一大堆不相关的代码。我希望我在这方面取得了一点成功

我甚至不确定结构是否是我想要的,但接收数量可变的主机名的动态性以及单独控制它们的要求是关键。稍后,我可能还需要逐个拆除连接,因此我正在寻找一个可以操纵它们的结构

非常感谢任何能为我指明正确方向的帮助。谢谢

-------------更新此行下面的内容---------------

在mu在下面的帖子和一些谷歌搜索之后,我的代码现在看起来像这样(删除导入):

var连接[]SSHConnections
//SSH连接结构
类型SSHConnections结构{
主机字符串
用户字符串
密码字符串
CLILogfilepath字符串
CLILogfile*os.File
SSHConn*ssh.Client
Session*ssh.Session
SSHOut io.Reader
SSHIn io.WriteCloser
SSHConfig*ssh.ClientConfig
CLIReady字符串
SSHConnErr错误
会话器错误
时差
SSH误差
CLILogfileErr错误
命令队列chan字符串
}
func handleError(e错误、致命错误、自定义消息…字符串){
var错误消息字符串
如果e!=nil{
如果len(customMessage)>0{
errorMessage=strings.Join(customMessage,“”)
}否则{
errorMessage=“%s”
}
如果致命==真{
log.Fatalf(错误消息,e)
}否则{
日志打印(错误消息,e)
}
}
}
func writeToFile(f*os.File,错误,输入字符串){
如果,err=f.WriteString(inputString); err!=nil{
恐慌(错误)
}   
}
func connectionWorker(i int){
//此功能用于监视SSH连接,并在它们终止时重新连接
fmt.Println(“goroutine for:+connections[i].Host)
对于{//不确定这是否必要?尚待测试
挑选{

case命令:=您的循环如下所示:

package main

import (
    "os"
    "fmt"
    "flag"
    "golang.org/x/crypto/ssh"
    "io"
    "log"
    "strings"
    "time"
    "encoding/xml"
    "bufio"
    "net"
)

// SSH connection struct
type SSHConnections struct {
    Host string
    User string
    Password string
    CLILogfile string
    SSHConn ssh.Client
    SSHConfig ssh.ClientConfig
}

func main() {
    //parse list of addresses
    var ipaddr = []string{"a.b.c.d","e.f.g.h"} //hard-coded for now

    //build out SSHConnections struct

    for i := 0; i < len(ipaddr); i++ {
        tempsshConfig := &ssh.ClientConfig{
            User:   "administrator",
            Auth: []ssh.AuthMethod{
                ssh.Password("Password!"),
            },
            HostKeyCallback: ssh.InsecureIgnoreHostKey(), 
        }
        tempsshConfig.Config.Ciphers = append(tempsshConfig.Config.Ciphers, "aes128-cbc")
        var newitem = SSHConnections{
            Host: ipaddr[i],
            User: "administrator",
            Password: "Password!",
            CLILogfile: ipaddr[1],
            SSHConn: ssh.Client,
            SSHConfig: *tempsshConfig,
        }

        SSHConnections = append(SSHConnections, newitem)
    }
for i := 0; i < len(ipaddr); i++ {
    // ...
    var newitem = SSHConnections{
        // ...
        SSHConn: ssh.Client,
        // ...
    }

    SSHConnections = append(SSHConnections, newitem)
}
var connections []SSHConnections
for i := 0; i < len(ipaddr); i++ {
    // ...
    var newitem = SSHConnections{
        // Everything that's there now except the SSHConn ...
    }
    connections = append(connections, newitem)    
}
应该是:

someSlice = append(someSlice, newitem)
该循环应该更像这样:

package main

import (
    "os"
    "fmt"
    "flag"
    "golang.org/x/crypto/ssh"
    "io"
    "log"
    "strings"
    "time"
    "encoding/xml"
    "bufio"
    "net"
)

// SSH connection struct
type SSHConnections struct {
    Host string
    User string
    Password string
    CLILogfile string
    SSHConn ssh.Client
    SSHConfig ssh.ClientConfig
}

func main() {
    //parse list of addresses
    var ipaddr = []string{"a.b.c.d","e.f.g.h"} //hard-coded for now

    //build out SSHConnections struct

    for i := 0; i < len(ipaddr); i++ {
        tempsshConfig := &ssh.ClientConfig{
            User:   "administrator",
            Auth: []ssh.AuthMethod{
                ssh.Password("Password!"),
            },
            HostKeyCallback: ssh.InsecureIgnoreHostKey(), 
        }
        tempsshConfig.Config.Ciphers = append(tempsshConfig.Config.Ciphers, "aes128-cbc")
        var newitem = SSHConnections{
            Host: ipaddr[i],
            User: "administrator",
            Password: "Password!",
            CLILogfile: ipaddr[1],
            SSHConn: ssh.Client,
            SSHConfig: *tempsshConfig,
        }

        SSHConnections = append(SSHConnections, newitem)
    }
for i := 0; i < len(ipaddr); i++ {
    // ...
    var newitem = SSHConnections{
        // ...
        SSHConn: ssh.Client,
        // ...
    }

    SSHConnections = append(SSHConnections, newitem)
}
var connections []SSHConnections
for i := 0; i < len(ipaddr); i++ {
    // ...
    var newitem = SSHConnections{
        // Everything that's there now except the SSHConn ...
    }
    connections = append(connections, newitem)    
}
然后稍后调用
ssh.Dial
connection.NewClient
以获取可能会在
连接中结束的
ssh.Client
s


另外,您最好使用
SSHConn
SSConfig
SSHConnections
指针,因为这是软件包的界面想要使用的。请原谅我的无知,这是我第一次发布。到目前为止,您的帖子非常有用。我现在有一行代码有问题,但我不确定如何最好地显示我所有的新代码而不破坏OP并仍然保持上下文。基本上
var connections[i]。SSHConn,err:=ssh.Dial(“tcp”,connections[i]。Host+“:22”,connections[i]。SSHConfig)
正在抛出“语法错误:意外,预期类型”.thinks?
var
用于声明变量,如
var c ssh.Client
,不能将其与短赋值组合
:=
,也不能将
var
与结构字段(如
connections[i].SSHConn
)因为这不是一个变量。您可能想要在局部变量中构建所有片段,然后在一切都解决后将它们放入结构中,或者执行
var err error
来声明
err
变量,然后说
connections[i].SSHConn,err=…
等等。再次感谢。我不知道为什么我不能标记你的名字,因为某种原因它不是自动填充的。在你的输入和大量的谷歌搜索之后,我现在已经解决了所有问题。非常感谢。将更新OP。我会收到关于我答案的所有评论的通知(就像您收到关于您的问题的所有评论以及您的问题的答案的通知一样),这可能就是你不能-我的原因。这是一个语法错误,你忽略了错误消息中告诉你错误确切位置的部分。请当场校对代码。为混淆向@JimB道歉。我已经解决了我的问题,将为他人的潜在利益更新OP。