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 only' Host package branch name. e.g. aosp-main")
265    parser.add_argument(
266        "--host-package-build-id", "--host_package_build_id",
267        type=str,
268        dest="host_package_build_id",
269        required=False,
270        help="'cuttlefish only' Host package build id, e.g. 2145099, P2804227")
271    parser.add_argument(
272        "--host-package-build-target", "--host_package_build_target",
273        type=str,
274        dest="host_package_build_target",
275        required=False,
276        help="'cuttlefish only' Host package build target, e.g. "
277        "cf_x86_64_phone-userdebug.")
278    parser.add_argument(
279        "--system-branch",
280        type=str,
281        dest="system_branch",
282        help="'cuttlefish only' Branch to consume the system image (system.img) "
283        "from, will default to what is defined by --branch. "
284        "That feature allows to (automatically) test various combinations "
285        "of vendor.img (CF, e.g.) and system images (GSI, e.g.). ",
286        required=False)
287    parser.add_argument(
288        "--system-build-id",
289        type=str,
290        dest="system_build_id",
291        help="'cuttlefish only' System image build id, e.g. 2145099, P2804227",
292        required=False)
293    parser.add_argument(
294        "--system-build-target",
295        type=str,
296        dest="system_build_target",
297        help="'cuttlefish only' System image build target, specify if different "
298        "from --build-target",
299        required=False)
300    parser.add_argument(
301        "--launch-args",
302        type=str,
303        dest="launch_args",
304        help="'cuttlefish only' Add extra args to launch_cvd command.",
305        required=False)
306    parser.add_argument(
307        "--pet-name",
308        "--webrtc_device_id",
309        type=str,
310        dest="webrtc_device_id",
311        help="'cuttlefish only' Give the pet name of the instance.",
312        required=False)
313    parser.add_argument(
314        "--gce-metadata",
315        type=str,
316        dest="gce_metadata",
317        default=None,
318        help="'GCE instance only' Record data into GCE instance metadata with "
319        "key-value pair format. e.g. id:12,name:unknown.")
320    parser.add_argument(
321        "--fetch_cvd-build-id",
322        type=str,
323        dest="fetch_cvd_build_id",
324        required=False,
325        help="'cuttlefish only' Build id of fetch_cvd, e.g. 2145099, P2804227")
326    # TODO(146314062): Remove --multi-stage-launch after infra don't use this
327    # args.
328    parser.add_argument(
329        "--multi-stage-launch",
330        dest="multi_stage_launch",
331        action="store_true",
332        required=False,
333        default=True,
334        help="Enable the multi-stage cuttlefish launch.")
335    parser.add_argument(
336        "--no-multi-stage-launch",
337        dest="multi_stage_launch",
338        action="store_false",
339        required=False,
340        default=None,
341        help="Disable the multi-stage cuttlefish launch.")
342    parser.add_argument(
343        "--no-pull-log",
344        dest="no_pull_log",
345        action="store_true",
346        required=False,
347        default=None,
348        help="Disable auto download logs when AVD booting up failed.")
349    parser.add_argument(
350        "--no-mkcert",
351        dest="mkcert",
352        action="store_false",
353        required=False,
354        default=True,
355        help="Disable mkcert setup process on the host.")
356    # TODO(147335651): Add gpu in user config.
357    # TODO(147335651): Support "--gpu" without giving any value.
358    parser.add_argument(
359        "--gpu",
360        type=str,
361        const=_DEFAULT_GPU,
362        nargs="?",
363        dest="gpu",
364        required=False,
365        default=None,
366        help="GPU accelerator to use if any. e.g. nvidia-tesla-k80. For local "
367             "instances, this arg without assigning any value is to enable "
368             "local gpu support.")
369    parser.add_argument(
370        "--num-avds-per-instance",
371        "--num-instances",
372        "--num_instances",
373        type=int,
374        dest="num_avds_per_instance",
375        required=False,
376        default=1,
377        help="'cuttlefish only' Create multiple cuttlefish AVDs in one local "
378             "instance.")
379    parser.add_argument(
380        "--connect-hostname",
381        action="store_true",
382        dest="connect_hostname",
383        required=False,
384        default=False,
385        help="Ssh connects to the GCE instance with hostname.")
386    parser.add_argument(
387        "--gce-only",
388        action="store_true",
389        dest="gce_only",
390        required=False,
391        default=False,
392        help="Only create the GCE instance. It won't create virtual devices.")
393    # Hide following args for users, it is only used in infra.
394    parser.add_argument(
395        "--local-instance-dir",
396        dest="local_instance_dir",
397        required=False,
398        help=argparse.SUPPRESS)
399    parser.add_argument(
400        "--remote-image-dir",
401        dest="remote_image_dir",
402        required=False,
403        # 'cuttlefish remote host only' Upload images and cvd host package to
404        # the remote directory instead of the instance's own directory. If the
405        # directory has been initialized, acloud ignores the image arguments
406        # given by command line and reuses the images in the directory.
407        help=argparse.SUPPRESS)
408    parser.add_argument(
409        "--oxygen",
410        action="store_true",
411        dest="oxygen",
412        required=False,
413        help=argparse.SUPPRESS)
414    parser.add_argument(
415        "--zone",
416        type=str,
417        dest="zone",
418        required=False,
419        help=argparse.SUPPRESS)
420
421    # TODO(b/118439885): Old arg formats to support transition, delete when
422    # transistion is done.
423    parser.add_argument(
424        "--serial_log_file",
425        type=str,
426        dest="serial_log_file",
427        required=False,
428        help=argparse.SUPPRESS)
429    parser.add_argument(
430        "--build_id",
431        type=str,
432        dest="build_id",
433        required=False,
434        help=argparse.SUPPRESS)
435    parser.add_argument(
436        "--build_target",
437        type=str,
438        dest="build_target",
439        required=False,
440        help=argparse.SUPPRESS)
441    parser.add_argument(
442        "--system_branch",
443        type=str,
444        dest="system_branch",
445        required=False,
446        help=argparse.SUPPRESS)
447    parser.add_argument(
448        "--system_build_id",
449        type=str,
450        dest="system_build_id",
451        required=False,
452        help=argparse.SUPPRESS)
453    parser.add_argument(
454        "--system_build_target",
455        type=str,
456        dest="system_build_target",
457        required=False,
458        help=argparse.SUPPRESS)
459    parser.add_argument(
460        "--kernel_build_id",
461        type=str,
462        dest="kernel_build_id",
463        required=False,
464        help=argparse.SUPPRESS)
465    parser.add_argument(
466        "--kernel_branch",
467        type=str,
468        dest="kernel_branch",
469        required=False,
470        help=argparse.SUPPRESS)
471    parser.add_argument(
472        "--kernel_build_target",
473        type=str,
474        dest="kernel_build_target",
475        default="kernel",
476        help=argparse.SUPPRESS)
477    parser.add_argument(
478        "--bootloader_branch",
479        type=str,
480        dest="bootloader_branch",
481        help=argparse.SUPPRESS,
482        required=False)
483    parser.add_argument(
484        "--bootloader_build_id",
485        type=str,
486        dest="bootloader_build_id",
487        help=argparse.SUPPRESS,
488        required=False)
489    parser.add_argument(
490        "--bootloader_build_target",
491        type=str,
492        dest="bootloader_build_target",
493        help=argparse.SUPPRESS,
494        required=False)
495    parser.add_argument(
496        "--fetch_cvd_build_id",
497        type=str,
498        dest="fetch_cvd_build_id",
499        help=argparse.SUPPRESS,
500        required=False)
501    parser.add_argument(
502        "--remote-fetch",
503        action="store_true",
504        dest="remote_fetch",
505        required=False,
506        default=None,
507        help="'cuttlefish only' Fetch artifacts in remote host.")
508    parser.add_argument(
509        "--fetch-cvd-wrapper",
510        dest="fetch_cvd_wrapper",
511        type=str,
512        required=False,
513        help="'cuttlefish only' Fetch artifacts in remote host by a"
514        " provided static executable fetch cvd wrapper file. "
515        " (Still in experiment, this flag only works on lab hosts"
516        " with special setup.)")
517
518
519def GetCreateArgParser(subparser):
520    """Return the create arg parser.
521
522    Args:
523       subparser: argparse.ArgumentParser that is attached to main acloud cmd.
524
525    Returns:
526        argparse.ArgumentParser with create options defined.
527    """
528    create_parser = subparser.add_parser(CMD_CREATE)
529    create_parser.required = False
530    create_parser.set_defaults(which=CMD_CREATE)
531    # Use default=None to distinguish remote instance or local. The instance
532    # type will be remote if the arg is not provided.
533    create_parser.add_argument(
534        "--local-instance",
535        type=_PositiveInteger,
536        const=0,
537        metavar="ID",
538        nargs="?",
539        dest="local_instance",
540        required=False,
541        help="Create a local AVD instance using the resources associated with "
542             "the ID. Choose an unused ID automatically if the value is "
543             "not specified (primarily for infra usage).")
544    create_parser.add_argument(
545        "--adb-port", "-p",
546        type=int,
547        default=None,
548        dest="adb_port",
549        required=False,
550        help="Specify port for adb forwarding.")
551    create_parser.add_argument(
552        "--base-instance-num",
553        type=int,
554        default=None,
555        dest="base_instance_num",
556        required=False,
557        help="'cuttlefish only' The instance number of the created device.")
558    create_parser.add_argument(
559        "--avd-type",
560        type=str,
561        dest="avd_type",
562        default=constants.TYPE_CF,
563        choices=[constants.TYPE_GCE, constants.TYPE_CF, constants.TYPE_GF, constants.TYPE_CHEEPS,
564                 constants.TYPE_FVP],
565        help="Android Virtual Device type (default %s)." % constants.TYPE_CF)
566    create_parser.add_argument(
567        "--config", "--flavor",
568        type=str,
569        dest="flavor",
570        help="The device flavor of the AVD (default %s). e.g. phone, tv, foldable."
571        % constants.FLAVOR_PHONE)
572    create_parser.add_argument(
573        "--local-image",
574        const=constants.FIND_IN_BUILD_ENV,
575        type=str,
576        dest="local_image",
577        nargs="?",
578        required=False,
579        help="Use the locally built image for the AVD. Look for the image "
580        "artifact in $ANDROID_PRODUCT_OUT if no args value is provided."
581        "e.g --local-image or --local-image /path/to/dir or --local-image "
582        "/path/to/file")
583    create_parser.add_argument(
584        "--local-kernel-image", "--local-boot-image",
585        const=constants.FIND_IN_BUILD_ENV,
586        type=str,
587        dest="local_kernel_image",
588        nargs="?",
589        required=False,
590        help="Use the locally built kernel and ramdisk for the AVD. Look "
591        "for boot.img, vendor_boot.img, kernel, initramfs.img, etc. if the "
592        "argument is a directory. Look for the images in $ANDROID_PRODUCT_OUT "
593        "if no argument is provided. e.g., --local-kernel-image, "
594        "--local-kernel-image /path/to/dir, or --local-kernel-image "
595        "/path/to/boot.img")
596    create_parser.add_argument(
597        "--local-system-image",
598        const=constants.FIND_IN_BUILD_ENV,
599        type=str,
600        dest="local_system_image",
601        nargs="?",
602        required=False,
603        help="Use the locally built system images for the AVD. Look for the "
604        "images in $ANDROID_PRODUCT_OUT if no args value is provided. "
605        "e.g., --local-system-image, --local-system-image /path/to/dir, or "
606        "--local-system-image /path/to/img")
607    create_parser.add_argument(
608        "--local-system_dlkm-image",
609        const=constants.FIND_IN_BUILD_ENV,
610        type=str,
611        dest="local_system_dlkm_image",
612        nargs="?",
613        required=False,
614        help="`remote host only` Use the locally built system_dlkm image for "
615        "the AVD. Look for the image in $ANDROID_PRODUCT_OUT if no args value "
616        "is provided.")
617    create_parser.add_argument(
618        "--local-vendor-image",
619        const=constants.FIND_IN_BUILD_ENV,
620        type=str,
621        dest="local_vendor_image",
622        nargs="?",
623        required=False,
624        help="'cuttlefish only' Use the locally built vendor images for the "
625        "AVD. Look for vendor.img, vendor_dlkm.img, odm.img, and odm_dlkm.img "
626        "if the argument is a directory. Look for the images in "
627        "$ANDROID_PRODUCT_OUT if no argument is provided. e.g., "
628        "--local-vendor-image, or --local-vendor-image /path/to/dir")
629    create_parser.add_argument(
630        "--local-vendor_boot-image", "--local-vendor-boot-image",
631        const=constants.FIND_IN_BUILD_ENV,
632        type=str,
633        dest="local_vendor_boot_image",
634        nargs="?",
635        required=False,
636        help="'cuttlefish only' Use the locally built vendor boot image for "
637        "the AVD. Look for the vendor_boot.img in $ANDROID_PRODUCT_OUT "
638        "if no argument is provided. e.g., --local-vendor-boot-image, or "
639        "--local-vendor-boot-image /path/to/dir, or "
640        "--local-vendor-boot-image /path/to/img")
641    create_parser.add_argument(
642        "--local-tool",
643        type=str,
644        dest="local_tool",
645        action="append",
646        default=[],
647        required=False,
648        help="Use the tools in the specified directory to create local "
649        "instances. The directory structure follows $ANDROID_SOONG_HOST_OUT "
650        "or $ANDROID_EMULATOR_PREBUILTS.")
651    create_parser.add_argument(
652        "--cvd-host-package",
653        type=str,
654        dest="cvd_host_package",
655        required=False,
656        help="Use the specified path of the cvd host package to create "
657        "instances. e.g. /path/cvd-host_package_v1.tar.gz")
658    create_parser.add_argument(
659        "--image-download-dir",
660        type=str,
661        dest="image_download_dir",
662        required=False,
663        help="Define remote image download directory, e.g. /usr/local/dl.")
664    create_parser.add_argument(
665        "--yes", "-y",
666        action="store_true",
667        dest="no_prompt",
668        required=False,
669        help=("Automatic yes to prompts. Assume 'yes' as answer to all prompts "
670              "and run non-interactively."))
671    create_parser.add_argument(
672        "--reuse-gce",
673        type=str,
674        const=constants.SELECT_ONE_GCE_INSTANCE,
675        nargs="?",
676        dest="reuse_gce",
677        required=False,
678        help="'cuttlefish only' This can help users use their own instance. "
679        "Reusing specific gce instance if --reuse-gce [instance_name] is "
680        "provided. Select one gce instance to reuse if --reuse-gce is "
681        "provided.")
682    create_parser.add_argument(
683        "--openwrt",
684        action="store_true",
685        dest="openwrt",
686        required=False,
687        help="'cuttlefish only' Create OpenWrt device when launching cuttlefish "
688        "device.")
689    create_parser.add_argument(
690        "--use-launch_cvd",
691        action="store_true",
692        dest="use_launch_cvd",
693        required=False,
694        help="'cuttlefish only' Use launch_cvd to create cuttlefish devices.")
695    create_parser.add_argument(
696        "--host",
697        type=str,
698        dest="remote_host",
699        default=None,
700        help="'cuttlefish only' Provide host name to clean up the remote host. "
701        "For example: '--host 1.1.1.1'")
702    create_parser.add_argument(
703        "--host-user",
704        type=str,
705        dest="host_user",
706        default=constants.GCE_USER,
707        help="'remote host only' Provide host user for logging in to the host. "
708        "The default value is vsoc-01. For example: '--host 1.1.1.1 --host-user "
709        "vsoc-02'")
710    create_parser.add_argument(
711        "--host-ssh-private-key-path",
712        type=str,
713        dest="host_ssh_private_key_path",
714        default=None,
715        help="'remote host only' Provide host key for login on on this host.")
716    # User should not specify --spec and --hw_property at the same time.
717    hw_spec_group = create_parser.add_mutually_exclusive_group()
718    hw_spec_group.add_argument(
719        "--hw-property",
720        type=str,
721        dest="hw_property",
722        required=False,
723        help="Supported HW properties and example values: %s" %
724        constants.HW_PROPERTIES_CMD_EXAMPLE)
725    hw_spec_group.add_argument(
726        "--spec",
727        type=str,
728        dest="spec",
729        required=False,
730        choices=constants.SPEC_NAMES,
731        help="The name of a pre-configured device spec that we are "
732        "going to use.")
733    create_parser.add_argument(
734        "--disk-type",
735        type=str,
736        dest="disk_type",
737        required=False,
738        help="This is used to customize the GCE instance disk type, the "
739        "default disk type is from the stable host image. Use pd-ssd or "
740        "pd-standard to specify instance disk type.")
741    create_parser.add_argument(
742        "--stable-host-image-name",
743        type=str,
744        dest="stable_host_image_name",
745        required=False,
746        default=None,
747        help=("'cuttlefish only' The Cuttlefish host image from which instances "
748              "are launched. If specified here, the value set in Acloud config "
749              "file will be overridden."))
750
751    # Arguments for goldfish type.
752    create_parser.add_argument(
753        "--emulator-build-id",
754        type=str,
755        dest="emulator_build_id",
756        required=False,
757        help="'goldfish only' Emulator build ID used to run the images. "
758        "e.g. 4669466.")
759    create_parser.add_argument(
760        "--emulator-build-target",
761        dest="emulator_build_target",
762        required=False,
763        help="'goldfish remote host only' Emulator build target used to run "
764        "the images. e.g. emulator-linux_x64_nolocationui.")
765    create_parser.add_argument(
766        "--emulator-zip",
767        dest="emulator_zip",
768        required=False,
769        help="'goldfish remote host only' Emulator zip used to run the "
770        "images. e.g., /path/sdk-repo-linux-emulator-1234567.zip.")
771
772    # Arguments for cheeps type.
773    create_parser.add_argument(
774        "--stable-cheeps-host-image-name",
775        type=str,
776        dest="stable_cheeps_host_image_name",
777        required=False,
778        default=None,
779        help=("'cheeps only' The Cheeps host image from which instances are "
780              "launched. If specified here, the value set in Acloud config "
781              "file will be overridden."))
782    create_parser.add_argument(
783        "--stable-cheeps-host-image-project",
784        type=str,
785        dest="stable_cheeps_host_image_project",
786        required=False,
787        default=None,
788        help=("'cheeps only' The project hosting the specified Cheeps host "
789              "image. If specified here, the value set in Acloud config file "
790              "will be overridden."))
791    create_parser.add_argument(
792        "--user",
793        type=str,
794        dest="username",
795        required=False,
796        default=None,
797        help="'cheeps only' username to log in to Chrome OS as.")
798    create_parser.add_argument(
799        "--password",
800        type=str,
801        dest="password",
802        required=False,
803        default=None,
804        help="'cheeps only' password to log in to Chrome OS with.")
805    create_parser.add_argument(
806        "--betty-image",
807        type=str,
808        dest="cheeps_betty_image",
809        required=False,
810        default=None,
811        help=("'cheeps only' The L1 betty version to use. Only makes sense "
812              "when launching a controller image with "
813              "stable-cheeps-host-image"))
814    create_parser.add_argument(
815        "--cheeps-feature",
816        type=str,
817        dest="cheeps_features",
818        required=False,
819        action="append",
820        default=[],
821        help=("'cheeps only' Cheeps feature to enable. Can be repeated."))
822
823    AddCommonCreateArgs(create_parser)
824    return create_parser
825
826
827def _PositiveInteger(arg):
828    """Convert an argument from a string to a positive integer."""
829    try:
830        value = int(arg)
831    except ValueError as e:
832        raise argparse.ArgumentTypeError(arg + " is not an integer.") from e
833    if value <= 0:
834        raise argparse.ArgumentTypeError(arg + " is not positive.")
835    return value
836
837
838def _VerifyLocalArgs(args):
839    """Verify args starting with --local.
840
841    Args:
842        args: Namespace object from argparse.parse_args.
843
844    Raises:
845        errors.CheckPathError: Image path doesn't exist.
846        errors.UnsupportedCreateArgs: The specified avd type does not support
847                                      --local-system-image.
848        errors.UnsupportedLocalInstanceId: Local instance ID is invalid.
849    """
850    if args.local_image and not os.path.exists(args.local_image):
851        raise errors.CheckPathError(
852            "Specified path doesn't exist: %s" % args.local_image)
853
854    if args.local_instance_dir and not os.path.exists(args.local_instance_dir):
855        raise errors.CheckPathError(
856            "Specified path doesn't exist: %s" % args.local_instance_dir)
857
858    if not (args.local_system_image is None or
859            args.avd_type in (constants.TYPE_CF, constants.TYPE_GF)):
860        raise errors.UnsupportedCreateArgs("%s instance does not support "
861                                           "--local-system-image" %
862                                           args.avd_type)
863    # TODO(b/179340595): To support local image remote instance with kernel build.
864    if args.local_instance is None and args.local_image is not None and (
865            args.kernel_branch or args.kernel_build_id):
866        raise errors.UnsupportedCreateArgs(
867            "Acloud didn't support local image with specific kernel. "
868            "Please download the specific kernel and put it into "
869            "your local image folder: '%s'." % (
870            args.local_image if args.local_image else
871            utils.GetBuildEnvironmentVariable(constants.ENV_ANDROID_PRODUCT_OUT)))
872
873    if (args.local_system_image and
874            not os.path.exists(args.local_system_image)):
875        raise errors.CheckPathError(
876            "Specified path doesn't exist: %s" % args.local_system_image)
877
878    for tool_dir in args.local_tool:
879        if not os.path.exists(tool_dir):
880            raise errors.CheckPathError(
881                "Specified path doesn't exist: %s" % tool_dir)
882
883
884def _VerifyHostArgs(args):
885    """Verify args starting with --host.
886
887    Args:
888        args: Namespace object from argparse.parse_args.
889
890    Raises:
891        errors.UnsupportedCreateArgs: When a create arg is specified but
892                                      unsupported for remote host mode.
893    """
894    if args.remote_host and args.local_instance is not None:
895        raise errors.UnsupportedCreateArgs(
896            "--host is not supported for local instance.")
897
898    if args.remote_host and args.num > 1:
899        raise errors.UnsupportedCreateArgs(
900            "--num is not supported for remote host.")
901
902    if args.host_user != constants.GCE_USER and args.remote_host is None:
903        raise errors.UnsupportedCreateArgs(
904            "--host-user is only supported for remote host.")
905
906    if args.host_ssh_private_key_path and args.remote_host is None:
907        raise errors.UnsupportedCreateArgs(
908            "--host-ssh-private-key-path is only supported for remote host.")
909
910    if args.remote_image_dir:
911        if args.remote_host is None:
912            raise errors.UnsupportedCreateArgs(
913                "--remote-image-dir is only supported for remote host.")
914        if remote_path.basename(
915                remote_path.normpath(args.remote_image_dir)) in ("..", "."):
916            raise errors.UnsupportedCreateArgs(
917                "--remote-image-dir must not include the working directory.")
918
919
920def _VerifyGoldfishArgs(args):
921    """Verify goldfish args.
922
923    Args:
924        args: Namespace object from argparse.parse_args.
925
926    Raises:
927        errors.UnsupportedCreateArgs: When a create arg is specified but
928                                      unsupported for goldfish.
929    """
930    goldfish_only_flags = [
931        args.emulator_build_id,
932        args.emulator_build_target,
933        args.emulator_zip
934    ]
935    if args.avd_type != constants.TYPE_GF and any(goldfish_only_flags):
936        raise errors.UnsupportedCreateArgs(
937            f"--emulator-* is only valid with avd_type == {constants.TYPE_GF}")
938
939    # Exclude kernel_build_target because the default value isn't empty.
940    remote_kernel_flags = [
941        args.kernel_build_id,
942        args.kernel_branch,
943    ]
944    if args.avd_type == constants.TYPE_GF and any(remote_kernel_flags):
945        raise errors.UnsupportedCreateArgs(
946            "--kernel-* is not supported for goldfish.")
947
948    remote_boot_flags = [
949        args.boot_build_id,
950        args.boot_build_target,
951        args.boot_branch,
952        args.boot_artifact,
953    ]
954    if (args.avd_type == constants.TYPE_GF and any(remote_boot_flags) and
955            not all(remote_boot_flags)):
956        raise errors.UnsupportedCreateArgs(
957            "Either none or all of --boot-branch, --boot-build-target, "
958            "--boot-build-id, and --boot-artifact must be specified for "
959            "goldfish.")
960
961    remote_system_flags = [
962        args.system_build_target,
963        args.system_build_id,
964        args.system_branch,
965    ]
966    if (args.avd_type == constants.TYPE_GF and any(remote_system_flags) and
967            not all(remote_system_flags)):
968        raise errors.UnsupportedCreateArgs(
969            "Either none or all of --system-branch, --system-build-target, "
970            "and --system-build-id must be specified for goldfish.")
971
972    remote_host_only_flags = remote_boot_flags + remote_system_flags
973    if args.avd_type == constants.TYPE_GF and args.remote_host is None and any(
974            remote_host_only_flags):
975        raise errors.UnsupportedCreateArgs(
976            "--boot-* and --system-* for goldfish are only supported for "
977            "remote host.")
978
979
980def VerifyArgs(args):
981    """Verify args.
982
983    Args:
984        args: Namespace object from argparse.parse_args.
985
986    Raises:
987        errors.UnsupportedMultiAdbPort: multi adb port doesn't support.
988        errors.UnsupportedCreateArgs: When a create arg is specified but
989                                      unsupported for a particular avd type.
990                                      (e.g. --system-build-id for gf)
991    """
992    # Verify that user specified flavor name is in support list.
993    # We don't use argparse's builtin validation because we need to be able to
994    # tell when a user doesn't specify a flavor.
995    if args.flavor and args.flavor not in constants.ALL_FLAVORS:
996        logger.debug("Flavor[%s] isn't in default support list: %s",
997                     args.flavor, constants.ALL_FLAVORS)
998
999    if args.avd_type not in (constants.TYPE_CF, constants.TYPE_GF):
1000        if args.system_branch or args.system_build_id or args.system_build_target:
1001            raise errors.UnsupportedCreateArgs(
1002                "--system-* args are not supported for AVD type: %s"
1003                % args.avd_type)
1004
1005    if args.num > 1 and args.adb_port:
1006        raise errors.UnsupportedMultiAdbPort(
1007            "--adb-port is not supported for multi-devices.")
1008
1009    if args.num > 1 and args.local_instance is not None:
1010        raise errors.UnsupportedCreateArgs(
1011            "--num is not supported for local instance.")
1012
1013    if args.local_instance is None and args.gpu == _DEFAULT_GPU:
1014        raise errors.UnsupportedCreateArgs(
1015            "Please assign one gpu model for GCE instance. Reference: "
1016            "https://cloud.google.com/compute/docs/gpus")
1017
1018    if args.adb_port:
1019        utils.CheckPortFree(args.adb_port)
1020
1021    hw_properties = create_common.ParseKeyValuePairArgs(args.hw_property)
1022    for key in hw_properties:
1023        if key not in constants.HW_PROPERTIES:
1024            raise errors.InvalidHWPropertyError(
1025                "[%s] is an invalid hw property, supported values are:%s. "
1026                % (key, constants.HW_PROPERTIES))
1027
1028    cheeps_only_flags = [args.stable_cheeps_host_image_name,
1029                         args.stable_cheeps_host_image_project,
1030                         args.username,
1031                         args.password,
1032                         args.cheeps_betty_image,
1033                         args.cheeps_features]
1034    if args.avd_type != constants.TYPE_CHEEPS and any(cheeps_only_flags):
1035        raise errors.UnsupportedCreateArgs(
1036            "--stable-cheeps-*, --betty-image, --cheeps-feature, --username "
1037            "and --password are only valid with avd_type == %s"
1038            % constants.TYPE_CHEEPS)
1039    if (args.username or args.password) and not (args.username and args.password):
1040        raise ValueError("--username and --password must both be set")
1041    if not args.autoconnect and args.unlock_screen:
1042        raise ValueError("--no-autoconnect and --unlock couldn't be "
1043                         "passed in together.")
1044
1045    _VerifyGoldfishArgs(args)
1046    _VerifyLocalArgs(args)
1047    _VerifyHostArgs(args)
1048