blob: a0e7fc6c4a50e35f1f9095cb4af86ff6c517bfee [file] [log] [blame]
Keun Soo Yimb293fdb2016-09-21 16:03:44 -07001#!/usr/bin/env python
2#
3# Copyright 2016 - The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
Fang Dengfed6a6f2017-03-01 18:27:28 -080016r"""Cloud Android Driver.
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070017
18This CLI manages google compute engine project for android devices.
19
20- Prerequisites:
21 See: go/acloud-manual
22
23- Configuration:
24 The script takes a required configuration file, which should look like
25 <Start of the file>
26 # If using service account
27 service_account_name: "your_account@developer.gserviceaccount.com"
28 service_account_private_key_path: "/path/to/your-project.p12"
29
30 # If using OAuth2 authentication flow
31 client_id: <client id created in the project>
32 client_secret: <client secret for the client id>
33
34 # Optional
Fang Deng69498c32017-03-02 14:29:30 -080035 ssh_private_key_path: "~/.ssh/acloud_rsa"
36 ssh_public_key_path: "~/.ssh/acloud_rsa.pub"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070037 orientation: "portrait"
38 resolution: "800x1280x32x213"
39 network: "default"
40 machine_type: "n1-standard-1"
41 extra_data_disk_size_gb: 10 # 4G or 10G
42
43 # Required
44 project: "your-project"
45 zone: "us-central1-f"
46 storage_bucket_name: "your_google_storage_bucket_name"
47 <End of the file>
48
49 Save it at /path/to/acloud.config
50
51- Example calls:
52 - Create two instances:
Kevin Chengb5963882018-05-09 00:06:27 -070053 $ acloud.par create_cf
54 --build_target aosp_cf_x86_phone-userdebug \
Fang Dengfed6a6f2017-03-01 18:27:28 -080055 --build_id 3744001 --num 2 --config_file /path/to/acloud.config \
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070056 --report_file /tmp/acloud_report.json --log_file /tmp/acloud.log
57
58 - Delete two instances:
59 $ acloud.par delete --instance_names
Fang Dengfed6a6f2017-03-01 18:27:28 -080060 ins-b638cdba-3744001-gce-x86-phone-userdebug-fastbuild3c-linux
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070061 --config_file /path/to/acloud.config
62 --report_file /tmp/acloud_report.json --log_file /tmp/acloud.log
63"""
64import argparse
65import getpass
66import logging
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070067import sys
68
Kevin Chengf4137c62018-05-22 16:06:58 -070069# Needed to silence oauth2client.
70logging.basicConfig(level=logging.CRITICAL)
Kevin Chengb5963882018-05-09 00:06:27 -070071
Kevin Chengf4137c62018-05-22 16:06:58 -070072# pylint: disable=wrong-import-position
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070073from acloud.internal import constants
74from acloud.public import acloud_common
75from acloud.public import config
76from acloud.public import device_driver
77from acloud.public import errors
Kevin Chengb5963882018-05-09 00:06:27 -070078from acloud.public.actions import create_cuttlefish_action
79from acloud.public.actions import create_goldfish_action
Kevin Chengee6030f2018-06-26 10:55:30 -070080from acloud.setup import setup
81from acloud.setup import setup_args
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070082
83LOGGING_FMT = "%(asctime)s |%(levelname)s| %(module)s:%(lineno)s| %(message)s"
Tri Vo8e292532016-10-01 16:55:51 -070084LOGGER_NAME = "acloud_main"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070085
86# Commands
87CMD_CREATE = "create"
Kevin Chengb5963882018-05-09 00:06:27 -070088CMD_CREATE_CUTTLEFISH = "create_cf"
89CMD_CREATE_GOLDFISH = "create_gf"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070090CMD_DELETE = "delete"
91CMD_CLEANUP = "cleanup"
Fang Deng69498c32017-03-02 14:29:30 -080092CMD_SSHKEY = "project_sshkey"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070093
94
Kevin Cheng3031f8a2018-05-16 13:21:51 -070095# pylint: disable=too-many-statements
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070096def _ParseArgs(args):
97 """Parse args.
98
99 Args:
100 args: Argument list passed from main.
101
102 Returns:
103 Parsed args.
104 """
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700105 usage = ",".join([
Kevin Chengee6030f2018-06-26 10:55:30 -0700106 CMD_CREATE, CMD_CREATE_CUTTLEFISH, CMD_DELETE, CMD_CLEANUP, CMD_SSHKEY,
107 setup_args.CMD_SETUP,
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700108 ])
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700109 parser = argparse.ArgumentParser(
110 description=__doc__,
111 formatter_class=argparse.RawDescriptionHelpFormatter,
Kevin Cheng7ff74be2018-05-23 14:18:55 -0700112 usage="acloud {" + usage + "} ...")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700113 subparsers = parser.add_subparsers()
114 subparser_list = []
115
116 # Command "create"
117 create_parser = subparsers.add_parser(CMD_CREATE)
118 create_parser.required = False
119 create_parser.set_defaults(which=CMD_CREATE)
120 create_parser.add_argument(
121 "--build_target",
122 type=str,
123 dest="build_target",
Kevin Chengb5963882018-05-09 00:06:27 -0700124 help="Android build target, e.g. aosp_cf_x86_phone-userdebug, "
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700125 "or short names: phone, tablet, or tablet_mobile.")
126 create_parser.add_argument(
127 "--branch",
128 type=str,
129 dest="branch",
130 help="Android branch, e.g. mnc-dev or git_mnc-dev")
Kevin Chengb5963882018-05-09 00:06:27 -0700131 # TODO: Support HEAD (the latest build)
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700132 create_parser.add_argument(
133 "--build_id",
134 type=str,
135 dest="build_id",
136 help="Android build id, e.g. 2145099, P2804227")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700137 create_parser.add_argument(
138 "--spec",
139 type=str,
140 dest="spec",
141 required=False,
142 help="The name of a pre-configured device spec that we are "
143 "going to use. Choose from: %s" % ", ".join(constants.SPEC_NAMES))
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700144 create_parser.add_argument(
145 "--num",
146 type=int,
147 dest="num",
148 required=False,
149 default=1,
150 help="Number of instances to create.")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700151 create_parser.add_argument(
152 "--gce_image",
153 type=str,
154 dest="gce_image",
155 required=False,
156 help="Name of an existing compute engine image to reuse.")
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700157 create_parser.add_argument(
158 "--local_disk_image",
159 type=str,
160 dest="local_disk_image",
161 required=False,
162 help="Path to a local disk image to use, "
163 "e.g /tmp/avd-system.tar.gz")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700164 create_parser.add_argument(
165 "--no_cleanup",
166 dest="no_cleanup",
167 default=False,
168 action="store_true",
169 help="Do not clean up temporary disk image and compute engine image. "
170 "For debugging purposes.")
171 create_parser.add_argument(
172 "--serial_log_file",
173 type=str,
174 dest="serial_log_file",
175 required=False,
176 help="Path to a *tar.gz file where serial logs will be saved "
177 "when a device fails on boot.")
178 create_parser.add_argument(
179 "--logcat_file",
180 type=str,
181 dest="logcat_file",
182 required=False,
183 help="Path to a *tar.gz file where logcat logs will be saved "
184 "when a device fails on boot.")
Kevin Chengb5963882018-05-09 00:06:27 -0700185 create_parser.add_argument(
186 "--autoconnect",
187 action="store_true",
188 dest="autoconnect",
189 required=False,
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700190 help=
191 "For each instance created, we will automatically creates both 2 ssh"
Kevin Chengb5963882018-05-09 00:06:27 -0700192 " tunnels forwarding both adb & vnc. Then add the device to adb.")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700193
194 subparser_list.append(create_parser)
195
Kevin Chengb5963882018-05-09 00:06:27 -0700196 # Command "create_cf", create cuttlefish instances
197 create_cf_parser = subparsers.add_parser(CMD_CREATE_CUTTLEFISH)
198 create_cf_parser.required = False
199 create_cf_parser.set_defaults(which=CMD_CREATE_CUTTLEFISH)
200 create_cf_parser.add_argument(
201 "--build_target",
202 type=str,
203 dest="build_target",
204 help="Android build target, should be a cuttlefish target name.")
205 create_cf_parser.add_argument(
206 "--branch",
207 type=str,
208 dest="branch",
209 help="Android branch, e.g. git_master")
210 create_cf_parser.add_argument(
211 "--build_id",
212 type=str,
213 dest="build_id",
214 help="Android build id, e.g. 2145099, P2804227")
215 create_cf_parser.add_argument(
216 "--kernel_build_id",
217 type=str,
218 dest="kernel_build_id",
219 required=False,
220 help="Android kernel build id, e.g. 4586590. This is to test a new"
221 " kernel build with a particular Android build (--build_id). If not"
222 " specified, the kernel that's bundled with the Android build would"
223 " be used.")
224 create_cf_parser.add_argument(
225 "--num",
226 type=int,
227 dest="num",
228 required=False,
229 default=1,
230 help="Number of instances to create.")
231 create_cf_parser.add_argument(
232 "--serial_log_file",
233 type=str,
234 dest="serial_log_file",
235 required=False,
236 help="Path to a *tar.gz file where serial logs will be saved "
237 "when a device fails on boot.")
238 create_cf_parser.add_argument(
239 "--logcat_file",
240 type=str,
241 dest="logcat_file",
242 required=False,
243 help="Path to a *tar.gz file where logcat logs will be saved "
244 "when a device fails on boot.")
245 create_cf_parser.add_argument(
246 "--autoconnect",
247 action="store_true",
248 dest="autoconnect",
249 required=False,
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700250 help=
251 "For each instance created, we will automatically creates both 2 ssh"
Kevin Chengb5963882018-05-09 00:06:27 -0700252 " tunnels forwarding both adb & vnc. Then add the device to adb.")
253
254 subparser_list.append(create_cf_parser)
255
256 # Command "create_gf", create goldfish instances
257 # In order to create a goldfish device we need the following parameters:
258 # 1. The emulator build we wish to use, this is the binary that emulates
259 # an android device. See go/emu-dev for more
260 # 2. A system-image. This is the android release we wish to run on the
261 # emulated hardware.
262 create_gf_parser = subparsers.add_parser(CMD_CREATE_GOLDFISH)
263 create_gf_parser.required = False
264 create_gf_parser.set_defaults(which=CMD_CREATE_GOLDFISH)
265 create_gf_parser.add_argument(
266 "--build_target",
267 type=str,
268 dest="build_target",
269 help="Android build target, should be a goldfish target name.")
270 create_gf_parser.add_argument(
271 "--branch",
272 type=str,
273 dest="branch",
274 help="Android branch, e.g. git_master")
275 create_gf_parser.add_argument(
276 "--build_id",
277 type=str,
278 dest="build_id",
279 help="Android build id, e.g. 4669424, P2804227")
280 create_gf_parser.add_argument(
281 "--emulator_build_id",
282 type=str,
283 dest="emulator_build_id",
284 required=False,
285 help="Emulator build used to run the images. e.g. 4669466.")
286 create_gf_parser.add_argument(
287 "--gpu",
288 type=str,
289 dest="gpu",
290 required=False,
291 default=None,
292 help="GPU accelerator to use if any."
293 " e.g. nvidia-tesla-k80, omit to use swiftshader")
294 create_gf_parser.add_argument(
295 "--num",
296 type=int,
297 dest="num",
298 required=False,
299 default=1,
300 help="Number of instances to create.")
301 create_gf_parser.add_argument(
302 "--serial_log_file",
303 type=str,
304 dest="serial_log_file",
305 required=False,
306 help="Path to a *tar.gz file where serial logs will be saved "
307 "when a device fails on boot.")
308 create_gf_parser.add_argument(
309 "--logcat_file",
310 type=str,
311 dest="logcat_file",
312 required=False,
313 help="Path to a *tar.gz file where logcat logs will be saved "
314 "when a device fails on boot.")
315 create_gf_parser.add_argument(
316 "--autoconnect",
317 action="store_true",
318 dest="autoconnect",
319 required=False,
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700320 help=
321 "For each instance created, we will automatically creates both 2 ssh"
Kevin Chengb5963882018-05-09 00:06:27 -0700322 " tunnels forwarding both adb & vnc. Then add the device to adb.")
Kevin Chengbced4af2018-06-26 10:35:01 -0700323 create_gf_parser.add_argument(
324 "--base_image",
325 type=str,
326 dest="base_image",
327 required=False,
328 help="Name of the goldfish base image to be used to create the instance. "
329 "This will override stable_goldfish_host_image_name from config. "
330 "e.g. emu-dev-cts-061118")
Kevin Chengb5963882018-05-09 00:06:27 -0700331
332 subparser_list.append(create_gf_parser)
333
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700334 # Command "Delete"
335 delete_parser = subparsers.add_parser(CMD_DELETE)
336 delete_parser.required = False
337 delete_parser.set_defaults(which=CMD_DELETE)
338 delete_parser.add_argument(
339 "--instance_names",
340 dest="instance_names",
341 nargs="+",
342 required=True,
343 help="The names of the instances that need to delete, "
344 "separated by spaces, e.g. --instance_names instance-1 instance-2")
345 subparser_list.append(delete_parser)
346
347 # Command "cleanup"
348 cleanup_parser = subparsers.add_parser(CMD_CLEANUP)
349 cleanup_parser.required = False
350 cleanup_parser.set_defaults(which=CMD_CLEANUP)
351 cleanup_parser.add_argument(
352 "--expiration_mins",
353 type=int,
354 dest="expiration_mins",
355 required=True,
356 help="Garbage collect all gce instances, gce images, cached disk "
357 "images that are older than |expiration_mins|.")
358 subparser_list.append(cleanup_parser)
359
Fang Deng69498c32017-03-02 14:29:30 -0800360 # Command "project_sshkey"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700361 sshkey_parser = subparsers.add_parser(CMD_SSHKEY)
362 sshkey_parser.required = False
363 sshkey_parser.set_defaults(which=CMD_SSHKEY)
364 sshkey_parser.add_argument(
365 "--user",
366 type=str,
367 dest="user",
368 default=getpass.getuser(),
369 help="The user name which the sshkey belongs to, default to: %s." %
370 getpass.getuser())
371 sshkey_parser.add_argument(
372 "--ssh_rsa_path",
373 type=str,
374 dest="ssh_rsa_path",
375 required=True,
Fang Deng69498c32017-03-02 14:29:30 -0800376 help="Absolute path to the file that contains the public rsa key "
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700377 "that will be added as project-wide ssh key.")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700378 subparser_list.append(sshkey_parser)
379
Kevin Chengee6030f2018-06-26 10:55:30 -0700380 # Command "setup"
381 subparser_list.append(setup_args.GetSetupArgParser(subparsers))
382
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700383 # Add common arguments.
Kevin Chengb21d7712018-05-24 14:54:55 -0700384 for subparser in subparser_list:
385 acloud_common.AddCommonArguments(subparser)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700386
387 return parser.parse_args(args)
388
389
390def _TranslateAlias(parsed_args):
391 """Translate alias to Launch Control compatible values.
392
393 This method translates alias to Launch Control compatible values.
394 - branch: "git_" prefix will be added if branch name doesn't have it.
395 - build_target: For example, "phone" will be translated to full target
396 name "git_x86_phone-userdebug",
397
398 Args:
399 parsed_args: Parsed args.
400
401 Returns:
402 Parsed args with its values being translated.
403 """
404 if parsed_args.which == CMD_CREATE:
405 if (parsed_args.branch and
406 not parsed_args.branch.startswith(constants.BRANCH_PREFIX)):
407 parsed_args.branch = constants.BRANCH_PREFIX + parsed_args.branch
408 parsed_args.build_target = constants.BUILD_TARGET_MAPPING.get(
409 parsed_args.build_target, parsed_args.build_target)
410 return parsed_args
411
412
413def _VerifyArgs(parsed_args):
414 """Verify args.
415
416 Args:
417 parsed_args: Parsed args.
418
419 Raises:
420 errors.CommandArgError: If args are invalid.
421 """
422 if parsed_args.which == CMD_CREATE:
423 if (parsed_args.spec and parsed_args.spec not in constants.SPEC_NAMES):
424 raise errors.CommandArgError(
425 "%s is not valid. Choose from: %s" %
426 (parsed_args.spec, ", ".join(constants.SPEC_NAMES)))
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700427 if not ((parsed_args.build_id and parsed_args.build_target)
428 or parsed_args.gce_image or parsed_args.local_disk_image):
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700429 raise errors.CommandArgError(
430 "At least one of the following should be specified: "
431 "--build_id and --build_target, or --gce_image, or "
432 "--local_disk_image.")
433 if bool(parsed_args.build_id) != bool(parsed_args.build_target):
434 raise errors.CommandArgError(
435 "Must specify --build_id and --build_target at the same time.")
Kevin Chengb5963882018-05-09 00:06:27 -0700436
437 if parsed_args.which in [CMD_CREATE_CUTTLEFISH, CMD_CREATE_GOLDFISH]:
438 if not parsed_args.build_id or not parsed_args.build_target:
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700439 raise errors.CommandArgError(
440 "Must specify --build_id and --build_target")
Kevin Chengb5963882018-05-09 00:06:27 -0700441
442 if parsed_args.which == CMD_CREATE_GOLDFISH:
443 if not parsed_args.emulator_build_id:
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700444 raise errors.CommandArgError("Must specify --emulator_build_id")
Kevin Chengb5963882018-05-09 00:06:27 -0700445
446 if parsed_args.which in [
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700447 CMD_CREATE, CMD_CREATE_CUTTLEFISH, CMD_CREATE_GOLDFISH
Kevin Chengb5963882018-05-09 00:06:27 -0700448 ]:
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700449 if (parsed_args.serial_log_file
450 and not parsed_args.serial_log_file.endswith(".tar.gz")):
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700451 raise errors.CommandArgError(
452 "--serial_log_file must ends with .tar.gz")
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700453 if (parsed_args.logcat_file
454 and not parsed_args.logcat_file.endswith(".tar.gz")):
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700455 raise errors.CommandArgError(
456 "--logcat_file must ends with .tar.gz")
457
458
459def _SetupLogging(log_file, verbose, very_verbose):
460 """Setup logging.
461
462 Args:
463 log_file: path to log file.
464 verbose: If True, log at DEBUG level, otherwise log at INFO level.
465 very_verbose: If True, log at DEBUG level and turn on logging on
466 all libraries. Take take precedence over |verbose|.
467 """
468 if very_verbose:
469 logger = logging.getLogger()
470 else:
471 logger = logging.getLogger(LOGGER_NAME)
472
473 logging_level = logging.DEBUG if verbose or very_verbose else logging.INFO
474 logger.setLevel(logging_level)
475
476 if not log_file:
477 handler = logging.StreamHandler()
478 else:
479 handler = logging.FileHandler(filename=log_file)
480 log_formatter = logging.Formatter(LOGGING_FMT)
481 handler.setFormatter(log_formatter)
482 logger.addHandler(handler)
483
484
485def main(argv):
486 """Main entry.
487
488 Args:
489 argv: A list of system arguments.
490
491 Returns:
492 0 if success. None-zero if fails.
493 """
494 args = _ParseArgs(argv)
495 _SetupLogging(args.log_file, args.verbose, args.very_verbose)
496 args = _TranslateAlias(args)
497 _VerifyArgs(args)
498
499 config_mgr = config.AcloudConfigManager(args.config_file)
500 cfg = config_mgr.Load()
501 cfg.OverrideWithArgs(args)
502
Fang Dengcef4b112017-03-02 11:20:17 -0800503 # Check access.
504 device_driver.CheckAccess(cfg)
505
Kevin Chengee6030f2018-06-26 10:55:30 -0700506 report = None
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700507 if args.which == CMD_CREATE:
508 report = device_driver.CreateAndroidVirtualDevices(
509 cfg,
510 args.build_target,
511 args.build_id,
512 args.num,
513 args.gce_image,
514 args.local_disk_image,
515 cleanup=not args.no_cleanup,
516 serial_log_file=args.serial_log_file,
Kevin Chengb5963882018-05-09 00:06:27 -0700517 logcat_file=args.logcat_file,
518 autoconnect=args.autoconnect)
519 elif args.which == CMD_CREATE_CUTTLEFISH:
520 report = create_cuttlefish_action.CreateDevices(
521 cfg=cfg,
522 build_target=args.build_target,
523 build_id=args.build_id,
524 kernel_build_id=args.kernel_build_id,
525 num=args.num,
526 serial_log_file=args.serial_log_file,
527 logcat_file=args.logcat_file,
528 autoconnect=args.autoconnect)
529 elif args.which == CMD_CREATE_GOLDFISH:
530 report = create_goldfish_action.CreateDevices(
531 cfg=cfg,
532 build_target=args.build_target,
533 build_id=args.build_id,
534 emulator_build_id=args.emulator_build_id,
535 gpu=args.gpu,
536 num=args.num,
537 serial_log_file=args.serial_log_file,
538 logcat_file=args.logcat_file,
539 autoconnect=args.autoconnect)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700540 elif args.which == CMD_DELETE:
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700541 report = device_driver.DeleteAndroidVirtualDevices(
542 cfg, args.instance_names)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700543 elif args.which == CMD_CLEANUP:
544 report = device_driver.Cleanup(cfg, args.expiration_mins)
545 elif args.which == CMD_SSHKEY:
546 report = device_driver.AddSshRsa(cfg, args.user, args.ssh_rsa_path)
Kevin Chengee6030f2018-06-26 10:55:30 -0700547 elif args.which == setup_args.CMD_SETUP:
548 setup.Run(args)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700549 else:
550 sys.stderr.write("Invalid command %s" % args.which)
551 return 2
552
Kevin Chengee6030f2018-06-26 10:55:30 -0700553 if report:
554 report.Dump(args.report_file)
555 if report.errors:
556 msg = "\n".join(report.errors)
557 sys.stderr.write("Encountered the following errors:\n%s\n" % msg)
558 return 1
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700559 return 0
Tri Vo8e292532016-10-01 16:55:51 -0700560
561
562if __name__ == "__main__":
563 main(sys.argv[1:])