Kuang-che Wu | 875c89a | 2020-01-08 14:30:55 +0800 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
Kuang-che Wu | 6e4beca | 2018-06-27 17:45:02 +0800 | [diff] [blame] | 2 | # -*- coding: utf-8 -*- |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 3 | # 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 Wu | 68db08a | 2018-03-30 11:50:34 +0800 | [diff] [blame] | 6 | """Helper script to manipulate chromeos DUT or query info.""" |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 7 | from __future__ import print_function |
| 8 | import argparse |
Kuang-che Wu | 9d14c16 | 2020-11-03 19:35:18 +0800 | [diff] [blame] | 9 | import asyncio |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 10 | import json |
| 11 | import logging |
Kuang-che Wu | 9d14c16 | 2020-11-03 19:35:18 +0800 | [diff] [blame] | 12 | import os |
Kuang-che Wu | 0c9b794 | 2019-10-30 16:55:39 +0800 | [diff] [blame] | 13 | import random |
| 14 | import time |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 15 | |
Kuang-che Wu | fe1e88a | 2019-09-10 21:52:25 +0800 | [diff] [blame] | 16 | from bisect_kit import cli |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 17 | from bisect_kit import common |
Kuang-che Wu | 9d14c16 | 2020-11-03 19:35:18 +0800 | [diff] [blame] | 18 | from bisect_kit import configure |
Kuang-che Wu | c45cfa4 | 2019-01-15 00:15:01 +0800 | [diff] [blame] | 19 | from bisect_kit import cros_lab_util |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 20 | from bisect_kit import cros_util |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 21 | from bisect_kit import errors |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 22 | |
Zheng-Jie Chang | 17f36c8 | 2020-06-16 05:21:59 +0800 | [diff] [blame] | 23 | DEFAULT_DUT_POOL = 'DUT_POOL_QUOTA' |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 24 | logger = logging.getLogger(__name__) |
| 25 | |
Kuang-che Wu | 2572342 | 2020-09-24 22:20:26 +0800 | [diff] [blame] | 26 | models_to_avoid = { |
| 27 | # model: reason |
Kuang-che Wu | 2572342 | 2020-09-24 22:20:26 +0800 | [diff] [blame] | 28 | } |
| 29 | |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 30 | |
| 31 | def 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 | |
| 41 | def cmd_query_dut_board(opts): |
| 42 | assert cros_util.is_dut(opts.dut) |
| 43 | print(cros_util.query_dut_board(opts.dut)) |
| 44 | |
| 45 | |
| 46 | def cmd_reboot(opts): |
Kuang-che Wu | 2534bab | 2020-10-23 17:37:16 +0800 | [diff] [blame] | 47 | 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 Wu | 2ac9a92 | 2020-09-03 16:50:12 +0800 | [diff] [blame] | 54 | cros_util.reboot( |
| 55 | opts.dut, force_reboot_callback=cros_lab_util.reboot_via_servo) |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 56 | |
| 57 | |
Kuang-che Wu | c45cfa4 | 2019-01-15 00:15:01 +0800 | [diff] [blame] | 58 | def _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 Wu | ca45646 | 2019-11-04 17:32:55 +0800 | [diff] [blame] | 65 | def cmd_lease_dut(opts): |
Kuang-che Wu | 5157dee | 2020-07-18 01:13:41 +0800 | [diff] [blame] | 66 | if opts.duration is not None and opts.duration < 60: |
| 67 | raise errors.ArgumentError('--duration', 'must be at least 60 seconds') |
Kuang-che Wu | 7c1fa58 | 2021-01-14 17:57:49 +0800 | [diff] [blame] | 68 | reason = opts.reason or cros_lab_util.make_lease_reason(opts.session) |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 69 | host = cros_lab_util.dut_host_name(opts.dut) |
Kuang-che Wu | 220cc16 | 2019-10-31 00:29:37 +0800 | [diff] [blame] | 70 | logger.info('trying to lease %s', host) |
Kuang-che Wu | 7c1fa58 | 2021-01-14 17:57:49 +0800 | [diff] [blame] | 71 | if cros_lab_util.skylab_lease_dut(host, reason, opts.duration): |
Kuang-che Wu | ca45646 | 2019-11-04 17:32:55 +0800 | [diff] [blame] | 72 | logger.info('leased %s', host) |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 73 | else: |
Kuang-che Wu | ca45646 | 2019-11-04 17:32:55 +0800 | [diff] [blame] | 74 | raise Exception('unable to lease %s' % host) |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 75 | |
| 76 | |
Kuang-che Wu | ca45646 | 2019-11-04 17:32:55 +0800 | [diff] [blame] | 77 | def cmd_release_dut(opts): |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 78 | host = cros_lab_util.dut_host_name(opts.dut) |
Kuang-che Wu | 220cc16 | 2019-10-31 00:29:37 +0800 | [diff] [blame] | 79 | cros_lab_util.skylab_release_dut(host) |
Kuang-che Wu | ca45646 | 2019-11-04 17:32:55 +0800 | [diff] [blame] | 80 | logger.info('%s released', host) |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 81 | |
| 82 | |
Zheng-Jie Chang | 21a5b15 | 2021-05-06 13:45:46 +0800 | [diff] [blame] | 83 | def verify_dimensions_by_lab(dimensions, tolerate_unknown=False): |
| 84 | dimensions = sorted(set(dimensions)) |
Kuang-che Wu | 1e56ce2 | 2020-06-29 11:21:51 +0800 | [diff] [blame] | 85 | 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 Chang | 21a5b15 | 2021-05-06 13:45:46 +0800 | [diff] [blame] | 92 | 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 Wu | 1e56ce2 | 2020-06-29 11:21:51 +0800 | [diff] [blame] | 98 | return result |
| 99 | |
| 100 | |
Kuang-che Wu | eac89bd | 2020-10-23 16:07:15 +0800 | [diff] [blame] | 101 | def select_available_bots_randomly(dimensions, |
| 102 | variants, |
| 103 | num=1, |
| 104 | is_busy=None, |
| 105 | filter_func=None): |
Kuang-che Wu | 1e56ce2 | 2020-06-29 11:21:51 +0800 | [diff] [blame] | 106 | 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 Wu | 2572342 | 2020-09-24 22:20:26 +0800 | [diff] [blame] | 112 | |
Kuang-che Wu | eac89bd | 2020-10-23 16:07:15 +0800 | [diff] [blame] | 113 | if filter_func: |
| 114 | bots = list(filter(filter_func, bots)) |
| 115 | return random.sample(bots, min(num, len(bots))) |
Kuang-che Wu | 5157dee | 2020-07-18 01:13:41 +0800 | [diff] [blame] | 116 | |
| 117 | |
Kuang-che Wu | 8480a8a | 2021-04-21 15:44:59 +0800 | [diff] [blame] | 118 | def filter_dimensions_by_board(boards_with_prebuilt, dimensions, pool): |
| 119 | result = set() |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 120 | for dimension in dimensions: |
Kuang-che Wu | 8480a8a | 2021-04-21 15:44:59 +0800 | [diff] [blame] | 121 | 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 Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 125 | 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 Wu | 8480a8a | 2021-04-21 15:44:59 +0800 | [diff] [blame] | 133 | result.add(dimension) |
| 134 | return list(result) |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 135 | |
| 136 | |
Kuang-che Wu | eac89bd | 2020-10-23 16:07:15 +0800 | [diff] [blame] | 137 | def 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 Wu | 79ea119 | 2020-10-27 15:16:05 +0800 | [diff] [blame] | 144 | 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 Wu | eac89bd | 2020-10-23 16:07:15 +0800 | [diff] [blame] | 154 | |
| 155 | return True |
Kuang-che Wu | 0461977 | 2020-10-22 18:57:07 +0800 | [diff] [blame] | 156 | |
| 157 | |
Kuang-che Wu | 7c1fa58 | 2021-01-14 17:57:49 +0800 | [diff] [blame] | 158 | async def lease_dut_parallelly(duration, bots, reason, timeout=None): |
Kuang-che Wu | 5157dee | 2020-07-18 01:13:41 +0800 | [diff] [blame] | 159 | tasks = [] |
| 160 | hosts = [] |
| 161 | for bot in bots: |
| 162 | host = bot['dimensions']['dut_name'][0] |
| 163 | hosts.append(host) |
| 164 | tasks.append( |
Kuang-che Wu | 7c1fa58 | 2021-01-14 17:57:49 +0800 | [diff] [blame] | 165 | asyncio.create_task( |
| 166 | cros_lab_util.async_lease(host, reason, duration=duration))) |
Kuang-che Wu | 5157dee | 2020-07-18 01:13:41 +0800 | [diff] [blame] | 167 | |
| 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 Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 174 | # Unfinished lease tasks will be cancelled when asyncio.run is |
| 175 | # finishing. |
Kuang-che Wu | 5157dee | 2020-07-18 01:13:41 +0800 | [diff] [blame] | 176 | return host |
| 177 | return None |
| 178 | except asyncio.TimeoutError: |
| 179 | return None |
Kuang-che Wu | 1e56ce2 | 2020-06-29 11:21:51 +0800 | [diff] [blame] | 180 | |
| 181 | |
Kuang-che Wu | 9d14c16 | 2020-11-03 19:35:18 +0800 | [diff] [blame] | 182 | def 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 Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 200 | def do_allocate_dut(opts): |
| 201 | """Helper of cmd_allocate_dut. |
| 202 | |
| 203 | Returns: |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 204 | (todo, host, board_to_build) |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 205 | todo: 'ready' or 'wait' |
Kuang-che Wu | ca45646 | 2019-11-04 17:32:55 +0800 | [diff] [blame] | 206 | host: leased host name |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 207 | board_to_build: board name for building image |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 208 | """ |
Kuang-che Wu | 1e56ce2 | 2020-06-29 11:21:51 +0800 | [diff] [blame] | 209 | 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 Wu | 7e8abe6 | 2020-07-02 09:42:27 +0800 | [diff] [blame] | 212 | 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 Wu | 5157dee | 2020-07-18 01:13:41 +0800 | [diff] [blame] | 219 | if opts.duration is not None and opts.duration < 60: |
| 220 | raise errors.ArgumentError('--duration', 'must be at least 60 seconds') |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 221 | |
Kuang-che Wu | c26dcdf | 2019-11-01 16:30:06 +0800 | [diff] [blame] | 222 | t0 = time.time() |
Zheng-Jie Chang | 17f36c8 | 2020-06-16 05:21:59 +0800 | [diff] [blame] | 223 | dimensions = ['dut_state:ready'] |
Kuang-che Wu | 8480a8a | 2021-04-21 15:44:59 +0800 | [diff] [blame] | 224 | if opts.dut_name: |
| 225 | # If dut_name is specified, pool is ignored. |
| 226 | opts.pool = None |
| 227 | else: |
Zheng-Jie Chang | 17f36c8 | 2020-06-16 05:21:59 +0800 | [diff] [blame] | 228 | dimensions.append('label-pool:' + opts.pool) |
Zheng-Jie Chang | 21a5b15 | 2021-05-06 13:45:46 +0800 | [diff] [blame] | 229 | for key, value in opts.dimensions: |
| 230 | dimensions.append('%s:%s' % (key, value)) |
| 231 | dimensions = verify_dimensions_by_lab(dimensions) |
Zheng-Jie Chang | 17f36c8 | 2020-06-16 05:21:59 +0800 | [diff] [blame] | 232 | |
Kuang-che Wu | 1e56ce2 | 2020-06-29 11:21:51 +0800 | [diff] [blame] | 233 | variants = [] |
| 234 | if opts.board: |
| 235 | for board in opts.board.split(','): |
Kuang-che Wu | 1e56ce2 | 2020-06-29 11:21:51 +0800 | [diff] [blame] | 236 | variants.append('label-board:' + |
Kuang-che Wu | 9d14c16 | 2020-11-03 19:35:18 +0800 | [diff] [blame] | 237 | normalize_board_name(opts.chromeos_root, board)) |
Kuang-che Wu | 0c9b794 | 2019-10-30 16:55:39 +0800 | [diff] [blame] | 238 | if opts.model: |
Kuang-che Wu | 1e56ce2 | 2020-06-29 11:21:51 +0800 | [diff] [blame] | 239 | for model in opts.model.split(','): |
Kuang-che Wu | 2572342 | 2020-09-24 22:20:26 +0800 | [diff] [blame] | 240 | 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 Wu | 1e56ce2 | 2020-06-29 11:21:51 +0800 | [diff] [blame] | 244 | variants.append('label-model:' + model) |
Kuang-che Wu | 2572342 | 2020-09-24 22:20:26 +0800 | [diff] [blame] | 245 | if not variants: |
| 246 | raise errors.ArgumentError('--model', |
| 247 | 'all specified models are not supported') |
Kuang-che Wu | 0c9b794 | 2019-10-30 16:55:39 +0800 | [diff] [blame] | 248 | if opts.sku: |
Kuang-che Wu | 1e56ce2 | 2020-06-29 11:21:51 +0800 | [diff] [blame] | 249 | 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 Chang | 21a5b15 | 2021-05-06 13:45:46 +0800 | [diff] [blame] | 255 | variants = verify_dimensions_by_lab(variants, tolerate_unknown=True) |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 256 | 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 Wu | 79ea119 | 2020-10-27 15:16:05 +0800 | [diff] [blame] | 262 | boards_with_prebuilt = None |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 263 | 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 Wu | 9d14c16 | 2020-11-03 19:35:18 +0800 | [diff] [blame] | 277 | boards_with_prebuilt.append( |
| 278 | normalize_board_name(opts.chromeos_root, builder)) |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 279 | 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 Wu | 8480a8a | 2021-04-21 15:44:59 +0800 | [diff] [blame] | 284 | variants = filter_dimensions_by_board(boards_with_prebuilt, variants, |
| 285 | opts.pool) |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 286 | if not variants: |
| 287 | raise errors.NoDutAvailable( |
| 288 | 'Devices with specified constraints have no prebuilt. ' |
| 289 | 'Wrong version number?') |
Kuang-che Wu | 0c9b794 | 2019-10-30 16:55:39 +0800 | [diff] [blame] | 290 | |
Kuang-che Wu | c26dcdf | 2019-11-01 16:30:06 +0800 | [diff] [blame] | 291 | while True: |
Kuang-che Wu | eac89bd | 2020-10-23 16:07:15 +0800 | [diff] [blame] | 292 | filter_func = lambda bot: is_acceptable_bot(boards_with_prebuilt, bot) |
Kuang-che Wu | 5157dee | 2020-07-18 01:13:41 +0800 | [diff] [blame] | 293 | # Query every time because each iteration takes a few minutes |
| 294 | bots = select_available_bots_randomly( |
Kuang-che Wu | eac89bd | 2020-10-23 16:07:15 +0800 | [diff] [blame] | 295 | dimensions, |
| 296 | variants, |
| 297 | num=opts.parallel, |
| 298 | is_busy=False, |
| 299 | filter_func=filter_func) |
Kuang-che Wu | 5157dee | 2020-07-18 01:13:41 +0800 | [diff] [blame] | 300 | if not bots: |
| 301 | bots = select_available_bots_randomly( |
Kuang-che Wu | eac89bd | 2020-10-23 16:07:15 +0800 | [diff] [blame] | 302 | dimensions, |
| 303 | variants, |
| 304 | num=opts.parallel, |
| 305 | is_busy=True, |
| 306 | filter_func=filter_func) |
Kuang-che Wu | 5157dee | 2020-07-18 01:13:41 +0800 | [diff] [blame] | 307 | if not bots: |
Kuang-che Wu | 0c9b794 | 2019-10-30 16:55:39 +0800 | [diff] [blame] | 308 | raise errors.NoDutAvailable( |
Kuang-che Wu | 1e56ce2 | 2020-06-29 11:21:51 +0800 | [diff] [blame] | 309 | 'no bots satisfy constraints; all are in maintenance state?') |
Kuang-che Wu | 0c9b794 | 2019-10-30 16:55:39 +0800 | [diff] [blame] | 310 | |
Kuang-che Wu | c26dcdf | 2019-11-01 16:30:06 +0800 | [diff] [blame] | 311 | remaining_time = opts.time_limit - (time.time() - t0) |
| 312 | if remaining_time <= 0: |
| 313 | break |
Kuang-che Wu | 5157dee | 2020-07-18 01:13:41 +0800 | [diff] [blame] | 314 | timeout = min(120, remaining_time) |
Kuang-che Wu | 7c1fa58 | 2021-01-14 17:57:49 +0800 | [diff] [blame] | 315 | reason = cros_lab_util.make_lease_reason(opts.session) |
| 316 | host = asyncio.run( |
| 317 | lease_dut_parallelly(opts.duration, bots, reason, timeout)) |
Kuang-che Wu | 5157dee | 2020-07-18 01:13:41 +0800 | [diff] [blame] | 318 | if host: |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 319 | # 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 Wu | 9d14c16 | 2020-11-03 19:35:18 +0800 | [diff] [blame] | 325 | if normalize_board_name(opts.chromeos_root, builder) == host_board: |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 326 | board_to_build = builder |
| 327 | break |
Kuang-che Wu | 0461977 | 2020-10-22 18:57:07 +0800 | [diff] [blame] | 328 | else: |
| 329 | raise errors.DutLeaseException('DUT with unexpected board:%s' % |
| 330 | host_board) |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 331 | else: |
| 332 | board_to_build = host_board |
| 333 | |
| 334 | return 'ready', host, board_to_build |
Kuang-che Wu | c26dcdf | 2019-11-01 16:30:06 +0800 | [diff] [blame] | 335 | time.sleep(1) |
Kuang-che Wu | 0c9b794 | 2019-10-30 16:55:39 +0800 | [diff] [blame] | 336 | |
Kuang-che Wu | c26dcdf | 2019-11-01 16:30:06 +0800 | [diff] [blame] | 337 | logger.warning('unable to lease DUT in time limit') |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 338 | return 'wait', None, None |
Kuang-che Wu | 0c9b794 | 2019-10-30 16:55:39 +0800 | [diff] [blame] | 339 | |
| 340 | |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 341 | def cmd_allocate_dut(opts): |
Kuang-che Wu | ca45646 | 2019-11-04 17:32:55 +0800 | [diff] [blame] | 342 | leased_dut = None |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 343 | try: |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 344 | todo, host, board = do_allocate_dut(opts) |
Kuang-che Wu | 5157dee | 2020-07-18 01:13:41 +0800 | [diff] [blame] | 345 | leased_dut = cros_lab_util.dut_name_to_address(host) if host else None |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 346 | result = {'result': todo, 'leased_dut': leased_dut, 'board': board} |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 347 | 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 Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 357 | |
| 358 | |
Kuang-che Wu | a8c3c3e | 2019-08-28 18:49:28 +0800 | [diff] [blame] | 359 | def cmd_repair_dut(opts): |
| 360 | cros_lab_util.repair(opts.dut) |
| 361 | |
| 362 | |
Zheng-Jie Chang | 21a5b15 | 2021-05-06 13:45:46 +0800 | [diff] [blame] | 363 | def 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 Wu | fe1e88a | 2019-09-10 21:52:25 +0800 | [diff] [blame] | 370 | @cli.fatal_error_handler |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 371 | def main(): |
| 372 | common.init() |
Kuang-che Wu | d2d6e41 | 2021-01-28 16:26:41 +0800 | [diff] [blame] | 373 | parents = [common.common_argument_parser, common.session_optional_parser] |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 374 | parser = argparse.ArgumentParser() |
Kuang-che Wu | fe1e88a | 2019-09-10 21:52:25 +0800 | [diff] [blame] | 375 | cli.patching_argparser_exit(parser) |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 376 | subparsers = parser.add_subparsers( |
Kuang-che Wu | d2d6e41 | 2021-01-28 16:26:41 +0800 | [diff] [blame] | 377 | dest='command', title='commands', metavar='<command>', required=True) |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 378 | |
| 379 | parser_version_info = subparsers.add_parser( |
| 380 | 'version_info', |
| 381 | help='Query version info of given chromeos build', |
Kuang-che Wu | d2d6e41 | 2021-01-28 16:26:41 +0800 | [diff] [blame] | 382 | parents=parents, |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 383 | 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 Wu | d2d6e41 | 2021-01-28 16:26:41 +0800 | [diff] [blame] | 399 | 'query_dut_board', help='Query board name of given DUT', parents=parents) |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 400 | 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 Wu | d2d6e41 | 2021-01-28 16:26:41 +0800 | [diff] [blame] | 406 | parents=parents, |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 407 | description='Reboot a DUT and verify the reboot is successful.') |
Kuang-che Wu | 2534bab | 2020-10-23 17:37:16 +0800 | [diff] [blame] | 408 | parser_reboot.add_argument('--force', action='store_true') |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 409 | parser_reboot.add_argument('dut') |
| 410 | parser_reboot.set_defaults(func=cmd_reboot) |
| 411 | |
Kuang-che Wu | ca45646 | 2019-11-04 17:32:55 +0800 | [diff] [blame] | 412 | parser_lease_dut = subparsers.add_parser( |
| 413 | 'lease_dut', |
| 414 | help='Lease a DUT in the lab', |
Kuang-che Wu | d2d6e41 | 2021-01-28 16:26:41 +0800 | [diff] [blame] | 415 | parents=[common.common_argument_parser], |
Kuang-che Wu | ca45646 | 2019-11-04 17:32:55 +0800 | [diff] [blame] | 416 | description='Lease a DUT in the lab. ' |
| 417 | 'This is implemented by `skylab lease-dut` with additional checking.') |
Kuang-che Wu | 7c1fa58 | 2021-01-14 17:57:49 +0800 | [diff] [blame] | 418 | 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 Wu | ca45646 | 2019-11-04 17:32:55 +0800 | [diff] [blame] | 421 | 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 Wu | d2d6e41 | 2021-01-28 16:26:41 +0800 | [diff] [blame] | 431 | parents=parents, |
Kuang-che Wu | ca45646 | 2019-11-04 17:32:55 +0800 | [diff] [blame] | 432 | description='Release a DUT in the lab. ' |
| 433 | 'This is implemented by `skylab release-dut` with additional checking.') |
Kuang-che Wu | ca45646 | 2019-11-04 17:32:55 +0800 | [diff] [blame] | 434 | parser_release_dut.add_argument('dut') |
| 435 | parser_release_dut.set_defaults(func=cmd_release_dut) |
| 436 | |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 437 | parser_allocate_dut = subparsers.add_parser( |
| 438 | 'allocate_dut', |
| 439 | help='Allocate a DUT in the lab', |
Kuang-che Wu | d2d6e41 | 2021-01-28 16:26:41 +0800 | [diff] [blame] | 440 | parents=[common.common_argument_parser], |
Kuang-che Wu | ca45646 | 2019-11-04 17:32:55 +0800 | [diff] [blame] | 441 | 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 Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 444 | parser_allocate_dut.add_argument( |
| 445 | '--session', required=True, help='session name') |
| 446 | parser_allocate_dut.add_argument( |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 447 | '--pool', |
| 448 | help='Pool to search DUT (default: %(default)s)', |
| 449 | default=DEFAULT_DUT_POOL) |
Zheng-Jie Chang | 21a5b15 | 2021-05-06 13:45:46 +0800 | [diff] [blame] | 450 | 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 Chang | 1b8c96b | 2021-05-12 21:27:58 +0800 | [diff] [blame] | 454 | nargs='+', |
| 455 | default=[]) |
Kuang-che Wu | 1e56ce2 | 2020-06-29 11:21:51 +0800 | [diff] [blame] | 456 | 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 Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 461 | parser_allocate_dut.add_argument( |
Kuang-che Wu | 1e56ce2 | 2020-06-29 11:21:51 +0800 | [diff] [blame] | 462 | '--version_hint', help='chromeos version; comma separated') |
Kuang-che Wu | b529d2d | 2020-09-10 12:26:56 +0800 | [diff] [blame] | 463 | parser_allocate_dut.add_argument( |
| 464 | '--builder_hint', help='chromeos builder; comma separated') |
Kuang-che Wu | c26dcdf | 2019-11-01 16:30:06 +0800 | [diff] [blame] | 465 | # Pubsub ack deadline is 10 minutes (b/143663659). Default 9 minutes with 1 |
| 466 | # minute buffer. |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 467 | parser_allocate_dut.add_argument( |
Kuang-che Wu | 0c9b794 | 2019-10-30 16:55:39 +0800 | [diff] [blame] | 468 | '--time_limit', |
| 469 | type=int, |
Kuang-che Wu | c26dcdf | 2019-11-01 16:30:06 +0800 | [diff] [blame] | 470 | default=9 * 60, |
Kuang-che Wu | 0c9b794 | 2019-10-30 16:55:39 +0800 | [diff] [blame] | 471 | 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 Wu | 5157dee | 2020-07-18 01:13:41 +0800 | [diff] [blame] | 476 | 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 Wu | 9d14c16 | 2020-11-03 19:35:18 +0800 | [diff] [blame] | 481 | 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 Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 487 | parser_allocate_dut.set_defaults(func=cmd_allocate_dut) |
| 488 | |
Kuang-che Wu | a8c3c3e | 2019-08-28 18:49:28 +0800 | [diff] [blame] | 489 | parser_repair_dut = subparsers.add_parser( |
| 490 | 'repair_dut', |
| 491 | help='Repair a DUT in the lab', |
Kuang-che Wu | d2d6e41 | 2021-01-28 16:26:41 +0800 | [diff] [blame] | 492 | parents=parents, |
Kuang-che Wu | a8c3c3e | 2019-08-28 18:49:28 +0800 | [diff] [blame] | 493 | 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 Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 498 | opts = parser.parse_args() |
| 499 | common.config_logging(opts) |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 500 | opts.func(opts) |
| 501 | |
| 502 | |
| 503 | if __name__ == '__main__': |
| 504 | main() |