xref: /aosp_15_r20/external/autotest/server/hosts/repair_utils.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Copyright 2016 The Chromium OS Authors. All rights reserved.
2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
3*9c5db199SXin Li# found in the LICENSE file.
4*9c5db199SXin Li
5*9c5db199SXin Liimport logging
6*9c5db199SXin Liimport socket
7*9c5db199SXin Li
8*9c5db199SXin Liimport common
9*9c5db199SXin Lifrom autotest_lib.client.common_lib import hosts
10*9c5db199SXin Lifrom autotest_lib.server import utils
11*9c5db199SXin Lifrom autotest_lib.server.hosts import servo_constants
12*9c5db199SXin Lifrom autotest_lib.server.hosts import cros_constants
13*9c5db199SXin Li
14*9c5db199SXin Lifrom autotest_lib.utils.frozen_chromite.lib import timeout_util
15*9c5db199SXin Li
16*9c5db199SXin Li
17*9c5db199SXin Lidef require_servo(host, ignore_state=False):
18*9c5db199SXin Li    """Require a DUT to have a working servo for a repair action.
19*9c5db199SXin Li
20*9c5db199SXin Li    @param host             Host object that require servo.
21*9c5db199SXin Li    @param ignore_state     Ignore servo state as long as the we still have
22*9c5db199SXin Li                            servo connection. Some non-critical verifier
23*9c5db199SXin Li                            failures may not cause servo connection been
24*9c5db199SXin Li                            disconnected.
25*9c5db199SXin Li    """
26*9c5db199SXin Li    servo_initialized = host.servo is not None
27*9c5db199SXin Li    servo_working = (host.get_servo_state() ==
28*9c5db199SXin Li                     servo_constants.SERVO_STATE_WORKING or ignore_state)
29*9c5db199SXin Li
30*9c5db199SXin Li    if not (servo_initialized and servo_working):
31*9c5db199SXin Li        raise hosts.AutoservRepairError(
32*9c5db199SXin Li                '%s has no working servo.' % host.hostname, 'no_working_servo')
33*9c5db199SXin Li    logging.info('Servo dependence is available for the RepairAction/test.')
34*9c5db199SXin Li
35*9c5db199SXin Li
36*9c5db199SXin Liclass SshVerifier(hosts.Verifier):
37*9c5db199SXin Li    """
38*9c5db199SXin Li    Verifier to test a host's accessibility via `ssh`.
39*9c5db199SXin Li
40*9c5db199SXin Li    This verifier checks whether a given host is reachable over `ssh`.
41*9c5db199SXin Li    In the event of failure, it distinguishes one of three distinct
42*9c5db199SXin Li    conditions:
43*9c5db199SXin Li      * The host can't be found with a DNS lookup.
44*9c5db199SXin Li      * The host doesn't answer to ping.
45*9c5db199SXin Li      * The host answers to ping, but not to ssh.
46*9c5db199SXin Li    """
47*9c5db199SXin Li
48*9c5db199SXin Li    @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC)
49*9c5db199SXin Li    def verify(self, host):
50*9c5db199SXin Li        if host.is_up():
51*9c5db199SXin Li            return
52*9c5db199SXin Li        msg = 'No answer to ssh from %s'
53*9c5db199SXin Li        try:
54*9c5db199SXin Li            socket.gethostbyname(host.hostname)
55*9c5db199SXin Li        except Exception as e:
56*9c5db199SXin Li            logging.exception('DNS lookup failure')
57*9c5db199SXin Li            msg = 'Unable to look up %%s in DNS: %s' % e
58*9c5db199SXin Li        else:
59*9c5db199SXin Li            if utils.ping(host.hostname, tries=1, deadline=1) != 0:
60*9c5db199SXin Li                msg = 'No answer to ping from %s'
61*9c5db199SXin Li        raise hosts.AutoservVerifyError(msg % host.hostname)
62*9c5db199SXin Li
63*9c5db199SXin Li    @property
64*9c5db199SXin Li    def description(self):
65*9c5db199SXin Li        return 'host is available via ssh'
66*9c5db199SXin Li
67*9c5db199SXin Li
68*9c5db199SXin Liclass PingVerifier(hosts.Verifier):
69*9c5db199SXin Li    """
70*9c5db199SXin Li    Verifier to test a host's accessibility via `ping`.
71*9c5db199SXin Li
72*9c5db199SXin Li    This verifier checks whether a given host is reachable over `ping`.
73*9c5db199SXin Li    The device is pingable as soon as booted to level when network driver
74*9c5db199SXin Li    can respond.
75*9c5db199SXin Li    In the event of failure, it distinguishes one of distinct conditions:
76*9c5db199SXin Li      * The host can't be found with a DNS lookup.
77*9c5db199SXin Li      * The host doesn't booted with network drivers.
78*9c5db199SXin Li    """
79*9c5db199SXin Li
80*9c5db199SXin Li    @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC)
81*9c5db199SXin Li    def verify(self, host):
82*9c5db199SXin Li        if host.is_up_fast(count=10):
83*9c5db199SXin Li            return
84*9c5db199SXin Li        msg = 'No answer to ping to %s'
85*9c5db199SXin Li        ip_address = None
86*9c5db199SXin Li        try:
87*9c5db199SXin Li            ip_address = socket.gethostbyname(host.hostname)
88*9c5db199SXin Li        except Exception as e:
89*9c5db199SXin Li            logging.exception('DNS lookup failure')
90*9c5db199SXin Li            msg = 'Unable to look up %s in DNS: %s' % (host.hostname, str(e))
91*9c5db199SXin Li            raise hosts.AutoservVerifyError(msg)
92*9c5db199SXin Li        if not ip_address:
93*9c5db199SXin Li            msg = 'Hostname: %s not present in DNS' % host.hostname
94*9c5db199SXin Li        else:
95*9c5db199SXin Li            msg = 'Hostname: %s not pingable' % host.hostname
96*9c5db199SXin Li        raise hosts.AutoservVerifyError(msg)
97*9c5db199SXin Li
98*9c5db199SXin Li    @property
99*9c5db199SXin Li    def description(self):
100*9c5db199SXin Li        return 'host is available via ping'
101*9c5db199SXin Li
102*9c5db199SXin Li
103*9c5db199SXin Liclass LegacyHostVerifier(hosts.Verifier):
104*9c5db199SXin Li    """
105*9c5db199SXin Li    Ask a Host instance to perform its own verification.
106*9c5db199SXin Li
107*9c5db199SXin Li    This class exists as a temporary legacy during refactoring to
108*9c5db199SXin Li    provide access to code that hasn't yet been rewritten to use the new
109*9c5db199SXin Li    repair and verify framework.
110*9c5db199SXin Li    """
111*9c5db199SXin Li
112*9c5db199SXin Li    @timeout_util.TimeoutDecorator(cros_constants.VERIFY_TIMEOUT_SEC)
113*9c5db199SXin Li    def verify(self, host):
114*9c5db199SXin Li        host.verify_software()
115*9c5db199SXin Li        host.verify_hardware()
116*9c5db199SXin Li
117*9c5db199SXin Li    @property
118*9c5db199SXin Li    def description(self):
119*9c5db199SXin Li        return 'Legacy host verification checks'
120*9c5db199SXin Li
121*9c5db199SXin Li
122*9c5db199SXin Liclass RebootRepair(hosts.RepairAction):
123*9c5db199SXin Li    """Repair a target device by rebooting it."""
124*9c5db199SXin Li
125*9c5db199SXin Li    # Timeout of this action should defined in child class.
126*9c5db199SXin Li    def repair(self, host):
127*9c5db199SXin Li        host.reboot()
128*9c5db199SXin Li
129*9c5db199SXin Li    @property
130*9c5db199SXin Li    def description(self):
131*9c5db199SXin Li        return 'Reboot the host'
132*9c5db199SXin Li
133*9c5db199SXin Li
134*9c5db199SXin Liclass RPMCycleRepair(hosts.RepairAction):
135*9c5db199SXin Li    """
136*9c5db199SXin Li    Cycle AC power using the RPM infrastructure.
137*9c5db199SXin Li
138*9c5db199SXin Li    This is meant to catch two distinct kinds of failure:
139*9c5db199SXin Li      * If the target has no battery (that is, a chromebox), power
140*9c5db199SXin Li        cycling it may force it back on.
141*9c5db199SXin Li      * If the target has a batter that is discharging or even fully
142*9c5db199SXin Li        drained, power cycling will leave power on, enabling other
143*9c5db199SXin Li        repair procedures.
144*9c5db199SXin Li    """
145*9c5db199SXin Li
146*9c5db199SXin Li    @timeout_util.TimeoutDecorator(cros_constants.REPAIR_TIMEOUT_SEC)
147*9c5db199SXin Li    def repair(self, host):
148*9c5db199SXin Li        if not host.has_power():
149*9c5db199SXin Li            raise hosts.AutoservRepairError(
150*9c5db199SXin Li                    '%s has no RPM connection.' % host.hostname,
151*9c5db199SXin Li                    'no_working_rpm')
152*9c5db199SXin Li        host.power_cycle()
153*9c5db199SXin Li
154*9c5db199SXin Li
155*9c5db199SXin Li    @property
156*9c5db199SXin Li    def description(self):
157*9c5db199SXin Li        return 'Power cycle the host with RPM'
158