1*800a58d9SAndroid Build Coastguard Worker#!/usr/bin/env python 2*800a58d9SAndroid Build Coastguard Worker# 3*800a58d9SAndroid Build Coastguard Worker# Copyright 2016 - The Android Open Source Project 4*800a58d9SAndroid Build Coastguard Worker# 5*800a58d9SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*800a58d9SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*800a58d9SAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*800a58d9SAndroid Build Coastguard Worker# 9*800a58d9SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*800a58d9SAndroid Build Coastguard Worker# 11*800a58d9SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*800a58d9SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*800a58d9SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*800a58d9SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*800a58d9SAndroid Build Coastguard Worker# limitations under the License. 16*800a58d9SAndroid Build Coastguard Worker"""A client that manages Android compute engine instances. 17*800a58d9SAndroid Build Coastguard Worker 18*800a58d9SAndroid Build Coastguard Worker** AndroidComputeClient ** 19*800a58d9SAndroid Build Coastguard Worker 20*800a58d9SAndroid Build Coastguard WorkerAndroidComputeClient derives from ComputeClient. It manges a google 21*800a58d9SAndroid Build Coastguard Workercompute engine project that is setup for running Android instances. 22*800a58d9SAndroid Build Coastguard WorkerIt knows how to create android GCE images and instances. 23*800a58d9SAndroid Build Coastguard Worker 24*800a58d9SAndroid Build Coastguard Worker** Class hierarchy ** 25*800a58d9SAndroid Build Coastguard Worker 26*800a58d9SAndroid Build Coastguard Worker base_cloud_client.BaseCloudApiClient 27*800a58d9SAndroid Build Coastguard Worker ^ 28*800a58d9SAndroid Build Coastguard Worker | 29*800a58d9SAndroid Build Coastguard Worker gcompute_client.ComputeClient 30*800a58d9SAndroid Build Coastguard Worker ^ 31*800a58d9SAndroid Build Coastguard Worker | 32*800a58d9SAndroid Build Coastguard Worker gcompute_client.AndroidComputeClient 33*800a58d9SAndroid Build Coastguard Worker""" 34*800a58d9SAndroid Build Coastguard Worker 35*800a58d9SAndroid Build Coastguard Workerimport getpass 36*800a58d9SAndroid Build Coastguard Workerimport logging 37*800a58d9SAndroid Build Coastguard Workerimport os 38*800a58d9SAndroid Build Coastguard Workerimport uuid 39*800a58d9SAndroid Build Coastguard Worker 40*800a58d9SAndroid Build Coastguard Workerfrom acloud import errors 41*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal import constants 42*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import gcompute_client 43*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import utils 44*800a58d9SAndroid Build Coastguard Workerfrom acloud.public import config 45*800a58d9SAndroid Build Coastguard Worker 46*800a58d9SAndroid Build Coastguard Worker 47*800a58d9SAndroid Build Coastguard Workerlogger = logging.getLogger(__name__) 48*800a58d9SAndroid Build Coastguard Worker_ZONE = "zone" 49*800a58d9SAndroid Build Coastguard Worker_VERSION = "version" 50*800a58d9SAndroid Build Coastguard Worker 51*800a58d9SAndroid Build Coastguard Worker 52*800a58d9SAndroid Build Coastguard Workerclass AndroidComputeClient(gcompute_client.ComputeClient): 53*800a58d9SAndroid Build Coastguard Worker """Client that manages Anadroid Virtual Device.""" 54*800a58d9SAndroid Build Coastguard Worker IMAGE_NAME_FMT = "img-{uuid}-{build_id}-{build_target}" 55*800a58d9SAndroid Build Coastguard Worker DATA_DISK_NAME_FMT = "data-{instance}" 56*800a58d9SAndroid Build Coastguard Worker BOOT_COMPLETED_MSG = "VIRTUAL_DEVICE_BOOT_COMPLETED" 57*800a58d9SAndroid Build Coastguard Worker BOOT_STARTED_MSG = "VIRTUAL_DEVICE_BOOT_STARTED" 58*800a58d9SAndroid Build Coastguard Worker BOOT_CHECK_INTERVAL_SECS = 10 59*800a58d9SAndroid Build Coastguard Worker OPERATION_TIMEOUT_SECS = 20 * 60 # Override parent value, 20 mins 60*800a58d9SAndroid Build Coastguard Worker 61*800a58d9SAndroid Build Coastguard Worker NAME_LENGTH_LIMIT = 63 62*800a58d9SAndroid Build Coastguard Worker # If the generated name ends with '-', replace it with REPLACER. 63*800a58d9SAndroid Build Coastguard Worker REPLACER = "e" 64*800a58d9SAndroid Build Coastguard Worker 65*800a58d9SAndroid Build Coastguard Worker def __init__(self, acloud_config, oauth2_credentials): 66*800a58d9SAndroid Build Coastguard Worker """Initialize. 67*800a58d9SAndroid Build Coastguard Worker 68*800a58d9SAndroid Build Coastguard Worker Args: 69*800a58d9SAndroid Build Coastguard Worker acloud_config: An AcloudConfig object. 70*800a58d9SAndroid Build Coastguard Worker oauth2_credentials: An oauth2client.OAuth2Credentials instance. 71*800a58d9SAndroid Build Coastguard Worker """ 72*800a58d9SAndroid Build Coastguard Worker super().__init__(acloud_config, oauth2_credentials) 73*800a58d9SAndroid Build Coastguard Worker self._zone = acloud_config.zone 74*800a58d9SAndroid Build Coastguard Worker self._machine_type = acloud_config.machine_type 75*800a58d9SAndroid Build Coastguard Worker self._min_machine_size = acloud_config.min_machine_size 76*800a58d9SAndroid Build Coastguard Worker self._network = acloud_config.network 77*800a58d9SAndroid Build Coastguard Worker self._orientation = acloud_config.orientation 78*800a58d9SAndroid Build Coastguard Worker self._resolution = acloud_config.resolution 79*800a58d9SAndroid Build Coastguard Worker self._metadata = acloud_config.metadata_variable.copy() 80*800a58d9SAndroid Build Coastguard Worker self._ssh_public_key_path = acloud_config.ssh_public_key_path 81*800a58d9SAndroid Build Coastguard Worker self._launch_args = acloud_config.launch_args 82*800a58d9SAndroid Build Coastguard Worker self._instance_name_pattern = acloud_config.instance_name_pattern 83*800a58d9SAndroid Build Coastguard Worker self._gce_hostname = None 84*800a58d9SAndroid Build Coastguard Worker self._AddPerInstanceSshkey() 85*800a58d9SAndroid Build Coastguard Worker self._dict_report = {_ZONE: self._zone, 86*800a58d9SAndroid Build Coastguard Worker _VERSION: config.GetVersion()} 87*800a58d9SAndroid Build Coastguard Worker 88*800a58d9SAndroid Build Coastguard Worker # TODO(147047953): New args to contorl zone metrics check. 89*800a58d9SAndroid Build Coastguard Worker def _VerifyZoneByQuota(self): 90*800a58d9SAndroid Build Coastguard Worker """Verify the zone must have enough quota to create instance. 91*800a58d9SAndroid Build Coastguard Worker 92*800a58d9SAndroid Build Coastguard Worker Returns: 93*800a58d9SAndroid Build Coastguard Worker Boolean, True if zone have enough quota to create instance. 94*800a58d9SAndroid Build Coastguard Worker 95*800a58d9SAndroid Build Coastguard Worker Raises: 96*800a58d9SAndroid Build Coastguard Worker errors.CheckGCEZonesQuotaError: the zone doesn't have enough quota. 97*800a58d9SAndroid Build Coastguard Worker """ 98*800a58d9SAndroid Build Coastguard Worker if self.EnoughMetricsInZone(self._zone): 99*800a58d9SAndroid Build Coastguard Worker return True 100*800a58d9SAndroid Build Coastguard Worker raise errors.CheckGCEZonesQuotaError( 101*800a58d9SAndroid Build Coastguard Worker "There is no enough quota in zone: %s" % self._zone) 102*800a58d9SAndroid Build Coastguard Worker 103*800a58d9SAndroid Build Coastguard Worker def _AddPerInstanceSshkey(self): 104*800a58d9SAndroid Build Coastguard Worker """Add per-instance ssh key. 105*800a58d9SAndroid Build Coastguard Worker 106*800a58d9SAndroid Build Coastguard Worker Assign the ssh publick key to instacne then use ssh command to 107*800a58d9SAndroid Build Coastguard Worker control remote instance via the ssh publick key. Added sshkey for two 108*800a58d9SAndroid Build Coastguard Worker users. One is vsoc01, another is current user. 109*800a58d9SAndroid Build Coastguard Worker 110*800a58d9SAndroid Build Coastguard Worker """ 111*800a58d9SAndroid Build Coastguard Worker if self._ssh_public_key_path: 112*800a58d9SAndroid Build Coastguard Worker rsa = self._LoadSshPublicKey(self._ssh_public_key_path) 113*800a58d9SAndroid Build Coastguard Worker logger.info("ssh_public_key_path is specified in config: %s, " 114*800a58d9SAndroid Build Coastguard Worker "will add the key to the instance.", 115*800a58d9SAndroid Build Coastguard Worker self._ssh_public_key_path) 116*800a58d9SAndroid Build Coastguard Worker self._metadata["sshKeys"] = "{0}:{2}\n{1}:{2}".format(getpass.getuser(), 117*800a58d9SAndroid Build Coastguard Worker constants.GCE_USER, 118*800a58d9SAndroid Build Coastguard Worker rsa) 119*800a58d9SAndroid Build Coastguard Worker else: 120*800a58d9SAndroid Build Coastguard Worker logger.warning( 121*800a58d9SAndroid Build Coastguard Worker "ssh_public_key_path is not specified in config, " 122*800a58d9SAndroid Build Coastguard Worker "only project-wide key will be effective.") 123*800a58d9SAndroid Build Coastguard Worker 124*800a58d9SAndroid Build Coastguard Worker @classmethod 125*800a58d9SAndroid Build Coastguard Worker def _FormalizeName(cls, name): 126*800a58d9SAndroid Build Coastguard Worker """Formalize the name to comply with RFC1035. 127*800a58d9SAndroid Build Coastguard Worker 128*800a58d9SAndroid Build Coastguard Worker The name must be 1-63 characters long and match the regular expression 129*800a58d9SAndroid Build Coastguard Worker [a-z]([-a-z0-9]*[a-z0-9])? which means the first character must be a 130*800a58d9SAndroid Build Coastguard Worker lowercase letter, and all following characters must be a dash, 131*800a58d9SAndroid Build Coastguard Worker lowercase letter, or digit, except the last character, which cannot be 132*800a58d9SAndroid Build Coastguard Worker a dash. 133*800a58d9SAndroid Build Coastguard Worker 134*800a58d9SAndroid Build Coastguard Worker Args: 135*800a58d9SAndroid Build Coastguard Worker name: A string. 136*800a58d9SAndroid Build Coastguard Worker 137*800a58d9SAndroid Build Coastguard Worker Returns: 138*800a58d9SAndroid Build Coastguard Worker name: A string that complies with RFC1035. 139*800a58d9SAndroid Build Coastguard Worker """ 140*800a58d9SAndroid Build Coastguard Worker name = name.replace("_", "-").replace(".", "-").lower() 141*800a58d9SAndroid Build Coastguard Worker name = name[:cls.NAME_LENGTH_LIMIT] 142*800a58d9SAndroid Build Coastguard Worker if name[-1] == "-": 143*800a58d9SAndroid Build Coastguard Worker name = name[:-1] + cls.REPLACER 144*800a58d9SAndroid Build Coastguard Worker return name 145*800a58d9SAndroid Build Coastguard Worker 146*800a58d9SAndroid Build Coastguard Worker def _CheckMachineSize(self): 147*800a58d9SAndroid Build Coastguard Worker """Check machine size. 148*800a58d9SAndroid Build Coastguard Worker 149*800a58d9SAndroid Build Coastguard Worker Check if the desired machine type |self._machine_type| meets 150*800a58d9SAndroid Build Coastguard Worker the requirement of minimum machine size specified as 151*800a58d9SAndroid Build Coastguard Worker |self._min_machine_size|. 152*800a58d9SAndroid Build Coastguard Worker 153*800a58d9SAndroid Build Coastguard Worker Raises: 154*800a58d9SAndroid Build Coastguard Worker errors.DriverError: if check fails. 155*800a58d9SAndroid Build Coastguard Worker """ 156*800a58d9SAndroid Build Coastguard Worker if self.CompareMachineSize(self._machine_type, self._min_machine_size, 157*800a58d9SAndroid Build Coastguard Worker self._zone) < 0: 158*800a58d9SAndroid Build Coastguard Worker raise errors.DriverError( 159*800a58d9SAndroid Build Coastguard Worker "%s does not meet the minimum required machine size %s" % 160*800a58d9SAndroid Build Coastguard Worker (self._machine_type, self._min_machine_size)) 161*800a58d9SAndroid Build Coastguard Worker 162*800a58d9SAndroid Build Coastguard Worker @classmethod 163*800a58d9SAndroid Build Coastguard Worker def GenerateImageName(cls, build_target=None, build_id=None): 164*800a58d9SAndroid Build Coastguard Worker """Generate an image name given build_target, build_id. 165*800a58d9SAndroid Build Coastguard Worker 166*800a58d9SAndroid Build Coastguard Worker Args: 167*800a58d9SAndroid Build Coastguard Worker build_target: Target name, e.g. "aosp_cf_x86_64_phone-userdebug" 168*800a58d9SAndroid Build Coastguard Worker build_id: Build id, a string, e.g. "2263051", "P2804227" 169*800a58d9SAndroid Build Coastguard Worker 170*800a58d9SAndroid Build Coastguard Worker Returns: 171*800a58d9SAndroid Build Coastguard Worker A string, representing image name. 172*800a58d9SAndroid Build Coastguard Worker """ 173*800a58d9SAndroid Build Coastguard Worker if not build_target and not build_id: 174*800a58d9SAndroid Build Coastguard Worker return "image-" + uuid.uuid4().hex 175*800a58d9SAndroid Build Coastguard Worker name = cls.IMAGE_NAME_FMT.format( 176*800a58d9SAndroid Build Coastguard Worker build_target=build_target, 177*800a58d9SAndroid Build Coastguard Worker build_id=build_id, 178*800a58d9SAndroid Build Coastguard Worker uuid=uuid.uuid4().hex[:8]) 179*800a58d9SAndroid Build Coastguard Worker return cls._FormalizeName(name) 180*800a58d9SAndroid Build Coastguard Worker 181*800a58d9SAndroid Build Coastguard Worker @classmethod 182*800a58d9SAndroid Build Coastguard Worker def GetDataDiskName(cls, instance): 183*800a58d9SAndroid Build Coastguard Worker """Get data disk name for an instance. 184*800a58d9SAndroid Build Coastguard Worker 185*800a58d9SAndroid Build Coastguard Worker Args: 186*800a58d9SAndroid Build Coastguard Worker instance: An instance_name. 187*800a58d9SAndroid Build Coastguard Worker 188*800a58d9SAndroid Build Coastguard Worker Returns: 189*800a58d9SAndroid Build Coastguard Worker The corresponding data disk name. 190*800a58d9SAndroid Build Coastguard Worker """ 191*800a58d9SAndroid Build Coastguard Worker name = cls.DATA_DISK_NAME_FMT.format(instance=instance) 192*800a58d9SAndroid Build Coastguard Worker return cls._FormalizeName(name) 193*800a58d9SAndroid Build Coastguard Worker 194*800a58d9SAndroid Build Coastguard Worker def GenerateInstanceName(self, build_target=None, build_id=None): 195*800a58d9SAndroid Build Coastguard Worker """Generate an instance name given build_target, build_id. 196*800a58d9SAndroid Build Coastguard Worker 197*800a58d9SAndroid Build Coastguard Worker Target is not used as instance name has a length limit. 198*800a58d9SAndroid Build Coastguard Worker 199*800a58d9SAndroid Build Coastguard Worker Args: 200*800a58d9SAndroid Build Coastguard Worker build_target: Target name, e.g. "aosp_cf_x86_64_phone-userdebug" 201*800a58d9SAndroid Build Coastguard Worker build_id: Build id, a string, e.g. "2263051", "P2804227" 202*800a58d9SAndroid Build Coastguard Worker 203*800a58d9SAndroid Build Coastguard Worker Returns: 204*800a58d9SAndroid Build Coastguard Worker A string, representing instance name. 205*800a58d9SAndroid Build Coastguard Worker """ 206*800a58d9SAndroid Build Coastguard Worker name = self._instance_name_pattern.format(build_target=build_target, 207*800a58d9SAndroid Build Coastguard Worker build_id=build_id, 208*800a58d9SAndroid Build Coastguard Worker uuid=uuid.uuid4().hex[:8]) 209*800a58d9SAndroid Build Coastguard Worker return self._FormalizeName(name) 210*800a58d9SAndroid Build Coastguard Worker 211*800a58d9SAndroid Build Coastguard Worker def CreateDisk(self, 212*800a58d9SAndroid Build Coastguard Worker disk_name, 213*800a58d9SAndroid Build Coastguard Worker source_image, 214*800a58d9SAndroid Build Coastguard Worker size_gb, 215*800a58d9SAndroid Build Coastguard Worker zone=None, 216*800a58d9SAndroid Build Coastguard Worker source_project=None, 217*800a58d9SAndroid Build Coastguard Worker disk_type=gcompute_client.PersistentDiskType.STANDARD): 218*800a58d9SAndroid Build Coastguard Worker """Create a gce disk. 219*800a58d9SAndroid Build Coastguard Worker 220*800a58d9SAndroid Build Coastguard Worker Args: 221*800a58d9SAndroid Build Coastguard Worker disk_name: String, name of disk. 222*800a58d9SAndroid Build Coastguard Worker source_image: String, name to the image name. 223*800a58d9SAndroid Build Coastguard Worker size_gb: Integer, size in gigabytes. 224*800a58d9SAndroid Build Coastguard Worker zone: String, name of the zone, e.g. us-central1-b. 225*800a58d9SAndroid Build Coastguard Worker source_project: String, required if the image is located in a different 226*800a58d9SAndroid Build Coastguard Worker project. 227*800a58d9SAndroid Build Coastguard Worker disk_type: String, a value from PersistentDiskType, STANDARD 228*800a58d9SAndroid Build Coastguard Worker for regular hard disk or SSD for solid state disk. 229*800a58d9SAndroid Build Coastguard Worker """ 230*800a58d9SAndroid Build Coastguard Worker if self.CheckDiskExists(disk_name, self._zone): 231*800a58d9SAndroid Build Coastguard Worker raise errors.DriverError( 232*800a58d9SAndroid Build Coastguard Worker "Failed to create disk %s, already exists." % disk_name) 233*800a58d9SAndroid Build Coastguard Worker if source_image and not self.CheckImageExists(source_image): 234*800a58d9SAndroid Build Coastguard Worker raise errors.DriverError( 235*800a58d9SAndroid Build Coastguard Worker "Failed to create disk %s, source image %s does not exist." % 236*800a58d9SAndroid Build Coastguard Worker (disk_name, source_image)) 237*800a58d9SAndroid Build Coastguard Worker super().CreateDisk( 238*800a58d9SAndroid Build Coastguard Worker disk_name, 239*800a58d9SAndroid Build Coastguard Worker source_image=source_image, 240*800a58d9SAndroid Build Coastguard Worker size_gb=size_gb, 241*800a58d9SAndroid Build Coastguard Worker zone=zone or self._zone) 242*800a58d9SAndroid Build Coastguard Worker 243*800a58d9SAndroid Build Coastguard Worker @staticmethod 244*800a58d9SAndroid Build Coastguard Worker def _LoadSshPublicKey(ssh_public_key_path): 245*800a58d9SAndroid Build Coastguard Worker """Load the content of ssh public key from a file. 246*800a58d9SAndroid Build Coastguard Worker 247*800a58d9SAndroid Build Coastguard Worker Args: 248*800a58d9SAndroid Build Coastguard Worker ssh_public_key_path: String, path to the public key file. 249*800a58d9SAndroid Build Coastguard Worker E.g. ~/.ssh/acloud_rsa.pub 250*800a58d9SAndroid Build Coastguard Worker Returns: 251*800a58d9SAndroid Build Coastguard Worker String, content of the file. 252*800a58d9SAndroid Build Coastguard Worker 253*800a58d9SAndroid Build Coastguard Worker Raises: 254*800a58d9SAndroid Build Coastguard Worker errors.DriverError if the public key file does not exist 255*800a58d9SAndroid Build Coastguard Worker or the content is not valid. 256*800a58d9SAndroid Build Coastguard Worker """ 257*800a58d9SAndroid Build Coastguard Worker key_path = os.path.expanduser(ssh_public_key_path) 258*800a58d9SAndroid Build Coastguard Worker if not os.path.exists(key_path): 259*800a58d9SAndroid Build Coastguard Worker raise errors.DriverError( 260*800a58d9SAndroid Build Coastguard Worker "SSH public key file %s does not exist." % key_path) 261*800a58d9SAndroid Build Coastguard Worker 262*800a58d9SAndroid Build Coastguard Worker with open(key_path) as f: 263*800a58d9SAndroid Build Coastguard Worker rsa = f.read() 264*800a58d9SAndroid Build Coastguard Worker rsa = rsa.strip() if rsa else rsa 265*800a58d9SAndroid Build Coastguard Worker utils.VerifyRsaPubKey(rsa) 266*800a58d9SAndroid Build Coastguard Worker return rsa 267*800a58d9SAndroid Build Coastguard Worker 268*800a58d9SAndroid Build Coastguard Worker # pylint: disable=too-many-locals, arguments-differ 269*800a58d9SAndroid Build Coastguard Worker @utils.TimeExecute("Creating GCE Instance") 270*800a58d9SAndroid Build Coastguard Worker def CreateInstance(self, 271*800a58d9SAndroid Build Coastguard Worker instance, 272*800a58d9SAndroid Build Coastguard Worker image_name, 273*800a58d9SAndroid Build Coastguard Worker machine_type=None, 274*800a58d9SAndroid Build Coastguard Worker metadata=None, 275*800a58d9SAndroid Build Coastguard Worker network=None, 276*800a58d9SAndroid Build Coastguard Worker zone=None, 277*800a58d9SAndroid Build Coastguard Worker disk_args=None, 278*800a58d9SAndroid Build Coastguard Worker image_project=None, 279*800a58d9SAndroid Build Coastguard Worker gpu=None, 280*800a58d9SAndroid Build Coastguard Worker extra_disk_name=None, 281*800a58d9SAndroid Build Coastguard Worker avd_spec=None, 282*800a58d9SAndroid Build Coastguard Worker extra_scopes=None, 283*800a58d9SAndroid Build Coastguard Worker tags=None): 284*800a58d9SAndroid Build Coastguard Worker """Create a gce instance with a gce image. 285*800a58d9SAndroid Build Coastguard Worker 286*800a58d9SAndroid Build Coastguard Worker Args: 287*800a58d9SAndroid Build Coastguard Worker instance: String, instance name. 288*800a58d9SAndroid Build Coastguard Worker image_name: String, source image used to create this disk. 289*800a58d9SAndroid Build Coastguard Worker machine_type: String, representing machine_type, 290*800a58d9SAndroid Build Coastguard Worker e.g. "n1-standard-1" 291*800a58d9SAndroid Build Coastguard Worker metadata: Dict, maps a metadata name to its value. 292*800a58d9SAndroid Build Coastguard Worker network: String, representing network name, e.g. "default" 293*800a58d9SAndroid Build Coastguard Worker zone: String, representing zone name, e.g. "us-central1-f" 294*800a58d9SAndroid Build Coastguard Worker disk_args: A list of extra disk args (strings), see _GetDiskArgs 295*800a58d9SAndroid Build Coastguard Worker for example, if None, will create a disk using the given 296*800a58d9SAndroid Build Coastguard Worker image. 297*800a58d9SAndroid Build Coastguard Worker image_project: String, name of the project where the image 298*800a58d9SAndroid Build Coastguard Worker belongs. Assume the default project if None. 299*800a58d9SAndroid Build Coastguard Worker gpu: String, type of gpu to attach. e.g. "nvidia-tesla-k80", if 300*800a58d9SAndroid Build Coastguard Worker None no gpus will be attached. For more details see: 301*800a58d9SAndroid Build Coastguard Worker https://cloud.google.com/compute/docs/gpus/add-gpus 302*800a58d9SAndroid Build Coastguard Worker extra_disk_name: String,the name of the extra disk to attach. 303*800a58d9SAndroid Build Coastguard Worker avd_spec: AVDSpec object that tells us what we're going to create. 304*800a58d9SAndroid Build Coastguard Worker extra_scopes: List, extra scopes (strings) to be passed to the 305*800a58d9SAndroid Build Coastguard Worker instance. 306*800a58d9SAndroid Build Coastguard Worker tags: A list of tags to associate with the instance. e.g. 307*800a58d9SAndroid Build Coastguard Worker ["http-server", "https-server"] 308*800a58d9SAndroid Build Coastguard Worker """ 309*800a58d9SAndroid Build Coastguard Worker self._CheckMachineSize() 310*800a58d9SAndroid Build Coastguard Worker disk_args = self._GetDiskArgs(instance, image_name) 311*800a58d9SAndroid Build Coastguard Worker metadata = self._metadata.copy() 312*800a58d9SAndroid Build Coastguard Worker metadata["cfg_sta_display_resolution"] = self._resolution 313*800a58d9SAndroid Build Coastguard Worker metadata["t_force_orientation"] = self._orientation 314*800a58d9SAndroid Build Coastguard Worker metadata[constants.INS_KEY_AVD_TYPE] = avd_spec.avd_type 315*800a58d9SAndroid Build Coastguard Worker 316*800a58d9SAndroid Build Coastguard Worker # Use another METADATA_DISPLAY to record resolution which will be 317*800a58d9SAndroid Build Coastguard Worker # retrieved in acloud list cmd. We try not to use cvd_01_x_res 318*800a58d9SAndroid Build Coastguard Worker # since cvd_01_xxx metadata is going to deprecated by cuttlefish. 319*800a58d9SAndroid Build Coastguard Worker metadata[constants.INS_KEY_DISPLAY] = ("%sx%s (%s)" % ( 320*800a58d9SAndroid Build Coastguard Worker avd_spec.hw_property[constants.HW_X_RES], 321*800a58d9SAndroid Build Coastguard Worker avd_spec.hw_property[constants.HW_Y_RES], 322*800a58d9SAndroid Build Coastguard Worker avd_spec.hw_property[constants.HW_ALIAS_DPI])) 323*800a58d9SAndroid Build Coastguard Worker 324*800a58d9SAndroid Build Coastguard Worker super().CreateInstance( 325*800a58d9SAndroid Build Coastguard Worker instance, image_name, self._machine_type, metadata, self._network, 326*800a58d9SAndroid Build Coastguard Worker self._zone, disk_args, image_project, gpu, extra_disk_name, 327*800a58d9SAndroid Build Coastguard Worker extra_scopes=extra_scopes, tags=tags) 328*800a58d9SAndroid Build Coastguard Worker 329*800a58d9SAndroid Build Coastguard Worker def CheckBootFailure(self, serial_out, instance): 330*800a58d9SAndroid Build Coastguard Worker """Determine if serial output has indicated any boot failure. 331*800a58d9SAndroid Build Coastguard Worker 332*800a58d9SAndroid Build Coastguard Worker Subclass has to define this function to detect failures 333*800a58d9SAndroid Build Coastguard Worker in the boot process 334*800a58d9SAndroid Build Coastguard Worker 335*800a58d9SAndroid Build Coastguard Worker Args: 336*800a58d9SAndroid Build Coastguard Worker serial_out: string 337*800a58d9SAndroid Build Coastguard Worker instance: string, instance name. 338*800a58d9SAndroid Build Coastguard Worker 339*800a58d9SAndroid Build Coastguard Worker Raises: 340*800a58d9SAndroid Build Coastguard Worker Raises errors.DeviceBootError exception if a failure is detected. 341*800a58d9SAndroid Build Coastguard Worker """ 342*800a58d9SAndroid Build Coastguard Worker pass 343*800a58d9SAndroid Build Coastguard Worker 344*800a58d9SAndroid Build Coastguard Worker def CheckBoot(self, instance): 345*800a58d9SAndroid Build Coastguard Worker """Check once to see if boot completes. 346*800a58d9SAndroid Build Coastguard Worker 347*800a58d9SAndroid Build Coastguard Worker Args: 348*800a58d9SAndroid Build Coastguard Worker instance: string, instance name. 349*800a58d9SAndroid Build Coastguard Worker 350*800a58d9SAndroid Build Coastguard Worker Returns: 351*800a58d9SAndroid Build Coastguard Worker True if the BOOT_COMPLETED_MSG or BOOT_STARTED_MSG appears in serial 352*800a58d9SAndroid Build Coastguard Worker port output, otherwise False. 353*800a58d9SAndroid Build Coastguard Worker """ 354*800a58d9SAndroid Build Coastguard Worker try: 355*800a58d9SAndroid Build Coastguard Worker serial_out = self.GetSerialPortOutput(instance=instance, port=1) 356*800a58d9SAndroid Build Coastguard Worker self.CheckBootFailure(serial_out, instance) 357*800a58d9SAndroid Build Coastguard Worker return ((self.BOOT_COMPLETED_MSG in serial_out) 358*800a58d9SAndroid Build Coastguard Worker or (self.BOOT_STARTED_MSG in serial_out)) 359*800a58d9SAndroid Build Coastguard Worker except errors.HttpError as e: 360*800a58d9SAndroid Build Coastguard Worker if e.code == 400: 361*800a58d9SAndroid Build Coastguard Worker logger.debug("CheckBoot: Instance is not ready yet %s", str(e)) 362*800a58d9SAndroid Build Coastguard Worker return False 363*800a58d9SAndroid Build Coastguard Worker logger.error("Unexpected http status: %d, %s", e.code, e.message) 364*800a58d9SAndroid Build Coastguard Worker raise 365*800a58d9SAndroid Build Coastguard Worker 366*800a58d9SAndroid Build Coastguard Worker def WaitForBoot(self, instance, boot_timeout_secs=None): 367*800a58d9SAndroid Build Coastguard Worker """Wait for boot to completes or hit timeout. 368*800a58d9SAndroid Build Coastguard Worker 369*800a58d9SAndroid Build Coastguard Worker Args: 370*800a58d9SAndroid Build Coastguard Worker instance: string, instance name. 371*800a58d9SAndroid Build Coastguard Worker boot_timeout_secs: Integer, the maximum time in seconds used to 372*800a58d9SAndroid Build Coastguard Worker wait for the AVD to boot. 373*800a58d9SAndroid Build Coastguard Worker """ 374*800a58d9SAndroid Build Coastguard Worker boot_timeout_secs = boot_timeout_secs or constants.DEFAULT_CF_BOOT_TIMEOUT 375*800a58d9SAndroid Build Coastguard Worker logger.info("Waiting for instance to boot up %s for %s secs", 376*800a58d9SAndroid Build Coastguard Worker instance, boot_timeout_secs) 377*800a58d9SAndroid Build Coastguard Worker timeout_exception = errors.DeviceBootTimeoutError( 378*800a58d9SAndroid Build Coastguard Worker "Device %s did not finish on boot within timeout (%s secs)" % 379*800a58d9SAndroid Build Coastguard Worker (instance, boot_timeout_secs)) 380*800a58d9SAndroid Build Coastguard Worker utils.PollAndWait( 381*800a58d9SAndroid Build Coastguard Worker func=self.CheckBoot, 382*800a58d9SAndroid Build Coastguard Worker expected_return=True, 383*800a58d9SAndroid Build Coastguard Worker timeout_exception=timeout_exception, 384*800a58d9SAndroid Build Coastguard Worker timeout_secs=boot_timeout_secs, 385*800a58d9SAndroid Build Coastguard Worker sleep_interval_secs=self.BOOT_CHECK_INTERVAL_SECS, 386*800a58d9SAndroid Build Coastguard Worker instance=instance) 387*800a58d9SAndroid Build Coastguard Worker logger.info("Instance boot completed: %s", instance) 388*800a58d9SAndroid Build Coastguard Worker 389*800a58d9SAndroid Build Coastguard Worker def GetInstanceIP(self, instance, zone=None): 390*800a58d9SAndroid Build Coastguard Worker """Get Instance IP given instance name. 391*800a58d9SAndroid Build Coastguard Worker 392*800a58d9SAndroid Build Coastguard Worker Args: 393*800a58d9SAndroid Build Coastguard Worker instance: String, representing instance name. 394*800a58d9SAndroid Build Coastguard Worker zone: String, representing zone name, e.g. "us-central1-f" 395*800a58d9SAndroid Build Coastguard Worker 396*800a58d9SAndroid Build Coastguard Worker Returns: 397*800a58d9SAndroid Build Coastguard Worker ssh.IP object, that stores internal and external ip of the instance. 398*800a58d9SAndroid Build Coastguard Worker """ 399*800a58d9SAndroid Build Coastguard Worker return super().GetInstanceIP(instance, zone or self._zone) 400*800a58d9SAndroid Build Coastguard Worker 401*800a58d9SAndroid Build Coastguard Worker def GetSerialPortOutput(self, instance, zone=None, port=1): 402*800a58d9SAndroid Build Coastguard Worker """Get serial port output. 403*800a58d9SAndroid Build Coastguard Worker 404*800a58d9SAndroid Build Coastguard Worker Args: 405*800a58d9SAndroid Build Coastguard Worker instance: string, instance name. 406*800a58d9SAndroid Build Coastguard Worker zone: String, representing zone name, e.g. "us-central1-f" 407*800a58d9SAndroid Build Coastguard Worker port: int, which COM port to read from, 1-4, default to 1. 408*800a58d9SAndroid Build Coastguard Worker 409*800a58d9SAndroid Build Coastguard Worker Returns: 410*800a58d9SAndroid Build Coastguard Worker String, contents of the output. 411*800a58d9SAndroid Build Coastguard Worker 412*800a58d9SAndroid Build Coastguard Worker Raises: 413*800a58d9SAndroid Build Coastguard Worker errors.DriverError: For malformed response. 414*800a58d9SAndroid Build Coastguard Worker """ 415*800a58d9SAndroid Build Coastguard Worker return super().GetSerialPortOutput( 416*800a58d9SAndroid Build Coastguard Worker instance, zone or self._zone, port) 417*800a58d9SAndroid Build Coastguard Worker 418*800a58d9SAndroid Build Coastguard Worker def ExtendReportData(self, key, value): 419*800a58d9SAndroid Build Coastguard Worker """Extend the report data. 420*800a58d9SAndroid Build Coastguard Worker 421*800a58d9SAndroid Build Coastguard Worker Args: 422*800a58d9SAndroid Build Coastguard Worker key: string of key name. 423*800a58d9SAndroid Build Coastguard Worker value: string of data value. 424*800a58d9SAndroid Build Coastguard Worker """ 425*800a58d9SAndroid Build Coastguard Worker self._dict_report.update({key: value}) 426*800a58d9SAndroid Build Coastguard Worker 427*800a58d9SAndroid Build Coastguard Worker @property 428*800a58d9SAndroid Build Coastguard Worker def dict_report(self): 429*800a58d9SAndroid Build Coastguard Worker """Return dict_report""" 430*800a58d9SAndroid Build Coastguard Worker return self._dict_report 431*800a58d9SAndroid Build Coastguard Worker 432*800a58d9SAndroid Build Coastguard Worker @property 433*800a58d9SAndroid Build Coastguard Worker def gce_hostname(self): 434*800a58d9SAndroid Build Coastguard Worker """Return gce_hostname""" 435*800a58d9SAndroid Build Coastguard Worker return self._gce_hostname 436