1 /* 2 * Copyright (C) 2016 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 17 package com.android.tradefed.host; 18 19 import com.android.tradefed.config.ConfigurationException; 20 import com.android.tradefed.config.Option; 21 import com.android.tradefed.config.OptionClass; 22 import com.android.tradefed.error.HarnessRuntimeException; 23 import com.android.tradefed.log.LogUtil.CLog; 24 import com.android.tradefed.result.error.InfraErrorIdentifier; 25 import com.android.tradefed.util.RunInterruptedException; 26 27 import java.io.File; 28 import java.net.InetAddress; 29 import java.net.NetworkInterface; 30 import java.net.SocketException; 31 import java.util.ArrayList; 32 import java.util.Enumeration; 33 import java.util.HashMap; 34 import java.util.HashSet; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.Map.Entry; 38 import java.util.Set; 39 import java.util.concurrent.Semaphore; 40 41 /** 42 * Host options holder class. 43 * This class is used to store host-wide options. 44 */ 45 @OptionClass(alias = "host_options", global_namespace = false) 46 public class HostOptions implements IHostOptions { 47 48 @Option(name = "concurrent-flasher-limit", description = 49 "The maximum number of concurrent flashers (may be useful to avoid memory constraints)") 50 private Integer mConcurrentFlasherLimit = 1; 51 52 @Option( 53 name = "concurrent-download-limit", 54 description = 55 "The maximum number of concurrent downloads (may be useful to avoid network " 56 + "constraints)" 57 ) 58 private Integer mConcurrentDownloadLimit = null; 59 60 @Option( 61 name = "concurrent-virtual-device-startup-limit", 62 description = 63 "The maximum number of concurrent virtual device startup to avoid resource" 64 + " contentions depending on factors such as network, CPU, I/O etc.") 65 private Integer mConcurrentVirtualDeviceStartupLimit = null; 66 67 @Option(name = "concurrent-limits", description = 68 "The maximum number of concurrent actions of a given type.") 69 private Map<PermitLimitType, Integer> mConcurrentLimit = new HashMap<>(); 70 71 @Option( 72 name = "fastboot-tmpdir", 73 description = "The location of temporary directory used by fastboot" 74 ) 75 private File mFastbootTmpDir = null; 76 77 @Option( 78 name = "enable-fastbootd-mode", 79 description = "Feature flag to enable the support for fastbootd.") 80 private boolean mEnableFastbootdMode = true; 81 82 @Option(name = "download-cache-dir", description = "the directory for caching downloaded " 83 + "flashing files. Should be on the same filesystem as java.io.tmpdir. Consider " 84 + "changing the java.io.tmpdir property if you want to move downloads to a different " 85 + "filesystem.") 86 private File mDownloadCacheDir = new File(System.getProperty("java.io.tmpdir"), "lc_cache"); 87 88 @Option(name = "use-sso-client", description = "Use a SingleSignOn client for HTTP requests.") 89 private Boolean mUseSsoClient = true; 90 91 @Option( 92 name = "service-account-json-key-file", 93 description = 94 "Specify a service account json key file, and a String key name to identify it." 95 ) 96 private Map<String, File> mJsonServiceAccountMap = new HashMap<>(); 97 98 @Option(name = "label", description = "Labels to describe the host.") 99 private List<String> mLabels = new ArrayList<>(); 100 101 @Option( 102 name = "known-gce-device-ip-pool", 103 description = 104 "known remote device available via ip associated with the " 105 + "gce-device placeholder.") 106 private Set<String> mKnownGceDeviceIpPool = new HashSet<>(); 107 108 @Option( 109 name = "known-remote-device-ip-pool", 110 description = 111 "known remote device available via ip associated with the " 112 + "remote-device placeholder.") 113 private Set<String> mKnownRemoteDeviceIpPool = new HashSet<>(); 114 115 @Option( 116 name = "use-zip64-in-partial-download", 117 description = "Whether to use zip64 format in partial download.") 118 private boolean mUseZip64InPartialDownload = true; 119 120 @Option( 121 name = "use-network-interface", 122 description = "The network interface used to connect to test devices.") 123 private String mNetworkInterface = null; 124 125 @Option( 126 name = "preconfigured-virtual-device-pool", 127 description = "Preconfigured virtual device pool. (Value format: $hostname:$user.)") 128 private List<String> mPreconfiguredVirtualDevicePool = new ArrayList<>(); 129 130 @Option( 131 name = "preconfigured-native-device-pool", 132 description = "Preconfigured Native virtual device pool. (Value format: $hostname.)") 133 private List<String> mPreconfiguredNativeDevicePool = new ArrayList<>(); 134 135 @Option( 136 name = "flash-with-fuse-zip", 137 description = "Use `fastboot flashall` on a folder of fuse mounted device image zip " 138 + "instead of `fastboot update` with zip") 139 private boolean mFlashWithFuseZip = false; 140 141 @Option( 142 name = "cache-size-limit", 143 description = 144 "The maximum allowed size(bytes) of the local file cache. (default: 15GB)") 145 private Long mCacheSizeLimit = 15L * 1024L * 1024L * 1024L; 146 147 @Option( 148 name = "test-phase-timeout", 149 description = 150 "the maximum time to wait for test phase to finish before attempting to force" 151 + "stop it. A value of zero will indicate no timeout.", 152 isTimeVal = true) 153 private long mTestPhaseTimeout = 0; 154 155 @Option( 156 name = "enable-incremental-flashing", 157 description = "Feature flag to enable incremental flashing on one host.") 158 private boolean mEnableIncrementalFlashing = false; 159 160 @Option( 161 name = "opt-out-incremental-flashing", 162 description = "Allows an host to fully opt-out of incremental flashing.") 163 private boolean mOptOutFromIncrementalFlashing = false; 164 165 @Option( 166 name = "disable-host-metric-reporting", 167 description = "Feature flag to disable the support for host metric reporting.") 168 private boolean mDisableHostMetricReporting = false; 169 170 private Map<PermitLimitType, Semaphore> mConcurrentLocks = new HashMap<>(); 171 private Map<PermitLimitType, Integer> mInternalConcurrentLimits = new HashMap<>(); 172 173 /** {@inheritDoc} */ 174 @Override getCacheSizeLimit()175 public Long getCacheSizeLimit() { 176 return mCacheSizeLimit; 177 } 178 179 /** {@inheritDoc} */ 180 @Override getConcurrentFlasherLimit()181 public Integer getConcurrentFlasherLimit() { 182 return mConcurrentFlasherLimit; 183 } 184 185 /** {@inheritDoc} */ 186 @Override getConcurrentDownloadLimit()187 public Integer getConcurrentDownloadLimit() { 188 return mConcurrentDownloadLimit; 189 } 190 191 @Override getConcurrentVirtualDeviceStartupLimit()192 public Integer getConcurrentVirtualDeviceStartupLimit() { 193 return mConcurrentVirtualDeviceStartupLimit; 194 } 195 196 /** {@inheritDoc} */ 197 @Override getFastbootTmpDir()198 public File getFastbootTmpDir() { 199 if (mFastbootTmpDir != null) { 200 if (!mFastbootTmpDir.exists() || !mFastbootTmpDir.isDirectory()) { 201 throw new HarnessRuntimeException( 202 String.format( 203 "Fastboot tmp dir '%s' is missing and was expected.", 204 mFastbootTmpDir), 205 InfraErrorIdentifier.LAB_HOST_FILESYSTEM_ERROR); 206 } 207 return mFastbootTmpDir; 208 } 209 return null; 210 } 211 212 /** {@inheritDoc} */ 213 @Override isFastbootdEnable()214 public boolean isFastbootdEnable() { 215 return mEnableFastbootdMode; 216 } 217 218 /** {@inheritDoc} */ 219 @Override getDownloadCacheDir()220 public File getDownloadCacheDir() { 221 return mDownloadCacheDir; 222 } 223 224 /** {@inheritDoc} */ 225 @Override shouldUseSsoClient()226 public Boolean shouldUseSsoClient() { 227 return mUseSsoClient; 228 } 229 230 /** {@inheritDoc} */ 231 @Override getServiceAccountJsonKeyFiles()232 public Map<String, File> getServiceAccountJsonKeyFiles() { 233 return new HashMap<>(mJsonServiceAccountMap); 234 } 235 236 /** {@inheritDoc} */ 237 @Override validateOptions()238 public void validateOptions() throws ConfigurationException { 239 // Validation of host options 240 } 241 242 /** {@inheritDoc} */ 243 @Override getLabels()244 public List<String> getLabels() { 245 return new ArrayList<>(mLabels); 246 } 247 248 /** {@inheritDoc} */ 249 @Override getKnownGceDeviceIpPool()250 public Set<String> getKnownGceDeviceIpPool() { 251 return new HashSet<>(mKnownGceDeviceIpPool); 252 } 253 254 /** {@inheritDoc} */ 255 @Override getKnownRemoteDeviceIpPool()256 public Set<String> getKnownRemoteDeviceIpPool() { 257 return new HashSet<>(mKnownRemoteDeviceIpPool); 258 } 259 260 /** {@inheritDoc} */ 261 @Override getKnownPreconfigureVirtualDevicePool()262 public List<String> getKnownPreconfigureVirtualDevicePool() { 263 return new ArrayList<>(mPreconfiguredVirtualDevicePool); 264 } 265 266 /** {@inheritDoc} */ 267 @Override getKnownPreconfigureNativeDevicePool()268 public List<String> getKnownPreconfigureNativeDevicePool() { 269 return new ArrayList<>(mPreconfiguredNativeDevicePool); 270 } 271 272 /** {@inheritDoc} */ 273 @Override getUseZip64InPartialDownload()274 public boolean getUseZip64InPartialDownload() { 275 return mUseZip64InPartialDownload; 276 } 277 278 /** {@inheritDoc} */ 279 @Override getNetworkInterface()280 public String getNetworkInterface() { 281 if (mNetworkInterface != null) { 282 return mNetworkInterface; 283 } 284 285 try { 286 for (Enumeration<NetworkInterface> enNetI = NetworkInterface.getNetworkInterfaces(); 287 enNetI.hasMoreElements(); ) { 288 NetworkInterface netI = enNetI.nextElement(); 289 if (!netI.isUp()) { 290 continue; 291 } 292 for (Enumeration<InetAddress> enumIpAddr = netI.getInetAddresses(); 293 enumIpAddr.hasMoreElements(); ) { 294 InetAddress inetAddress = enumIpAddr.nextElement(); 295 if (!inetAddress.isAnyLocalAddress() 296 && !inetAddress.isLinkLocalAddress() 297 && !inetAddress.isLoopbackAddress() 298 && !inetAddress.isMulticastAddress()) { 299 mNetworkInterface = netI.getName(); 300 return mNetworkInterface; 301 } 302 } 303 } 304 } catch (SocketException e) { 305 CLog.w("Failed to get host's active interface"); 306 CLog.w(e); 307 } 308 return null; 309 } 310 311 @Override getTestPhaseTimeout()312 public long getTestPhaseTimeout() { 313 return mTestPhaseTimeout; 314 } 315 316 @Override initConcurrentLocks()317 public void initConcurrentLocks() { 318 // Do not reinit if it has been called before 319 if (!mConcurrentLocks.isEmpty()) { 320 return; 321 } 322 mInternalConcurrentLimits.putAll(mConcurrentLimit); 323 // Backfill flasher & download limit from their dedicated option 324 if (!mInternalConcurrentLimits.containsKey(PermitLimitType.CONCURRENT_FLASHER)) { 325 mInternalConcurrentLimits.put( 326 PermitLimitType.CONCURRENT_FLASHER, mConcurrentFlasherLimit); 327 } 328 if (!mInternalConcurrentLimits.containsKey(PermitLimitType.CONCURRENT_DOWNLOAD)) { 329 mInternalConcurrentLimits.put( 330 PermitLimitType.CONCURRENT_DOWNLOAD, mConcurrentDownloadLimit); 331 } 332 333 if (!mInternalConcurrentLimits.containsKey( 334 PermitLimitType.CONCURRENT_VIRTUAL_DEVICE_STARTUP)) { 335 mInternalConcurrentLimits.put( 336 PermitLimitType.CONCURRENT_VIRTUAL_DEVICE_STARTUP, 337 mConcurrentVirtualDeviceStartupLimit); 338 } 339 340 for (Entry<PermitLimitType, Integer> limits : mInternalConcurrentLimits.entrySet()) { 341 if (limits.getValue() == null) { 342 continue; 343 } 344 mConcurrentLocks.put(limits.getKey(), 345 new Semaphore(limits.getValue(), true /* fair */)); 346 } 347 } 348 349 @Override takePermit(PermitLimitType type)350 public void takePermit(PermitLimitType type) { 351 if (!mConcurrentLocks.containsKey(type)) { 352 return; 353 } 354 CLog.i( 355 "Requesting a '%s' permit out of the max limit of %s. Current queue " 356 + "length: %s", 357 type, 358 mInternalConcurrentLimits.get(type), 359 mConcurrentLocks.get(type).getQueueLength()); 360 try { 361 mConcurrentLocks.get(type).acquire(); 362 } catch (InterruptedException e) { 363 throw new RunInterruptedException(e.getMessage(), e, InfraErrorIdentifier.UNDETERMINED); 364 } 365 } 366 367 @Override returnPermit(PermitLimitType type)368 public void returnPermit(PermitLimitType type) { 369 if (!mConcurrentLocks.containsKey(type)) { 370 return; 371 } 372 mConcurrentLocks.get(type).release(); 373 } 374 375 @Override getAvailablePermits(PermitLimitType type)376 public Integer getAvailablePermits(PermitLimitType type) { 377 if (!mConcurrentLocks.containsKey(type)) { 378 return Integer.MAX_VALUE; 379 } 380 return mConcurrentLocks.get(type).availablePermits(); 381 } 382 383 @Override getInUsePermits(PermitLimitType type)384 public int getInUsePermits(PermitLimitType type) { 385 if (!mConcurrentLocks.containsKey(type)) { 386 return 0; 387 } 388 return mInternalConcurrentLimits.get(type) - mConcurrentLocks.get(type).availablePermits(); 389 } 390 391 @Override shouldFlashWithFuseZip()392 public boolean shouldFlashWithFuseZip() { 393 return mFlashWithFuseZip; 394 } 395 396 /** {@inheritDoc} */ 397 @Override isIncrementalFlashingEnabled()398 public boolean isIncrementalFlashingEnabled() { 399 return mEnableIncrementalFlashing; 400 } 401 402 /** {@inheritDoc} */ 403 @Override isOptOutOfIncrementalFlashing()404 public boolean isOptOutOfIncrementalFlashing() { 405 return mOptOutFromIncrementalFlashing; 406 } 407 408 /** {@inheritDoc} */ 409 @Override isHostMetricReportingDisabled()410 public boolean isHostMetricReportingDisabled() { 411 return mDisableHostMetricReporting; 412 } 413 } 414