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.log.LogUtil.CLog; 19 20 import org.json.JSONArray; 21 import org.json.JSONException; 22 import org.json.JSONObject; 23 24 import java.util.HashMap; 25 import java.util.HashSet; 26 import java.util.Map; 27 import java.util.Set; 28 29 /** A class to encapsulate cluster command events to be uploaded. */ 30 public class ClusterCommandEvent implements IClusterEvent { 31 32 public static final String DATA_KEY_ERROR = "error"; 33 public static final String DATA_KEY_SUMMARY = "summary"; 34 public static final String DATA_KEY_SETUP_TIME_MILLIS = "setup_time_millis"; 35 public static final String DATA_KEY_FETCH_BUILD_TIME_MILLIS = "fetch_build_time_millis"; 36 public static final String DATA_KEY_TOTAL_TEST_COUNT = "total_test_count"; 37 public static final String DATA_KEY_FAILED_TEST_COUNT = "failed_test_count"; 38 public static final String DATA_KEY_PASSED_TEST_COUNT = "passed_test_count"; 39 public static final String DATA_KEY_FAILED_TEST_RUN_COUNT = "failed_test_run_count"; 40 public static final String DATA_KEY_LOST_DEVICE_DETECTED = "device_lost_detected"; 41 42 // Maximum size of an individual data string value. 43 public static final int MAX_DATA_STRING_SIZE = 4095; 44 45 public enum Type { 46 AllocationFailed, 47 ConfigurationError, 48 FetchFailed, 49 ExecuteFailed, 50 InvocationInitiated, 51 InvocationStarted, 52 InvocationFailed, 53 InvocationEnded, 54 InvocationCompleted, 55 TestRunInProgress, 56 TestEnded 57 } 58 59 private long mTimestamp; 60 private Type mType; 61 private String mCommandTaskId; 62 private String mAttemptId; 63 private String mHostName; 64 private InvocationStatus mInvocationStatus; 65 private Map<String, Object> mData = new HashMap<>(); 66 private Set<String> mDeviceSerials; 67 ClusterCommandEvent()68 private ClusterCommandEvent() {} 69 getHostName()70 public String getHostName() { 71 return mHostName; 72 } 73 getTimestamp()74 public long getTimestamp() { 75 return mTimestamp; 76 } 77 getType()78 public Type getType() { 79 return mType; 80 } 81 getCommandTaskId()82 public String getCommandTaskId() { 83 return mCommandTaskId; 84 } 85 getAttemptId()86 public String getAttemptId() { 87 return mAttemptId; 88 } 89 getInvocationStatus()90 public InvocationStatus getInvocationStatus() { 91 return mInvocationStatus; 92 } 93 getData()94 public Map<String, Object> getData() { 95 return mData; 96 } 97 getDeviceSerials()98 public Set<String> getDeviceSerials() { 99 return mDeviceSerials; 100 } 101 102 public static class Builder { 103 104 private long mTimestamp = System.currentTimeMillis(); 105 private Type mType; 106 private String mCommandTaskId; 107 private String mAttemptId; 108 private String mHostName; 109 private InvocationStatus mInvocationStatus; 110 private Map<String, Object> mData = new HashMap<>(); 111 private Set<String> mDeviceSerials = new HashSet<>(); 112 Builder()113 public Builder() {} 114 setTimestamp(final long timestamp)115 public Builder setTimestamp(final long timestamp) { 116 mTimestamp = timestamp; 117 return this; 118 } 119 setType(final Type type)120 public Builder setType(final Type type) { 121 mType = type; 122 return this; 123 } 124 setCommandTaskId(final String commandTaskId)125 public Builder setCommandTaskId(final String commandTaskId) { 126 mCommandTaskId = commandTaskId; 127 return this; 128 } 129 setAttemptId(final String attemptId)130 public Builder setAttemptId(final String attemptId) { 131 mAttemptId = attemptId; 132 return this; 133 } 134 setHostName(final String hostName)135 public Builder setHostName(final String hostName) { 136 mHostName = hostName; 137 return this; 138 } 139 setInvocationStatus(final InvocationStatus invocationStatus)140 public Builder setInvocationStatus(final InvocationStatus invocationStatus) { 141 mInvocationStatus = invocationStatus; 142 return this; 143 } 144 setData(final String name, final Object value)145 public Builder setData(final String name, final Object value) { 146 if (value instanceof String && ((String) value).length() > MAX_DATA_STRING_SIZE) { 147 CLog.w( 148 String.format( 149 "Data for '%s' exceeds %d characters, and has been truncated.", 150 name, MAX_DATA_STRING_SIZE)); 151 mData.put(name, ((String) value).substring(0, MAX_DATA_STRING_SIZE)); 152 } else { 153 mData.put(name, value); 154 } 155 return this; 156 } 157 setDeviceSerials(final Set<String> deviceSerials)158 public Builder setDeviceSerials(final Set<String> deviceSerials) { 159 mDeviceSerials = deviceSerials; 160 return this; 161 } 162 addDeviceSerial(final String deviceSerial)163 public Builder addDeviceSerial(final String deviceSerial) { 164 mDeviceSerials.add(deviceSerial); 165 return this; 166 } 167 build()168 public ClusterCommandEvent build() { 169 final ClusterCommandEvent obj = new ClusterCommandEvent(); 170 obj.mTimestamp = mTimestamp; 171 obj.mType = mType; 172 obj.mCommandTaskId = mCommandTaskId; 173 obj.mAttemptId = mAttemptId; 174 obj.mHostName = mHostName; 175 obj.mInvocationStatus = mInvocationStatus; 176 obj.mData = new HashMap<>(mData); 177 obj.mDeviceSerials = mDeviceSerials; 178 return obj; 179 } 180 } 181 182 /** 183 * Creates a base {@link Builder}. 184 * 185 * @return a {@link Builder}. 186 */ createEventBuilder()187 public static Builder createEventBuilder() { 188 return createEventBuilder(null); 189 } 190 191 /** 192 * Creates a base {@link Builder} for the given {@link ClusterCommand}. 193 * 194 * @return a {@link Builder}. 195 */ createEventBuilder(final ClusterCommand command)196 public static Builder createEventBuilder(final ClusterCommand command) { 197 final ClusterCommandEvent.Builder builder = new ClusterCommandEvent.Builder(); 198 if (command != null) { 199 builder.setCommandTaskId(command.getTaskId()); 200 builder.setAttemptId(command.getAttemptId()); 201 } 202 return builder; 203 } 204 205 /** {@inheritDoc} */ 206 @Override toJSON()207 public JSONObject toJSON() throws JSONException { 208 final JSONObject json = new JSONObject(); 209 json.put("type", this.getType().toString()); 210 // event time should be in POSIX timestamp. 211 json.put("time", this.getTimestamp() / 1000); 212 json.put("task_id", this.getCommandTaskId()); 213 json.put("attempt_id", this.getAttemptId()); 214 json.put("hostname", this.getHostName()); 215 // TODO(b/79583735): deprecated. 216 if (!this.getDeviceSerials().isEmpty()) { 217 json.put("device_serial", this.getDeviceSerials().iterator().next()); 218 } 219 json.put("device_serials", new JSONArray(this.getDeviceSerials())); 220 if (mInvocationStatus != null) { 221 json.put("invocation_status", mInvocationStatus.toJSON()); 222 } 223 json.put("data", new JSONObject(this.getData())); 224 return json; 225 } 226 227 @Override toString()228 public String toString() { 229 String str = null; 230 try { 231 str = toJSON().toString(); 232 } catch (final JSONException e) { 233 // ignore 234 } 235 return str; 236 } 237 } 238