blob: ad62411e188502c62fe1c99ddb538ceac3ac2b3b [file] [log] [blame]
package devices
import (
"context"
"fmt"
"io"
"time"
"go.fuchsia.dev/infra/devices/power"
"go.fuchsia.dev/tools/netboot"
)
// Nuc represents an Intel NUC
type Nuc struct {
fuchsiaDevice
}
// NewNuc initializes a NUC for use by other infra utilites.
func NewNuc(config DeviceConfig, bootserverCmdStub []string) (*Nuc, error) {
n := &Nuc{
fuchsiaDevice{
power: power.NewAMTPowerManager(
config.Power.Host,
config.Power.Username,
config.Power.Password,
),
},
}
if err := initFuchsiaDevice(&n.fuchsiaDevice, config, bootserverCmdStub); err != nil {
return nil, err
}
// All transitions in a NUC attempt to get it into Zedboot.
transitionMap := map[DeviceState]*Transition{
Initial: &Transition{
PerformAction: n.sshRebootRecovery,
Validate: n.checkTransitionSuccess,
SuccessState: Healthy,
FailureState: "ssh_failed",
},
"ssh_failed": &Transition{
PerformAction: n.serialRebootRecovery,
Validate: n.checkTransitionSuccess,
SuccessState: Healthy,
FailureState: "serial_failed",
},
"serial_failed": &Transition{
PerformAction: n.Powercycle,
Validate: n.checkTransitionSuccess,
SuccessState: Healthy,
FailureState: Unrecoverable,
},
}
n.transitionMap = transitionMap
return n, nil
}
// checkSerial performs an echo test over serial.
func (n *Nuc) checkSerial(ctx context.Context) error {
if n.serial == nil {
return nil
}
cmdString := "echo hello"
resultString := "\r\n$ echo hello\r\nhello"
n.SendSerialCommand(ctx, cmdString)
buffer := make([]byte, len(resultString))
if _, err := io.ReadAtLeast(n.serial, buffer, len(resultString)); err != nil {
return err
}
if string(buffer) != resultString {
return fmt.Errorf("serial test got unexpected output: %s", string(buffer))
}
return nil
}
// checkTransitionSuccess is the validation function used by all transitions
// on a NUC. It checks that serial works and the NUC is in Zedboot.
func (n *Nuc) checkTransitionSuccess(ctx context.Context) error {
// Give the device time to transition.
time.Sleep(1 * time.Minute)
client := netboot.NewClient(netbootTimeout)
if err := n.checkSerial(ctx); err != nil {
return err
}
return deviceInZedboot(client, n.nodename)
}
// ToTaskState moves a NUC into a state in which it can run a test task.
func (n *Nuc) ToTaskState(ctx context.Context) error {
// This assumes that there is a NUC reboot server running to
// deliver the build's version of zedboot.
return n.Powercycle(ctx)
}