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 17"""Create cuttlefish instances. 18 19TODO: This module now just contains the skeleton but not the actual logic. 20 Need to fill in the actuall logic. 21""" 22 23import logging 24 25from acloud.public.actions import common_operations 26from acloud.public.actions import base_device_factory 27from acloud.internal import constants 28from acloud.internal.lib import android_build_client 29from acloud.internal.lib import auth 30from acloud.internal.lib import cvd_compute_client 31from acloud.internal.lib import cvd_compute_client_multi_stage 32from acloud.internal.lib import utils 33 34 35logger = logging.getLogger(__name__) 36 37 38class CuttlefishDeviceFactory(base_device_factory.BaseDeviceFactory): 39 """A class that can produce a cuttlefish device. 40 41 Attributes: 42 cfg: An AcloudConfig instance. 43 build_target: String,Target name. 44 build_id: String, Build id, e.g. "2263051", "P2804227" 45 kernel_build_id: String, Kernel build id. 46 gpu: String, GPU to attach to the device or None. e.g. "nvidia-tesla-k80" 47 """ 48 49 LOG_FILES = ["/home/vsoc-01/cuttlefish_runtime/kernel.log", 50 "/home/vsoc-01/cuttlefish_runtime/logcat", 51 "/home/vsoc-01/cuttlefish_runtime/cuttlefish_config.json"] 52 53 #pylint: disable=too-many-locals 54 def __init__(self, cfg, build_target, build_id, branch=None, 55 kernel_build_id=None, kernel_branch=None, 56 kernel_build_target=None, system_branch=None, 57 system_build_id=None, system_build_target=None, 58 boot_timeout_secs=None, ins_timeout_secs=None, 59 report_internal_ip=None, gpu=None): 60 61 self.credentials = auth.CreateCredentials(cfg) 62 63 if cfg.enable_multi_stage: 64 compute_client = cvd_compute_client_multi_stage.CvdComputeClient( 65 cfg, self.credentials, boot_timeout_secs, ins_timeout_secs, 66 report_internal_ip, gpu) 67 else: 68 compute_client = cvd_compute_client.CvdComputeClient( 69 cfg, self.credentials) 70 super(CuttlefishDeviceFactory, self).__init__(compute_client) 71 72 # Private creation parameters 73 self._cfg = cfg 74 self._build_target = build_target 75 self._build_id = build_id 76 self._branch = branch 77 self._kernel_build_id = kernel_build_id 78 self._blank_data_disk_size_gb = cfg.extra_data_disk_size_gb 79 self._extra_scopes = cfg.extra_scopes 80 81 # Configure clients for interaction with GCE/Build servers 82 self._build_client = android_build_client.AndroidBuildClient( 83 self.credentials) 84 85 # Get build_info namedtuple for platform, kernel, system build 86 self.build_info = self._build_client.GetBuildInfo( 87 build_target, build_id, branch) 88 self.kernel_build_info = self._build_client.GetBuildInfo( 89 kernel_build_target or cfg.kernel_build_target, kernel_build_id, 90 kernel_branch) 91 self.system_build_info = self._build_client.GetBuildInfo( 92 system_build_target or build_target, system_build_id, system_branch) 93 94 def GetBuildInfoDict(self): 95 """Get build info dictionary. 96 97 Returns: 98 A build info dictionary. 99 """ 100 build_info_dict = { 101 key: val for key, val in utils.GetDictItems(self.build_info) if val} 102 103 build_info_dict.update( 104 {"kernel_%s" % key: val 105 for key, val in utils.GetDictItems(self.kernel_build_info) if val} 106 ) 107 build_info_dict.update( 108 {"system_%s" % key: val 109 for key, val in utils.GetDictItems(self.system_build_info) if val} 110 ) 111 return build_info_dict 112 113 def GetFailures(self): 114 """Get failures from all devices. 115 116 Returns: 117 A dictionary that contains all the failures. 118 The key is the name of the instance that fails to boot, 119 and the value is an errors.DeviceBootError object. 120 """ 121 return self._compute_client.all_failures 122 123 @staticmethod 124 def _GetGcsBucketBuildId(build_id, release_id): 125 """Get GCS Bucket Build Id. 126 127 Args: 128 build_id: The incremental build id. For example 5325535. 129 release_id: The release build id, None if not a release build. 130 For example AAAA.190220.001. 131 132 Returns: 133 GCS bucket build id. For example: AAAA.190220.001-5325535 134 """ 135 return "-".join([release_id, build_id]) if release_id else build_id 136 137 def CreateInstance(self): 138 """Creates singe configured cuttlefish device. 139 140 Override method from parent class. 141 142 Returns: 143 A string, representing instance name. 144 """ 145 146 # Create host instances for cuttlefish device. Currently one host instance 147 # has one cuttlefish device. In the future, these logics should be modified 148 # to support multiple cuttlefish devices per host instance. 149 instance = self._compute_client.GenerateInstanceName( 150 build_id=self.build_info.build_id, build_target=self._build_target) 151 152 if self._cfg.enable_multi_stage: 153 remote_build_id = self.build_info.build_id 154 else: 155 remote_build_id = self._GetGcsBucketBuildId( 156 self.build_info.build_id, self.build_info.release_build_id) 157 158 if self._cfg.enable_multi_stage: 159 remote_system_build_id = self.system_build_info.build_id 160 else: 161 remote_system_build_id = self._GetGcsBucketBuildId( 162 self.system_build_info.build_id, self.system_build_info.release_build_id) 163 164 host_image_name = self._compute_client.GetHostImageName( 165 self._cfg.stable_host_image_name, 166 self._cfg.stable_host_image_family, 167 self._cfg.stable_host_image_project) 168 # Create an instance from Stable Host Image 169 self._compute_client.CreateInstance( 170 instance=instance, 171 image_name=host_image_name, 172 image_project=self._cfg.stable_host_image_project, 173 build_target=self.build_info.build_target, 174 branch=self.build_info.branch, 175 build_id=remote_build_id, 176 kernel_branch=self.kernel_build_info.branch, 177 kernel_build_id=self.kernel_build_info.build_id, 178 kernel_build_target=self.kernel_build_info.build_target, 179 blank_data_disk_size_gb=self._blank_data_disk_size_gb, 180 extra_scopes=self._extra_scopes, 181 system_build_target=self.system_build_info.build_target, 182 system_branch=self.system_build_info.branch, 183 system_build_id=remote_system_build_id) 184 185 return instance 186 187 188#pylint: disable=too-many-locals 189def CreateDevices(cfg, 190 build_target=None, 191 build_id=None, 192 branch=None, 193 kernel_build_id=None, 194 kernel_branch=None, 195 kernel_build_target=None, 196 system_branch=None, 197 system_build_id=None, 198 system_build_target=None, 199 gpu=None, 200 num=1, 201 serial_log_file=None, 202 autoconnect=False, 203 report_internal_ip=False, 204 boot_timeout_secs=None, 205 ins_timeout_secs=None): 206 """Create one or multiple Cuttlefish devices. 207 208 Args: 209 cfg: An AcloudConfig instance. 210 build_target: String, Target name. 211 build_id: String, Build id, e.g. "2263051", "P2804227" 212 branch: Branch name, a string, e.g. aosp_master 213 kernel_build_id: String, Kernel build id. 214 kernel_branch: String, Kernel branch name. 215 kernel_build_target: String, Kernel build target name. 216 system_branch: Branch name to consume the system.img from, a string. 217 system_build_id: System branch build id, a string. 218 system_build_target: System image build target, a string. 219 gpu: String, GPU to attach to the device or None. e.g. "nvidia-tesla-k80" 220 num: Integer, Number of devices to create. 221 serial_log_file: String, A path to a tar file where serial output should 222 be saved to. 223 autoconnect: Boolean, Create ssh tunnel(s) and adb connect after device 224 creation. 225 report_internal_ip: Boolean to report the internal ip instead of 226 external ip. 227 boot_timeout_secs: Integer, the maximum time in seconds used to wait 228 for the AVD to boot. 229 ins_timeout_secs: Integer, the maximum time in seconds used to wait for 230 the instance ready. 231 232 Returns: 233 A Report instance. 234 """ 235 client_adb_port = None 236 unlock_screen = False 237 wait_for_boot = True 238 logger.info( 239 "Creating a cuttlefish device in project %s, " 240 "build_target: %s, " 241 "build_id: %s, " 242 "branch: %s, " 243 "kernel_build_id: %s, " 244 "kernel_branch: %s, " 245 "kernel_build_target: %s, " 246 "system_branch: %s, " 247 "system_build_id: %s, " 248 "system_build_target: %s, " 249 "gpu: %s" 250 "num: %s, " 251 "serial_log_file: %s, " 252 "autoconnect: %s, " 253 "report_internal_ip: %s", cfg.project, build_target, 254 build_id, branch, kernel_build_id, kernel_branch, kernel_build_target, 255 system_branch, system_build_id, system_build_target, gpu, num, 256 serial_log_file, autoconnect, report_internal_ip) 257 # If multi_stage enable, launch_cvd don't write serial log to instance. So 258 # it doesn't go WaitForBoot function. 259 if cfg.enable_multi_stage: 260 wait_for_boot = False 261 device_factory = CuttlefishDeviceFactory( 262 cfg, build_target, build_id, branch=branch, 263 kernel_build_id=kernel_build_id, kernel_branch=kernel_branch, 264 kernel_build_target=kernel_build_target, system_branch=system_branch, 265 system_build_id=system_build_id, 266 system_build_target=system_build_target, 267 boot_timeout_secs=boot_timeout_secs, 268 ins_timeout_secs=ins_timeout_secs, 269 report_internal_ip=report_internal_ip, 270 gpu=gpu) 271 return common_operations.CreateDevices("create_cf", cfg, device_factory, 272 num, constants.TYPE_CF, 273 report_internal_ip, autoconnect, 274 serial_log_file, client_adb_port, 275 boot_timeout_secs, unlock_screen, 276 wait_for_boot) 277