blob: fb8b991413b4953b13676075e5c10901b041ade8 [file] [log] [blame]
Kuang-che Wu875c89a2020-01-08 14:30:55 +08001#!/usr/bin/env python3
Kuang-che Wu6e4beca2018-06-27 17:45:02 +08002# -*- coding: utf-8 -*-
Kuang-che Wu2ea804f2017-11-28 17:11:41 +08003# Copyright 2017 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
Kuang-che Wu68db08a2018-03-30 11:50:34 +08006"""Helper script to manipulate chromeos DUT or query info."""
Kuang-che Wu2ea804f2017-11-28 17:11:41 +08007from __future__ import print_function
8import argparse
Kuang-che Wu9d14c162020-11-03 19:35:18 +08009import asyncio
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080010import json
11import logging
Kuang-che Wu9d14c162020-11-03 19:35:18 +080012import os
Kuang-che Wu0c9b7942019-10-30 16:55:39 +080013import random
14import time
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080015
Kuang-che Wufe1e88a2019-09-10 21:52:25 +080016from bisect_kit import cli
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080017from bisect_kit import common
Kuang-che Wu9d14c162020-11-03 19:35:18 +080018from bisect_kit import configure
Kuang-che Wuc45cfa42019-01-15 00:15:01 +080019from bisect_kit import cros_lab_util
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080020from bisect_kit import cros_util
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080021from bisect_kit import errors
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080022
Zheng-Jie Chang17f36c82020-06-16 05:21:59 +080023DEFAULT_DUT_POOL = 'DUT_POOL_QUOTA'
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080024logger = logging.getLogger(__name__)
25
Kuang-che Wu25723422020-09-24 22:20:26 +080026models_to_avoid = {
27 # model: reason
Kuang-che Wu25723422020-09-24 22:20:26 +080028}
29
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080030
31def cmd_version_info(opts):
32 info = cros_util.version_info(opts.board, opts.version)
33 if opts.name:
34 if opts.name not in info:
35 logger.error('unknown name=%s', opts.name)
36 print(info[opts.name])
37 else:
38 print(json.dumps(info, sort_keys=True, indent=4))
39
40
41def cmd_query_dut_board(opts):
42 assert cros_util.is_dut(opts.dut)
43 print(cros_util.query_dut_board(opts.dut))
44
45
46def cmd_reboot(opts):
Kuang-che Wu2534bab2020-10-23 17:37:16 +080047 if not cros_util.is_dut(opts.dut):
48 if opts.force:
49 logger.warning('%s is not a Chrome OS device?', opts.dut)
50 else:
51 raise errors.ArgumentError(
52 'dut', 'not a Chrome OS device (--force to continue)')
53
Kuang-che Wu2ac9a922020-09-03 16:50:12 +080054 cros_util.reboot(
55 opts.dut, force_reboot_callback=cros_lab_util.reboot_via_servo)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080056
57
Kuang-che Wuc45cfa42019-01-15 00:15:01 +080058def _get_label_by_prefix(info, prefix):
59 for label in info['Labels']:
60 if label.startswith(prefix + ':'):
61 return label
62 return None
63
64
Kuang-che Wuca456462019-11-04 17:32:55 +080065def cmd_lease_dut(opts):
Kuang-che Wu5157dee2020-07-18 01:13:41 +080066 if opts.duration is not None and opts.duration < 60:
67 raise errors.ArgumentError('--duration', 'must be at least 60 seconds')
Kuang-che Wu7c1fa582021-01-14 17:57:49 +080068 reason = opts.reason or cros_lab_util.make_lease_reason(opts.session)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080069 host = cros_lab_util.dut_host_name(opts.dut)
Kuang-che Wu220cc162019-10-31 00:29:37 +080070 logger.info('trying to lease %s', host)
Kuang-che Wu7c1fa582021-01-14 17:57:49 +080071 if cros_lab_util.skylab_lease_dut(host, reason, opts.duration):
Kuang-che Wuca456462019-11-04 17:32:55 +080072 logger.info('leased %s', host)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080073 else:
Kuang-che Wuca456462019-11-04 17:32:55 +080074 raise Exception('unable to lease %s' % host)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080075
76
Kuang-che Wuca456462019-11-04 17:32:55 +080077def cmd_release_dut(opts):
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080078 host = cros_lab_util.dut_host_name(opts.dut)
Kuang-che Wu220cc162019-10-31 00:29:37 +080079 cros_lab_util.skylab_release_dut(host)
Kuang-che Wuca456462019-11-04 17:32:55 +080080 logger.info('%s released', host)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080081
82
Zheng-Jie Chang21a5b152021-05-06 13:45:46 +080083def verify_dimensions_by_lab(dimensions, tolerate_unknown=False):
84 dimensions = sorted(set(dimensions))
Kuang-che Wu1e56ce22020-06-29 11:21:51 +080085 result = []
86 bots_dimensions = cros_lab_util.swarming_bots_dimensions()
87 for dimension in dimensions:
88 key, value = dimension.split(':', 1)
89 if value in bots_dimensions.get(key, []):
90 result.append(dimension)
91 else:
Zheng-Jie Chang21a5b152021-05-06 13:45:46 +080092 if tolerate_unknown:
93 logger.warning('dimension=%s is unknown in the lab, typo? ignored',
94 dimension)
95 else:
96 raise Exception('dimension=%s is unknown in the lab, typo? ignored' %
97 dimension)
Kuang-che Wu1e56ce22020-06-29 11:21:51 +080098 return result
99
100
Kuang-che Wueac89bd2020-10-23 16:07:15 +0800101def select_available_bots_randomly(dimensions,
102 variants,
103 num=1,
104 is_busy=None,
105 filter_func=None):
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800106 bots = []
107 for variant in variants:
108 # There might be thousand bots available, set 'limit' to reduce swarming
109 # API cost. This is not uniform random, but should be good enough.
110 bots += cros_lab_util.swarming_bots_list(
111 dimensions + [variant], is_busy=is_busy, limit=10)
Kuang-che Wu25723422020-09-24 22:20:26 +0800112
Kuang-che Wueac89bd2020-10-23 16:07:15 +0800113 if filter_func:
114 bots = list(filter(filter_func, bots))
115 return random.sample(bots, min(num, len(bots)))
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800116
117
Kuang-che Wu8480a8a2021-04-21 15:44:59 +0800118def filter_dimensions_by_board(boards_with_prebuilt, dimensions, pool):
119 result = set()
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800120 for dimension in dimensions:
Kuang-che Wu8480a8a2021-04-21 15:44:59 +0800121 constraints = [dimension]
122 if pool:
123 constraints.append('label-pool:' + pool)
124 bots = cros_lab_util.swarming_bots_list(constraints, is_busy=None, limit=1)
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800125 if not bots:
126 continue
127 board = bots[0]['dimensions']['label-board'][0]
128 if board not in boards_with_prebuilt:
129 logger.warning(
130 'dimension=%s (board=%s) does not have corresponding '
131 'prebuilt image, ignore', dimension, board)
132 continue
Kuang-che Wu8480a8a2021-04-21 15:44:59 +0800133 result.add(dimension)
134 return list(result)
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800135
136
Kuang-che Wueac89bd2020-10-23 16:07:15 +0800137def is_acceptable_bot(boards_with_prebuilt, bot):
138 model = bot['dimensions']['label-model'][0]
139 if model in models_to_avoid:
140 logger.warning('model=%s is bad (reason:%s), ignore', model,
141 models_to_avoid[model])
142 return False
143
Kuang-che Wu79ea1192020-10-27 15:16:05 +0800144 if boards_with_prebuilt is not None:
145 # Sometimes swarming database has inconsistent records. For example,
146 # label-model=kefka + label-board=strago are incorrect (should be
147 # label-board=kefka). It is probably human errors (strago is kefka's
148 # reference board).
149 board = bot['dimensions']['label-board'][0]
150 if board not in boards_with_prebuilt:
151 logger.warning('%s has unexpected board=%s, ignore',
152 bot['dimensions']['dut_name'][0], board)
153 return False
Kuang-che Wueac89bd2020-10-23 16:07:15 +0800154
155 return True
Kuang-che Wu04619772020-10-22 18:57:07 +0800156
157
Kuang-che Wu7c1fa582021-01-14 17:57:49 +0800158async def lease_dut_parallelly(duration, bots, reason, timeout=None):
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800159 tasks = []
160 hosts = []
161 for bot in bots:
162 host = bot['dimensions']['dut_name'][0]
163 hosts.append(host)
164 tasks.append(
Kuang-che Wu7c1fa582021-01-14 17:57:49 +0800165 asyncio.create_task(
166 cros_lab_util.async_lease(host, reason, duration=duration)))
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800167
168 try:
169 logger.info('trying to lease %d DUTs: %s', len(hosts), hosts)
170 for coro in asyncio.as_completed(tasks, timeout=timeout):
171 host = await coro
172 if host:
173 logger.info('leased %s', host)
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800174 # Unfinished lease tasks will be cancelled when asyncio.run is
175 # finishing.
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800176 return host
177 return None
178 except asyncio.TimeoutError:
179 return None
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800180
181
Kuang-che Wu9d14c162020-11-03 19:35:18 +0800182def normalize_board_name(chromeos_root, board):
183 """Normalize BOARD name.
184
185 Here, we want to find the actual device board. Suffixes like -kernelnext will
186 be removed. So we can use that name to query DUTs inside the lab.
187
188 Args:
189 chromeos_root: chromeos source root
190 board: BOARD name
191
192 Returns:
193 normalized BOARD name
194 """
195 overlays = cros_util.parse_chromeos_overlays(chromeos_root)
196 boards_info = cros_util.resolve_basic_boards(overlays)
197 return boards_info[board]
198
199
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800200def do_allocate_dut(opts):
201 """Helper of cmd_allocate_dut.
202
203 Returns:
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800204 (todo, host, board_to_build)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800205 todo: 'ready' or 'wait'
Kuang-che Wuca456462019-11-04 17:32:55 +0800206 host: leased host name
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800207 board_to_build: board name for building image
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800208 """
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800209 if not opts.dut_name and not opts.pool:
210 raise errors.ArgumentError('--pool',
211 'need to be specified if not --dut_name')
Kuang-che Wu7e8abe62020-07-02 09:42:27 +0800212 if opts.version_hint:
213 for v in opts.version_hint.split(','):
214 if cros_util.is_cros_version(v) or cros_util.is_cros_snapshot_version(v):
215 continue
216 raise errors.ArgumentError(
217 '--version_hint',
218 'should be Chrome OS version numbers, separated by comma')
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800219 if opts.duration is not None and opts.duration < 60:
220 raise errors.ArgumentError('--duration', 'must be at least 60 seconds')
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800221
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800222 t0 = time.time()
Zheng-Jie Chang17f36c82020-06-16 05:21:59 +0800223 dimensions = ['dut_state:ready']
Kuang-che Wu8480a8a2021-04-21 15:44:59 +0800224 if opts.dut_name:
225 # If dut_name is specified, pool is ignored.
226 opts.pool = None
227 else:
Zheng-Jie Chang17f36c82020-06-16 05:21:59 +0800228 dimensions.append('label-pool:' + opts.pool)
Zheng-Jie Chang21a5b152021-05-06 13:45:46 +0800229 for key, value in opts.dimensions:
230 dimensions.append('%s:%s' % (key, value))
231 dimensions = verify_dimensions_by_lab(dimensions)
Zheng-Jie Chang17f36c82020-06-16 05:21:59 +0800232
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800233 variants = []
234 if opts.board:
235 for board in opts.board.split(','):
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800236 variants.append('label-board:' +
Kuang-che Wu9d14c162020-11-03 19:35:18 +0800237 normalize_board_name(opts.chromeos_root, board))
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800238 if opts.model:
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800239 for model in opts.model.split(','):
Kuang-che Wu25723422020-09-24 22:20:26 +0800240 if model in models_to_avoid:
241 logger.warning('model=%s is bad (reason:%s), ignore', model,
242 models_to_avoid[model])
243 continue
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800244 variants.append('label-model:' + model)
Kuang-che Wu25723422020-09-24 22:20:26 +0800245 if not variants:
246 raise errors.ArgumentError('--model',
247 'all specified models are not supported')
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800248 if opts.sku:
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800249 for sku in opts.sku.split(','):
250 variants.append('label-hwid_sku:' + cros_lab_util.normalize_sku_name(sku))
251 if opts.dut_name:
252 for dut_name in opts.dut_name.split(','):
253 variants.append('dut_name:' + dut_name)
254
Zheng-Jie Chang21a5b152021-05-06 13:45:46 +0800255 variants = verify_dimensions_by_lab(variants, tolerate_unknown=True)
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800256 if not variants:
257 raise errors.NoDutAvailable(
258 'Invalid constraints: %s;%s;%s;%s' %
259 (opts.board, opts.model, opts.sku, opts.dut_name))
260
261 # Filter variants by prebuilt images.
Kuang-che Wu79ea1192020-10-27 15:16:05 +0800262 boards_with_prebuilt = None
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800263 if opts.version_hint:
264 if not opts.builder_hint:
265 opts.builder_hint = opts.board
266 if not opts.builder_hint:
267 raise errors.ArgumentError('--builder_hint',
268 'must be specified along with --version_hint')
269 boards_with_prebuilt = []
270 versions = opts.version_hint.split(',')
271 for builder in opts.builder_hint.split(','):
272 if not all(cros_util.has_test_image(builder, v) for v in versions):
273 logger.warning(
274 'builder=%s does not have prebuilt test image for %s, ignore',
275 builder, opts.version_hint)
276 continue
Kuang-che Wu9d14c162020-11-03 19:35:18 +0800277 boards_with_prebuilt.append(
278 normalize_board_name(opts.chromeos_root, builder))
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800279 logger.info('boards with prebuilt: %s', boards_with_prebuilt)
280 if not boards_with_prebuilt:
281 raise errors.ArgumentError(
282 '--version_hint',
283 'given builders have no prebuilt for %s' % opts.version_hint)
Kuang-che Wu8480a8a2021-04-21 15:44:59 +0800284 variants = filter_dimensions_by_board(boards_with_prebuilt, variants,
285 opts.pool)
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800286 if not variants:
287 raise errors.NoDutAvailable(
288 'Devices with specified constraints have no prebuilt. '
289 'Wrong version number?')
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800290
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800291 while True:
Kuang-che Wueac89bd2020-10-23 16:07:15 +0800292 filter_func = lambda bot: is_acceptable_bot(boards_with_prebuilt, bot)
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800293 # Query every time because each iteration takes a few minutes
294 bots = select_available_bots_randomly(
Kuang-che Wueac89bd2020-10-23 16:07:15 +0800295 dimensions,
296 variants,
297 num=opts.parallel,
298 is_busy=False,
299 filter_func=filter_func)
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800300 if not bots:
301 bots = select_available_bots_randomly(
Kuang-che Wueac89bd2020-10-23 16:07:15 +0800302 dimensions,
303 variants,
304 num=opts.parallel,
305 is_busy=True,
306 filter_func=filter_func)
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800307 if not bots:
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800308 raise errors.NoDutAvailable(
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800309 'no bots satisfy constraints; all are in maintenance state?')
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800310
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800311 remaining_time = opts.time_limit - (time.time() - t0)
312 if remaining_time <= 0:
313 break
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800314 timeout = min(120, remaining_time)
Kuang-che Wu7c1fa582021-01-14 17:57:49 +0800315 reason = cros_lab_util.make_lease_reason(opts.session)
316 host = asyncio.run(
317 lease_dut_parallelly(opts.duration, bots, reason, timeout))
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800318 if host:
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800319 # Resolve what board we should build during bisection.
320 board_to_build = None
321 bots = cros_lab_util.swarming_bots_list(['dut_name:' + host])
322 host_board = bots[0]['dimensions']['label-board'][0]
323 if opts.builder_hint:
324 for builder in opts.builder_hint.split(','):
Kuang-che Wu9d14c162020-11-03 19:35:18 +0800325 if normalize_board_name(opts.chromeos_root, builder) == host_board:
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800326 board_to_build = builder
327 break
Kuang-che Wu04619772020-10-22 18:57:07 +0800328 else:
329 raise errors.DutLeaseException('DUT with unexpected board:%s' %
330 host_board)
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800331 else:
332 board_to_build = host_board
333
334 return 'ready', host, board_to_build
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800335 time.sleep(1)
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800336
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800337 logger.warning('unable to lease DUT in time limit')
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800338 return 'wait', None, None
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800339
340
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800341def cmd_allocate_dut(opts):
Kuang-che Wuca456462019-11-04 17:32:55 +0800342 leased_dut = None
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800343 try:
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800344 todo, host, board = do_allocate_dut(opts)
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800345 leased_dut = cros_lab_util.dut_name_to_address(host) if host else None
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800346 result = {'result': todo, 'leased_dut': leased_dut, 'board': board}
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800347 print(json.dumps(result))
348 except Exception as e:
349 logger.exception('cmd_allocate_dut failed')
350 exception_name = e.__class__.__name__
351 result = {
352 'result': 'failed',
353 'exception': exception_name,
354 'text': str(e),
355 }
356 print(json.dumps(result))
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800357
358
Kuang-che Wua8c3c3e2019-08-28 18:49:28 +0800359def cmd_repair_dut(opts):
360 cros_lab_util.repair(opts.dut)
361
362
Zheng-Jie Chang21a5b152021-05-06 13:45:46 +0800363def parse_dimension(s):
364 splitted = s.split('=')
365 if len(splitted) != 2:
366 raise argparse.ArgumentTypeError('Invalid dimension: %s' % s)
367 return splitted
368
369
Kuang-che Wufe1e88a2019-09-10 21:52:25 +0800370@cli.fatal_error_handler
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800371def main():
372 common.init()
Kuang-che Wud2d6e412021-01-28 16:26:41 +0800373 parents = [common.common_argument_parser, common.session_optional_parser]
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800374 parser = argparse.ArgumentParser()
Kuang-che Wufe1e88a2019-09-10 21:52:25 +0800375 cli.patching_argparser_exit(parser)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800376 subparsers = parser.add_subparsers(
Kuang-che Wud2d6e412021-01-28 16:26:41 +0800377 dest='command', title='commands', metavar='<command>', required=True)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800378
379 parser_version_info = subparsers.add_parser(
380 'version_info',
381 help='Query version info of given chromeos build',
Kuang-che Wud2d6e412021-01-28 16:26:41 +0800382 parents=parents,
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800383 description='Given chromeos `board` and `version`, '
384 'print version information of components.')
385 parser_version_info.add_argument(
386 'board', help='ChromeOS board name, like "samus".')
387 parser_version_info.add_argument(
388 'version',
389 type=cros_util.argtype_cros_version,
390 help='ChromeOS version, like "9876.0.0" or "R62-9876.0.0"')
391 parser_version_info.add_argument(
392 'name',
393 nargs='?',
394 help='Component name. If specified, output its version string. '
395 'Otherwise output all version info as dict in json format.')
396 parser_version_info.set_defaults(func=cmd_version_info)
397
398 parser_query_dut_board = subparsers.add_parser(
Kuang-che Wud2d6e412021-01-28 16:26:41 +0800399 'query_dut_board', help='Query board name of given DUT', parents=parents)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800400 parser_query_dut_board.add_argument('dut')
401 parser_query_dut_board.set_defaults(func=cmd_query_dut_board)
402
403 parser_reboot = subparsers.add_parser(
404 'reboot',
405 help='Reboot a DUT',
Kuang-che Wud2d6e412021-01-28 16:26:41 +0800406 parents=parents,
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800407 description='Reboot a DUT and verify the reboot is successful.')
Kuang-che Wu2534bab2020-10-23 17:37:16 +0800408 parser_reboot.add_argument('--force', action='store_true')
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800409 parser_reboot.add_argument('dut')
410 parser_reboot.set_defaults(func=cmd_reboot)
411
Kuang-che Wuca456462019-11-04 17:32:55 +0800412 parser_lease_dut = subparsers.add_parser(
413 'lease_dut',
414 help='Lease a DUT in the lab',
Kuang-che Wud2d6e412021-01-28 16:26:41 +0800415 parents=[common.common_argument_parser],
Kuang-che Wuca456462019-11-04 17:32:55 +0800416 description='Lease a DUT in the lab. '
417 'This is implemented by `skylab lease-dut` with additional checking.')
Kuang-che Wu7c1fa582021-01-14 17:57:49 +0800418 group = parser_lease_dut.add_mutually_exclusive_group(required=True)
419 group.add_argument('--session', help='session name')
420 group.add_argument('--reason', help='specify lease reason manually')
Kuang-che Wuca456462019-11-04 17:32:55 +0800421 parser_lease_dut.add_argument('dut')
422 parser_lease_dut.add_argument(
423 '--duration',
424 type=float,
425 help='duration in seconds; will be round to minutes')
426 parser_lease_dut.set_defaults(func=cmd_lease_dut)
427
428 parser_release_dut = subparsers.add_parser(
429 'release_dut',
430 help='Release a DUT in the lab',
Kuang-che Wud2d6e412021-01-28 16:26:41 +0800431 parents=parents,
Kuang-che Wuca456462019-11-04 17:32:55 +0800432 description='Release a DUT in the lab. '
433 'This is implemented by `skylab release-dut` with additional checking.')
Kuang-che Wuca456462019-11-04 17:32:55 +0800434 parser_release_dut.add_argument('dut')
435 parser_release_dut.set_defaults(func=cmd_release_dut)
436
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800437 parser_allocate_dut = subparsers.add_parser(
438 'allocate_dut',
439 help='Allocate a DUT in the lab',
Kuang-che Wud2d6e412021-01-28 16:26:41 +0800440 parents=[common.common_argument_parser],
Kuang-che Wuca456462019-11-04 17:32:55 +0800441 description='Allocate a DUT in the lab. It will lease a DUT in the lab '
442 'for bisecting. The caller (bisect-kit runner) of this command should '
443 'retry this command again later if no DUT available now.')
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800444 parser_allocate_dut.add_argument(
445 '--session', required=True, help='session name')
446 parser_allocate_dut.add_argument(
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800447 '--pool',
448 help='Pool to search DUT (default: %(default)s)',
449 default=DEFAULT_DUT_POOL)
Zheng-Jie Chang21a5b152021-05-06 13:45:46 +0800450 parser_allocate_dut.add_argument(
451 '--dimensions',
452 help='Dimension filters used for searching DUTs, in format key1=val1 key2=val2 ...',
453 type=parse_dimension,
Zheng-Jie Chang1b8c96b2021-05-12 21:27:58 +0800454 nargs='+',
455 default=[])
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800456 group = parser_allocate_dut.add_mutually_exclusive_group(required=True)
457 group.add_argument('--board', help='allocation criteria; comma separated')
458 group.add_argument('--model', help='allocation criteria; comma separated')
459 group.add_argument('--sku', help='allocation criteria; comma separated')
460 group.add_argument('--dut_name', help='allocation criteria; comma separated')
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800461 parser_allocate_dut.add_argument(
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800462 '--version_hint', help='chromeos version; comma separated')
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800463 parser_allocate_dut.add_argument(
464 '--builder_hint', help='chromeos builder; comma separated')
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800465 # Pubsub ack deadline is 10 minutes (b/143663659). Default 9 minutes with 1
466 # minute buffer.
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800467 parser_allocate_dut.add_argument(
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800468 '--time_limit',
469 type=int,
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800470 default=9 * 60,
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800471 help='Time limit to attempt lease in seconds (default: %(default)s)')
472 parser_allocate_dut.add_argument(
473 '--duration',
474 type=float,
475 help='lease duration in seconds; will be round to minutes')
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800476 parser_allocate_dut.add_argument(
477 '--parallel',
478 type=int,
479 default=1,
480 help='Submit multiple lease tasks to speed up (default: %(default)d)')
Kuang-che Wu9d14c162020-11-03 19:35:18 +0800481 parser_allocate_dut.add_argument(
482 '--chromeos_root',
483 type=cli.argtype_dir_path,
484 default=configure.get('DEFAULT_CHROMEOS_ROOT',
485 os.path.expanduser('~/chromiumos')),
486 help='Chrome OS source tree, for overlay data (default: %(default)s)')
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800487 parser_allocate_dut.set_defaults(func=cmd_allocate_dut)
488
Kuang-che Wua8c3c3e2019-08-28 18:49:28 +0800489 parser_repair_dut = subparsers.add_parser(
490 'repair_dut',
491 help='Repair a DUT in the lab',
Kuang-che Wud2d6e412021-01-28 16:26:41 +0800492 parents=parents,
Kuang-che Wua8c3c3e2019-08-28 18:49:28 +0800493 description='Repair a DUT in the lab. '
494 'This is simply wrapper of "deploy repair" with additional checking.')
495 parser_repair_dut.add_argument('dut')
496 parser_repair_dut.set_defaults(func=cmd_repair_dut)
497
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800498 opts = parser.parse_args()
499 common.config_logging(opts)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800500 opts.func(opts)
501
502
503if __name__ == '__main__':
504 main()