blob: 0ee0142f83f8137d1f122a3e9ecaa00dfdff13d3 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package harness
import (
"context"
"log"
"go.chromium.org/luci/common/errors"
"infra/libs/skylab/inventory"
"infra/libs/skylab/autotest/hostinfo"
"infra/cmd/skylab_swarming_worker/internal/swmbot"
h_hostinfo "infra/cmd/skylab_swarming_worker/internal/swmbot/harness/hostinfo"
"infra/cmd/skylab_swarming_worker/internal/swmbot/harness/labelupdater"
"infra/cmd/skylab_swarming_worker/internal/swmbot/harness/localdutinfo"
"infra/cmd/skylab_swarming_worker/internal/swmbot/harness/resultsdir"
"infra/cmd/skylab_swarming_worker/internal/swmbot/harness/ufsdutinfo"
)
// DUTHarness holds information about a DUT's harness
type DUTHarness struct {
BotInfo *swmbot.Info
DUTID string
DUTHostname string
ResultsDir string
LocalState *swmbot.LocalDUTState
labelUpdater *labelupdater.LabelUpdater
// err tracks errors during setup to simplify error handling logic.
err error
closers []closer
}
// Close closes and flushes out the harness resources. This is safe
// to call multiple times.
func (dh *DUTHarness) Close(ctx context.Context) error {
log.Printf("Wrapping up harness for %s", dh.DUTHostname)
var errs []error
for n := len(dh.closers) - 1; n >= 0; n-- {
if err := dh.closers[n].Close(ctx); err != nil {
errs = append(errs, err)
}
}
if len(errs) > 0 {
return errors.Annotate(errors.MultiError(errs), "close harness").Err()
}
return nil
}
func makeDUTHarness(b *swmbot.Info) *DUTHarness {
return &DUTHarness{
BotInfo: b,
labelUpdater: &labelupdater.LabelUpdater{
BotInfo: b,
},
}
}
func (dh *DUTHarness) loadLocalDUTInfo(ctx context.Context) {
if dh.err != nil {
return
}
if dh.DUTHostname == "" {
dh.err = errors.Reason("DUTHostname cannot be blank").Err()
return
}
ldi, err := localdutinfo.Open(ctx, dh.BotInfo, dh.DUTHostname)
if err != nil {
dh.err = err
return
}
dh.closers = append(dh.closers, ldi)
dh.LocalState = &ldi.LocalDUTState
}
func (dh *DUTHarness) loadUFSDUTInfo(ctx context.Context) (*inventory.DeviceUnderTest, map[string]string) {
if dh.err != nil {
return nil, nil
}
var s *ufsdutinfo.Store
if dh.DUTID != "" {
s, dh.err = ufsdutinfo.LoadByID(ctx, dh.BotInfo, dh.DUTID, dh.labelUpdater.Update)
} else if dh.DUTHostname != "" {
s, dh.err = ufsdutinfo.LoadByHostname(ctx, dh.BotInfo, dh.DUTHostname, dh.labelUpdater.Update)
} else {
dh.err = errors.Reason("Both DUTID and DUTHostname field is empty.").Err()
}
if dh.err != nil {
return nil, nil
}
// We overwrite both DUTHostname and DUTID based on UFS data because in
// single DUT tasks we don't have DUTHostname when we start, and in the
// scheduling_unit (multi-DUT) tasks we don't have DUTID when we start.
dh.DUTHostname = s.DUT.GetCommon().GetHostname()
dh.DUTID = s.DUT.GetCommon().GetId()
dh.closers = append(dh.closers, s)
return s.DUT, s.StableVersions
}
func (dh *DUTHarness) makeHostInfo(d *inventory.DeviceUnderTest, stableVersion map[string]string) *hostinfo.HostInfo {
if dh.err != nil {
return nil
}
hip := h_hostinfo.FromDUT(d, stableVersion)
dh.closers = append(dh.closers, hip)
return hip.HostInfo
}
func (dh *DUTHarness) addLocalStateToHostInfo(hi *hostinfo.HostInfo) {
if dh.err != nil {
return
}
hib := h_hostinfo.BorrowLocalDUTState(hi, dh.LocalState)
dh.closers = append(dh.closers, hib)
}
func (dh *DUTHarness) makeDUTResultsDir(d *resultsdir.Dir) {
if dh.err != nil {
return
}
path, err := d.OpenSubDir(dh.DUTHostname)
if err != nil {
dh.err = err
return
}
log.Printf("Created DUT level results sub-dir %s", path)
dh.ResultsDir = path
}
func (dh *DUTHarness) exposeHostInfo(hi *hostinfo.HostInfo) {
if dh.err != nil {
return
}
hif, err := h_hostinfo.Expose(hi, dh.ResultsDir, dh.DUTHostname)
if err != nil {
dh.err = err
return
}
dh.closers = append(dh.closers, hif)
}