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