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 23import re 24 25from acloud import errors 26from acloud.public.actions import base_device_factory 27from acloud.public.actions import common_operations 28from acloud.internal import constants 29from acloud.internal.lib import android_build_client 30from acloud.internal.lib import auth 31from acloud.internal.lib import goldfish_compute_client 32from acloud.internal.lib import utils 33 34 35logger = logging.getLogger(__name__) 36 37_EMULATOR_INFO_FILENAME = "emulator-info.txt" 38_SYSIMAGE_INFO_FILENAME = "android-info.txt" 39_VERSION_PATTERN = r"version-.*=(\d+)" 40 41 42class GoldfishDeviceFactory(base_device_factory.BaseDeviceFactory): 43 """A class that can produce a goldfish device. 44 45 Attributes: 46 _cfg: An AcloudConfig instance. 47 _build_target: String, the build target, e.g. aosp_x86-eng. 48 _build_id: String, Build id, e.g. "2263051", "P2804227" 49 _emulator_build_target: String, the emulator build target, e.g. aosp_x86-eng. 50 _emulator_build_id: String, emulator build id. 51 _gpu: String, GPU to attach to the device or None. e.g. "nvidia-tesla-k80" 52 _blank_data_disk_size_gb: Integer, extra disk size 53 _build_client: An AndroidBuildClient instance 54 _branch: String, android branch name, e.g. git_master 55 _emulator_branch: String, emulator branch name, e.g. "aosp-emu-master-dev" 56 """ 57 LOG_FILES = ["/home/vsoc-01/emulator.log", 58 "/home/vsoc-01/log/logcat.log", 59 "/home/vsoc-01/log/adb.log", 60 "/var/log/daemon.log"] 61 62 def __init__(self, 63 cfg, 64 build_target, 65 build_id, 66 emulator_build_target, 67 emulator_build_id, 68 kernel_build_id=None, 69 kernel_branch=None, 70 kernel_build_target=None, 71 gpu=None, 72 avd_spec=None, 73 tags=None, 74 branch=None, 75 emulator_branch=None): 76 77 """Initialize. 78 79 Args: 80 cfg: An AcloudConfig instance. 81 build_target: String, the build target, e.g. aosp_x86-eng. 82 build_id: String, Build id, e.g. "2263051", "P2804227" 83 emulator_build_target: String, the emulator build target, e.g. aosp_x86-eng. 84 emulator_build_id: String, emulator build id. 85 gpu: String, GPU to attach to the device or None. e.g. "nvidia-tesla-k80" 86 avd_spec: An AVDSpec instance. 87 tags: A list of tags to associate with the instance. e.g. 88 ["http-server", "https-server"] 89 branch: String, branch of the emulator build target. 90 emulator_branch: String, branch of the emulator. 91 """ 92 93 self.credentials = auth.CreateCredentials(cfg) 94 95 compute_client = goldfish_compute_client.GoldfishComputeClient( 96 cfg, self.credentials) 97 super(GoldfishDeviceFactory, self).__init__(compute_client) 98 99 # Private creation parameters 100 self._cfg = cfg 101 self._gpu = gpu 102 self._avd_spec = avd_spec 103 self._blank_data_disk_size_gb = cfg.extra_data_disk_size_gb 104 self._extra_scopes = cfg.extra_scopes 105 self._tags = tags 106 107 # Configure clients 108 self._build_client = android_build_client.AndroidBuildClient( 109 self.credentials) 110 111 # Get build info 112 self.build_info = self._build_client.GetBuildInfo( 113 build_target, build_id, branch) 114 self.emulator_build_info = self._build_client.GetBuildInfo( 115 emulator_build_target, emulator_build_id, emulator_branch) 116 self.kernel_build_info = self._build_client.GetBuildInfo( 117 kernel_build_target or cfg.kernel_build_target, kernel_build_id, 118 kernel_branch) 119 120 def GetBuildInfoDict(self): 121 """Get build info dictionary. 122 123 Returns: 124 A build info dictionary 125 """ 126 build_info_dict = { 127 key: val for key, val in utils.GetDictItems(self.build_info) if val} 128 129 build_info_dict.update( 130 {"emulator_%s" % key: val 131 for key, val in utils.GetDictItems(self.emulator_build_info) if val} 132 ) 133 134 build_info_dict.update( 135 {"kernel_%s" % key: val 136 for key, val in utils.GetDictItems(self.kernel_build_info) if val} 137 ) 138 139 return build_info_dict 140 141 def CreateInstance(self): 142 """Creates single configured goldfish device. 143 144 Override method from parent class. 145 146 Returns: 147 String, the name of the created instance. 148 """ 149 instance = self._compute_client.GenerateInstanceName( 150 build_id=self.build_info.build_id, 151 build_target=self.build_info.build_target) 152 153 self._compute_client.CreateInstance( 154 instance=instance, 155 image_name=self._cfg.stable_goldfish_host_image_name, 156 image_project=self._cfg.stable_goldfish_host_image_project, 157 build_target=self.build_info.build_target, 158 branch=self.build_info.branch, 159 build_id=self.build_info.build_id, 160 emulator_branch=self.emulator_build_info.branch, 161 emulator_build_id=self.emulator_build_info.build_id, 162 kernel_branch=self.kernel_build_info.branch, 163 kernel_build_id=self.kernel_build_info.build_id, 164 kernel_build_target=self.kernel_build_info.build_target, 165 gpu=self._gpu, 166 blank_data_disk_size_gb=self._blank_data_disk_size_gb, 167 avd_spec=self._avd_spec, 168 tags=self._tags, 169 extra_scopes=self._extra_scopes) 170 171 return instance 172 173 174def ParseBuildInfo(filename): 175 """Parse build id based on a substring. 176 177 This will parse a file which contains build information to be used. For an 178 emulator build, the file will contain the information about the 179 corresponding stable system image build id. In emulator-info.txt, the file 180 will contains the information about the corresponding stable emulator 181 build id, for example "require version-emulator=5292001". In 182 android-info.txt, the file will contains the information about a stable 183 system image build id, for example 184 "version-sysimage-git_pi-dev-sdk_gphone_x86_64-userdebug=4833817" 185 186 Args: 187 filename: Name of file to parse. 188 189 Returns: 190 Build id parsed from the file based on pattern 191 Returns None if pattern not found in file 192 """ 193 with open(filename) as build_info_file: 194 for line in build_info_file: 195 match = re.search(_VERSION_PATTERN, line) 196 if match: 197 return match.group(1) 198 return None 199 200 201def _FetchBuildIdFromFile(cfg, build_target, build_id, filename): 202 """Parse and fetch build id from a file based on a pattern. 203 204 Verify if one of the system image or emulator binary build id is missing. 205 If found missing, then update according to the resource file. 206 207 Args: 208 cfg: An AcloudConfig instance. 209 build_target: Target name. 210 build_id: Build id, a string, e.g. "2263051", "P2804227" 211 filename: Name of file containing the build info. 212 213 Returns: 214 A build id or None 215 """ 216 build_client = android_build_client.AndroidBuildClient( 217 auth.CreateCredentials(cfg)) 218 219 with utils.TempDir() as tempdir: 220 temp_filename = os.path.join(tempdir, filename) 221 build_client.DownloadArtifact(build_target, 222 build_id, 223 filename, 224 temp_filename) 225 226 return ParseBuildInfo(temp_filename) 227 228 229#pylint: disable=too-many-locals 230def CreateDevices(avd_spec=None, 231 cfg=None, 232 build_target=None, 233 build_id=None, 234 emulator_build_id=None, 235 emulator_branch=None, 236 kernel_build_id=None, 237 kernel_branch=None, 238 kernel_build_target=None, 239 gpu=None, 240 num=1, 241 serial_log_file=None, 242 autoconnect=False, 243 branch=None, 244 tags=None, 245 report_internal_ip=False): 246 """Create one or multiple Goldfish devices. 247 248 Args: 249 avd_spec: An AVDSpec instance. 250 cfg: An AcloudConfig instance. 251 build_target: String, the build target, e.g. aosp_x86-eng. 252 build_id: String, Build id, e.g. "2263051", "P2804227" 253 branch: String, Branch name for system image. 254 emulator_build_id: String, emulator build id. 255 emulator_branch: String, Emulator branch name. 256 gpu: String, GPU to attach to the device or None. e.g. "nvidia-k80" 257 kernel_build_id: Kernel build id, a string. 258 kernel_branch: Kernel branch name, a string. 259 kernel_build_target: Kernel build artifact, a string. 260 num: Integer, Number of devices to create. 261 serial_log_file: String, A path to a file where serial output should 262 be saved to. 263 autoconnect: Boolean, Create ssh tunnel(s) and adb connect after device 264 creation. 265 branch: String, Branch name for system image. 266 tags: A list of tags to associate with the instance. e.g. 267 ["http-server", "https-server"] 268 report_internal_ip: Boolean to report the internal ip instead of 269 external ip. 270 271 Returns: 272 A Report instance. 273 """ 274 client_adb_port = None 275 boot_timeout_secs = None 276 if avd_spec: 277 cfg = avd_spec.cfg 278 build_target = avd_spec.remote_image[constants.BUILD_TARGET] 279 build_id = avd_spec.remote_image[constants.BUILD_ID] 280 branch = avd_spec.remote_image[constants.BUILD_BRANCH] 281 num = avd_spec.num 282 emulator_build_id = avd_spec.emulator_build_id 283 gpu = avd_spec.gpu 284 serial_log_file = avd_spec.serial_log_file 285 autoconnect = avd_spec.autoconnect 286 report_internal_ip = avd_spec.report_internal_ip 287 client_adb_port = avd_spec.client_adb_port 288 boot_timeout_secs = avd_spec.boot_timeout_secs 289 290 # If emulator_build_id and emulator_branch is None, retrieve emulator 291 # build id from platform build emulator-info.txt artifact 292 # Example: require version-emulator=5292001 293 if not emulator_build_id and not emulator_branch: 294 logger.info("emulator_build_id not provided. " 295 "Try to get %s from build %s/%s.", _EMULATOR_INFO_FILENAME, 296 build_id, build_target) 297 emulator_build_id = _FetchBuildIdFromFile(cfg, 298 build_target, 299 build_id, 300 _EMULATOR_INFO_FILENAME) 301 302 if not emulator_build_id: 303 raise errors.CommandArgError("Emulator build id not found " 304 "in %s" % _EMULATOR_INFO_FILENAME) 305 306 # If build_id and branch is None, retrieve build_id from 307 # emulator build android-info.txt artifact 308 # Example: version-sysimage-git_pi-dev-sdk_gphone_x86_64-userdebug=4833817 309 if not build_id and not branch: 310 build_id = _FetchBuildIdFromFile(cfg, 311 cfg.emulator_build_target, 312 emulator_build_id, 313 _SYSIMAGE_INFO_FILENAME) 314 315 if not build_id: 316 raise errors.CommandArgError("Emulator system image build id not found " 317 "in %s" % _SYSIMAGE_INFO_FILENAME) 318 logger.info( 319 "Creating a goldfish device in project %s, build_target: %s, " 320 "build_id: %s, emulator_bid: %s, kernel_build_id: %s, " 321 "kernel_branch: %s, kernel_build_target: %s, GPU: %s, num: %s, " 322 "serial_log_file: %s, " 323 "autoconnect: %s", cfg.project, build_target, build_id, 324 emulator_build_id, kernel_build_id, kernel_branch, kernel_build_target, 325 gpu, num, serial_log_file, autoconnect) 326 327 device_factory = GoldfishDeviceFactory( 328 cfg, build_target, build_id, 329 cfg.emulator_build_target, 330 emulator_build_id, gpu=gpu, 331 avd_spec=avd_spec, tags=tags, 332 branch=branch, 333 emulator_branch=emulator_branch, 334 kernel_build_id=kernel_build_id, 335 kernel_branch=kernel_branch, 336 kernel_build_target=kernel_build_target) 337 338 return common_operations.CreateDevices("create_gf", cfg, device_factory, 339 num, constants.TYPE_GF, 340 report_internal_ip, autoconnect, 341 serial_log_file, client_adb_port, 342 boot_timeout_secs) 343