xref: /aosp_15_r20/tools/acloud/public/device_driver.py (revision 800a58d989c669b8eb8a71d8df53b1ba3d411444)
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
17*800a58d9SAndroid Build Coastguard Worker"""Public Device Driver APIs.
18*800a58d9SAndroid Build Coastguard Worker
19*800a58d9SAndroid Build Coastguard WorkerThis module provides public device driver APIs that can be called
20*800a58d9SAndroid Build Coastguard Workeras a Python library.
21*800a58d9SAndroid Build Coastguard Worker
22*800a58d9SAndroid Build Coastguard WorkerTODO: The following APIs have not been implemented
23*800a58d9SAndroid Build Coastguard Worker  - RebootAVD(ip):
24*800a58d9SAndroid Build Coastguard Worker  - RegisterSshPubKey(username, key):
25*800a58d9SAndroid Build Coastguard Worker  - UnregisterSshPubKey(username, key):
26*800a58d9SAndroid Build Coastguard Worker  - CleanupStaleImages():
27*800a58d9SAndroid Build Coastguard Worker  - CleanupStaleDevices():
28*800a58d9SAndroid Build Coastguard Worker"""
29*800a58d9SAndroid Build Coastguard Worker
30*800a58d9SAndroid Build Coastguard Workerfrom __future__ import print_function
31*800a58d9SAndroid Build Coastguard Workerimport logging
32*800a58d9SAndroid Build Coastguard Workerimport os
33*800a58d9SAndroid Build Coastguard Worker
34*800a58d9SAndroid Build Coastguard Workerfrom acloud import errors
35*800a58d9SAndroid Build Coastguard Workerfrom acloud.public import avd
36*800a58d9SAndroid Build Coastguard Workerfrom acloud.public import report
37*800a58d9SAndroid Build Coastguard Workerfrom acloud.public.actions import common_operations
38*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal import constants
39*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import auth
40*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import android_build_client
41*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import android_compute_client
42*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import gstorage_client
43*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import utils
44*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib.adb_tools import AdbTools
45*800a58d9SAndroid Build Coastguard Worker
46*800a58d9SAndroid Build Coastguard Worker
47*800a58d9SAndroid Build Coastguard Workerlogger = logging.getLogger(__name__)
48*800a58d9SAndroid Build Coastguard Worker
49*800a58d9SAndroid Build Coastguard WorkerMAX_BATCH_CLEANUP_COUNT = 100
50*800a58d9SAndroid Build Coastguard Worker
51*800a58d9SAndroid Build Coastguard Worker_SSH_USER = "root"
52*800a58d9SAndroid Build Coastguard Worker
53*800a58d9SAndroid Build Coastguard Worker
54*800a58d9SAndroid Build Coastguard Worker# pylint: disable=invalid-name
55*800a58d9SAndroid Build Coastguard Workerclass AndroidVirtualDevicePool():
56*800a58d9SAndroid Build Coastguard Worker    """A class that manages a pool of devices."""
57*800a58d9SAndroid Build Coastguard Worker
58*800a58d9SAndroid Build Coastguard Worker    def __init__(self, cfg, devices=None):
59*800a58d9SAndroid Build Coastguard Worker        self._devices = devices or []
60*800a58d9SAndroid Build Coastguard Worker        self._cfg = cfg
61*800a58d9SAndroid Build Coastguard Worker        credentials = auth.CreateCredentials(cfg)
62*800a58d9SAndroid Build Coastguard Worker        self._build_client = android_build_client.AndroidBuildClient(
63*800a58d9SAndroid Build Coastguard Worker            credentials)
64*800a58d9SAndroid Build Coastguard Worker        self._storage_client = gstorage_client.StorageClient(credentials)
65*800a58d9SAndroid Build Coastguard Worker        self._compute_client = android_compute_client.AndroidComputeClient(
66*800a58d9SAndroid Build Coastguard Worker            cfg, credentials)
67*800a58d9SAndroid Build Coastguard Worker
68*800a58d9SAndroid Build Coastguard Worker    @utils.TimeExecute("Creating GCE image")
69*800a58d9SAndroid Build Coastguard Worker    def _CreateGceImageWithBuildInfo(self, build_target, build_id):
70*800a58d9SAndroid Build Coastguard Worker        """Creates a Gce image using build from Launch Control.
71*800a58d9SAndroid Build Coastguard Worker
72*800a58d9SAndroid Build Coastguard Worker        Clone avd-system.tar.gz of a build to a cache storage bucket
73*800a58d9SAndroid Build Coastguard Worker        using launch control api. And then create a Gce image.
74*800a58d9SAndroid Build Coastguard Worker
75*800a58d9SAndroid Build Coastguard Worker        Args:
76*800a58d9SAndroid Build Coastguard Worker            build_target: Target name, e.g. "aosp_cf_x86_64_phone-userdebug"
77*800a58d9SAndroid Build Coastguard Worker            build_id: Build id, a string, e.g. "2263051", "P2804227"
78*800a58d9SAndroid Build Coastguard Worker
79*800a58d9SAndroid Build Coastguard Worker        Returns:
80*800a58d9SAndroid Build Coastguard Worker            String, name of the Gce image that has been created.
81*800a58d9SAndroid Build Coastguard Worker        """
82*800a58d9SAndroid Build Coastguard Worker        logger.info("Creating a new gce image using build: build_id %s, "
83*800a58d9SAndroid Build Coastguard Worker                    "build_target %s", build_id, build_target)
84*800a58d9SAndroid Build Coastguard Worker        disk_image_id = utils.GenerateUniqueName(
85*800a58d9SAndroid Build Coastguard Worker            suffix=self._cfg.disk_image_name)
86*800a58d9SAndroid Build Coastguard Worker        self._build_client.CopyTo(
87*800a58d9SAndroid Build Coastguard Worker            build_target,
88*800a58d9SAndroid Build Coastguard Worker            build_id,
89*800a58d9SAndroid Build Coastguard Worker            artifact_name=self._cfg.disk_image_name,
90*800a58d9SAndroid Build Coastguard Worker            destination_bucket=self._cfg.storage_bucket_name,
91*800a58d9SAndroid Build Coastguard Worker            destination_path=disk_image_id)
92*800a58d9SAndroid Build Coastguard Worker        disk_image_url = self._storage_client.GetUrl(
93*800a58d9SAndroid Build Coastguard Worker            self._cfg.storage_bucket_name, disk_image_id)
94*800a58d9SAndroid Build Coastguard Worker        try:
95*800a58d9SAndroid Build Coastguard Worker            image_name = self._compute_client.GenerateImageName(build_target,
96*800a58d9SAndroid Build Coastguard Worker                                                                build_id)
97*800a58d9SAndroid Build Coastguard Worker            self._compute_client.CreateImage(image_name=image_name,
98*800a58d9SAndroid Build Coastguard Worker                                             source_uri=disk_image_url)
99*800a58d9SAndroid Build Coastguard Worker        finally:
100*800a58d9SAndroid Build Coastguard Worker            self._storage_client.Delete(self._cfg.storage_bucket_name,
101*800a58d9SAndroid Build Coastguard Worker                                        disk_image_id)
102*800a58d9SAndroid Build Coastguard Worker        return image_name
103*800a58d9SAndroid Build Coastguard Worker
104*800a58d9SAndroid Build Coastguard Worker    @utils.TimeExecute("Creating GCE image")
105*800a58d9SAndroid Build Coastguard Worker    def _CreateGceImageWithLocalFile(self, local_disk_image):
106*800a58d9SAndroid Build Coastguard Worker        """Create a Gce image with a local image file.
107*800a58d9SAndroid Build Coastguard Worker
108*800a58d9SAndroid Build Coastguard Worker        The local disk image can be either a tar.gz file or a
109*800a58d9SAndroid Build Coastguard Worker        raw vmlinux image.
110*800a58d9SAndroid Build Coastguard Worker        e.g.  /tmp/avd-system.tar.gz or /tmp/android_system_disk_syslinux.img
111*800a58d9SAndroid Build Coastguard Worker        If a raw vmlinux image is provided, it will be archived into a tar.gz file.
112*800a58d9SAndroid Build Coastguard Worker
113*800a58d9SAndroid Build Coastguard Worker        The final tar.gz file will be uploaded to a cache bucket in storage.
114*800a58d9SAndroid Build Coastguard Worker
115*800a58d9SAndroid Build Coastguard Worker        Args:
116*800a58d9SAndroid Build Coastguard Worker            local_disk_image: string, path to a local disk image,
117*800a58d9SAndroid Build Coastguard Worker
118*800a58d9SAndroid Build Coastguard Worker        Returns:
119*800a58d9SAndroid Build Coastguard Worker            String, name of the Gce image that has been created.
120*800a58d9SAndroid Build Coastguard Worker
121*800a58d9SAndroid Build Coastguard Worker        Raises:
122*800a58d9SAndroid Build Coastguard Worker            DriverError: if a file with an unexpected extension is given.
123*800a58d9SAndroid Build Coastguard Worker        """
124*800a58d9SAndroid Build Coastguard Worker        logger.info("Creating a new gce image from a local file %s",
125*800a58d9SAndroid Build Coastguard Worker                    local_disk_image)
126*800a58d9SAndroid Build Coastguard Worker        with utils.TempDir() as tempdir:
127*800a58d9SAndroid Build Coastguard Worker            if local_disk_image.endswith(self._cfg.disk_raw_image_extension):
128*800a58d9SAndroid Build Coastguard Worker                dest_tar_file = os.path.join(tempdir,
129*800a58d9SAndroid Build Coastguard Worker                                             self._cfg.disk_image_name)
130*800a58d9SAndroid Build Coastguard Worker                utils.MakeTarFile(
131*800a58d9SAndroid Build Coastguard Worker                    src_dict={local_disk_image: self._cfg.disk_raw_image_name},
132*800a58d9SAndroid Build Coastguard Worker                    dest=dest_tar_file)
133*800a58d9SAndroid Build Coastguard Worker                local_disk_image = dest_tar_file
134*800a58d9SAndroid Build Coastguard Worker            elif not local_disk_image.endswith(self._cfg.disk_image_extension):
135*800a58d9SAndroid Build Coastguard Worker                raise errors.DriverError(
136*800a58d9SAndroid Build Coastguard Worker                    "Wrong local_disk_image type, must be a *%s file or *%s file"
137*800a58d9SAndroid Build Coastguard Worker                    % (self._cfg.disk_raw_image_extension,
138*800a58d9SAndroid Build Coastguard Worker                       self._cfg.disk_image_extension))
139*800a58d9SAndroid Build Coastguard Worker
140*800a58d9SAndroid Build Coastguard Worker            disk_image_id = utils.GenerateUniqueName(
141*800a58d9SAndroid Build Coastguard Worker                suffix=self._cfg.disk_image_name)
142*800a58d9SAndroid Build Coastguard Worker            self._storage_client.Upload(
143*800a58d9SAndroid Build Coastguard Worker                local_src=local_disk_image,
144*800a58d9SAndroid Build Coastguard Worker                bucket_name=self._cfg.storage_bucket_name,
145*800a58d9SAndroid Build Coastguard Worker                object_name=disk_image_id,
146*800a58d9SAndroid Build Coastguard Worker                mime_type=self._cfg.disk_image_mime_type)
147*800a58d9SAndroid Build Coastguard Worker        disk_image_url = self._storage_client.GetUrl(
148*800a58d9SAndroid Build Coastguard Worker            self._cfg.storage_bucket_name, disk_image_id)
149*800a58d9SAndroid Build Coastguard Worker        try:
150*800a58d9SAndroid Build Coastguard Worker            image_name = self._compute_client.GenerateImageName()
151*800a58d9SAndroid Build Coastguard Worker            self._compute_client.CreateImage(image_name=image_name,
152*800a58d9SAndroid Build Coastguard Worker                                             source_uri=disk_image_url)
153*800a58d9SAndroid Build Coastguard Worker        finally:
154*800a58d9SAndroid Build Coastguard Worker            self._storage_client.Delete(self._cfg.storage_bucket_name,
155*800a58d9SAndroid Build Coastguard Worker                                        disk_image_id)
156*800a58d9SAndroid Build Coastguard Worker        return image_name
157*800a58d9SAndroid Build Coastguard Worker
158*800a58d9SAndroid Build Coastguard Worker    # pylint: disable=too-many-locals
159*800a58d9SAndroid Build Coastguard Worker    def CreateDevices(self,
160*800a58d9SAndroid Build Coastguard Worker                      num,
161*800a58d9SAndroid Build Coastguard Worker                      build_target=None,
162*800a58d9SAndroid Build Coastguard Worker                      build_id=None,
163*800a58d9SAndroid Build Coastguard Worker                      gce_image=None,
164*800a58d9SAndroid Build Coastguard Worker                      local_disk_image=None,
165*800a58d9SAndroid Build Coastguard Worker                      cleanup=True,
166*800a58d9SAndroid Build Coastguard Worker                      extra_data_disk_size_gb=None,
167*800a58d9SAndroid Build Coastguard Worker                      precreated_data_image=None,
168*800a58d9SAndroid Build Coastguard Worker                      avd_spec=None,
169*800a58d9SAndroid Build Coastguard Worker                      extra_scopes=None):
170*800a58d9SAndroid Build Coastguard Worker        """Creates |num| devices for given build_target and build_id.
171*800a58d9SAndroid Build Coastguard Worker
172*800a58d9SAndroid Build Coastguard Worker        - If gce_image is provided, will use it to create an instance.
173*800a58d9SAndroid Build Coastguard Worker        - If local_disk_image is provided, will upload it to a temporary
174*800a58d9SAndroid Build Coastguard Worker          caching storage bucket which is defined by user as |storage_bucket_name|
175*800a58d9SAndroid Build Coastguard Worker          And then create an gce image with it; and then create an instance.
176*800a58d9SAndroid Build Coastguard Worker        - If build_target and build_id are provided, will clone the disk image
177*800a58d9SAndroid Build Coastguard Worker          via launch control to the temporary caching storage bucket.
178*800a58d9SAndroid Build Coastguard Worker          And then create an gce image with it; and then create an instance.
179*800a58d9SAndroid Build Coastguard Worker
180*800a58d9SAndroid Build Coastguard Worker        Args:
181*800a58d9SAndroid Build Coastguard Worker            num: Number of devices to create.
182*800a58d9SAndroid Build Coastguard Worker            build_target: Target name, e.g. "aosp_cf_x86_64_phone-userdebug"
183*800a58d9SAndroid Build Coastguard Worker            build_id: Build id, a string, e.g. "2263051", "P2804227"
184*800a58d9SAndroid Build Coastguard Worker            gce_image: string, if given, will use this image
185*800a58d9SAndroid Build Coastguard Worker                       instead of creating a new one.
186*800a58d9SAndroid Build Coastguard Worker                       implies cleanup=False.
187*800a58d9SAndroid Build Coastguard Worker            local_disk_image: string, path to a local disk image, e.g.
188*800a58d9SAndroid Build Coastguard Worker                              /tmp/avd-system.tar.gz
189*800a58d9SAndroid Build Coastguard Worker            cleanup: boolean, if True clean up compute engine image after creating
190*800a58d9SAndroid Build Coastguard Worker                     the instance.
191*800a58d9SAndroid Build Coastguard Worker            extra_data_disk_size_gb: Integer, size of extra disk, or None.
192*800a58d9SAndroid Build Coastguard Worker            precreated_data_image: A string, the image to use for the extra disk.
193*800a58d9SAndroid Build Coastguard Worker            avd_spec: AVDSpec object for pass hw_property.
194*800a58d9SAndroid Build Coastguard Worker            extra_scopes: A list of extra scopes given to the new instance.
195*800a58d9SAndroid Build Coastguard Worker
196*800a58d9SAndroid Build Coastguard Worker        Raises:
197*800a58d9SAndroid Build Coastguard Worker            errors.DriverError: If no source is specified for image creation.
198*800a58d9SAndroid Build Coastguard Worker        """
199*800a58d9SAndroid Build Coastguard Worker        if gce_image:
200*800a58d9SAndroid Build Coastguard Worker            # GCE image is provided, we can directly move to instance creation.
201*800a58d9SAndroid Build Coastguard Worker            logger.info("Using existing gce image %s", gce_image)
202*800a58d9SAndroid Build Coastguard Worker            image_name = gce_image
203*800a58d9SAndroid Build Coastguard Worker            cleanup = False
204*800a58d9SAndroid Build Coastguard Worker        elif local_disk_image:
205*800a58d9SAndroid Build Coastguard Worker            image_name = self._CreateGceImageWithLocalFile(local_disk_image)
206*800a58d9SAndroid Build Coastguard Worker        elif build_target and build_id:
207*800a58d9SAndroid Build Coastguard Worker            image_name = self._CreateGceImageWithBuildInfo(build_target,
208*800a58d9SAndroid Build Coastguard Worker                                                           build_id)
209*800a58d9SAndroid Build Coastguard Worker        else:
210*800a58d9SAndroid Build Coastguard Worker            raise errors.DriverError(
211*800a58d9SAndroid Build Coastguard Worker                "Invalid image source, must specify one of the following: gce_image, "
212*800a58d9SAndroid Build Coastguard Worker                "local_disk_image, or build_target and build id.")
213*800a58d9SAndroid Build Coastguard Worker
214*800a58d9SAndroid Build Coastguard Worker        # Create GCE instances.
215*800a58d9SAndroid Build Coastguard Worker        try:
216*800a58d9SAndroid Build Coastguard Worker            for _ in range(num):
217*800a58d9SAndroid Build Coastguard Worker                instance = self._compute_client.GenerateInstanceName(
218*800a58d9SAndroid Build Coastguard Worker                    build_target, build_id)
219*800a58d9SAndroid Build Coastguard Worker                extra_disk_name = None
220*800a58d9SAndroid Build Coastguard Worker                if extra_data_disk_size_gb > 0:
221*800a58d9SAndroid Build Coastguard Worker                    extra_disk_name = self._compute_client.GetDataDiskName(
222*800a58d9SAndroid Build Coastguard Worker                        instance)
223*800a58d9SAndroid Build Coastguard Worker                    self._compute_client.CreateDisk(extra_disk_name,
224*800a58d9SAndroid Build Coastguard Worker                                                    precreated_data_image,
225*800a58d9SAndroid Build Coastguard Worker                                                    extra_data_disk_size_gb,
226*800a58d9SAndroid Build Coastguard Worker                                                    disk_type=avd_spec.disk_type)
227*800a58d9SAndroid Build Coastguard Worker                self._compute_client.CreateInstance(
228*800a58d9SAndroid Build Coastguard Worker                    instance=instance,
229*800a58d9SAndroid Build Coastguard Worker                    image_name=image_name,
230*800a58d9SAndroid Build Coastguard Worker                    extra_disk_name=extra_disk_name,
231*800a58d9SAndroid Build Coastguard Worker                    avd_spec=avd_spec,
232*800a58d9SAndroid Build Coastguard Worker                    extra_scopes=extra_scopes)
233*800a58d9SAndroid Build Coastguard Worker                ip = self._compute_client.GetInstanceIP(instance)
234*800a58d9SAndroid Build Coastguard Worker                self.devices.append(avd.AndroidVirtualDevice(
235*800a58d9SAndroid Build Coastguard Worker                    ip=ip, instance_name=instance))
236*800a58d9SAndroid Build Coastguard Worker        finally:
237*800a58d9SAndroid Build Coastguard Worker            if cleanup:
238*800a58d9SAndroid Build Coastguard Worker                self._compute_client.DeleteImage(image_name)
239*800a58d9SAndroid Build Coastguard Worker
240*800a58d9SAndroid Build Coastguard Worker    def DeleteDevices(self):
241*800a58d9SAndroid Build Coastguard Worker        """Deletes devices.
242*800a58d9SAndroid Build Coastguard Worker
243*800a58d9SAndroid Build Coastguard Worker        Returns:
244*800a58d9SAndroid Build Coastguard Worker            A tuple, (deleted, failed, error_msgs)
245*800a58d9SAndroid Build Coastguard Worker            deleted: A list of names of instances that have been deleted.
246*800a58d9SAndroid Build Coastguard Worker            faild: A list of names of instances that we fail to delete.
247*800a58d9SAndroid Build Coastguard Worker            error_msgs: A list of failure messages.
248*800a58d9SAndroid Build Coastguard Worker        """
249*800a58d9SAndroid Build Coastguard Worker        instance_names = [device.instance_name for device in self._devices]
250*800a58d9SAndroid Build Coastguard Worker        return self._compute_client.DeleteInstances(instance_names,
251*800a58d9SAndroid Build Coastguard Worker                                                    self._cfg.zone)
252*800a58d9SAndroid Build Coastguard Worker
253*800a58d9SAndroid Build Coastguard Worker    @utils.TimeExecute("Waiting for AVD to boot")
254*800a58d9SAndroid Build Coastguard Worker    def WaitForBoot(self):
255*800a58d9SAndroid Build Coastguard Worker        """Waits for all devices to boot up.
256*800a58d9SAndroid Build Coastguard Worker
257*800a58d9SAndroid Build Coastguard Worker        Returns:
258*800a58d9SAndroid Build Coastguard Worker            A dictionary that contains all the failures.
259*800a58d9SAndroid Build Coastguard Worker            The key is the name of the instance that fails to boot,
260*800a58d9SAndroid Build Coastguard Worker            the value is an errors.DeviceBoottError object.
261*800a58d9SAndroid Build Coastguard Worker        """
262*800a58d9SAndroid Build Coastguard Worker        failures = {}
263*800a58d9SAndroid Build Coastguard Worker        for device in self._devices:
264*800a58d9SAndroid Build Coastguard Worker            try:
265*800a58d9SAndroid Build Coastguard Worker                self._compute_client.WaitForBoot(device.instance_name)
266*800a58d9SAndroid Build Coastguard Worker            except errors.DeviceBootError as e:
267*800a58d9SAndroid Build Coastguard Worker                failures[device.instance_name] = e
268*800a58d9SAndroid Build Coastguard Worker        return failures
269*800a58d9SAndroid Build Coastguard Worker
270*800a58d9SAndroid Build Coastguard Worker    @property
271*800a58d9SAndroid Build Coastguard Worker    def devices(self):
272*800a58d9SAndroid Build Coastguard Worker        """Returns a list of devices in the pool.
273*800a58d9SAndroid Build Coastguard Worker
274*800a58d9SAndroid Build Coastguard Worker        Returns:
275*800a58d9SAndroid Build Coastguard Worker            A list of devices in the pool.
276*800a58d9SAndroid Build Coastguard Worker        """
277*800a58d9SAndroid Build Coastguard Worker        return self._devices
278*800a58d9SAndroid Build Coastguard Worker
279*800a58d9SAndroid Build Coastguard Worker
280*800a58d9SAndroid Build Coastguard Workerdef AddDeletionResultToReport(report_obj, deleted, failed, error_msgs,
281*800a58d9SAndroid Build Coastguard Worker                              resource_name):
282*800a58d9SAndroid Build Coastguard Worker    """Adds deletion result to a Report object.
283*800a58d9SAndroid Build Coastguard Worker
284*800a58d9SAndroid Build Coastguard Worker    This function will add the following to report.data.
285*800a58d9SAndroid Build Coastguard Worker      "deleted": [
286*800a58d9SAndroid Build Coastguard Worker         {"name": "resource_name", "type": "resource_name"},
287*800a58d9SAndroid Build Coastguard Worker       ],
288*800a58d9SAndroid Build Coastguard Worker      "failed": [
289*800a58d9SAndroid Build Coastguard Worker         {"name": "resource_name", "type": "resource_name"},
290*800a58d9SAndroid Build Coastguard Worker       ],
291*800a58d9SAndroid Build Coastguard Worker    This function will append error_msgs to report.errors.
292*800a58d9SAndroid Build Coastguard Worker
293*800a58d9SAndroid Build Coastguard Worker    Args:
294*800a58d9SAndroid Build Coastguard Worker        report_obj: A Report object.
295*800a58d9SAndroid Build Coastguard Worker        deleted: A list of names of the resources that have been deleted.
296*800a58d9SAndroid Build Coastguard Worker        failed: A list of names of the resources that we fail to delete.
297*800a58d9SAndroid Build Coastguard Worker        error_msgs: A list of error message strings to be added to the report.
298*800a58d9SAndroid Build Coastguard Worker        resource_name: A string, representing the name of the resource.
299*800a58d9SAndroid Build Coastguard Worker    """
300*800a58d9SAndroid Build Coastguard Worker    for name in deleted:
301*800a58d9SAndroid Build Coastguard Worker        report_obj.AddData(key="deleted",
302*800a58d9SAndroid Build Coastguard Worker                           value={"name": name,
303*800a58d9SAndroid Build Coastguard Worker                                  "type": resource_name})
304*800a58d9SAndroid Build Coastguard Worker    for name in failed:
305*800a58d9SAndroid Build Coastguard Worker        report_obj.AddData(key="failed",
306*800a58d9SAndroid Build Coastguard Worker                           value={"name": name,
307*800a58d9SAndroid Build Coastguard Worker                                  "type": resource_name})
308*800a58d9SAndroid Build Coastguard Worker    report_obj.AddErrors(error_msgs)
309*800a58d9SAndroid Build Coastguard Worker    if failed or error_msgs:
310*800a58d9SAndroid Build Coastguard Worker        report_obj.SetStatus(report.Status.FAIL)
311*800a58d9SAndroid Build Coastguard Worker
312*800a58d9SAndroid Build Coastguard Worker
313*800a58d9SAndroid Build Coastguard Workerdef _FetchSerialLogsFromDevices(compute_client, instance_names, output_file,
314*800a58d9SAndroid Build Coastguard Worker                                port):
315*800a58d9SAndroid Build Coastguard Worker    """Fetch serial logs from a port for a list of devices to a local file.
316*800a58d9SAndroid Build Coastguard Worker
317*800a58d9SAndroid Build Coastguard Worker    Args:
318*800a58d9SAndroid Build Coastguard Worker        compute_client: An object of android_compute_client.AndroidComputeClient
319*800a58d9SAndroid Build Coastguard Worker        instance_names: A list of instance names.
320*800a58d9SAndroid Build Coastguard Worker        output_file: A path to a file ending with "tar.gz"
321*800a58d9SAndroid Build Coastguard Worker        port: The number of serial port to read from, 0 for serial output, 1 for
322*800a58d9SAndroid Build Coastguard Worker              logcat.
323*800a58d9SAndroid Build Coastguard Worker    """
324*800a58d9SAndroid Build Coastguard Worker    with utils.TempDir() as tempdir:
325*800a58d9SAndroid Build Coastguard Worker        src_dict = {}
326*800a58d9SAndroid Build Coastguard Worker        for instance_name in instance_names:
327*800a58d9SAndroid Build Coastguard Worker            serial_log = compute_client.GetSerialPortOutput(
328*800a58d9SAndroid Build Coastguard Worker                instance=instance_name, port=port)
329*800a58d9SAndroid Build Coastguard Worker            file_name = "%s.log" % instance_name
330*800a58d9SAndroid Build Coastguard Worker            file_path = os.path.join(tempdir, file_name)
331*800a58d9SAndroid Build Coastguard Worker            src_dict[file_path] = file_name
332*800a58d9SAndroid Build Coastguard Worker            with open(file_path, "w") as f:
333*800a58d9SAndroid Build Coastguard Worker                f.write(serial_log.encode("utf-8"))
334*800a58d9SAndroid Build Coastguard Worker        utils.MakeTarFile(src_dict, output_file)
335*800a58d9SAndroid Build Coastguard Worker
336*800a58d9SAndroid Build Coastguard Worker
337*800a58d9SAndroid Build Coastguard Worker# pylint: disable=too-many-locals
338*800a58d9SAndroid Build Coastguard Workerdef CreateGCETypeAVD(cfg,
339*800a58d9SAndroid Build Coastguard Worker                     build_target=None,
340*800a58d9SAndroid Build Coastguard Worker                     build_id=None,
341*800a58d9SAndroid Build Coastguard Worker                     num=1,
342*800a58d9SAndroid Build Coastguard Worker                     gce_image=None,
343*800a58d9SAndroid Build Coastguard Worker                     local_disk_image=None,
344*800a58d9SAndroid Build Coastguard Worker                     cleanup=True,
345*800a58d9SAndroid Build Coastguard Worker                     serial_log_file=None,
346*800a58d9SAndroid Build Coastguard Worker                     autoconnect=False,
347*800a58d9SAndroid Build Coastguard Worker                     report_internal_ip=False,
348*800a58d9SAndroid Build Coastguard Worker                     avd_spec=None):
349*800a58d9SAndroid Build Coastguard Worker    """Creates one or multiple gce android devices.
350*800a58d9SAndroid Build Coastguard Worker
351*800a58d9SAndroid Build Coastguard Worker    Args:
352*800a58d9SAndroid Build Coastguard Worker        cfg: An AcloudConfig instance.
353*800a58d9SAndroid Build Coastguard Worker        build_target: Target name, e.g. "aosp_cf_x86_64_phone-userdebug"
354*800a58d9SAndroid Build Coastguard Worker        build_id: Build id, a string, e.g. "2263051", "P2804227"
355*800a58d9SAndroid Build Coastguard Worker        num: Number of devices to create.
356*800a58d9SAndroid Build Coastguard Worker        gce_image: string, if given, will use this gce image
357*800a58d9SAndroid Build Coastguard Worker                   instead of creating a new one.
358*800a58d9SAndroid Build Coastguard Worker                   implies cleanup=False.
359*800a58d9SAndroid Build Coastguard Worker        local_disk_image: string, path to a local disk image, e.g.
360*800a58d9SAndroid Build Coastguard Worker                          /tmp/avd-system.tar.gz
361*800a58d9SAndroid Build Coastguard Worker        cleanup: boolean, if True clean up compute engine image and
362*800a58d9SAndroid Build Coastguard Worker                 disk image in storage after creating the instance.
363*800a58d9SAndroid Build Coastguard Worker        serial_log_file: A path to a file where serial output should
364*800a58d9SAndroid Build Coastguard Worker                         be saved to.
365*800a58d9SAndroid Build Coastguard Worker        autoconnect: Create ssh tunnel(s) and adb connect after device creation.
366*800a58d9SAndroid Build Coastguard Worker        report_internal_ip: Boolean to report the internal ip instead of
367*800a58d9SAndroid Build Coastguard Worker                            external ip.
368*800a58d9SAndroid Build Coastguard Worker        avd_spec: AVDSpec object for pass hw_property.
369*800a58d9SAndroid Build Coastguard Worker
370*800a58d9SAndroid Build Coastguard Worker    Returns:
371*800a58d9SAndroid Build Coastguard Worker        A Report instance.
372*800a58d9SAndroid Build Coastguard Worker    """
373*800a58d9SAndroid Build Coastguard Worker    r = report.Report(command="create")
374*800a58d9SAndroid Build Coastguard Worker    credentials = auth.CreateCredentials(cfg)
375*800a58d9SAndroid Build Coastguard Worker    compute_client = android_compute_client.AndroidComputeClient(cfg,
376*800a58d9SAndroid Build Coastguard Worker                                                                 credentials)
377*800a58d9SAndroid Build Coastguard Worker    try:
378*800a58d9SAndroid Build Coastguard Worker        common_operations.CreateSshKeyPairIfNecessary(cfg)
379*800a58d9SAndroid Build Coastguard Worker        device_pool = AndroidVirtualDevicePool(cfg)
380*800a58d9SAndroid Build Coastguard Worker        device_pool.CreateDevices(
381*800a58d9SAndroid Build Coastguard Worker            num,
382*800a58d9SAndroid Build Coastguard Worker            build_target,
383*800a58d9SAndroid Build Coastguard Worker            build_id,
384*800a58d9SAndroid Build Coastguard Worker            gce_image,
385*800a58d9SAndroid Build Coastguard Worker            local_disk_image,
386*800a58d9SAndroid Build Coastguard Worker            cleanup,
387*800a58d9SAndroid Build Coastguard Worker            extra_data_disk_size_gb=cfg.extra_data_disk_size_gb,
388*800a58d9SAndroid Build Coastguard Worker            precreated_data_image=cfg.precreated_data_image_map.get(
389*800a58d9SAndroid Build Coastguard Worker                cfg.extra_data_disk_size_gb),
390*800a58d9SAndroid Build Coastguard Worker            avd_spec=avd_spec,
391*800a58d9SAndroid Build Coastguard Worker            extra_scopes=cfg.extra_scopes)
392*800a58d9SAndroid Build Coastguard Worker        failures = device_pool.WaitForBoot()
393*800a58d9SAndroid Build Coastguard Worker        # Write result to report.
394*800a58d9SAndroid Build Coastguard Worker        for device in device_pool.devices:
395*800a58d9SAndroid Build Coastguard Worker            ip = (device.ip.internal if report_internal_ip
396*800a58d9SAndroid Build Coastguard Worker                  else device.ip.external)
397*800a58d9SAndroid Build Coastguard Worker            device_dict = {
398*800a58d9SAndroid Build Coastguard Worker                "ip": ip,
399*800a58d9SAndroid Build Coastguard Worker                "instance_name": device.instance_name
400*800a58d9SAndroid Build Coastguard Worker            }
401*800a58d9SAndroid Build Coastguard Worker            if autoconnect:
402*800a58d9SAndroid Build Coastguard Worker                forwarded_ports = utils.AutoConnect(
403*800a58d9SAndroid Build Coastguard Worker                    ip_addr=ip,
404*800a58d9SAndroid Build Coastguard Worker                    rsa_key_file=cfg.ssh_private_key_path,
405*800a58d9SAndroid Build Coastguard Worker                    target_vnc_port=constants.GCE_VNC_PORT,
406*800a58d9SAndroid Build Coastguard Worker                    target_adb_port=constants.GCE_ADB_PORT,
407*800a58d9SAndroid Build Coastguard Worker                    ssh_user=_SSH_USER,
408*800a58d9SAndroid Build Coastguard Worker                    client_adb_port=avd_spec.client_adb_port,
409*800a58d9SAndroid Build Coastguard Worker                    extra_args_ssh_tunnel=cfg.extra_args_ssh_tunnel)
410*800a58d9SAndroid Build Coastguard Worker                device_dict[constants.VNC_PORT] = forwarded_ports.vnc_port
411*800a58d9SAndroid Build Coastguard Worker                device_dict[constants.ADB_PORT] = forwarded_ports.adb_port
412*800a58d9SAndroid Build Coastguard Worker                if avd_spec.unlock_screen:
413*800a58d9SAndroid Build Coastguard Worker                    AdbTools(forwarded_ports.adb_port).AutoUnlockScreen()
414*800a58d9SAndroid Build Coastguard Worker            if device.instance_name in failures:
415*800a58d9SAndroid Build Coastguard Worker                r.AddData(key="devices_failing_boot", value=device_dict)
416*800a58d9SAndroid Build Coastguard Worker                r.AddError(str(failures[device.instance_name]))
417*800a58d9SAndroid Build Coastguard Worker            else:
418*800a58d9SAndroid Build Coastguard Worker                r.AddData(key="devices", value=device_dict)
419*800a58d9SAndroid Build Coastguard Worker        if failures:
420*800a58d9SAndroid Build Coastguard Worker            r.SetStatus(report.Status.BOOT_FAIL)
421*800a58d9SAndroid Build Coastguard Worker        else:
422*800a58d9SAndroid Build Coastguard Worker            r.SetStatus(report.Status.SUCCESS)
423*800a58d9SAndroid Build Coastguard Worker
424*800a58d9SAndroid Build Coastguard Worker    except errors.DriverError as e:
425*800a58d9SAndroid Build Coastguard Worker        r.AddError(str(e))
426*800a58d9SAndroid Build Coastguard Worker        r.SetStatus(report.Status.FAIL)
427*800a58d9SAndroid Build Coastguard Worker    finally:
428*800a58d9SAndroid Build Coastguard Worker         # Let's do our best to obtain the serial log, even though this
429*800a58d9SAndroid Build Coastguard Worker         # could fail in case of failed boots.
430*800a58d9SAndroid Build Coastguard Worker        if serial_log_file:
431*800a58d9SAndroid Build Coastguard Worker            instance_names=[d.instance_name for d in device_pool.devices]
432*800a58d9SAndroid Build Coastguard Worker            try:
433*800a58d9SAndroid Build Coastguard Worker                _FetchSerialLogsFromDevices(
434*800a58d9SAndroid Build Coastguard Worker                    compute_client,
435*800a58d9SAndroid Build Coastguard Worker                    instance_names=instance_names,
436*800a58d9SAndroid Build Coastguard Worker                    port=constants.DEFAULT_SERIAL_PORT,
437*800a58d9SAndroid Build Coastguard Worker                    output_file=serial_log_file)
438*800a58d9SAndroid Build Coastguard Worker            except Exception as log_err:
439*800a58d9SAndroid Build Coastguard Worker                logging.warning("Failed to obtain serial logs from %s", ", ".join(instance_names))
440*800a58d9SAndroid Build Coastguard Worker    return r
441*800a58d9SAndroid Build Coastguard Worker
442*800a58d9SAndroid Build Coastguard Worker
443*800a58d9SAndroid Build Coastguard Workerdef DeleteAndroidVirtualDevices(cfg, instance_names, default_report=None):
444*800a58d9SAndroid Build Coastguard Worker    """Deletes android devices.
445*800a58d9SAndroid Build Coastguard Worker
446*800a58d9SAndroid Build Coastguard Worker    Args:
447*800a58d9SAndroid Build Coastguard Worker        cfg: An AcloudConfig instance.
448*800a58d9SAndroid Build Coastguard Worker        instance_names: A list of names of the instances to delete.
449*800a58d9SAndroid Build Coastguard Worker        default_report: A initialized Report instance.
450*800a58d9SAndroid Build Coastguard Worker
451*800a58d9SAndroid Build Coastguard Worker    Returns:
452*800a58d9SAndroid Build Coastguard Worker        A Report instance.
453*800a58d9SAndroid Build Coastguard Worker    """
454*800a58d9SAndroid Build Coastguard Worker    # delete, failed, error_msgs are used to record result.
455*800a58d9SAndroid Build Coastguard Worker    deleted = []
456*800a58d9SAndroid Build Coastguard Worker    failed = []
457*800a58d9SAndroid Build Coastguard Worker    error_msgs = []
458*800a58d9SAndroid Build Coastguard Worker
459*800a58d9SAndroid Build Coastguard Worker    r = default_report if default_report else report.Report(command="delete")
460*800a58d9SAndroid Build Coastguard Worker    credentials = auth.CreateCredentials(cfg)
461*800a58d9SAndroid Build Coastguard Worker    compute_client = android_compute_client.AndroidComputeClient(cfg,
462*800a58d9SAndroid Build Coastguard Worker                                                                 credentials)
463*800a58d9SAndroid Build Coastguard Worker    zone_instances = compute_client.GetZonesByInstances(instance_names)
464*800a58d9SAndroid Build Coastguard Worker
465*800a58d9SAndroid Build Coastguard Worker    try:
466*800a58d9SAndroid Build Coastguard Worker        for zone, instances in zone_instances.items():
467*800a58d9SAndroid Build Coastguard Worker            deleted_ins, failed_ins, error_ins = compute_client.DeleteInstances(
468*800a58d9SAndroid Build Coastguard Worker                instances, zone)
469*800a58d9SAndroid Build Coastguard Worker            deleted.extend(deleted_ins)
470*800a58d9SAndroid Build Coastguard Worker            failed.extend(failed_ins)
471*800a58d9SAndroid Build Coastguard Worker            error_msgs.extend(error_ins)
472*800a58d9SAndroid Build Coastguard Worker        AddDeletionResultToReport(
473*800a58d9SAndroid Build Coastguard Worker            r, deleted,
474*800a58d9SAndroid Build Coastguard Worker            failed, error_msgs,
475*800a58d9SAndroid Build Coastguard Worker            resource_name="instance")
476*800a58d9SAndroid Build Coastguard Worker        if r.status == report.Status.UNKNOWN:
477*800a58d9SAndroid Build Coastguard Worker            r.SetStatus(report.Status.SUCCESS)
478*800a58d9SAndroid Build Coastguard Worker    except errors.DriverError as e:
479*800a58d9SAndroid Build Coastguard Worker        r.AddError(str(e))
480*800a58d9SAndroid Build Coastguard Worker        r.SetStatus(report.Status.FAIL)
481*800a58d9SAndroid Build Coastguard Worker    return r
482*800a58d9SAndroid Build Coastguard Worker
483*800a58d9SAndroid Build Coastguard Worker
484*800a58d9SAndroid Build Coastguard Workerdef CheckAccess(cfg):
485*800a58d9SAndroid Build Coastguard Worker    """Check if user has access.
486*800a58d9SAndroid Build Coastguard Worker
487*800a58d9SAndroid Build Coastguard Worker    Args:
488*800a58d9SAndroid Build Coastguard Worker         cfg: An AcloudConfig instance.
489*800a58d9SAndroid Build Coastguard Worker    """
490*800a58d9SAndroid Build Coastguard Worker    credentials = auth.CreateCredentials(cfg)
491*800a58d9SAndroid Build Coastguard Worker    compute_client = android_compute_client.AndroidComputeClient(
492*800a58d9SAndroid Build Coastguard Worker        cfg, credentials)
493*800a58d9SAndroid Build Coastguard Worker    logger.info("Checking if user has access to project %s", cfg.project)
494*800a58d9SAndroid Build Coastguard Worker    if not compute_client.CheckAccess():
495*800a58d9SAndroid Build Coastguard Worker        logger.error("User does not have access to project %s", cfg.project)
496*800a58d9SAndroid Build Coastguard Worker        # Print here so that command line user can see it.
497*800a58d9SAndroid Build Coastguard Worker        print("Looks like you do not have access to %s. " % cfg.project)
498*800a58d9SAndroid Build Coastguard Worker        if cfg.project in cfg.no_project_access_msg_map:
499*800a58d9SAndroid Build Coastguard Worker            print(cfg.no_project_access_msg_map[cfg.project])
500