Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 1 | #!/usr/bin/env python2 |
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 |
| 9 | import json |
| 10 | import logging |
Kuang-che Wu | 0c9b794 | 2019-10-30 16:55:39 +0800 | [diff] [blame] | 11 | import random |
| 12 | import time |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 13 | |
Kuang-che Wu | fe1e88a | 2019-09-10 21:52:25 +0800 | [diff] [blame] | 14 | from bisect_kit import cli |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 15 | from bisect_kit import common |
Kuang-che Wu | c45cfa4 | 2019-01-15 00:15:01 +0800 | [diff] [blame] | 16 | from bisect_kit import cros_lab_util |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 17 | from bisect_kit import cros_util |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 18 | from bisect_kit import errors |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 19 | |
| 20 | logger = logging.getLogger(__name__) |
| 21 | |
| 22 | |
| 23 | def cmd_version_info(opts): |
| 24 | info = cros_util.version_info(opts.board, opts.version) |
| 25 | if opts.name: |
| 26 | if opts.name not in info: |
| 27 | logger.error('unknown name=%s', opts.name) |
| 28 | print(info[opts.name]) |
| 29 | else: |
| 30 | print(json.dumps(info, sort_keys=True, indent=4)) |
| 31 | |
| 32 | |
| 33 | def cmd_query_dut_board(opts): |
| 34 | assert cros_util.is_dut(opts.dut) |
| 35 | print(cros_util.query_dut_board(opts.dut)) |
| 36 | |
| 37 | |
| 38 | def cmd_reboot(opts): |
| 39 | assert cros_util.is_dut(opts.dut) |
| 40 | cros_util.reboot(opts.dut) |
| 41 | |
| 42 | |
Kuang-che Wu | c45cfa4 | 2019-01-15 00:15:01 +0800 | [diff] [blame] | 43 | def _get_label_by_prefix(info, prefix): |
| 44 | for label in info['Labels']: |
| 45 | if label.startswith(prefix + ':'): |
| 46 | return label |
| 47 | return None |
| 48 | |
| 49 | |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 50 | def cmd_lock_dut(opts): |
| 51 | host = cros_lab_util.dut_host_name(opts.dut) |
Kuang-che Wu | 220cc16 | 2019-10-31 00:29:37 +0800 | [diff] [blame^] | 52 | logger.info('trying to lease %s', host) |
| 53 | if cros_lab_util.skylab_lease_dut(host, opts.duration): |
| 54 | logger.info('locked %s', host) |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 55 | else: |
Kuang-che Wu | 220cc16 | 2019-10-31 00:29:37 +0800 | [diff] [blame^] | 56 | raise Exception('unable to lock %s' % host) |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 57 | |
| 58 | |
| 59 | def cmd_unlock_dut(opts): |
| 60 | host = cros_lab_util.dut_host_name(opts.dut) |
Kuang-che Wu | 220cc16 | 2019-10-31 00:29:37 +0800 | [diff] [blame^] | 61 | cros_lab_util.skylab_release_dut(host) |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 62 | logger.info('%s unlocked', host) |
| 63 | |
| 64 | |
| 65 | def do_allocate_dut(opts): |
| 66 | """Helper of cmd_allocate_dut. |
| 67 | |
| 68 | Returns: |
| 69 | (todo, host) |
| 70 | todo: 'ready' or 'wait' |
| 71 | host: locked host name |
| 72 | """ |
| 73 | if not opts.model and not opts.sku: |
| 74 | raise errors.ArgumentError('--model or --sku', 'need to be specified') |
| 75 | |
Kuang-che Wu | 0c9b794 | 2019-10-30 16:55:39 +0800 | [diff] [blame] | 76 | dimensions = ['dut_state:ready', 'label-pool:DUT_POOL_QUOTA'] |
| 77 | if opts.model: |
| 78 | dimensions.append('label-model:' + opts.model) |
| 79 | if opts.sku: |
| 80 | dimensions.append('label-hwid_sku:' + |
| 81 | cros_lab_util.normalize_sku_name(opts.sku)) |
| 82 | |
| 83 | t0 = time.time() |
| 84 | while time.time() - t0 < opts.time_limit: |
| 85 | # Query every time because each iteration takes 10+ miuntes |
| 86 | bots = cros_lab_util.swarming_bots_list(dimensions, is_busy=False) |
| 87 | if not bots: |
| 88 | bots = cros_lab_util.swarming_bots_list(dimensions) |
| 89 | if not bots: |
| 90 | raise errors.NoDutAvailable( |
| 91 | 'no bots satisfy constraints; incorrect model/sku? %s' % dimensions) |
| 92 | |
| 93 | bot = random.choice(bots) |
| 94 | host = bot['dimensions']['dut_name'][0] |
| 95 | logger.info('trying to lease %s', host) |
| 96 | if cros_lab_util.skylab_lease_dut(host, opts.duration): |
| 97 | logger.info('leased %s (bot_id=%s)', host, bot['bot_id']) |
| 98 | return 'ready', host |
| 99 | |
| 100 | return 'wait', None |
| 101 | |
| 102 | |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 103 | def cmd_allocate_dut(opts): |
| 104 | locked_dut = None |
| 105 | try: |
| 106 | todo, host = do_allocate_dut(opts) |
| 107 | locked_dut = host + '.cros' if host else None |
| 108 | result = {'result': todo, 'locked_dut': locked_dut} |
| 109 | print(json.dumps(result)) |
| 110 | except Exception as e: |
| 111 | logger.exception('cmd_allocate_dut failed') |
| 112 | exception_name = e.__class__.__name__ |
| 113 | result = { |
| 114 | 'result': 'failed', |
| 115 | 'exception': exception_name, |
| 116 | 'text': str(e), |
| 117 | } |
| 118 | print(json.dumps(result)) |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 119 | |
| 120 | |
Kuang-che Wu | a8c3c3e | 2019-08-28 18:49:28 +0800 | [diff] [blame] | 121 | def cmd_repair_dut(opts): |
| 122 | cros_lab_util.repair(opts.dut) |
| 123 | |
| 124 | |
Kuang-che Wu | fe1e88a | 2019-09-10 21:52:25 +0800 | [diff] [blame] | 125 | @cli.fatal_error_handler |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 126 | def main(): |
| 127 | common.init() |
| 128 | parser = argparse.ArgumentParser() |
Kuang-che Wu | fe1e88a | 2019-09-10 21:52:25 +0800 | [diff] [blame] | 129 | cli.patching_argparser_exit(parser) |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 130 | common.add_common_arguments(parser) |
| 131 | subparsers = parser.add_subparsers( |
| 132 | dest='command', title='commands', metavar='<command>') |
| 133 | |
| 134 | parser_version_info = subparsers.add_parser( |
| 135 | 'version_info', |
| 136 | help='Query version info of given chromeos build', |
| 137 | description='Given chromeos `board` and `version`, ' |
| 138 | 'print version information of components.') |
| 139 | parser_version_info.add_argument( |
| 140 | 'board', help='ChromeOS board name, like "samus".') |
| 141 | parser_version_info.add_argument( |
| 142 | 'version', |
| 143 | type=cros_util.argtype_cros_version, |
| 144 | help='ChromeOS version, like "9876.0.0" or "R62-9876.0.0"') |
| 145 | parser_version_info.add_argument( |
| 146 | 'name', |
| 147 | nargs='?', |
| 148 | help='Component name. If specified, output its version string. ' |
| 149 | 'Otherwise output all version info as dict in json format.') |
| 150 | parser_version_info.set_defaults(func=cmd_version_info) |
| 151 | |
| 152 | parser_query_dut_board = subparsers.add_parser( |
| 153 | 'query_dut_board', help='Query board name of given DUT') |
| 154 | parser_query_dut_board.add_argument('dut') |
| 155 | parser_query_dut_board.set_defaults(func=cmd_query_dut_board) |
| 156 | |
| 157 | parser_reboot = subparsers.add_parser( |
| 158 | 'reboot', |
| 159 | help='Reboot a DUT', |
| 160 | description='Reboot a DUT and verify the reboot is successful.') |
| 161 | parser_reboot.add_argument('dut') |
| 162 | parser_reboot.set_defaults(func=cmd_reboot) |
| 163 | |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 164 | parser_lock_dut = subparsers.add_parser( |
| 165 | 'lock_dut', |
| 166 | help='Lock a DUT in the lab', |
| 167 | description='Lock a DUT in the lab. ' |
| 168 | 'This is simply wrapper of "atest" with additional checking.') |
Kuang-che Wu | 0c9b794 | 2019-10-30 16:55:39 +0800 | [diff] [blame] | 169 | # "skylab lease-dut" doesn't take reason, so this is not required=True. |
| 170 | group = parser_lock_dut.add_mutually_exclusive_group() |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 171 | group.add_argument('--session', help='session name; for creating lock reason') |
| 172 | group.add_argument('--reason', help='specify lock reason manually') |
| 173 | parser_lock_dut.add_argument('dut') |
Kuang-che Wu | 0c9b794 | 2019-10-30 16:55:39 +0800 | [diff] [blame] | 174 | parser_lock_dut.add_argument( |
| 175 | '--duration', |
| 176 | type=float, |
| 177 | help='duration in seconds; will be round to minutes') |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 178 | parser_lock_dut.set_defaults(func=cmd_lock_dut) |
| 179 | |
| 180 | parser_unlock_dut = subparsers.add_parser( |
| 181 | 'unlock_dut', |
| 182 | help='Unlock a DUT in the lab', |
| 183 | description='Unlock a DUT in the lab. ' |
| 184 | 'This is simply wrapper of "atest" with additional checking.') |
| 185 | parser_unlock_dut.add_argument( |
| 186 | '--session', help='session name; for checking lock reason before unlock') |
| 187 | parser_unlock_dut.add_argument('dut') |
| 188 | parser_unlock_dut.set_defaults(func=cmd_unlock_dut) |
| 189 | |
| 190 | parser_allocate_dut = subparsers.add_parser( |
| 191 | 'allocate_dut', |
| 192 | help='Allocate a DUT in the lab', |
| 193 | description='Allocate a DUT in the lab. It will lock a DUT in the lab ' |
| 194 | 'for bisecting. If no DUT is available (ready), it will lock one. The ' |
| 195 | 'caller (bisect-kit runner) of this command should keep note of the ' |
| 196 | 'locked DUT name and retry this command again later.') |
| 197 | parser_allocate_dut.add_argument( |
| 198 | '--session', required=True, help='session name') |
| 199 | parser_allocate_dut.add_argument( |
| 200 | '--pools', required=True, help='Pools to search dut, comma separated') |
| 201 | parser_allocate_dut.add_argument('--model', help='allocation criteria') |
| 202 | parser_allocate_dut.add_argument('--sku', help='allocation criteria') |
| 203 | parser_allocate_dut.add_argument( |
| 204 | '--label', '-b', help='Additional required labels, comma separated') |
| 205 | parser_allocate_dut.add_argument( |
Kuang-che Wu | 0c9b794 | 2019-10-30 16:55:39 +0800 | [diff] [blame] | 206 | '--time_limit', |
| 207 | type=int, |
| 208 | default=15 * 60, |
| 209 | help='Time limit to attempt lease in seconds (default: %(default)s)') |
| 210 | parser_allocate_dut.add_argument( |
| 211 | '--duration', |
| 212 | type=float, |
| 213 | help='lease duration in seconds; will be round to minutes') |
Kuang-che Wu | 22aa9d4 | 2019-01-25 10:35:33 +0800 | [diff] [blame] | 214 | parser_allocate_dut.set_defaults(func=cmd_allocate_dut) |
| 215 | |
Kuang-che Wu | a8c3c3e | 2019-08-28 18:49:28 +0800 | [diff] [blame] | 216 | parser_repair_dut = subparsers.add_parser( |
| 217 | 'repair_dut', |
| 218 | help='Repair a DUT in the lab', |
| 219 | description='Repair a DUT in the lab. ' |
| 220 | 'This is simply wrapper of "deploy repair" with additional checking.') |
| 221 | parser_repair_dut.add_argument('dut') |
| 222 | parser_repair_dut.set_defaults(func=cmd_repair_dut) |
| 223 | |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 224 | opts = parser.parse_args() |
| 225 | common.config_logging(opts) |
| 226 | opts.func(opts) |
| 227 | |
| 228 | |
| 229 | if __name__ == '__main__': |
| 230 | main() |