xref: /aosp_15_r20/tools/acloud/create/create_args.py (revision 800a58d989c669b8eb8a71d8df53b1ba3d411444)
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
22import posixpath as remote_path
23
24from acloud import errors
25from acloud.create import create_common
26from acloud.internal import constants
27from acloud.internal.lib import utils
28
29logger = logging.getLogger(__name__)
30_DEFAULT_GPU = "default"
31CMD_CREATE = "create"
32
33
34# TODO: Add this into main create args once create_cf/gf is deprecated.
35# pylint: disable=too-many-statements
36def AddCommonCreateArgs(parser):
37    """Adds arguments common to create parsers.
38
39    Args:
40        parser: ArgumentParser object, used to parse flags.
41    """
42    parser.add_argument(
43        "--num",
44        type=int,
45        dest="num",
46        required=False,
47        default=1,
48        help="Number of instances to create.")
49    parser.add_argument(
50        "--serial-log-file",
51        type=str,
52        dest="serial_log_file",
53        required=False,
54        help="Path to a *tar.gz file where serial logs will be saved "
55             "when a device fails on boot.")
56    parser.add_argument(
57        "--autoconnect",
58        type=str,
59        nargs="?",
60        const=constants.INS_KEY_WEBRTC,
61        dest="autoconnect",
62        required=False,
63        choices=[constants.INS_KEY_VNC, constants.INS_KEY_ADB,
64                 constants.INS_KEY_WEBRTC],
65        help="Determines to establish a tunnel forwarding adb/vnc and "
66             "launch VNC/webrtc. Establish a tunnel forwarding adb and vnc "
67             "then launch vnc if --autoconnect vnc is provided. Establish a "
68             "tunnel forwarding adb if --autoconnect adb is provided. "
69             "Establish a tunnel forwarding adb and auto-launch on the browser "
70             "if --autoconnect webrtc is provided. For local goldfish "
71             "instance, create a window.")
72    parser.add_argument(
73        "--no-autoconnect",
74        action="store_false",
75        dest="autoconnect",
76        required=False,
77        help="Will not automatically create ssh tunnels forwarding adb & vnc "
78             "when instance created.")
79    parser.set_defaults(autoconnect=constants.INS_KEY_WEBRTC)
80    parser.add_argument(
81        "--unlock",
82        action="store_true",
83        dest="unlock_screen",
84        required=False,
85        default=False,
86        help="This can unlock screen after invoke vnc client.")
87    parser.add_argument(
88        "--report-internal-ip",
89        action="store_true",
90        dest="report_internal_ip",
91        required=False,
92        help="Report internal ip of the created instance instead of external "
93             "ip. Using the internal ip is used when connecting from another "
94             "GCE instance.")
95    parser.add_argument(
96        "--disable-external-ip",
97        action="store_true",
98        dest="disable_external_ip",
99        required=False,
100        help="Disable the external ip of the created instance.")
101    parser.add_argument(
102        "--extra-files",
103        nargs='+',
104        type=str,
105        dest="extra_files",
106        required=False,
107        help="Upload the extra files into GCE instance. e.g. "
108             "/path/to/file_in_local,/path/to/file_in_gce")
109    parser.add_argument(
110        "--network",
111        type=str,
112        dest="network",
113        required=False,
114        help="Set the network the GCE instance will utilize.")
115    parser.add_argument(
116        "--skip-pre-run-check",
117        action="store_true",
118        dest="skip_pre_run_check",
119        required=False,
120        help="Skip the pre-run check.")
121    parser.add_argument(
122        "--force-sync",
123        action="store_true",
124        dest="force_sync",
125        required=False,
126        help="Force to sync image files from Android Build servers even if "
127             "they are already existed for local instance mode.")
128    parser.add_argument(
129        "--boot-timeout",
130        dest="boot_timeout_secs",
131        type=int,
132        required=False,
133        help="The maximum time in seconds used to wait for the AVD to download "
134             "artifacts and boot.")
135    parser.add_argument(
136        "--wait-for-ins-stable",
137        dest="ins_timeout_secs",
138        type=int,
139        required=False,
140        help="The maximum time in seconds used to wait for the instance boot "
141             "up. The default value to wait for instance up time is 300 secs.")
142    parser.add_argument(
143        "--build-target",
144        type=str,
145        dest="build_target",
146        help="Android build target, e.g. aosp_cf_x86_64_phone-userdebug, "
147             "or short names: phone, tablet, or tablet_mobile.")
148    parser.add_argument(
149        "--branch",
150        type=str,
151        dest="branch",
152        help="Android branch, e.g. mnc-dev or git_mnc-dev")
153    parser.add_argument(
154        "--build-id",
155        type=str,
156        dest="build_id",
157        help="Android build id, e.g. 2145099, P2804227")
158    parser.add_argument(
159        "--bootloader-branch",
160        type=str,
161        dest="bootloader_branch",
162        help="'cuttlefish only' Branch to consume the bootloader from.",
163        required=False)
164    parser.add_argument(
165        "--bootloader-build-id",
166        type=str,
167        dest="bootloader_build_id",
168        help="'cuttlefish only' Bootloader build id, e.g. P2804227",
169        required=False)
170    parser.add_argument(
171        "--bootloader-build-target",
172        type=str,
173        dest="bootloader_build_target",
174        help="'cuttlefish only' Bootloader build target.",
175        required=False)
176    parser.add_argument(
177        "--android-efi-loader-build-id",
178        type=str,
179        dest="android_efi_loader_build_id",
180        help="'cuttlefish only' Android EFI loader build id, e.g. P2804227",
181        required=False)
182    parser.add_argument(
183        "--android-efi-loader-artifact",
184        type=str,
185        dest="android_efi_loader_artifact",
186        help="'cuttlefish only' Android EFI loader artifact name, e.g. gbl_aarch64.efi",
187        required=False)
188    parser.add_argument(
189        "--kernel-build-id",
190        type=str,
191        dest="kernel_build_id",
192        required=False,
193        help="Android kernel build id, e.g. 4586590. This is to test a new"
194        " kernel build with a particular Android build (--build-id). If neither"
195        " kernel-branch nor kernel-build-id are specified, the kernel that's"
196        " bundled with the Android build would be used.")
197    parser.add_argument(
198        "--kernel-branch",
199        type=str,
200        dest="kernel_branch",
201        required=False,
202        help="Android kernel build branch name, e.g."
203        " kernel-common-android-4.14. This is to test a new kernel build with a"
204        " particular Android build (--build-id). If specified without"
205        " specifying kernel-build-id, the last green build in the branch will"
206        " be used. If neither kernel-branch nor kernel-build-id are specified,"
207        " the kernel that's bundled with the Android build would be used.")
208    parser.add_argument(
209        "--kernel-build-target",
210        type=str,
211        dest="kernel_build_target",
212        default="kernel",
213        help="Kernel build target, specify if different from 'kernel'")
214    parser.add_argument(
215        "--boot-build-id",
216        type=str,
217        dest="boot_build_id",
218        required=False,
219        help="Boot image build ID, e.g., 8747889, 8748012.")
220    parser.add_argument(
221        "--boot-branch",
222        type=str,
223        dest="boot_branch",
224        required=False,
225        help="Boot image branch, e.g., aosp-gki13-boot-release, aosp-master.")
226    parser.add_argument(
227        "--boot-build-target",
228        type=str,
229        dest="boot_build_target",
230        required=False,
231        help="Boot image build target, "
232        "e.g., gki_x86_64-userdebug, aosp_cf_x86_64_phone-userdebug.")
233    parser.add_argument(
234        "--boot-artifact",
235        type=str,
236        dest="boot_artifact",
237        required=False,
238        help="The name of the boot image to be retrieved from Android build, "
239        "e.g., boot-5.10.img, boot.img.")
240    parser.add_argument(
241        "--ota-branch",
242        type=str,
243        dest="ota_branch",
244        required=False,
245        help="'cuttlefish only' OTA tools branch name. e.g. aosp-master")
246    parser.add_argument(
247        "--ota-build-id",
248        type=str,
249        dest="ota_build_id",
250        required=False,
251        help="'cuttlefish only' OTA tools build id, e.g. 2145099, P2804227")
252    parser.add_argument(
253        "--ota-build-target",
254        type=str,
255        dest="ota_build_target",
256        required=False,
257        help="'cuttlefish only' OTA tools build target, e.g. "
258        "cf_x86_64_phone-userdebug.")
259    parser.add_argument(
260        "--host-package-branch", "--host_package_branch",
261        type=str,
262        dest="host_package_branch",
263        required=False,
264        help="'cuttlefish and trusty only' Host package branch name. e.g. "
265        "aosp-main")
266    parser.add_argument(
267        "--host-package-build-id", "--host_package_build_id",
268        type=str,
269        dest="host_package_build_id",
270        required=False,
271        help="'cuttlefish and trusty only' Host package build id, e.g. "
272        "2145099, P2804227")
273    parser.add_argument(
274        "--host-package-build-target", "--host_package_build_target",
275        type=str,
276        dest="host_package_build_target",
277        required=False,
278        help="'cuttlefish and trusty only' Host package build target, e.g. "
279        "cf_x86_64_phone-userdebug.")
280    parser.add_argument(
281        "--system-branch",
282        type=str,
283        dest="system_branch",
284        help="'cuttlefish only' Branch to consume the system image (system.img) "
285        "from, will default to what is defined by --branch. "
286        "That feature allows to (automatically) test various combinations "
287        "of vendor.img (CF, e.g.) and system images (GSI, e.g.). ",
288        required=False)
289    parser.add_argument(
290        "--system-build-id",
291        type=str,
292        dest="system_build_id",
293        help="'cuttlefish only' System image build id, e.g. 2145099, P2804227",
294        required=False)
295    parser.add_argument(
296        "--system-build-target",
297        type=str,
298        dest="system_build_target",
299        help="'cuttlefish only' System image build target, specify if different "
300        "from --build-target",
301        required=False)
302    parser.add_argument(
303        "--launch-args",
304        type=str,
305        dest="launch_args",
306        help="'cuttlefish only' Add extra args to launch_cvd command.",
307        required=False)
308    parser.add_argument(
309        "--pet-name",
310        "--webrtc_device_id",
311        type=str,
312        dest="webrtc_device_id",
313        help="'cuttlefish only' Give the pet name of the instance.",
314        required=False)
315    parser.add_argument(
316        "--gce-metadata",
317        type=str,
318        dest="gce_metadata",
319        default=None,
320        help="'GCE instance only' Record data into GCE instance metadata with "
321        "key-value pair format. e.g. id:12,name:unknown.")
322    parser.add_argument(
323        "--fetch_cvd-build-id",
324        type=str,
325        dest="fetch_cvd_build_id",
326        required=False,
327        help="'cuttlefish only' Build id of fetch_cvd, e.g. 2145099, P2804227")
328    # TODO(146314062): Remove --multi-stage-launch after infra don't use this
329    # args.
330    parser.add_argument(
331        "--multi-stage-launch",
332        dest="multi_stage_launch",
333        action="store_true",
334        required=False,
335        default=True,
336        help="Enable the multi-stage cuttlefish launch.")
337    parser.add_argument(
338        "--no-multi-stage-launch",
339        dest="multi_stage_launch",
340        action="store_false",
341        required=False,
342        default=None,
343        help="Disable the multi-stage cuttlefish launch.")
344    parser.add_argument(
345        "--no-pull-log",
346        dest="no_pull_log",
347        action="store_true",
348        required=False,
349        default=None,
350        help="Disable auto download logs when AVD booting up failed.")
351    parser.add_argument(
352        "--no-mkcert",
353        dest="mkcert",
354        action="store_false",
355        required=False,
356        default=True,
357        help="Disable mkcert setup process on the host.")
358    # TODO(147335651): Add gpu in user config.
359    # TODO(147335651): Support "--gpu" without giving any value.
360    parser.add_argument(
361        "--gpu",
362        type=str,
363        const=_DEFAULT_GPU,
364        nargs="?",
365        dest="gpu",
366        required=False,
367        default=None,
368        help="GPU accelerator to use if any. e.g. nvidia-tesla-k80. For local "
369             "instances, this arg without assigning any value is to enable "
370             "local gpu support.")
371    parser.add_argument(
372        "--num-avds-per-instance",
373        "--num-instances",
374        "--num_instances",
375        type=int,
376        dest="num_avds_per_instance",
377        required=False,
378        default=1,
379        help="'cuttlefish only' Create multiple cuttlefish AVDs in one local "
380             "instance.")
381    parser.add_argument(
382        "--connect-hostname",
383        action="store_true",
384        dest="connect_hostname",
385        required=False,
386        default=False,
387        help="Ssh connects to the GCE instance with hostname.")
388    parser.add_argument(
389        "--gce-only",
390        action="store_true",
391        dest="gce_only",
392        required=False,
393        default=False,
394        help="Only create the GCE instance. It won't create virtual devices.")
395    # Hide following args for users, it is only used in infra.
396    parser.add_argument(
397        "--local-instance-dir",
398        dest="local_instance_dir",
399        required=False,
400        help=argparse.SUPPRESS)
401    parser.add_argument(
402        "--remote-image-dir",
403        dest="remote_image_dir",
404        required=False,
405        # 'cuttlefish remote host only' Upload images and cvd host package to
406        # the remote directory instead of the instance's own directory. If the
407        # directory has been initialized, acloud ignores the image arguments
408        # given by command line and reuses the images in the directory.
409        help=argparse.SUPPRESS)
410    parser.add_argument(
411        "--oxygen",
412        action="store_true",
413        dest="oxygen",
414        required=False,
415        help=argparse.SUPPRESS)
416    parser.add_argument(
417        "--zone",
418        type=str,
419        dest="zone",
420        required=False,
421        help=argparse.SUPPRESS)
422
423    # TODO(b/118439885): Old arg formats to support transition, delete when
424    # transistion is done.
425    parser.add_argument(
426        "--serial_log_file",
427        type=str,
428        dest="serial_log_file",
429        required=False,
430        help=argparse.SUPPRESS)
431    parser.add_argument(
432        "--build_id",
433        type=str,
434        dest="build_id",
435        required=False,
436        help=argparse.SUPPRESS)
437    parser.add_argument(
438        "--build_target",
439        type=str,
440        dest="build_target",
441        required=False,
442        help=argparse.SUPPRESS)
443    parser.add_argument(
444        "--system_branch",
445        type=str,
446        dest="system_branch",
447        required=False,
448        help=argparse.SUPPRESS)
449    parser.add_argument(
450        "--system_build_id",
451        type=str,
452        dest="system_build_id",
453        required=False,
454        help=argparse.SUPPRESS)
455    parser.add_argument(
456        "--system_build_target",
457        type=str,
458        dest="system_build_target",
459        required=False,
460        help=argparse.SUPPRESS)
461    parser.add_argument(
462        "--kernel_build_id",
463        type=str,
464        dest="kernel_build_id",
465        required=False,
466        help=argparse.SUPPRESS)
467    parser.add_argument(
468        "--kernel_branch",
469        type=str,
470        dest="kernel_branch",
471        required=False,
472        help=argparse.SUPPRESS)
473    parser.add_argument(
474        "--kernel_build_target",
475        type=str,
476        dest="kernel_build_target",
477        default="kernel",
478        help=argparse.SUPPRESS)
479    parser.add_argument(
480        "--bootloader_branch",
481        type=str,
482        dest="bootloader_branch",
483        help=argparse.SUPPRESS,
484        required=False)
485    parser.add_argument(
486        "--bootloader_build_id",
487        type=str,
488        dest="bootloader_build_id",
489        help=argparse.SUPPRESS,
490        required=False)
491    parser.add_argument(
492        "--bootloader_build_target",
493        type=str,
494        dest="bootloader_build_target",
495        help=argparse.SUPPRESS,
496        required=False)
497    parser.add_argument(
498        "--fetch_cvd_build_id",
499        type=str,
500        dest="fetch_cvd_build_id",
501        help=argparse.SUPPRESS,
502        required=False)
503    parser.add_argument(
504        "--remote-fetch",
505        action="store_true",
506        dest="remote_fetch",
507        required=False,
508        default=None,
509        help="'cuttlefish only' Fetch artifacts in remote host.")
510    parser.add_argument(
511        "--fetch-cvd-wrapper",
512        dest="fetch_cvd_wrapper",
513        type=str,
514        required=False,
515        help="'cuttlefish only' Fetch artifacts in remote host by a"
516        " provided static executable fetch cvd wrapper file. "
517        " (Still in experiment, this flag only works on lab hosts"
518        " with special setup.)")
519
520
521def GetCreateArgParser(subparser):
522    """Return the create arg parser.
523
524    Args:
525       subparser: argparse.ArgumentParser that is attached to main acloud cmd.
526
527    Returns:
528        argparse.ArgumentParser with create options defined.
529    """
530    create_parser = subparser.add_parser(CMD_CREATE)
531    create_parser.required = False
532    create_parser.set_defaults(which=CMD_CREATE)
533    # Use default=None to distinguish remote instance or local. The instance
534    # type will be remote if the arg is not provided.
535    create_parser.add_argument(
536        "--local-instance",
537        type=_PositiveInteger,
538        const=0,
539        metavar="ID",
540        nargs="?",
541        dest="local_instance",
542        required=False,
543        help="Create a local AVD instance using the resources associated with "
544             "the ID. Choose an unused ID automatically if the value is "
545             "not specified (primarily for infra usage).")
546    create_parser.add_argument(
547        "--adb-port", "-p",
548        type=int,
549        default=None,
550        dest="adb_port",
551        required=False,
552        help="Specify port for adb forwarding.")
553    create_parser.add_argument(
554        "--base-instance-num",
555        type=int,
556        default=None,
557        dest="base_instance_num",
558        required=False,
559        help="'cuttlefish only' The instance number of the created device.")
560    create_parser.add_argument(
561        "--avd-type",
562        type=str,
563        dest="avd_type",
564        default=constants.TYPE_CF,
565        choices=[constants.TYPE_GCE, constants.TYPE_CF, constants.TYPE_GF, constants.TYPE_CHEEPS,
566                 constants.TYPE_FVP, constants.TYPE_TRUSTY],
567        help="Android Virtual Device type (default %s)." % constants.TYPE_CF)
568    create_parser.add_argument(
569        "--config", "--flavor",
570        type=str,
571        dest="flavor",
572        help="The device flavor of the AVD (default %s). e.g. phone, tv, foldable."
573        % constants.FLAVOR_PHONE)
574    create_parser.add_argument(
575        "--local-image",
576        const=constants.FIND_IN_BUILD_ENV,
577        type=str,
578        dest="local_image",
579        nargs="?",
580        required=False,
581        help="Use the locally built image for the AVD. Look for the image "
582        "artifact in $ANDROID_PRODUCT_OUT if no args value is provided."
583        "e.g --local-image or --local-image /path/to/dir or --local-image "
584        "/path/to/file")
585    create_parser.add_argument(
586        "--local-kernel-image", "--local-boot-image",
587        const=constants.FIND_IN_BUILD_ENV,
588        type=str,
589        dest="local_kernel_image",
590        nargs="?",
591        required=False,
592        help="Use the locally built kernel and ramdisk for the AVD. Look "
593        "for boot.img, vendor_boot.img, kernel, initramfs.img, etc. if the "
594        "argument is a directory. Look for the images in $ANDROID_PRODUCT_OUT "
595        "if no argument is provided. e.g., --local-kernel-image, "
596        "--local-kernel-image /path/to/dir, or --local-kernel-image "
597        "/path/to/boot.img")
598    create_parser.add_argument(
599        "--local-system-image",
600        const=constants.FIND_IN_BUILD_ENV,
601        type=str,
602        dest="local_system_image",
603        nargs="?",
604        required=False,
605        help="Use the locally built system images for the AVD. Look for the "
606        "images in $ANDROID_PRODUCT_OUT if no args value is provided. "
607        "e.g., --local-system-image, --local-system-image /path/to/dir, or "
608        "--local-system-image /path/to/img")
609    create_parser.add_argument(
610        "--local-system_dlkm-image",
611        const=constants.FIND_IN_BUILD_ENV,
612        type=str,
613        dest="local_system_dlkm_image",
614        nargs="?",
615        required=False,
616        help="`remote host only` Use the locally built system_dlkm image for "
617        "the AVD. Look for the image in $ANDROID_PRODUCT_OUT if no args value "
618        "is provided.")
619    create_parser.add_argument(
620        "--local-vendor-image",
621        const=constants.FIND_IN_BUILD_ENV,
622        type=str,
623        dest="local_vendor_image",
624        nargs="?",
625        required=False,
626        help="'cuttlefish only' Use the locally built vendor images for the "
627        "AVD. Look for vendor.img, vendor_dlkm.img, odm.img, and odm_dlkm.img "
628        "if the argument is a directory. Look for the images in "
629        "$ANDROID_PRODUCT_OUT if no argument is provided. e.g., "
630        "--local-vendor-image, or --local-vendor-image /path/to/dir")
631    create_parser.add_argument(
632        "--local-vendor_boot-image", "--local-vendor-boot-image",
633        const=constants.FIND_IN_BUILD_ENV,
634        type=str,
635        dest="local_vendor_boot_image",
636        nargs="?",
637        required=False,
638        help="'cuttlefish only' Use the locally built vendor boot image for "
639        "the AVD. Look for the vendor_boot.img in $ANDROID_PRODUCT_OUT "
640        "if no argument is provided. e.g., --local-vendor-boot-image, or "
641        "--local-vendor-boot-image /path/to/dir, or "
642        "--local-vendor-boot-image /path/to/img")
643    create_parser.add_argument(
644        "--local-tool",
645        type=str,
646        dest="local_tool",
647        action="append",
648        default=[],
649        required=False,
650        help="Use the tools in the specified directory to create local "
651        "instances. The directory structure follows $ANDROID_SOONG_HOST_OUT "
652        "or $ANDROID_EMULATOR_PREBUILTS.")
653    create_parser.add_argument(
654        "--cvd-host-package",
655        type=str,
656        dest="cvd_host_package",
657        required=False,
658        help="Use the specified path of the cvd host package to create "
659        "instances. e.g. /path/cvd-host_package_v1.tar.gz")
660    create_parser.add_argument(
661        "--image-download-dir",
662        type=str,
663        dest="image_download_dir",
664        required=False,
665        help="Define remote image download directory, e.g. /usr/local/dl.")
666    create_parser.add_argument(
667        "--yes", "-y",
668        action="store_true",
669        dest="no_prompt",
670        required=False,
671        help=("Automatic yes to prompts. Assume 'yes' as answer to all prompts "
672              "and run non-interactively."))
673    create_parser.add_argument(
674        "--reuse-gce",
675        type=str,
676        const=constants.SELECT_ONE_GCE_INSTANCE,
677        nargs="?",
678        dest="reuse_gce",
679        required=False,
680        help="'cuttlefish only' This can help users use their own instance. "
681        "Reusing specific gce instance if --reuse-gce [instance_name] is "
682        "provided. Select one gce instance to reuse if --reuse-gce is "
683        "provided.")
684    create_parser.add_argument(
685        "--openwrt",
686        action="store_true",
687        dest="openwrt",
688        required=False,
689        help="'cuttlefish only' Create OpenWrt device when launching cuttlefish "
690        "device.")
691    create_parser.add_argument(
692        "--use-launch_cvd",
693        action="store_true",
694        dest="use_launch_cvd",
695        required=False,
696        help="'cuttlefish only' Use launch_cvd to create cuttlefish devices.")
697    create_parser.add_argument(
698        "--host",
699        type=str,
700        dest="remote_host",
701        default=None,
702        help="'cuttlefish only' Provide host name to clean up the remote host. "
703        "For example: '--host 1.1.1.1'")
704    create_parser.add_argument(
705        "--host-user",
706        type=str,
707        dest="host_user",
708        default=constants.GCE_USER,
709        help="'remote host only' Provide host user for logging in to the host. "
710        "The default value is vsoc-01. For example: '--host 1.1.1.1 --host-user "
711        "vsoc-02'")
712    create_parser.add_argument(
713        "--host-ssh-private-key-path",
714        type=str,
715        dest="host_ssh_private_key_path",
716        default=None,
717        help="'remote host only' Provide host key for login on on this host.")
718    # User should not specify --spec and --hw_property at the same time.
719    hw_spec_group = create_parser.add_mutually_exclusive_group()
720    hw_spec_group.add_argument(
721        "--hw-property",
722        type=str,
723        dest="hw_property",
724        required=False,
725        help="Supported HW properties and example values: %s" %
726        constants.HW_PROPERTIES_CMD_EXAMPLE)
727    hw_spec_group.add_argument(
728        "--spec",
729        type=str,
730        dest="spec",
731        required=False,
732        choices=constants.SPEC_NAMES,
733        help="The name of a pre-configured device spec that we are "
734        "going to use.")
735    create_parser.add_argument(
736        "--disk-type",
737        type=str,
738        dest="disk_type",
739        required=False,
740        help="This is used to customize the GCE instance disk type, the "
741        "default disk type is from the stable host image. Use pd-ssd or "
742        "pd-standard to specify instance disk type.")
743    create_parser.add_argument(
744        "--stable-host-image-name",
745        type=str,
746        dest="stable_host_image_name",
747        required=False,
748        default=None,
749        help=("'cuttlefish only' The Cuttlefish host image from which instances "
750              "are launched. If specified here, the value set in Acloud config "
751              "file will be overridden."))
752
753    # Arguments for goldfish type.
754    create_parser.add_argument(
755        "--emulator-build-id",
756        type=str,
757        dest="emulator_build_id",
758        required=False,
759        help="'goldfish only' Emulator build ID used to run the images. "
760        "e.g. 4669466.")
761    create_parser.add_argument(
762        "--emulator-build-target",
763        dest="emulator_build_target",
764        required=False,
765        help="'goldfish remote host only' Emulator build target used to run "
766        "the images. e.g. emulator-linux_x64_nolocationui.")
767    create_parser.add_argument(
768        "--emulator-zip",
769        dest="emulator_zip",
770        required=False,
771        help="'goldfish remote host only' Emulator zip used to run the "
772        "images. e.g., /path/sdk-repo-linux-emulator-1234567.zip.")
773
774    # Arguments for cheeps type.
775    create_parser.add_argument(
776        "--stable-cheeps-host-image-name",
777        type=str,
778        dest="stable_cheeps_host_image_name",
779        required=False,
780        default=None,
781        help=("'cheeps only' The Cheeps host image from which instances are "
782              "launched. If specified here, the value set in Acloud config "
783              "file will be overridden."))
784    create_parser.add_argument(
785        "--stable-cheeps-host-image-project",
786        type=str,
787        dest="stable_cheeps_host_image_project",
788        required=False,
789        default=None,
790        help=("'cheeps only' The project hosting the specified Cheeps host "
791              "image. If specified here, the value set in Acloud config file "
792              "will be overridden."))
793    create_parser.add_argument(
794        "--user",
795        type=str,
796        dest="username",
797        required=False,
798        default=None,
799        help="'cheeps only' username to log in to Chrome OS as.")
800    create_parser.add_argument(
801        "--password",
802        type=str,
803        dest="password",
804        required=False,
805        default=None,
806        help="'cheeps only' password to log in to Chrome OS with.")
807    create_parser.add_argument(
808        "--betty-image",
809        type=str,
810        dest="cheeps_betty_image",
811        required=False,
812        default=None,
813        help=("'cheeps only' The L1 betty version to use. Only makes sense "
814              "when launching a controller image with "
815              "stable-cheeps-host-image"))
816    create_parser.add_argument(
817        "--cheeps-feature",
818        type=str,
819        dest="cheeps_features",
820        required=False,
821        action="append",
822        default=[],
823        help=("'cheeps only' Cheeps feature to enable. Can be repeated."))
824
825    # Arguments for trusty type
826    create_parser.add_argument(
827        "--trusty-host-package",
828        type=str,
829        dest="trusty_host_package",
830        required=False,
831        help="Use the specified path of the trusty host package to create "
832        "instances. e.g. /path/trusty-host_package.tar.gz")
833    create_parser.add_argument(
834        "--local-trusty-image",
835        type=str,
836        dest="local_trusty_image",
837        required=False,
838        help="'trusty only' Use the specified path for the locally built "
839        "trusty emulator images package, built with "
840        "PACKAGE_TRUSTY_IMAGE_TARBALL=true in the Trusty build. E.g., "
841        "/path/trusty_image_package.tar.gz")
842    create_parser.add_argument(
843        "--trusty-build-id",
844        type=str,
845        dest="trusty_build_id",
846        required=False,
847        help="Trusty image package build ID, e.g., 8747889, 8748012.")
848    create_parser.add_argument(
849        "--trusty-branch",
850        type=str,
851        dest="trusty_branch",
852        required=False,
853        help="Trusty image package branch, e.g., aosp-trusty-master.")
854    create_parser.add_argument(
855        "--trusty-build-target",
856        type=str,
857        dest="trusty_build_target",
858        required=False,
859        help="Trusty image package build target, "
860        "e.g., qemu_generic_arm64_test_debug.")
861
862    AddCommonCreateArgs(create_parser)
863    return create_parser
864
865
866def _PositiveInteger(arg):
867    """Convert an argument from a string to a positive integer."""
868    try:
869        value = int(arg)
870    except ValueError as e:
871        raise argparse.ArgumentTypeError(arg + " is not an integer.") from e
872    if value <= 0:
873        raise argparse.ArgumentTypeError(arg + " is not positive.")
874    return value
875
876
877def _VerifyLocalArgs(args):
878    """Verify args starting with --local.
879
880    Args:
881        args: Namespace object from argparse.parse_args.
882
883    Raises:
884        errors.CheckPathError: Image path doesn't exist.
885        errors.UnsupportedCreateArgs: The specified avd type does not support
886                                      a provided argument.
887        errors.UnsupportedLocalInstanceId: Local instance ID is invalid.
888    """
889    if args.local_image and not os.path.exists(args.local_image):
890        raise errors.CheckPathError(
891            "Specified path doesn't exist: %s" % args.local_image)
892
893    if args.local_instance_dir and not os.path.exists(args.local_instance_dir):
894        raise errors.CheckPathError(
895            "Specified path doesn't exist: %s" % args.local_instance_dir)
896
897    if not (args.local_system_image is None or
898            args.avd_type in (constants.TYPE_CF, constants.TYPE_GF)):
899        raise errors.UnsupportedCreateArgs("%s instance does not support "
900                                           "--local-system-image" %
901                                           args.avd_type)
902    # TODO(b/179340595): To support local image remote instance with kernel build.
903    if args.local_instance is None and args.local_image is not None and (
904            args.kernel_branch or args.kernel_build_id):
905        raise errors.UnsupportedCreateArgs(
906            "Acloud didn't support local image with specific kernel. "
907            "Please download the specific kernel and put it into "
908            "your local image folder: '%s'." % (
909            args.local_image if args.local_image else
910            utils.GetBuildEnvironmentVariable(constants.ENV_ANDROID_PRODUCT_OUT)))
911
912    if (args.local_system_image and
913            not os.path.exists(args.local_system_image)):
914        raise errors.CheckPathError(
915            "Specified path doesn't exist: %s" % args.local_system_image)
916
917    for tool_dir in args.local_tool:
918        if not os.path.exists(tool_dir):
919            raise errors.CheckPathError(
920                "Specified path doesn't exist: %s" % tool_dir)
921
922
923def _VerifyHostArgs(args):
924    """Verify args starting with --host.
925
926    Args:
927        args: Namespace object from argparse.parse_args.
928
929    Raises:
930        errors.UnsupportedCreateArgs: When a create arg is specified but
931                                      unsupported for remote host mode.
932    """
933    if args.remote_host and args.local_instance is not None:
934        raise errors.UnsupportedCreateArgs(
935            "--host is not supported for local instance.")
936
937    if args.remote_host and args.num > 1:
938        raise errors.UnsupportedCreateArgs(
939            "--num is not supported for remote host.")
940
941    if args.host_user != constants.GCE_USER and args.remote_host is None:
942        raise errors.UnsupportedCreateArgs(
943            "--host-user is only supported for remote host.")
944
945    if args.host_ssh_private_key_path and args.remote_host is None:
946        raise errors.UnsupportedCreateArgs(
947            "--host-ssh-private-key-path is only supported for remote host.")
948
949    if args.remote_image_dir:
950        if args.remote_host is None:
951            raise errors.UnsupportedCreateArgs(
952                "--remote-image-dir is only supported for remote host.")
953        if remote_path.basename(
954                remote_path.normpath(args.remote_image_dir)) in ("..", "."):
955            raise errors.UnsupportedCreateArgs(
956                "--remote-image-dir must not include the working directory.")
957
958
959def _VerifyGoldfishArgs(args):
960    """Verify goldfish args.
961
962    Args:
963        args: Namespace object from argparse.parse_args.
964
965    Raises:
966        errors.UnsupportedCreateArgs: When a create arg is specified but
967                                      unsupported for goldfish.
968    """
969    goldfish_only_flags = [
970        args.emulator_build_id,
971        args.emulator_build_target,
972        args.emulator_zip
973    ]
974    if args.avd_type != constants.TYPE_GF and any(goldfish_only_flags):
975        raise errors.UnsupportedCreateArgs(
976            f"--emulator-* is only valid with avd_type == {constants.TYPE_GF}")
977
978    # Exclude kernel_build_target because the default value isn't empty.
979    remote_kernel_flags = [
980        args.kernel_build_id,
981        args.kernel_branch,
982    ]
983    if args.avd_type == constants.TYPE_GF and any(remote_kernel_flags):
984        raise errors.UnsupportedCreateArgs(
985            "--kernel-* is not supported for goldfish.")
986
987    remote_boot_flags = [
988        args.boot_build_id,
989        args.boot_build_target,
990        args.boot_branch,
991        args.boot_artifact,
992    ]
993    if (args.avd_type == constants.TYPE_GF and any(remote_boot_flags) and
994            not all(remote_boot_flags)):
995        raise errors.UnsupportedCreateArgs(
996            "Either none or all of --boot-branch, --boot-build-target, "
997            "--boot-build-id, and --boot-artifact must be specified for "
998            "goldfish.")
999
1000    remote_system_flags = [
1001        args.system_build_target,
1002        args.system_build_id,
1003        args.system_branch,
1004    ]
1005    if (args.avd_type == constants.TYPE_GF and any(remote_system_flags) and
1006            not all(remote_system_flags)):
1007        raise errors.UnsupportedCreateArgs(
1008            "Either none or all of --system-branch, --system-build-target, "
1009            "and --system-build-id must be specified for goldfish.")
1010
1011    remote_host_only_flags = remote_boot_flags + remote_system_flags
1012    if args.avd_type == constants.TYPE_GF and args.remote_host is None and any(
1013            remote_host_only_flags):
1014        raise errors.UnsupportedCreateArgs(
1015            "--boot-* and --system-* for goldfish are only supported for "
1016            "remote host.")
1017
1018
1019def _VerifyTrustyArgs(args):
1020    """Verify trusty args.
1021
1022    Args:
1023        args: Namespace object from argparse.parse_args.
1024
1025    Raises:
1026        errors.UnsupportedCreateArgs: When specified arguments are
1027                                      unsupported for trusty.
1028        errors.CheckPathError: A specified local path does not exist.
1029    """
1030    if args.avd_type != constants.TYPE_TRUSTY:
1031        if args.local_trusty_image:
1032            raise errors.UnsupportedCreateArgs(
1033                "--local-trusty-image is only valid with "
1034                f"avd_type == {constants.TYPE_TRUSTY}")
1035        if args.trusty_host_package:
1036            raise errors.UnsupportedCreateArgs(
1037                "--trusty-host-package is only valid with "
1038                f"avd_type == {constants.TYPE_TRUSTY}")
1039        # Only check these args if AVD type is Trusty
1040        return
1041
1042    for arg_type, unsupported_args in [
1043        (
1044            "--boot-*",
1045            [
1046                args.boot_build_id,
1047                args.boot_build_target,
1048                args.boot_branch,
1049                args.boot_artifact,
1050            ],
1051        ),
1052        (
1053            "--bootloader-*",
1054            [
1055                args.bootloader_build_id,
1056                args.bootloader_build_target,
1057                args.bootloader_branch,
1058            ],
1059        ),
1060        (
1061            "--android-efi-loader-*",
1062            [
1063                args.android_efi_loader_build_id,
1064                args.android_efi_loader_artifact,
1065            ],
1066        ),
1067        (
1068            "--ota-*",
1069            [
1070                args.ota_branch,
1071                args.ota_build_target,
1072                args.ota_build_id,
1073            ],
1074        ),
1075    ]:
1076        if any(unsupported_args):
1077            raise errors.UnsupportedCreateArgs(
1078                f"{arg_type} is not supported for Trusty."
1079            )
1080
1081    if args.local_image is None and not args.build_target:
1082        raise errors.UnsupportedCreateArgs(
1083            "Trusty android build target not provided and cannot be "
1084            "auto-detected, use --build-target to specify a build target, "
1085            "e.g. qemu_trusty_arm64-trunk_staging-userdebug")
1086    if args.local_trusty_image:
1087        if not os.path.exists(args.local_trusty_image):
1088            raise errors.CheckPathError(
1089                f"Specified path doesn't exist: {args.local_trusty_image}")
1090    if args.trusty_host_package:
1091        if not os.path.exists(args.trusty_host_package):
1092            raise errors.CheckPathError(
1093                f"Specified path doesn't exist: {args.trusty_host_package}")
1094
1095
1096def VerifyArgs(args):
1097    """Verify args.
1098
1099    Args:
1100        args: Namespace object from argparse.parse_args.
1101
1102    Raises:
1103        errors.UnsupportedMultiAdbPort: multi adb port doesn't support.
1104        errors.UnsupportedCreateArgs: When a create arg is specified but
1105                                      unsupported for a particular avd type.
1106                                      (e.g. --system-build-id for gf)
1107    """
1108    # Verify that user specified flavor name is in support list.
1109    # We don't use argparse's builtin validation because we need to be able to
1110    # tell when a user doesn't specify a flavor.
1111    if args.flavor and args.flavor not in constants.ALL_FLAVORS:
1112        logger.debug("Flavor[%s] isn't in default support list: %s",
1113                     args.flavor, constants.ALL_FLAVORS)
1114
1115    if args.avd_type not in (constants.TYPE_CF, constants.TYPE_GF):
1116        if args.system_branch or args.system_build_id or args.system_build_target:
1117            raise errors.UnsupportedCreateArgs(
1118                "--system-* args are not supported for AVD type: %s"
1119                % args.avd_type)
1120
1121    if args.num > 1 and args.adb_port:
1122        raise errors.UnsupportedMultiAdbPort(
1123            "--adb-port is not supported for multi-devices.")
1124
1125    if args.num > 1 and args.local_instance is not None:
1126        raise errors.UnsupportedCreateArgs(
1127            "--num is not supported for local instance.")
1128
1129    if args.local_instance is None and args.gpu == _DEFAULT_GPU:
1130        raise errors.UnsupportedCreateArgs(
1131            "Please assign one gpu model for GCE instance. Reference: "
1132            "https://cloud.google.com/compute/docs/gpus")
1133
1134    if args.adb_port:
1135        utils.CheckPortFree(args.adb_port)
1136
1137    hw_properties = create_common.ParseKeyValuePairArgs(args.hw_property)
1138    for key in hw_properties:
1139        if key not in constants.HW_PROPERTIES:
1140            raise errors.InvalidHWPropertyError(
1141                "[%s] is an invalid hw property, supported values are:%s. "
1142                % (key, constants.HW_PROPERTIES))
1143
1144    cheeps_only_flags = [args.stable_cheeps_host_image_name,
1145                         args.stable_cheeps_host_image_project,
1146                         args.username,
1147                         args.password,
1148                         args.cheeps_betty_image,
1149                         args.cheeps_features]
1150    if args.avd_type != constants.TYPE_CHEEPS and any(cheeps_only_flags):
1151        raise errors.UnsupportedCreateArgs(
1152            "--stable-cheeps-*, --betty-image, --cheeps-feature, --username "
1153            "and --password are only valid with avd_type == %s"
1154            % constants.TYPE_CHEEPS)
1155    if (args.username or args.password) and not (args.username and args.password):
1156        raise ValueError("--username and --password must both be set")
1157    if not args.autoconnect and args.unlock_screen:
1158        raise ValueError("--no-autoconnect and --unlock couldn't be "
1159                         "passed in together.")
1160
1161    _VerifyGoldfishArgs(args)
1162    _VerifyTrustyArgs(args)
1163    _VerifyLocalArgs(args)
1164    _VerifyHostArgs(args)
1165