• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python -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
17
18import common
19from autotest_lib.server import afe_utils
20from autotest_lib.server.hosts import file_store
21from autotest_lib.site_utils.deployment.prepare import dut as preparedut
22
23
24class DutPreparationError(Exception):
25  """Generic error raised during DUT preparation."""
26
27
28def main():
29  """Tool to (re)prepare a DUT for lab deployment."""
30  opts = _parse_args()
31  _configure_logging('prepare_dut', os.path.join(opts.results_dir, _LOG_FILE))
32
33  info = _read_store(opts.host_info_file)
34  repair_image = _get_cros_repair_image_name(info.board)
35  logging.info('Using repair image %s, obtained from AFE', repair_image)
36  with _create_host(opts.hostname, info, opts.results_dir) as host:
37    if opts.dry_run:
38      logging.info('DRY RUN: Would have run actions %s', opts.actions)
39      return
40
41    if 'stage-usb' in opts.actions:
42      preparedut.download_image_to_servo_usb(host, repair_image)
43    if 'install-firmware' in opts.actions:
44      preparedut.install_firmware(host, opts.force_firmware)
45    if 'install-test-image' in opts.actions:
46      preparedut.install_test_image(host)
47
48
49_LOG_FILE = 'prepare_dut.log'
50_DUT_LOGS_DIR = 'dut_logs'
51
52
53def _parse_args():
54  parser = argparse.ArgumentParser(
55      description='Prepare / validate DUT for lab deployment.')
56
57  parser.add_argument(
58      'actions',
59      nargs='+',
60      choices=['stage-usb', 'install-firmware', 'install-test-image'],
61      help='DUT preparation actions to execute.',
62  )
63  parser.add_argument(
64      '--dry-run',
65      action='store_true',
66      default=False,
67      help='Run in dry-run mode. No changes will be made to the DUT.',
68  )
69  parser.add_argument(
70      '--results-dir',
71      required=True,
72      help='Directory to drop logs and output artifacts in.',
73  )
74
75  parser.add_argument(
76      '--hostname',
77      required=True,
78      help='Hostname of the DUT to prepare.',
79  )
80  parser.add_argument(
81      '--host-info-file',
82      required=True,
83      help=('Full path to HostInfo file.'
84            ' DUT inventory information is read from the HostInfo file.'),
85  )
86
87  parser.add_argument(
88      '--force-firmware',
89      action='store_true',
90      help='Force firmware isntallation via chromeos-installfirmware.',
91  )
92
93  return parser.parse_args()
94
95
96def _configure_logging(name, tee_file):
97    """Configure logging globally.
98
99    @param name: Name to prepend to log messages.
100                 This should be the name of the program.
101    @param tee_file: File to tee logs to, in addition to stderr.
102    """
103    logging.config.dictConfig({
104        'version': 1,
105        'formatters': {
106            'stderr': {
107                'format': ('{name}: '
108                           '%(asctime)s:%(levelname)s'
109                           ':%(module)s:%(funcName)s:%(lineno)d'
110                           ': %(message)s'
111                           .format(name=name)),
112            },
113            'tee_file': {
114                'format': ('%(asctime)s:%(levelname)s'
115                           ':%(module)s:%(funcName)s:%(lineno)d'
116                           ': %(message)s'),
117            },
118        },
119        'handlers': {
120            'stderr': {
121                'class': 'logging.StreamHandler',
122                'formatter': 'stderr',
123            },
124            'tee_file': {
125                'class': 'logging.FileHandler',
126                'formatter': 'tee_file',
127                'filename': tee_file,
128            },
129        },
130        'root': {
131            'level': 'DEBUG',
132            'handlers': ['stderr', 'tee_file'],
133        },
134        'disable_existing_loggers': False,
135    })
136
137
138def _read_store(path):
139  """Read a HostInfo from a file at path."""
140  store = file_store.FileStore(path)
141  return store.get()
142
143
144def _create_host(hostname, info, results_dir):
145  """Yield a hosts.CrosHost object with the given inventory information.
146
147  @param hostname: Hostname of the DUT.
148  @param info: A HostInfo with the inventory information to use.
149  @param results_dir: Path to directory for logs / output artifacts.
150  @yield server.hosts.CrosHost object.
151  """
152  if not info.board:
153    raise DutPreparationError('No board in DUT labels')
154  if not info.model:
155    raise DutPreparationError('No model in DUT labels')
156
157  servo_args = {}
158  if 'servo_host' not in info.attributes:
159    raise DutPreparationError('No servo_host in DUT attributes')
160  if 'servo_port' not in info.attributes:
161    raise DutPreparationError('No servo_port in DUT attributes')
162
163  dut_logs_dir = os.path.join(results_dir, _DUT_LOGS_DIR)
164  try:
165    os.makedirs(dut_logs_dir)
166  except OSError as e:
167    if e.errno != errno.EEXIST:
168      raise
169
170  return preparedut.create_host(
171      hostname,
172      info.board,
173      info.model,
174      info.attributes['servo_host'],
175      info.attributes['servo_port'],
176      info.attributes.get('servo_serial', ''),
177      dut_logs_dir,
178  )
179
180
181def _get_cros_repair_image_name(board):
182  """Get the CrOS repair image name for given host.
183
184  TODO(pprabhu): This is an evil function with dependence on the environment
185  (global_config information) and the AFE. Remove this dependence when stable
186  image mappings move off of the AFE.
187  """
188  return afe_utils.get_stable_cros_image_name(board)
189
190
191if __name__ == '__main__':
192  main()
193