blob: 4d6beaf90191e99d7a24ffe7084980f70498e194 [file] [log] [blame]
Dan Shi888cfca2015-07-31 15:49:00 -07001import argparse
2import logging
3import os
4import shlex
5import sys
Dan Shi07e09af2013-04-12 09:31:29 -07006
Dan Shi888cfca2015-07-31 15:49:00 -07007from autotest_lib.client.common_lib import host_protections
jadmanskifbc1f0a2008-07-09 14:12:54 +00008
mblighb7dcc7f2008-06-02 19:34:25 +00009
Dan Shi888cfca2015-07-31 15:49:00 -070010class autoserv_parser(object):
jadmanski0afbb632008-06-06 21:10:57 +000011 """Custom command-line options parser for autoserv.
mblighb7dcc7f2008-06-02 19:34:25 +000012
jadmanski0afbb632008-06-06 21:10:57 +000013 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:]
Dan Shi888cfca2015-07-31 15:49:00 -070020 self.parser = argparse.ArgumentParser(
21 usage='%(prog)s [options] [control-file]')
jadmanski0afbb632008-06-06 21:10:57 +000022 self.setup_options()
mblighb7dcc7f2008-06-02 19:34:25 +000023
mblighf82a1822009-02-26 00:47:14 +000024 # 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
Dan Shi888cfca2015-07-31 15:49:00 -070028 self.options = self.parser.parse_args(args=[])
mblighf82a1822009-02-26 00:47:14 +000029
mblighb7dcc7f2008-06-02 19:34:25 +000030
jadmanski0afbb632008-06-06 21:10:57 +000031 def setup_options(self):
Dan Shi888cfca2015-07-31 15:49:00 -070032 """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')
jadmanskifbc1f0a2008-07-09 14:12:54 +0000123 protection_levels = [host_protections.Protection.get_attr_name(s)
Dan Shi888cfca2015-07-31 15:49:00 -0700124 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 '
Dan Shid7254862015-08-07 10:21:21 -0700156 'failed [default: %(default)d]'))
Dan Shi888cfca2015-07-31 15:49:00 -0700157 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 '
Dan Shid7254862015-08-07 10:21:21 -0700173 'and 3 inclusive. '
174 '[default: %(default)s]'))
Dan Shi888cfca2015-07-31 15:49:00 -0700175 self.parser.add_argument('--ssh_options', action='store',
176 dest='ssh_options', default='',
177 help=('A string giving command line flags '
178 'that will be included in ssh commands'))
179 self.parser.add_argument('--require-ssp', action='store_true',
180 dest='require_ssp', default=False,
181 help=('Force the autoserv process to run with '
182 'server-side packaging'))
183 self.parser.add_argument('--warn-no-ssp', action='store_true',
184 dest='warn_no_ssp', default=False,
185 help=('Post a warning in autoserv log that '
186 'the process runs in a drone without '
187 'server-side packaging support, even '
188 'though the job requires server-side '
189 'packaging'))
190 self.parser.add_argument('--no_use_packaging', action='store_true',
191 dest='no_use_packaging', default=False,
192 help=('Disable install modes that use the '
193 'packaging system.'))
194 self.parser.add_argument('--test_source_build', action='store',
195 type=str, default='',
196 dest='test_source_build',
197 help=('Name of the build that contains the '
198 'test code. Default is empty, that is, '
199 'use the build specified in --image to '
200 'retrieve tests.'))
201 self.parser.add_argument('--parent_job_id', action='store',
202 type=str, default=None,
203 dest='parent_job_id',
204 help=('ID of the parent job. Default to None '
205 'if the job does not have a parent job'))
206 self.parser.add_argument('--image', action='store', type=str,
207 default='', dest='image',
208 help=('Full path of an OS image to install, e.g.'
209 ' http://devserver/update/alex-release/'
210 'R27-3837.0.0 or a build name: '
211 'x86-alex-release/R27-3837.0.0 to '
212 'utilize lab devservers automatically.'))
213 #
214 # Warning! Please read before adding any new arguments!
215 #
216 # New arguments will be ignored if a test runs with server-side
217 # packaging and if the test source build does not have the new
218 # arguments.
219 #
220 # New argument should NOT set action to `store_true`. A workaround is to
221 # use string value of `True` or `False`, then convert them to boolean in
222 # code.
223 # The reason is that parse_args will always ignore the argument name and
224 # value. An unknown argument without a value will lead to positional
225 # argument being removed unexpectedly.
226 #
jadmanski0afbb632008-06-06 21:10:57 +0000227
228
229 def parse_args(self):
Dan Shi888cfca2015-07-31 15:49:00 -0700230 """Parse and process command line arguments.
231 """
232 # Positional arguments from the end of the command line will be included
233 # in the list of unknown_args.
234 self.options, unknown_args = self.parser.parse_known_args()
235 # Filter out none-positional arguments
236 removed_args = []
237 while unknown_args and unknown_args[0][0] == '-':
238 removed_args.append(unknown_args.pop(0))
239 # Always assume the argument has a value.
240 if unknown_args:
241 removed_args.append(unknown_args.pop(0))
242 if removed_args:
243 logging.warn('Unknown arguments are removed from the options: %s',
244 removed_args)
mblighb7dcc7f2008-06-02 19:34:25 +0000245
Dan Shi888cfca2015-07-31 15:49:00 -0700246 self.args = unknown_args + shlex.split(self.options.args or '')
mblighb7dcc7f2008-06-02 19:34:25 +0000247
Dan Shi888cfca2015-07-31 15:49:00 -0700248 if self.options.image:
249 self.options.install_before = True
250 self.options.image = self.options.image.strip()
mblighb7dcc7f2008-06-02 19:34:25 +0000251
252
253# create the one and only one instance of autoserv_parser
254autoserv_parser = autoserv_parser()