blob: 01278fc1253d1c592404ff9d29ababa30a9a4b5d [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.
16
17"""A client that manages Goldfish Virtual Device on compute engine.
18
19** GoldfishComputeClient **
20
21GoldfishComputeClient derives from AndroidComputeClient. It manges a google
22compute engine project that is setup for running Goldfish Virtual Devices.
23It knows how to create a host instance from a Goldfish Stable Host Image, fetch
24Android 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
40TODO(jansen): This class should likely be merged with CvdComputeClient
41"""
42
43import getpass
44import logging
45import os
46
47from acloud.internal.lib import android_compute_client
48from acloud.internal.lib import gcompute_client
49from acloud.public import errors
50
51logger = logging.getLogger(__name__)
52
53
54class 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)