More of Go
I have already migrated some scripts from python to go, mostly with API. Decided to check how to SSH into network devices and run some commands. Of course, I used the well-known cisco always-on sandbox.
note: using some code as a core and as an example (link in the comments
Everything is pretty straightforward here:
var (
User string = "developer"
Password string = "C1sco12345"
hosts = []string{"sandbox-iosxe-latest-1.cisco.com", "sandbox-iosxe-recomm-1.cisco.com"}
cmds = []string{"show ip route | i 0.0.0.0/0", "show ip arp"}
)
Execute commands for each host and keep the output into the channel: results := make(chan string, 100)
for _, hostname := range hosts {
go func(hostname string) {
results <- executeCmd(hostname, cmds, config)
}(hostname)
}
This part is to handle “More” while running the command:
for _, cmd := range cmds {
stdinBuf.Write([]byte(cmd + "n"))
for {
stdoutBuf := make([]byte, 1000000)
time.Sleep(time.Millisecond * 700)
byteCount, err := stdBuf.Read(stdoutBuf)
if err != nil {
log.Fatal(err)
}
cmd_output += string(stdoutBuf[:byteCount])
if !(strings.Contains(string(stdoutBuf[:byteCount]), "More")) {
break
}
stdinBuf.Write([]byte(" "))
}
}
Action!
From here, the one and only regex will help to do anything.
The code:
package main
import (
"fmt"
"log"
"strings"
"time"
"golang.org/x/crypto/ssh"
)
func TimeTrack(start time.Time, name string) {
elapsed := time.Since(start)
log.Printf("%s took %s", name, elapsed)
}
func executeCmd(hostname string, cmds []string, config *ssh.ClientConfig) string {
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
}
conn, err := ssh.Dial("tcp", hostname+":22", config)
if err != nil {
log.Println(err)
}
session, err := conn.NewSession()
if err != nil {
log.Fatal(err)
}
// You can use session.Run() here but that only works
// if you need a run a single command or you commands
// are independent of each other.
err = session.RequestPty("xterm", 80, 40, modes)
if err != nil {
log.Fatalf("request for pseudo terminal failed: %s", err)
}
stdBuf, err := session.StdoutPipe()
if err != nil {
log.Fatalf("request for stdout pipe failed: %s", err)
}
stdinBuf, err := session.StdinPipe()
if err != nil {
log.Fatalf("request for stdin pipe failed: %s", err)
}
err = session.Shell()
if err != nil {
log.Fatalf("failed to start shell: %s", err)
}
var cmd_output string
for _, cmd := range cmds {
stdinBuf.Write([]byte(cmd + "\n"))
for {
stdoutBuf := make([]byte, 1000000)
time.Sleep(time.Millisecond * 700)
byteCount, err := stdBuf.Read(stdoutBuf)
if err != nil {
log.Fatal(err)
}
cmd_output += string(stdoutBuf[:byteCount])
if !(strings.Contains(string(stdoutBuf[:byteCount]), "More")) {
break
}
stdinBuf.Write([]byte(" "))
}
}
return cmd_output
}
func main() {
defer TimeTrack(time.Now(), "Cisco Sandbox Device Access using GO")
var (
User string = "developer"
Password string = "C1sco12345"
hosts = []string{"sandbox-iosxe-latest-1.cisco.com", "sandbox-iosxe-recomm-1.cisco.com"}
cmds = []string{"show ip route | i 0.0.0.0/0", "show ip arp"}
)
outStrings := make(map[string]string)
results := make(chan string, 100)
config := &ssh.ClientConfig{
User: User,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Auth: []ssh.AuthMethod{
ssh.Password(Password),
},
}
for _, hostname := range hosts {
go func(hostname string) {
results <- executeCmd(hostname, cmds, config)
}(hostname)
}
for i := 0; i < len(hosts); i++ {
res := <-results
outStrings[hosts[i]] = res
}
for _, device_output := range outStrings {
fmt.Printf("%s", device_output)
fmt.Printf("n================================nn")
}
}
Used this link as a reference