blob: 4377d6db1656ead2464acfd185ac58f0a760258f [file] [log] [blame]
Kevin Chengb5963882018-05-09 00:06:27 -07001#!/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.
Kevin Chengb5963882018-05-09 00:06:27 -070016"""A client that manages Goldfish Virtual Device on compute engine.
17
18** GoldfishComputeClient **
19
20GoldfishComputeClient derives from AndroidComputeClient. It manges a google
21compute engine project that is setup for running Goldfish Virtual Devices.
22It knows how to create a host instance from a Goldfish Stable Host Image, fetch
23Android build, an emulator build, and start Android within the host instance.
24
25** Class hierarchy **
26
27 base_cloud_client.BaseCloudApiClient
28 ^
29 |
30 gcompute_client.ComputeClient
31 ^
32 |
33 android_compute_client.AndroidComputeClient
34 ^
35 |
36 goldfish_compute_client.GoldfishComputeClient
37
38
cylan0d77ae12018-05-18 08:36:48 +000039TODO: This class should likely be merged with CvdComputeClient
Kevin Chengb5963882018-05-09 00:06:27 -070040"""
41
42import getpass
43import logging
Kevin Chengb5963882018-05-09 00:06:27 -070044
Sam Chiu7de3b232018-12-06 19:45:52 +080045from acloud import errors
Kevin Chengb5963882018-05-09 00:06:27 -070046from acloud.internal.lib import android_compute_client
47from acloud.internal.lib import gcompute_client
Kevin Chengb5963882018-05-09 00:06:27 -070048
49logger = logging.getLogger(__name__)
50
51
52class GoldfishComputeClient(android_compute_client.AndroidComputeClient):
cylan0d77ae12018-05-18 08:36:48 +000053 """Client that manages Goldfish based Android Virtual Device.
Kevin Chengb5963882018-05-09 00:06:27 -070054
cylan0d77ae12018-05-18 08:36:48 +000055 Attributes:
56 acloud_config: An AcloudConfig object.
57 oauth2_credentials: An oauth2client.OAuth2Credentials instance.
Kevin Chengb5963882018-05-09 00:06:27 -070058 """
Kevin Chengb5963882018-05-09 00:06:27 -070059
cylan0d77ae12018-05-18 08:36:48 +000060 # To determine if the boot failed
61 BOOT_FAILED_MSG = "VIRTUAL_DEVICE_FAILED"
Kevin Chengb5963882018-05-09 00:06:27 -070062
cylan0d77ae12018-05-18 08:36:48 +000063 # To determine the failure reason
64 # If the emulator build is not available
65 EMULATOR_FETCH_FAILED_MSG = "EMULATOR_FETCH_FAILED"
66 # If the system image build is not available
67 ANDROID_FETCH_FAILED_MSG = "ANDROID_FETCH_FAILED"
68 # If the emulator could not boot in time
69 BOOT_TIMEOUT_MSG = "VIRTUAL_DEVICE_BOOT_FAILED"
Kevin Chengb5963882018-05-09 00:06:27 -070070
cylan0d77ae12018-05-18 08:36:48 +000071 #pylint: disable=signature-differs
72 def _GetDiskArgs(self, disk_name, image_name, image_project, disk_size_gb):
73 """Helper to generate disk args that is used to create an instance.
74
75 Args:
76 disk_name: String, the name of disk.
77 image_name: String, the name of the system image.
78 image_project: String, the name of the project where the image.
79 disk_size_gb: Integer, size of the blank data disk in GB.
80
81 Returns:
82 A dictionary representing disk args.
83 """
84 return [{
85 "type": "PERSISTENT",
86 "boot": True,
87 "mode": "READ_WRITE",
88 "autoDelete": True,
89 "initializeParams": {
90 "diskName":
Kevin Chengb5963882018-05-09 00:06:27 -070091 disk_name,
cylan0d77ae12018-05-18 08:36:48 +000092 "sourceImage":
Kevin Chengb5963882018-05-09 00:06:27 -070093 self.GetImage(image_name, image_project)["selfLink"],
cylan0d77ae12018-05-18 08:36:48 +000094 "diskSizeGb":
Kevin Chengb5963882018-05-09 00:06:27 -070095 disk_size_gb
cylan0d77ae12018-05-18 08:36:48 +000096 },
97 }]
98 #pylint: disable=signature-differs
Kevin Chengb5963882018-05-09 00:06:27 -070099
cylan0d77ae12018-05-18 08:36:48 +0000100 def CheckBootFailure(self, serial_out, instance):
101 """Overriding method from the parent class.
Kevin Chengb5963882018-05-09 00:06:27 -0700102
cylan0d77ae12018-05-18 08:36:48 +0000103 Args:
104 serial_out: String
105 instance: String
Kevin Chengb5963882018-05-09 00:06:27 -0700106
cylan0d77ae12018-05-18 08:36:48 +0000107 Raises:
108 Raises an errors.DeviceBootError exception if a failure is detected.
109 """
110 if self.BOOT_FAILED_MSG in serial_out:
111 if self.EMULATOR_FETCH_FAILED_MSG in serial_out:
112 raise errors.DeviceBootError(
113 "Failed to download emulator build. Re-run with a newer build."
114 )
115 if self.ANDROID_FETCH_FAILED_MSG in serial_out:
116 raise errors.DeviceBootError(
117 "Failed to download system image build. Re-run with a newer build."
118 )
119 if self.BOOT_TIMEOUT_MSG in serial_out:
120 raise errors.DeviceBootError(
121 "Emulator timed out while booting.")
Kevin Chengb5963882018-05-09 00:06:27 -0700122
cylan0d77ae12018-05-18 08:36:48 +0000123 # pylint: disable=too-many-locals,arguments-differ
124 # TODO: Refactor CreateInstance to pass in an object instead of all these args.
125 def CreateInstance(self,
126 instance,
127 image_name,
128 image_project,
129 build_target,
130 branch,
131 build_id,
132 emulator_branch=None,
133 emulator_build_id=None,
134 blank_data_disk_size_gb=None,
135 gpu=None):
136 """Create a goldfish instance given a stable host image and a build id.
Kevin Chengb5963882018-05-09 00:06:27 -0700137
cylan0d77ae12018-05-18 08:36:48 +0000138 Args:
139 instance: String, instance name.
140 image_name: String, the name of the system image.
141 image_project: String, name of the project where the image belongs.
142 Assume the default project if None.
143 build_target: String, target name, e.g. "sdk_phone_x86_64-sdk"
144 branch: String, branch name, e.g. "git_pi-dev"
145 build_id: String, build id, a string, e.g. "2263051", "P2804227"
146 emulator_branch: String, emulator branch name, e.g."aosp-emu-master-dev"
147 emulator_build_id: String, emulator build id, a string, e.g. "2263051", "P2804227"
148 blank_data_disk_size_gb: Integer, size of the blank data disk in GB.
149 gpu: String, GPU that should be attached to the instance, or None of no
150 acceleration is needed. e.g. "nvidia-tesla-k80"
151 """
152 self._CheckMachineSize()
Kevin Chengb5963882018-05-09 00:06:27 -0700153
cylan0d77ae12018-05-18 08:36:48 +0000154 # Add space for possible data partition.
155 boot_disk_size_gb = (
156 int(self.GetImage(image_name, image_project)["diskSizeGb"]) +
157 blank_data_disk_size_gb)
158 disk_args = self._GetDiskArgs(instance, image_name, image_project,
159 boot_disk_size_gb)
Kevin Chengb5963882018-05-09 00:06:27 -0700160
cylan0d77ae12018-05-18 08:36:48 +0000161 # Goldfish instances are metadata compatible with cuttlefish devices.
162 # See details goto/goldfish-deployment
163 metadata = self._metadata.copy()
164 resolution = self._resolution.split("x")
Kevin Chengb5963882018-05-09 00:06:27 -0700165
cylan0d77ae12018-05-18 08:36:48 +0000166 # Note that we use the same metadata naming conventions as cuttlefish
167 metadata["cvd_01_dpi"] = resolution[3]
168 metadata["cvd_01_fetch_android_build_target"] = build_target
169 metadata["cvd_01_fetch_android_bid"] = "{branch}/{build_id}".format(
170 branch=branch, build_id=build_id)
171 if emulator_branch and emulator_build_id:
172 metadata[
173 "cvd_01_fetch_emulator_bid"] = "{branch}/{build_id}".format(
174 branch=emulator_branch, build_id=emulator_build_id)
175 metadata["cvd_01_launch"] = "1"
176 metadata["cvd_01_x_res"] = resolution[0]
177 metadata["cvd_01_y_res"] = resolution[1]
Kevin Chengb5963882018-05-09 00:06:27 -0700178
cylan0d77ae12018-05-18 08:36:48 +0000179 # Add per-instance ssh key
180 if self._ssh_public_key_path:
181 rsa = self._LoadSshPublicKey(self._ssh_public_key_path)
182 logger.info(
183 "ssh_public_key_path is specified in config: %s, "
184 "will add the key to the instance.", self._ssh_public_key_path)
185 metadata["sshKeys"] = "%s:%s" % (getpass.getuser(), rsa)
186 else:
187 logger.warning("ssh_public_key_path is not specified in config, "
188 "only project-wide key will be effective.")
Kevin Chengb5963882018-05-09 00:06:27 -0700189
cylan0d77ae12018-05-18 08:36:48 +0000190 gcompute_client.ComputeClient.CreateInstance(
191 self,
192 instance=instance,
193 image_name=image_name,
194 image_project=image_project,
195 disk_args=disk_args,
196 metadata=metadata,
197 machine_type=self._machine_type,
198 network=self._network,
199 zone=self._zone,
200 gpu=gpu)