blob: 81624dabe34c40a86e3c59fe788e65725b9206b4 [file] [log] [blame]
package devices
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"golang.org/x/crypto/ssh"
)
// DeviceConfig contains the static properties of a target device.
type DeviceConfig struct {
// Type tells us what kind of machine this is.
Type string `json:"type"`
// Network is the network properties of the target.
Network *NetworkProperties `json:"network"`
// Power is the attached power management configuration.
Power *PowerClient `json:"power,omitempty"`
// SSHKeys are the default system keys to be used with the device.
SSHKeys []string `json:"keys,omitempty"`
// Serial is the path to the device file for serial i/o.
Serial string `json:"serial,omitempty"`
}
// NetworkProperties are the static network properties of a target.
type NetworkProperties struct {
// Nodename is the hostname of the device that we want to boot on.
Nodename string `json:"nodename"`
// IPv4Addr is the IPv4 address, if statically given. If not provided, it may be
// resolved via the netstack's MDNS server.
IPv4Addr string `json:"ipv4"`
// Mac is the mac address of the device
Mac string `json:"mac"`
// The network interface used to communicate with this device.
Interface string `json:"interface"`
}
// PowerClient represents a power management configuration for a particular device.
type PowerClient struct {
// Type is the type of manager to use.
Type string `json:"type"`
// Host is the network hostname of the manager
Host string `json:"host"`
// Username is the username used to log in to the manager.
Username string `json:"username"`
// Password is the password used to log in to the manager..
Password string `json:"password"`
// PDUIp is the IP address of the pdu
PDUIp string `json:"pduIp"`
// PDUPort is the port the PDU uses to connect to this device.
PDUPort string `json:"pduPort"`
}
// Device represents a generic device in our infrastructure.
// TODO(rudymathu) This interface should eventually be pared down to:
// State()
// Nodename()
// Transition()
type Device interface {
State() DeviceState
Nodename() string
Mac() string
Interface() string
HasSerial() bool
Transition(context.Context) error
ToTaskState(context.Context) error
Powercycle(context.Context) error
SoftReboot(context.Context, string, string) error
SendSerialCommand(context.Context, string) error
ReadSerialData(context.Context, []byte) error
}
// LoadDeviceConfigs unmarshalls a slice of device configs from a given file.
func LoadDeviceConfigs(path string) ([]DeviceConfig, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read device properties file %q", path)
}
var configs []DeviceConfig
if err := json.Unmarshal(data, &configs); err != nil {
return nil, fmt.Errorf("failed to unmarshal configs: %v", err)
}
return configs, nil
}
// CreateDevices loads configs from the given file and creates an appropriate Device for each one.
func CreateDevices(ctx context.Context, deviceConfigs []DeviceConfig, bootserverCmdStub []string) ([]Device, error) {
var devices []Device
for _, deviceConfig := range deviceConfigs {
var device Device
var err error
switch deviceConfig.Type {
case "Intel NUC Kit NUC7i5DNHE":
device, err = NewNuc(deviceConfig, bootserverCmdStub)
case "Khadas Vim2 Max":
device, err = NewVim(deviceConfig, bootserverCmdStub)
case "Astro":
fallthrough
case "Sherlock":
fallthrough
case "Nelson":
device, err = NewArmCDCether(deviceConfig, bootserverCmdStub)
}
if err != nil {
return nil, err
}
devices = append(devices, device)
}
return devices, nil
}
// parseOutSigners parses out ssh signers from the given key files.
func parseOutSigners(keyPaths []string) ([]ssh.Signer, error) {
if len(keyPaths) == 0 {
return nil, errors.New("must supply SSH keys in the config")
}
var keys [][]byte
for _, keyPath := range keyPaths {
p, err := ioutil.ReadFile(keyPath)
if err != nil {
return nil, fmt.Errorf("could not read SSH key file %q: %v", keyPath, err)
}
keys = append(keys, p)
}
var signers []ssh.Signer
for _, p := range keys {
signer, err := ssh.ParsePrivateKey(p)
if err != nil {
return nil, err
}
signers = append(signers, signer)
}
return signers, nil
}