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         self.parser.add_argument('--cloud_trace_context', type=str, default='',
    216                                  action='store', dest='cloud_trace_context',
    217                                  help=('Global trace context to configure '
    218                                        'emission of data to Cloud Trace.'))
    219         self.parser.add_argument('--cloud_trace_context_enabled', type=str,
    220                                  default='False', action='store',
    221                                  dest='cloud_trace_context_enabled',
    222                                  help=('Global trace context to configure '
    223                                        'emission of data to Cloud Trace.'))
    224         #
    225         # Warning! Please read before adding any new arguments!
    226         #
    227         # New arguments will be ignored if a test runs with server-side
    228         # packaging and if the test source build does not have the new
    229         # arguments.
    230         #
    231         # New argument should NOT set action to `store_true`. A workaround is to
    232         # use string value of `True` or `False`, then convert them to boolean in
    233         # code.
    234         # The reason is that parse_args will always ignore the argument name and
    235         # value. An unknown argument without a value will lead to positional
    236         # argument being removed unexpectedly.
    237         #
    238 
    239 
    240     def parse_args(self):
    241         """Parse and process command line arguments.
    242         """
    243         # Positional arguments from the end of the command line will be included
    244         # in the list of unknown_args.
    245         self.options, unknown_args = self.parser.parse_known_args()
    246         # Filter out none-positional arguments
    247         removed_args = []
    248         while unknown_args and unknown_args[0][0] == '-':
    249             removed_args.append(unknown_args.pop(0))
    250             # Always assume the argument has a value.
    251             if unknown_args:
    252                 removed_args.append(unknown_args.pop(0))
    253         if removed_args:
    254             logging.warn('Unknown arguments are removed from the options: %s',
    255                          removed_args)
    256 
    257         self.args = unknown_args + shlex.split(self.options.args or '')
    258 
    259         if self.options.image:
    260             self.options.install_before = True
    261             self.options.image =  self.options.image.strip()
    262 
    263         self.options.host_attributes = ast.literal_eval(
    264                 self.options.host_attributes)
    265         if self.options.lab and self.options.host_attributes:
    266             logging.warn(
    267                     '--lab and --host-attributes are mutually exclusive. '
    268                     'Ignoring custom host attributes: %s',
    269                     str(self.options.host_attributes))
    270             self.options.host_attributes = []
    271 
    272 
    273 # create the one and only one instance of autoserv_parser
    274 autoserv_parser = autoserv_parser()
    275