1*9c5db199SXin Li#!/usr/bin/python3 -u 2*9c5db199SXin Li# Copyright 2019 The Chromium OS Authors. All rights reserved. 3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 4*9c5db199SXin Li# found in the LICENSE file. 5*9c5db199SXin Li 6*9c5db199SXin Li"""Tool to (re)prepare a DUT for lab deployment.""" 7*9c5db199SXin Li 8*9c5db199SXin Lifrom __future__ import absolute_import 9*9c5db199SXin Lifrom __future__ import division 10*9c5db199SXin Lifrom __future__ import print_function 11*9c5db199SXin Li 12*9c5db199SXin Liimport argparse 13*9c5db199SXin Liimport errno 14*9c5db199SXin Liimport logging 15*9c5db199SXin Liimport logging.config 16*9c5db199SXin Liimport os 17*9c5db199SXin Liimport sys 18*9c5db199SXin Li 19*9c5db199SXin Liimport common 20*9c5db199SXin Lifrom autotest_lib.client.common_lib import autotest_enum 21*9c5db199SXin Lifrom autotest_lib.client.common_lib import logging_manager 22*9c5db199SXin Lifrom autotest_lib.server import afe_utils 23*9c5db199SXin Lifrom autotest_lib.server import server_logging_config 24*9c5db199SXin Lifrom autotest_lib.server.hosts import file_store 25*9c5db199SXin Lifrom autotest_lib.site_utils.deployment.prepare import dut as preparedut 26*9c5db199SXin Lifrom autotest_lib.server.hosts import factory 27*9c5db199SXin Lifrom autotest_lib.site_utils.admin_audit import rpm_validator 28*9c5db199SXin Li 29*9c5db199SXin Li 30*9c5db199SXin LiRETURN_CODES = autotest_enum.AutotestEnum( 31*9c5db199SXin Li 'OK', 32*9c5db199SXin Li 'SERVO_VERIFICATION_FAILURE', 33*9c5db199SXin Li 'STAGE_USB_FAILURE', 34*9c5db199SXin Li 'INSTALL_FIRMWARE_FAILURE', 35*9c5db199SXin Li 'INSTALL_TEST_IMAGE_FAILURE', 36*9c5db199SXin Li 'PRE_DEPLOY_VERIFICATION_FAILURE', 37*9c5db199SXin Li 'BOOT_FROM_RECOVERY_MODE_FAILURE', 38*9c5db199SXin Li 'SETUP_LABSTATION_FAILURE', 39*9c5db199SXin Li 'UPDATE_LABEL_FAILURE', 40*9c5db199SXin Li 'OTHER_FAILURES', 41*9c5db199SXin Li) 42*9c5db199SXin Li 43*9c5db199SXin Li_SERVO_UART_LOGS = 'servo_uart' 44*9c5db199SXin Li 45*9c5db199SXin Li 46*9c5db199SXin Liclass DutPreparationError(Exception): 47*9c5db199SXin Li """Generic error raised during DUT preparation.""" 48*9c5db199SXin Li 49*9c5db199SXin Li 50*9c5db199SXin Lidef main(): 51*9c5db199SXin Li """Tool to (re)prepare a DUT for lab deployment.""" 52*9c5db199SXin Li opts = _parse_args() 53*9c5db199SXin Li 54*9c5db199SXin Li # Create logging setting 55*9c5db199SXin Li logging_manager.configure_logging( 56*9c5db199SXin Li server_logging_config.ServerLoggingConfig(), 57*9c5db199SXin Li results_dir=opts.results_dir) 58*9c5db199SXin Li 59*9c5db199SXin Li try: 60*9c5db199SXin Li host_info = _read_store(opts.host_info_file) 61*9c5db199SXin Li except Exception as err: 62*9c5db199SXin Li logging.error("fail to prepare: %s", err) 63*9c5db199SXin Li return RETURN_CODES.OTHER_FAILURES 64*9c5db199SXin Li 65*9c5db199SXin Li with create_host(opts.hostname, host_info, opts.results_dir) as host: 66*9c5db199SXin Li if opts.dry_run: 67*9c5db199SXin Li logging.info('DRY RUN: Would have run actions %s', opts.actions) 68*9c5db199SXin Li return 69*9c5db199SXin Li 70*9c5db199SXin Li is_labstation = (host_info.get().os == "labstation") 71*9c5db199SXin Li 72*9c5db199SXin Li if 'servo-verification' in opts.actions: 73*9c5db199SXin Li try: 74*9c5db199SXin Li if not is_labstation: 75*9c5db199SXin Li preparedut.verify_servo(host) 76*9c5db199SXin Li except Exception as err: 77*9c5db199SXin Li logging.error("fail to check servo: %s", err) 78*9c5db199SXin Li return RETURN_CODES.SERVO_VERIFICATION_FAILURE 79*9c5db199SXin Li 80*9c5db199SXin Li if 'stage-usb' in opts.actions: 81*9c5db199SXin Li try: 82*9c5db199SXin Li repair_image = afe_utils.get_stable_cros_image_name_v2( 83*9c5db199SXin Li host_info.get()) 84*9c5db199SXin Li logging.info('Using repair image %s, obtained from AFE', 85*9c5db199SXin Li repair_image) 86*9c5db199SXin Li preparedut.download_image_to_servo_usb(host, repair_image) 87*9c5db199SXin Li except Exception as err: 88*9c5db199SXin Li logging.error("fail to stage image to usb: %s", err) 89*9c5db199SXin Li return RETURN_CODES.STAGE_USB_FAILURE 90*9c5db199SXin Li 91*9c5db199SXin Li if 'install-test-image' in opts.actions: 92*9c5db199SXin Li try: 93*9c5db199SXin Li preparedut.install_test_image(host) 94*9c5db199SXin Li except Exception as err: 95*9c5db199SXin Li logging.error("fail to install test image: %s", err) 96*9c5db199SXin Li return RETURN_CODES.INSTALL_TEST_IMAGE_FAILURE 97*9c5db199SXin Li 98*9c5db199SXin Li if 'install-firmware' in opts.actions: 99*9c5db199SXin Li try: 100*9c5db199SXin Li preparedut.install_firmware(host) 101*9c5db199SXin Li except Exception as err: 102*9c5db199SXin Li logging.error("fail to install firmware: %s", err) 103*9c5db199SXin Li return RETURN_CODES.INSTALL_FIRMWARE_FAILURE 104*9c5db199SXin Li 105*9c5db199SXin Li if 'verify-recovery-mode' in opts.actions: 106*9c5db199SXin Li try: 107*9c5db199SXin Li preparedut.verify_boot_into_rec_mode(host) 108*9c5db199SXin Li except Exception as err: 109*9c5db199SXin Li logging.error("fail to boot from recovery mode: %s", err) 110*9c5db199SXin Li return RETURN_CODES.BOOT_FROM_RECOVERY_MODE_FAILURE 111*9c5db199SXin Li 112*9c5db199SXin Li # TODO (otabek): mix this step with update-label later. 113*9c5db199SXin Li if 'setup-labstation' in opts.actions: 114*9c5db199SXin Li try: 115*9c5db199SXin Li preparedut.setup_hwid_and_serialnumber(host) 116*9c5db199SXin Li except Exception as err: 117*9c5db199SXin Li logging.error("fail to setup labstation: %s", err) 118*9c5db199SXin Li return RETURN_CODES.SETUP_LABSTATION_FAILURE 119*9c5db199SXin Li 120*9c5db199SXin Li if 'update-label' in opts.actions: 121*9c5db199SXin Li try: 122*9c5db199SXin Li preparedut.setup_hwid_and_serialnumber(host) 123*9c5db199SXin Li if not is_labstation: 124*9c5db199SXin Li host.labels.update_labels(host, task_name='deploy') 125*9c5db199SXin Li except Exception as err: 126*9c5db199SXin Li logging.error("fail to update label: %s", err) 127*9c5db199SXin Li return RETURN_CODES.UPDATE_LABEL_FAILURE 128*9c5db199SXin Li 129*9c5db199SXin Li if 'run-pre-deploy-verification' in opts.actions: 130*9c5db199SXin Li try: 131*9c5db199SXin Li if is_labstation: 132*9c5db199SXin Li logging.info("testing RPM information on labstation") 133*9c5db199SXin Li preparedut.verify_labstation_RPM_config_unsafe(host) 134*9c5db199SXin Li else: 135*9c5db199SXin Li preparedut.verify_servo(host) 136*9c5db199SXin Li preparedut.verify_battery_status(host) 137*9c5db199SXin Li preparedut.verify_ccd_testlab_enable(host) 138*9c5db199SXin Li rpm_validator.verify_unsafe(host) 139*9c5db199SXin Li except Exception as err: 140*9c5db199SXin Li logging.error("fail on pre-deploy verification: %s", err) 141*9c5db199SXin Li return RETURN_CODES.PRE_DEPLOY_VERIFICATION_FAILURE 142*9c5db199SXin Li 143*9c5db199SXin Li return RETURN_CODES.OK 144*9c5db199SXin Li 145*9c5db199SXin Li 146*9c5db199SXin Lidef _parse_args(): 147*9c5db199SXin Li parser = argparse.ArgumentParser( 148*9c5db199SXin Li description='Prepare / validate DUT for lab deployment.') 149*9c5db199SXin Li 150*9c5db199SXin Li parser.add_argument( 151*9c5db199SXin Li 'actions', 152*9c5db199SXin Li nargs='+', 153*9c5db199SXin Li choices=[ 154*9c5db199SXin Li 'servo-verification', 'stage-usb', 'install-test-image', 155*9c5db199SXin Li 'install-firmware', 'verify-recovery-mode', 156*9c5db199SXin Li 'run-pre-deploy-verification', 'update-label', 157*9c5db199SXin Li 'setup-labstation' 158*9c5db199SXin Li ], 159*9c5db199SXin Li help='DUT preparation actions to execute.', 160*9c5db199SXin Li ) 161*9c5db199SXin Li parser.add_argument( 162*9c5db199SXin Li '--dry-run', 163*9c5db199SXin Li action='store_true', 164*9c5db199SXin Li default=False, 165*9c5db199SXin Li help='Run in dry-run mode. No changes will be made to the DUT.', 166*9c5db199SXin Li ) 167*9c5db199SXin Li parser.add_argument( 168*9c5db199SXin Li '--results-dir', 169*9c5db199SXin Li required=True, 170*9c5db199SXin Li help='Directory to drop logs and output artifacts in.', 171*9c5db199SXin Li ) 172*9c5db199SXin Li 173*9c5db199SXin Li parser.add_argument( 174*9c5db199SXin Li '--hostname', 175*9c5db199SXin Li required=True, 176*9c5db199SXin Li help='Hostname of the DUT to prepare.', 177*9c5db199SXin Li ) 178*9c5db199SXin Li parser.add_argument( 179*9c5db199SXin Li '--host-info-file', 180*9c5db199SXin Li required=True, 181*9c5db199SXin Li help=('Full path to HostInfo file.' 182*9c5db199SXin Li ' DUT inventory information is read from the HostInfo file.' 183*9c5db199SXin Li ), 184*9c5db199SXin Li ) 185*9c5db199SXin Li 186*9c5db199SXin Li return parser.parse_args() 187*9c5db199SXin Li 188*9c5db199SXin Li 189*9c5db199SXin Lidef _read_store(path): 190*9c5db199SXin Li """Read a HostInfo from a file at path.""" 191*9c5db199SXin Li store = file_store.FileStore(path) 192*9c5db199SXin Li return store 193*9c5db199SXin Li 194*9c5db199SXin Li 195*9c5db199SXin Lidef create_host(hostname, host_info, results_dir): 196*9c5db199SXin Li """Yield a hosts.CrosHost object with the given inventory information. 197*9c5db199SXin Li 198*9c5db199SXin Li @param hostname: Hostname of the DUT. 199*9c5db199SXin Li @param info: A HostInfo with the inventory information to use. 200*9c5db199SXin Li @param results_dir: Path to directory for logs / output artifacts. 201*9c5db199SXin Li 202*9c5db199SXin Li @yield server.hosts.CrosHost object. 203*9c5db199SXin Li """ 204*9c5db199SXin Li info = host_info.get() 205*9c5db199SXin Li if not info.board: 206*9c5db199SXin Li raise DutPreparationError('No board in DUT labels') 207*9c5db199SXin Li if not info.model: 208*9c5db199SXin Li raise DutPreparationError('No model in DUT labels') 209*9c5db199SXin Li 210*9c5db199SXin Li need_servo = info.os != 'labstation' 211*9c5db199SXin Li dut_logs_dir = None 212*9c5db199SXin Li 213*9c5db199SXin Li if need_servo: 214*9c5db199SXin Li # We assume target host is a cros DUT by default 215*9c5db199SXin Li if 'servo_host' not in info.attributes: 216*9c5db199SXin Li raise DutPreparationError('No servo_host in DUT attributes') 217*9c5db199SXin Li if 'servo_port' not in info.attributes: 218*9c5db199SXin Li raise DutPreparationError('No servo_port in DUT attributes') 219*9c5db199SXin Li 220*9c5db199SXin Li dut_logs_dir = os.path.join(results_dir, _SERVO_UART_LOGS) 221*9c5db199SXin Li try: 222*9c5db199SXin Li os.makedirs(dut_logs_dir) 223*9c5db199SXin Li except OSError as e: 224*9c5db199SXin Li if e.errno != errno.EEXIST: 225*9c5db199SXin Li raise 226*9c5db199SXin Li 227*9c5db199SXin Li return factory.create_target_host(hostname, 228*9c5db199SXin Li host_info_store=host_info, 229*9c5db199SXin Li try_lab_servo=need_servo, 230*9c5db199SXin Li try_servo_repair=need_servo, 231*9c5db199SXin Li try_servo_recovery=need_servo, 232*9c5db199SXin Li servo_uart_logs_dir=dut_logs_dir) 233*9c5db199SXin Li 234*9c5db199SXin Li 235*9c5db199SXin Liif __name__ == '__main__': 236*9c5db199SXin Li sys.exit(main()) 237