xref: /aosp_15_r20/external/autotest/site_utils/deployment/prepare/main.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
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