blob: 33c6adf8072d0378df9d7af5bcf06873313746da [file] [log] [blame]
Kuang-che Wu2ea804f2017-11-28 17:11:41 +08001#!/usr/bin/env python2
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
9import json
10import logging
Kuang-che Wu0c9b7942019-10-30 16:55:39 +080011import random
12import time
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080013
Kuang-che Wufe1e88a2019-09-10 21:52:25 +080014from bisect_kit import cli
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080015from bisect_kit import common
Kuang-che Wuc45cfa42019-01-15 00:15:01 +080016from bisect_kit import cros_lab_util
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080017from bisect_kit import cros_util
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080018from bisect_kit import errors
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080019
20logger = logging.getLogger(__name__)
21
22
23def 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
33def cmd_query_dut_board(opts):
34 assert cros_util.is_dut(opts.dut)
35 print(cros_util.query_dut_board(opts.dut))
36
37
38def cmd_reboot(opts):
39 assert cros_util.is_dut(opts.dut)
40 cros_util.reboot(opts.dut)
41
42
Kuang-che Wuc45cfa42019-01-15 00:15:01 +080043def _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 Wu22aa9d42019-01-25 10:35:33 +080050def cmd_lock_dut(opts):
51 host = cros_lab_util.dut_host_name(opts.dut)
Kuang-che Wu220cc162019-10-31 00:29:37 +080052 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 Wu22aa9d42019-01-25 10:35:33 +080055 else:
Kuang-che Wu220cc162019-10-31 00:29:37 +080056 raise Exception('unable to lock %s' % host)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080057
58
59def cmd_unlock_dut(opts):
60 host = cros_lab_util.dut_host_name(opts.dut)
Kuang-che Wu220cc162019-10-31 00:29:37 +080061 cros_lab_util.skylab_release_dut(host)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080062 logger.info('%s unlocked', host)
63
64
65def 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 Wu0c9b7942019-10-30 16:55:39 +080076 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 Wu22aa9d42019-01-25 10:35:33 +0800103def 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 Wu22aa9d42019-01-25 10:35:33 +0800119
120
Kuang-che Wua8c3c3e2019-08-28 18:49:28 +0800121def cmd_repair_dut(opts):
122 cros_lab_util.repair(opts.dut)
123
124
Kuang-che Wufe1e88a2019-09-10 21:52:25 +0800125@cli.fatal_error_handler
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800126def main():
127 common.init()
128 parser = argparse.ArgumentParser()
Kuang-che Wufe1e88a2019-09-10 21:52:25 +0800129 cli.patching_argparser_exit(parser)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800130 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 Wu22aa9d42019-01-25 10:35:33 +0800164 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 Wu0c9b7942019-10-30 16:55:39 +0800169 # "skylab lease-dut" doesn't take reason, so this is not required=True.
170 group = parser_lock_dut.add_mutually_exclusive_group()
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800171 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 Wu0c9b7942019-10-30 16:55:39 +0800174 parser_lock_dut.add_argument(
175 '--duration',
176 type=float,
177 help='duration in seconds; will be round to minutes')
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800178 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 Wu0c9b7942019-10-30 16:55:39 +0800206 '--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 Wu22aa9d42019-01-25 10:35:33 +0800214 parser_allocate_dut.set_defaults(func=cmd_allocate_dut)
215
Kuang-che Wua8c3c3e2019-08-28 18:49:28 +0800216 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 Wu2ea804f2017-11-28 17:11:41 +0800224 opts = parser.parse_args()
225 common.config_logging(opts)
226 opts.func(opts)
227
228
229if __name__ == '__main__':
230 main()