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.cluster; 17 18 import com.android.tradefed.command.remote.DeviceDescriptor; 19 import com.android.tradefed.config.GlobalConfiguration; 20 import com.android.tradefed.device.DeviceManager; 21 import com.android.tradefed.device.DeviceManager.FastbootDevice; 22 import com.android.tradefed.log.LogUtil.CLog; 23 import com.android.tradefed.util.VersionParser; 24 25 import com.google.common.base.Strings; 26 import com.google.common.net.HostAndPort; 27 import com.google.common.net.InetAddresses; 28 import com.google.common.primitives.Longs; 29 30 import java.net.InetAddress; 31 import java.net.UnknownHostException; 32 import java.security.InvalidParameterException; 33 import java.util.Map; 34 import java.util.regex.Matcher; 35 import java.util.regex.Pattern; 36 37 /** Static util functions for TF Cluster to get global config instances, host information, etc. */ 38 public class ClusterHostUtil { 39 40 private static String sHostName = null; 41 42 private static String sHostIpAddress = null; 43 44 static final String DEFAULT_TF_VERSION = "(unknown)"; 45 46 private static long sTfStartTime = getCurrentTimeMillis(); 47 48 /** 49 * Gets the hostname. 50 * 51 * @return the hostname or null if we were unable to fetch it. 52 */ getHostName()53 public static String getHostName() { 54 if (sHostName == null) { 55 try { 56 sHostName = InetAddress.getLocalHost().getHostName(); 57 } catch (UnknownHostException e) { 58 CLog.w("failed to get hostname: %s", e); 59 } 60 } 61 return sHostName; 62 } 63 64 /** 65 * Gets the IP address. 66 * 67 * @return the IP address or null if we were unable to fetch it. 68 */ getHostIpAddress()69 public static String getHostIpAddress() { 70 if (sHostIpAddress == null) { 71 try { 72 sHostIpAddress = InetAddress.getLocalHost().getHostAddress(); 73 } catch (UnknownHostException e) { 74 CLog.w("failed to get hostname: %s", e); 75 } 76 } 77 return sHostIpAddress; 78 } 79 80 /** 81 * Gets the TF version running on this host. 82 * 83 * @return this host's TF version. 84 */ getTfVersion()85 public static String getTfVersion() { 86 final String version = VersionParser.fetchVersion(); 87 return toValidTfVersion(version); 88 } 89 90 /** 91 * Validates a TF version and returns it if it is OK. 92 * 93 * @param version The string for a TF version provided by {@link VersionParser} 94 * @return the version if valid or a default if not. 95 */ toValidTfVersion(String version)96 protected static String toValidTfVersion(String version) { 97 if (Strings.isNullOrEmpty(version) || Longs.tryParse(version) == null) { 98 // Making sure the version is valid. It should be a build number 99 return DEFAULT_TF_VERSION; 100 } 101 return version; 102 } 103 104 /** 105 * Returns the run target for a given device descriptor. 106 * 107 * @param device {@link DeviceDescriptor} to get run target for. 108 * @return run target. 109 */ getRunTarget( DeviceDescriptor device, String runTargetFormat, Map<String, String> deviceTags)110 public static String getRunTarget( 111 DeviceDescriptor device, String runTargetFormat, Map<String, String> deviceTags) { 112 if (runTargetFormat != null) { 113 // Make sure the pattern is non-greedy. 114 Pattern p = Pattern.compile("\\{([^:\\}]+)(:.*)?\\}"); 115 Matcher m = p.matcher(runTargetFormat); 116 StringBuffer sb = new StringBuffer(); 117 while (m.find()) { 118 String pattern = m.group(1); 119 String key = null; 120 String txt = null; 121 switch (pattern) { 122 case "PRODUCT": 123 txt = device.getProduct(); 124 break; 125 case "PRODUCT_OR_DEVICE_CLASS": 126 // TODO: Refactor the logic to handle more flexible combinations. 127 txt = device.getProduct(); 128 if (device.isStubDevice()) { 129 String deviceClass = device.getDeviceClass(); 130 // If it's a fastboot device we report it as the product 131 if (!FastbootDevice.class.getSimpleName().equals(deviceClass)) { 132 txt = deviceClass; 133 } 134 } 135 break; 136 case "PRODUCT_VARIANT": 137 txt = device.getProductVariant(); 138 break; 139 case "API_LEVEL": 140 txt = device.getSdkVersion(); 141 break; 142 case "DEVICE_CLASS": 143 txt = device.getDeviceClass(); 144 break; 145 case "SERIAL": 146 txt = device.getSerial(); 147 break; 148 case "TAG": 149 if (deviceTags == null || deviceTags.isEmpty()) { 150 // simply delete the placeholder if there's nothing to match 151 txt = ""; 152 } else { 153 txt = deviceTags.get(device.getSerial()); 154 if (txt == null) { 155 txt = ""; // simply delete it if a tag does not exist 156 } 157 } 158 break; 159 case "DEVICE_PROP": 160 key = m.group(2).substring(1); 161 txt = device.getProperty(key); 162 break; 163 default: 164 throw new InvalidParameterException( 165 String.format( 166 "Unsupported pattern '%s' found for run target '%s'", 167 pattern, runTargetFormat)); 168 } 169 if (txt == null || DeviceManager.UNKNOWN_DISPLAY_STRING.equals(txt)) { 170 return DeviceManager.UNKNOWN_DISPLAY_STRING; 171 } 172 m.appendReplacement(sb, Matcher.quoteReplacement(txt)); 173 } 174 m.appendTail(sb); 175 return sb.toString(); 176 } 177 // Default behavior. 178 // TODO: Remove this when we cluster default run target is changed. 179 String runTarget = device.getProduct(); 180 if (!runTarget.equals(device.getProductVariant())) { 181 runTarget += ":" + device.getProductVariant(); 182 } 183 return runTarget; 184 } 185 186 /** 187 * Checks if a given input is a valid IP:PORT string. 188 * 189 * @param input a string to check 190 * @return true if the given input is an IP:PORT string 191 */ isIpPort(String input)192 public static boolean isIpPort(String input) { 193 try { 194 HostAndPort hostAndPort = HostAndPort.fromString(input); 195 return InetAddresses.isInetAddress(hostAndPort.getHost()); 196 } catch (IllegalArgumentException e) { 197 return false; 198 } 199 } 200 201 /** 202 * Returns the current system time. 203 * 204 * @return time in millis. 205 */ getCurrentTimeMillis()206 public static long getCurrentTimeMillis() { 207 return System.currentTimeMillis(); 208 } 209 getTfStartTimeMillis()210 public static long getTfStartTimeMillis() { 211 return sTfStartTime; 212 } 213 214 /** Get the {@link IClusterOptions} instance used to store cluster-related settings. */ getClusterOptions()215 public static IClusterOptions getClusterOptions() { 216 IClusterOptions clusterOptions = 217 (IClusterOptions) 218 GlobalConfiguration.getInstance() 219 .getConfigurationObject(ClusterOptions.TYPE_NAME); 220 if (clusterOptions == null) { 221 throw new IllegalStateException( 222 "cluster_options not defined. You must add this " 223 + "object to your global config. See google/atp/cluster.xml."); 224 } 225 226 return clusterOptions; 227 } 228 229 /** Get the {@link IClusterClient} instance used to interact with the TFC backend. */ getClusterClient()230 public static IClusterClient getClusterClient() { 231 IClusterClient ClusterClient = 232 (IClusterClient) 233 GlobalConfiguration.getInstance() 234 .getConfigurationObject(IClusterClient.TYPE_NAME); 235 if (ClusterClient == null) { 236 throw new IllegalStateException( 237 "cluster_client not defined. You must add this " 238 + "object to your global config. See google/atp/cluster.xml."); 239 } 240 241 return ClusterClient; 242 } 243 } 244