blob: feeaa3a6a20c0f31b3f2bcf830fcf97fa79e1408 [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"
xingdai8a00d462018-07-30 14:24:48 -070029 # Or
30 service_account_json_private_key_path: "/path/to/your-project.json"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070031
32 # If using OAuth2 authentication flow
33 client_id: <client id created in the project>
34 client_secret: <client secret for the client id>
35
36 # Optional
Fang Deng69498c32017-03-02 14:29:30 -080037 ssh_private_key_path: "~/.ssh/acloud_rsa"
38 ssh_public_key_path: "~/.ssh/acloud_rsa.pub"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070039 orientation: "portrait"
40 resolution: "800x1280x32x213"
41 network: "default"
42 machine_type: "n1-standard-1"
43 extra_data_disk_size_gb: 10 # 4G or 10G
44
45 # Required
46 project: "your-project"
47 zone: "us-central1-f"
48 storage_bucket_name: "your_google_storage_bucket_name"
49 <End of the file>
50
51 Save it at /path/to/acloud.config
52
53- Example calls:
54 - Create two instances:
Kevin Chengb5963882018-05-09 00:06:27 -070055 $ acloud.par create_cf
56 --build_target aosp_cf_x86_phone-userdebug \
Fang Dengfed6a6f2017-03-01 18:27:28 -080057 --build_id 3744001 --num 2 --config_file /path/to/acloud.config \
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070058 --report_file /tmp/acloud_report.json --log_file /tmp/acloud.log
59
60 - Delete two instances:
61 $ acloud.par delete --instance_names
Fang Dengfed6a6f2017-03-01 18:27:28 -080062 ins-b638cdba-3744001-gce-x86-phone-userdebug-fastbuild3c-linux
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070063 --config_file /path/to/acloud.config
64 --report_file /tmp/acloud_report.json --log_file /tmp/acloud.log
65"""
66import argparse
67import getpass
68import logging
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070069import sys
70
Kevin Chengf4137c62018-05-22 16:06:58 -070071# Needed to silence oauth2client.
72logging.basicConfig(level=logging.CRITICAL)
Kevin Chengb5963882018-05-09 00:06:27 -070073
Kevin Chengf4137c62018-05-22 16:06:58 -070074# pylint: disable=wrong-import-position
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070075from acloud.internal import constants
76from acloud.public import acloud_common
77from acloud.public import config
78from acloud.public import device_driver
79from acloud.public import errors
Kevin Chengb5963882018-05-09 00:06:27 -070080from acloud.public.actions import create_cuttlefish_action
81from acloud.public.actions import create_goldfish_action
Kevin Chengee6030f2018-06-26 10:55:30 -070082from acloud.setup import setup
83from acloud.setup import setup_args
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070084
85LOGGING_FMT = "%(asctime)s |%(levelname)s| %(module)s:%(lineno)s| %(message)s"
Tri Vo8e292532016-10-01 16:55:51 -070086LOGGER_NAME = "acloud_main"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070087
88# Commands
89CMD_CREATE = "create"
Kevin Chengb5963882018-05-09 00:06:27 -070090CMD_CREATE_CUTTLEFISH = "create_cf"
91CMD_CREATE_GOLDFISH = "create_gf"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070092CMD_DELETE = "delete"
93CMD_CLEANUP = "cleanup"
Fang Deng69498c32017-03-02 14:29:30 -080094CMD_SSHKEY = "project_sshkey"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070095
96
Kevin Cheng3031f8a2018-05-16 13:21:51 -070097# pylint: disable=too-many-statements
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070098def _ParseArgs(args):
99 """Parse args.
100
101 Args:
102 args: Argument list passed from main.
103
104 Returns:
105 Parsed args.
106 """
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700107 usage = ",".join([
Kevin Chengee6030f2018-06-26 10:55:30 -0700108 CMD_CREATE, CMD_CREATE_CUTTLEFISH, CMD_DELETE, CMD_CLEANUP, CMD_SSHKEY,
109 setup_args.CMD_SETUP,
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700110 ])
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700111 parser = argparse.ArgumentParser(
112 description=__doc__,
113 formatter_class=argparse.RawDescriptionHelpFormatter,
Kevin Cheng7ff74be2018-05-23 14:18:55 -0700114 usage="acloud {" + usage + "} ...")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700115 subparsers = parser.add_subparsers()
116 subparser_list = []
117
118 # Command "create"
119 create_parser = subparsers.add_parser(CMD_CREATE)
120 create_parser.required = False
121 create_parser.set_defaults(which=CMD_CREATE)
122 create_parser.add_argument(
123 "--build_target",
124 type=str,
125 dest="build_target",
Kevin Chengb5963882018-05-09 00:06:27 -0700126 help="Android build target, e.g. aosp_cf_x86_phone-userdebug, "
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700127 "or short names: phone, tablet, or tablet_mobile.")
128 create_parser.add_argument(
129 "--branch",
130 type=str,
131 dest="branch",
132 help="Android branch, e.g. mnc-dev or git_mnc-dev")
Kevin Chengb5963882018-05-09 00:06:27 -0700133 # TODO: Support HEAD (the latest build)
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700134 create_parser.add_argument(
135 "--build_id",
136 type=str,
137 dest="build_id",
138 help="Android build id, e.g. 2145099, P2804227")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700139 create_parser.add_argument(
140 "--spec",
141 type=str,
142 dest="spec",
143 required=False,
144 help="The name of a pre-configured device spec that we are "
145 "going to use. Choose from: %s" % ", ".join(constants.SPEC_NAMES))
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700146 create_parser.add_argument(
147 "--num",
148 type=int,
149 dest="num",
150 required=False,
151 default=1,
152 help="Number of instances to create.")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700153 create_parser.add_argument(
154 "--gce_image",
155 type=str,
156 dest="gce_image",
157 required=False,
158 help="Name of an existing compute engine image to reuse.")
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700159 create_parser.add_argument(
160 "--local_disk_image",
161 type=str,
162 dest="local_disk_image",
163 required=False,
164 help="Path to a local disk image to use, "
165 "e.g /tmp/avd-system.tar.gz")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700166 create_parser.add_argument(
167 "--no_cleanup",
168 dest="no_cleanup",
169 default=False,
170 action="store_true",
171 help="Do not clean up temporary disk image and compute engine image. "
172 "For debugging purposes.")
173 create_parser.add_argument(
174 "--serial_log_file",
175 type=str,
176 dest="serial_log_file",
177 required=False,
178 help="Path to a *tar.gz file where serial logs will be saved "
179 "when a device fails on boot.")
180 create_parser.add_argument(
181 "--logcat_file",
182 type=str,
183 dest="logcat_file",
184 required=False,
185 help="Path to a *tar.gz file where logcat logs will be saved "
186 "when a device fails on boot.")
Kevin Chengb5963882018-05-09 00:06:27 -0700187 create_parser.add_argument(
188 "--autoconnect",
189 action="store_true",
190 dest="autoconnect",
191 required=False,
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700192 help=
193 "For each instance created, we will automatically creates both 2 ssh"
Kevin Chengb5963882018-05-09 00:06:27 -0700194 " tunnels forwarding both adb & vnc. Then add the device to adb.")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700195
196 subparser_list.append(create_parser)
197
Kevin Chengb5963882018-05-09 00:06:27 -0700198 # Command "create_cf", create cuttlefish instances
199 create_cf_parser = subparsers.add_parser(CMD_CREATE_CUTTLEFISH)
200 create_cf_parser.required = False
201 create_cf_parser.set_defaults(which=CMD_CREATE_CUTTLEFISH)
202 create_cf_parser.add_argument(
203 "--build_target",
204 type=str,
205 dest="build_target",
206 help="Android build target, should be a cuttlefish target name.")
207 create_cf_parser.add_argument(
208 "--branch",
209 type=str,
210 dest="branch",
211 help="Android branch, e.g. git_master")
212 create_cf_parser.add_argument(
213 "--build_id",
214 type=str,
215 dest="build_id",
216 help="Android build id, e.g. 2145099, P2804227")
217 create_cf_parser.add_argument(
218 "--kernel_build_id",
219 type=str,
220 dest="kernel_build_id",
221 required=False,
222 help="Android kernel build id, e.g. 4586590. This is to test a new"
223 " kernel build with a particular Android build (--build_id). If not"
224 " specified, the kernel that's bundled with the Android build would"
225 " be used.")
226 create_cf_parser.add_argument(
227 "--num",
228 type=int,
229 dest="num",
230 required=False,
231 default=1,
232 help="Number of instances to create.")
233 create_cf_parser.add_argument(
234 "--serial_log_file",
235 type=str,
236 dest="serial_log_file",
237 required=False,
238 help="Path to a *tar.gz file where serial logs will be saved "
239 "when a device fails on boot.")
240 create_cf_parser.add_argument(
241 "--logcat_file",
242 type=str,
243 dest="logcat_file",
244 required=False,
245 help="Path to a *tar.gz file where logcat logs will be saved "
246 "when a device fails on boot.")
247 create_cf_parser.add_argument(
248 "--autoconnect",
249 action="store_true",
250 dest="autoconnect",
251 required=False,
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700252 help=
253 "For each instance created, we will automatically creates both 2 ssh"
Kevin Chengb5963882018-05-09 00:06:27 -0700254 " tunnels forwarding both adb & vnc. Then add the device to adb.")
255
256 subparser_list.append(create_cf_parser)
257
258 # Command "create_gf", create goldfish instances
259 # In order to create a goldfish device we need the following parameters:
260 # 1. The emulator build we wish to use, this is the binary that emulates
261 # an android device. See go/emu-dev for more
262 # 2. A system-image. This is the android release we wish to run on the
263 # emulated hardware.
264 create_gf_parser = subparsers.add_parser(CMD_CREATE_GOLDFISH)
265 create_gf_parser.required = False
266 create_gf_parser.set_defaults(which=CMD_CREATE_GOLDFISH)
267 create_gf_parser.add_argument(
268 "--build_target",
269 type=str,
270 dest="build_target",
271 help="Android build target, should be a goldfish target name.")
272 create_gf_parser.add_argument(
273 "--branch",
274 type=str,
275 dest="branch",
276 help="Android branch, e.g. git_master")
277 create_gf_parser.add_argument(
278 "--build_id",
279 type=str,
280 dest="build_id",
281 help="Android build id, e.g. 4669424, P2804227")
282 create_gf_parser.add_argument(
283 "--emulator_build_id",
284 type=str,
285 dest="emulator_build_id",
286 required=False,
287 help="Emulator build used to run the images. e.g. 4669466.")
288 create_gf_parser.add_argument(
289 "--gpu",
290 type=str,
291 dest="gpu",
292 required=False,
293 default=None,
294 help="GPU accelerator to use if any."
295 " e.g. nvidia-tesla-k80, omit to use swiftshader")
296 create_gf_parser.add_argument(
297 "--num",
298 type=int,
299 dest="num",
300 required=False,
301 default=1,
302 help="Number of instances to create.")
303 create_gf_parser.add_argument(
304 "--serial_log_file",
305 type=str,
306 dest="serial_log_file",
307 required=False,
308 help="Path to a *tar.gz file where serial logs will be saved "
309 "when a device fails on boot.")
310 create_gf_parser.add_argument(
311 "--logcat_file",
312 type=str,
313 dest="logcat_file",
314 required=False,
315 help="Path to a *tar.gz file where logcat logs will be saved "
316 "when a device fails on boot.")
317 create_gf_parser.add_argument(
318 "--autoconnect",
319 action="store_true",
320 dest="autoconnect",
321 required=False,
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700322 help=
323 "For each instance created, we will automatically creates both 2 ssh"
Kevin Chengb5963882018-05-09 00:06:27 -0700324 " tunnels forwarding both adb & vnc. Then add the device to adb.")
Kevin Chengbced4af2018-06-26 10:35:01 -0700325 create_gf_parser.add_argument(
326 "--base_image",
327 type=str,
328 dest="base_image",
329 required=False,
330 help="Name of the goldfish base image to be used to create the instance. "
331 "This will override stable_goldfish_host_image_name from config. "
332 "e.g. emu-dev-cts-061118")
Kevin Chengb5963882018-05-09 00:06:27 -0700333
334 subparser_list.append(create_gf_parser)
335
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700336 # Command "Delete"
337 delete_parser = subparsers.add_parser(CMD_DELETE)
338 delete_parser.required = False
339 delete_parser.set_defaults(which=CMD_DELETE)
340 delete_parser.add_argument(
341 "--instance_names",
342 dest="instance_names",
343 nargs="+",
344 required=True,
345 help="The names of the instances that need to delete, "
346 "separated by spaces, e.g. --instance_names instance-1 instance-2")
347 subparser_list.append(delete_parser)
348
349 # Command "cleanup"
350 cleanup_parser = subparsers.add_parser(CMD_CLEANUP)
351 cleanup_parser.required = False
352 cleanup_parser.set_defaults(which=CMD_CLEANUP)
353 cleanup_parser.add_argument(
354 "--expiration_mins",
355 type=int,
356 dest="expiration_mins",
357 required=True,
358 help="Garbage collect all gce instances, gce images, cached disk "
359 "images that are older than |expiration_mins|.")
360 subparser_list.append(cleanup_parser)
361
Fang Deng69498c32017-03-02 14:29:30 -0800362 # Command "project_sshkey"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700363 sshkey_parser = subparsers.add_parser(CMD_SSHKEY)
364 sshkey_parser.required = False
365 sshkey_parser.set_defaults(which=CMD_SSHKEY)
366 sshkey_parser.add_argument(
367 "--user",
368 type=str,
369 dest="user",
370 default=getpass.getuser(),
371 help="The user name which the sshkey belongs to, default to: %s." %
372 getpass.getuser())
373 sshkey_parser.add_argument(
374 "--ssh_rsa_path",
375 type=str,
376 dest="ssh_rsa_path",
377 required=True,
Fang Deng69498c32017-03-02 14:29:30 -0800378 help="Absolute path to the file that contains the public rsa key "
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700379 "that will be added as project-wide ssh key.")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700380 subparser_list.append(sshkey_parser)
381
Kevin Chengee6030f2018-06-26 10:55:30 -0700382 # Command "setup"
383 subparser_list.append(setup_args.GetSetupArgParser(subparsers))
384
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700385 # Add common arguments.
Kevin Chengb21d7712018-05-24 14:54:55 -0700386 for subparser in subparser_list:
387 acloud_common.AddCommonArguments(subparser)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700388
389 return parser.parse_args(args)
390
391
392def _TranslateAlias(parsed_args):
393 """Translate alias to Launch Control compatible values.
394
395 This method translates alias to Launch Control compatible values.
396 - branch: "git_" prefix will be added if branch name doesn't have it.
397 - build_target: For example, "phone" will be translated to full target
398 name "git_x86_phone-userdebug",
399
400 Args:
401 parsed_args: Parsed args.
402
403 Returns:
404 Parsed args with its values being translated.
405 """
406 if parsed_args.which == CMD_CREATE:
407 if (parsed_args.branch and
408 not parsed_args.branch.startswith(constants.BRANCH_PREFIX)):
409 parsed_args.branch = constants.BRANCH_PREFIX + parsed_args.branch
410 parsed_args.build_target = constants.BUILD_TARGET_MAPPING.get(
411 parsed_args.build_target, parsed_args.build_target)
412 return parsed_args
413
414
415def _VerifyArgs(parsed_args):
416 """Verify args.
417
418 Args:
419 parsed_args: Parsed args.
420
421 Raises:
422 errors.CommandArgError: If args are invalid.
423 """
424 if parsed_args.which == CMD_CREATE:
425 if (parsed_args.spec and parsed_args.spec not in constants.SPEC_NAMES):
426 raise errors.CommandArgError(
427 "%s is not valid. Choose from: %s" %
428 (parsed_args.spec, ", ".join(constants.SPEC_NAMES)))
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700429 if not ((parsed_args.build_id and parsed_args.build_target)
430 or parsed_args.gce_image or parsed_args.local_disk_image):
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700431 raise errors.CommandArgError(
432 "At least one of the following should be specified: "
433 "--build_id and --build_target, or --gce_image, or "
434 "--local_disk_image.")
435 if bool(parsed_args.build_id) != bool(parsed_args.build_target):
436 raise errors.CommandArgError(
437 "Must specify --build_id and --build_target at the same time.")
Kevin Chengb5963882018-05-09 00:06:27 -0700438
439 if parsed_args.which in [CMD_CREATE_CUTTLEFISH, CMD_CREATE_GOLDFISH]:
440 if not parsed_args.build_id or not parsed_args.build_target:
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700441 raise errors.CommandArgError(
442 "Must specify --build_id and --build_target")
Kevin Chengb5963882018-05-09 00:06:27 -0700443
444 if parsed_args.which == CMD_CREATE_GOLDFISH:
445 if not parsed_args.emulator_build_id:
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700446 raise errors.CommandArgError("Must specify --emulator_build_id")
Kevin Chengb5963882018-05-09 00:06:27 -0700447
448 if parsed_args.which in [
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700449 CMD_CREATE, CMD_CREATE_CUTTLEFISH, CMD_CREATE_GOLDFISH
Kevin Chengb5963882018-05-09 00:06:27 -0700450 ]:
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700451 if (parsed_args.serial_log_file
452 and not parsed_args.serial_log_file.endswith(".tar.gz")):
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700453 raise errors.CommandArgError(
454 "--serial_log_file must ends with .tar.gz")
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700455 if (parsed_args.logcat_file
456 and not parsed_args.logcat_file.endswith(".tar.gz")):
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700457 raise errors.CommandArgError(
458 "--logcat_file must ends with .tar.gz")
459
460
461def _SetupLogging(log_file, verbose, very_verbose):
462 """Setup logging.
463
464 Args:
465 log_file: path to log file.
466 verbose: If True, log at DEBUG level, otherwise log at INFO level.
467 very_verbose: If True, log at DEBUG level and turn on logging on
468 all libraries. Take take precedence over |verbose|.
469 """
470 if very_verbose:
471 logger = logging.getLogger()
472 else:
473 logger = logging.getLogger(LOGGER_NAME)
474
475 logging_level = logging.DEBUG if verbose or very_verbose else logging.INFO
476 logger.setLevel(logging_level)
477
478 if not log_file:
479 handler = logging.StreamHandler()
480 else:
481 handler = logging.FileHandler(filename=log_file)
482 log_formatter = logging.Formatter(LOGGING_FMT)
483 handler.setFormatter(log_formatter)
484 logger.addHandler(handler)
485
486
487def main(argv):
488 """Main entry.
489
490 Args:
491 argv: A list of system arguments.
492
493 Returns:
494 0 if success. None-zero if fails.
495 """
496 args = _ParseArgs(argv)
497 _SetupLogging(args.log_file, args.verbose, args.very_verbose)
498 args = _TranslateAlias(args)
499 _VerifyArgs(args)
500
501 config_mgr = config.AcloudConfigManager(args.config_file)
502 cfg = config_mgr.Load()
503 cfg.OverrideWithArgs(args)
504
Fang Dengcef4b112017-03-02 11:20:17 -0800505 # Check access.
506 device_driver.CheckAccess(cfg)
507
Kevin Chengee6030f2018-06-26 10:55:30 -0700508 report = None
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700509 if args.which == CMD_CREATE:
510 report = device_driver.CreateAndroidVirtualDevices(
511 cfg,
512 args.build_target,
513 args.build_id,
514 args.num,
515 args.gce_image,
516 args.local_disk_image,
517 cleanup=not args.no_cleanup,
518 serial_log_file=args.serial_log_file,
Kevin Chengb5963882018-05-09 00:06:27 -0700519 logcat_file=args.logcat_file,
520 autoconnect=args.autoconnect)
521 elif args.which == CMD_CREATE_CUTTLEFISH:
522 report = create_cuttlefish_action.CreateDevices(
523 cfg=cfg,
524 build_target=args.build_target,
525 build_id=args.build_id,
526 kernel_build_id=args.kernel_build_id,
527 num=args.num,
528 serial_log_file=args.serial_log_file,
529 logcat_file=args.logcat_file,
530 autoconnect=args.autoconnect)
531 elif args.which == CMD_CREATE_GOLDFISH:
532 report = create_goldfish_action.CreateDevices(
533 cfg=cfg,
534 build_target=args.build_target,
535 build_id=args.build_id,
536 emulator_build_id=args.emulator_build_id,
537 gpu=args.gpu,
538 num=args.num,
539 serial_log_file=args.serial_log_file,
540 logcat_file=args.logcat_file,
541 autoconnect=args.autoconnect)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700542 elif args.which == CMD_DELETE:
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700543 report = device_driver.DeleteAndroidVirtualDevices(
544 cfg, args.instance_names)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700545 elif args.which == CMD_CLEANUP:
546 report = device_driver.Cleanup(cfg, args.expiration_mins)
547 elif args.which == CMD_SSHKEY:
548 report = device_driver.AddSshRsa(cfg, args.user, args.ssh_rsa_path)
Kevin Chengee6030f2018-06-26 10:55:30 -0700549 elif args.which == setup_args.CMD_SETUP:
550 setup.Run(args)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700551 else:
552 sys.stderr.write("Invalid command %s" % args.which)
553 return 2
554
Kevin Chengee6030f2018-06-26 10:55:30 -0700555 if report:
556 report.Dump(args.report_file)
557 if report.errors:
558 msg = "\n".join(report.errors)
559 sys.stderr.write("Encountered the following errors:\n%s\n" % msg)
560 return 1
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700561 return 0
Tri Vo8e292532016-10-01 16:55:51 -0700562
563
564if __name__ == "__main__":
565 main(sys.argv[1:])