blob: 2d2b0bf3325ea691352125e656de408b99724fab [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 Wuc26dcdf2019-11-01 16:30:06 +080019from bisect_kit import util
Kuang-che Wu2ea804f2017-11-28 17:11:41 +080020
21logger = logging.getLogger(__name__)
22
23
24def cmd_version_info(opts):
25 info = cros_util.version_info(opts.board, opts.version)
26 if opts.name:
27 if opts.name not in info:
28 logger.error('unknown name=%s', opts.name)
29 print(info[opts.name])
30 else:
31 print(json.dumps(info, sort_keys=True, indent=4))
32
33
34def cmd_query_dut_board(opts):
35 assert cros_util.is_dut(opts.dut)
36 print(cros_util.query_dut_board(opts.dut))
37
38
39def cmd_reboot(opts):
40 assert cros_util.is_dut(opts.dut)
41 cros_util.reboot(opts.dut)
42
43
Kuang-che Wuc45cfa42019-01-15 00:15:01 +080044def _get_label_by_prefix(info, prefix):
45 for label in info['Labels']:
46 if label.startswith(prefix + ':'):
47 return label
48 return None
49
50
Kuang-che Wuca456462019-11-04 17:32:55 +080051def cmd_lease_dut(opts):
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080052 host = cros_lab_util.dut_host_name(opts.dut)
Kuang-che Wu220cc162019-10-31 00:29:37 +080053 logger.info('trying to lease %s', host)
54 if cros_lab_util.skylab_lease_dut(host, opts.duration):
Kuang-che Wuca456462019-11-04 17:32:55 +080055 logger.info('leased %s', host)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080056 else:
Kuang-che Wuca456462019-11-04 17:32:55 +080057 raise Exception('unable to lease %s' % host)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080058
59
Kuang-che Wuca456462019-11-04 17:32:55 +080060def cmd_release_dut(opts):
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080061 host = cros_lab_util.dut_host_name(opts.dut)
Kuang-che Wu220cc162019-10-31 00:29:37 +080062 cros_lab_util.skylab_release_dut(host)
Kuang-che Wuca456462019-11-04 17:32:55 +080063 logger.info('%s released', host)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080064
65
66def do_allocate_dut(opts):
67 """Helper of cmd_allocate_dut.
68
69 Returns:
70 (todo, host)
71 todo: 'ready' or 'wait'
Kuang-che Wuca456462019-11-04 17:32:55 +080072 host: leased host name
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080073 """
74 if not opts.model and not opts.sku:
75 raise errors.ArgumentError('--model or --sku', 'need to be specified')
76
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +080077 t0 = time.time()
Kuang-che Wu0c9b7942019-10-30 16:55:39 +080078 dimensions = ['dut_state:ready', 'label-pool:DUT_POOL_QUOTA']
79 if opts.model:
80 dimensions.append('label-model:' + opts.model)
81 if opts.sku:
82 dimensions.append('label-hwid_sku:' +
83 cros_lab_util.normalize_sku_name(opts.sku))
84
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +080085 while True:
Kuang-che Wu9501f342019-11-15 17:15:21 +080086 # Query every time because each iteration takes 10+ minutes
Kuang-che Wu0c9b7942019-10-30 16:55:39 +080087 bots = cros_lab_util.swarming_bots_list(dimensions, is_busy=False)
88 if not bots:
89 bots = cros_lab_util.swarming_bots_list(dimensions)
90 if not bots:
91 raise errors.NoDutAvailable(
92 'no bots satisfy constraints; incorrect model/sku? %s' % dimensions)
93
94 bot = random.choice(bots)
95 host = bot['dimensions']['dut_name'][0]
96 logger.info('trying to lease %s', host)
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +080097 remaining_time = opts.time_limit - (time.time() - t0)
98 if remaining_time <= 0:
99 break
100 try:
101 if cros_lab_util.skylab_lease_dut(
102 host, opts.duration, timeout=remaining_time):
103 logger.info('leased %s (bot_id=%s)', host, bot['bot_id'])
Kuang-che Wuceaa5ec2019-11-05 14:21:33 +0800104 dut = host + '.cros'
105 if cros_util.is_good_dut(dut):
106 return 'ready', host
107 logger.warning('the leased DUT is broken; '
108 'return it and lease another one later')
109 cros_lab_util.skylab_release_dut(host)
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800110 except util.TimeoutExpired:
111 break
112 time.sleep(1)
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800113
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800114 logger.warning('unable to lease DUT in time limit')
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800115 return 'wait', None
116
117
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800118def cmd_allocate_dut(opts):
Kuang-che Wuca456462019-11-04 17:32:55 +0800119 leased_dut = None
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800120 try:
121 todo, host = do_allocate_dut(opts)
Kuang-che Wuca456462019-11-04 17:32:55 +0800122 leased_dut = host + '.cros' if host else None
123 # TODO(kcwu): remove "locked_dut" after bkr updated
124 result = {
125 'result': todo,
126 'locked_dut': leased_dut,
127 'leased_dut': leased_dut
128 }
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800129 print(json.dumps(result))
130 except Exception as e:
131 logger.exception('cmd_allocate_dut failed')
132 exception_name = e.__class__.__name__
133 result = {
134 'result': 'failed',
135 'exception': exception_name,
136 'text': str(e),
137 }
138 print(json.dumps(result))
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800139
140
Kuang-che Wua8c3c3e2019-08-28 18:49:28 +0800141def cmd_repair_dut(opts):
142 cros_lab_util.repair(opts.dut)
143
144
Kuang-che Wufe1e88a2019-09-10 21:52:25 +0800145@cli.fatal_error_handler
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800146def main():
147 common.init()
148 parser = argparse.ArgumentParser()
Kuang-che Wufe1e88a2019-09-10 21:52:25 +0800149 cli.patching_argparser_exit(parser)
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800150 common.add_common_arguments(parser)
151 subparsers = parser.add_subparsers(
152 dest='command', title='commands', metavar='<command>')
153
154 parser_version_info = subparsers.add_parser(
155 'version_info',
156 help='Query version info of given chromeos build',
157 description='Given chromeos `board` and `version`, '
158 'print version information of components.')
159 parser_version_info.add_argument(
160 'board', help='ChromeOS board name, like "samus".')
161 parser_version_info.add_argument(
162 'version',
163 type=cros_util.argtype_cros_version,
164 help='ChromeOS version, like "9876.0.0" or "R62-9876.0.0"')
165 parser_version_info.add_argument(
166 'name',
167 nargs='?',
168 help='Component name. If specified, output its version string. '
169 'Otherwise output all version info as dict in json format.')
170 parser_version_info.set_defaults(func=cmd_version_info)
171
172 parser_query_dut_board = subparsers.add_parser(
173 'query_dut_board', help='Query board name of given DUT')
174 parser_query_dut_board.add_argument('dut')
175 parser_query_dut_board.set_defaults(func=cmd_query_dut_board)
176
177 parser_reboot = subparsers.add_parser(
178 'reboot',
179 help='Reboot a DUT',
180 description='Reboot a DUT and verify the reboot is successful.')
181 parser_reboot.add_argument('dut')
182 parser_reboot.set_defaults(func=cmd_reboot)
183
Kuang-che Wuca456462019-11-04 17:32:55 +0800184 parser_lease_dut = subparsers.add_parser(
185 'lease_dut',
186 help='Lease a DUT in the lab',
187 description='Lease a DUT in the lab. '
188 'This is implemented by `skylab lease-dut` with additional checking.')
189 # "skylab lease-dut" doesn't take reason, so this is not required=True.
190 parser_lease_dut.add_argument('--session', help='session name')
191 parser_lease_dut.add_argument('dut')
192 parser_lease_dut.add_argument(
193 '--duration',
194 type=float,
195 help='duration in seconds; will be round to minutes')
196 parser_lease_dut.set_defaults(func=cmd_lease_dut)
197
198 parser_release_dut = subparsers.add_parser(
199 'release_dut',
200 help='Release a DUT in the lab',
201 description='Release a DUT in the lab. '
202 'This is implemented by `skylab release-dut` with additional checking.')
203 parser_release_dut.add_argument('--session', help='session name')
204 parser_release_dut.add_argument('dut')
205 parser_release_dut.set_defaults(func=cmd_release_dut)
206
207 # TODO(kcwu): remove `lock_dut` and `unlock_dut` after bkr updated
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800208 parser_lock_dut = subparsers.add_parser(
209 'lock_dut',
210 help='Lock a DUT in the lab',
211 description='Lock a DUT in the lab. '
212 'This is simply wrapper of "atest" with additional checking.')
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800213 # "skylab lease-dut" doesn't take reason, so this is not required=True.
214 group = parser_lock_dut.add_mutually_exclusive_group()
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800215 group.add_argument('--session', help='session name; for creating lock reason')
216 group.add_argument('--reason', help='specify lock reason manually')
217 parser_lock_dut.add_argument('dut')
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800218 parser_lock_dut.add_argument(
219 '--duration',
220 type=float,
221 help='duration in seconds; will be round to minutes')
Kuang-che Wuca456462019-11-04 17:32:55 +0800222 parser_lock_dut.set_defaults(func=cmd_lease_dut)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800223
224 parser_unlock_dut = subparsers.add_parser(
225 'unlock_dut',
226 help='Unlock a DUT in the lab',
227 description='Unlock a DUT in the lab. '
228 'This is simply wrapper of "atest" with additional checking.')
229 parser_unlock_dut.add_argument(
230 '--session', help='session name; for checking lock reason before unlock')
231 parser_unlock_dut.add_argument('dut')
Kuang-che Wuca456462019-11-04 17:32:55 +0800232 parser_unlock_dut.set_defaults(func=cmd_release_dut)
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800233
234 parser_allocate_dut = subparsers.add_parser(
235 'allocate_dut',
236 help='Allocate a DUT in the lab',
Kuang-che Wuca456462019-11-04 17:32:55 +0800237 description='Allocate a DUT in the lab. It will lease a DUT in the lab '
238 'for bisecting. The caller (bisect-kit runner) of this command should '
239 'retry this command again later if no DUT available now.')
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800240 parser_allocate_dut.add_argument(
241 '--session', required=True, help='session name')
242 parser_allocate_dut.add_argument(
243 '--pools', required=True, help='Pools to search dut, comma separated')
244 parser_allocate_dut.add_argument('--model', help='allocation criteria')
245 parser_allocate_dut.add_argument('--sku', help='allocation criteria')
246 parser_allocate_dut.add_argument(
247 '--label', '-b', help='Additional required labels, comma separated')
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800248 # Pubsub ack deadline is 10 minutes (b/143663659). Default 9 minutes with 1
249 # minute buffer.
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800250 parser_allocate_dut.add_argument(
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800251 '--time_limit',
252 type=int,
Kuang-che Wuc26dcdf2019-11-01 16:30:06 +0800253 default=9 * 60,
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800254 help='Time limit to attempt lease in seconds (default: %(default)s)')
255 parser_allocate_dut.add_argument(
256 '--duration',
257 type=float,
258 help='lease duration in seconds; will be round to minutes')
Kuang-che Wu22aa9d42019-01-25 10:35:33 +0800259 parser_allocate_dut.set_defaults(func=cmd_allocate_dut)
260
Kuang-che Wua8c3c3e2019-08-28 18:49:28 +0800261 parser_repair_dut = subparsers.add_parser(
262 'repair_dut',
263 help='Repair a DUT in the lab',
264 description='Repair a DUT in the lab. '
265 'This is simply wrapper of "deploy repair" with additional checking.')
266 parser_repair_dut.add_argument('dut')
267 parser_repair_dut.set_defaults(func=cmd_repair_dut)
268
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800269 opts = parser.parse_args()
270 common.config_logging(opts)
Kuang-che Wud3a4e842019-12-11 12:15:23 +0800271
272 # It's optional by default since python3.
273 if not opts.command:
274 parser.error('command is missing')
Kuang-che Wu2ea804f2017-11-28 17:11:41 +0800275 opts.func(opts)
276
277
278if __name__ == '__main__':
279 main()