1 /* 2 * Copyright (C) 2017 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.sandbox; 17 18 import com.android.ddmlib.Log.LogLevel; 19 import com.android.tradefed.config.Configuration; 20 import com.android.tradefed.config.ConfigurationException; 21 import com.android.tradefed.config.GlobalConfiguration; 22 import com.android.tradefed.config.IConfiguration; 23 import com.android.tradefed.config.IDeviceConfiguration; 24 import com.android.tradefed.config.SandboxConfigurationFactory; 25 import com.android.tradefed.device.IDeviceSelection; 26 import com.android.tradefed.log.FileLogger; 27 import com.android.tradefed.log.ILeveledLogOutput; 28 import com.android.tradefed.result.FileSystemLogSaver; 29 import com.android.tradefed.result.ILogSaver; 30 import com.android.tradefed.result.ITestInvocationListener; 31 import com.android.tradefed.result.SubprocessResultsReporter; 32 import com.android.tradefed.result.proto.StreamProtoResultReporter; 33 import com.android.tradefed.testtype.SubprocessTfLauncher; 34 import com.android.tradefed.util.StreamUtil; 35 36 import java.io.File; 37 import java.io.IOException; 38 import java.io.PrintWriter; 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.HashSet; 42 import java.util.List; 43 import java.util.Set; 44 45 /** 46 * Runner class that creates a {@link IConfiguration} based on a command line and dump it to a file. 47 * args: <DumpCmd> <output File> <remaing command line> 48 */ 49 public class SandboxConfigDump { 50 51 public enum DumpCmd { 52 /** The full xml based on the command line will be outputted */ 53 FULL_XML, 54 /** Only non-versioned element of the xml will be outputted */ 55 NON_VERSIONED_CONFIG, 56 /** A run-ready config will be outputted */ 57 RUN_CONFIG, 58 /** Special mode that allows the sandbox to generate another layer of sandboxing. */ 59 TEST_MODE, 60 } 61 62 /** 63 * We do not output the versioned elements to avoid causing the parent process to have issues 64 * with them when trying to resolve them 65 */ 66 public static final Set<String> VERSIONED_ELEMENTS = new HashSet<>(); 67 68 static { 69 VERSIONED_ELEMENTS.add(Configuration.SYSTEM_STATUS_CHECKER_TYPE_NAME); 70 VERSIONED_ELEMENTS.add(Configuration.DEVICE_METRICS_COLLECTOR_TYPE_NAME); 71 VERSIONED_ELEMENTS.add(Configuration.MULTI_PRE_TARGET_PREPARER_TYPE_NAME); 72 VERSIONED_ELEMENTS.add(Configuration.MULTI_PREPARER_TYPE_NAME); 73 VERSIONED_ELEMENTS.add(Configuration.TARGET_PREPARER_TYPE_NAME); 74 VERSIONED_ELEMENTS.add(Configuration.TEST_TYPE_NAME); 75 } 76 77 /** 78 * Parse the args and creates a {@link IConfiguration} from it then dumps it to the result file. 79 */ parse(String[] args)80 public int parse(String[] args) { 81 // TODO: add some more checking 82 List<String> argList = new ArrayList<>(Arrays.asList(args)); 83 DumpCmd cmd = DumpCmd.valueOf(argList.remove(0)); 84 File resFile = new File(argList.remove(0)); 85 SandboxConfigurationFactory factory = SandboxConfigurationFactory.getInstance(); 86 PrintWriter pw = null; 87 try { 88 IConfiguration config = 89 factory.createConfigurationFromArgs(argList.toArray(new String[0]), cmd); 90 if (DumpCmd.RUN_CONFIG.equals(cmd) || DumpCmd.TEST_MODE.equals(cmd)) { 91 config.getCommandOptions().setShouldUseSandboxing(false); 92 config.getConfigurationDescription().setSandboxed(true); 93 // Don't use replication in the sandbox 94 config.getCommandOptions().setReplicateSetup(false); 95 // Set the reporter 96 ITestInvocationListener reporter = null; 97 if (getSandboxOptions(config).shouldUseProtoReporter()) { 98 reporter = new StreamProtoResultReporter(); 99 } else { 100 reporter = new SubprocessResultsReporter(); 101 ((SubprocessResultsReporter) reporter).setOutputTestLog(true); 102 } 103 config.setTestInvocationListener(reporter); 104 // Set log level for sandbox 105 ILeveledLogOutput logger = config.getLogOutput(); 106 logger.setLogLevel(LogLevel.VERBOSE); 107 if (logger instanceof FileLogger) { 108 // Ensure we get the stdout logging in FileLogger case. 109 ((FileLogger) logger).setLogLevelDisplay(LogLevel.VERBOSE); 110 } 111 112 ILogSaver logSaver = config.getLogSaver(); 113 if (logSaver instanceof FileSystemLogSaver) { 114 // Send the files directly, the parent will take care of compression if needed 115 ((FileSystemLogSaver) logSaver).setCompressFiles(false); 116 } 117 118 // Ensure in special conditions (placeholder devices) we can still allocate. 119 secureDeviceAllocation(config); 120 121 // Mark as subprocess 122 config.getCommandOptions() 123 .getInvocationData() 124 .put(SubprocessTfLauncher.SUBPROCESS_TAG_NAME, "true"); 125 } 126 if (DumpCmd.TEST_MODE.equals(cmd)) { 127 // We allow one more layer of sandbox to be generated 128 config.getCommandOptions().setShouldUseSandboxing(true); 129 config.getConfigurationDescription().setSandboxed(false); 130 // Ensure we turn off test mode afterward to avoid infinite sandboxing 131 config.getCommandOptions().setUseSandboxTestMode(false); 132 } 133 pw = new PrintWriter(resFile); 134 if (DumpCmd.NON_VERSIONED_CONFIG.equals(cmd)) { 135 // Remove elements that are versioned. 136 config.dumpXml( 137 pw, 138 new ArrayList<>(VERSIONED_ELEMENTS), 139 true, /* Don't print unchanged options */ 140 false); 141 } else { 142 // FULL_XML in that case. 143 config.dumpXml(pw); 144 } 145 } catch (ConfigurationException | IOException e) { 146 e.printStackTrace(); 147 return 1; 148 } finally { 149 StreamUtil.close(pw); 150 } 151 return 0; 152 } 153 main(final String[] mainArgs)154 public static void main(final String[] mainArgs) { 155 try { 156 GlobalConfiguration.createGlobalConfiguration(new String[] {}); 157 } catch (ConfigurationException e) { 158 e.printStackTrace(); 159 System.exit(1); 160 } 161 SandboxConfigDump configDump = new SandboxConfigDump(); 162 int code = configDump.parse(mainArgs); 163 System.exit(code); 164 } 165 getSandboxOptions(IConfiguration config)166 private SandboxOptions getSandboxOptions(IConfiguration config) { 167 return (SandboxOptions) 168 config.getConfigurationObject(Configuration.SANBOX_OPTIONS_TYPE_NAME); 169 } 170 secureDeviceAllocation(IConfiguration config)171 private void secureDeviceAllocation(IConfiguration config) { 172 for (IDeviceConfiguration deviceConfig : config.getDeviceConfig()) { 173 IDeviceSelection requirements = deviceConfig.getDeviceRequirements(); 174 if (requirements.nullDeviceRequested() 175 || requirements.tcpDeviceRequested() 176 || requirements.gceDeviceRequested()) { 177 // Reset serials, ensure any null/tcp/gce-device can be selected. 178 requirements.setSerial(); 179 } 180 } 181 } 182 } 183