[botanist][serial] Allow reading of serial from DUTs.
This allows reading logs over serial iff the Serial key is specified in
the botanist config, and the invocation specifies a path to write a
serial log.
Change-Id: I34cf5a5be4964e6755e5a5c18b776b572be84ed7
diff --git a/botanist/target/device.go b/botanist/target/device.go
index dadafdb..01e7281 100644
--- a/botanist/target/device.go
+++ b/botanist/target/device.go
@@ -37,6 +37,9 @@
// 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.
@@ -102,6 +105,11 @@
return botanist.ResolveIPv4(context.Background(), t.Nodename(), netstackTimeout)
}
+// Serial returns the serial device associated with the target for serial i/o.
+func (t *DeviceTarget) Serial() string {
+ return t.config.Serial
+}
+
// SSHKey returns the private SSH key path associated with the authorized key to be paved.
func (t *DeviceTarget) SSHKey() string {
return t.config.SSHKeys[0]
diff --git a/botanist/target/qemu.go b/botanist/target/qemu.go
index 7b8f816..77f368a 100644
--- a/botanist/target/qemu.go
+++ b/botanist/target/qemu.go
@@ -100,6 +100,11 @@
return nil, nil
}
+// Serial returns the serial device associated with the target for serial i/o.
+func (t *QEMUTarget) Serial() string {
+ return ""
+}
+
// SSHKey returns the private SSH key path associated with the authorized key to be pavet.
func (t *QEMUTarget) SSHKey() string {
return t.opts.SSHKey
diff --git a/cmd/botanist/run.go b/cmd/botanist/run.go
index a3839d2..fdd9421 100644
--- a/cmd/botanist/run.go
+++ b/cmd/botanist/run.go
@@ -20,6 +20,7 @@
"fuchsia.googlesource.com/tools/command"
"fuchsia.googlesource.com/tools/logger"
"fuchsia.googlesource.com/tools/runner"
+ "fuchsia.googlesource.com/tools/serial"
"fuchsia.googlesource.com/tools/sshutil"
"github.com/google/subcommands"
@@ -37,6 +38,9 @@
// IPv4Addr returns the IPv4 address of the target.
IPv4Addr() (net.IP, error)
+ // Serial returns the serial device associated with the target for serial i/o.
+ Serial() string
+
// SSHKey returns the private key corresponding an authorized SSH key of the target.
SSHKey() string
@@ -84,8 +88,11 @@
// SysloggerFile, if nonempty, is the file to where the system's logs will be written.
syslogFile string
- // sshKey is the path to a private SSH user key.
+ // SshKey is the path to a private SSH user key.
sshKey string
+
+ // SerialLogFile, if nonempty, is the file where the system's serial logs will be written.
+ serialLogFile string
}
func (*RunCommand) Name() string {
@@ -115,6 +122,7 @@
f.StringVar(&r.cmdStderr, "stderr", "", "file to redirect the command's stderr into; if unspecified, it will be redirected to the process' stderr")
f.StringVar(&r.syslogFile, "syslog", "", "file to write the systems logs to")
f.StringVar(&r.sshKey, "ssh", "", "file containing a private SSH user key; if not provided, a private key will be generated.")
+ f.StringVar(&r.serialLogFile, "serial-log", "", "file to write the serial logs to.")
}
func (r *RunCommand) runCmd(ctx context.Context, args []string, t Target, syslog io.Writer) error {
@@ -235,6 +243,38 @@
defer syslog.Close()
}
+ if t.Serial() != "" && r.serialLogFile != "" {
+ serialLog, err := os.Create(r.serialLogFile)
+ if err != nil {
+ return err
+ }
+ defer serialLog.Close()
+
+ serialDevice, err := serial.Open(t.Serial())
+ if err != nil {
+ return fmt.Errorf("unable to open %s: %v", t.Serial(), err)
+ }
+ defer serialDevice.Close()
+
+ // Here we invoke the `dlog` command over serial to tail the existing log buffer into the
+ // output file. This should give us everything since Zedboot boot, and new messages should
+ // be written to directly to the serial port without needing to tail with `dlog -f`.
+ if _, err = io.WriteString(serialDevice, "\ndlog\n"); err != nil {
+ logger.Errorf(ctx, "failed to tail zedboot dlog: %v", err)
+ }
+
+ go func() {
+ _, err := io.Copy(serialLog, serialDevice)
+ if err != nil {
+ logger.Errorf(ctx, "failed to write serial log: %v", err)
+ }
+ }()
+
+ // Modify the zirconArgs passed to the kernel on boot to enable serial logging on x64.
+ // arm64 devices should already be enabling kernel.serial at compile time.
+ r.zirconArgs = append(r.zirconArgs, "kernel.bypass-debuglog=true", "kernel.serial=legacy")
+ }
+
defer func() {
logger.Debugf(ctx, "stopping or rebooting the node %q\n", t.Nodename())
if err := t.Stop(ctx); err == target.ErrUnimplemented {