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.tradefed.build.BuildInfo; 19 import com.android.tradefed.build.IBuildInfo; 20 import com.android.tradefed.command.remote.DeviceDescriptor; 21 import com.android.tradefed.device.TestDeviceOptions; 22 import com.android.tradefed.device.cloud.AcloudConfigParser.AcloudKeys; 23 import com.android.tradefed.device.cloud.GceAvdInfo.GceStatus; 24 import com.android.tradefed.log.ITestLogger; 25 import com.android.tradefed.log.LogUtil.CLog; 26 import com.android.tradefed.result.ByteArrayInputStreamSource; 27 import com.android.tradefed.result.FileInputStreamSource; 28 import com.android.tradefed.result.InputStreamSource; 29 import com.android.tradefed.result.LogDataType; 30 import com.android.tradefed.targetprep.TargetSetupError; 31 import com.android.tradefed.util.ArrayUtil; 32 import com.android.tradefed.util.CommandResult; 33 import com.android.tradefed.util.CommandStatus; 34 import com.android.tradefed.util.FileUtil; 35 import com.android.tradefed.util.GoogleApiClientUtil; 36 import com.android.tradefed.util.IRunUtil; 37 import com.android.tradefed.util.RunUtil; 38 39 import com.google.api.client.auth.oauth2.Credential; 40 import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; 41 import com.google.api.client.json.JsonFactory; 42 import com.google.api.client.json.jackson2.JacksonFactory; 43 import com.google.api.services.compute.Compute; 44 import com.google.api.services.compute.Compute.Instances.GetSerialPortOutput; 45 import com.google.api.services.compute.ComputeScopes; 46 import com.google.api.services.compute.model.SerialPortOutput; 47 import com.google.common.annotations.VisibleForTesting; 48 import com.google.common.net.HostAndPort; 49 50 import java.io.File; 51 import java.io.IOException; 52 import java.security.GeneralSecurityException; 53 import java.util.Arrays; 54 import java.util.List; 55 import java.util.regex.Matcher; 56 import java.util.regex.Pattern; 57 58 /** Helper that manages the GCE calls to start/stop and collect logs from GCE. */ 59 public class GceManager { 60 private static final long BUGREPORT_TIMEOUT = 15 * 60 * 1000L; 61 private static final long REMOTE_FILE_OP_TIMEOUT = 10 * 60 * 1000L; 62 private static final Pattern BUGREPORTZ_RESPONSE_PATTERN = Pattern.compile("(OK:)(.*)"); 63 private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); 64 private static final List<String> SCOPES = Arrays.asList(ComputeScopes.COMPUTE_READONLY); 65 66 private DeviceDescriptor mDeviceDescriptor; 67 private TestDeviceOptions mDeviceOptions; 68 private IBuildInfo mBuildInfo; 69 private List<IBuildInfo> mTestResourceBuildInfos; 70 71 private String mGceInstanceName = null; 72 private String mGceHost = null; 73 private GceAvdInfo mGceAvdInfo = null; 74 75 /** 76 * Ctor 77 * 78 * @param deviceDesc The {@link DeviceDescriptor} that will be associated with the GCE device. 79 * @param deviceOptions A {@link TestDeviceOptions} associated with the device. 80 * @param buildInfo A {@link IBuildInfo} describing the gce build to start. 81 * @param testResourceBuildInfos A list {@link IBuildInfo} describing test resources 82 */ GceManager( DeviceDescriptor deviceDesc, TestDeviceOptions deviceOptions, IBuildInfo buildInfo, List<IBuildInfo> testResourceBuildInfos)83 public GceManager( 84 DeviceDescriptor deviceDesc, 85 TestDeviceOptions deviceOptions, 86 IBuildInfo buildInfo, 87 List<IBuildInfo> testResourceBuildInfos) { 88 mDeviceDescriptor = deviceDesc; 89 mDeviceOptions = deviceOptions; 90 mBuildInfo = buildInfo; 91 mTestResourceBuildInfos = testResourceBuildInfos; 92 } 93 94 /** 95 * Ctor, variation that can be used to provide the GCE instance name to use directly. 96 * 97 * @param deviceDesc The {@link DeviceDescriptor} that will be associated with the GCE device. 98 * @param deviceOptions A {@link TestDeviceOptions} associated with the device 99 * @param buildInfo A {@link IBuildInfo} describing the gce build to start. 100 * @param testResourceBuildInfos A list {@link IBuildInfo} describing test resources 101 * @param gceInstanceName The instance name to use. 102 * @param gceHost The host name or ip of the instance to use. 103 */ GceManager( DeviceDescriptor deviceDesc, TestDeviceOptions deviceOptions, IBuildInfo buildInfo, List<IBuildInfo> testResourceBuildInfos, String gceInstanceName, String gceHost)104 public GceManager( 105 DeviceDescriptor deviceDesc, 106 TestDeviceOptions deviceOptions, 107 IBuildInfo buildInfo, 108 List<IBuildInfo> testResourceBuildInfos, 109 String gceInstanceName, 110 String gceHost) { 111 this(deviceDesc, deviceOptions, buildInfo, testResourceBuildInfos); 112 mGceInstanceName = gceInstanceName; 113 mGceHost = gceHost; 114 } 115 116 /** 117 * Attempt to start a gce instance 118 * 119 * @return a {@link GceAvdInfo} describing the GCE instance. Could be a BOOT_FAIL instance. 120 * @throws TargetSetupError 121 */ startGce()122 public GceAvdInfo startGce() throws TargetSetupError { 123 mGceAvdInfo = null; 124 // For debugging purposes bypass. 125 if (mGceHost != null && mGceInstanceName != null) { 126 mGceAvdInfo = 127 new GceAvdInfo( 128 mGceInstanceName, 129 HostAndPort.fromString(mGceHost) 130 .withDefaultPort(mDeviceOptions.getRemoteAdbPort())); 131 return mGceAvdInfo; 132 } 133 // Add extra args. 134 File reportFile = null; 135 try { 136 reportFile = FileUtil.createTempFile("gce_avd_driver", ".json"); 137 List<String> gceArgs = buildGceCmd(reportFile, mBuildInfo); 138 139 CLog.i("Launching GCE with %s", gceArgs.toString()); 140 CommandResult cmd = 141 getRunUtil() 142 .runTimedCmd( 143 getTestDeviceOptions().getGceCmdTimeout(), 144 gceArgs.toArray(new String[gceArgs.size()])); 145 CLog.i("GCE driver stderr: %s", cmd.getStderr()); 146 String instanceName = extractInstanceName(cmd.getStderr()); 147 if (instanceName != null) { 148 mBuildInfo.addBuildAttribute("gce-instance-name", instanceName); 149 } else { 150 CLog.w("Could not extract an instance name for the gce device."); 151 } 152 if (CommandStatus.TIMED_OUT.equals(cmd.getStatus())) { 153 String errors = 154 String.format( 155 "acloud errors: timeout after %dms, " + "acloud did not return", 156 getTestDeviceOptions().getGceCmdTimeout()); 157 if (instanceName != null) { 158 // If we managed to parse the instance name, report the boot failure so it 159 // can be shutdown. 160 mGceAvdInfo = new GceAvdInfo(instanceName, null, errors, GceStatus.BOOT_FAIL); 161 return mGceAvdInfo; 162 } 163 throw new TargetSetupError(errors, mDeviceDescriptor); 164 } else if (!CommandStatus.SUCCESS.equals(cmd.getStatus())) { 165 CLog.w("Error when booting the Gce instance, reading output of gce driver"); 166 mGceAvdInfo = 167 GceAvdInfo.parseGceInfoFromFile( 168 reportFile, mDeviceDescriptor, mDeviceOptions.getRemoteAdbPort()); 169 String errors = ""; 170 if (mGceAvdInfo != null) { 171 // We always return the GceAvdInfo describing the instance when possible 172 // The caller can decide actions to be taken. 173 return mGceAvdInfo; 174 } else { 175 errors = 176 "Could not get a valid instance name, check the gce driver's output." 177 + "The instance may not have booted up at all."; 178 CLog.e(errors); 179 throw new TargetSetupError( 180 String.format("acloud errors: %s", errors), mDeviceDescriptor); 181 } 182 } 183 mGceAvdInfo = 184 GceAvdInfo.parseGceInfoFromFile( 185 reportFile, mDeviceDescriptor, mDeviceOptions.getRemoteAdbPort()); 186 return mGceAvdInfo; 187 } catch (IOException e) { 188 throw new TargetSetupError("failed to create log file", e, mDeviceDescriptor); 189 } finally { 190 FileUtil.deleteFile(reportFile); 191 } 192 } 193 194 /** 195 * Retrieve the instance name from the gce boot logs. Search for the 'name': 'gce-<name>' 196 * pattern to extract the name of it. We extract from the logs instead of result file because on 197 * gce boot failure, the attempted instance name won't show in json. 198 */ extractInstanceName(String bootupLogs)199 protected String extractInstanceName(String bootupLogs) { 200 if (bootupLogs != null) { 201 final String pattern = "'name': '(((gce-)|(ins-))(.*?))'"; 202 Pattern namePattern = Pattern.compile(pattern); 203 Matcher matcher = namePattern.matcher(bootupLogs); 204 if (matcher.find()) { 205 return matcher.group(1); 206 } 207 } 208 return null; 209 } 210 211 /** Build and return the command to launch GCE. Exposed for testing. */ buildGceCmd(File reportFile, IBuildInfo b)212 protected List<String> buildGceCmd(File reportFile, IBuildInfo b) { 213 List<String> gceArgs = 214 ArrayUtil.list(getTestDeviceOptions().getAvdDriverBinary().getAbsolutePath()); 215 gceArgs.add( 216 TestDeviceOptions.getCreateCommandByInstanceType( 217 getTestDeviceOptions().getInstanceType())); 218 // Handle the build id related params 219 List<String> gceDriverParams = getTestDeviceOptions().getGceDriverParams(); 220 221 if (TestDeviceOptions.InstanceType.CHEEPS.equals( 222 getTestDeviceOptions().getInstanceType())) { 223 gceArgs.add("--avd-type"); 224 gceArgs.add("cheeps"); 225 } 226 227 // If args passed by gce-driver-param do not contain build_id or branch, 228 // use build_id and branch from device BuildInfo 229 if (!gceDriverParams.contains("--build_id") && !gceDriverParams.contains("--branch")) { 230 gceArgs.add("--build_target"); 231 if (b.getBuildAttributes().containsKey("build_target")) { 232 // If BuildInfo contains the attribute for a build target, use that. 233 gceArgs.add(b.getBuildAttributes().get("build_target")); 234 } else { 235 gceArgs.add(b.getBuildFlavor()); 236 } 237 gceArgs.add("--branch"); 238 gceArgs.add(b.getBuildBranch()); 239 gceArgs.add("--build_id"); 240 gceArgs.add(b.getBuildId()); 241 } 242 // Add additional args passed by gce-driver-param. 243 gceArgs.addAll(gceDriverParams); 244 // Get extra params by instance type 245 gceArgs.addAll( 246 TestDeviceOptions.getExtraParamsByInstanceType( 247 getTestDeviceOptions().getInstanceType(), 248 getTestDeviceOptions().getBaseImage())); 249 gceArgs.add("--config_file"); 250 gceArgs.add(getAvdConfigFile().getAbsolutePath()); 251 if (getTestDeviceOptions().getSerivceAccountJsonKeyFile() != null) { 252 gceArgs.add("--service_account_json_private_key_path"); 253 gceArgs.add(getTestDeviceOptions().getSerivceAccountJsonKeyFile().getAbsolutePath()); 254 } 255 gceArgs.add("--report_file"); 256 gceArgs.add(reportFile.getAbsolutePath()); 257 switch (getTestDeviceOptions().getGceDriverLogLevel()) { 258 case DEBUG: 259 gceArgs.add("-v"); 260 break; 261 case VERBOSE: 262 gceArgs.add("-vv"); 263 break; 264 default: 265 break; 266 } 267 if (getTestDeviceOptions().getGceAccount() != null) { 268 gceArgs.add("--email"); 269 gceArgs.add(getTestDeviceOptions().getGceAccount()); 270 } 271 // Do not pass flags --logcat_file and --serial_log_file to collect logcat and serial logs. 272 273 return gceArgs; 274 } 275 276 /** Shutdown the Gce instance associated with the {@link #startGce()}. */ shutdownGce()277 public void shutdownGce() { 278 if (!getTestDeviceOptions().getAvdDriverBinary().canExecute()) { 279 mGceAvdInfo = null; 280 throw new RuntimeException( 281 String.format( 282 "GCE launcher %s is invalid", 283 getTestDeviceOptions().getAvdDriverBinary())); 284 } 285 if (mGceAvdInfo == null) { 286 CLog.d("No instance to shutdown."); 287 return; 288 } 289 List<String> gceArgs = 290 ArrayUtil.list(getTestDeviceOptions().getAvdDriverBinary().getAbsolutePath()); 291 gceArgs.add("delete"); 292 // Add extra args. 293 File f = null; 294 try { 295 gceArgs.add("--instance_names"); 296 gceArgs.add(mGceAvdInfo.instanceName()); 297 gceArgs.add("--config_file"); 298 gceArgs.add(getAvdConfigFile().getAbsolutePath()); 299 if (getTestDeviceOptions().getSerivceAccountJsonKeyFile() != null) { 300 gceArgs.add("--service_account_json_private_key_path"); 301 gceArgs.add( 302 getTestDeviceOptions().getSerivceAccountJsonKeyFile().getAbsolutePath()); 303 } 304 f = FileUtil.createTempFile("gce_avd_driver", ".json"); 305 gceArgs.add("--report_file"); 306 gceArgs.add(f.getAbsolutePath()); 307 CLog.i("Tear down of GCE with %s", gceArgs.toString()); 308 if (getTestDeviceOptions().waitForGceTearDown()) { 309 CommandResult cmd = 310 getRunUtil() 311 .runTimedCmd( 312 getTestDeviceOptions().getGceCmdTimeout(), 313 gceArgs.toArray(new String[gceArgs.size()])); 314 if (!CommandStatus.SUCCESS.equals(cmd.getStatus())) { 315 CLog.w( 316 "Failed to tear down GCE %s with the following arg, %s", 317 mGceAvdInfo.instanceName(), gceArgs); 318 } 319 } else { 320 getRunUtil().runCmdInBackground(gceArgs.toArray(new String[gceArgs.size()])); 321 } 322 } catch (IOException e) { 323 CLog.e("failed to create log file for GCE Teardown"); 324 CLog.e(e); 325 } finally { 326 FileUtil.deleteFile(f); 327 mGceAvdInfo = null; 328 } 329 } 330 331 /** 332 * Get a bugreportz from the device using ssh to avoid any adb connection potential issue. 333 * 334 * @param gceAvd The {@link GceAvdInfo} that describe the device. 335 * @param options a {@link TestDeviceOptions} describing the device options to be used for the 336 * GCE device. 337 * @param runUtil a {@link IRunUtil} to execute commands. 338 * @return A file pointing to the zip bugreport, or null if an issue occurred. 339 * @throws IOException 340 */ getBugreportzWithSsh( GceAvdInfo gceAvd, TestDeviceOptions options, IRunUtil runUtil)341 public static File getBugreportzWithSsh( 342 GceAvdInfo gceAvd, TestDeviceOptions options, IRunUtil runUtil) throws IOException { 343 String output = remoteSshCommandExec(gceAvd, options, runUtil, "bugreportz"); 344 Matcher match = BUGREPORTZ_RESPONSE_PATTERN.matcher(output); 345 if (!match.find()) { 346 CLog.e("Something went wrong during bugreportz collection: '%s'", output); 347 return null; 348 } 349 String remoteFilePath = match.group(2); 350 File localTmpFile = FileUtil.createTempFile("bugreport-ssh", ".zip"); 351 if (!RemoteFileUtil.fetchRemoteFile( 352 gceAvd, options, runUtil, REMOTE_FILE_OP_TIMEOUT, remoteFilePath, localTmpFile)) { 353 FileUtil.deleteFile(localTmpFile); 354 return null; 355 } 356 return localTmpFile; 357 } 358 359 /** 360 * Get a bugreport via ssh for a nested instance. This requires requesting the adb in the nested 361 * virtual instance. 362 * 363 * @param gceAvd The {@link GceAvdInfo} that describe the device. 364 * @param options a {@link TestDeviceOptions} describing the device options to be used for the 365 * GCE device. 366 * @param runUtil a {@link IRunUtil} to execute commands. 367 * @return A file pointing to the zip bugreport, or null if an issue occurred. 368 * @throws IOException 369 */ getNestedDeviceSshBugreportz( GceAvdInfo gceAvd, TestDeviceOptions options, IRunUtil runUtil)370 public static File getNestedDeviceSshBugreportz( 371 GceAvdInfo gceAvd, TestDeviceOptions options, IRunUtil runUtil) throws IOException { 372 String output = ""; 373 // Retry a couple of time because adb might not be started for that user. 374 // FIXME: See if we can use vsoc-01 directly to avoid this 375 for (int i = 0; i < 3; i++) { 376 output = remoteSshCommandExec(gceAvd, options, runUtil, "adb", "shell", "bugreportz"); 377 Matcher match = BUGREPORTZ_RESPONSE_PATTERN.matcher(output); 378 if (match.find()) { 379 break; 380 } 381 } 382 Matcher match = BUGREPORTZ_RESPONSE_PATTERN.matcher(output); 383 if (!match.find()) { 384 CLog.e("Something went wrong during bugreportz collection: '%s'", output); 385 return null; 386 } 387 String deviceFilePath = match.group(2); 388 String pullOutput = 389 remoteSshCommandExec(gceAvd, options, runUtil, "adb", "pull", deviceFilePath); 390 CLog.d(pullOutput); 391 String remoteFilePath = "./" + new File(deviceFilePath).getName(); 392 File localTmpFile = FileUtil.createTempFile("bugreport-ssh", ".zip"); 393 if (!RemoteFileUtil.fetchRemoteFile( 394 gceAvd, options, runUtil, REMOTE_FILE_OP_TIMEOUT, remoteFilePath, localTmpFile)) { 395 FileUtil.deleteFile(localTmpFile); 396 return null; 397 } 398 return localTmpFile; 399 } 400 401 /** 402 * Fetch a remote file from a nested instance and log it. 403 * 404 * @param logger The {@link ITestLogger} where to log the file. 405 * @param gceAvd The {@link GceAvdInfo} that describe the device. 406 * @param options a {@link TestDeviceOptions} describing the device options to be used for the 407 * GCE device. 408 * @param runUtil a {@link IRunUtil} to execute commands. 409 * @param remoteFilePath The remote path where to find the file. 410 * @param type the {@link LogDataType} of the logged file. 411 */ logNestedRemoteFile( ITestLogger logger, GceAvdInfo gceAvd, TestDeviceOptions options, IRunUtil runUtil, String remoteFilePath, LogDataType type)412 public static void logNestedRemoteFile( 413 ITestLogger logger, 414 GceAvdInfo gceAvd, 415 TestDeviceOptions options, 416 IRunUtil runUtil, 417 String remoteFilePath, 418 LogDataType type) { 419 logNestedRemoteFile(logger, gceAvd, options, runUtil, remoteFilePath, type, null); 420 } 421 422 /** 423 * Fetch a remote file from a nested instance and log it. 424 * 425 * @param logger The {@link ITestLogger} where to log the file. 426 * @param gceAvd The {@link GceAvdInfo} that describe the device. 427 * @param options a {@link TestDeviceOptions} describing the device options to be used for the 428 * GCE device. 429 * @param runUtil a {@link IRunUtil} to execute commands. 430 * @param remoteFilePath The remote path where to find the file. 431 * @param type the {@link LogDataType} of the logged file. 432 * @param baseName The base name to use to log the file. If null the actual file name will be 433 * used. 434 */ logNestedRemoteFile( ITestLogger logger, GceAvdInfo gceAvd, TestDeviceOptions options, IRunUtil runUtil, String remoteFilePath, LogDataType type, String baseName)435 public static void logNestedRemoteFile( 436 ITestLogger logger, 437 GceAvdInfo gceAvd, 438 TestDeviceOptions options, 439 IRunUtil runUtil, 440 String remoteFilePath, 441 LogDataType type, 442 String baseName) { 443 File remoteFile = 444 RemoteFileUtil.fetchRemoteFile( 445 gceAvd, options, runUtil, REMOTE_FILE_OP_TIMEOUT, remoteFilePath); 446 if (remoteFile != null) { 447 // If we happened to fetch a directory, log all the subfiles 448 logFile(remoteFile, baseName, logger, type); 449 } 450 } 451 logFile( File remoteFile, String baseName, ITestLogger logger, LogDataType type)452 private static void logFile( 453 File remoteFile, String baseName, ITestLogger logger, LogDataType type) { 454 if (remoteFile.isDirectory()) { 455 for (File f : remoteFile.listFiles()) { 456 logFile(f, null, logger, type); 457 } 458 } else { 459 try (InputStreamSource remoteFileStream = new FileInputStreamSource(remoteFile, true)) { 460 String name = baseName; 461 if (name == null) { 462 name = remoteFile.getName(); 463 } 464 logger.testLog(name, type, remoteFileStream); 465 } 466 } 467 } 468 469 /** 470 * Execute the remote command via ssh on an instance. 471 * 472 * @param gceAvd The {@link GceAvdInfo} that describe the device. 473 * @param options a {@link TestDeviceOptions} describing the device options to be used for the 474 * GCE device. 475 * @param runUtil a {@link IRunUtil} to execute commands. 476 * @param timeoutMs The timeout in millisecond for the command. 0 means no timeout. 477 * @param command The remote command to execute. 478 * @return {@link CommandResult} containing the result of the execution. 479 */ remoteSshCommandExecution( GceAvdInfo gceAvd, TestDeviceOptions options, IRunUtil runUtil, long timeoutMs, String... command)480 public static CommandResult remoteSshCommandExecution( 481 GceAvdInfo gceAvd, 482 TestDeviceOptions options, 483 IRunUtil runUtil, 484 long timeoutMs, 485 String... command) { 486 return RemoteSshUtil.remoteSshCommandExec(gceAvd, options, runUtil, timeoutMs, command); 487 } 488 remoteSshCommandExec( GceAvdInfo gceAvd, TestDeviceOptions options, IRunUtil runUtil, String... command)489 private static String remoteSshCommandExec( 490 GceAvdInfo gceAvd, TestDeviceOptions options, IRunUtil runUtil, String... command) { 491 CommandResult res = 492 remoteSshCommandExecution(gceAvd, options, runUtil, BUGREPORT_TIMEOUT, command); 493 if (!CommandStatus.SUCCESS.equals(res.getStatus())) { 494 CLog.e("issue when attempting to execute '%s':", Arrays.asList(command)); 495 CLog.e("%s", res.getStderr()); 496 } 497 // We attempt to get a clean output from our command 498 String output = res.getStdout().trim(); 499 return output; 500 } 501 502 /** 503 * Reads the current content of the Gce Avd instance serial log. 504 * 505 * @param infos The {@link GceAvdInfo} describing the instance. 506 * @param avdConfigFile the avd config file 507 * @param jsonKeyFile the service account json key file. 508 * @param runUtil a {@link IRunUtil} to execute commands. 509 * @return The serial log output or null if something goes wrong. 510 */ getInstanceSerialLog( GceAvdInfo infos, File avdConfigFile, File jsonKeyFile, IRunUtil runUtil)511 public static String getInstanceSerialLog( 512 GceAvdInfo infos, File avdConfigFile, File jsonKeyFile, IRunUtil runUtil) { 513 AcloudConfigParser config = AcloudConfigParser.parseConfig(avdConfigFile); 514 if (config == null) { 515 CLog.e("Failed to parse our acloud config."); 516 return null; 517 } 518 if (infos == null) { 519 return null; 520 } 521 try { 522 Credential credential = createCredential(config, jsonKeyFile); 523 String project = config.getValueForKey(AcloudKeys.PROJECT); 524 String zone = config.getValueForKey(AcloudKeys.ZONE); 525 String instanceName = infos.instanceName(); 526 Compute compute = 527 new Compute.Builder( 528 GoogleNetHttpTransport.newTrustedTransport(), 529 JSON_FACTORY, 530 null) 531 .setApplicationName(project) 532 .setHttpRequestInitializer(credential) 533 .build(); 534 GetSerialPortOutput outputPort = 535 compute.instances().getSerialPortOutput(project, zone, instanceName); 536 SerialPortOutput output = outputPort.execute(); 537 return output.getContents(); 538 } catch (GeneralSecurityException | IOException e) { 539 CLog.e(e); 540 return null; 541 } 542 } 543 createCredential(AcloudConfigParser config, File jsonKeyFile)544 private static Credential createCredential(AcloudConfigParser config, File jsonKeyFile) 545 throws GeneralSecurityException, IOException { 546 if (jsonKeyFile != null) { 547 return GoogleApiClientUtil.createCredentialFromJsonKeyFile(jsonKeyFile, SCOPES); 548 } else if (config.getValueForKey(AcloudKeys.SERVICE_ACCOUNT_JSON_PRIVATE_KEY) != null) { 549 jsonKeyFile = 550 new File(config.getValueForKey(AcloudKeys.SERVICE_ACCOUNT_JSON_PRIVATE_KEY)); 551 return GoogleApiClientUtil.createCredentialFromJsonKeyFile(jsonKeyFile, SCOPES); 552 } else { 553 String serviceAccount = config.getValueForKey(AcloudKeys.SERVICE_ACCOUNT_NAME); 554 String serviceKey = config.getValueForKey(AcloudKeys.SERVICE_ACCOUNT_PRIVATE_KEY); 555 return GoogleApiClientUtil.createCredentialFromP12File( 556 serviceAccount, new File(serviceKey), SCOPES); 557 } 558 } 559 cleanUp()560 public void cleanUp() { 561 // Clean up logs file if any was created. 562 } 563 564 /** Returns the instance of the {@link IRunUtil}. */ 565 @VisibleForTesting getRunUtil()566 IRunUtil getRunUtil() { 567 return RunUtil.getDefault(); 568 } 569 570 /** 571 * Log the serial output of a device described by {@link GceAvdInfo}. 572 * 573 * @param infos The {@link GceAvdInfo} describing the instance. 574 * @param logger The {@link ITestLogger} where to log the serial log. 575 */ logSerialOutput(GceAvdInfo infos, ITestLogger logger)576 public void logSerialOutput(GceAvdInfo infos, ITestLogger logger) { 577 String output = 578 GceManager.getInstanceSerialLog( 579 infos, 580 getAvdConfigFile(), 581 getTestDeviceOptions().getSerivceAccountJsonKeyFile(), 582 getRunUtil()); 583 if (output == null) { 584 CLog.w("Failed to collect the instance serial logs."); 585 return; 586 } 587 try (ByteArrayInputStreamSource source = 588 new ByteArrayInputStreamSource(output.getBytes())) { 589 logger.testLog("gce_full_serial_log", LogDataType.TEXT, source); 590 } 591 } 592 593 /** Log the information related to the stable host image used. */ logStableHostImageInfos(IBuildInfo build)594 public void logStableHostImageInfos(IBuildInfo build) { 595 AcloudConfigParser config = AcloudConfigParser.parseConfig(getAvdConfigFile()); 596 if (config == null) { 597 CLog.e("Failed to parse our acloud config."); 598 return; 599 } 600 if (build == null) { 601 return; 602 } 603 if (config.getValueForKey(AcloudKeys.STABLE_HOST_IMAGE_NAME) != null) { 604 build.addBuildAttribute( 605 AcloudKeys.STABLE_HOST_IMAGE_NAME.toString(), 606 config.getValueForKey(AcloudKeys.STABLE_HOST_IMAGE_NAME)); 607 } 608 if (config.getValueForKey(AcloudKeys.STABLE_HOST_IMAGE_PROJECT) != null) { 609 build.addBuildAttribute( 610 AcloudKeys.STABLE_HOST_IMAGE_PROJECT.toString(), 611 config.getValueForKey(AcloudKeys.STABLE_HOST_IMAGE_PROJECT)); 612 } 613 } 614 615 /** 616 * Returns the {@link TestDeviceOptions} associated with the device that the gce manager was 617 * initialized with. 618 */ getTestDeviceOptions()619 private TestDeviceOptions getTestDeviceOptions() { 620 return mDeviceOptions; 621 } 622 623 @VisibleForTesting getAvdConfigFile()624 File getAvdConfigFile() { 625 if (getTestDeviceOptions().getAvdConfigTestResourceName() != null) { 626 return BuildInfo.getTestResource( 627 mTestResourceBuildInfos, getTestDeviceOptions().getAvdConfigTestResourceName()); 628 } 629 return getTestDeviceOptions().getAvdConfigFile(); 630 } 631 } 632