blob: 14af0485a56b6c0c65d6e16871ab484e74f6784d [file] [log] [blame]
// Copyright 2020 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 dut
import (
"context"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/maruel/subcommands"
"go.chromium.org/luci/auth/client/authcli"
"go.chromium.org/luci/common/cli"
"go.chromium.org/luci/grpc/prpc"
"google.golang.org/genproto/protobuf/field_mask"
"infra/cmd/shivas/cmdhelp"
"infra/cmd/shivas/site"
"infra/cmd/shivas/utils"
"infra/cmdsupport/cmdlib"
swarming "infra/libs/swarming"
ufspb "infra/unifiedfleet/api/v1/models"
chromeosLab "infra/unifiedfleet/api/v1/models/chromeos/lab"
ufsAPI "infra/unifiedfleet/api/v1/rpc"
ufsUtil "infra/unifiedfleet/app/util"
)
// Regexp to enforce the input format
var servoHostPortRegexp = regexp.MustCompile(`^[a-zA-Z0-9\-\.]+:[0-9]+$`)
var defaultDeployTaskActions = []string{"servo-verification", "update-label", "verify-recovery-mode", "run-pre-deploy-verification"}
// TODO(anushruth): Find a better place to put these tags.
var shivasTags = []string{"shivas:" + site.VersionNumber, "triggered_using:shivas"}
// defaultPools contains the list of pools used by default.
var defaultPools = []string{"DUT_POOL_QUOTA"}
// defaultSwarmingPool is the swarming pool used for all DUTs.
var defaultSwarmingPool = "ChromeOSSkylab"
// AddDUTCmd adds a MachineLSE to the database. And starts a swarming job to deploy.
var AddDUTCmd = &subcommands.Command{
UsageLine: "dut [options ...]",
ShortDesc: "Deploy a DUT",
LongDesc: cmdhelp.AddDUTLongDesc,
CommandRun: func() subcommands.CommandRun {
c := &addDUT{
pools: []string{},
chameleons: []string{},
cameras: []string{},
cables: []string{},
deployTags: shivasTags,
deployActions: defaultDeployTaskActions,
}
c.authFlags.Register(&c.Flags, site.DefaultAuthOptions)
c.envFlags.Register(&c.Flags)
c.commonFlags.Register(&c.Flags)
c.Flags.StringVar(&c.newSpecsFile, "f", "", cmdhelp.DUTRegistrationFileText)
// Asset location fields
c.Flags.StringVar(&c.zone, "zone", "", "Zone that the asset is in. "+cmdhelp.ZoneFilterHelpText)
c.Flags.StringVar(&c.rack, "rack", "", "Rack that the asset is in.")
// DUT/MachineLSE common fields
c.Flags.StringVar(&c.hostname, "name", "", "hostname of the DUT.")
c.Flags.StringVar(&c.asset, "asset", "", "asset tag of the machine.")
c.Flags.StringVar(&c.servo, "servo", "", "servo hostname and port as hostname:port. (port is assigned by UFS if missing)")
c.Flags.StringVar(&c.servoSerial, "servo-serial", "", "serial number for the servo. Can skip for Servo V3.")
c.Flags.StringVar(&c.servoSetupType, "servo-setup", "", "servo setup type. Allowed values are "+cmdhelp.ServoSetupTypeAllowedValuesString()+", UFS assigns REGULAR if unassigned.")
c.Flags.Var(utils.CSVString(&c.pools), "pools", "comma separated pools assigned to the DUT. 'DUT_POOL_QUOTA' is used if nothing is specified")
c.Flags.Var(utils.CSVString(&c.licenseTypes), "licensetype", cmdhelp.LicenseTypeHelpText)
c.Flags.Var(utils.CSVString(&c.licenseIds), "licenseid", "the name of the license type. Can specify multiple comma separated values.")
c.Flags.StringVar(&c.rpm, "rpm", "", "rpm assigned to the DUT.")
c.Flags.StringVar(&c.rpmOutlet, "rpm-outlet", "", "rpm outlet used for the DUT.")
c.Flags.Int64Var(&c.deployTaskTimeout, "deploy-timeout", swarming.DeployTaskExecutionTimeout, "execution timeout for deploy task in seconds.")
c.Flags.BoolVar(&c.ignoreUFS, "ignore-ufs", false, "skip updating UFS create a deploy task.")
c.Flags.Var(utils.CSVString(&c.deployTags), "deploy-tags", "comma separated tags for deployment task.")
c.Flags.BoolVar(&c.deploySkipDownloadImage, "deploy-skip-download-image", false, "skips downloading image and staging usb")
c.Flags.BoolVar(&c.deploySkipInstallFirmware, "deploy-skip-install-fw", false, "skips installing firmware")
c.Flags.BoolVar(&c.deploySkipInstallOS, "deploy-skip-install-os", false, "skips installing os image")
c.Flags.StringVar(&c.deploymentTicket, "ticket", "", "the deployment ticket for this machine.")
c.Flags.Var(utils.CSVString(&c.tags), "tags", "comma separated tags.")
c.Flags.StringVar(&c.state, "state", "", cmdhelp.StateHelp)
c.Flags.StringVar(&c.description, "desc", "", "description for the machine.")
// ACS DUT fields
c.Flags.Var(utils.CSVString(&c.chameleons), "chameleons", cmdhelp.ChameleonTypeHelpText)
c.Flags.Var(utils.CSVString(&c.cameras), "cameras", cmdhelp.CameraTypeHelpText)
c.Flags.Var(utils.CSVString(&c.cables), "cables", cmdhelp.CableTypeHelpText)
c.Flags.StringVar(&c.antennaConnection, "antennaconnection", "", cmdhelp.AntennaConnectionHelpText)
c.Flags.StringVar(&c.router, "router", "", cmdhelp.RouterHelpText)
c.Flags.StringVar(&c.facing, "facing", "", cmdhelp.FacingHelpText)
c.Flags.StringVar(&c.light, "light", "", cmdhelp.LightHelpText)
c.Flags.StringVar(&c.carrier, "carrier", "", "name of the carrier.")
c.Flags.BoolVar(&c.audioBoard, "audioboard", false, "adding this flag will specify if audioboard is present")
c.Flags.BoolVar(&c.audioBox, "audiobox", false, "adding this flag will specify if audiobox is present")
c.Flags.BoolVar(&c.atrus, "atrus", false, "adding this flag will specify if atrus is present")
c.Flags.BoolVar(&c.wifiCell, "wificell", false, "adding this flag will specify if wificell is present")
c.Flags.BoolVar(&c.touchMimo, "touchmimo", false, "adding this flag will specify if touchmimo is present")
c.Flags.BoolVar(&c.cameraBox, "camerabox", false, "adding this flag will specify if camerabox is present")
c.Flags.BoolVar(&c.chaos, "chaos", false, "adding this flag will specify if chaos is present")
c.Flags.BoolVar(&c.audioCable, "audiocable", false, "adding this flag will specify if audiocable is present")
c.Flags.BoolVar(&c.smartUSBHub, "smartusbhub", false, "adding this flag will specify if smartusbhub is present")
// Machine fields
// crbug.com/1188488 showed us that it might be wise to add model/board during deployment if required.
c.Flags.StringVar(&c.model, "model", "", "model of the DUT undergoing deployment. If not given, HaRT data is used. Fails if model is not known for the DUT")
c.Flags.StringVar(&c.board, "board", "", "board of the DUT undergoing deployment. If not given, HaRT data is used. Fails if board is not known for the DUT")
return c
},
}
type addDUT struct {
subcommands.CommandRunBase
authFlags authcli.Flags
envFlags site.EnvFlags
commonFlags site.CommonFlags
newSpecsFile string
hostname string
asset string
servo string
servoSerial string
servoSetupType string
licenseTypes []string
licenseIds []string
pools []string
rpm string
rpmOutlet string
ignoreUFS bool
deployTaskTimeout int64
deployActions []string
deployTags []string
deploySkipDownloadImage bool
deploySkipInstallOS bool
deploySkipInstallFirmware bool
deploymentTicket string
tags []string
state string
description string
// Asset location fields
zone string
rack string
// ACS DUT fields
chameleons []string
cameras []string
antennaConnection string
router string
cables []string
facing string
light string
carrier string
audioBoard bool
audioBox bool
atrus bool
wifiCell bool
touchMimo bool
cameraBox bool
chaos bool
audioCable bool
smartUSBHub bool
// Machine specific fields
model string
board string
}
var mcsvFields = []string{
"name",
"asset",
"model",
"board",
"servo_host",
"servo_port",
"servo_serial",
"servo_setup",
"rpm_host",
"rpm_outlet",
"pools",
}
// dutDeployUFSParams contains all the data that are needed for deployment of a single DUT
// Asset and its update paths are required here to update location, model and board for the DUT
// See: crbug.com/1188488 for why model and board need to be updated.
type dutDeployUFSParams struct {
DUT *ufspb.MachineLSE // MachineLSE of the DUT to be updated
Asset *ufspb.Asset // Asset underlying the DUT being updated
Paths []string // Update paths for the Asset being updated
}
func (c *addDUT) Run(a subcommands.Application, args []string, env subcommands.Env) int {
if err := c.innerRun(a, args, env); err != nil {
cmdlib.PrintError(a, err)
return 1
}
return 0
}
func (c *addDUT) innerRun(a subcommands.Application, args []string, env subcommands.Env) error {
if err := c.validateArgs(); err != nil {
return err
}
ctx := cli.GetContext(a, c, env)
ctx = utils.SetupContext(ctx, ufsUtil.OSNamespace)
hc, err := cmdlib.NewHTTPClient(ctx, &c.authFlags)
if err != nil {
return err
}
e := c.envFlags.Env()
if c.commonFlags.Verbose() {
if !c.ignoreUFS {
fmt.Printf("Using UFS service %s \n", e.UnifiedFleetService)
}
fmt.Printf("Using swarming service %s \n", e.SwarmingService)
}
tc, err := swarming.NewTaskCreator(ctx, &c.authFlags, e.SwarmingService)
if err != nil {
return err
}
tc.LogdogService = e.LogdogService
tc.SwarmingServiceAccount = e.SwarmingServiceAccount
dutParams, err := c.parseArgs()
if err != nil {
return err
}
c.updateDeployActions()
// Update the UFS database if enabled.
if !c.ignoreUFS {
ic := ufsAPI.NewFleetPRPCClient(&prpc.Client{
C: hc,
Host: e.UnifiedFleetService,
Options: site.DefaultPRPCOptions,
})
for _, param := range dutParams {
if len(param.DUT.GetMachines()) == 0 {
fmt.Printf("Failed to add DUT %s to UFS. It is not linked to any Asset(Machine).\n", param.DUT.GetName())
continue
}
if err := c.addDutToUFS(ctx, ic, param); err != nil {
fmt.Printf("Failed to add DUT %s to UFS. Skipping deployment. %s", param.DUT.GetName(), err.Error())
// skip deployment
continue
}
c.deployDutToSwarming(ctx, tc, param.DUT)
}
if len(dutParams) > 1 {
fmt.Fprintf(a.GetOut(), "\nBatch tasks URL: %s\n\n", tc.SessionTasksURL())
}
return nil
}
// Run the deployment task
for _, param := range dutParams {
c.deployDutToSwarming(ctx, tc, param.DUT)
}
return nil
}
func (c addDUT) validateArgs() error {
if !c.ignoreUFS && c.newSpecsFile == "" {
if c.asset == "" {
return cmdlib.NewQuietUsageError(c.Flags, "Need asset ID to create a DUT")
}
if c.servo == "" {
return cmdlib.NewQuietUsageError(c.Flags, "Need servo config to create a DUT")
}
if c.servo != "" {
// If the servo is not servo V3. Then servo serial is needed
host, _, err := parseServoHostnamePort(c.servo)
if err != nil {
return err
}
if !ufsUtil.ServoV3HostnameRegex.MatchString(host) && c.servoSerial == "" {
return cmdlib.NewQuietUsageError(c.Flags, "Cannot skip servo serial. Not a servo V3 device.")
}
}
if c.servoSetupType != "" {
if _, ok := chromeosLab.ServoSetupType_value[appendServoSetupPrefix(c.servoSetupType)]; !ok {
return cmdlib.NewQuietUsageError(c.Flags, "Invalid servo setup %s", c.servoSetupType)
}
}
if (c.rpm != "" && c.rpmOutlet == "") || (c.rpm == "" && c.rpmOutlet != "") {
return cmdlib.NewQuietUsageError(c.Flags, "Need both rpm and its outlet. %s:%s is invalid", c.rpm, c.rpmOutlet)
}
if c.zone != "" && !ufsUtil.IsUFSZone(ufsUtil.RemoveZonePrefix(c.zone)) {
return cmdlib.NewQuietUsageError(c.Flags, "Invalid zone %s", c.zone)
}
if len(c.licenseTypes) != len(c.licenseIds) {
return cmdlib.NewQuietUsageError(c.Flags, "Wrong usage!!\nNumber of -licensetype(%s) and -licenseid(%s) must be same.", c.licenseTypes, c.licenseIds)
}
for _, cp := range c.licenseTypes {
if !ufsUtil.IsLicenseType(cp) {
return cmdlib.NewQuietUsageError(c.Flags, "Wrong usage!!\n%s is not a valid license type name, please check help info for '-licensetype'.", cp)
}
}
for _, cp := range c.chameleons {
if !ufsUtil.IsChameleonType(cp) {
return cmdlib.NewQuietUsageError(c.Flags, "Wrong usage!!\n%s is not a valid chameleon type name, please check help info for '-chameleons'.", cp)
}
}
for _, cp := range c.cameras {
if !ufsUtil.IsCameraType(cp) {
return cmdlib.NewQuietUsageError(c.Flags, "Wrong usage!!\n%s is not a valid camera type name, please check help info for '-cameras'.", cp)
}
}
for _, cp := range c.cables {
if !ufsUtil.IsCableType(cp) {
return cmdlib.NewQuietUsageError(c.Flags, "Wrong usage!!\n%s is not a valid cable type name, please check help info for '-cables'.", cp)
}
}
if c.antennaConnection != "" && !ufsUtil.IsAntennaConnection(c.antennaConnection) {
return cmdlib.NewQuietUsageError(c.Flags, "Wrong usage!!\n%s is not a valid antenna connection name, please check help info for '-antennaconnection'.", c.antennaConnection)
}
if c.router != "" && !ufsUtil.IsRouter(c.router) {
return cmdlib.NewQuietUsageError(c.Flags, "Wrong usage!!\n%s is not a valid router name, please check help info for '-router'.", c.router)
}
if c.facing != "" && !ufsUtil.IsFacing(c.facing) {
return cmdlib.NewQuietUsageError(c.Flags, "Wrong usage!!\n%s is not a valid facing name, please check help info for '-facing'.", c.facing)
}
if c.light != "" && !ufsUtil.IsLight(c.light) {
return cmdlib.NewQuietUsageError(c.Flags, "Wrong usage!!\n%s is not a valid light name, please check help info for '-light'.", c.light)
}
}
if c.newSpecsFile == "" && c.hostname == "" {
return cmdlib.NewQuietUsageError(c.Flags, "Need hostname to create a DUT")
}
return nil
}
func (c *addDUT) parseArgs() ([]*dutDeployUFSParams, error) {
if c.newSpecsFile != "" {
if utils.IsCSVFile(c.newSpecsFile) {
return c.parseMCSV()
}
machinelse := &ufspb.MachineLSE{}
if err := utils.ParseJSONFile(c.newSpecsFile, machinelse); err != nil {
return nil, err
}
asset, paths, err := utils.GenerateAssetUpdate(machinelse.GetName(), machinelse.GetMachines()[0], "", "", "", "")
if err != nil {
return nil, err
}
return []*dutDeployUFSParams{{
DUT: machinelse,
Asset: asset,
Paths: paths,
}}, nil
}
// command line parameters
dutParams, err := c.initializeLSEAndAsset(nil)
if err != nil {
return nil, err
}
return []*dutDeployUFSParams{dutParams}, nil
}
// parseMCSV parses the MCSV file and returns MachineLSEs
func (c *addDUT) parseMCSV() ([]*dutDeployUFSParams, error) {
records, err := utils.ParseMCSVFile(c.newSpecsFile)
if err != nil {
return nil, err
}
var dutParams []*dutDeployUFSParams
for i, rec := range records {
// if i is 1, determine whether this is a header
if i == 0 && utils.LooksLikeHeader(rec) {
if err := utils.ValidateSameStringArray(mcsvFields, rec); err != nil {
return nil, err
}
continue
}
recMap := make(map[string]string)
for j, title := range mcsvFields {
recMap[title] = rec[j]
}
p, err := c.initializeLSEAndAsset(recMap)
if err != nil {
fmt.Printf("Error [%s:%v]: %v. Skipping add on this line\n", c.newSpecsFile, i+1, err.Error())
} else {
dutParams = append(dutParams, p)
}
}
return dutParams, nil
}
func (c *addDUT) addDutToUFS(ctx context.Context, ic ufsAPI.FleetClient, param *dutDeployUFSParams) error {
// Attempt to update the changes to asset first.
if err := c.updateAssetToUFS(ctx, ic, param.Asset, param.Paths); err != nil {
return err
}
if !ufsUtil.ValidateTags(param.DUT.Tags) {
return fmt.Errorf(ufsAPI.InvalidTags)
}
res, err := ic.CreateMachineLSE(ctx, &ufsAPI.CreateMachineLSERequest{
MachineLSE: param.DUT,
MachineLSEId: param.DUT.GetName(),
})
if err != nil {
fmt.Printf("Failed to add DUT %s to UFS. UFS add failed %s\n", param.DUT.GetName(), err)
return err
}
res.Name = ufsUtil.RemovePrefix(res.Name)
utils.PrintProtoJSON(res, !utils.NoEmitMode(false))
fmt.Printf("Successfully added DUT to UFS: %s \n", res.GetName())
return nil
}
func (c *addDUT) deployDutToSwarming(ctx context.Context, tc *swarming.TaskCreator, lse *ufspb.MachineLSE) error {
task, err := tc.DeployDut(ctx, lse.Name, lse.GetMachines()[0], defaultSwarmingPool, c.deployTaskTimeout, c.deployActions, c.deployTags, nil)
if err != nil {
fmt.Printf("Failed to trigger Deploy task for DUT %s. Deploy failed %s\n", lse.GetName(), err)
return err
}
fmt.Printf("Triggered Deploy task for DUT %s. Follow the deploy job at %s\n", lse.GetName(), task.TaskURL)
return nil
}
func (c *addDUT) initializeLSEAndAsset(recMap map[string]string) (*dutDeployUFSParams, error) {
lse := &ufspb.MachineLSE{
Lse: &ufspb.MachineLSE_ChromeosMachineLse{
ChromeosMachineLse: &ufspb.ChromeOSMachineLSE{
ChromeosLse: &ufspb.ChromeOSMachineLSE_DeviceLse{
DeviceLse: &ufspb.ChromeOSDeviceLSE{
Device: &ufspb.ChromeOSDeviceLSE_Dut{
Dut: &chromeosLab.DeviceUnderTest{
Peripherals: &chromeosLab.Peripherals{
Chameleon: &chromeosLab.Chameleon{},
Servo: &chromeosLab.Servo{},
Rpm: &chromeosLab.OSRPM{},
Audio: &chromeosLab.Audio{},
Wifi: &chromeosLab.Wifi{},
Touch: &chromeosLab.Touch{},
CameraboxInfo: &chromeosLab.Camerabox{},
},
},
},
},
},
},
},
}
var name, servoHost, servoSerial, rpmHost, rpmOutlet, model, board string
var pools, machines []string
var servoPort int32
var servoSetup chromeosLab.ServoSetupType
resourceState := ufsUtil.ToUFSState(c.state)
if recMap != nil {
// CSV map
name = recMap["name"]
servoHost = recMap["servo_host"]
if recMap["servo_port"] != "" {
port, err := strconv.ParseInt(recMap["servo_port"], 10, 32)
if err != nil {
return nil, fmt.Errorf("failed to parse servo port %s. %s", recMap["servo_port"], err)
}
servoPort = int32(port)
}
servoSerial = recMap["servo_serial"]
// Check if the host is servo V3. Need servo serial otherwise.
if !ufsUtil.ServoV3HostnameRegex.MatchString(servoHost) && servoSerial == "" {
return nil, fmt.Errorf("Not a servo V3 host[%s]. Need servo serial", servoHost)
}
sst, ok := chromeosLab.ServoSetupType_value[appendServoSetupPrefix(recMap["servo_setup"])]
if !ok && recMap["servo_setup"] != "" {
return nil, fmt.Errorf("Invalid servo setup %s. Valid types are %s", recMap["servo_setup"], cmdhelp.ServoSetupTypeAllowedValuesString())
}
servoSetup = chromeosLab.ServoSetupType(sst) // Default value is REGULAR(0).
rpmHost = recMap["rpm_host"]
rpmOutlet = recMap["rpm_outlet"]
machines = []string{recMap["asset"]}
pools = strings.Fields(recMap["pools"])
model = recMap["model"]
board = recMap["board"]
} else {
// command line parameters
name = c.hostname
var err error
servoHost, servoPort, err = parseServoHostnamePort(c.servo)
if err != nil {
return nil, err
}
servoSerial = c.servoSerial
servoSetup = chromeosLab.ServoSetupType(chromeosLab.ServoSetupType_value[appendServoSetupPrefix(c.servoSetupType)])
rpmHost = c.rpm
rpmOutlet = c.rpmOutlet
machines = []string{c.asset}
pools = c.pools
model = c.model
board = c.board
}
lse.Name = name
lse.Hostname = name
lse.GetChromeosMachineLse().GetDeviceLse().GetDut().Hostname = name
lse.Machines = machines
// Use the input params if available for all the options.
lse.Description = c.description
lse.DeploymentTicket = c.deploymentTicket
lse.Tags = c.tags
lse.ResourceState = resourceState
licenses := make([]*chromeosLab.License, 0, len(c.licenseTypes))
for i := range c.licenseTypes {
licenses = append(licenses, &chromeosLab.License{
Type: ufsUtil.ToLicenseType(c.licenseTypes[i]),
Identifier: c.licenseIds[i],
})
}
lse.GetChromeosMachineLse().GetDeviceLse().GetDut().Licenses = licenses
peripherals := lse.GetChromeosMachineLse().GetDeviceLse().GetDut().GetPeripherals()
peripherals.GetServo().ServoHostname = servoHost
peripherals.GetServo().ServoPort = servoPort
peripherals.GetServo().ServoSerial = servoSerial
peripherals.GetServo().ServoSetup = servoSetup
peripherals.GetRpm().PowerunitName = rpmHost
peripherals.GetRpm().PowerunitOutlet = rpmOutlet
if len(pools) > 0 && pools[0] != "" {
lse.GetChromeosMachineLse().GetDeviceLse().GetDut().Pools = pools
} else {
lse.GetChromeosMachineLse().GetDeviceLse().GetDut().Pools = defaultPools
}
// ACS DUT fields
chameleons := make([]chromeosLab.ChameleonType, 0, len(c.chameleons))
for _, cp := range c.chameleons {
chameleons = append(chameleons, ufsUtil.ToChameleonType(cp))
}
cameras := make([]*chromeosLab.Camera, 0, len(c.cameras))
for _, cp := range c.cameras {
camera := &chromeosLab.Camera{
CameraType: ufsUtil.ToCameraType(cp),
}
cameras = append(cameras, camera)
}
cables := make([]*chromeosLab.Cable, 0, len(c.cables))
for _, cp := range c.cables {
cable := &chromeosLab.Cable{
Type: ufsUtil.ToCableType(cp),
}
cables = append(cables, cable)
}
peripherals.GetChameleon().ChameleonPeripherals = chameleons
peripherals.ConnectedCamera = cameras
peripherals.Cable = cables
peripherals.GetWifi().AntennaConn = ufsUtil.ToAntennaConnection(c.antennaConnection)
peripherals.GetWifi().Router = ufsUtil.ToRouter(c.router)
peripherals.GetCameraboxInfo().Facing = ufsUtil.ToFacing(c.facing)
peripherals.GetCameraboxInfo().Light = ufsUtil.ToLight(c.light)
peripherals.GetChameleon().AudioBoard = c.audioBoard
peripherals.GetAudio().AudioBox = c.audioBox
peripherals.GetAudio().Atrus = c.atrus
peripherals.GetAudio().AudioCable = c.audioCable
peripherals.GetWifi().Wificell = c.wifiCell
peripherals.GetTouch().Mimo = c.touchMimo
peripherals.Carrier = c.carrier
peripherals.Camerabox = c.cameraBox
peripherals.Chaos = c.chaos
peripherals.SmartUsbhub = c.smartUSBHub
// Get the updated asset and update paths
asset, paths, err := utils.GenerateAssetUpdate(lse.GetName(), machines[0], model, board, c.zone, c.rack)
if err != nil {
return nil, err
}
return &dutDeployUFSParams{
DUT: lse,
Asset: asset,
Paths: paths,
}, nil
}
func parseServoHostnamePort(servo string) (string, int32, error) {
var servoHostname string
var servoPort int32
servoHostnamePort := strings.Split(servo, ":")
if len(servoHostnamePort) == 2 {
servoHostname = servoHostnamePort[0]
port, err := strconv.ParseInt(servoHostnamePort[1], 10, 32)
if err != nil {
return "", int32(0), err
}
servoPort = int32(port)
} else {
servoHostname = servoHostnamePort[0]
}
return servoHostname, servoPort, nil
}
// updateDeployActions updates the deploySkipActions based on boolean skip options
func (c *addDUT) updateDeployActions() {
if !c.deploySkipDownloadImage {
c.deployActions = append(c.deployActions, "stage-usb")
}
if !c.deploySkipInstallOS {
c.deployActions = append(c.deployActions, "install-test-image")
}
if !c.deploySkipInstallFirmware {
c.deployActions = append(c.deployActions, "install-firmware")
}
}
func appendServoSetupPrefix(servoSetup string) string {
return fmt.Sprintf("SERVO_SETUP_%s", servoSetup)
}
// updateAssetToUFS calls UpdateAsset API in UFS with asset and partial paths
func (c *addDUT) updateAssetToUFS(ctx context.Context, ic ufsAPI.FleetClient, asset *ufspb.Asset, paths []string) error {
if len(paths) == 0 {
// If no update is available. Skip doing anything
return nil
}
mask := &field_mask.FieldMask{
Paths: paths,
}
_, err := ic.UpdateAsset(ctx, &ufsAPI.UpdateAssetRequest{
Asset: asset,
UpdateMask: mask,
})
return err
}