Home | History | Annotate | Download | only in server
      1 import argparse
      2 import ast
      3 import logging
      4 import os
      5 import shlex
      6 import sys
      7 
      8 
      9 class autoserv_parser(object):
     10     """Custom command-line options parser for autoserv.
     11 
     12     We can't use the general getopt methods here, as there will be unknown
     13     extra arguments that we pass down into the control file instead.
     14     Thus we process the arguments by hand, for which we are duly repentant.
     15     Making a single function here just makes it harder to read. Suck it up.
     16     """
     17     def __init__(self):
     18         self.args = sys.argv[1:]
     19         self.parser = argparse.ArgumentParser(
     20                 usage='%(prog)s [options] [control-file]')
     21         self.setup_options()
     22 
     23         # parse an empty list of arguments in order to set self.options
     24         # to default values so that codepaths that assume they are always
     25         # reached from an autoserv process (when they actually are not)
     26         # will still work
     27         self.options = self.parser.parse_args(args=[])
     28 
     29 
     30     def setup_options(self):
     31         """Setup options to call autoserv command.
     32         """
     33         self.parser.add_argument('-m', action='store', type=str,
     34                                  dest='machines',
     35                                  help='list of machines')
     36         self.parser.add_argument('-M', action='store', type=str,
     37                                  dest='machines_file',
     38                                  help='list of machines from file')
     39         self.parser.add_argument('-c', action='store_true',
     40                                  dest='client', default=False,
     41                                  help='control file is client side')
     42         self.parser.add_argument('-s', action='store_true',
     43                                  dest='server', default=False,
     44                                  help='control file is server side')
     45         self.parser.add_argument('-r', action='store', type=str,
     46                                  dest='results', default=None,
     47                                  help='specify results directory')
     48         self.parser.add_argument('-l', action='store', type=str,
     49                                  dest='label', default='',
     50                                  help='label for the job')
     51         self.parser.add_argument('-G', action='store', type=str,
     52                                  dest='group_name', default='',
     53                                  help='The host_group_name to store in keyvals')
     54         self.parser.add_argument('-u', action='store', type=str,
     55                                  dest='user',
     56                                  default=os.environ.get('USER'),
     57                                  help='username for the job')
     58         self.parser.add_argument('-P', action='store', type=str,
     59                                  dest='parse_job',
     60                                  default='',
     61                                  help=('DEPRECATED.'
     62                                        ' Use --execution-tag instead.'))
     63         self.parser.add_argument('--execution-tag', action='store',
     64                                  type=str, dest='execution_tag',
     65                                  default='',
     66                                  help=('Accessible in control files as job.tag;'
     67                                        ' Defaults to the value passed to -P.'))
     68         self.parser.add_argument('-v', action='store_true',
     69                                  dest='verify', default=False,
     70                                  help='verify the machines only')
     71         self.parser.add_argument('-R', action='store_true',
     72                                  dest='repair', default=False,
     73                                  help='repair the machines')
     74         self.parser.add_argument('-C', '--cleanup', action='store_true',
     75                                  default=False,
     76                                  help='cleanup all machines after the job')
     77         self.parser.add_argument('--provision', action='store_true',
     78                                  default=False,
     79                                  help='Provision the machine.')
     80         self.parser.add_argument('--job-labels', action='store',
     81                                  help='Comma seperated job labels.')
     82         self.parser.add_argument('-T', '--reset', action='store_true',
     83                                  default=False,
     84                                  help=('Reset (cleanup and verify) all machines'
     85                                        ' after the job'))
     86         self.parser.add_argument('-n', action='store_true',
     87                                  dest='no_tee', default=False,
     88                                  help='no teeing the status to stdout/err')
     89         self.parser.add_argument('-N', action='store_true',
     90                                  dest='no_logging', default=False,
     91                                  help='no logging')
     92         self.parser.add_argument('--verbose', action='store_true',
     93                                  help=('Include DEBUG messages in console '
     94                                        'output'))
     95         self.parser.add_argument('--no_console_prefix', action='store_true',
     96                                  help=('Disable the logging prefix on console '
     97                                        'output'))
     98         self.parser.add_argument('-p', '--write-pidfile', action='store_true',
     99                                  dest='write_pidfile', default=False,
    100                                  help=('write pidfile (pidfile name is '
    101                                        'determined by --pidfile-label'))
    102         self.parser.add_argument('--pidfile-label', action='store',
    103                                  default='autoserv',
    104                                  help=('Determines filename to use as pidfile '
    105                                        '(if -p is specified). Pidfile will be '
    106                                        '.<label>_execute. Default to '
    107                                        'autoserv.'))
    108         self.parser.add_argument('--use-existing-results', action='store_true',
    109                                  help=('Indicates that autoserv is working with'
    110                                        ' an existing results directory'))
    111         self.parser.add_argument('-a', '--args', dest='args',
    112                                  help='additional args to pass to control file')
    113         self.parser.add_argument('--ssh-user', action='store',
    114                                  type=str, dest='ssh_user', default='root',
    115                                  help='specify the user for ssh connections')
    116         self.parser.add_argument('--ssh-port', action='store',
    117                                  type=int, dest='ssh_port', default=22,
    118                                  help=('specify the port to use for ssh '
    119                                        'connections'))
    120         self.parser.add_argument('--ssh-pass', action='store',
    121                                  type=str, dest='ssh_pass',
    122                                  default='',
    123                                  help=('specify the password to use for ssh '
    124                                        'connections'))
    125         self.parser.add_argument('--install-in-tmpdir', action='store_true',
    126                                  dest='install_in_tmpdir', default=False,
    127                                  help=('by default install autotest clients in '
    128                                        'a temporary directory'))
    129         self.parser.add_argument('--collect-crashinfo', action='store_true',
    130                                  dest='collect_crashinfo', default=False,
    131                                  help='just run crashinfo collection')
    132         self.parser.add_argument('--control-filename', action='store',
    133                                  type=str, default=None,
    134                                  help=('filename to use for the server control '
    135                                        'file in the results directory'))
    136         self.parser.add_argument('--verify_job_repo_url', action='store_true',
    137                                  dest='verify_job_repo_url', default=False,
    138                                  help=('Verify that the job_repo_url of the '
    139                                        'host has staged packages for the job.'))
    140         self.parser.add_argument('--no_collect_crashinfo', action='store_true',
    141                                  dest='skip_crash_collection', default=False,
    142                                  help=('Turns off crash collection to shave '
    143                                        'time off test runs.'))
    144         self.parser.add_argument('--disable_sysinfo', action='store_true',
    145                                  dest='disable_sysinfo', default=False,
    146                                  help=('Turns off sysinfo collection to shave '
    147                                        'time off test runs.'))
    148         self.parser.add_argument('--ssh_verbosity', action='store',
    149                                  dest='ssh_verbosity', default=0,
    150                                  type=str, choices=['0', '1', '2', '3'],
    151                                  help=('Verbosity level for ssh, between 0 '
    152                                        'and 3 inclusive. '
    153                                        '[default: %(default)s]'))
    154         self.parser.add_argument('--ssh_options', action='store',
    155                                  dest='ssh_options', default='',
    156                                  help=('A string giving command line flags '
    157                                        'that will be included in ssh commands'))
    158         self.parser.add_argument('--require-ssp', action='store_true',
    159                                  dest='require_ssp', default=False,
    160                                  help=('Force the autoserv process to run with '
    161                                        'server-side packaging'))
    162         self.parser.add_argument('--no_use_packaging', action='store_true',
    163                                  dest='no_use_packaging', default=False,
    164                                  help=('Disable install modes that use the '
    165                                        'packaging system.'))
    166         self.parser.add_argument('--source_isolate', action='store',
    167                                  type=str, default='',
    168                                  dest='isolate',
    169                                  help=('Hash for isolate containing build '
    170                                        'contents needed for server-side '
    171                                        'packaging. Takes precedence over '
    172                                        'test_source_build, if present.'))
    173         self.parser.add_argument('--test_source_build', action='store',
    174                                  type=str, default='',
    175                                  dest='test_source_build',
    176                                  help=('Alternative build that contains the '
    177                                        'test code for server-side packaging. '
    178                                        'Default is to use the build on the '
    179                                        'target DUT.'))
    180         self.parser.add_argument('--parent_job_id', action='store',
    181                                  type=str, default=None,
    182                                  dest='parent_job_id',
    183                                  help=('ID of the parent job. Default to None '
    184                                        'if the job does not have a parent job'))
    185         self.parser.add_argument('--host_attributes', action='store',
    186                                  dest='host_attributes', default='{}',
    187                                  help=('Host attribute to be applied to all '
    188                                        'machines/hosts for this autoserv run. '
    189                                        'Must be a string-encoded dict. '
    190                                        'Example: {"key1":"value1", "key2":'
    191                                        '"value2"}'))
    192         self.parser.add_argument('--lab', action='store', type=str,
    193                                  dest='lab', default='',
    194                                  help=argparse.SUPPRESS)
    195         self.parser.add_argument('--cloud_trace_context', type=str, default='',
    196                                  action='store', dest='cloud_trace_context',
    197                                  help=('Global trace context to configure '
    198                                        'emission of data to Cloud Trace.'))
    199         self.parser.add_argument('--cloud_trace_context_enabled', type=str,
    200                                  default='False', action='store',
    201                                  dest='cloud_trace_context_enabled',
    202                                  help=('Global trace context to configure '
    203                                        'emission of data to Cloud Trace.'))
    204         self.parser.add_argument(
    205                 '--host-info-subdir',
    206                 default='host_info_store',
    207                 help='Optional path to a directory containing host '
    208                      'information for the machines. The path is relative to '
    209                      'the results directory (see -r) and must be inside '
    210                      'the directory.'
    211         )
    212         self.parser.add_argument(
    213                 '--local-only-host-info',
    214                 default='False',
    215                 help='By default, host status are immediately reported back to '
    216                      'the AFE, shadowing the updates to disk. This flag '
    217                      'disables the AFE interaction completely, and assumes '
    218                      'that initial host information is supplied to autoserv. '
    219                      'See also: --host-info-subdir',
    220         )
    221         self.parser.add_argument(
    222                 '--control-name',
    223                 action='store',
    224                 help='NAME attribute of the control file to stage and run. '
    225                      'This overrides the control file provided via the '
    226                      'positional args.',
    227         )
    228 
    229         #
    230         # Warning! Please read before adding any new arguments!
    231         #
    232         # New arguments will be ignored if a test runs with server-side
    233         # packaging and if the test source build does not have the new
    234         # arguments.
    235         #
    236         # New argument should NOT set action to `store_true`. A workaround is to
    237         # use string value of `True` or `False`, then convert them to boolean in
    238         # code.
    239         # The reason is that parse_args will always ignore the argument name and
    240         # value. An unknown argument without a value will lead to positional
    241         # argument being removed unexpectedly.
    242         #
    243 
    244 
    245     def parse_args(self):
    246         """Parse and process command line arguments.
    247         """
    248         # Positional arguments from the end of the command line will be included
    249         # in the list of unknown_args.
    250         self.options, unknown_args = self.parser.parse_known_args()
    251         # Filter out none-positional arguments
    252         removed_args = []
    253         while unknown_args and unknown_args[0][0] == '-':
    254             removed_args.append(unknown_args.pop(0))
    255             # Always assume the argument has a value.
    256             if unknown_args:
    257                 removed_args.append(unknown_args.pop(0))
    258         if removed_args:
    259             logging.warn('Unknown arguments are removed from the options: %s',
    260                          removed_args)
    261 
    262         self.args = unknown_args + shlex.split(self.options.args or '')
    263 
    264         self.options.host_attributes = ast.literal_eval(
    265                 self.options.host_attributes)
    266         if self.options.lab and self.options.host_attributes:
    267             logging.warn(
    268                     '--lab and --host-attributes are mutually exclusive. '
    269                     'Ignoring custom host attributes: %s',
    270                     str(self.options.host_attributes))
    271             self.options.host_attributes = []
    272 
    273         self.options.local_only_host_info = _interpret_bool_arg(
    274                 self.options.local_only_host_info)
    275         if not self.options.execution_tag:
    276             self.options.execution_tag = self.options.parse_job
    277 
    278 
    279 def _interpret_bool_arg(value):
    280     return value.lower() in {'yes', 'true'}
    281 
    282 
    283 # create the one and only one instance of autoserv_parser
    284 autoserv_parser = autoserv_parser()
    285