autoserv_parser.py revision 888cfcaeeffdf302e25b066d9bef9ba535ebbc20
1import argparse
2import logging
3import os
4import shlex
5import sys
6
7from autotest_lib.client.common_lib import host_protections
8
9
10class autoserv_parser(object):
11    """Custom command-line options parser for autoserv.
12
13    We can't use the general getopt methods here, as there will be unknown
14    extra arguments that we pass down into the control file instead.
15    Thus we process the arguments by hand, for which we are duly repentant.
16    Making a single function here just makes it harder to read. Suck it up.
17    """
18    def __init__(self):
19        self.args = sys.argv[1:]
20        self.parser = argparse.ArgumentParser(
21                usage='%(prog)s [options] [control-file]')
22        self.setup_options()
23
24        # parse an empty list of arguments in order to set self.options
25        # to default values so that codepaths that assume they are always
26        # reached from an autoserv process (when they actually are not)
27        # will still work
28        self.options = self.parser.parse_args(args=[])
29
30
31    def setup_options(self):
32        """Setup options to call autoserv command.
33        """
34        self.parser.add_argument('-m', action='store', type=str,
35                                 dest='machines',
36                                 help='list of machines')
37        self.parser.add_argument('-M', action='store', type=str,
38                                 dest='machines_file',
39                                 help='list of machines from file')
40        self.parser.add_argument('-c', action='store_true',
41                                 dest='client', default=False,
42                                 help='control file is client side')
43        self.parser.add_argument('-s', action='store_true',
44                                 dest='server', default=False,
45                                 help='control file is server side')
46        self.parser.add_argument('-r', action='store', type=str,
47                                 dest='results', default=None,
48                                 help='specify results directory')
49        self.parser.add_argument('-l', action='store', type=str,
50                                 dest='label', default='',
51                                 help='label for the job')
52        self.parser.add_argument('-G', action='store', type=str,
53                                 dest='group_name', default='',
54                                 help='The host_group_name to store in keyvals')
55        self.parser.add_argument('-u', action='store', type=str,
56                                 dest='user',
57                                 default=os.environ.get('USER'),
58                                 help='username for the job')
59        self.parser.add_argument('-P', action='store', type=str,
60                                 dest='parse_job',
61                                 default='',
62                                 help=('Parse the results of the job using this'
63                                       ' execution tag. Accessible in control '
64                                       'files as job.tag.'))
65        self.parser.add_argument('--execution-tag', action='store',
66                                 type=str, dest='execution_tag',
67                                 default='',
68                                 help=('Accessible in control files as job.tag;'
69                                       ' Defaults to the value passed to -P.'))
70        self.parser.add_argument('-i', action='store_true',
71                                 dest='install_before', default=False,
72                                 help=('reinstall machines before running the '
73                                       'job'))
74        self.parser.add_argument('-I', action='store_true',
75                                 dest='install_after', default=False,
76                                 help=('reinstall machines after running the '
77                                       'job'))
78        self.parser.add_argument('-v', action='store_true',
79                                 dest='verify', default=False,
80                                 help='verify the machines only')
81        self.parser.add_argument('-R', action='store_true',
82                                 dest='repair', default=False,
83                                 help='repair the machines')
84        self.parser.add_argument('-C', '--cleanup', action='store_true',
85                                 default=False,
86                                 help='cleanup all machines after the job')
87        self.parser.add_argument('--provision', action='store_true',
88                                 default=False,
89                                 help='Provision the machine.')
90        self.parser.add_argument('--job-labels', action='store',
91                                 help='Comma seperated job labels.')
92        self.parser.add_argument('-T', '--reset', action='store_true',
93                                 default=False,
94                                 help=('Reset (cleanup and verify) all machines'
95                                       ' after the job'))
96        self.parser.add_argument('-n', action='store_true',
97                                 dest='no_tee', default=False,
98                                 help='no teeing the status to stdout/err')
99        self.parser.add_argument('-N', action='store_true',
100                                 dest='no_logging', default=False,
101                                 help='no logging')
102        self.parser.add_argument('--verbose', action='store_true',
103                                 help=('Include DEBUG messages in console '
104                                       'output'))
105        self.parser.add_argument('--no_console_prefix', action='store_true',
106                                 help=('Disable the logging prefix on console '
107                                       'output'))
108        self.parser.add_argument('-p', '--write-pidfile', action='store_true',
109                                 dest='write_pidfile', default=False,
110                                 help=('write pidfile (pidfile name is '
111                                       'determined by --pidfile-label'))
112        self.parser.add_argument('--pidfile-label', action='store',
113                                 default='autoserv',
114                                 help=('Determines filename to use as pidfile '
115                                       '(if -p is specified). Pidfile will be '
116                                       '.<label>_execute. Default to '
117                                       'autoserv.'))
118        self.parser.add_argument('--use-existing-results', action='store_true',
119                                 help=('Indicates that autoserv is working with'
120                                       ' an existing results directory'))
121        self.parser.add_argument('-a', '--args', dest='args',
122                                 help='additional args to pass to control file')
123        protection_levels = [host_protections.Protection.get_attr_name(s)
124                             for _, s in host_protections.choices]
125        self.parser.add_argument('--host-protection', action='store',
126                                 type=str, dest='host_protection',
127                                 default=host_protections.default,
128                                 choices=protection_levels,
129                                 help='level of host protection during repair')
130        self.parser.add_argument('--ssh-user', action='store',
131                                 type=str, dest='ssh_user', default='root',
132                                 help='specify the user for ssh connections')
133        self.parser.add_argument('--ssh-port', action='store',
134                                 type=int, dest='ssh_port', default=22,
135                                 help=('specify the port to use for ssh '
136                                       'connections'))
137        self.parser.add_argument('--ssh-pass', action='store',
138                                 type=str, dest='ssh_pass',
139                                 default='',
140                                 help=('specify the password to use for ssh '
141                                       'connections'))
142        self.parser.add_argument('--install-in-tmpdir', action='store_true',
143                                 dest='install_in_tmpdir', default=False,
144                                 help=('by default install autotest clients in '
145                                       'a temporary directory'))
146        self.parser.add_argument('--collect-crashinfo', action='store_true',
147                                 dest='collect_crashinfo', default=False,
148                                 help='just run crashinfo collection')
149        self.parser.add_argument('--control-filename', action='store',
150                                 type=str, default=None,
151                                 help=('filename to use for the server control '
152                                       'file in the results directory'))
153        self.parser.add_argument('--test-retry', action='store',
154                                 type=int, default=0,
155                                 help=('Num of times to retry a test that '
156                                       'failed [default: %default]'))
157        self.parser.add_argument('--verify_job_repo_url', action='store_true',
158                                 dest='verify_job_repo_url', default=False,
159                                 help=('Verify that the job_repo_url of the '
160                                       'host has staged packages for the job.'))
161        self.parser.add_argument('--no_collect_crashinfo', action='store_true',
162                                 dest='skip_crash_collection', default=False,
163                                 help=('Turns off crash collection to shave '
164                                       'time off test runs.'))
165        self.parser.add_argument('--disable_sysinfo', action='store_true',
166                                 dest='disable_sysinfo', default=False,
167                                 help=('Turns off sysinfo collection to shave '
168                                       'time off test runs.'))
169        self.parser.add_argument('--ssh_verbosity', action='store',
170                                 dest='ssh_verbosity', default=0,
171                                 type=str, choices=['0', '1', '2', '3'],
172                                 help=('Verbosity level for ssh, between 0 '
173                                       'and 3 inclusive. [default: 0]'))
174        self.parser.add_argument('--ssh_options', action='store',
175                                 dest='ssh_options', default='',
176                                 help=('A string giving command line flags '
177                                       'that will be included in ssh commands'))
178        self.parser.add_argument('--require-ssp', action='store_true',
179                                 dest='require_ssp', default=False,
180                                 help=('Force the autoserv process to run with '
181                                       'server-side packaging'))
182        self.parser.add_argument('--warn-no-ssp', action='store_true',
183                                 dest='warn_no_ssp', default=False,
184                                 help=('Post a warning in autoserv log that '
185                                       'the process runs in a drone without '
186                                       'server-side packaging support, even '
187                                       'though the job requires server-side '
188                                       'packaging'))
189        self.parser.add_argument('--no_use_packaging', action='store_true',
190                                 dest='no_use_packaging', default=False,
191                                 help=('Disable install modes that use the '
192                                       'packaging system.'))
193        self.parser.add_argument('--test_source_build', action='store',
194                                 type=str, default='',
195                                 dest='test_source_build',
196                                 help=('Name of the build that contains the '
197                                       'test code. Default is empty, that is, '
198                                       'use the build specified in --image to '
199                                       'retrieve tests.'))
200        self.parser.add_argument('--parent_job_id', action='store',
201                                 type=str, default=None,
202                                 dest='parent_job_id',
203                                 help=('ID of the parent job. Default to None '
204                                       'if the job does not have a parent job'))
205        self.parser.add_argument('--image', action='store', type=str,
206                               default='', dest='image',
207                               help=('Full path of an OS image to install, e.g.'
208                                     ' http://devserver/update/alex-release/'
209                                     'R27-3837.0.0 or a build name: '
210                                     'x86-alex-release/R27-3837.0.0 to '
211                                     'utilize lab devservers automatically.'))
212        #
213        # Warning! Please read before adding any new arguments!
214        #
215        # New arguments will be ignored if a test runs with server-side
216        # packaging and if the test source build does not have the new
217        # arguments.
218        #
219        # New argument should NOT set action to `store_true`. A workaround is to
220        # use string value of `True` or `False`, then convert them to boolean in
221        # code.
222        # The reason is that parse_args will always ignore the argument name and
223        # value. An unknown argument without a value will lead to positional
224        # argument being removed unexpectedly.
225        #
226
227
228    def parse_args(self):
229        """Parse and process command line arguments.
230        """
231        # Positional arguments from the end of the command line will be included
232        # in the list of unknown_args.
233        self.options, unknown_args = self.parser.parse_known_args()
234        # Filter out none-positional arguments
235        removed_args = []
236        while unknown_args and unknown_args[0][0] == '-':
237            removed_args.append(unknown_args.pop(0))
238            # Always assume the argument has a value.
239            if unknown_args:
240                removed_args.append(unknown_args.pop(0))
241        if removed_args:
242            logging.warn('Unknown arguments are removed from the options: %s',
243                         removed_args)
244
245        self.args = unknown_args + shlex.split(self.options.args or '')
246
247        if self.options.image:
248            self.options.install_before = True
249            self.options.image =  self.options.image.strip()
250
251
252# create the one and only one instance of autoserv_parser
253autoserv_parser = autoserv_parser()
254