1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.tradefed.device.cloud;
17 
18 import com.android.tradefed.device.TestDeviceOptions;
19 import com.android.tradefed.device.TestDeviceOptions.InstanceType;
20 import com.android.tradefed.log.ITestLogger;
21 import com.android.tradefed.log.LogUtil.CLog;
22 import com.android.tradefed.result.FileInputStreamSource;
23 import com.android.tradefed.result.InputStreamSource;
24 import com.android.tradefed.result.LogDataType;
25 import com.android.tradefed.util.CommandResult;
26 import com.android.tradefed.util.CommandStatus;
27 import com.android.tradefed.util.IRunUtil;
28 import com.android.tradefed.util.MultiMap;
29 import com.android.tradefed.util.ZipUtil;
30 
31 import java.io.File;
32 import java.io.IOException;
33 import java.util.List;
34 
35 /**
36  * This utility allows to avoid code duplication across the different remote device representation
37  * for the remote log fetching logic of common files.
38  */
39 public class CommonLogRemoteFileUtil {
40 
41     /** The directory where to find debug logs for a nested remote instance. */
42     public static final String NESTED_REMOTE_LOG_DIR = "/home/%s/cuttlefish_runtime/";
43     /** The directory where to find debug logs for an emulator instance. */
44     public static final String EMULATOR_REMOTE_LOG_DIR = "/home/%s/log/";
45     public static final String TOMBSTONES_ZIP_NAME = "tombstones-zip";
46 
47     public static final MultiMap<InstanceType, KnownLogFileEntry> KNOWN_FILES_TO_FETCH =
48             new MultiMap<>();
49 
50     static {
51         // Cuttlefish known files to collect
KNOWN_FILES_TO_FETCH.put( InstanceType.CUTTLEFISH, new KnownLogFileEntry( NESTED_REMOTE_LOG_DIR + "kernel.log", null, LogDataType.TEXT))52         KNOWN_FILES_TO_FETCH.put(
53                 InstanceType.CUTTLEFISH,
54                 new KnownLogFileEntry(
55                         NESTED_REMOTE_LOG_DIR + "kernel.log", null, LogDataType.TEXT));
KNOWN_FILES_TO_FETCH.put( InstanceType.CUTTLEFISH, new KnownLogFileEntry( NESTED_REMOTE_LOG_DIR + "logcat", "full_gce_logcat", LogDataType.LOGCAT))56         KNOWN_FILES_TO_FETCH.put(
57                 InstanceType.CUTTLEFISH,
58                 new KnownLogFileEntry(
59                         NESTED_REMOTE_LOG_DIR + "logcat", "full_gce_logcat", LogDataType.LOGCAT));
KNOWN_FILES_TO_FETCH.put( InstanceType.CUTTLEFISH, new KnownLogFileEntry( NESTED_REMOTE_LOG_DIR + "cuttlefish_config.json", null, LogDataType.TEXT))60         KNOWN_FILES_TO_FETCH.put(
61                 InstanceType.CUTTLEFISH,
62                 new KnownLogFileEntry(
63                         NESTED_REMOTE_LOG_DIR + "cuttlefish_config.json", null, LogDataType.TEXT));
KNOWN_FILES_TO_FETCH.put( InstanceType.CUTTLEFISH, new KnownLogFileEntry( NESTED_REMOTE_LOG_DIR + "launcher.log", "cuttlefish_launcher.log", LogDataType.TEXT))64         KNOWN_FILES_TO_FETCH.put(
65                 InstanceType.CUTTLEFISH,
66                 new KnownLogFileEntry(
67                         NESTED_REMOTE_LOG_DIR + "launcher.log",
68                         "cuttlefish_launcher.log",
69                         LogDataType.TEXT));
70         // Emulator known files to collect
KNOWN_FILES_TO_FETCH.put( InstanceType.EMULATOR, new KnownLogFileEntry( EMULATOR_REMOTE_LOG_DIR + "logcat.log", "full_gce_emulator_logcat", LogDataType.LOGCAT))71         KNOWN_FILES_TO_FETCH.put(
72                 InstanceType.EMULATOR,
73                 new KnownLogFileEntry(
74                         EMULATOR_REMOTE_LOG_DIR + "logcat.log",
75                         "full_gce_emulator_logcat",
76                         LogDataType.LOGCAT));
KNOWN_FILES_TO_FETCH.put( InstanceType.EMULATOR, new KnownLogFileEntry(EMULATOR_REMOTE_LOG_DIR + "adb.log", null, LogDataType.TEXT))77         KNOWN_FILES_TO_FETCH.put(
78                 InstanceType.EMULATOR,
79                 new KnownLogFileEntry(EMULATOR_REMOTE_LOG_DIR + "adb.log", null, LogDataType.TEXT));
KNOWN_FILES_TO_FETCH.put( InstanceType.EMULATOR, new KnownLogFileEntry( EMULATOR_REMOTE_LOG_DIR + "kernel.log", null, LogDataType.TEXT))80         KNOWN_FILES_TO_FETCH.put(
81                 InstanceType.EMULATOR,
82                 new KnownLogFileEntry(
83                         EMULATOR_REMOTE_LOG_DIR + "kernel.log", null, LogDataType.TEXT));
KNOWN_FILES_TO_FETCH.put( InstanceType.EMULATOR, new KnownLogFileEntry("/var/log/daemon.log", null, LogDataType.TEXT))84         KNOWN_FILES_TO_FETCH.put(
85                 InstanceType.EMULATOR,
86                 new KnownLogFileEntry("/var/log/daemon.log", null, LogDataType.TEXT));
87     }
88 
89     /** A representation of a known log entry for remote devices. */
90     public static class KnownLogFileEntry {
91         public String path;
92         public String logName;
93         public LogDataType type;
94 
KnownLogFileEntry(String path, String logName, LogDataType type)95         KnownLogFileEntry(String path, String logName, LogDataType type) {
96             this.path = path;
97             this.logName = logName;
98             this.type = type;
99         }
100     }
101 
102     /**
103      * Fetch and log the commonly known files from remote instances.
104      *
105      * @param testLogger The {@link ITestLogger} where to log the files.
106      * @param gceAvd The descriptor of the remote instance.
107      * @param options The {@link TestDeviceOptions} describing the device options
108      * @param runUtil A {@link IRunUtil} to execute commands.
109      */
fetchCommonFiles( ITestLogger testLogger, GceAvdInfo gceAvd, TestDeviceOptions options, IRunUtil runUtil)110     public static void fetchCommonFiles(
111             ITestLogger testLogger,
112             GceAvdInfo gceAvd,
113             TestDeviceOptions options,
114             IRunUtil runUtil) {
115         if (gceAvd == null) {
116             CLog.e("GceAvdInfo was null, cannot collect remote files.");
117             return;
118         }
119         // Capture known extra files
120         List<KnownLogFileEntry> toFetch = KNOWN_FILES_TO_FETCH.get(options.getInstanceType());
121         if (toFetch != null) {
122             for (KnownLogFileEntry entry : toFetch) {
123                 LogRemoteFile(
124                         testLogger,
125                         gceAvd,
126                         options,
127                         runUtil,
128                         // Default fetch rely on main user
129                         String.format(entry.path, options.getInstanceUser()),
130                         entry.type,
131                         entry.logName);
132             }
133         }
134 
135         if (options.getRemoteFetchFilePattern().isEmpty()) {
136             return;
137         }
138         for (String file : options.getRemoteFetchFilePattern()) {
139             // TODO: Improve type of files.
140             LogRemoteFile(testLogger, gceAvd, options, runUtil, file, LogDataType.TEXT, null);
141         }
142     }
143 
144     /**
145      * Fetch and log the tombstones from the remote instance.
146      *
147      * @param testLogger The {@link ITestLogger} where to log the files.
148      * @param gceAvd The descriptor of the remote instance.
149      * @param options The {@link TestDeviceOptions} describing the device options
150      * @param runUtil A {@link IRunUtil} to execute commands.
151      */
fetchTombstones( ITestLogger testLogger, GceAvdInfo gceAvd, TestDeviceOptions options, IRunUtil runUtil)152     public static void fetchTombstones(
153             ITestLogger testLogger,
154             GceAvdInfo gceAvd,
155             TestDeviceOptions options,
156             IRunUtil runUtil) {
157         if (gceAvd == null) {
158             CLog.e("GceAvdInfo was null, cannot collect remote files.");
159             return;
160         }
161         InstanceType type = options.getInstanceType();
162         if (!InstanceType.CUTTLEFISH.equals(type) && !InstanceType.REMOTE_AVD.equals(type)) {
163             return;
164         }
165         String pattern =
166                 String.format(
167                         "/home/%s/cuttlefish_runtime/tombstones/*", options.getInstanceUser());
168         CommandResult resultList =
169                 GceManager.remoteSshCommandExecution(
170                         gceAvd, options, runUtil, 60000, "ls", "-A1", pattern);
171         if (!CommandStatus.SUCCESS.equals(resultList.getStatus())) {
172             CLog.e("Failed to list the tombstones: %s", resultList.getStderr());
173             return;
174         }
175         if (resultList.getStdout().split("\n").length <= 0) {
176             return;
177         }
178         File tombstonesDir =
179                 RemoteFileUtil.fetchRemoteDir(
180                         gceAvd,
181                         options,
182                         runUtil,
183                         120000,
184                         String.format(
185                                 "/home/%s/cuttlefish_runtime/tombstones",
186                                 options.getInstanceUser()));
187         if (tombstonesDir == null) {
188             CLog.w("No tombstones directory was pulled.");
189             return;
190         }
191         try {
192             File zipTombstones = ZipUtil.createZip(tombstonesDir);
193             try (InputStreamSource source = new FileInputStreamSource(zipTombstones, true)) {
194                 testLogger.testLog(TOMBSTONES_ZIP_NAME, LogDataType.ZIP, source);
195             }
196         } catch (IOException e) {
197             CLog.e("Failed to zip the tombstones:");
198             CLog.e(e);
199         }
200     }
201 
202     /**
203      * Captures a log from the remote destination.
204      *
205      * @param testLogger The {@link ITestLogger} where to log the files.
206      * @param gceAvd The descriptor of the remote instance.
207      * @param options The {@link TestDeviceOptions} describing the device options
208      * @param runUtil A {@link IRunUtil} to execute commands.
209      * @param fileToRetrieve The remote path to the file to pull.
210      * @param logType The expected type of the pulled log.
211      * @param baseName The base name that will be used to log the file, if null the actually file
212      *     name will be used.
213      */
LogRemoteFile( ITestLogger testLogger, GceAvdInfo gceAvd, TestDeviceOptions options, IRunUtil runUtil, String fileToRetrieve, LogDataType logType, String baseName)214     private static void LogRemoteFile(
215             ITestLogger testLogger,
216             GceAvdInfo gceAvd,
217             TestDeviceOptions options,
218             IRunUtil runUtil,
219             String fileToRetrieve,
220             LogDataType logType,
221             String baseName) {
222         GceManager.logNestedRemoteFile(
223                 testLogger, gceAvd, options, runUtil, fileToRetrieve, logType, baseName);
224     }
225 }
226