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