• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import argparse
2import ast
3import logging
4import os
5import shlex
6import sys
7
8
9class 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
274autoserv_parser = autoserv_parser()
275