| #!/usr/bin/python2 -u |
| # Copyright 2019 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Tool to (re)prepare a DUT for lab deployment.""" |
| |
| from __future__ import absolute_import |
| from __future__ import division |
| from __future__ import print_function |
| |
| import argparse |
| import errno |
| import logging |
| import logging.config |
| import os |
| import sys |
| |
| import common |
| from autotest_lib.client.common_lib import autotest_enum |
| from autotest_lib.client.common_lib import logging_manager |
| from autotest_lib.server import afe_utils |
| from autotest_lib.server import server_logging_config |
| from autotest_lib.server.hosts import file_store |
| from autotest_lib.site_utils.deployment.prepare import dut as preparedut |
| from autotest_lib.server.hosts import factory |
| |
| |
| RETURN_CODES = autotest_enum.AutotestEnum( |
| 'OK', |
| 'STAGE_USB_FAILURE', |
| 'INSTALL_FIRMWARE_FAILURE', |
| 'INSTALL_TEST_IMAGE_FAILURE', |
| 'PRE_DEPLOY_VERIFICATION_FAILURE', |
| 'BOOT_FROM_RECOVERY_MODE_FAILURE', |
| 'SETUP_LABSTATION_FAILURE', |
| 'UPDATE_LABEL_FAILURE', |
| 'OTHER_FAILURES', |
| ) |
| |
| _SERVO_UART_LOGS = 'servo_uart' |
| |
| |
| class DutPreparationError(Exception): |
| """Generic error raised during DUT preparation.""" |
| |
| |
| def main(): |
| """Tool to (re)prepare a DUT for lab deployment.""" |
| opts = _parse_args() |
| |
| # Create logging setting |
| logging_manager.configure_logging( |
| server_logging_config.ServerLoggingConfig(), |
| results_dir=opts.results_dir) |
| |
| try: |
| host_info = _read_store(opts.host_info_file) |
| except Exception as err: |
| logging.error("fail to prepare: %s", err) |
| return RETURN_CODES.OTHER_FAILURES |
| |
| with create_host(opts.hostname, host_info, opts.results_dir) as host: |
| if opts.dry_run: |
| logging.info('DRY RUN: Would have run actions %s', opts.actions) |
| return |
| |
| if 'stage-usb' in opts.actions: |
| try: |
| repair_image = afe_utils.get_stable_cros_image_name_v2( |
| host_info.get()) |
| logging.info('Using repair image %s, obtained from AFE', |
| repair_image) |
| preparedut.download_image_to_servo_usb(host, repair_image) |
| except Exception as err: |
| logging.error("fail to stage image to usb: %s", err) |
| return RETURN_CODES.STAGE_USB_FAILURE |
| |
| if 'install-test-image' in opts.actions: |
| try: |
| preparedut.install_test_image(host) |
| except Exception as err: |
| logging.error("fail to install test image: %s", err) |
| return RETURN_CODES.INSTALL_TEST_IMAGE_FAILURE |
| |
| if 'install-firmware' in opts.actions: |
| try: |
| preparedut.install_firmware(host) |
| except Exception as err: |
| logging.error("fail to install firmware: %s", err) |
| return RETURN_CODES.INSTALL_FIRMWARE_FAILURE |
| |
| if 'run-pre-deploy-verification' in opts.actions: |
| try: |
| if host_info.get().os == "labstation": |
| logging.info("testing RPM information on labstation") |
| preparedut.verify_labstation_RPM_config_unsafe(host) |
| else: |
| preparedut.verify_servo(host) |
| preparedut.verify_battery_status(host) |
| preparedut.verify_ccd_testlab_enable(host) |
| except Exception as err: |
| logging.error("fail on pre-deploy verification: %s", err) |
| return RETURN_CODES.PRE_DEPLOY_VERIFICATION_FAILURE |
| |
| if 'verify-recovery-mode' in opts.actions: |
| try: |
| preparedut.verify_boot_into_rec_mode(host) |
| except Exception as err: |
| logging.error("fail to boot from recovery mode: %s", err) |
| return RETURN_CODES.BOOT_FROM_RECOVERY_MODE_FAILURE |
| |
| if 'setup-labstation' in opts.actions: |
| try: |
| preparedut.setup_labstation(host) |
| except Exception as err: |
| logging.error("fail to setup labstation: %s", err) |
| return RETURN_CODES.SETUP_LABSTATION_FAILURE |
| |
| if 'update-label' in opts.actions: |
| try: |
| host.labels.update_labels(host, task_name='deploy') |
| except Exception as err: |
| logging.error("fail to update label: %s", err) |
| return RETURN_CODES.UPDATE_LABEL_FAILURE |
| |
| return RETURN_CODES.OK |
| |
| |
| def _parse_args(): |
| parser = argparse.ArgumentParser( |
| description='Prepare / validate DUT for lab deployment.') |
| |
| parser.add_argument( |
| 'actions', |
| nargs='+', |
| choices=[ |
| 'stage-usb', 'install-test-image', 'install-firmware', |
| 'verify-recovery-mode', 'run-pre-deploy-verification', |
| 'update-label', 'setup-labstation' |
| ], |
| help='DUT preparation actions to execute.', |
| ) |
| parser.add_argument( |
| '--dry-run', |
| action='store_true', |
| default=False, |
| help='Run in dry-run mode. No changes will be made to the DUT.', |
| ) |
| parser.add_argument( |
| '--results-dir', |
| required=True, |
| help='Directory to drop logs and output artifacts in.', |
| ) |
| |
| parser.add_argument( |
| '--hostname', |
| required=True, |
| help='Hostname of the DUT to prepare.', |
| ) |
| parser.add_argument( |
| '--host-info-file', |
| required=True, |
| help=('Full path to HostInfo file.' |
| ' DUT inventory information is read from the HostInfo file.' |
| ), |
| ) |
| |
| return parser.parse_args() |
| |
| |
| def _read_store(path): |
| """Read a HostInfo from a file at path.""" |
| store = file_store.FileStore(path) |
| return store |
| |
| |
| def create_host(hostname, host_info, results_dir): |
| """Yield a hosts.CrosHost object with the given inventory information. |
| |
| @param hostname: Hostname of the DUT. |
| @param info: A HostInfo with the inventory information to use. |
| @param results_dir: Path to directory for logs / output artifacts. |
| |
| @yield server.hosts.CrosHost object. |
| """ |
| info = host_info.get() |
| if not info.board: |
| raise DutPreparationError('No board in DUT labels') |
| if not info.model: |
| raise DutPreparationError('No model in DUT labels') |
| |
| need_servo = info.os != 'labstation' |
| dut_logs_dir = None |
| |
| if need_servo: |
| # We assume target host is a cros DUT by default |
| if 'servo_host' not in info.attributes: |
| raise DutPreparationError('No servo_host in DUT attributes') |
| if 'servo_port' not in info.attributes: |
| raise DutPreparationError('No servo_port in DUT attributes') |
| |
| dut_logs_dir = os.path.join(results_dir, _SERVO_UART_LOGS) |
| try: |
| os.makedirs(dut_logs_dir) |
| except OSError as e: |
| if e.errno != errno.EEXIST: |
| raise |
| |
| return factory.create_target_host(hostname, |
| host_info_store=host_info, |
| try_lab_servo=need_servo, |
| try_servo_repair=need_servo, |
| servo_uart_logs_dir=dut_logs_dir) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |