xref: /aosp_15_r20/tools/acloud/public/actions/common_operations.py (revision 800a58d989c669b8eb8a71d8df53b1ba3d411444)
1*800a58d9SAndroid Build Coastguard Worker#!/usr/bin/env python
2*800a58d9SAndroid Build Coastguard Worker#
3*800a58d9SAndroid Build Coastguard Worker# Copyright 2018 - 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"""Common operations to create remote devices."""
17*800a58d9SAndroid Build Coastguard Worker
18*800a58d9SAndroid Build Coastguard Workerimport logging
19*800a58d9SAndroid Build Coastguard Workerimport os
20*800a58d9SAndroid Build Coastguard Worker
21*800a58d9SAndroid Build Coastguard Workerfrom acloud import errors
22*800a58d9SAndroid Build Coastguard Workerfrom acloud.public import avd
23*800a58d9SAndroid Build Coastguard Workerfrom acloud.public import report
24*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal import constants
25*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import utils
26*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib.adb_tools import AdbTools
27*800a58d9SAndroid Build Coastguard Worker
28*800a58d9SAndroid Build Coastguard Worker
29*800a58d9SAndroid Build Coastguard Workerlogger = logging.getLogger(__name__)
30*800a58d9SAndroid Build Coastguard Worker_GCE_QUOTA_ERROR_KEYWORDS = [
31*800a58d9SAndroid Build Coastguard Worker    "Quota exceeded for quota",
32*800a58d9SAndroid Build Coastguard Worker    "ZONE_RESOURCE_POOL_EXHAUSTED",
33*800a58d9SAndroid Build Coastguard Worker    "ZONE_RESOURCE_POOL_EXHAUSTED_WITH_DETAILS"]
34*800a58d9SAndroid Build Coastguard Worker_DICT_ERROR_TYPE = {
35*800a58d9SAndroid Build Coastguard Worker    constants.STAGE_INIT: constants.ACLOUD_INIT_ERROR,
36*800a58d9SAndroid Build Coastguard Worker    constants.STAGE_GCE: constants.ACLOUD_CREATE_GCE_ERROR,
37*800a58d9SAndroid Build Coastguard Worker    constants.STAGE_SSH_CONNECT: constants.ACLOUD_SSH_CONNECT_ERROR,
38*800a58d9SAndroid Build Coastguard Worker    constants.STAGE_ARTIFACT: constants.ACLOUD_DOWNLOAD_ARTIFACT_ERROR,
39*800a58d9SAndroid Build Coastguard Worker    constants.STAGE_BOOT_UP: constants.ACLOUD_BOOT_UP_ERROR,
40*800a58d9SAndroid Build Coastguard Worker}
41*800a58d9SAndroid Build Coastguard Worker
42*800a58d9SAndroid Build Coastguard Worker
43*800a58d9SAndroid Build Coastguard Workerdef CreateSshKeyPairIfNecessary(cfg):
44*800a58d9SAndroid Build Coastguard Worker    """Create ssh key pair if necessary.
45*800a58d9SAndroid Build Coastguard Worker
46*800a58d9SAndroid Build Coastguard Worker    Args:
47*800a58d9SAndroid Build Coastguard Worker        cfg: An Acloudconfig instance.
48*800a58d9SAndroid Build Coastguard Worker
49*800a58d9SAndroid Build Coastguard Worker    Raises:
50*800a58d9SAndroid Build Coastguard Worker        error.DriverError: If it falls into an unexpected condition.
51*800a58d9SAndroid Build Coastguard Worker    """
52*800a58d9SAndroid Build Coastguard Worker    if not cfg.ssh_public_key_path:
53*800a58d9SAndroid Build Coastguard Worker        logger.warning(
54*800a58d9SAndroid Build Coastguard Worker            "ssh_public_key_path is not specified in acloud config. "
55*800a58d9SAndroid Build Coastguard Worker            "Project-wide public key will "
56*800a58d9SAndroid Build Coastguard Worker            "be used when creating AVD instances. "
57*800a58d9SAndroid Build Coastguard Worker            "Please ensure you have the correct private half of "
58*800a58d9SAndroid Build Coastguard Worker            "a project-wide public key if you want to ssh into the "
59*800a58d9SAndroid Build Coastguard Worker            "instances after creation.")
60*800a58d9SAndroid Build Coastguard Worker    elif cfg.ssh_public_key_path and not cfg.ssh_private_key_path:
61*800a58d9SAndroid Build Coastguard Worker        logger.warning(
62*800a58d9SAndroid Build Coastguard Worker            "Only ssh_public_key_path is specified in acloud config, "
63*800a58d9SAndroid Build Coastguard Worker            "but ssh_private_key_path is missing. "
64*800a58d9SAndroid Build Coastguard Worker            "Please ensure you have the correct private half "
65*800a58d9SAndroid Build Coastguard Worker            "if you want to ssh into the instances after creation.")
66*800a58d9SAndroid Build Coastguard Worker    elif cfg.ssh_public_key_path and cfg.ssh_private_key_path:
67*800a58d9SAndroid Build Coastguard Worker        utils.CreateSshKeyPairIfNotExist(cfg.ssh_private_key_path,
68*800a58d9SAndroid Build Coastguard Worker                                         cfg.ssh_public_key_path)
69*800a58d9SAndroid Build Coastguard Worker    else:
70*800a58d9SAndroid Build Coastguard Worker        # Should never reach here.
71*800a58d9SAndroid Build Coastguard Worker        raise errors.DriverError(
72*800a58d9SAndroid Build Coastguard Worker            "Unexpected error in CreateSshKeyPairIfNecessary")
73*800a58d9SAndroid Build Coastguard Worker
74*800a58d9SAndroid Build Coastguard Worker
75*800a58d9SAndroid Build Coastguard Workerclass DevicePool:
76*800a58d9SAndroid Build Coastguard Worker    """A class that manages a pool of virtual devices.
77*800a58d9SAndroid Build Coastguard Worker
78*800a58d9SAndroid Build Coastguard Worker    Attributes:
79*800a58d9SAndroid Build Coastguard Worker        devices: A list of devices in the pool.
80*800a58d9SAndroid Build Coastguard Worker    """
81*800a58d9SAndroid Build Coastguard Worker
82*800a58d9SAndroid Build Coastguard Worker    def __init__(self, device_factory, devices=None):
83*800a58d9SAndroid Build Coastguard Worker        """Constructs a new DevicePool.
84*800a58d9SAndroid Build Coastguard Worker
85*800a58d9SAndroid Build Coastguard Worker        Args:
86*800a58d9SAndroid Build Coastguard Worker            device_factory: A device factory capable of producing a goldfish or
87*800a58d9SAndroid Build Coastguard Worker                cuttlefish device. The device factory must expose an attribute with
88*800a58d9SAndroid Build Coastguard Worker                the credentials that can be used to retrieve information from the
89*800a58d9SAndroid Build Coastguard Worker                constructed device.
90*800a58d9SAndroid Build Coastguard Worker            devices: List of devices managed by this pool.
91*800a58d9SAndroid Build Coastguard Worker        """
92*800a58d9SAndroid Build Coastguard Worker        self._devices = devices or []
93*800a58d9SAndroid Build Coastguard Worker        self._device_factory = device_factory
94*800a58d9SAndroid Build Coastguard Worker        self._compute_client = device_factory.GetComputeClient()
95*800a58d9SAndroid Build Coastguard Worker
96*800a58d9SAndroid Build Coastguard Worker    def CreateDevices(self, num):
97*800a58d9SAndroid Build Coastguard Worker        """Creates |num| devices for given build_target and build_id.
98*800a58d9SAndroid Build Coastguard Worker
99*800a58d9SAndroid Build Coastguard Worker        Args:
100*800a58d9SAndroid Build Coastguard Worker            num: Number of devices to create.
101*800a58d9SAndroid Build Coastguard Worker        """
102*800a58d9SAndroid Build Coastguard Worker        # Create host instances for cuttlefish/goldfish device.
103*800a58d9SAndroid Build Coastguard Worker        # Currently one instance supports only 1 device.
104*800a58d9SAndroid Build Coastguard Worker        for _ in range(num):
105*800a58d9SAndroid Build Coastguard Worker            instance = self._device_factory.CreateInstance()
106*800a58d9SAndroid Build Coastguard Worker            ip = self._compute_client.GetInstanceIP(instance)
107*800a58d9SAndroid Build Coastguard Worker            time_info = {
108*800a58d9SAndroid Build Coastguard Worker                stage: round(exec_time, 2) for stage, exec_time in
109*800a58d9SAndroid Build Coastguard Worker                getattr(self._compute_client, "execution_time", {}).items()}
110*800a58d9SAndroid Build Coastguard Worker            stage = self._compute_client.stage if hasattr(
111*800a58d9SAndroid Build Coastguard Worker                self._compute_client, "stage") else 0
112*800a58d9SAndroid Build Coastguard Worker            openwrt = self._compute_client.openwrt if hasattr(
113*800a58d9SAndroid Build Coastguard Worker                self._compute_client, "openwrt") else False
114*800a58d9SAndroid Build Coastguard Worker            gce_hostname = self._compute_client.gce_hostname if hasattr(
115*800a58d9SAndroid Build Coastguard Worker                self._compute_client, "gce_hostname") else None
116*800a58d9SAndroid Build Coastguard Worker            self.devices.append(
117*800a58d9SAndroid Build Coastguard Worker                avd.AndroidVirtualDevice(ip=ip, instance_name=instance,
118*800a58d9SAndroid Build Coastguard Worker                                         time_info=time_info, stage=stage,
119*800a58d9SAndroid Build Coastguard Worker                                         openwrt=openwrt, gce_hostname=gce_hostname))
120*800a58d9SAndroid Build Coastguard Worker
121*800a58d9SAndroid Build Coastguard Worker    @utils.TimeExecute(function_description="Waiting for AVD(s) to boot up",
122*800a58d9SAndroid Build Coastguard Worker                       result_evaluator=utils.BootEvaluator)
123*800a58d9SAndroid Build Coastguard Worker    def WaitForBoot(self, boot_timeout_secs):
124*800a58d9SAndroid Build Coastguard Worker        """Waits for all devices to boot up.
125*800a58d9SAndroid Build Coastguard Worker
126*800a58d9SAndroid Build Coastguard Worker        Args:
127*800a58d9SAndroid Build Coastguard Worker            boot_timeout_secs: Integer, the maximum time in seconds used to
128*800a58d9SAndroid Build Coastguard Worker                               wait for the AVD to boot.
129*800a58d9SAndroid Build Coastguard Worker
130*800a58d9SAndroid Build Coastguard Worker        Returns:
131*800a58d9SAndroid Build Coastguard Worker            A dictionary that contains all the failures.
132*800a58d9SAndroid Build Coastguard Worker            The key is the name of the instance that fails to boot,
133*800a58d9SAndroid Build Coastguard Worker            and the value is an errors.DeviceBootError object.
134*800a58d9SAndroid Build Coastguard Worker        """
135*800a58d9SAndroid Build Coastguard Worker        failures = {}
136*800a58d9SAndroid Build Coastguard Worker        for device in self._devices:
137*800a58d9SAndroid Build Coastguard Worker            try:
138*800a58d9SAndroid Build Coastguard Worker                self._compute_client.WaitForBoot(device.instance_name, boot_timeout_secs)
139*800a58d9SAndroid Build Coastguard Worker            except errors.DeviceBootError as e:
140*800a58d9SAndroid Build Coastguard Worker                failures[device.instance_name] = e
141*800a58d9SAndroid Build Coastguard Worker        return failures
142*800a58d9SAndroid Build Coastguard Worker
143*800a58d9SAndroid Build Coastguard Worker    def UpdateReport(self, reporter):
144*800a58d9SAndroid Build Coastguard Worker        """Update report from compute client.
145*800a58d9SAndroid Build Coastguard Worker
146*800a58d9SAndroid Build Coastguard Worker        Args:
147*800a58d9SAndroid Build Coastguard Worker            reporter: Report object.
148*800a58d9SAndroid Build Coastguard Worker        """
149*800a58d9SAndroid Build Coastguard Worker        reporter.UpdateData(self._compute_client.dict_report)
150*800a58d9SAndroid Build Coastguard Worker
151*800a58d9SAndroid Build Coastguard Worker    def CollectSerialPortLogs(self, output_file,
152*800a58d9SAndroid Build Coastguard Worker                              port=constants.DEFAULT_SERIAL_PORT):
153*800a58d9SAndroid Build Coastguard Worker        """Tar the instance serial logs into specified output_file.
154*800a58d9SAndroid Build Coastguard Worker
155*800a58d9SAndroid Build Coastguard Worker        Args:
156*800a58d9SAndroid Build Coastguard Worker            output_file: String, the output tar file path
157*800a58d9SAndroid Build Coastguard Worker            port: The serial port number to be collected
158*800a58d9SAndroid Build Coastguard Worker        """
159*800a58d9SAndroid Build Coastguard Worker        # For emulator, the serial log is the virtual host serial log.
160*800a58d9SAndroid Build Coastguard Worker        # For GCE AVD device, the serial log is the AVD device serial log.
161*800a58d9SAndroid Build Coastguard Worker        with utils.TempDir() as tempdir:
162*800a58d9SAndroid Build Coastguard Worker            src_dict = {}
163*800a58d9SAndroid Build Coastguard Worker            for device in self._devices:
164*800a58d9SAndroid Build Coastguard Worker                logger.info("Store instance %s serial port %s output to %s",
165*800a58d9SAndroid Build Coastguard Worker                            device.instance_name, port, output_file)
166*800a58d9SAndroid Build Coastguard Worker                serial_log = self._compute_client.GetSerialPortOutput(
167*800a58d9SAndroid Build Coastguard Worker                    instance=device.instance_name, port=port)
168*800a58d9SAndroid Build Coastguard Worker                file_name = "%s_serial_%s.log" % (device.instance_name, port)
169*800a58d9SAndroid Build Coastguard Worker                file_path = os.path.join(tempdir, file_name)
170*800a58d9SAndroid Build Coastguard Worker                src_dict[file_path] = file_name
171*800a58d9SAndroid Build Coastguard Worker                with open(file_path, "w") as f:
172*800a58d9SAndroid Build Coastguard Worker                    f.write(serial_log.encode("utf-8"))
173*800a58d9SAndroid Build Coastguard Worker            utils.MakeTarFile(src_dict, output_file)
174*800a58d9SAndroid Build Coastguard Worker
175*800a58d9SAndroid Build Coastguard Worker    def SetDeviceBuildInfo(self):
176*800a58d9SAndroid Build Coastguard Worker        """Add devices build info."""
177*800a58d9SAndroid Build Coastguard Worker        for device in self._devices:
178*800a58d9SAndroid Build Coastguard Worker            device.build_info = self._device_factory.GetBuildInfoDict()
179*800a58d9SAndroid Build Coastguard Worker
180*800a58d9SAndroid Build Coastguard Worker    @property
181*800a58d9SAndroid Build Coastguard Worker    def devices(self):
182*800a58d9SAndroid Build Coastguard Worker        """Returns a list of devices in the pool.
183*800a58d9SAndroid Build Coastguard Worker
184*800a58d9SAndroid Build Coastguard Worker        Returns:
185*800a58d9SAndroid Build Coastguard Worker            A list of devices in the pool.
186*800a58d9SAndroid Build Coastguard Worker        """
187*800a58d9SAndroid Build Coastguard Worker        return self._devices
188*800a58d9SAndroid Build Coastguard Worker
189*800a58d9SAndroid Build Coastguard Workerdef _GetErrorType(error):
190*800a58d9SAndroid Build Coastguard Worker    """Get proper error type from the exception error.
191*800a58d9SAndroid Build Coastguard Worker
192*800a58d9SAndroid Build Coastguard Worker    Args:
193*800a58d9SAndroid Build Coastguard Worker        error: errors object.
194*800a58d9SAndroid Build Coastguard Worker
195*800a58d9SAndroid Build Coastguard Worker    Returns:
196*800a58d9SAndroid Build Coastguard Worker        String of error type. e.g. "ACLOUD_BOOT_UP_ERROR".
197*800a58d9SAndroid Build Coastguard Worker    """
198*800a58d9SAndroid Build Coastguard Worker    if isinstance(error, errors.CheckGCEZonesQuotaError):
199*800a58d9SAndroid Build Coastguard Worker        return constants.GCE_QUOTA_ERROR
200*800a58d9SAndroid Build Coastguard Worker    if isinstance(error, errors.DownloadArtifactError):
201*800a58d9SAndroid Build Coastguard Worker        return constants.ACLOUD_DOWNLOAD_ARTIFACT_ERROR
202*800a58d9SAndroid Build Coastguard Worker    if isinstance(error, errors.DeviceConnectionError):
203*800a58d9SAndroid Build Coastguard Worker        return constants.ACLOUD_SSH_CONNECT_ERROR
204*800a58d9SAndroid Build Coastguard Worker    for keyword in _GCE_QUOTA_ERROR_KEYWORDS:
205*800a58d9SAndroid Build Coastguard Worker        if keyword in str(error):
206*800a58d9SAndroid Build Coastguard Worker            return constants.GCE_QUOTA_ERROR
207*800a58d9SAndroid Build Coastguard Worker    return constants.ACLOUD_UNKNOWN_ERROR
208*800a58d9SAndroid Build Coastguard Worker
209*800a58d9SAndroid Build Coastguard Worker# pylint: disable=too-many-locals,unused-argument,too-many-branches,too-many-statements
210*800a58d9SAndroid Build Coastguard Workerdef CreateDevices(command, cfg, device_factory, num, avd_type,
211*800a58d9SAndroid Build Coastguard Worker                  report_internal_ip=False, autoconnect=False,
212*800a58d9SAndroid Build Coastguard Worker                  serial_log_file=None, client_adb_port=None,
213*800a58d9SAndroid Build Coastguard Worker                  boot_timeout_secs=None, unlock_screen=False,
214*800a58d9SAndroid Build Coastguard Worker                  wait_for_boot=True, connect_webrtc=False,
215*800a58d9SAndroid Build Coastguard Worker                  ssh_private_key_path=None,
216*800a58d9SAndroid Build Coastguard Worker                  ssh_user=constants.GCE_USER):
217*800a58d9SAndroid Build Coastguard Worker    """Create a set of devices using the given factory.
218*800a58d9SAndroid Build Coastguard Worker
219*800a58d9SAndroid Build Coastguard Worker    Main jobs in create devices.
220*800a58d9SAndroid Build Coastguard Worker        1. Create GCE instance: Launch instance in GCP(Google Cloud Platform).
221*800a58d9SAndroid Build Coastguard Worker        2. Starting up AVD: Wait device boot up.
222*800a58d9SAndroid Build Coastguard Worker
223*800a58d9SAndroid Build Coastguard Worker    Args:
224*800a58d9SAndroid Build Coastguard Worker        command: The name of the command, used for reporting.
225*800a58d9SAndroid Build Coastguard Worker        cfg: An AcloudConfig instance.
226*800a58d9SAndroid Build Coastguard Worker        device_factory: A factory capable of producing a single device.
227*800a58d9SAndroid Build Coastguard Worker        num: The number of devices to create.
228*800a58d9SAndroid Build Coastguard Worker        avd_type: String, the AVD type(cuttlefish, goldfish...).
229*800a58d9SAndroid Build Coastguard Worker        report_internal_ip: Boolean to report the internal ip instead of
230*800a58d9SAndroid Build Coastguard Worker                            external ip.
231*800a58d9SAndroid Build Coastguard Worker        serial_log_file: String, the file path to tar the serial logs.
232*800a58d9SAndroid Build Coastguard Worker        autoconnect: Boolean, whether to auto connect to device.
233*800a58d9SAndroid Build Coastguard Worker        client_adb_port: Integer, Specify port for adb forwarding.
234*800a58d9SAndroid Build Coastguard Worker        boot_timeout_secs: Integer, boot timeout secs.
235*800a58d9SAndroid Build Coastguard Worker        unlock_screen: Boolean, whether to unlock screen after invoke vnc client.
236*800a58d9SAndroid Build Coastguard Worker        wait_for_boot: Boolean, True to check serial log include boot up
237*800a58d9SAndroid Build Coastguard Worker                       message.
238*800a58d9SAndroid Build Coastguard Worker        connect_webrtc: Boolean, whether to auto connect webrtc to device.
239*800a58d9SAndroid Build Coastguard Worker        ssh_private_key_path: String, the private key for SSH tunneling.
240*800a58d9SAndroid Build Coastguard Worker        ssh_user: String, the user name for SSH tunneling.
241*800a58d9SAndroid Build Coastguard Worker
242*800a58d9SAndroid Build Coastguard Worker    Raises:
243*800a58d9SAndroid Build Coastguard Worker        errors: Create instance fail.
244*800a58d9SAndroid Build Coastguard Worker
245*800a58d9SAndroid Build Coastguard Worker    Returns:
246*800a58d9SAndroid Build Coastguard Worker        A Report instance.
247*800a58d9SAndroid Build Coastguard Worker    """
248*800a58d9SAndroid Build Coastguard Worker    reporter = report.Report(command=command)
249*800a58d9SAndroid Build Coastguard Worker    try:
250*800a58d9SAndroid Build Coastguard Worker        CreateSshKeyPairIfNecessary(cfg)
251*800a58d9SAndroid Build Coastguard Worker        device_pool = DevicePool(device_factory)
252*800a58d9SAndroid Build Coastguard Worker        device_pool.CreateDevices(num)
253*800a58d9SAndroid Build Coastguard Worker        device_pool.SetDeviceBuildInfo()
254*800a58d9SAndroid Build Coastguard Worker        if wait_for_boot:
255*800a58d9SAndroid Build Coastguard Worker            failures = device_pool.WaitForBoot(boot_timeout_secs)
256*800a58d9SAndroid Build Coastguard Worker        else:
257*800a58d9SAndroid Build Coastguard Worker            failures = device_factory.GetFailures()
258*800a58d9SAndroid Build Coastguard Worker
259*800a58d9SAndroid Build Coastguard Worker        if failures:
260*800a58d9SAndroid Build Coastguard Worker            reporter.SetStatus(report.Status.BOOT_FAIL)
261*800a58d9SAndroid Build Coastguard Worker        else:
262*800a58d9SAndroid Build Coastguard Worker            reporter.SetStatus(report.Status.SUCCESS)
263*800a58d9SAndroid Build Coastguard Worker
264*800a58d9SAndroid Build Coastguard Worker        # Collect logs
265*800a58d9SAndroid Build Coastguard Worker        logs = device_factory.GetLogs()
266*800a58d9SAndroid Build Coastguard Worker        if serial_log_file:
267*800a58d9SAndroid Build Coastguard Worker            device_pool.CollectSerialPortLogs(
268*800a58d9SAndroid Build Coastguard Worker                serial_log_file, port=constants.DEFAULT_SERIAL_PORT)
269*800a58d9SAndroid Build Coastguard Worker
270*800a58d9SAndroid Build Coastguard Worker        device_pool.UpdateReport(reporter)
271*800a58d9SAndroid Build Coastguard Worker        # Write result to report.
272*800a58d9SAndroid Build Coastguard Worker        for device in device_pool.devices:
273*800a58d9SAndroid Build Coastguard Worker            ip = (device.ip.internal if report_internal_ip
274*800a58d9SAndroid Build Coastguard Worker                  else device.ip.external)
275*800a58d9SAndroid Build Coastguard Worker            extra_args_ssh_tunnel=cfg.extra_args_ssh_tunnel
276*800a58d9SAndroid Build Coastguard Worker            # TODO(b/154175542): Report multiple devices.
277*800a58d9SAndroid Build Coastguard Worker            vnc_ports = device_factory.GetVncPorts()
278*800a58d9SAndroid Build Coastguard Worker            adb_ports = device_factory.GetAdbPorts()
279*800a58d9SAndroid Build Coastguard Worker            if not vnc_ports[0] and not adb_ports[0]:
280*800a58d9SAndroid Build Coastguard Worker                vnc_ports[0], adb_ports[0] = utils.AVD_PORT_DICT[avd_type]
281*800a58d9SAndroid Build Coastguard Worker
282*800a58d9SAndroid Build Coastguard Worker            device_dict = {
283*800a58d9SAndroid Build Coastguard Worker                "ip": ip + (":" + str(adb_ports[0]) if adb_ports[0] else ""),
284*800a58d9SAndroid Build Coastguard Worker                "instance_name": device.instance_name
285*800a58d9SAndroid Build Coastguard Worker            }
286*800a58d9SAndroid Build Coastguard Worker            if device.build_info:
287*800a58d9SAndroid Build Coastguard Worker                device_dict.update(device.build_info)
288*800a58d9SAndroid Build Coastguard Worker            if device.time_info:
289*800a58d9SAndroid Build Coastguard Worker                device_dict.update(device.time_info)
290*800a58d9SAndroid Build Coastguard Worker            if device.openwrt:
291*800a58d9SAndroid Build Coastguard Worker                device_dict.update(device_factory.GetOpenWrtInfoDict())
292*800a58d9SAndroid Build Coastguard Worker            if device.gce_hostname:
293*800a58d9SAndroid Build Coastguard Worker                device_dict[constants.GCE_HOSTNAME] = device.gce_hostname
294*800a58d9SAndroid Build Coastguard Worker                logger.debug(
295*800a58d9SAndroid Build Coastguard Worker                    "To connect with hostname, erase the extra_args_ssh_tunnel: %s",
296*800a58d9SAndroid Build Coastguard Worker                    extra_args_ssh_tunnel)
297*800a58d9SAndroid Build Coastguard Worker                extra_args_ssh_tunnel=""
298*800a58d9SAndroid Build Coastguard Worker            if autoconnect and reporter.status == report.Status.SUCCESS:
299*800a58d9SAndroid Build Coastguard Worker                forwarded_ports = _EstablishAdbVncConnections(
300*800a58d9SAndroid Build Coastguard Worker                    device.gce_hostname or ip, vnc_ports, adb_ports,
301*800a58d9SAndroid Build Coastguard Worker                    client_adb_port, ssh_user,
302*800a58d9SAndroid Build Coastguard Worker                    ssh_private_key_path=(ssh_private_key_path or
303*800a58d9SAndroid Build Coastguard Worker                                          cfg.ssh_private_key_path),
304*800a58d9SAndroid Build Coastguard Worker                    extra_args_ssh_tunnel=extra_args_ssh_tunnel,
305*800a58d9SAndroid Build Coastguard Worker                    unlock_screen=unlock_screen)
306*800a58d9SAndroid Build Coastguard Worker                if forwarded_ports:
307*800a58d9SAndroid Build Coastguard Worker                    forwarded_port = forwarded_ports[0]
308*800a58d9SAndroid Build Coastguard Worker                    device_dict[constants.VNC_PORT] = forwarded_port.vnc_port
309*800a58d9SAndroid Build Coastguard Worker                    device_dict[constants.ADB_PORT] = forwarded_port.adb_port
310*800a58d9SAndroid Build Coastguard Worker                    device_dict[constants.DEVICE_SERIAL] = (
311*800a58d9SAndroid Build Coastguard Worker                        constants.REMOTE_INSTANCE_ADB_SERIAL %
312*800a58d9SAndroid Build Coastguard Worker                        forwarded_port.adb_port)
313*800a58d9SAndroid Build Coastguard Worker            if connect_webrtc and reporter.status == report.Status.SUCCESS:
314*800a58d9SAndroid Build Coastguard Worker                webrtc_local_port = utils.PickFreePort()
315*800a58d9SAndroid Build Coastguard Worker                device_dict[constants.WEBRTC_PORT] = webrtc_local_port
316*800a58d9SAndroid Build Coastguard Worker                utils.EstablishWebRTCSshTunnel(
317*800a58d9SAndroid Build Coastguard Worker                    ip_addr=device.gce_hostname or ip,
318*800a58d9SAndroid Build Coastguard Worker                    webrtc_local_port=webrtc_local_port,
319*800a58d9SAndroid Build Coastguard Worker                    rsa_key_file=(ssh_private_key_path or
320*800a58d9SAndroid Build Coastguard Worker                                  cfg.ssh_private_key_path),
321*800a58d9SAndroid Build Coastguard Worker                    ssh_user=ssh_user,
322*800a58d9SAndroid Build Coastguard Worker                    extra_args_ssh_tunnel=extra_args_ssh_tunnel)
323*800a58d9SAndroid Build Coastguard Worker            if device.instance_name in logs:
324*800a58d9SAndroid Build Coastguard Worker                device_dict[constants.LOGS] = logs[device.instance_name]
325*800a58d9SAndroid Build Coastguard Worker            if hasattr(device_factory, 'GetFetchCvdWrapperLogIfExist'):
326*800a58d9SAndroid Build Coastguard Worker                fetch_cvd_wrapper_log = device_factory.GetFetchCvdWrapperLogIfExist()
327*800a58d9SAndroid Build Coastguard Worker                if fetch_cvd_wrapper_log:
328*800a58d9SAndroid Build Coastguard Worker                    device_dict["fetch_cvd_wrapper_log"] = fetch_cvd_wrapper_log
329*800a58d9SAndroid Build Coastguard Worker            if device.instance_name in failures:
330*800a58d9SAndroid Build Coastguard Worker                reporter.SetErrorType(constants.ACLOUD_BOOT_UP_ERROR)
331*800a58d9SAndroid Build Coastguard Worker                if device.stage:
332*800a58d9SAndroid Build Coastguard Worker                    reporter.SetErrorType(_DICT_ERROR_TYPE[device.stage])
333*800a58d9SAndroid Build Coastguard Worker                reporter.AddData(key="devices_failing_boot", value=device_dict)
334*800a58d9SAndroid Build Coastguard Worker                reporter.AddError(str(failures[device.instance_name]))
335*800a58d9SAndroid Build Coastguard Worker            else:
336*800a58d9SAndroid Build Coastguard Worker                reporter.AddData(key="devices", value=device_dict)
337*800a58d9SAndroid Build Coastguard Worker    except (errors.DriverError, errors.CheckGCEZonesQuotaError) as e:
338*800a58d9SAndroid Build Coastguard Worker        reporter.SetErrorType(_GetErrorType(e))
339*800a58d9SAndroid Build Coastguard Worker        reporter.AddError(str(e))
340*800a58d9SAndroid Build Coastguard Worker        reporter.SetStatus(report.Status.FAIL)
341*800a58d9SAndroid Build Coastguard Worker    return reporter
342*800a58d9SAndroid Build Coastguard Worker
343*800a58d9SAndroid Build Coastguard Worker
344*800a58d9SAndroid Build Coastguard Workerdef _EstablishAdbVncConnections(ip, vnc_ports, adb_ports, client_adb_port,
345*800a58d9SAndroid Build Coastguard Worker                                ssh_user, ssh_private_key_path,
346*800a58d9SAndroid Build Coastguard Worker                                extra_args_ssh_tunnel, unlock_screen):
347*800a58d9SAndroid Build Coastguard Worker    """Establish the adb and vnc connections.
348*800a58d9SAndroid Build Coastguard Worker
349*800a58d9SAndroid Build Coastguard Worker    Create the ssh tunnels with adb ports and vnc ports. Then unlock the device
350*800a58d9SAndroid Build Coastguard Worker    screen via the adb port.
351*800a58d9SAndroid Build Coastguard Worker
352*800a58d9SAndroid Build Coastguard Worker    Args:
353*800a58d9SAndroid Build Coastguard Worker        ip: String, the IPv4 address.
354*800a58d9SAndroid Build Coastguard Worker        vnc_ports: List of integer, the vnc ports.
355*800a58d9SAndroid Build Coastguard Worker        adb_ports: List of integer, the adb ports.
356*800a58d9SAndroid Build Coastguard Worker        client_adb_port: Integer, Specify port for adb forwarding.
357*800a58d9SAndroid Build Coastguard Worker        ssh_user: String, the user name for SSH tunneling.
358*800a58d9SAndroid Build Coastguard Worker        ssh_private_key_path: String, the private key for SSH tunneling.
359*800a58d9SAndroid Build Coastguard Worker        extra_args_ssh_tunnel: String, extra args for ssh tunnel connection.
360*800a58d9SAndroid Build Coastguard Worker        unlock_screen: Boolean, whether to unlock screen after invoking vnc client.
361*800a58d9SAndroid Build Coastguard Worker
362*800a58d9SAndroid Build Coastguard Worker    Returns:
363*800a58d9SAndroid Build Coastguard Worker        A list of namedtuple of (vnc_port, adb_port)
364*800a58d9SAndroid Build Coastguard Worker    """
365*800a58d9SAndroid Build Coastguard Worker    forwarded_ports = []
366*800a58d9SAndroid Build Coastguard Worker    for vnc_port, adb_port in zip(vnc_ports, adb_ports):
367*800a58d9SAndroid Build Coastguard Worker        forwarded_port = utils.AutoConnect(
368*800a58d9SAndroid Build Coastguard Worker            ip_addr=ip,
369*800a58d9SAndroid Build Coastguard Worker            rsa_key_file=ssh_private_key_path,
370*800a58d9SAndroid Build Coastguard Worker            target_vnc_port=vnc_port,
371*800a58d9SAndroid Build Coastguard Worker            target_adb_port=adb_port,
372*800a58d9SAndroid Build Coastguard Worker            ssh_user=ssh_user,
373*800a58d9SAndroid Build Coastguard Worker            client_adb_port=client_adb_port,
374*800a58d9SAndroid Build Coastguard Worker            extra_args_ssh_tunnel=extra_args_ssh_tunnel)
375*800a58d9SAndroid Build Coastguard Worker        forwarded_ports.append(forwarded_port)
376*800a58d9SAndroid Build Coastguard Worker        if unlock_screen:
377*800a58d9SAndroid Build Coastguard Worker            AdbTools(forwarded_port.adb_port).AutoUnlockScreen()
378*800a58d9SAndroid Build Coastguard Worker    return forwarded_ports
379