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