• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python2
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                servo_uart_logs_dir=servo_uart_logs_dir)
104    except Exception as err:
105        logging.error("fail to create host: %s", err)
106        return RETURN_CODES.OTHER_FAILURES
107
108    with host_object as host:
109        if need_servo_host and not need_servod:
110            try:
111                host.set_servo_host(servo_host.ServoHost(
112                    **servo_host.get_servo_args_for_host(host)
113                ))
114            except Exception as err:
115                logging.error("fail to init servo host: %s", err)
116                return RETURN_CODES.OTHER_FAILURES
117        for action in opts.actions:
118            if opts.dry_run:
119                logging.info('DRY RUN: Would have run actions %s', action)
120                return
121
122            response = _verify(action, host, opts.results_dir)
123            if response:
124                return response
125
126    return RETURN_CODES.OK
127
128
129def _verify(action, host, resultdir):
130    """Run verifier for the action with targeted host.
131
132    @param action: The action requested to run the verifier.
133    @param host: The host presentation of the DUT.
134    """
135    try:
136        _log("START", action)
137        verifier = VERIFIER_MAP[action]
138        if verifier:
139            v = verifier(host)
140            v.set_result_dir(resultdir)
141            v.verify()
142        else:
143            logging.info('Verifier is not specified')
144        _log("END_GOOD", action)
145    except Exception as err:
146        _log("END_FAIL", action, err)
147        return RETURN_CODES.VERIFY_FAILURE
148
149
150def _log(status, action, err=None):
151    if err:
152        message = '%s:%s; %s' % (action, status, str(err))
153    else:
154        message = '%s:%s' % (action, status)
155    logging.info(message)
156
157
158def _create_servo_uart_path(results_dir):
159    servo_uart_logs = os.path.join(results_dir, _SERVO_UART_LOGS)
160    try:
161        if not os.path.exists(servo_uart_logs):
162            os.makedirs(servo_uart_logs)
163    except OSError as e:
164        logging.debug(
165                '(not critical) Fail to create dir for servo logs;'
166                ' %s', e)
167        if not (e.errno == errno.EEXIST):
168            servo_uart_logs = None
169    return servo_uart_logs
170
171
172def _parse_args():
173    parser = argparse.ArgumentParser(description='Audit DUT in a lab.')
174
175    parser.add_argument(
176            'actions',
177            nargs='+',
178            choices=list(VERIFIER_MAP),
179            help='DUT audit actions to execute.',
180    )
181    parser.add_argument(
182            '--dry-run',
183            action='store_true',
184            default=False,
185            help='Run in dry-run mode. No changes will be made to the DUT.',
186    )
187    parser.add_argument(
188            '--results-dir',
189            required=True,
190            help='Directory to drop logs and output artifacts in.',
191    )
192
193    parser.add_argument(
194            '--hostname',
195            required=True,
196            help='Hostname of the DUT to audit.',
197    )
198    parser.add_argument(
199            '--host-info-file',
200            required=True,
201            help=('Full path to HostInfo file.'
202                  ' DUT inventory information is read from the HostInfo file.'
203                  ),
204    )
205
206    return parser.parse_args()
207
208
209if __name__ == '__main__':
210    sys.exit(main())
211