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 # Create an instance from Stable Host Image 165 self._compute_client.CreateInstance( 166 instance=instance, 167 image_name=self._cfg.stable_host_image_name, 168 image_project=self._cfg.stable_host_image_project, 169 build_target=self.build_info.build_target, 170 branch=self.build_info.branch, 171 build_id=remote_build_id, 172 kernel_branch=self.kernel_build_info.branch, 173 kernel_build_id=self.kernel_build_info.build_id, 174 kernel_build_target=self.kernel_build_info.build_target, 175 blank_data_disk_size_gb=self._blank_data_disk_size_gb, 176 extra_scopes=self._extra_scopes, 177 system_build_target=self.system_build_info.build_target, 178 system_branch=self.system_build_info.branch, 179 system_build_id=remote_system_build_id) 180 181 return instance 182 183 184#pylint: disable=too-many-locals 185def CreateDevices(cfg, 186 build_target=None, 187 build_id=None, 188 branch=None, 189 kernel_build_id=None, 190 kernel_branch=None, 191 kernel_build_target=None, 192 system_branch=None, 193 system_build_id=None, 194 system_build_target=None, 195 gpu=None, 196 num=1, 197 serial_log_file=None, 198 autoconnect=False, 199 report_internal_ip=False, 200 boot_timeout_secs=None, 201 ins_timeout_secs=None): 202 """Create one or multiple Cuttlefish devices. 203 204 Args: 205 cfg: An AcloudConfig instance. 206 build_target: String, Target name. 207 build_id: String, Build id, e.g. "2263051", "P2804227" 208 branch: Branch name, a string, e.g. aosp_master 209 kernel_build_id: String, Kernel build id. 210 kernel_branch: String, Kernel branch name. 211 kernel_build_target: String, Kernel build target name. 212 system_branch: Branch name to consume the system.img from, a string. 213 system_build_id: System branch build id, a string. 214 system_build_target: System image build target, a string. 215 gpu: String, GPU to attach to the device or None. e.g. "nvidia-tesla-k80" 216 num: Integer, Number of devices to create. 217 serial_log_file: String, A path to a tar file where serial output should 218 be saved to. 219 autoconnect: Boolean, Create ssh tunnel(s) and adb connect after device 220 creation. 221 report_internal_ip: Boolean to report the internal ip instead of 222 external ip. 223 boot_timeout_secs: Integer, the maximum time in seconds used to wait 224 for the AVD to boot. 225 ins_timeout_secs: Integer, the maximum time in seconds used to wait for 226 the instance ready. 227 228 Returns: 229 A Report instance. 230 """ 231 client_adb_port = None 232 unlock_screen = False 233 wait_for_boot = True 234 logger.info( 235 "Creating a cuttlefish device in project %s, " 236 "build_target: %s, " 237 "build_id: %s, " 238 "branch: %s, " 239 "kernel_build_id: %s, " 240 "kernel_branch: %s, " 241 "kernel_build_target: %s, " 242 "system_branch: %s, " 243 "system_build_id: %s, " 244 "system_build_target: %s, " 245 "gpu: %s" 246 "num: %s, " 247 "serial_log_file: %s, " 248 "autoconnect: %s, " 249 "report_internal_ip: %s", cfg.project, build_target, 250 build_id, branch, kernel_build_id, kernel_branch, kernel_build_target, 251 system_branch, system_build_id, system_build_target, gpu, num, 252 serial_log_file, autoconnect, report_internal_ip) 253 # If multi_stage enable, launch_cvd don't write serial log to instance. So 254 # it doesn't go WaitForBoot function. 255 if cfg.enable_multi_stage: 256 wait_for_boot = False 257 device_factory = CuttlefishDeviceFactory( 258 cfg, build_target, build_id, branch=branch, 259 kernel_build_id=kernel_build_id, kernel_branch=kernel_branch, 260 kernel_build_target=kernel_build_target, system_branch=system_branch, 261 system_build_id=system_build_id, 262 system_build_target=system_build_target, 263 boot_timeout_secs=boot_timeout_secs, 264 ins_timeout_secs=ins_timeout_secs, 265 report_internal_ip=report_internal_ip, 266 gpu=gpu) 267 return common_operations.CreateDevices("create_cf", cfg, device_factory, 268 num, constants.TYPE_CF, 269 report_internal_ip, autoconnect, 270 serial_log_file, client_adb_port, 271 boot_timeout_secs, unlock_screen, 272 wait_for_boot) 273