xref: /aosp_15_r20/external/autotest/site_utils/admin_audit/main.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1#!/usr/bin/env python3
2# Copyright 2020 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6
7"""Tool to audit a DUT in the lab."""
8
9from __future__ import absolute_import
10from __future__ import division
11from __future__ import print_function
12
13import argparse
14import logging
15import logging.config
16import os
17import sys
18import socket
19import errno
20
21import common
22from autotest_lib.client.common_lib import autotest_enum
23from autotest_lib.client.common_lib import logging_manager
24from autotest_lib.server import server_logging_config
25from autotest_lib.server.hosts import factory
26from autotest_lib.server.hosts import servo_host
27
28import verifiers
29
30RETURN_CODES = autotest_enum.AutotestEnum(
31        'OK',
32        'VERIFY_FAILURE',
33        'OTHER_FAILURES'
34)
35
36ACTION_VERIFY_DUT_STORAGE = 'verify-dut-storage'
37ACTION_VERIFY_SERVO_USB = 'verify-servo-usb-drive'
38ACTION_VERIFY_SERVO_FW = 'verify-servo-fw'
39ACTION_FLASH_SERVO_KEYBOARD_MAP = 'flash-servo-keyboard-map'
40ACTION_VERIFY_DUT_MACADDR = 'verify-dut-macaddr'
41ACTION_VERIFY_RPM_CONFIG = 'verify-rpm-config'
42
43_LOG_FILE = 'audit.log'
44_SERVO_UART_LOGS = 'servo_uart'
45
46VERIFIER_MAP = {
47        ACTION_VERIFY_DUT_STORAGE: verifiers.VerifyDutStorage,
48        ACTION_VERIFY_SERVO_USB: verifiers.VerifyServoUsb,
49        ACTION_VERIFY_SERVO_FW: verifiers.VerifyServoFw,
50        ACTION_FLASH_SERVO_KEYBOARD_MAP:
51        verifiers.FlashServoKeyboardMapVerifier,
52        ACTION_VERIFY_DUT_MACADDR: verifiers.VerifyDUTMacAddress,
53        ACTION_VERIFY_RPM_CONFIG: verifiers.VerifyRPMConfig,
54}
55
56# Actions required Servod service
57ACTIONS_REQUIRED_SERVOD = set([
58        ACTION_VERIFY_DUT_STORAGE,
59        ACTION_VERIFY_SERVO_USB,
60        ACTION_FLASH_SERVO_KEYBOARD_MAP,
61        ACTION_VERIFY_DUT_MACADDR,
62])
63
64# Actions required ServoHost without Servod process
65ACTIONS_REQUIRED_SERVO_HOST = set([
66    ACTION_VERIFY_SERVO_FW,
67])
68
69class DutAuditError(Exception):
70    """Generic error raised during DUT audit."""
71
72
73def main():
74    """Tool to audit a DUT."""
75    opts = _parse_args()
76
77    # Create logging setting
78    logging_manager.configure_logging(
79        server_logging_config.ServerLoggingConfig(),
80        results_dir=opts.results_dir)
81
82    logging.debug('autoserv is running in drone %s.', socket.gethostname())
83    logging.debug('audit environment: %r', os.environ)
84    logging.debug('audit command was: %s', ' '.join(sys.argv))
85    logging.debug('audit parsed options: %s', opts)
86
87    # Initialize ServoHost without running Servod process.
88    need_servo_host = bool(set(opts.actions) & ACTIONS_REQUIRED_SERVO_HOST)
89    # Initialize ServoHost with running Servod process.
90    need_servod = bool(set(opts.actions) & ACTIONS_REQUIRED_SERVOD)
91
92    # Create folder for servo uart logs.
93    servo_uart_logs_dir = None
94    if need_servod:
95        servo_uart_logs_dir = _create_servo_uart_path(opts.results_dir)
96
97    try:
98        host_object = factory.create_target_host(
99                opts.hostname,
100                host_info_path=opts.host_info_file,
101                try_lab_servo=need_servod,
102                try_servo_repair=need_servod,
103                try_servo_recovery=need_servod,
104                servo_uart_logs_dir=servo_uart_logs_dir)
105    except Exception as err:
106        logging.error("fail to create host: %s", err)
107        return RETURN_CODES.OTHER_FAILURES
108
109    with host_object as host:
110        if need_servo_host and not need_servod:
111            try:
112                host.set_servo_host(servo_host.ServoHost(
113                    **servo_host.get_servo_args_for_host(host)
114                ))
115            except Exception as err:
116                logging.error("fail to init servo host: %s", err)
117                return RETURN_CODES.OTHER_FAILURES
118        for action in opts.actions:
119            if opts.dry_run:
120                logging.info('DRY RUN: Would have run actions %s', action)
121                return
122
123            response = _verify(action, host, opts.results_dir)
124            if response:
125                return response
126
127    return RETURN_CODES.OK
128
129
130def _verify(action, host, resultdir):
131    """Run verifier for the action with targeted host.
132
133    @param action: The action requested to run the verifier.
134    @param host: The host presentation of the DUT.
135    """
136    try:
137        _log("START", action)
138        verifier = VERIFIER_MAP[action]
139        if verifier:
140            v = verifier(host)
141            v.set_result_dir(resultdir)
142            v.verify()
143        else:
144            logging.info('Verifier is not specified')
145        _log("END_GOOD", action)
146    except Exception as err:
147        _log("END_FAIL", action, err)
148        return RETURN_CODES.VERIFY_FAILURE
149
150
151def _log(status, action, err=None):
152    if err:
153        message = '%s:%s; %s' % (action, status, str(err))
154    else:
155        message = '%s:%s' % (action, status)
156    logging.info(message)
157
158
159def _create_servo_uart_path(results_dir):
160    servo_uart_logs = os.path.join(results_dir, _SERVO_UART_LOGS)
161    try:
162        if not os.path.exists(servo_uart_logs):
163            os.makedirs(servo_uart_logs)
164    except OSError as e:
165        logging.debug(
166                '(not critical) Fail to create dir for servo logs;'
167                ' %s', e)
168        if not (e.errno == errno.EEXIST):
169            servo_uart_logs = None
170    return servo_uart_logs
171
172
173def _parse_args():
174    parser = argparse.ArgumentParser(description='Audit DUT in a lab.')
175
176    parser.add_argument(
177            'actions',
178            nargs='+',
179            choices=list(VERIFIER_MAP),
180            help='DUT audit actions to execute.',
181    )
182    parser.add_argument(
183            '--dry-run',
184            action='store_true',
185            default=False,
186            help='Run in dry-run mode. No changes will be made to the DUT.',
187    )
188    parser.add_argument(
189            '--results-dir',
190            required=True,
191            help='Directory to drop logs and output artifacts in.',
192    )
193
194    parser.add_argument(
195            '--hostname',
196            required=True,
197            help='Hostname of the DUT to audit.',
198    )
199    parser.add_argument(
200            '--host-info-file',
201            required=True,
202            help=('Full path to HostInfo file.'
203                  ' DUT inventory information is read from the HostInfo file.'
204                  ),
205    )
206
207    return parser.parse_args()
208
209
210if __name__ == '__main__':
211    sys.exit(main())
212