blob: c8895c8f085ae65cb57ceda52c390bfa04bb5de3 [file] [log] [blame]
package main
import (
"fmt"
"regexp"
"strconv"
"strings"
"go.skia.org/infra/skolo/go/powercycle"
)
// The BotNameGetter interface abstracts the logic to collect the EdgeSwitch ports
// to which our devices are connected.
type BotPortGetter interface {
// Collects and returns a list of Bot with just the MAC addresses
// and port numbers. This is a cheap call (<10 seconds).
GetBotPortsAddresses() ([]Bot, error)
}
// Implements BotPortGetter
type edgeswitchBotSource struct {
client powercycle.CommandRunner
}
// NewEdgeSwitchBotPortGetter returns an implementation of BotPortGetter
func NewEdgeSwitchBotPortGetter(address string) *edgeswitchBotSource {
return &edgeswitchBotSource{client: powercycle.NewEdgeSwitchClient(address)}
}
// GetBotPortsAddresses, see BotPortGetter
func (e *edgeswitchBotSource) GetBotPortsAddresses() ([]Bot, error) {
lines, err := e.client.ExecCmds([]string{"show mac-addr-table all"})
if err != nil {
return nil, err
}
bots, err := parseSSHResult(lines)
if err != nil {
return nil, err
}
return dedupeBots(bots), nil
}
var edgeswitchLine = regexp.MustCompile(`^\S+\s+(?P<mac_address>[0-9A-Fa-f\:]+)\s+\S+\s+(?P<interface>\d+)\s+\S+`)
// parseSSHResult looks at the lines output by the EdgeSwitchClient. These are
// already split by \n. It then parses the lines into the various components.
// See the unit tests for an example of what this data looks like.
func parseSSHResult(lines []string) ([]Bot, error) {
bots := []Bot{}
for _, l := range lines {
if matches := edgeswitchLine.FindStringSubmatch(l); matches != nil {
port, err := strconv.ParseInt(matches[2], 10, 0)
if err != nil {
return nil, fmt.Errorf("Unexpected formatting error. %s is not an int: %s", matches[2], err)
}
bots = append(bots, Bot{MACAddress: strings.ToUpper(matches[1]), Port: int(port)})
}
}
return bots, nil
}
// dedupeBots filters out the list of bots such that only bots whose port
// assignments are unique are in the list. Bots with duplicate ports are
// likely not directly attached to this switch.
func dedupeBots(bots []Bot) []Bot {
uniquePorts := map[int]bool{}
for _, b := range bots {
if _, ok := uniquePorts[b.Port]; ok {
uniquePorts[b.Port] = false
} else {
uniquePorts[b.Port] = true
}
}
unique := []Bot{}
for _, b := range bots {
if uniquePorts[b.Port] {
unique = append(unique, b)
}
}
return unique
}