ronnie
2022-10-23 d7a691c7a2527f2da145355a40a0402c95c67aac
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/usr/bin/env python
#
# Copyright 2018 - The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
r"""Create entry point.
 
Create will handle all the logic related to creating a local/remote instance
an Android Virtual Device and the logic related to prepping the local/remote
image artifacts.
"""
 
from __future__ import print_function
 
from distutils.spawn import find_executable
import os
import subprocess
import sys
 
from acloud import errors
from acloud.create import avd_spec
from acloud.create import cheeps_remote_image_remote_instance
from acloud.create import gce_local_image_remote_instance
from acloud.create import gce_remote_image_remote_instance
from acloud.create import goldfish_remote_image_remote_instance
from acloud.create import local_image_local_instance
from acloud.create import local_image_remote_instance
from acloud.create import remote_image_remote_instance
from acloud.create import remote_image_local_instance
from acloud.internal import constants
from acloud.internal.lib import utils
from acloud.setup import setup
from acloud.setup import gcp_setup_runner
from acloud.setup import host_setup_runner
 
_MAKE_CMD = "build/soong/soong_ui.bash"
_MAKE_ARG = "--make-mode"
 
_CREATOR_CLASS_DICT = {
    # GCE types
    (constants.TYPE_GCE, constants.IMAGE_SRC_LOCAL, constants.INSTANCE_TYPE_REMOTE):
        gce_local_image_remote_instance.GceLocalImageRemoteInstance,
    (constants.TYPE_GCE, constants.IMAGE_SRC_REMOTE, constants.INSTANCE_TYPE_REMOTE):
        gce_remote_image_remote_instance.GceRemoteImageRemoteInstance,
    # CF types
    (constants.TYPE_CF, constants.IMAGE_SRC_LOCAL, constants.INSTANCE_TYPE_LOCAL):
        local_image_local_instance.LocalImageLocalInstance,
    (constants.TYPE_CF, constants.IMAGE_SRC_LOCAL, constants.INSTANCE_TYPE_REMOTE):
        local_image_remote_instance.LocalImageRemoteInstance,
    (constants.TYPE_CF, constants.IMAGE_SRC_REMOTE, constants.INSTANCE_TYPE_REMOTE):
        remote_image_remote_instance.RemoteImageRemoteInstance,
    (constants.TYPE_CF, constants.IMAGE_SRC_REMOTE, constants.INSTANCE_TYPE_LOCAL):
        remote_image_local_instance.RemoteImageLocalInstance,
    # Cheeps types
    (constants.TYPE_CHEEPS, constants.IMAGE_SRC_REMOTE, constants.INSTANCE_TYPE_REMOTE):
        cheeps_remote_image_remote_instance.CheepsRemoteImageRemoteInstance,
    # GF types
    (constants.TYPE_GF, constants.IMAGE_SRC_REMOTE, constants.INSTANCE_TYPE_REMOTE):
        goldfish_remote_image_remote_instance.GoldfishRemoteImageRemoteInstance,
}
 
 
def GetAvdCreatorClass(avd_type, instance_type, image_source):
    """Return the creator class for the specified spec.
 
    Based on the image source and the instance type, return the proper
    creator class.
 
    Args:
        avd_type: String, the AVD type(cuttlefish, gce).
        instance_type: String, the AVD instance type (local or remote).
        image_source: String, the source of the image (local or remote).
 
    Returns:
        An AVD creator class (e.g. LocalImageRemoteInstance).
 
    Raises:
        UnsupportedInstanceImageType if argments didn't match _CREATOR_CLASS_DICT.
    """
    creator_class = _CREATOR_CLASS_DICT.get(
        (avd_type, image_source, instance_type))
 
    if not creator_class:
        raise errors.UnsupportedInstanceImageType(
            "unsupported creation of avd type: %s, instance type: %s, "
            "image source: %s" % (avd_type, instance_type, image_source))
    return creator_class
 
def _CheckForAutoconnect(args):
    """Check that we have all prerequisites for autoconnect.
 
    Autoconnect requires adb and ssh, we'll just check for adb for now and
    assume ssh is everywhere. If adb isn't around, ask the user if they want us
    to build it, if not we'll disable autoconnect.
 
    Args:
        args: Namespace object from argparse.parse_args.
    """
    if not args.autoconnect or find_executable(constants.ADB_BIN):
        return
 
    disable_autoconnect = False
    answer = utils.InteractWithQuestion(
        "adb is required for autoconnect, without it autoconnect will be "
        "disabled, would you like acloud to build it[y/N]? ")
    if answer in constants.USER_ANSWER_YES:
        utils.PrintColorString("Building adb ... ", end="")
        android_build_top = os.environ.get(
            constants.ENV_ANDROID_BUILD_TOP)
        if not android_build_top:
            utils.PrintColorString("Fail! (Not in a lunch'd env)",
                                   utils.TextColors.FAIL)
            disable_autoconnect = True
        else:
            make_cmd = os.path.join(android_build_top, _MAKE_CMD)
            build_adb_cmd = [make_cmd, _MAKE_ARG, "adb"]
            try:
                with open(os.devnull, "w") as dev_null:
                    subprocess.check_call(build_adb_cmd, stderr=dev_null,
                                          stdout=dev_null)
                    utils.PrintColorString("OK!", utils.TextColors.OKGREEN)
            except subprocess.CalledProcessError:
                utils.PrintColorString("Fail! (build failed)",
                                       utils.TextColors.FAIL)
                disable_autoconnect = True
    else:
        disable_autoconnect = True
 
    if disable_autoconnect:
        utils.PrintColorString("Disabling autoconnect",
                               utils.TextColors.WARNING)
        args.autoconnect = False
 
 
def _CheckForSetup(args):
    """Check that host is setup to run the create commands.
 
    We'll check we have the necessary bits setup to do what the user wants, and
    if not, tell them what they need to do before running create again.
 
    Args:
        args: Namespace object from argparse.parse_args.
    """
    run_setup = False
    # Need to set all these so if we need to run setup, it won't barf on us
    # because of some missing fields.
    args.gcp_init = False
    args.host = False
    args.force = False
    # Remote image/instance requires the GCP config setup.
    if not args.local_instance or args.local_image == "":
        gcp_setup = gcp_setup_runner.GcpTaskRunner(args.config_file)
        if gcp_setup.ShouldRun():
            args.gcp_init = True
            run_setup = True
 
    # Local instance requires host to be setup. We'll assume that if the
    # packages were installed, then the user was added into the groups. This
    # avoids the scenario where a user runs setup and creates a local instance.
    # The following local instance create will trigger this if statment and go
    # through the whole setup again even though it's already done because the
    # user groups aren't set until the user logs out and back in.
    if args.local_instance:
        host_pkg_setup = host_setup_runner.AvdPkgInstaller()
        if host_pkg_setup.ShouldRun():
            args.host = True
            run_setup = True
 
    if run_setup:
        answer = utils.InteractWithQuestion("Missing necessary acloud setup, "
                                            "would you like to run setup[y/N]?")
        if answer in constants.USER_ANSWER_YES:
            setup.Run(args)
        else:
            print("Please run '#acloud setup' so we can get your host setup")
            sys.exit(constants.EXIT_BY_USER)
 
 
def PreRunCheck(args):
    """Do some pre-run checks to ensure a smooth create experience.
 
    Args:
        args: Namespace object from argparse.parse_args.
    """
    _CheckForSetup(args)
    _CheckForAutoconnect(args)
 
 
def Run(args):
    """Run create.
 
    Args:
        args: Namespace object from argparse.parse_args.
    """
    PreRunCheck(args)
    spec = avd_spec.AVDSpec(args)
    avd_creator_class = GetAvdCreatorClass(spec.avd_type,
                                           spec.instance_type,
                                           spec.image_source)
    avd_creator = avd_creator_class()
    report = avd_creator.Create(spec, args.no_prompt)
    if report and args.report_file:
        report.Dump(args.report_file)