Kevin Cheng | b596388 | 2018-05-09 00:06:27 -0700 | [diff] [blame^] | 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 | """A client that manages Goldfish Virtual Device on compute engine. |
| 18 | |
| 19 | ** GoldfishComputeClient ** |
| 20 | |
| 21 | GoldfishComputeClient derives from AndroidComputeClient. It manges a google |
| 22 | compute engine project that is setup for running Goldfish Virtual Devices. |
| 23 | It knows how to create a host instance from a Goldfish Stable Host Image, fetch |
| 24 | Android build, an emulator build, and start Android within the host instance. |
| 25 | |
| 26 | ** Class hierarchy ** |
| 27 | |
| 28 | base_cloud_client.BaseCloudApiClient |
| 29 | ^ |
| 30 | | |
| 31 | gcompute_client.ComputeClient |
| 32 | ^ |
| 33 | | |
| 34 | android_compute_client.AndroidComputeClient |
| 35 | ^ |
| 36 | | |
| 37 | goldfish_compute_client.GoldfishComputeClient |
| 38 | |
| 39 | |
| 40 | TODO(jansen): This class should likely be merged with CvdComputeClient |
| 41 | """ |
| 42 | |
| 43 | import getpass |
| 44 | import logging |
| 45 | import os |
| 46 | |
| 47 | from acloud.internal.lib import android_compute_client |
| 48 | from acloud.internal.lib import gcompute_client |
| 49 | from acloud.public import errors |
| 50 | |
| 51 | logger = logging.getLogger(__name__) |
| 52 | |
| 53 | |
| 54 | class GoldfishComputeClient(android_compute_client.AndroidComputeClient): |
| 55 | """Client that manages Goldfish based Android Virtual Device.""" |
| 56 | |
| 57 | # To determine if the boot failed |
| 58 | BOOT_FAILED_MSG = "VIRTUAL_DEVICE_FAILED" |
| 59 | |
| 60 | # To determine the failure reason |
| 61 | # If the emulator build is not available |
| 62 | EMULATOR_FETCH_FAILED_MSG = "EMULATOR_FETCH_FAILED" |
| 63 | # If the system image build is not available |
| 64 | ANDROID_FETCH_FAILED_MSG = "ANDROID_FETCH_FAILED" |
| 65 | # If the emulator could not boot in time |
| 66 | BOOT_TIMEOUT_MSG = "VIRTUAL_DEVICE_BOOT_FAILED" |
| 67 | |
| 68 | def __init__(self, acloud_config, oauth2_credentials): |
| 69 | """Initialize. |
| 70 | |
| 71 | Args: |
| 72 | acloud_config: An AcloudConfig object. |
| 73 | oauth2_credentials: An oauth2client.OAuth2Credentials instance. |
| 74 | """ |
| 75 | super(GoldfishComputeClient, self).__init__(acloud_config, |
| 76 | oauth2_credentials) |
| 77 | |
| 78 | def _GetDiskArgs(self, disk_name, image_name, image_project, disk_size_gb): |
| 79 | """Helper to generate disk args that is used to create an instance. |
| 80 | |
| 81 | Args: |
| 82 | disk_name: A string |
| 83 | image_name: A string |
| 84 | image_project: A string |
| 85 | disk_size_gb: An integer |
| 86 | |
| 87 | Returns: |
| 88 | A dictionary representing disk args. |
| 89 | """ |
| 90 | return [{ |
| 91 | "type": "PERSISTENT", |
| 92 | "boot": True, |
| 93 | "mode": "READ_WRITE", |
| 94 | "autoDelete": True, |
| 95 | "initializeParams": { |
| 96 | "diskName": |
| 97 | disk_name, |
| 98 | "sourceImage": |
| 99 | self.GetImage(image_name, image_project)["selfLink"], |
| 100 | "diskSizeGb": |
| 101 | disk_size_gb |
| 102 | }, |
| 103 | }] |
| 104 | |
| 105 | def CheckBootFailure(self, serial_out, instance): |
| 106 | """Overriding method from the parent class. |
| 107 | |
| 108 | Args: |
| 109 | serial_out: A string |
| 110 | instance: A string |
| 111 | |
| 112 | Raises: |
| 113 | Raises an errors.DeviceBootError exception if a failure is detected. |
| 114 | """ |
| 115 | if self.BOOT_FAILED_MSG in serial_out: |
| 116 | if self.EMULATOR_FETCH_FAILED_MSG in serial_out: |
| 117 | raise errors.DeviceBootError( |
| 118 | "Failed to download emulator build. Re-run with a newer build.") |
| 119 | if self.ANDROID_FETCH_FAILED_MSG in serial_out: |
| 120 | raise errors.DeviceBootError( |
| 121 | "Failed to download system image build. Re-run with a newer build.") |
| 122 | if self.BOOT_TIMEOUT_MSG in serial_out: |
| 123 | raise errors.DeviceBootError( |
| 124 | "Emulator timed out while booting.") |
| 125 | |
| 126 | def CreateInstance(self, |
| 127 | instance, |
| 128 | image_name, |
| 129 | image_project, |
| 130 | build_target, |
| 131 | branch, |
| 132 | build_id, |
| 133 | emulator_branch=None, |
| 134 | emulator_build_id=None, |
| 135 | blank_data_disk_size_gb=None, |
| 136 | gpu=None): |
| 137 | """Create a goldfish instance given a stable host image and a build id. |
| 138 | |
| 139 | Args: |
| 140 | instance: instance name. |
| 141 | image_name: A string, the name of the system image. |
| 142 | image_project: A string, name of the project where the image belongs. |
| 143 | Assume the default project if None. |
| 144 | build_target: Target name, e.g. "sdk_phone_x86_64-sdk" |
| 145 | branch: Branch name, e.g. "git_pi-dev" |
| 146 | build_id: Build id, a string, e.g. "2263051", "P2804227" |
| 147 | emulator_branch: emulator branch name, e.g. |
| 148 | "aosp-emu-master-dev" |
| 149 | emulator_build_id: emulator build id, a string, e.g. "2263051", "P2804227" |
| 150 | blank_data_disk_size_gb: Size of the blank data disk in GB. |
| 151 | gpu: GPU that should be attached to the instance, or None of no |
| 152 | acceleration is needed. e.g. "nvidia-tesla-k80" |
| 153 | """ |
| 154 | self._CheckMachineSize() |
| 155 | |
| 156 | # Add space for possible data partition. |
| 157 | boot_disk_size_gb = ( |
| 158 | int(self.GetImage(image_name, image_project)["diskSizeGb"]) + |
| 159 | blank_data_disk_size_gb) |
| 160 | disk_args = self._GetDiskArgs(instance, image_name, image_project, |
| 161 | boot_disk_size_gb) |
| 162 | |
| 163 | # Goldfish instances are metadata compatible with cuttlefish devices. |
| 164 | # See details goto/goldfish-deployment |
| 165 | metadata = self._metadata.copy() |
| 166 | resolution = self._resolution.split("x") |
| 167 | |
| 168 | # Note that we use the same metadata naming conventions as cuttlefish |
| 169 | metadata["cvd_01_dpi"] = resolution[3] |
| 170 | metadata["cvd_01_fetch_android_build_target"] = build_target |
| 171 | metadata["cvd_01_fetch_android_bid"] = "{branch}/{build_id}".format( |
| 172 | branch=branch, build_id=build_id) |
| 173 | if emulator_branch and emulator_build_id: |
| 174 | metadata["cvd_01_fetch_emulator_bid"] = "{branch}/{build_id}".format( |
| 175 | branch=emulator_branch, build_id=emulator_build_id) |
| 176 | metadata["cvd_01_launch"] = "1" |
| 177 | metadata["cvd_01_x_res"] = resolution[0] |
| 178 | metadata["cvd_01_y_res"] = resolution[1] |
| 179 | |
| 180 | # Add per-instance ssh key |
| 181 | if self._ssh_public_key_path: |
| 182 | rsa = self._LoadSshPublicKey(self._ssh_public_key_path) |
| 183 | logger.info("ssh_public_key_path is specified in config: %s, " |
| 184 | "will add the key to the instance.", |
| 185 | self._ssh_public_key_path) |
| 186 | metadata["sshKeys"] = "%s:%s" % (getpass.getuser(), rsa) |
| 187 | else: |
| 188 | logger.warning("ssh_public_key_path is not specified in config, " |
| 189 | "only project-wide key will be effective.") |
| 190 | |
| 191 | gcompute_client.ComputeClient.CreateInstance( |
| 192 | self, |
| 193 | instance=instance, |
| 194 | image_name=image_name, |
| 195 | image_project=image_project, |
| 196 | disk_args=disk_args, |
| 197 | metadata=metadata, |
| 198 | machine_type=self._machine_type, |
| 199 | network=self._network, |
| 200 | zone=self._zone, |
| 201 | gpu=gpu) |