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