1*9c5db199SXin Li# Copyright 2015 The Chromium OS Authors. All rights reserved. 2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 3*9c5db199SXin Li# found in the LICENSE file. 4*9c5db199SXin Li 5*9c5db199SXin Li"""Command-line parsing for the DUT deployment tool. 6*9c5db199SXin Li 7*9c5db199SXin LiThis contains parsing for the legacy `repair_test` and `deployment_test` 8*9c5db199SXin Licommands, and for the new `deploy` command. 9*9c5db199SXin Li 10*9c5db199SXin LiThe syntax for the two legacy commands is identical; the difference in 11*9c5db199SXin Lithe two commands is merely slightly different default options. 12*9c5db199SXin Li 13*9c5db199SXin LiThe full deployment flow performs all of the following actions: 14*9c5db199SXin Li * Stage the USB image: Install the DUT's assigned repair image onto 15*9c5db199SXin Li the Servo USB stick. 16*9c5db199SXin Li * Install firmware: Boot the DUT from the USB stick, and run 17*9c5db199SXin Li `chromeos-firmwareupdate` to install dev-signed RO and RW firmware. 18*9c5db199SXin Li The DUT must begin in dev-mode, with hardware write-protect 19*9c5db199SXin Li disabled. At successful completion, the DUT is in verified boot 20*9c5db199SXin Li mode. 21*9c5db199SXin Li * Install test image: Boot the DUT in recovery mode from the USB 22*9c5db199SXin Li stick, and run `chromeos-install` to install the OS. 23*9c5db199SXin Li 24*9c5db199SXin LiThe new `deploy` command chooses particular combinations of the steps 25*9c5db199SXin Liabove based on a subcommand and options: 26*9c5db199SXin Li `deploy servo`: Only stage the USB image. 27*9c5db199SXin Li `deploy firmware`: Install both the firmware and the test image, 28*9c5db199SXin Li in that order. Optionally, first stage the USB image. 29*9c5db199SXin Li `deploy test-image`: Install the test image. Optionally, first 30*9c5db199SXin Li stage the USB image. 31*9c5db199SXin Li `deploy repair`: Equivalent to `deploy test-image`, except that 32*9c5db199SXin Li by default it doesn't upload its logs to storage. 33*9c5db199SXin Li 34*9c5db199SXin LiThis module exports two functions, `parse_deprecated_command()` (for the 35*9c5db199SXin Litwo legacy commands) and `parse_command()` (for the new `deploy` 36*9c5db199SXin Licommand). Although the functions parse slightly different syntaxes, 37*9c5db199SXin Lithey return `argparse.Namespace` objects with identical fields, described 38*9c5db199SXin Libelow. 39*9c5db199SXin Li 40*9c5db199SXin LiThe following fields represent parameter inputs to the underlying 41*9c5db199SXin Lideployment: 42*9c5db199SXin Li `web`: Server name (or URL) for the AFE RPC service. 43*9c5db199SXin Li `logdir`: The directory where logs are to be stored. 44*9c5db199SXin Li `board`: Specifies the board to be used when creating DUTs. 45*9c5db199SXin Li `build`: When provided, the repair image assigned to the board for 46*9c5db199SXin Li the target DUT will be updated to this value prior to staging 47*9c5db199SXin Li USB image. The build is in a form like 'R66-10447.0.0'. 48*9c5db199SXin Li `hostname_file`: Name of a file in CSV format with information 49*9c5db199SXin Li about the hosts and servos to be deployed/repaired. 50*9c5db199SXin Li `hostnames`: List of DUT host names. 51*9c5db199SXin Li 52*9c5db199SXin LiThe following fields specify options that are used to enable or disable 53*9c5db199SXin Lispecific deployment steps: 54*9c5db199SXin Li `upload`: When true, logs will be uploaded to googlestorage after 55*9c5db199SXin Li the command completes. 56*9c5db199SXin Li `dry_run`: When true, disables operations with any kind of 57*9c5db199SXin Li side-effect. This option implicitly overrides and disables all 58*9c5db199SXin Li of the deployment steps below. 59*9c5db199SXin Li `stageusb`: When true, enable staging the USB image. Disabling 60*9c5db199SXin Li this will speed up operations when the stick is known to already 61*9c5db199SXin Li have the proper image. 62*9c5db199SXin Li `install_firmware`: When true, enable firmware installation. 63*9c5db199SXin Li `install_test_image`: When true, enable installing the test image via 64*9c5db199SXin Li send ctrl_u to boot into USB, which only apply to initial DUT deployment. 65*9c5db199SXin Li `reinstall test image`: when true, enable installing test image through 66*9c5db199SXin Li recover mode. 67*9c5db199SXin Li `labstation`: when true, deploy labstation instead of DUT. 68*9c5db199SXin Li 69*9c5db199SXin LiThe `dry_run` option is off by default. The `upload` option is on by 70*9c5db199SXin Lidefault, except for `deploy repair` and `repair_test`. The values for 71*9c5db199SXin Liall other options are determined by the subcommand. 72*9c5db199SXin Li""" 73*9c5db199SXin Li 74*9c5db199SXin Liimport argparse 75*9c5db199SXin Liimport os 76*9c5db199SXin Li 77*9c5db199SXin Li 78*9c5db199SXin Liclass _ArgumentParser(argparse.ArgumentParser): 79*9c5db199SXin Li """`argparse.ArgumentParser` extended with boolean option pairs.""" 80*9c5db199SXin Li 81*9c5db199SXin Li def add_boolean_argument(self, name, default, **kwargs): 82*9c5db199SXin Li """Add a pair of argument flags for a boolean option. 83*9c5db199SXin Li 84*9c5db199SXin Li This add a pair of options, named `--<name>` and `--no<name>`. 85*9c5db199SXin Li The actions of the two options are 'store_true' and 86*9c5db199SXin Li 'store_false', respectively, with the destination `<name>`. 87*9c5db199SXin Li 88*9c5db199SXin Li If neither option is present on the command line, the default 89*9c5db199SXin Li value for destination `<name>` is given by `default`. 90*9c5db199SXin Li 91*9c5db199SXin Li The given `kwargs` may be any arguments accepted by 92*9c5db199SXin Li `ArgumentParser.add_argument()`, except for `action` and `dest`. 93*9c5db199SXin Li 94*9c5db199SXin Li @param name The name of the boolean argument, used to 95*9c5db199SXin Li construct the option names and destination field 96*9c5db199SXin Li name. 97*9c5db199SXin Li @param default Default setting for the option when not present 98*9c5db199SXin Li on the command line. 99*9c5db199SXin Li """ 100*9c5db199SXin Li exclusion_group = self.add_mutually_exclusive_group() 101*9c5db199SXin Li exclusion_group.add_argument('--%s' % name, action='store_true', 102*9c5db199SXin Li dest=name, **kwargs) 103*9c5db199SXin Li exclusion_group.add_argument('--no%s' % name, action='store_false', 104*9c5db199SXin Li dest=name, **kwargs) 105*9c5db199SXin Li self.set_defaults(**{name: bool(default)}) 106*9c5db199SXin Li 107*9c5db199SXin Li 108*9c5db199SXin Lidef _add_common_options(parser): 109*9c5db199SXin Li # frontend.AFE(server=None) will use the default web server, 110*9c5db199SXin Li # so default for --web is `None`. 111*9c5db199SXin Li parser.add_argument('-w', '--web', metavar='SERVER', default=None, 112*9c5db199SXin Li help='specify web server') 113*9c5db199SXin Li parser.add_argument('-d', '--dir', dest='logdir', 114*9c5db199SXin Li help='directory for logs') 115*9c5db199SXin Li parser.add_argument('-n', '--dry-run', action='store_true', 116*9c5db199SXin Li help='apply no changes, install nothing') 117*9c5db199SXin Li parser.add_argument('-i', '--build', 118*9c5db199SXin Li help='select stable test build version') 119*9c5db199SXin Li parser.add_argument('-f', '--hostname_file', 120*9c5db199SXin Li help='CSV file that contains a list of hostnames and ' 121*9c5db199SXin Li 'their details to install with.') 122*9c5db199SXin Li 123*9c5db199SXin Li 124*9c5db199SXin Lidef _add_upload_option(parser, default): 125*9c5db199SXin Li """Add a boolean option for whether to upload logs. 126*9c5db199SXin Li 127*9c5db199SXin Li @param parser _ArgumentParser instance. 128*9c5db199SXin Li @param default Default option value. 129*9c5db199SXin Li """ 130*9c5db199SXin Li parser.add_boolean_argument('upload', default, 131*9c5db199SXin Li help='whether to upload logs to GS bucket') 132*9c5db199SXin Li 133*9c5db199SXin Li 134*9c5db199SXin Lidef _add_subcommand(subcommands, name, upload_default, description): 135*9c5db199SXin Li """Add a subcommand plus standard arguments to the `deploy` command. 136*9c5db199SXin Li 137*9c5db199SXin Li This creates a new argument parser for a subcommand (as for 138*9c5db199SXin Li `subcommands.add_parser()`). The parser is populated with the 139*9c5db199SXin Li standard arguments required by all `deploy` subcommands. 140*9c5db199SXin Li 141*9c5db199SXin Li @param subcommands Subcommand object as returned by 142*9c5db199SXin Li `ArgumentParser.add_subcommands` 143*9c5db199SXin Li @param name Name of the new subcommand. 144*9c5db199SXin Li @param upload_default Default setting for the `--upload` option. 145*9c5db199SXin Li @param description Description for the subcommand, for help text. 146*9c5db199SXin Li @returns The argument parser for the new subcommand. 147*9c5db199SXin Li """ 148*9c5db199SXin Li subparser = subcommands.add_parser(name, description=description) 149*9c5db199SXin Li _add_common_options(subparser) 150*9c5db199SXin Li _add_upload_option(subparser, upload_default) 151*9c5db199SXin Li subparser.add_argument('-b', '--board', metavar='BOARD', 152*9c5db199SXin Li help='board for DUTs to be installed') 153*9c5db199SXin Li subparser.add_argument('-m', '--model', metavar='MODEL', 154*9c5db199SXin Li help='model for DUTs to be installed.') 155*9c5db199SXin Li subparser.add_argument('hostnames', nargs='*', metavar='HOSTNAME', 156*9c5db199SXin Li help='host names of DUTs to be installed') 157*9c5db199SXin Li return subparser 158*9c5db199SXin Li 159*9c5db199SXin Li 160*9c5db199SXin Lidef _add_servo_subcommand(subcommands): 161*9c5db199SXin Li """Add the `servo` subcommand to `subcommands`. 162*9c5db199SXin Li 163*9c5db199SXin Li @param subcommands Subcommand object as returned by 164*9c5db199SXin Li `ArgumentParser.add_subcommands` 165*9c5db199SXin Li """ 166*9c5db199SXin Li subparser = _add_subcommand( 167*9c5db199SXin Li subcommands, 'servo', True, 168*9c5db199SXin Li 'Test servo and install the image on the USB stick') 169*9c5db199SXin Li subparser.set_defaults(stageusb=True, 170*9c5db199SXin Li labstation=False, 171*9c5db199SXin Li install_firmware=False, 172*9c5db199SXin Li install_test_image=False, 173*9c5db199SXin Li reinstall_test_image=False) 174*9c5db199SXin Li 175*9c5db199SXin Li 176*9c5db199SXin Lidef _add_stageusb_option(parser): 177*9c5db199SXin Li """Add a boolean option for whether to stage an image to USB. 178*9c5db199SXin Li 179*9c5db199SXin Li @param parser _ArgumentParser instance. 180*9c5db199SXin Li """ 181*9c5db199SXin Li parser.add_boolean_argument('stageusb', False, 182*9c5db199SXin Li help='Include USB stick setup') 183*9c5db199SXin Li 184*9c5db199SXin Li 185*9c5db199SXin Lidef _add_firmware_subcommand(subcommands): 186*9c5db199SXin Li """Add the `firmware` subcommand to `subcommands`. 187*9c5db199SXin Li 188*9c5db199SXin Li @param subcommands Subcommand object as returned by 189*9c5db199SXin Li `ArgumentParser.add_subcommands` 190*9c5db199SXin Li """ 191*9c5db199SXin Li subparser = _add_subcommand( 192*9c5db199SXin Li subcommands, 'firmware', True, 193*9c5db199SXin Li 'Install firmware and initial test image on DUT') 194*9c5db199SXin Li _add_stageusb_option(subparser) 195*9c5db199SXin Li subparser.add_argument( 196*9c5db199SXin Li '--using-servo', action='store_true', 197*9c5db199SXin Li help='Flash DUT firmware directly using servo') 198*9c5db199SXin Li subparser.set_defaults(labstation=False, 199*9c5db199SXin Li install_firmware=True, 200*9c5db199SXin Li install_test_image=True, 201*9c5db199SXin Li reinstall_test_image=False) 202*9c5db199SXin Li 203*9c5db199SXin Li 204*9c5db199SXin Lidef _add_test_image_subcommand(subcommands): 205*9c5db199SXin Li """Add the `test-image` subcommand to `subcommands`. 206*9c5db199SXin Li 207*9c5db199SXin Li @param subcommands Subcommand object as returned by 208*9c5db199SXin Li `ArgumentParser.add_subcommands` 209*9c5db199SXin Li """ 210*9c5db199SXin Li subparser = _add_subcommand( 211*9c5db199SXin Li subcommands, 'test-image', True, 212*9c5db199SXin Li 'Install initial test image on DUT from servo') 213*9c5db199SXin Li _add_stageusb_option(subparser) 214*9c5db199SXin Li subparser.set_defaults(labstation=False, 215*9c5db199SXin Li install_firmware=False, 216*9c5db199SXin Li install_test_image=True, 217*9c5db199SXin Li reinstall_test_image=False) 218*9c5db199SXin Li 219*9c5db199SXin Li 220*9c5db199SXin Lidef _add_repair_subcommand(subcommands): 221*9c5db199SXin Li """Add the `repair` subcommand to `subcommands`. 222*9c5db199SXin Li 223*9c5db199SXin Li @param subcommands Subcommand object as returned by 224*9c5db199SXin Li `ArgumentParser.add_subcommands` 225*9c5db199SXin Li """ 226*9c5db199SXin Li subparser = _add_subcommand( 227*9c5db199SXin Li subcommands, 'repair', False, 228*9c5db199SXin Li 'Re-install test image on DUT from servo') 229*9c5db199SXin Li _add_stageusb_option(subparser) 230*9c5db199SXin Li subparser.set_defaults(labstation=False, 231*9c5db199SXin Li install_firmware=False, 232*9c5db199SXin Li install_test_image=False, 233*9c5db199SXin Li reinstall_test_image=True) 234*9c5db199SXin Li 235*9c5db199SXin Li 236*9c5db199SXin Lidef _add_labstation_subcommand(subcommands): 237*9c5db199SXin Li """Add the `labstation` subcommand to `subcommands`. 238*9c5db199SXin Li 239*9c5db199SXin Li @param subcommands Subcommand object as returned by 240*9c5db199SXin Li `ArgumentParser.add_subcommands` 241*9c5db199SXin Li """ 242*9c5db199SXin Li subparser = _add_subcommand( 243*9c5db199SXin Li subcommands, 'labstation', False, 244*9c5db199SXin Li 'Deploy a labstation to autotest, the labstation must be already' 245*9c5db199SXin Li ' imaged with a labstation test image.') 246*9c5db199SXin Li subparser.set_defaults(labstation=True, 247*9c5db199SXin Li install_firmware=False, 248*9c5db199SXin Li install_test_image=False, 249*9c5db199SXin Li reinstall_test_image=False) 250*9c5db199SXin Li 251*9c5db199SXin Li 252*9c5db199SXin Lidef parse_command(argv): 253*9c5db199SXin Li """Parse arguments for the `deploy` command. 254*9c5db199SXin Li 255*9c5db199SXin Li Create an argument parser for the `deploy` command and its 256*9c5db199SXin Li subcommands. Then parse the command line arguments, and return an 257*9c5db199SXin Li `argparse.Namespace` object with the results. 258*9c5db199SXin Li 259*9c5db199SXin Li @param argv Standard command line argument vector; 260*9c5db199SXin Li argv[0] is assumed to be the command name. 261*9c5db199SXin Li @return `Namespace` object with standard fields as described in the 262*9c5db199SXin Li module docstring. 263*9c5db199SXin Li """ 264*9c5db199SXin Li parser = _ArgumentParser( 265*9c5db199SXin Li prog=os.path.basename(argv[0]), 266*9c5db199SXin Li description='DUT deployment and repair operations') 267*9c5db199SXin Li subcommands = parser.add_subparsers() 268*9c5db199SXin Li _add_servo_subcommand(subcommands) 269*9c5db199SXin Li _add_firmware_subcommand(subcommands) 270*9c5db199SXin Li _add_test_image_subcommand(subcommands) 271*9c5db199SXin Li _add_repair_subcommand(subcommands) 272*9c5db199SXin Li _add_labstation_subcommand(subcommands) 273*9c5db199SXin Li return parser.parse_args(argv[1:]) 274