blob: c5cd9d6e76d254612b361cb2e516d6c547c6ac91 [file] [edit]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package cros
import (
"context"
"fmt"
"regexp"
"time"
"go.chromium.org/luci/common/errors"
"go.chromium.org/infra/cros/recovery/internal/components"
"go.chromium.org/infra/cros/recovery/internal/components/cros/power"
"go.chromium.org/infra/cros/recovery/internal/components/servo"
"go.chromium.org/infra/cros/recovery/internal/log"
"go.chromium.org/infra/cros/recovery/tlw"
)
const (
// The percentage of the battery that is considered to be not
// enough.
MinimumBatteryLevel = 70
)
// RecoveryModeRequiredPDOff examines whether servo_pd_role has to be set to
// `snk` before booting into recovery mode.
func RecoveryModeRequiredPDOff(ctx context.Context, run components.Runner, servod components.Servod, dut *tlw.Dut) (bool, error) {
// DUT with PDC do not need servo to be in snk mode during recovery boot.
if err := servod.Has(ctx, "pdc_ccd_keepalive_en"); err == nil {
log.Debugf(ctx, "PDC DUT detected, snk mode is not needed for recovery boot.")
return false, nil
}
// Some DUTs do not have battery, so we cannot cut PD.
expectBattery := dut.GetChromeos().GetBattery() != nil
if !expectBattery {
log.Debugf(ctx, "DUT is not expected to have the battery, so recovery mode does not required PD:snk!")
return false, nil
}
// Verify that device is accessible to avoid an unnecessary attempt to read power-info.
if sshErr := IsAccessible(ctx, run, DefaultSSHTimeout); sshErr != nil {
log.Debugf(ctx, "Skipping check battery present on the devices as DUT is not accessible!")
} else if p, err := power.ReadPowerInfo(ctx, run); err == nil {
expectBattery, _ = p.HasBattery()
}
if !expectBattery {
log.Debugf(ctx, "The DUT does not have a battery, so recovery mode does not required PD:snk!")
return false, nil
}
if sType, err := servo.WrappedServoType(ctx, servod, dut.GetChromeos().GetServo()); err != nil {
return false, errors.Annotate(err, "require sink mode in recovery")
} else if sType.IsMicro() {
log.Debugf(ctx, "DUT uses servo_micro, so recovery mode does not required PD:snk!")
return false, nil
}
if pdControlSupported, err := servo.ServoSupportsBuiltInPDControl(ctx, servod); err != nil {
log.Debugf(ctx, "Servo does not support PD, so recovery mode does not required PD:snk!")
return false, errors.Annotate(err, "require sink mode in recovery")
} else if !pdControlSupported {
log.Debugf(ctx, "Require Sink Mode in Recovery: power delivery is no tsupported on this servo, snk mode is not needed for recovery.")
return false, nil
}
return true, nil
}
// Uptime returns uptime of resource.
func Uptime(ctx context.Context, run components.Runner) (*time.Duration, error) {
// Received value represent two parts where the first value represents the total number
// of seconds the system has been up and the second value is the sum of how much time
// each core has spent idle, in seconds. We are looking
// E.g.: 683503.88 1003324.85
// Consequently, the second value may be greater than the overall system uptime on systems with multiple cores.
out, err := run(ctx, time.Minute, "cat /proc/uptime")
if err != nil {
return nil, errors.Annotate(err, "uptime")
}
log.Debugf(ctx, "Uptime value read: %q.", out)
dur, err := ProcessUptime(out)
if err != nil {
return nil, errors.Annotate(err, "get uptime")
}
return dur, nil
}
func ProcessUptime(uptimeVal string) (*time.Duration, error) {
// uptimePattern is a decimal number, possibly containing a decimal point.
uptimePattern := regexp.MustCompile(`^\s*(\d+\.?\d*)\s+(\d+\.?\d*)\s*$`)
parts := uptimePattern.FindStringSubmatch(uptimeVal)
if len(parts) != 3 {
// 'parts' consists of the complete match, as well as the two
// captured groups for the two numerical quantities returned
// upon reading /proc/uptime. Hence, in a correct read, the
// array should have exactly three elements.
return nil, errors.Reason("process uptime: fail to read value from %s", uptimeVal)
}
dur, err := time.ParseDuration(fmt.Sprintf("%ss", parts[1]))
if err != nil {
return nil, errors.Annotate(err, "process uptime")
}
return &dur, nil
}