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. 16"""Action to create goldfish device instances. 17 18A Goldfish device is an emulated android device based on the android 19emulator. 20""" 21import logging 22import os 23 24from acloud import errors 25from acloud.public.actions import base_device_factory 26from acloud.public.actions import common_operations 27from acloud.internal import constants 28from acloud.internal.lib import android_build_client 29from acloud.internal.lib import auth 30from acloud.internal.lib import goldfish_compute_client 31from acloud.internal.lib import utils 32 33logger = logging.getLogger(__name__) 34 35_EMULATOR_INFO_FILENAME = "emulator-info.txt" 36_EMULATOR_VERSION_PATTERN = "version-emulator" 37_SYSIMAGE_INFO_FILENAME = "android-info.txt" 38_SYSIMAGE_VERSION_PATTERN = "version-sysimage-{}-{}" 39 40 41class GoldfishDeviceFactory(base_device_factory.BaseDeviceFactory): 42 """A class that can produce a goldfish device. 43 44 Attributes: 45 _cfg: An AcloudConfig instance. 46 _build_target: String, the build target, e.g. aosp_x86-eng. 47 _build_id: String, Build id, e.g. "2263051", "P2804227" 48 _emulator_build_target: String, the emulator build target, e.g. aosp_x86-eng. 49 _emulator_build_id: String, emulator build id. 50 _gpu: String, GPU to attach to the device or None. e.g. "nvidia-tesla-k80" 51 _blank_data_disk_size_gb: Integer, extra disk size 52 _build_client: An AndroidBuildClient instance 53 _branch: String, android branch name, e.g. git_master 54 _emulator_branch: String, emulator branch name, e.g. "aosp-emu-master-dev" 55 """ 56 LOG_FILES = ["/home/vsoc-01/emulator.log", 57 "/home/vsoc-01/log/logcat.log", 58 "/home/vsoc-01/log/adb.log", 59 "/var/log/daemon.log"] 60 61 def __init__(self, 62 cfg, 63 build_target, 64 build_id, 65 emulator_build_target, 66 emulator_build_id, 67 gpu=None, 68 avd_spec=None): 69 70 """Initialize. 71 72 Args: 73 cfg: An AcloudConfig instance. 74 build_target: String, the build target, e.g. aosp_x86-eng. 75 build_id: String, Build id, e.g. "2263051", "P2804227" 76 emulator_build_target: String, the emulator build target, e.g. aosp_x86-eng. 77 emulator_build_id: String, emulator build id. 78 gpu: String, GPU to attach to the device or None. e.g. "nvidia-tesla-k80" 79 avd_spec: An AVDSpec instance. 80 """ 81 82 self.credentials = auth.CreateCredentials(cfg) 83 84 compute_client = goldfish_compute_client.GoldfishComputeClient( 85 cfg, self.credentials) 86 super(GoldfishDeviceFactory, self).__init__(compute_client) 87 88 # Private creation parameters 89 self._cfg = cfg 90 self._build_target = build_target 91 self._build_id = build_id 92 self._emulator_build_id = emulator_build_id 93 self._emulator_build_target = emulator_build_target 94 self._gpu = gpu 95 self._avd_spec = avd_spec 96 self._blank_data_disk_size_gb = cfg.extra_data_disk_size_gb 97 self._extra_scopes = cfg.extra_scopes 98 99 # Configure clients 100 self._build_client = android_build_client.AndroidBuildClient( 101 self.credentials) 102 103 # Discover branches 104 self._branch = self._build_client.GetBranch(build_target, build_id) 105 self._emulator_branch = self._build_client.GetBranch( 106 emulator_build_target, emulator_build_id) 107 108 def CreateInstance(self): 109 """Creates single configured goldfish device. 110 111 Override method from parent class. 112 113 Returns: 114 String, the name of the created instance. 115 """ 116 instance = self._compute_client.GenerateInstanceName( 117 build_id=self._build_id, build_target=self._build_target) 118 119 self._compute_client.CreateInstance( 120 instance=instance, 121 image_name=self._cfg.stable_goldfish_host_image_name, 122 image_project=self._cfg.stable_goldfish_host_image_project, 123 build_target=self._build_target, 124 branch=self._branch, 125 build_id=self._build_id, 126 emulator_branch=self._emulator_branch, 127 emulator_build_id=self._emulator_build_id, 128 gpu=self._gpu, 129 blank_data_disk_size_gb=self._blank_data_disk_size_gb, 130 avd_spec=self._avd_spec, 131 extra_scopes=self._extra_scopes) 132 133 return instance 134 135 136def ParseBuildInfo(filename, pattern): 137 """Parse build id based on a substring. 138 139 This will parse a file which contains build information to be used. For an 140 emulator build, the file will contain the information about the corresponding 141 stable system image build id. Similarly, for a system image build, the file 142 will contain the information about the corresponding stable emulator build id. 143 Pattern is a substring being used as a key to parse the build info. For 144 example, "version-sysimage-git_pi-dev-sdk_gphone_x86_64-userdebug". 145 146 Args: 147 filename: Name of file to parse. 148 pattern: Substring to look for in file 149 150 Returns: 151 Build id parsed from the file based on pattern 152 Returns None if pattern not found in file 153 """ 154 with open(filename) as build_info_file: 155 for line in build_info_file: 156 if pattern in line: 157 return line.rstrip().split("=")[1] 158 return None 159 160 161def _FetchBuildIdFromFile(cfg, build_target, build_id, pattern, filename): 162 """Parse and fetch build id from a file based on a pattern. 163 164 Verify if one of the system image or emulator binary build id is missing. 165 If found missing, then update according to the resource file. 166 167 Args: 168 cfg: An AcloudConfig instance. 169 build_target: Target name. 170 build_id: Build id, a string, e.g. "2263051", "P2804227" 171 pattern: A string to parse build info file. 172 filename: Name of file containing the build info. 173 174 Returns: 175 A build id or None 176 """ 177 build_client = android_build_client.AndroidBuildClient( 178 auth.CreateCredentials(cfg)) 179 180 with utils.TempDir() as tempdir: 181 temp_filename = os.path.join(tempdir, filename) 182 build_client.DownloadArtifact(build_target, 183 build_id, 184 filename, 185 temp_filename) 186 187 return ParseBuildInfo(temp_filename, pattern) 188 189 190def CreateDevices(avd_spec=None, 191 cfg=None, 192 build_target=None, 193 build_id=None, 194 emulator_build_id=None, 195 gpu=None, 196 num=1, 197 serial_log_file=None, 198 logcat_file=None, 199 autoconnect=False, 200 branch=None, 201 report_internal_ip=False): 202 """Create one or multiple Goldfish devices. 203 204 Args: 205 avd_spec: An AVDSpec instance. 206 cfg: An AcloudConfig instance. 207 build_target: String, the build target, e.g. aosp_x86-eng. 208 build_id: String, Build id, e.g. "2263051", "P2804227" 209 emulator_build_id: String, emulator build id. 210 gpu: String, GPU to attach to the device or None. e.g. "nvidia-tesla-k80" 211 num: Integer, Number of devices to create. 212 serial_log_file: String, A path to a file where serial output should 213 be saved to. 214 logcat_file: String, A path to a file where logcat logs should be saved. 215 autoconnect: Boolean, Create ssh tunnel(s) and adb connect after device creation. 216 branch: String, Branch name for system image. 217 report_internal_ip: Boolean to report the internal ip instead of 218 external ip. 219 220 Returns: 221 A Report instance. 222 """ 223 if avd_spec: 224 cfg = avd_spec.cfg 225 build_target = avd_spec.remote_image[constants.BUILD_TARGET] 226 build_id = avd_spec.remote_image[constants.BUILD_ID] 227 branch = avd_spec.remote_image[constants.BUILD_BRANCH] 228 num = avd_spec.num 229 emulator_build_id = avd_spec.emulator_build_id 230 gpu = avd_spec.gpu 231 serial_log_file = avd_spec.serial_log_file 232 logcat_file = avd_spec.logcat_file 233 autoconnect = avd_spec.autoconnect 234 report_internal_ip = avd_spec.report_internal_ip 235 236 if emulator_build_id is None: 237 logger.info("emulator_build_id not provided. " 238 "Try to get %s from build %s/%s.", _EMULATOR_INFO_FILENAME, 239 build_id, build_target) 240 emulator_build_id = _FetchBuildIdFromFile(cfg, 241 build_target, 242 build_id, 243 _EMULATOR_VERSION_PATTERN, 244 _EMULATOR_INFO_FILENAME) 245 246 if emulator_build_id is None: 247 raise errors.CommandArgError("Emulator build id not found " 248 "in %s" % _EMULATOR_INFO_FILENAME) 249 250 if build_id is None: 251 pattern = _SYSIMAGE_VERSION_PATTERN.format(branch, build_target) 252 build_id = _FetchBuildIdFromFile(cfg, 253 cfg.emulator_build_target, 254 emulator_build_id, 255 pattern, 256 _SYSIMAGE_INFO_FILENAME) 257 258 if build_id is None: 259 raise errors.CommandArgError("Emulator system image build id not found " 260 "in %s" % _SYSIMAGE_INFO_FILENAME) 261 logger.info( 262 "Creating a goldfish device in project %s, build_target: %s, " 263 "build_id: %s, emulator_bid: %s, GPU: %s, num: %s, " 264 "serial_log_file: %s, logcat_file: %s, " 265 "autoconnect: %s", cfg.project, build_target, build_id, 266 emulator_build_id, gpu, num, serial_log_file, logcat_file, autoconnect) 267 268 device_factory = GoldfishDeviceFactory(cfg, build_target, build_id, 269 cfg.emulator_build_target, 270 emulator_build_id, gpu, avd_spec) 271 272 return common_operations.CreateDevices("create_gf", cfg, device_factory, 273 num, constants.TYPE_GF, 274 report_internal_ip, autoconnect, 275 serial_log_file, logcat_file) 276