• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2019 - The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""RemoteInstanceDeviceFactory provides basic interface to create a cuttlefish
16device factory."""
17
18import logging
19import os
20import shutil
21import tempfile
22
23from acloud.create import create_common
24from acloud.internal import constants
25from acloud.internal.lib import cvd_utils
26from acloud.internal.lib import utils
27from acloud.public.actions import gce_device_factory
28from acloud.pull import pull
29
30
31logger = logging.getLogger(__name__)
32
33
34class RemoteInstanceDeviceFactory(gce_device_factory.GCEDeviceFactory):
35    """A class that can produce a cuttlefish device.
36
37    Attributes:
38        avd_spec: AVDSpec object that tells us what we're going to create.
39        cfg: An AcloudConfig instance.
40        local_image_artifact: A string, path to local image.
41        cvd_host_package_artifact: A string, path to cvd host package.
42        report_internal_ip: Boolean, True for the internal ip is used when
43                            connecting from another GCE instance.
44        credentials: An oauth2client.OAuth2Credentials instance.
45        compute_client: An object of cvd_compute_client.CvdComputeClient.
46        ssh: An Ssh object.
47    """
48    def __init__(self, avd_spec, local_image_artifact=None,
49                 cvd_host_package_artifact=None):
50        super().__init__(avd_spec, local_image_artifact)
51        self._all_logs = {}
52        self._cvd_host_package_artifact = cvd_host_package_artifact
53
54    # pylint: disable=broad-except
55    def CreateInstance(self):
56        """Create a single configured cuttlefish device.
57
58        Returns:
59            A string, representing instance name.
60        """
61        instance = self.CreateGceInstance()
62        # If instance is failed, no need to go next step.
63        if instance in self.GetFailures():
64            return instance
65        try:
66            image_args = self._ProcessArtifacts()
67            failures = self._compute_client.LaunchCvd(
68                instance, self._avd_spec, cvd_utils.GCE_BASE_DIR, image_args)
69            for failing_instance, error_msg in failures.items():
70                self._SetFailures(failing_instance, error_msg)
71        except Exception as e:
72            self._SetFailures(instance, e)
73
74        self._FindLogFiles(
75            instance,
76            instance in self.GetFailures() and not self._avd_spec.no_pull_log)
77        return instance
78
79    def _ProcessArtifacts(self):
80        """Process artifacts.
81
82        - If images source is local, tool will upload images from local site to
83          remote instance.
84        - If images source is remote, tool will download images from android
85          build to remote instance. Before download images, we have to update
86          fetch_cvd to remote instance.
87
88        Returns:
89            A list of strings, the launch_cvd arguments.
90        """
91        avd_spec = self._avd_spec
92        launch_cvd_args = []
93        temp_dir = None
94        try:
95            target_files_dir = None
96            if cvd_utils.AreTargetFilesRequired(avd_spec):
97                temp_dir = tempfile.mkdtemp(prefix="acloud_remote_ins")
98                target_files_dir = self._GetLocalTargetFilesDir(temp_dir)
99
100            if avd_spec.image_source == constants.IMAGE_SRC_LOCAL:
101                cvd_utils.UploadArtifacts(
102                    self._ssh, cvd_utils.GCE_BASE_DIR,
103                    (target_files_dir or self._local_image_artifact or
104                     avd_spec.local_image_dir),
105                    self._cvd_host_package_artifact)
106            elif avd_spec.image_source == constants.IMAGE_SRC_REMOTE:
107                self._compute_client.UpdateFetchCvd(avd_spec.fetch_cvd_version)
108                self._compute_client.FetchBuild(
109                    avd_spec.remote_image,
110                    avd_spec.system_build_info,
111                    avd_spec.kernel_build_info,
112                    avd_spec.boot_build_info,
113                    avd_spec.bootloader_build_info,
114                    avd_spec.android_efi_loader_build_info,
115                    avd_spec.ota_build_info,
116                    avd_spec.host_package_build_info)
117
118            launch_cvd_args += cvd_utils.UploadExtraImages(
119                self._ssh, cvd_utils.GCE_BASE_DIR, avd_spec, target_files_dir)
120        finally:
121            if temp_dir:
122                shutil.rmtree(temp_dir)
123
124        if avd_spec.mkcert and avd_spec.connect_webrtc:
125            self._compute_client.UpdateCertificate()
126
127        if avd_spec.extra_files:
128            self._compute_client.UploadExtraFiles(avd_spec.extra_files)
129
130        return [arg for arg_pair in launch_cvd_args for arg in arg_pair]
131
132    @utils.TimeExecute(function_description="Downloading target_files archive")
133    def _DownloadTargetFiles(self, download_dir):
134        """Download target_files zip to a directory.
135
136        Args:
137            download_dir: The directory to which the zip is downloaded.
138
139        Returns:
140            The path to the target_files zip.
141        """
142        avd_spec = self._avd_spec
143        build_id = avd_spec.remote_image[constants.BUILD_ID]
144        build_target = avd_spec.remote_image[constants.BUILD_TARGET]
145        name = cvd_utils.GetMixBuildTargetFilename(build_target, build_id)
146        create_common.DownloadRemoteArtifact(avd_spec.cfg, build_target,
147                                             build_id, name, download_dir)
148        return os.path.join(download_dir, name)
149
150    def _GetLocalTargetFilesDir(self, temp_dir):
151        """Return a directory of extracted target_files or local images.
152
153        Args:
154            temp_dir: Temporary directory to store downloaded build artifacts
155                      and extracted target_files archive.
156
157        Returns:
158            The path to the target_files directory.
159        """
160        avd_spec = self._avd_spec
161        if avd_spec.image_source == constants.IMAGE_SRC_LOCAL:
162            if self._local_image_artifact:
163                target_files_zip = self._local_image_artifact
164                target_files_dir = os.path.join(temp_dir, "local_images")
165            else:
166                return os.path.abspath(avd_spec.local_image_dir)
167        else:  # must be IMAGE_SRC_REMOTE
168            target_files_zip = self._DownloadTargetFiles(temp_dir)
169            target_files_dir = os.path.join(temp_dir, "remote_images")
170
171        os.makedirs(target_files_dir, exist_ok=True)
172        cvd_utils.ExtractTargetFilesZip(target_files_zip, target_files_dir)
173        return target_files_dir
174
175    def _FindLogFiles(self, instance, download):
176        """Find and pull all log files from instance.
177
178        Args:
179            instance: String, instance name.
180            download: Whether to download the files to a temporary directory
181                      and show messages to the user.
182        """
183        logs = [cvd_utils.HOST_KERNEL_LOG]
184        if self._avd_spec.image_source == constants.IMAGE_SRC_REMOTE:
185            logs.append(
186                cvd_utils.GetRemoteFetcherConfigJson(cvd_utils.GCE_BASE_DIR))
187        logs.extend(cvd_utils.FindRemoteLogs(
188            self._ssh,
189            cvd_utils.GCE_BASE_DIR,
190            self._avd_spec.base_instance_num,
191            self._avd_spec.num_avds_per_instance))
192        self._all_logs[instance] = logs
193
194        if download:
195            # To avoid long download time, fetch from the first device only.
196            log_files = pull.GetAllLogFilePaths(self._ssh,
197                                                constants.REMOTE_LOG_FOLDER)
198            error_log_folder = pull.PullLogs(self._ssh, log_files, instance)
199            self._compute_client.ExtendReportData(constants.ERROR_LOG_FOLDER,
200                                                  error_log_folder)
201
202    def GetOpenWrtInfoDict(self):
203        """Get openwrt info dictionary.
204
205        Returns:
206            A openwrt info dictionary. None for the case is not openwrt device.
207        """
208        if not self._avd_spec.openwrt:
209            return None
210        return cvd_utils.GetOpenWrtInfoDict(self._ssh, cvd_utils.GCE_BASE_DIR)
211
212    def GetAdbPorts(self):
213        """Get ADB ports of the created devices.
214
215        Returns:
216            The port numbers as a list of integers.
217        """
218        return cvd_utils.GetAdbPorts(self._avd_spec.base_instance_num,
219                                     self._avd_spec.num_avds_per_instance)
220
221    def GetVncPorts(self):
222        """Get VNC ports of the created devices.
223
224        Returns:
225            The port numbers as a list of integers.
226        """
227        return cvd_utils.GetVncPorts(self._avd_spec.base_instance_num,
228                                     self._avd_spec.num_avds_per_instance)
229
230    def GetBuildInfoDict(self):
231        """Get build info dictionary.
232
233        Returns:
234            A build info dictionary. None for local image case.
235        """
236        if self._avd_spec.image_source == constants.IMAGE_SRC_LOCAL:
237            return None
238        return cvd_utils.GetRemoteBuildInfoDict(self._avd_spec)
239
240    def GetLogs(self):
241        """Get all device logs.
242
243        Returns:
244            A dictionary that maps instance names to lists of report.LogFile.
245        """
246        return self._all_logs
247