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 |
Kuang-che Wu | c45cfa4 | 2019-01-15 00:15:01 +0800 | [diff] [blame] | 9 | import collections |
| 10 | import functools |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 11 | import json |
| 12 | import logging |
| 13 | |
| 14 | from bisect_kit import common |
Kuang-che Wu | c45cfa4 | 2019-01-15 00:15:01 +0800 | [diff] [blame] | 15 | from bisect_kit import cros_lab_util |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 16 | from bisect_kit import cros_util |
| 17 | |
| 18 | logger = logging.getLogger(__name__) |
| 19 | |
| 20 | |
| 21 | def cmd_version_info(opts): |
| 22 | info = cros_util.version_info(opts.board, opts.version) |
| 23 | if opts.name: |
| 24 | if opts.name not in info: |
| 25 | logger.error('unknown name=%s', opts.name) |
| 26 | print(info[opts.name]) |
| 27 | else: |
| 28 | print(json.dumps(info, sort_keys=True, indent=4)) |
| 29 | |
| 30 | |
| 31 | def cmd_query_dut_board(opts): |
| 32 | assert cros_util.is_dut(opts.dut) |
| 33 | print(cros_util.query_dut_board(opts.dut)) |
| 34 | |
| 35 | |
| 36 | def cmd_reboot(opts): |
| 37 | assert cros_util.is_dut(opts.dut) |
| 38 | cros_util.reboot(opts.dut) |
| 39 | |
| 40 | |
Kuang-che Wu | c45cfa4 | 2019-01-15 00:15:01 +0800 | [diff] [blame] | 41 | def _get_label_by_prefix(info, prefix): |
| 42 | for label in info['Labels']: |
| 43 | if label.startswith(prefix + ':'): |
| 44 | return label |
| 45 | return None |
| 46 | |
| 47 | |
| 48 | def cmd_search_dut(opts): |
| 49 | labels = [] |
| 50 | if opts.label: |
| 51 | labels += opts.label.split(',') |
| 52 | |
| 53 | def match(info): |
| 54 | if not opts.condition: |
| 55 | return True |
| 56 | |
| 57 | for label in info['Labels']: |
| 58 | for condition in opts.condition: |
| 59 | if ':' in condition: |
| 60 | keys = [condition] |
| 61 | else: |
| 62 | keys = [ |
| 63 | 'board:' + condition, |
| 64 | 'model:' + condition, |
| 65 | 'sku:' + condition, |
| 66 | ] |
| 67 | for key in keys: |
| 68 | if label.lower().startswith(key.lower()): |
| 69 | return True |
| 70 | return False |
| 71 | |
| 72 | for pool in opts.pools.split(','): |
| 73 | print('pool:' + pool) |
| 74 | |
| 75 | group = collections.defaultdict(dict) |
| 76 | counter = collections.defaultdict(collections.Counter) |
| 77 | for host, info in cros_lab_util.list_host(labels=labels + |
| 78 | ['pool:' + pool]).items(): |
| 79 | if not match(info): |
| 80 | continue |
| 81 | |
| 82 | model = _get_label_by_prefix(info, 'model') |
| 83 | if info.get('Locked') == 'True': |
| 84 | state = 'Locked' |
| 85 | else: |
| 86 | state = info['Status'] |
| 87 | if state not in group[model]: |
| 88 | group[model][state] = {} |
| 89 | group[model][state][host] = info |
| 90 | counter[model][state] += 1 |
| 91 | |
| 92 | def availability(counter, model): |
| 93 | return -counter[model]['Ready'] |
| 94 | |
| 95 | for model in sorted(group, key=functools.partial(availability, counter)): |
| 96 | print('%s\t%s' % (model, dict(counter[model]))) |
| 97 | for host, info in group[model].get('Ready', {}).items(): |
| 98 | print('\t%s\t%s' % (host, _get_label_by_prefix(info, 'board'))) |
| 99 | if not opts.all: |
| 100 | break |
| 101 | print('-' * 30) |
| 102 | |
| 103 | if not opts.all: |
| 104 | print('Only list one host per model by default. Use --all to output all.') |
| 105 | |
| 106 | |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 107 | def main(): |
| 108 | common.init() |
| 109 | parser = argparse.ArgumentParser() |
| 110 | common.add_common_arguments(parser) |
| 111 | subparsers = parser.add_subparsers( |
| 112 | dest='command', title='commands', metavar='<command>') |
| 113 | |
| 114 | parser_version_info = subparsers.add_parser( |
| 115 | 'version_info', |
| 116 | help='Query version info of given chromeos build', |
| 117 | description='Given chromeos `board` and `version`, ' |
| 118 | 'print version information of components.') |
| 119 | parser_version_info.add_argument( |
| 120 | 'board', help='ChromeOS board name, like "samus".') |
| 121 | parser_version_info.add_argument( |
| 122 | 'version', |
| 123 | type=cros_util.argtype_cros_version, |
| 124 | help='ChromeOS version, like "9876.0.0" or "R62-9876.0.0"') |
| 125 | parser_version_info.add_argument( |
| 126 | 'name', |
| 127 | nargs='?', |
| 128 | help='Component name. If specified, output its version string. ' |
| 129 | 'Otherwise output all version info as dict in json format.') |
| 130 | parser_version_info.set_defaults(func=cmd_version_info) |
| 131 | |
| 132 | parser_query_dut_board = subparsers.add_parser( |
| 133 | 'query_dut_board', help='Query board name of given DUT') |
| 134 | parser_query_dut_board.add_argument('dut') |
| 135 | parser_query_dut_board.set_defaults(func=cmd_query_dut_board) |
| 136 | |
| 137 | parser_reboot = subparsers.add_parser( |
| 138 | 'reboot', |
| 139 | help='Reboot a DUT', |
| 140 | description='Reboot a DUT and verify the reboot is successful.') |
| 141 | parser_reboot.add_argument('dut') |
| 142 | parser_reboot.set_defaults(func=cmd_reboot) |
| 143 | |
Kuang-che Wu | c45cfa4 | 2019-01-15 00:15:01 +0800 | [diff] [blame] | 144 | parser_search_dut = subparsers.add_parser( |
| 145 | 'search_dut', |
| 146 | help='Search DUT with conditions', |
| 147 | description='Search hosts in the lab. Grouped by model and ordered by ' |
| 148 | 'availability. When your test could be run on many models, this command ' |
| 149 | 'help you to decide what model to use.') |
| 150 | parser_search_dut.add_argument( |
| 151 | '--pools', |
| 152 | default='performance,crosperf,suites', |
| 153 | help='Pools to search, comma separated. The searching will be performed ' |
| 154 | 'for each pool.') |
| 155 | parser_search_dut.add_argument( |
| 156 | '--label', |
| 157 | '-b', |
| 158 | help='Additional required labels, comma separated. ' |
| 159 | 'If more than one, all need to be satisfied.') |
| 160 | parser_search_dut.add_argument( |
| 161 | '--all', |
| 162 | action='store_true', |
| 163 | help='List all DUTs; (list only one DUT per board by default)') |
| 164 | parser_search_dut.add_argument( |
| 165 | 'condition', |
| 166 | nargs='*', |
| 167 | help='Conditions, ex. board name, model name, sku name, etc. If more ' |
| 168 | 'than one is specified, matching any one is sufficient. If none is ' |
| 169 | 'specified, all hosts will be listed.') |
| 170 | parser_search_dut.set_defaults(func=cmd_search_dut) |
| 171 | |
Kuang-che Wu | 2ea804f | 2017-11-28 17:11:41 +0800 | [diff] [blame] | 172 | opts = parser.parse_args() |
| 173 | common.config_logging(opts) |
| 174 | opts.func(opts) |
| 175 | |
| 176 | |
| 177 | if __name__ == '__main__': |
| 178 | main() |