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