1*9c5db199SXin Li#!/usr/bin/env python3 2*9c5db199SXin Li# Copyright 2014 The Chromium OS Authors. All rights reserved. 3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 4*9c5db199SXin Li# found in the LICENSE file. 5*9c5db199SXin Li 6*9c5db199SXin Li"""Report whether DUTs are working or broken. 7*9c5db199SXin Li 8*9c5db199SXin Liusage: dut_status [ <options> ] [hostname ...] 9*9c5db199SXin Li 10*9c5db199SXin LiReports on the history and status of selected DUT hosts, to 11*9c5db199SXin Lidetermine whether they're "working" or "broken". For purposes of 12*9c5db199SXin Lithe script, "broken" means "the DUT requires manual intervention 13*9c5db199SXin Libefore it can be used for further testing", and "working" means "not 14*9c5db199SXin Libroken". The status determination is based on the history of 15*9c5db199SXin Licompleted jobs for the DUT in a given time interval; still-running 16*9c5db199SXin Lijobs are not considered. 17*9c5db199SXin Li 18*9c5db199SXin LiTime Interval Selection 19*9c5db199SXin Li~~~~~~~~~~~~~~~~~~~~~~~ 20*9c5db199SXin LiA DUT's reported status is based on the DUT's job history in a time 21*9c5db199SXin Liinterval determined by command line options. The interval is 22*9c5db199SXin Lispecified with up to two of three options: 23*9c5db199SXin Li --until/-u DATE/TIME - Specifies an end time for the search 24*9c5db199SXin Li range. (default: now) 25*9c5db199SXin Li --since/-s DATE/TIME - Specifies a start time for the search 26*9c5db199SXin Li range. (no default) 27*9c5db199SXin Li --duration/-d HOURS - Specifies the length of the search interval 28*9c5db199SXin Li in hours. (default: 24 hours) 29*9c5db199SXin Li 30*9c5db199SXin LiAny two time options completely specify the time interval. If only 31*9c5db199SXin Lione option is provided, these defaults are used: 32*9c5db199SXin Li --until - Use the given end time with the default duration. 33*9c5db199SXin Li --since - Use the given start time with the default end time. 34*9c5db199SXin Li --duration - Use the given duration with the default end time. 35*9c5db199SXin Li 36*9c5db199SXin LiIf no time options are given, use the default end time and duration. 37*9c5db199SXin Li 38*9c5db199SXin LiDATE/TIME values are of the form '2014-11-06 17:21:34'. 39*9c5db199SXin Li 40*9c5db199SXin LiDUT Selection 41*9c5db199SXin Li~~~~~~~~~~~~~ 42*9c5db199SXin LiBy default, information is reported for DUTs named as command-line 43*9c5db199SXin Liarguments. Options are also available for selecting groups of 44*9c5db199SXin Lihosts: 45*9c5db199SXin Li --board/-b BOARD - Only include hosts with the given board. 46*9c5db199SXin Li --pool/-p POOL - Only include hosts in the given pool. The user 47*9c5db199SXin Li might be interested in the following pools: bvt, cq, 48*9c5db199SXin Li continuous, cts, or suites. 49*9c5db199SXin Li 50*9c5db199SXin Li 51*9c5db199SXin LiThe selected hosts may also be filtered based on status: 52*9c5db199SXin Li -w/--working - Only include hosts in a working state. 53*9c5db199SXin Li -n/--broken - Only include hosts in a non-working state. Hosts 54*9c5db199SXin Li with no job history are considered non-working. 55*9c5db199SXin Li 56*9c5db199SXin LiOutput Formats 57*9c5db199SXin Li~~~~~~~~~~~~~~ 58*9c5db199SXin LiThere are four available output formats: 59*9c5db199SXin Li * A simple list of host names. 60*9c5db199SXin Li * A status summary showing one line per host. 61*9c5db199SXin Li * A detailed job history for all selected DUTs, sorted by 62*9c5db199SXin Li time of execution. 63*9c5db199SXin Li * A job history for all selected DUTs showing only the history 64*9c5db199SXin Li surrounding the DUT's last change from working to broken, 65*9c5db199SXin Li or vice versa. 66*9c5db199SXin Li 67*9c5db199SXin LiThe default format depends on whether hosts are filtered by 68*9c5db199SXin Listatus: 69*9c5db199SXin Li * With the --working or --broken options, the list of host names 70*9c5db199SXin Li is the default format. 71*9c5db199SXin Li * Without those options, the default format is the one-line status 72*9c5db199SXin Li summary. 73*9c5db199SXin Li 74*9c5db199SXin LiThese options override the default formats: 75*9c5db199SXin Li -o/--oneline - Use the one-line summary with the --working or 76*9c5db199SXin Li --broken options. 77*9c5db199SXin Li -f/--full_history - Print detailed per-host job history. 78*9c5db199SXin Li -g/--diagnosis - Print the job history surrounding a status 79*9c5db199SXin Li change. 80*9c5db199SXin Li 81*9c5db199SXin LiExamples 82*9c5db199SXin Li~~~~~~~~ 83*9c5db199SXin Li $ dut_status chromeos2-row4-rack2-host12 84*9c5db199SXin Li hostname S last checked URL 85*9c5db199SXin Li chromeos2-row4-rack2-host12 NO 2014-11-06 15:25:29 http://... 86*9c5db199SXin Li 87*9c5db199SXin Li'NO' means the DUT is broken. That diagnosis is based on a job that 88*9c5db199SXin Lifailed: 'last checked' is the time of the failed job, and the URL 89*9c5db199SXin Lipoints to the job's logs. 90*9c5db199SXin Li 91*9c5db199SXin Li $ dut_status.py -u '2014-11-06 15:30:00' -d 1 -f chromeos2-row4-rack2-host12 92*9c5db199SXin Li chromeos2-row4-rack2-host12 93*9c5db199SXin Li 2014-11-06 15:25:29 NO http://... 94*9c5db199SXin Li 2014-11-06 14:44:07 -- http://... 95*9c5db199SXin Li 2014-11-06 14:42:56 OK http://... 96*9c5db199SXin Li 97*9c5db199SXin LiThe times are the start times of the jobs; the URL points to the 98*9c5db199SXin Lijob's logs. The status indicates the working or broken status after 99*9c5db199SXin Lithe job: 100*9c5db199SXin Li 'NO' Indicates that the DUT was believed broken after the job. 101*9c5db199SXin Li 'OK' Indicates that the DUT was believed working after the job. 102*9c5db199SXin Li '--' Indicates that the job probably didn't change the DUT's 103*9c5db199SXin Li status. 104*9c5db199SXin LiTypically, logs of the actual failure will be found at the last job 105*9c5db199SXin Lito report 'OK', or the first job to report '--'. 106*9c5db199SXin Li 107*9c5db199SXin Li""" 108*9c5db199SXin Li 109*9c5db199SXin Lifrom __future__ import absolute_import 110*9c5db199SXin Lifrom __future__ import division 111*9c5db199SXin Lifrom __future__ import print_function 112*9c5db199SXin Li 113*9c5db199SXin Liimport argparse 114*9c5db199SXin Liimport sys 115*9c5db199SXin Liimport time 116*9c5db199SXin Li 117*9c5db199SXin Liimport common 118*9c5db199SXin Lifrom autotest_lib.client.common_lib import time_utils 119*9c5db199SXin Lifrom autotest_lib.server import constants 120*9c5db199SXin Lifrom autotest_lib.server import frontend 121*9c5db199SXin Lifrom autotest_lib.server.lib import status_history 122*9c5db199SXin Lifrom autotest_lib.utils import labellib 123*9c5db199SXin Li 124*9c5db199SXin Li# The fully qualified name makes for lines that are too long, so 125*9c5db199SXin Li# shorten it locally. 126*9c5db199SXin LiHostJobHistory = status_history.HostJobHistory 127*9c5db199SXin Li 128*9c5db199SXin Li# _DIAGNOSIS_IDS - 129*9c5db199SXin Li# Dictionary to map the known diagnosis codes to string values. 130*9c5db199SXin Li 131*9c5db199SXin Li_DIAGNOSIS_IDS = { 132*9c5db199SXin Li status_history.UNUSED: '??', 133*9c5db199SXin Li status_history.UNKNOWN: '--', 134*9c5db199SXin Li status_history.WORKING: 'OK', 135*9c5db199SXin Li status_history.BROKEN: 'NO' 136*9c5db199SXin Li} 137*9c5db199SXin Li 138*9c5db199SXin Li 139*9c5db199SXin Li# Default time interval for the --duration option when a value isn't 140*9c5db199SXin Li# specified on the command line. 141*9c5db199SXin Li_DEFAULT_DURATION = 24 142*9c5db199SXin Li 143*9c5db199SXin Li 144*9c5db199SXin Lidef _include_status(status, arguments): 145*9c5db199SXin Li """Determine whether the given status should be filtered. 146*9c5db199SXin Li 147*9c5db199SXin Li Checks the given `status` against the command line options in 148*9c5db199SXin Li `arguments`. Return whether a host with that status should be 149*9c5db199SXin Li printed based on the options. 150*9c5db199SXin Li 151*9c5db199SXin Li @param status Status of a host to be printed or skipped. 152*9c5db199SXin Li @param arguments Parsed arguments object as returned by 153*9c5db199SXin Li ArgumentParser.parse_args(). 154*9c5db199SXin Li 155*9c5db199SXin Li @return Returns `True` if the command-line options call for 156*9c5db199SXin Li printing hosts with the status, or `False` otherwise. 157*9c5db199SXin Li 158*9c5db199SXin Li """ 159*9c5db199SXin Li if status == status_history.WORKING: 160*9c5db199SXin Li return arguments.working 161*9c5db199SXin Li else: 162*9c5db199SXin Li return arguments.broken 163*9c5db199SXin Li 164*9c5db199SXin Li 165*9c5db199SXin Lidef _print_host_summaries(history_list, arguments): 166*9c5db199SXin Li """Print one-line summaries of host history. 167*9c5db199SXin Li 168*9c5db199SXin Li This function handles the output format of the --oneline option. 169*9c5db199SXin Li 170*9c5db199SXin Li @param history_list A list of HostHistory objects to be printed. 171*9c5db199SXin Li @param arguments Parsed arguments object as returned by 172*9c5db199SXin Li ArgumentParser.parse_args(). 173*9c5db199SXin Li 174*9c5db199SXin Li """ 175*9c5db199SXin Li fmt = '%-30s %-2s %-19s %s' 176*9c5db199SXin Li print(fmt % ('hostname', 'S', 'last checked', 'URL')) 177*9c5db199SXin Li for history in history_list: 178*9c5db199SXin Li status, event = history.last_diagnosis() 179*9c5db199SXin Li if not _include_status(status, arguments): 180*9c5db199SXin Li continue 181*9c5db199SXin Li datestr = '---' 182*9c5db199SXin Li url = '---' 183*9c5db199SXin Li if event is not None: 184*9c5db199SXin Li datestr = time_utils.epoch_time_to_date_string( 185*9c5db199SXin Li event.start_time) 186*9c5db199SXin Li url = event.job_url 187*9c5db199SXin Li 188*9c5db199SXin Li print(fmt % (history.hostname, 189*9c5db199SXin Li _DIAGNOSIS_IDS[status], 190*9c5db199SXin Li datestr, 191*9c5db199SXin Li url)) 192*9c5db199SXin Li 193*9c5db199SXin Li 194*9c5db199SXin Lidef _print_event_summary(event): 195*9c5db199SXin Li """Print a one-line summary of a job or special task.""" 196*9c5db199SXin Li start_time = time_utils.epoch_time_to_date_string( 197*9c5db199SXin Li event.start_time) 198*9c5db199SXin Li print(' %s %s %s' % ( 199*9c5db199SXin Li start_time, 200*9c5db199SXin Li _DIAGNOSIS_IDS[event.diagnosis], 201*9c5db199SXin Li event.job_url)) 202*9c5db199SXin Li 203*9c5db199SXin Li 204*9c5db199SXin Lidef _print_hosts(history_list, arguments): 205*9c5db199SXin Li """Print hosts, optionally with a job history. 206*9c5db199SXin Li 207*9c5db199SXin Li This function handles both the default format for --working 208*9c5db199SXin Li and --broken options, as well as the output for the 209*9c5db199SXin Li --full_history and --diagnosis options. The `arguments` 210*9c5db199SXin Li parameter determines the format to use. 211*9c5db199SXin Li 212*9c5db199SXin Li @param history_list A list of HostHistory objects to be printed. 213*9c5db199SXin Li @param arguments Parsed arguments object as returned by 214*9c5db199SXin Li ArgumentParser.parse_args(). 215*9c5db199SXin Li 216*9c5db199SXin Li """ 217*9c5db199SXin Li for history in history_list: 218*9c5db199SXin Li status, _ = history.last_diagnosis() 219*9c5db199SXin Li if not _include_status(status, arguments): 220*9c5db199SXin Li continue 221*9c5db199SXin Li print(history.hostname) 222*9c5db199SXin Li if arguments.full_history: 223*9c5db199SXin Li for event in history: 224*9c5db199SXin Li _print_event_summary(event) 225*9c5db199SXin Li elif arguments.diagnosis: 226*9c5db199SXin Li for event in history.diagnosis_interval(): 227*9c5db199SXin Li _print_event_summary(event) 228*9c5db199SXin Li 229*9c5db199SXin Li 230*9c5db199SXin Lidef _validate_time_range(arguments): 231*9c5db199SXin Li """Validate the time range requested on the command line. 232*9c5db199SXin Li 233*9c5db199SXin Li Enforces the rules for the --until, --since, and --duration 234*9c5db199SXin Li options are followed, and calculates defaults: 235*9c5db199SXin Li * It isn't allowed to supply all three options. 236*9c5db199SXin Li * If only two options are supplied, they completely determine 237*9c5db199SXin Li the time interval. 238*9c5db199SXin Li * If only one option is supplied, or no options, then apply 239*9c5db199SXin Li specified defaults to the arguments object. 240*9c5db199SXin Li 241*9c5db199SXin Li @param arguments Parsed arguments object as returned by 242*9c5db199SXin Li ArgumentParser.parse_args(). 243*9c5db199SXin Li 244*9c5db199SXin Li """ 245*9c5db199SXin Li if (arguments.duration is not None and 246*9c5db199SXin Li arguments.since is not None and arguments.until is not None): 247*9c5db199SXin Li print('FATAL: Can specify at most two of ' 248*9c5db199SXin Li '--since, --until, and --duration', 249*9c5db199SXin Li file=sys.stderr) 250*9c5db199SXin Li sys.exit(1) 251*9c5db199SXin Li if (arguments.until is None and (arguments.since is None or 252*9c5db199SXin Li arguments.duration is None)): 253*9c5db199SXin Li arguments.until = int(time.time()) 254*9c5db199SXin Li if arguments.since is None: 255*9c5db199SXin Li if arguments.duration is None: 256*9c5db199SXin Li arguments.duration = _DEFAULT_DURATION 257*9c5db199SXin Li arguments.since = (arguments.until - 258*9c5db199SXin Li arguments.duration * 60 * 60) 259*9c5db199SXin Li elif arguments.until is None: 260*9c5db199SXin Li arguments.until = (arguments.since + 261*9c5db199SXin Li arguments.duration * 60 * 60) 262*9c5db199SXin Li 263*9c5db199SXin Li 264*9c5db199SXin Lidef _get_host_histories(afe, arguments): 265*9c5db199SXin Li """Return HostJobHistory objects for the requested hosts. 266*9c5db199SXin Li 267*9c5db199SXin Li Checks that individual hosts specified on the command line are 268*9c5db199SXin Li valid. Invalid hosts generate a warning message, and are 269*9c5db199SXin Li omitted from futher processing. 270*9c5db199SXin Li 271*9c5db199SXin Li The return value is a list of HostJobHistory objects for the 272*9c5db199SXin Li valid requested hostnames, using the time range supplied on the 273*9c5db199SXin Li command line. 274*9c5db199SXin Li 275*9c5db199SXin Li @param afe Autotest frontend 276*9c5db199SXin Li @param arguments Parsed arguments object as returned by 277*9c5db199SXin Li ArgumentParser.parse_args(). 278*9c5db199SXin Li @return List of HostJobHistory objects for the hosts requested 279*9c5db199SXin Li on the command line. 280*9c5db199SXin Li 281*9c5db199SXin Li """ 282*9c5db199SXin Li histories = [] 283*9c5db199SXin Li saw_error = False 284*9c5db199SXin Li for hostname in arguments.hostnames: 285*9c5db199SXin Li try: 286*9c5db199SXin Li h = HostJobHistory.get_host_history( 287*9c5db199SXin Li afe, hostname, arguments.since, arguments.until) 288*9c5db199SXin Li histories.append(h) 289*9c5db199SXin Li except: 290*9c5db199SXin Li print('WARNING: Ignoring unknown host %s' % 291*9c5db199SXin Li hostname, file=sys.stderr) 292*9c5db199SXin Li saw_error = True 293*9c5db199SXin Li if saw_error: 294*9c5db199SXin Li # Create separation from the output that follows 295*9c5db199SXin Li print(file=sys.stderr) 296*9c5db199SXin Li return histories 297*9c5db199SXin Li 298*9c5db199SXin Li 299*9c5db199SXin Lidef _validate_host_list(afe, arguments): 300*9c5db199SXin Li """Validate the user-specified list of hosts. 301*9c5db199SXin Li 302*9c5db199SXin Li Hosts may be specified implicitly with --board or --pool, or 303*9c5db199SXin Li explictly as command line arguments. This enforces these 304*9c5db199SXin Li rules: 305*9c5db199SXin Li * If --board or --pool, or both are specified, individual 306*9c5db199SXin Li hosts may not be specified. 307*9c5db199SXin Li * However specified, there must be at least one host. 308*9c5db199SXin Li 309*9c5db199SXin Li The return value is a list of HostJobHistory objects for the 310*9c5db199SXin Li requested hosts, using the time range supplied on the command 311*9c5db199SXin Li line. 312*9c5db199SXin Li 313*9c5db199SXin Li @param afe Autotest frontend 314*9c5db199SXin Li @param arguments Parsed arguments object as returned by 315*9c5db199SXin Li ArgumentParser.parse_args(). 316*9c5db199SXin Li @return List of HostJobHistory objects for the hosts requested 317*9c5db199SXin Li on the command line. 318*9c5db199SXin Li 319*9c5db199SXin Li """ 320*9c5db199SXin Li if arguments.board or arguments.pool or arguments.model: 321*9c5db199SXin Li if arguments.hostnames: 322*9c5db199SXin Li print('FATAL: Hostname arguments provided ' 323*9c5db199SXin Li 'with --board or --pool', file=sys.stderr) 324*9c5db199SXin Li sys.exit(1) 325*9c5db199SXin Li 326*9c5db199SXin Li labels = labellib.LabelsMapping() 327*9c5db199SXin Li labels['board'] = arguments.board 328*9c5db199SXin Li labels['pool'] = arguments.pool 329*9c5db199SXin Li labels['model'] = arguments.model 330*9c5db199SXin Li histories = HostJobHistory.get_multiple_histories( 331*9c5db199SXin Li afe, arguments.since, arguments.until, labels.getlabels()) 332*9c5db199SXin Li else: 333*9c5db199SXin Li histories = _get_host_histories(afe, arguments) 334*9c5db199SXin Li if not histories: 335*9c5db199SXin Li print('FATAL: no valid hosts found', file=sys.stderr) 336*9c5db199SXin Li sys.exit(1) 337*9c5db199SXin Li return histories 338*9c5db199SXin Li 339*9c5db199SXin Li 340*9c5db199SXin Lidef _validate_format_options(arguments): 341*9c5db199SXin Li """Check the options for what output format to use. 342*9c5db199SXin Li 343*9c5db199SXin Li Enforce these rules: 344*9c5db199SXin Li * If neither --broken nor --working was used, then --oneline 345*9c5db199SXin Li becomes the selected format. 346*9c5db199SXin Li * If neither --broken nor --working was used, included both 347*9c5db199SXin Li working and broken DUTs. 348*9c5db199SXin Li 349*9c5db199SXin Li @param arguments Parsed arguments object as returned by 350*9c5db199SXin Li ArgumentParser.parse_args(). 351*9c5db199SXin Li 352*9c5db199SXin Li """ 353*9c5db199SXin Li if (not arguments.oneline and not arguments.diagnosis and 354*9c5db199SXin Li not arguments.full_history): 355*9c5db199SXin Li arguments.oneline = (not arguments.working and 356*9c5db199SXin Li not arguments.broken) 357*9c5db199SXin Li if not arguments.working and not arguments.broken: 358*9c5db199SXin Li arguments.working = True 359*9c5db199SXin Li arguments.broken = True 360*9c5db199SXin Li 361*9c5db199SXin Li 362*9c5db199SXin Lidef _validate_command(afe, arguments): 363*9c5db199SXin Li """Check that the command's arguments are valid. 364*9c5db199SXin Li 365*9c5db199SXin Li This performs command line checking to enforce command line 366*9c5db199SXin Li rules that ArgumentParser can't handle. Additionally, this 367*9c5db199SXin Li handles calculation of default arguments/options when a simple 368*9c5db199SXin Li constant default won't do. 369*9c5db199SXin Li 370*9c5db199SXin Li Areas checked: 371*9c5db199SXin Li * Check that a valid time range was provided, supplying 372*9c5db199SXin Li defaults as necessary. 373*9c5db199SXin Li * Identify invalid host names. 374*9c5db199SXin Li 375*9c5db199SXin Li @param afe Autotest frontend 376*9c5db199SXin Li @param arguments Parsed arguments object as returned by 377*9c5db199SXin Li ArgumentParser.parse_args(). 378*9c5db199SXin Li @return List of HostJobHistory objects for the hosts requested 379*9c5db199SXin Li on the command line. 380*9c5db199SXin Li 381*9c5db199SXin Li """ 382*9c5db199SXin Li _validate_time_range(arguments) 383*9c5db199SXin Li _validate_format_options(arguments) 384*9c5db199SXin Li return _validate_host_list(afe, arguments) 385*9c5db199SXin Li 386*9c5db199SXin Li 387*9c5db199SXin Lidef _parse_command(argv): 388*9c5db199SXin Li """Parse the command line arguments. 389*9c5db199SXin Li 390*9c5db199SXin Li Create an argument parser for this command's syntax, parse the 391*9c5db199SXin Li command line, and return the result of the ArgumentParser 392*9c5db199SXin Li parse_args() method. 393*9c5db199SXin Li 394*9c5db199SXin Li @param argv Standard command line argument vector; argv[0] is 395*9c5db199SXin Li assumed to be the command name. 396*9c5db199SXin Li @return Result returned by ArgumentParser.parse_args(). 397*9c5db199SXin Li 398*9c5db199SXin Li """ 399*9c5db199SXin Li parser = argparse.ArgumentParser( 400*9c5db199SXin Li prog=argv[0], 401*9c5db199SXin Li description='Report DUT status and execution history', 402*9c5db199SXin Li epilog='You can specify one or two of --since, --until, ' 403*9c5db199SXin Li 'and --duration, but not all three.') 404*9c5db199SXin Li parser.add_argument('-s', '--since', type=status_history.parse_time, 405*9c5db199SXin Li metavar='DATE/TIME', 406*9c5db199SXin Li help=('Starting time for history display. ' 407*9c5db199SXin Li 'Format: "YYYY-MM-DD HH:MM:SS"')) 408*9c5db199SXin Li parser.add_argument('-u', '--until', type=status_history.parse_time, 409*9c5db199SXin Li metavar='DATE/TIME', 410*9c5db199SXin Li help=('Ending time for history display. ' 411*9c5db199SXin Li 'Format: "YYYY-MM-DD HH:MM:SS" ' 412*9c5db199SXin Li 'Default: now')) 413*9c5db199SXin Li parser.add_argument('-d', '--duration', type=int, 414*9c5db199SXin Li metavar='HOURS', 415*9c5db199SXin Li help='Number of hours of history to display' 416*9c5db199SXin Li ' (default: %d)' % _DEFAULT_DURATION) 417*9c5db199SXin Li 418*9c5db199SXin Li format_group = parser.add_mutually_exclusive_group() 419*9c5db199SXin Li format_group.add_argument('-f', '--full_history', action='store_true', 420*9c5db199SXin Li help='Display host history from most ' 421*9c5db199SXin Li 'to least recent for each DUT') 422*9c5db199SXin Li format_group.add_argument('-g', '--diagnosis', action='store_true', 423*9c5db199SXin Li help='Display host history for the ' 424*9c5db199SXin Li 'most recent DUT status change') 425*9c5db199SXin Li format_group.add_argument('-o', '--oneline', action='store_true', 426*9c5db199SXin Li help='Display host status summary') 427*9c5db199SXin Li 428*9c5db199SXin Li parser.add_argument('-w', '--working', action='store_true', 429*9c5db199SXin Li help='List working devices by name only') 430*9c5db199SXin Li parser.add_argument('-n', '--broken', action='store_true', 431*9c5db199SXin Li help='List non-working devices by name only') 432*9c5db199SXin Li 433*9c5db199SXin Li parser.add_argument('-b', '--board', 434*9c5db199SXin Li help='Display history for all DUTs ' 435*9c5db199SXin Li 'of the given board') 436*9c5db199SXin Li parser.add_argument('-m', '--model', 437*9c5db199SXin Li help='Display history for all DUTs of the given model.') 438*9c5db199SXin Li parser.add_argument('-p', '--pool', 439*9c5db199SXin Li help='Display history for all DUTs ' 440*9c5db199SXin Li 'in the given pool. You might ' 441*9c5db199SXin Li 'be interested in the following pools: ' 442*9c5db199SXin Li + ', '.join(constants.Pools.MANAGED_POOLS[:-1]) 443*9c5db199SXin Li +', or '+ constants.Pools.MANAGED_POOLS[-1] +'.') 444*9c5db199SXin Li parser.add_argument('hostnames', 445*9c5db199SXin Li nargs='*', 446*9c5db199SXin Li help='Host names of DUTs to report on') 447*9c5db199SXin Li parser.add_argument('--web', 448*9c5db199SXin Li help='Autotest frontend hostname. If no value ' 449*9c5db199SXin Li 'is given, the one in global config will be used.', 450*9c5db199SXin Li default=None) 451*9c5db199SXin Li arguments = parser.parse_args(argv[1:]) 452*9c5db199SXin Li return arguments 453*9c5db199SXin Li 454*9c5db199SXin Li 455*9c5db199SXin Lidef main(argv): 456*9c5db199SXin Li """Standard main() for command line processing. 457*9c5db199SXin Li 458*9c5db199SXin Li @param argv Command line arguments (normally sys.argv). 459*9c5db199SXin Li 460*9c5db199SXin Li """ 461*9c5db199SXin Li arguments = _parse_command(argv) 462*9c5db199SXin Li afe = frontend.AFE(server=arguments.web) 463*9c5db199SXin Li history_list = _validate_command(afe, arguments) 464*9c5db199SXin Li if arguments.oneline: 465*9c5db199SXin Li _print_host_summaries(history_list, arguments) 466*9c5db199SXin Li else: 467*9c5db199SXin Li _print_hosts(history_list, arguments) 468*9c5db199SXin Li 469*9c5db199SXin Li 470*9c5db199SXin Liif __name__ == '__main__': 471*9c5db199SXin Li main(sys.argv) 472