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