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