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