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=('Parse the results of the job using this'
     62                                        ' execution tag. Accessible in control '
     63                                        'files as job.tag.'))
     64         self.parser.add_argument('--execution-tag', action='store',
     65                                  type=str, dest='execution_tag',
     66                                  default='',
     67                                  help=('Accessible in control files as job.tag;'
     68                                        ' Defaults to the value passed to -P.'))
     69         self.parser.add_argument('-i', action='store_true',
     70                                  dest='install_before', default=False,
     71                                  help=('reinstall machines before running the '
     72                                        'job'))
     73         self.parser.add_argument('-I', action='store_true',
     74                                  dest='install_after', default=False,
     75                                  help=('reinstall machines after running the '
     76                                        'job'))
     77         self.parser.add_argument('-v', action='store_true',
     78                                  dest='verify', default=False,
     79                                  help='verify the machines only')
     80         self.parser.add_argument('-R', action='store_true',
     81                                  dest='repair', default=False,
     82                                  help='repair the machines')
     83         self.parser.add_argument('-C', '--cleanup', action='store_true',
     84                                  default=False,
     85                                  help='cleanup all machines after the job')
     86         self.parser.add_argument('--provision', action='store_true',
     87                                  default=False,
     88                                  help='Provision the machine.')
     89         self.parser.add_argument('--job-labels', action='store',
     90                                  help='Comma seperated job labels.')
     91         self.parser.add_argument('-T', '--reset', action='store_true',
     92                                  default=False,
     93                                  help=('Reset (cleanup and verify) all machines'
     94                                        ' after the job'))
     95         self.parser.add_argument('-n', action='store_true',
     96                                  dest='no_tee', default=False,
     97                                  help='no teeing the status to stdout/err')
     98         self.parser.add_argument('-N', action='store_true',
     99                                  dest='no_logging', default=False,
    100                                  help='no logging')
    101         self.parser.add_argument('--verbose', action='store_true',
    102                                  help=('Include DEBUG messages in console '
    103                                        'output'))
    104         self.parser.add_argument('--no_console_prefix', action='store_true',
    105                                  help=('Disable the logging prefix on console '
    106                                        'output'))
    107         self.parser.add_argument('-p', '--write-pidfile', action='store_true',
    108                                  dest='write_pidfile', default=False,
    109                                  help=('write pidfile (pidfile name is '
    110                                        'determined by --pidfile-label'))
    111         self.parser.add_argument('--pidfile-label', action='store',
    112                                  default='autoserv',
    113                                  help=('Determines filename to use as pidfile '
    114                                        '(if -p is specified). Pidfile will be '
    115                                        '.<label>_execute. Default to '
    116                                        'autoserv.'))
    117         self.parser.add_argument('--use-existing-results', action='store_true',
    118                                  help=('Indicates that autoserv is working with'
    119                                        ' an existing results directory'))
    120         self.parser.add_argument('-a', '--args', dest='args',
    121                                  help='additional args to pass to control file')
    122         self.parser.add_argument('--ssh-user', action='store',
    123                                  type=str, dest='ssh_user', default='root',
    124                                  help='specify the user for ssh connections')
    125         self.parser.add_argument('--ssh-port', action='store',
    126                                  type=int, dest='ssh_port', default=22,
    127                                  help=('specify the port to use for ssh '
    128                                        'connections'))
    129         self.parser.add_argument('--ssh-pass', action='store',
    130                                  type=str, dest='ssh_pass',
    131                                  default='',
    132                                  help=('specify the password to use for ssh '
    133                                        'connections'))
    134         self.parser.add_argument('--install-in-tmpdir', action='store_true',
    135                                  dest='install_in_tmpdir', default=False,
    136                                  help=('by default install autotest clients in '
    137                                        'a temporary directory'))
    138         self.parser.add_argument('--collect-crashinfo', action='store_true',
    139                                  dest='collect_crashinfo', default=False,
    140                                  help='just run crashinfo collection')
    141         self.parser.add_argument('--control-filename', action='store',
    142                                  type=str, default=None,
    143                                  help=('filename to use for the server control '
    144                                        'file in the results directory'))
    145         self.parser.add_argument('--test-retry', action='store',
    146                                  type=int, default=0,
    147                                  help=('Num of times to retry a test that '
    148                                        'failed [default: %(default)d]'))
    149         self.parser.add_argument('--verify_job_repo_url', action='store_true',
    150                                  dest='verify_job_repo_url', default=False,
    151                                  help=('Verify that the job_repo_url of the '
    152                                        'host has staged packages for the job.'))
    153         self.parser.add_argument('--no_collect_crashinfo', action='store_true',
    154                                  dest='skip_crash_collection', default=False,
    155                                  help=('Turns off crash collection to shave '
    156                                        'time off test runs.'))
    157         self.parser.add_argument('--disable_sysinfo', action='store_true',
    158                                  dest='disable_sysinfo', default=False,
    159                                  help=('Turns off sysinfo collection to shave '
    160                                        'time off test runs.'))
    161         self.parser.add_argument('--ssh_verbosity', action='store',
    162                                  dest='ssh_verbosity', default=0,
    163                                  type=str, choices=['0', '1', '2', '3'],
    164                                  help=('Verbosity level for ssh, between 0 '
    165                                        'and 3 inclusive. '
    166                                        '[default: %(default)s]'))
    167         self.parser.add_argument('--ssh_options', action='store',
    168                                  dest='ssh_options', default='',
    169                                  help=('A string giving command line flags '
    170                                        'that will be included in ssh commands'))
    171         self.parser.add_argument('--require-ssp', action='store_true',
    172                                  dest='require_ssp', default=False,
    173                                  help=('Force the autoserv process to run with '
    174                                        'server-side packaging'))
    175         self.parser.add_argument('--warn-no-ssp', action='store_true',
    176                                  dest='warn_no_ssp', default=False,
    177                                  help=('Post a warning in autoserv log that '
    178                                        'the process runs in a drone without '
    179                                        'server-side packaging support, even '
    180                                        'though the job requires server-side '
    181                                        'packaging'))
    182         self.parser.add_argument('--no_use_packaging', action='store_true',
    183                                  dest='no_use_packaging', default=False,
    184                                  help=('Disable install modes that use the '
    185                                        'packaging system.'))
    186         self.parser.add_argument('--test_source_build', action='store',
    187                                  type=str, default='',
    188                                  dest='test_source_build',
    189                                  help=('Name of the build that contains the '
    190                                        'test code. Default is empty, that is, '
    191                                        'use the build specified in --image to '
    192                                        'retrieve tests.'))
    193         self.parser.add_argument('--parent_job_id', action='store',
    194                                  type=str, default=None,
    195                                  dest='parent_job_id',
    196                                  help=('ID of the parent job. Default to None '
    197                                        'if the job does not have a parent job'))
    198         self.parser.add_argument('--image', action='store', type=str,
    199                                default='', dest='image',
    200                                help=('Full path of an OS image to install, e.g.'
    201                                      ' http://devserver/update/alex-release/'
    202                                      'R27-3837.0.0 or a build name: '
    203                                      'x86-alex-release/R27-3837.0.0 to '
    204                                      'utilize lab devservers automatically.'))
    205         self.parser.add_argument('--host_attributes', action='store',
    206                                  dest='host_attributes', default='{}',
    207                                  help=('Host attribute to be applied to all '
    208                                        'machines/hosts for this autoserv run. '
    209                                        'Must be a string-encoded dict. '
    210                                        'Example: {"key1":"value1", "key2":'
    211                                        '"value2"}'))
    212         self.parser.add_argument('--lab', action='store', type=str,
    213                                  dest='lab', default='',
    214                                  help=argparse.SUPPRESS)
    215         #
    216         # Warning! Please read before adding any new arguments!
    217         #
    218         # New arguments will be ignored if a test runs with server-side
    219         # packaging and if the test source build does not have the new
    220         # arguments.
    221         #
    222         # New argument should NOT set action to `store_true`. A workaround is to
    223         # use string value of `True` or `False`, then convert them to boolean in
    224         # code.
    225         # The reason is that parse_args will always ignore the argument name and
    226         # value. An unknown argument without a value will lead to positional
    227         # argument being removed unexpectedly.
    228         #
    229 
    230 
    231     def parse_args(self):
    232         """Parse and process command line arguments.
    233         """
    234         # Positional arguments from the end of the command line will be included
    235         # in the list of unknown_args.
    236         self.options, unknown_args = self.parser.parse_known_args()
    237         # Filter out none-positional arguments
    238         removed_args = []
    239         while unknown_args and unknown_args[0][0] == '-':
    240             removed_args.append(unknown_args.pop(0))
    241             # Always assume the argument has a value.
    242             if unknown_args:
    243                 removed_args.append(unknown_args.pop(0))
    244         if removed_args:
    245             logging.warn('Unknown arguments are removed from the options: %s',
    246                          removed_args)
    247 
    248         self.args = unknown_args + shlex.split(self.options.args or '')
    249 
    250         if self.options.image:
    251             self.options.install_before = True
    252             self.options.image =  self.options.image.strip()
    253         self.options.host_attributes = ast.literal_eval(
    254                 self.options.host_attributes)
    255 
    256 
    257 # create the one and only one instance of autoserv_parser
    258 autoserv_parser = autoserv_parser()
    259