1# Copyright 2018 - The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14r"""Create args. 15 16Defines the create arg parser that holds create specific args. 17""" 18 19import argparse 20import logging 21import os 22 23from acloud import errors 24from acloud.create import create_common 25from acloud.internal import constants 26from acloud.internal.lib import utils 27 28logger = logging.getLogger(__name__) 29_DEFAULT_GPU = "default" 30CMD_CREATE = "create" 31 32 33# TODO: Add this into main create args once create_cf/gf is deprecated. 34def AddCommonCreateArgs(parser): 35 """Adds arguments common to create parsers. 36 37 Args: 38 parser: ArgumentParser object, used to parse flags. 39 """ 40 parser.add_argument( 41 "--num", 42 type=int, 43 dest="num", 44 required=False, 45 default=1, 46 help="Number of instances to create.") 47 parser.add_argument( 48 "--serial-log-file", 49 type=str, 50 dest="serial_log_file", 51 required=False, 52 help="Path to a *tar.gz file where serial logs will be saved " 53 "when a device fails on boot.") 54 parser.add_argument( 55 "--autoconnect", 56 type=str, 57 nargs="?", 58 const=constants.INS_KEY_VNC, 59 dest="autoconnect", 60 required=False, 61 choices=[constants.INS_KEY_VNC, constants.INS_KEY_ADB, 62 constants.INS_KEY_WEBRTC], 63 help="Determines to establish a tunnel forwarding adb/vnc and " 64 "launch VNC/webrtc. Establish a tunnel forwarding adb and vnc " 65 "then launch vnc if --autoconnect vnc is provided. Establish a " 66 "tunnel forwarding adb if --autoconnect adb is provided. " 67 "Establish a tunnel forwarding adb and auto-launch on the browser " 68 "if --autoconnect webrtc is provided. For local goldfish " 69 "instance, create a window.") 70 parser.add_argument( 71 "--no-autoconnect", 72 action="store_false", 73 dest="autoconnect", 74 required=False, 75 help="Will not automatically create ssh tunnels forwarding adb & vnc " 76 "when instance created.") 77 parser.set_defaults(autoconnect=constants.INS_KEY_VNC) 78 parser.add_argument( 79 "--unlock", 80 action="store_true", 81 dest="unlock_screen", 82 required=False, 83 default=False, 84 help="This can unlock screen after invoke vnc client.") 85 parser.add_argument( 86 "--report-internal-ip", 87 action="store_true", 88 dest="report_internal_ip", 89 required=False, 90 help="Report internal ip of the created instance instead of external " 91 "ip. Using the internal ip is used when connecting from another " 92 "GCE instance.") 93 parser.add_argument( 94 "--network", 95 type=str, 96 dest="network", 97 required=False, 98 help="Set the network the GCE instance will utilize.") 99 parser.add_argument( 100 "--skip-pre-run-check", 101 action="store_true", 102 dest="skip_pre_run_check", 103 required=False, 104 help="Skip the pre-run check.") 105 parser.add_argument( 106 "--boot-timeout", 107 dest="boot_timeout_secs", 108 type=int, 109 required=False, 110 help="The maximum time in seconds used to wait for the AVD to boot.") 111 parser.add_argument( 112 "--wait-for-ins-stable", 113 dest="ins_timeout_secs", 114 type=int, 115 required=False, 116 help="The maximum time in seconds used to wait for the instance boot " 117 "up. The default value to wait for instance up time is 300 secs.") 118 parser.add_argument( 119 "--build-target", 120 type=str, 121 dest="build_target", 122 help="Android build target, e.g. aosp_cf_x86_phone-userdebug, " 123 "or short names: phone, tablet, or tablet_mobile.") 124 parser.add_argument( 125 "--branch", 126 type=str, 127 dest="branch", 128 help="Android branch, e.g. mnc-dev or git_mnc-dev") 129 parser.add_argument( 130 "--build-id", 131 type=str, 132 dest="build_id", 133 help="Android build id, e.g. 2145099, P2804227") 134 parser.add_argument( 135 "--bootloader-branch", 136 type=str, 137 dest="bootloader_branch", 138 help="'cuttlefish only' Branch to consume the bootloader from.", 139 required=False) 140 parser.add_argument( 141 "--bootloader-build-id", 142 type=str, 143 dest="bootloader_build_id", 144 help="'cuttlefish only' Bootloader build id, e.g. P2804227", 145 required=False) 146 parser.add_argument( 147 "--bootloader-build-target", 148 type=str, 149 dest="bootloader_build_target", 150 help="'cuttlefish only' Bootloader build target.", 151 required=False) 152 parser.add_argument( 153 "--kernel-build-id", 154 type=str, 155 dest="kernel_build_id", 156 required=False, 157 help="Android kernel build id, e.g. 4586590. This is to test a new" 158 " kernel build with a particular Android build (--build-id). If neither" 159 " kernel-branch nor kernel-build-id are specified, the kernel that's" 160 " bundled with the Android build would be used.") 161 parser.add_argument( 162 "--kernel-branch", 163 type=str, 164 dest="kernel_branch", 165 required=False, 166 help="Android kernel build branch name, e.g." 167 " kernel-common-android-4.14. This is to test a new kernel build with a" 168 " particular Android build (--build-id). If specified without" 169 " specifying kernel-build-id, the last green build in the branch will" 170 " be used. If neither kernel-branch nor kernel-build-id are specified," 171 " the kernel that's bundled with the Android build would be used.") 172 parser.add_argument( 173 "--kernel-build-target", 174 type=str, 175 dest="kernel_build_target", 176 default="kernel", 177 help="Kernel build target, specify if different from 'kernel'") 178 parser.add_argument( 179 "--system-branch", 180 type=str, 181 dest="system_branch", 182 help="'cuttlefish only' Branch to consume the system image (system.img) " 183 "from, will default to what is defined by --branch. " 184 "That feature allows to (automatically) test various combinations " 185 "of vendor.img (CF, e.g.) and system images (GSI, e.g.). ", 186 required=False) 187 parser.add_argument( 188 "--system-build-id", 189 type=str, 190 dest="system_build_id", 191 help="'cuttlefish only' System image build id, e.g. 2145099, P2804227", 192 required=False) 193 parser.add_argument( 194 "--system-build-target", 195 type=str, 196 dest="system_build_target", 197 help="'cuttlefish only' System image build target, specify if different " 198 "from --build-target", 199 required=False) 200 parser.add_argument( 201 "--launch-args", 202 type=str, 203 dest="launch_args", 204 help="'cuttlefish only' Add extra args to launch_cvd command.", 205 required=False) 206 # TODO(146314062): Remove --multi-stage-launch after infra don't use this 207 # args. 208 parser.add_argument( 209 "--multi-stage-launch", 210 dest="multi_stage_launch", 211 action="store_true", 212 required=False, 213 default=True, 214 help="Enable the multi-stage cuttlefish launch.") 215 parser.add_argument( 216 "--no-multi-stage-launch", 217 dest="multi_stage_launch", 218 action="store_false", 219 required=False, 220 default=None, 221 help="Disable the multi-stage cuttlefish launch.") 222 parser.add_argument( 223 "--no-pull-log", 224 dest="no_pull_log", 225 action="store_true", 226 required=False, 227 default=None, 228 help="Disable auto download logs when AVD booting up failed.") 229 # TODO(147335651): Add gpu in user config. 230 # TODO(147335651): Support "--gpu" without giving any value. 231 parser.add_argument( 232 "--gpu", 233 type=str, 234 const=_DEFAULT_GPU, 235 nargs="?", 236 dest="gpu", 237 required=False, 238 default=None, 239 help="GPU accelerator to use if any. e.g. nvidia-tesla-k80. For local " 240 "instances, this arg without assigning any value is to enable " 241 "local gpu support.") 242 # Hide following args for users, it is only used in infra. 243 parser.add_argument( 244 "--local-instance-dir", 245 dest="local_instance_dir", 246 required=False, 247 help=argparse.SUPPRESS) 248 parser.add_argument( 249 "--num-avds-per-instance", 250 type=int, 251 dest="num_avds_per_instance", 252 required=False, 253 default=1, 254 help=argparse.SUPPRESS) 255 parser.add_argument( 256 "--oxygen", 257 action="store_true", 258 dest="oxygen", 259 required=False, 260 help=argparse.SUPPRESS) 261 parser.add_argument( 262 "--zone", 263 type=str, 264 dest="zone", 265 required=False, 266 help=argparse.SUPPRESS) 267 268 # TODO(b/118439885): Old arg formats to support transition, delete when 269 # transistion is done. 270 parser.add_argument( 271 "--serial_log_file", 272 type=str, 273 dest="serial_log_file", 274 required=False, 275 help=argparse.SUPPRESS) 276 parser.add_argument( 277 "--build_id", 278 type=str, 279 dest="build_id", 280 required=False, 281 help=argparse.SUPPRESS) 282 parser.add_argument( 283 "--build_target", 284 type=str, 285 dest="build_target", 286 required=False, 287 help=argparse.SUPPRESS) 288 parser.add_argument( 289 "--system_branch", 290 type=str, 291 dest="system_branch", 292 required=False, 293 help=argparse.SUPPRESS) 294 parser.add_argument( 295 "--system_build_id", 296 type=str, 297 dest="system_build_id", 298 required=False, 299 help=argparse.SUPPRESS) 300 parser.add_argument( 301 "--system_build_target", 302 type=str, 303 dest="system_build_target", 304 required=False, 305 help=argparse.SUPPRESS) 306 parser.add_argument( 307 "--kernel_build_id", 308 type=str, 309 dest="kernel_build_id", 310 required=False, 311 help=argparse.SUPPRESS) 312 parser.add_argument( 313 "--kernel_branch", 314 type=str, 315 dest="kernel_branch", 316 required=False, 317 help=argparse.SUPPRESS) 318 parser.add_argument( 319 "--kernel_build_target", 320 type=str, 321 dest="kernel_build_target", 322 default="kernel", 323 help=argparse.SUPPRESS) 324 parser.add_argument( 325 "--bootloader_branch", 326 type=str, 327 dest="bootloader_branch", 328 help=argparse.SUPPRESS, 329 required=False) 330 parser.add_argument( 331 "--bootloader_build_id", 332 type=str, 333 dest="bootloader_build_id", 334 help=argparse.SUPPRESS, 335 required=False) 336 parser.add_argument( 337 "--bootloader_build_target", 338 type=str, 339 dest="bootloader_build_target", 340 help=argparse.SUPPRESS, 341 required=False) 342 343 344def GetCreateArgParser(subparser): 345 """Return the create arg parser. 346 347 Args: 348 subparser: argparse.ArgumentParser that is attached to main acloud cmd. 349 350 Returns: 351 argparse.ArgumentParser with create options defined. 352 """ 353 create_parser = subparser.add_parser(CMD_CREATE) 354 create_parser.required = False 355 create_parser.set_defaults(which=CMD_CREATE) 356 # Use default=None to distinguish remote instance or local. The instance 357 # type will be remote if the arg is not provided. 358 create_parser.add_argument( 359 "--local-instance", 360 type=_PositiveInteger, 361 const=0, 362 metavar="ID", 363 nargs="?", 364 dest="local_instance", 365 required=False, 366 help="Create a local AVD instance using the resources associated with " 367 "the ID. Choose an unused ID automatically if the value is " 368 "not specified (primarily for infra usage).") 369 create_parser.add_argument( 370 "--adb-port", "-p", 371 type=int, 372 default=None, 373 dest="adb_port", 374 required=False, 375 help="Specify port for adb forwarding.") 376 create_parser.add_argument( 377 "--avd-type", 378 type=str, 379 dest="avd_type", 380 default=constants.TYPE_CF, 381 choices=[constants.TYPE_GCE, constants.TYPE_CF, constants.TYPE_GF, constants.TYPE_CHEEPS, 382 constants.TYPE_FVP], 383 help="Android Virtual Device type (default %s)." % constants.TYPE_CF) 384 create_parser.add_argument( 385 "--config", "--flavor", 386 type=str, 387 dest="flavor", 388 help="The device flavor of the AVD (default %s). e.g. phone, tv, foldable." 389 % constants.FLAVOR_PHONE) 390 create_parser.add_argument( 391 "--local-image", 392 const=constants.FIND_IN_BUILD_ENV, 393 type=str, 394 dest="local_image", 395 nargs="?", 396 required=False, 397 help="Use the locally built image for the AVD. Look for the image " 398 "artifact in $ANDROID_PRODUCT_OUT if no args value is provided." 399 "e.g --local-image or --local-image /path/to/dir or --local-image " 400 "/path/to/file") 401 create_parser.add_argument( 402 "--local-kernel-image", 403 const=constants.FIND_IN_BUILD_ENV, 404 type=str, 405 dest="local_kernel_image", 406 nargs="?", 407 required=False, 408 help="Use the locally built kernel image for the AVD. Look for " 409 "boot.img or boot-*.img if the argument is a directory. Look for the " 410 "image in $ANDROID_PRODUCT_OUT if no argument is provided. e.g., " 411 "--local-kernel-image, --local-kernel-image /path/to/dir, or " 412 "--local-kernel-image /path/to/img") 413 create_parser.add_argument( 414 "--local-system-image", 415 const=constants.FIND_IN_BUILD_ENV, 416 type=str, 417 dest="local_system_image", 418 nargs="?", 419 required=False, 420 help="Use the locally built system images for the AVD. Look for the " 421 "images in $ANDROID_PRODUCT_OUT if no args value is provided. " 422 "e.g., --local-system-image, --local-system-image /path/to/dir, or " 423 "--local-system-image /path/to/img") 424 create_parser.add_argument( 425 "--local-tool", 426 type=str, 427 dest="local_tool", 428 action="append", 429 default=[], 430 required=False, 431 help="Use the tools in the specified directory to create local " 432 "instances. The directory structure follows $ANDROID_HOST_OUT or " 433 "$ANDROID_EMULATOR_PREBUILTS.") 434 create_parser.add_argument( 435 "--image-download-dir", 436 type=str, 437 dest="image_download_dir", 438 required=False, 439 help="Define remote image download directory, e.g. /usr/local/dl.") 440 create_parser.add_argument( 441 "--yes", "-y", 442 action="store_true", 443 dest="no_prompt", 444 required=False, 445 help=("Automatic yes to prompts. Assume 'yes' as answer to all prompts " 446 "and run non-interactively.")) 447 create_parser.add_argument( 448 "--reuse-gce", 449 type=str, 450 const=constants.SELECT_ONE_GCE_INSTANCE, 451 nargs="?", 452 dest="reuse_gce", 453 required=False, 454 help="'cuttlefish only' This can help users use their own instance. " 455 "Reusing specific gce instance if --reuse-gce [instance_name] is " 456 "provided. Select one gce instance to reuse if --reuse-gce is " 457 "provided.") 458 create_parser.add_argument( 459 "--gce-metadata", 460 type=str, 461 dest="gce_metadata", 462 default=None, 463 help="'GCE instance only' Record data into GCE instance metadata with " 464 "key-value pair format. e.g. id:12,name:unknown.") 465 create_parser.add_argument( 466 "--host", 467 type=str, 468 dest="remote_host", 469 default=None, 470 help="'cuttlefish only' Provide host name to clean up the remote host. " 471 "For example: '--host 1.1.1.1'") 472 create_parser.add_argument( 473 "--host-user", 474 type=str, 475 dest="host_user", 476 default=constants.GCE_USER, 477 help="'remote host only' Provide host user for logging in to the host. " 478 "The default value is vsoc-01. For example: '--host 1.1.1.1 --host-user " 479 "vsoc-02'") 480 create_parser.add_argument( 481 "--host-ssh-private-key-path", 482 type=str, 483 dest="host_ssh_private_key_path", 484 default=None, 485 help="'remote host only' Provide host key for login on on this host.") 486 # User should not specify --spec and --hw_property at the same time. 487 hw_spec_group = create_parser.add_mutually_exclusive_group() 488 hw_spec_group.add_argument( 489 "--hw-property", 490 type=str, 491 dest="hw_property", 492 required=False, 493 help="Supported HW properties and example values: %s" % 494 constants.HW_PROPERTIES_CMD_EXAMPLE) 495 hw_spec_group.add_argument( 496 "--spec", 497 type=str, 498 dest="spec", 499 required=False, 500 choices=constants.SPEC_NAMES, 501 help="The name of a pre-configured device spec that we are " 502 "going to use.") 503 # Arguments for goldfish type. 504 # TODO(b/118439885): Verify args that are used in wrong avd_type. 505 # e.g. $acloud create --avd-type cuttlefish --emulator-build-id 506 create_parser.add_argument( 507 "--emulator-build-id", 508 type=int, 509 dest="emulator_build_id", 510 required=False, 511 help="'goldfish only' Emulator build used to run the images. " 512 "e.g. 4669466.") 513 514 # Arguments for cheeps type. 515 create_parser.add_argument( 516 "--stable-cheeps-host-image-name", 517 type=str, 518 dest="stable_cheeps_host_image_name", 519 required=False, 520 default=None, 521 help=("'cheeps only' The Cheeps host image from which instances are " 522 "launched. If specified here, the value set in Acloud config " 523 "file will be overridden.")) 524 create_parser.add_argument( 525 "--stable-cheeps-host-image-project", 526 type=str, 527 dest="stable_cheeps_host_image_project", 528 required=False, 529 default=None, 530 help=("'cheeps only' The project hosting the specified Cheeps host " 531 "image. If specified here, the value set in Acloud config file " 532 "will be overridden.")) 533 create_parser.add_argument( 534 "--user", 535 type=str, 536 dest="username", 537 required=False, 538 default=None, 539 help="'cheeps only' username to log in to Chrome OS as.") 540 create_parser.add_argument( 541 "--password", 542 type=str, 543 dest="password", 544 required=False, 545 default=None, 546 help="'cheeps only' password to log in to Chrome OS with.") 547 create_parser.add_argument( 548 "--betty-image", 549 type=str, 550 dest="cheeps_betty_image", 551 required=False, 552 default=None, 553 help=("'cheeps only' The L1 betty version to use. Only makes sense " 554 "when launching a controller image with " 555 "stable-cheeps-host-image")) 556 557 AddCommonCreateArgs(create_parser) 558 return create_parser 559 560 561def _PositiveInteger(arg): 562 """Convert an argument from a string to a positive integer.""" 563 try: 564 value = int(arg) 565 except ValueError as e: 566 raise argparse.ArgumentTypeError(arg + " is not an integer.") from e 567 if value <= 0: 568 raise argparse.ArgumentTypeError(arg + " is not positive.") 569 return value 570 571 572def _VerifyLocalArgs(args): 573 """Verify args starting with --local. 574 575 Args: 576 args: Namespace object from argparse.parse_args. 577 578 Raises: 579 errors.CheckPathError: Image path doesn't exist. 580 errors.UnsupportedCreateArgs: The specified avd type does not support 581 --local-system-image. 582 errors.UnsupportedLocalInstanceId: Local instance ID is invalid. 583 """ 584 if args.local_image and not os.path.exists(args.local_image): 585 raise errors.CheckPathError( 586 "Specified path doesn't exist: %s" % args.local_image) 587 588 if args.local_instance_dir and not os.path.exists(args.local_instance_dir): 589 raise errors.CheckPathError( 590 "Specified path doesn't exist: %s" % args.local_instance_dir) 591 592 if not (args.local_system_image is None or 593 args.avd_type in (constants.TYPE_CF, constants.TYPE_GF)): 594 raise errors.UnsupportedCreateArgs("%s instance does not support " 595 "--local-system-image" % 596 args.avd_type) 597 598 if (args.local_system_image and 599 not os.path.exists(args.local_system_image)): 600 raise errors.CheckPathError( 601 "Specified path doesn't exist: %s" % args.local_system_image) 602 603 for tool_dir in args.local_tool: 604 if not os.path.exists(tool_dir): 605 raise errors.CheckPathError( 606 "Specified path doesn't exist: %s" % tool_dir) 607 608 if args.autoconnect == constants.INS_KEY_WEBRTC: 609 if args.avd_type != constants.TYPE_CF: 610 raise errors.UnsupportedCreateArgs( 611 "'--autoconnect webrtc' only support cuttlefish.") 612 613 614def _VerifyHostArgs(args): 615 """Verify args starting with --host. 616 617 Args: 618 args: Namespace object from argparse.parse_args. 619 620 Raises: 621 errors.UnsupportedCreateArgs: When a create arg is specified but 622 unsupported for remote host mode. 623 """ 624 if args.remote_host and args.local_instance is not None: 625 raise errors.UnsupportedCreateArgs( 626 "--host is not supported for local instance.") 627 628 if args.remote_host and args.num > 1: 629 raise errors.UnsupportedCreateArgs( 630 "--num is not supported for remote host.") 631 632 if args.host_user != constants.GCE_USER and args.remote_host is None: 633 raise errors.UnsupportedCreateArgs( 634 "--host-user only support for remote host.") 635 636 if args.host_ssh_private_key_path and args.remote_host is None: 637 raise errors.UnsupportedCreateArgs( 638 "--host-ssh-private-key-path only support for remote host.") 639 640 641def VerifyArgs(args): 642 """Verify args. 643 644 Args: 645 args: Namespace object from argparse.parse_args. 646 647 Raises: 648 errors.UnsupportedMultiAdbPort: multi adb port doesn't support. 649 errors.UnsupportedCreateArgs: When a create arg is specified but 650 unsupported for a particular avd type. 651 (e.g. --system-build-id for gf) 652 """ 653 # Verify that user specified flavor name is in support list. 654 # We don't use argparse's builtin validation because we need to be able to 655 # tell when a user doesn't specify a flavor. 656 if args.flavor and args.flavor not in constants.ALL_FLAVORS: 657 logger.debug("Flavor[%s] isn't in default support list: %s", 658 args.flavor, constants.ALL_FLAVORS) 659 660 if args.avd_type != constants.TYPE_CF: 661 if args.system_branch or args.system_build_id or args.system_build_target: 662 raise errors.UnsupportedCreateArgs( 663 "--system-* args are not supported for AVD type: %s" 664 % args.avd_type) 665 666 if args.num > 1 and args.adb_port: 667 raise errors.UnsupportedMultiAdbPort( 668 "--adb-port is not supported for multi-devices.") 669 670 if args.num > 1 and args.local_instance is not None: 671 raise errors.UnsupportedCreateArgs( 672 "--num is not supported for local instance.") 673 674 if args.local_instance is None and args.gpu == _DEFAULT_GPU: 675 raise errors.UnsupportedCreateArgs( 676 "Please assign one gpu model for GCE instance. Reference: " 677 "https://cloud.google.com/compute/docs/gpus") 678 679 if args.adb_port: 680 utils.CheckPortFree(args.adb_port) 681 682 hw_properties = create_common.ParseKeyValuePairArgs(args.hw_property) 683 for key in hw_properties: 684 if key not in constants.HW_PROPERTIES: 685 raise errors.InvalidHWPropertyError( 686 "[%s] is an invalid hw property, supported values are:%s. " 687 % (key, constants.HW_PROPERTIES)) 688 689 cheeps_only_flags = [args.stable_cheeps_host_image_name, 690 args.stable_cheeps_host_image_project, 691 args.username, 692 args.password, 693 args.cheeps_betty_image] 694 if args.avd_type != constants.TYPE_CHEEPS and any(cheeps_only_flags): 695 raise errors.UnsupportedCreateArgs( 696 "--stable-cheeps-*, --betty-image, --username and --password are " 697 "only valid with avd_type == %s" % constants.TYPE_CHEEPS) 698 if (args.username or args.password) and not (args.username and args.password): 699 raise ValueError("--username and --password must both be set") 700 if not args.autoconnect and args.unlock_screen: 701 raise ValueError("--no-autoconnect and --unlock couldn't be " 702 "passed in together.") 703 704 _VerifyLocalArgs(args) 705 _VerifyHostArgs(args) 706