• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.ddmlib.IDevice;
19 import com.android.tradefed.build.IBuildInfo;
20 import com.android.tradefed.device.DeviceNotAvailableException;
21 import com.android.tradefed.device.IDeviceMonitor;
22 import com.android.tradefed.device.IDeviceStateMonitor;
23 import com.android.tradefed.device.StubDevice;
24 import com.android.tradefed.device.TestDevice;
25 import com.android.tradefed.device.cloud.GceAvdInfo.GceStatus;
26 import com.android.tradefed.log.ITestLogger;
27 import com.android.tradefed.log.LogUtil.CLog;
28 import com.android.tradefed.result.FileInputStreamSource;
29 import com.android.tradefed.result.ITestLoggerReceiver;
30 import com.android.tradefed.result.InputStreamSource;
31 import com.android.tradefed.result.LogDataType;
32 import com.android.tradefed.targetprep.TargetSetupError;
33 import com.android.tradefed.util.FileUtil;
34 import com.android.tradefed.util.StreamUtil;
35 
36 import com.google.common.annotations.VisibleForTesting;
37 
38 import java.io.File;
39 import java.io.IOException;
40 import java.util.List;
41 
42 /**
43  * A device running inside a virtual machine that we manage remotely via a Tradefed instance inside
44  * the VM.
45  */
46 public class ManagedRemoteDevice extends TestDevice implements ITestLoggerReceiver {
47 
48     private GceManager mGceHandler = null;
49     private GceAvdInfo mGceAvd;
50     private ITestLogger mTestLogger;
51 
52     /**
53      * Creates a {@link ManagedRemoteDevice}.
54      *
55      * @param device the associated {@link IDevice}
56      * @param stateMonitor the {@link IDeviceStateMonitor} mechanism to use
57      * @param allocationMonitor the {@link IDeviceMonitor} to inform of allocation state changes.
58      */
ManagedRemoteDevice( IDevice device, IDeviceStateMonitor stateMonitor, IDeviceMonitor allocationMonitor)59     public ManagedRemoteDevice(
60             IDevice device, IDeviceStateMonitor stateMonitor, IDeviceMonitor allocationMonitor) {
61         super(device, stateMonitor, allocationMonitor);
62     }
63 
64     @Override
preInvocationSetup(IBuildInfo info, List<IBuildInfo> testResourceBuildInfos)65     public void preInvocationSetup(IBuildInfo info, List<IBuildInfo> testResourceBuildInfos)
66             throws TargetSetupError, DeviceNotAvailableException {
67         super.preInvocationSetup(info, testResourceBuildInfos);
68         mGceAvd = null;
69 
70         // We create a brand new GceManager each time to ensure clean state.
71         mGceHandler =
72                 new GceManager(getDeviceDescriptor(), getOptions(), info, testResourceBuildInfos);
73         getGceHandler().logStableHostImageInfos(info);
74         setFastbootEnabled(false);
75 
76         // Launch GCE helper script.
77         long startTime = getCurrentTime();
78         launchGce();
79         long remainingTime = getOptions().getGceCmdTimeout() - (getCurrentTime() - startTime);
80         if (remainingTime < 0) {
81             throw new DeviceNotAvailableException(
82                     String.format(
83                             "Failed to launch GCE after %sms", getOptions().getGceCmdTimeout()),
84                     getSerialNumber());
85         }
86     }
87 
88     /** {@inheritDoc} */
89     @Override
postInvocationTearDown()90     public void postInvocationTearDown() {
91         try {
92             CLog.i("Shutting down GCE device %s", getSerialNumber());
93             // Log the last part of the logcat from the tear down.
94             if (!(getIDevice() instanceof StubDevice)) {
95                 try (InputStreamSource logcatSource = getLogcat()) {
96                     clearLogcat();
97                     String name = "device_logcat_teardown_gce";
98                     mTestLogger.testLog(name, LogDataType.LOGCAT, logcatSource);
99                 }
100             }
101 
102             if (mGceAvd != null) {
103                 // attempt to get a bugreport if Gce Avd is a failure
104                 if (!GceStatus.SUCCESS.equals(mGceAvd.getStatus())) {
105                     // Get a bugreport via ssh
106                     getSshBugreport();
107                 }
108                 // Log the serial output of the instance.
109                 getGceHandler().logSerialOutput(mGceAvd, mTestLogger);
110 
111                 // Fetch remote files
112                 CommonLogRemoteFileUtil.fetchCommonFiles(
113                         mTestLogger, mGceAvd, getOptions(), getRunUtil());
114 
115                 // Cleanup GCE first to make sure ssh tunnel has nowhere to go.
116                 if (!getOptions().shouldSkipTearDown()) {
117                     getGceHandler().shutdownGce();
118                 }
119             }
120 
121             setFastbootEnabled(false);
122 
123             if (getGceHandler() != null) {
124                 getGceHandler().cleanUp();
125             }
126         } finally {
127             // Ensure parent postInvocationTearDown is always called.
128             super.postInvocationTearDown();
129         }
130     }
131 
132     @Override
setTestLogger(ITestLogger testLogger)133     public void setTestLogger(ITestLogger testLogger) {
134         mTestLogger = testLogger;
135     }
136 
137     /** Returns the {@link GceAvdInfo} describing the remote instance. */
getRemoteAvdInfo()138     public GceAvdInfo getRemoteAvdInfo() {
139         return mGceAvd;
140     }
141 
142     /** Launch the actual gce device based on the build info. */
launchGce()143     protected void launchGce() throws TargetSetupError {
144         TargetSetupError exception = null;
145         for (int attempt = 0; attempt < getOptions().getGceMaxAttempt(); attempt++) {
146             try {
147                 mGceAvd = getGceHandler().startGce();
148                 if (mGceAvd != null) break;
149             } catch (TargetSetupError tse) {
150                 CLog.w(
151                         "Failed to start Gce with attempt: %s out of %s. With Exception: %s",
152                         attempt + 1, getOptions().getGceMaxAttempt(), tse);
153                 exception = tse;
154             }
155         }
156         if (mGceAvd == null) {
157             throw exception;
158         } else {
159             CLog.i("GCE AVD has been started: %s", mGceAvd);
160             if (GceAvdInfo.GceStatus.BOOT_FAIL.equals(mGceAvd.getStatus())) {
161                 throw new TargetSetupError(mGceAvd.getErrors(), getDeviceDescriptor());
162             }
163         }
164     }
165 
166     /** Capture a remote bugreport by ssh-ing into the device directly. */
getSshBugreport()167     private void getSshBugreport() {
168         File bugreportFile = null;
169         try {
170             bugreportFile =
171                     GceManager.getNestedDeviceSshBugreportz(mGceAvd, getOptions(), getRunUtil());
172             if (bugreportFile != null) {
173                 InputStreamSource bugreport = new FileInputStreamSource(bugreportFile);
174                 mTestLogger.testLog("bugreportz-ssh", LogDataType.BUGREPORTZ, bugreport);
175                 StreamUtil.cancel(bugreport);
176             }
177         } catch (IOException e) {
178             CLog.e(e);
179         } finally {
180             FileUtil.deleteFile(bugreportFile);
181         }
182     }
183 
184     /** Returns the current system time. Exposed for testing. */
185     @VisibleForTesting
getCurrentTime()186     protected long getCurrentTime() {
187         return System.currentTimeMillis();
188     }
189 
190     /** Returns the instance of the {@link GceManager}. */
191     @VisibleForTesting
getGceHandler()192     GceManager getGceHandler() {
193         return mGceHandler;
194     }
195 }
196