blob: 610c3a799abe77f3abb957a0c1b1099f3cb6a53b [file] [log] [blame]
package powercycle
import (
"bytes"
"fmt"
"io"
"strings"
"go.skia.org/infra/go/sklog"
"go.skia.org/infra/go/util"
"golang.org/x/crypto/ssh"
)
// For details about the Ubiquiti EdgeSwitch see
// https://dl.ubnt.com/guides/edgemax/EdgeSwitch_CLI_Command_Reference_UG.pdf
const (
// EdgeSwitch default user and password.
DEFAULT_USER = "ubnt"
)
// The CommandRunner interface adds a layer of abstraction around a device
// that can have commands executed on it.
type CommandRunner interface {
// ExecCmds executes a series of commands and returns the accumulated
// output of all commands.
ExecCmds(cmds []string) ([]string, error)
}
// EdgeSwitchClient implements the CommandRunner interface.
type EdgeSwitchClient struct {
ipaddress string
}
// NewEdgeSwitchClient connects to the EdgeSwitch identified by the given
// configuration and returns a new instance of EdgeSwitchClient.
func NewEdgeSwitchClient(ipaddress string) *EdgeSwitchClient {
return &EdgeSwitchClient{ipaddress: ipaddress}
}
// newClient returns a new ssh client.
func (e *EdgeSwitchClient) newClient() (*ssh.Client, error) {
sshConfig := &ssh.ClientConfig{
User: DEFAULT_USER,
Auth: []ssh.AuthMethod{ssh.Password(DEFAULT_USER)},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
client, err := ssh.Dial("tcp", e.ipaddress, sshConfig)
if err != nil {
return nil, err
}
return client, nil
}
// ExecCmds, see CommandRunner
func (e *EdgeSwitchClient) ExecCmds(cmds []string) ([]string, error) {
// The EdgeSwitch server doesn't like to re-use a client. So we create
// a new connection for every series of commands.
client, err := e.newClient()
if err != nil {
return nil, err
}
defer util.Close(client)
session, err := client.NewSession()
if err != nil {
return nil, err
}
defer util.Close(session)
// Set a terminal with many lines so we are not paginated.
if err := session.RequestPty("xterm", 80, 5000, nil); err != nil {
return nil, fmt.Errorf("Error: Could not retrieve pseudo terminal: %s", err)
}
stdinPipe, err := session.StdinPipe()
if err != nil {
return nil, err
}
stdoutPipe, err := session.StdoutPipe()
if err != nil {
return nil, err
}
if err := session.Shell(); err != nil {
return nil, err
}
// Switch to exec mode.
if _, err := stdinPipe.Write([]byte("enable\n")); err != nil {
return nil, err
}
// Execute the commands.
for _, cmd := range cmds {
sklog.Infof("Executing: %s", cmd)
if _, err := stdinPipe.Write([]byte(cmd + "\n")); err != nil {
return nil, err
}
}
// Switch out of exec mode and leave the shell.
if _, err := stdinPipe.Write([]byte("exit\nexit\n")); err != nil {
return nil, err
}
// Get the output and return it.
var buf bytes.Buffer
if _, err := io.Copy(&buf, stdoutPipe); err != nil {
return nil, err
}
// Strip out empty lines and all lines with the prompt.
lines := strings.Split(buf.String(), "\n")
ret := make([]string, 0, len(lines))
for _, line := range lines {
oneLine := strings.TrimSpace(line)
if (oneLine == "") || (strings.HasPrefix(oneLine, "(UBNT EdgeSwitch)")) {
continue
}
ret = append(ret, oneLine)
}
return ret, nil
}
// The following are convenience methods for common functionality and
// act as example usage.
// TurnOffPort disables PoE at the given port.
func TurnOffPort(client CommandRunner, port int) error {
_, err := client.ExecCmds([]string{
"configure",
"interface " + fmt.Sprintf("0/%d", port),
"poe opmode shutdown",
"exit", // leave the interface config mode (entered via 'interface ...')
"exit", // leave the global configuration mode (entered via 'configure')
})
return err
}
// TurnOnPort enables PoE at the given port.
func TurnOnPort(client CommandRunner, port int) error {
_, err := client.ExecCmds([]string{
"configure",
"interface " + fmt.Sprintf("0/%d", port),
"poe opmode auto",
"exit", // leave the interface config mode (entered via 'interface ...')
"exit", // leave the global configuration mode (entered via 'configure')
})
return err
}
// Ping runs a simple command to make sure the connection works.
func Ping(client CommandRunner) error {
sklog.Infof("Executing ping.")
output, err := client.ExecCmds([]string{
"show clock",
})
sklog.Infof("OUT:%s", strings.Join(output, "\n"))
return err
}