blob: a56fb84538b4dcfa29c5613d4fdd37cdea4a3c40 [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
28 'kasumi': 'b/160458394 stateful partition is too small',
29 'mimrock': 'b/160458394 stateful partition is too small',
30 'vorticon': 'b/160458394 stateful partition is too small',
31}
32
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080033
34def cmd_version_info(opts):
35 info = cros_util.version_info(opts.board, opts.version)
36 if opts.name:
37 if opts.name not in info:
38 logger.error('unknown name=%s', opts.name)
39 print(info[opts.name])
40 else:
41 print(json.dumps(info, sort_keys=True, indent=4))
42
43
44def cmd_query_dut_board(opts):
45 assert cros_util.is_dut(opts.dut)
46 print(cros_util.query_dut_board(opts.dut))
47
48
49def cmd_reboot(opts):
Kuang-che Wu2534bab2020-10-23 17:37:16 +080050 if not cros_util.is_dut(opts.dut):
51 if opts.force:
52 logger.warning('%s is not a Chrome OS device?', opts.dut)
53 else:
54 raise errors.ArgumentError(
55 'dut', 'not a Chrome OS device (--force to continue)')
56
Kuang-che Wu2ac9a922020-09-03 16:50:12 +080057 cros_util.reboot(
58 opts.dut, force_reboot_callback=cros_lab_util.reboot_via_servo)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080059
60
Kuang-che Wuc45cfa42019-01-15 00:15:01 +080061def _get_label_by_prefix(info, prefix):
62 for label in info['Labels']:
63 if label.startswith(prefix + ':'):
64 return label
65 return None
66
67
Kuang-che Wuca456462019-11-04 17:32:55 +080068def cmd_lease_dut(opts):
Kuang-che Wu5157dee2020-07-18 01:13:41 +080069 if opts.duration is not None and opts.duration < 60:
70 raise errors.ArgumentError('--duration', 'must be at least 60 seconds')
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080071 host = cros_lab_util.dut_host_name(opts.dut)
Kuang-che Wu220cc162019-10-31 00:29:37 +080072 logger.info('trying to lease %s', host)
73 if cros_lab_util.skylab_lease_dut(host, opts.duration):
Kuang-che Wuca456462019-11-04 17:32:55 +080074 logger.info('leased %s', host)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080075 else:
Kuang-che Wuca456462019-11-04 17:32:55 +080076 raise Exception('unable to lease %s' % host)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080077
78
Kuang-che Wuca456462019-11-04 17:32:55 +080079def cmd_release_dut(opts):
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080080 host = cros_lab_util.dut_host_name(opts.dut)
Kuang-che Wu220cc162019-10-31 00:29:37 +080081 cros_lab_util.skylab_release_dut(host)
Kuang-che Wuca456462019-11-04 17:32:55 +080082 logger.info('%s released', host)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080083
84
Kuang-che Wu1e56ce22020-06-29 11:21:51 +080085def verify_dimensions_by_lab(dimensions):
86 result = []
87 bots_dimensions = cros_lab_util.swarming_bots_dimensions()
88 for dimension in dimensions:
89 key, value = dimension.split(':', 1)
90 if value in bots_dimensions.get(key, []):
91 result.append(dimension)
92 else:
93 logger.warning('dimension=%s is unknown in the lab, typo? ignored',
94 dimension)
95 return result
96
97
Kuang-che Wueac89bd2020-10-23 16:07:15 +080098def select_available_bots_randomly(dimensions,
99 variants,
100 num=1,
101 is_busy=None,
102 filter_func=None):
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800103 bots = []
104 for variant in variants:
105 # There might be thousand bots available, set 'limit' to reduce swarming
106 # API cost. This is not uniform random, but should be good enough.
107 bots += cros_lab_util.swarming_bots_list(
108 dimensions + [variant], is_busy=is_busy, limit=10)
Kuang-che Wu25723422020-09-24 22:20:26 +0800109
Kuang-che Wueac89bd2020-10-23 16:07:15 +0800110 if filter_func:
111 bots = list(filter(filter_func, bots))
112 return random.sample(bots, min(num, len(bots)))
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800113
114
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800115def filter_dimensions_by_board(boards_with_prebuilt, dimensions):
116 result = []
117 for dimension in dimensions:
118 bots = cros_lab_util.swarming_bots_list([dimension], is_busy=None, limit=1)
119 if not bots:
120 continue
121 board = bots[0]['dimensions']['label-board'][0]
122 if board not in boards_with_prebuilt:
123 logger.warning(
124 'dimension=%s (board=%s) does not have corresponding '
125 'prebuilt image, ignore', dimension, board)
126 continue
127 result.append(dimension)
128 return result
129
130
Kuang-che Wueac89bd2020-10-23 16:07:15 +0800131def is_acceptable_bot(boards_with_prebuilt, bot):
132 model = bot['dimensions']['label-model'][0]
133 if model in models_to_avoid:
134 logger.warning('model=%s is bad (reason:%s), ignore', model,
135 models_to_avoid[model])
136 return False
137
Kuang-che Wu79ea1192020-10-27 15:16:05 +0800138 if boards_with_prebuilt is not None:
139 # Sometimes swarming database has inconsistent records. For example,
140 # label-model=kefka + label-board=strago are incorrect (should be
141 # label-board=kefka). It is probably human errors (strago is kefka's
142 # reference board).
143 board = bot['dimensions']['label-board'][0]
144 if board not in boards_with_prebuilt:
145 logger.warning('%s has unexpected board=%s, ignore',
146 bot['dimensions']['dut_name'][0], board)
147 return False
Kuang-che Wueac89bd2020-10-23 16:07:15 +0800148
149 return True
Kuang-che Wu04619772020-10-22 18:57:07 +0800150
151
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800152async def lease_dut_parallelly(duration, bots, timeout=None):
153 tasks = []
154 hosts = []
155 for bot in bots:
156 host = bot['dimensions']['dut_name'][0]
157 hosts.append(host)
158 tasks.append(
159 asyncio.create_task(cros_lab_util.async_lease(host, duration=duration)))
160
161 try:
162 logger.info('trying to lease %d DUTs: %s', len(hosts), hosts)
163 for coro in asyncio.as_completed(tasks, timeout=timeout):
164 host = await coro
165 if host:
166 logger.info('leased %s', host)
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800167 # Unfinished lease tasks will be cancelled when asyncio.run is
168 # finishing.
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800169 return host
170 return None
171 except asyncio.TimeoutError:
172 return None
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800173
174
Kuang-che Wu9d14c162020-11-03 19:35:18 +0800175def normalize_board_name(chromeos_root, board):
176 """Normalize BOARD name.
177
178 Here, we want to find the actual device board. Suffixes like -kernelnext will
179 be removed. So we can use that name to query DUTs inside the lab.
180
181 Args:
182 chromeos_root: chromeos source root
183 board: BOARD name
184
185 Returns:
186 normalized BOARD name
187 """
188 overlays = cros_util.parse_chromeos_overlays(chromeos_root)
189 boards_info = cros_util.resolve_basic_boards(overlays)
190 return boards_info[board]
191
192
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800193def do_allocate_dut(opts):
194 """Helper of cmd_allocate_dut.
195
196 Returns:
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800197 (todo, host, board_to_build)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800198 todo: 'ready' or 'wait'
Kuang-che Wuca456462019-11-04 17:32:55 +0800199 host: leased host name
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800200 board_to_build: board name for building image
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800201 """
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800202 if not opts.dut_name and not opts.pool:
203 raise errors.ArgumentError('--pool',
204 'need to be specified if not --dut_name')
Kuang-che Wu7e8abe62020-07-02 09:42:27 +0800205 if opts.version_hint:
206 for v in opts.version_hint.split(','):
207 if cros_util.is_cros_version(v) or cros_util.is_cros_snapshot_version(v):
208 continue
209 raise errors.ArgumentError(
210 '--version_hint',
211 'should be Chrome OS version numbers, separated by comma')
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800212 if opts.duration is not None and opts.duration < 60:
213 raise errors.ArgumentError('--duration', 'must be at least 60 seconds')
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800214
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800215 t0 = time.time()
Zheng-Jie Chang17f36c82020-06-16 05:21:59 +0800216 dimensions = ['dut_state:ready']
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800217 if not opts.dut_name:
Zheng-Jie Chang17f36c82020-06-16 05:21:59 +0800218 dimensions.append('label-pool:' + opts.pool)
219
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800220 variants = []
221 if opts.board:
222 for board in opts.board.split(','):
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800223 variants.append('label-board:' +
Kuang-che Wu9d14c162020-11-03 19:35:18 +0800224 normalize_board_name(opts.chromeos_root, board))
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800225 if opts.model:
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800226 for model in opts.model.split(','):
Kuang-che Wu25723422020-09-24 22:20:26 +0800227 if model in models_to_avoid:
228 logger.warning('model=%s is bad (reason:%s), ignore', model,
229 models_to_avoid[model])
230 continue
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800231 variants.append('label-model:' + model)
Kuang-che Wu25723422020-09-24 22:20:26 +0800232 if not variants:
233 raise errors.ArgumentError('--model',
234 'all specified models are not supported')
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800235 if opts.sku:
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800236 for sku in opts.sku.split(','):
237 variants.append('label-hwid_sku:' + cros_lab_util.normalize_sku_name(sku))
238 if opts.dut_name:
239 for dut_name in opts.dut_name.split(','):
240 variants.append('dut_name:' + dut_name)
241
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800242 variants = verify_dimensions_by_lab(variants)
243 variants = sorted(set(variants)) # dedup
244 if not variants:
245 raise errors.NoDutAvailable(
246 'Invalid constraints: %s;%s;%s;%s' %
247 (opts.board, opts.model, opts.sku, opts.dut_name))
248
249 # Filter variants by prebuilt images.
Kuang-che Wu79ea1192020-10-27 15:16:05 +0800250 boards_with_prebuilt = None
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800251 if opts.version_hint:
252 if not opts.builder_hint:
253 opts.builder_hint = opts.board
254 if not opts.builder_hint:
255 raise errors.ArgumentError('--builder_hint',
256 'must be specified along with --version_hint')
257 boards_with_prebuilt = []
258 versions = opts.version_hint.split(',')
259 for builder in opts.builder_hint.split(','):
260 if not all(cros_util.has_test_image(builder, v) for v in versions):
261 logger.warning(
262 'builder=%s does not have prebuilt test image for %s, ignore',
263 builder, opts.version_hint)
264 continue
Kuang-che Wu9d14c162020-11-03 19:35:18 +0800265 boards_with_prebuilt.append(
266 normalize_board_name(opts.chromeos_root, builder))
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800267 logger.info('boards with prebuilt: %s', boards_with_prebuilt)
268 if not boards_with_prebuilt:
269 raise errors.ArgumentError(
270 '--version_hint',
271 'given builders have no prebuilt for %s' % opts.version_hint)
272 variants = filter_dimensions_by_board(boards_with_prebuilt, variants)
273 if not variants:
274 raise errors.NoDutAvailable(
275 'Devices with specified constraints have no prebuilt. '
276 'Wrong version number?')
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800277
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800278 while True:
Kuang-che Wueac89bd2020-10-23 16:07:15 +0800279 filter_func = lambda bot: is_acceptable_bot(boards_with_prebuilt, bot)
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800280 # Query every time because each iteration takes a few minutes
281 bots = select_available_bots_randomly(
Kuang-che Wueac89bd2020-10-23 16:07:15 +0800282 dimensions,
283 variants,
284 num=opts.parallel,
285 is_busy=False,
286 filter_func=filter_func)
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800287 if not bots:
288 bots = select_available_bots_randomly(
Kuang-che Wueac89bd2020-10-23 16:07:15 +0800289 dimensions,
290 variants,
291 num=opts.parallel,
292 is_busy=True,
293 filter_func=filter_func)
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800294 if not bots:
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800295 raise errors.NoDutAvailable(
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800296 'no bots satisfy constraints; all are in maintenance state?')
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800297
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800298 remaining_time = opts.time_limit - (time.time() - t0)
299 if remaining_time <= 0:
300 break
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800301 timeout = min(120, remaining_time)
302 host = asyncio.run(lease_dut_parallelly(opts.duration, bots, timeout))
303 if host:
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800304 # Resolve what board we should build during bisection.
305 board_to_build = None
306 bots = cros_lab_util.swarming_bots_list(['dut_name:' + host])
307 host_board = bots[0]['dimensions']['label-board'][0]
308 if opts.builder_hint:
309 for builder in opts.builder_hint.split(','):
Kuang-che Wu9d14c162020-11-03 19:35:18 +0800310 if normalize_board_name(opts.chromeos_root, builder) == host_board:
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800311 board_to_build = builder
312 break
Kuang-che Wu04619772020-10-22 18:57:07 +0800313 else:
314 raise errors.DutLeaseException('DUT with unexpected board:%s' %
315 host_board)
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800316 else:
317 board_to_build = host_board
318
319 return 'ready', host, board_to_build
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800320 time.sleep(1)
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800321
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800322 logger.warning('unable to lease DUT in time limit')
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800323 return 'wait', None, None
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800324
325
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800326def cmd_allocate_dut(opts):
Kuang-che Wuca456462019-11-04 17:32:55 +0800327 leased_dut = None
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800328 try:
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800329 todo, host, board = do_allocate_dut(opts)
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800330 leased_dut = cros_lab_util.dut_name_to_address(host) if host else None
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800331 result = {'result': todo, 'leased_dut': leased_dut, 'board': board}
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800332 print(json.dumps(result))
333 except Exception as e:
334 logger.exception('cmd_allocate_dut failed')
335 exception_name = e.__class__.__name__
336 result = {
337 'result': 'failed',
338 'exception': exception_name,
339 'text': str(e),
340 }
341 print(json.dumps(result))
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800342
343
Kuang-che Wua8c3c3e2019-08-28 18:49:28 +0800344def cmd_repair_dut(opts):
345 cros_lab_util.repair(opts.dut)
346
347
Kuang-che Wufe1e88a2019-09-10 21:52:25 +0800348@cli.fatal_error_handler
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800349def main():
350 common.init()
351 parser = argparse.ArgumentParser()
Kuang-che Wufe1e88a2019-09-10 21:52:25 +0800352 cli.patching_argparser_exit(parser)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800353 common.add_common_arguments(parser)
354 subparsers = parser.add_subparsers(
355 dest='command', title='commands', metavar='<command>')
356
357 parser_version_info = subparsers.add_parser(
358 'version_info',
359 help='Query version info of given chromeos build',
360 description='Given chromeos `board` and `version`, '
361 'print version information of components.')
362 parser_version_info.add_argument(
363 'board', help='ChromeOS board name, like "samus".')
364 parser_version_info.add_argument(
365 'version',
366 type=cros_util.argtype_cros_version,
367 help='ChromeOS version, like "9876.0.0" or "R62-9876.0.0"')
368 parser_version_info.add_argument(
369 'name',
370 nargs='?',
371 help='Component name. If specified, output its version string. '
372 'Otherwise output all version info as dict in json format.')
373 parser_version_info.set_defaults(func=cmd_version_info)
374
375 parser_query_dut_board = subparsers.add_parser(
376 'query_dut_board', help='Query board name of given DUT')
377 parser_query_dut_board.add_argument('dut')
378 parser_query_dut_board.set_defaults(func=cmd_query_dut_board)
379
380 parser_reboot = subparsers.add_parser(
381 'reboot',
382 help='Reboot a DUT',
383 description='Reboot a DUT and verify the reboot is successful.')
Kuang-che Wu2534bab2020-10-23 17:37:16 +0800384 parser_reboot.add_argument('--force', action='store_true')
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800385 parser_reboot.add_argument('dut')
386 parser_reboot.set_defaults(func=cmd_reboot)
387
Kuang-che Wuca456462019-11-04 17:32:55 +0800388 parser_lease_dut = subparsers.add_parser(
389 'lease_dut',
390 help='Lease a DUT in the lab',
391 description='Lease a DUT in the lab. '
392 'This is implemented by `skylab lease-dut` with additional checking.')
393 # "skylab lease-dut" doesn't take reason, so this is not required=True.
394 parser_lease_dut.add_argument('--session', help='session name')
395 parser_lease_dut.add_argument('dut')
396 parser_lease_dut.add_argument(
397 '--duration',
398 type=float,
399 help='duration in seconds; will be round to minutes')
400 parser_lease_dut.set_defaults(func=cmd_lease_dut)
401
402 parser_release_dut = subparsers.add_parser(
403 'release_dut',
404 help='Release a DUT in the lab',
405 description='Release a DUT in the lab. '
406 'This is implemented by `skylab release-dut` with additional checking.')
407 parser_release_dut.add_argument('--session', help='session name')
408 parser_release_dut.add_argument('dut')
409 parser_release_dut.set_defaults(func=cmd_release_dut)
410
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800411 parser_allocate_dut = subparsers.add_parser(
412 'allocate_dut',
413 help='Allocate a DUT in the lab',
Kuang-che Wuca456462019-11-04 17:32:55 +0800414 description='Allocate a DUT in the lab. It will lease a DUT in the lab '
415 'for bisecting. The caller (bisect-kit runner) of this command should '
416 'retry this command again later if no DUT available now.')
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800417 parser_allocate_dut.add_argument(
418 '--session', required=True, help='session name')
419 parser_allocate_dut.add_argument(
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800420 '--pool',
421 help='Pool to search DUT (default: %(default)s)',
422 default=DEFAULT_DUT_POOL)
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800423 group = parser_allocate_dut.add_mutually_exclusive_group(required=True)
424 group.add_argument('--board', help='allocation criteria; comma separated')
425 group.add_argument('--model', help='allocation criteria; comma separated')
426 group.add_argument('--sku', help='allocation criteria; comma separated')
427 group.add_argument('--dut_name', help='allocation criteria; comma separated')
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800428 parser_allocate_dut.add_argument(
Kuang-che Wu1e56ce22020-06-29 11:21:51 +0800429 '--version_hint', help='chromeos version; comma separated')
Kuang-che Wub529d2d2020-09-10 12:26:56 +0800430 parser_allocate_dut.add_argument(
431 '--builder_hint', help='chromeos builder; comma separated')
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800432 # Pubsub ack deadline is 10 minutes (b/143663659). Default 9 minutes with 1
433 # minute buffer.
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800434 parser_allocate_dut.add_argument(
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800435 '--time_limit',
436 type=int,
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800437 default=9 * 60,
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800438 help='Time limit to attempt lease in seconds (default: %(default)s)')
439 parser_allocate_dut.add_argument(
440 '--duration',
441 type=float,
442 help='lease duration in seconds; will be round to minutes')
Kuang-che Wu5157dee2020-07-18 01:13:41 +0800443 parser_allocate_dut.add_argument(
444 '--parallel',
445 type=int,
446 default=1,
447 help='Submit multiple lease tasks to speed up (default: %(default)d)')
Kuang-che Wu9d14c162020-11-03 19:35:18 +0800448 parser_allocate_dut.add_argument(
449 '--chromeos_root',
450 type=cli.argtype_dir_path,
451 default=configure.get('DEFAULT_CHROMEOS_ROOT',
452 os.path.expanduser('~/chromiumos')),
453 help='Chrome OS source tree, for overlay data (default: %(default)s)')
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800454 parser_allocate_dut.set_defaults(func=cmd_allocate_dut)
455
Kuang-che Wua8c3c3e2019-08-28 18:49:28 +0800456 parser_repair_dut = subparsers.add_parser(
457 'repair_dut',
458 help='Repair a DUT in the lab',
459 description='Repair a DUT in the lab. '
460 'This is simply wrapper of "deploy repair" with additional checking.')
461 parser_repair_dut.add_argument('dut')
462 parser_repair_dut.set_defaults(func=cmd_repair_dut)
463
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800464 opts = parser.parse_args()
465 common.config_logging(opts)
Kuang-che Wud3a4e842019-12-11 12:15:23 +0800466
467 # It's optional by default since python3.
468 if not opts.command:
469 parser.error('command is missing')
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800470 opts.func(opts)
471
472
473if __name__ == '__main__':
474 main()