PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/com/atlassian/bamboo/plugin/agentsmith/ViewAgentWallboard.java

https://bitbucket.org/davidrizzuto/agent-smith-wallboard-plugin
Java | 368 lines | 294 code | 63 blank | 11 comment | 71 complexity | e0887028e005658d0de06cca72892382 MD5 | raw file
Possible License(s): Apache-2.0
  1. package com.atlassian.bamboo.plugin.agentsmith;
  2. import com.atlassian.bamboo.agent.elastic.server.ElasticInstanceManager;
  3. import com.atlassian.bamboo.agent.elastic.server.RemoteElasticInstance;
  4. import com.atlassian.bamboo.agent.elastic.server.RemoteElasticInstanceState;
  5. import com.atlassian.bamboo.build.BuildExecutionManager;
  6. import com.atlassian.bamboo.buildqueue.manager.AgentManager;
  7. import com.atlassian.bamboo.buildqueue.manager.LocalAgentManager;
  8. import com.atlassian.bamboo.v2.build.BuildContext;
  9. import com.atlassian.bamboo.v2.build.CurrentlyBuilding;
  10. import com.atlassian.bamboo.v2.build.agent.BuildAgent;
  11. import com.atlassian.bamboo.v2.build.queue.BuildQueueManager;
  12. import com.atlassian.bamboo.ww2.BambooActionSupport;
  13. import com.atlassian.bamboo.ww2.actions.admin.elastic.ElasticUIBean;
  14. import org.apache.log4j.Logger;
  15. import java.util.*;
  16. public class ViewAgentWallboard extends BambooActionSupport {
  17. // ------------------------------------------------------------------------------------------------------- Constants
  18. @SuppressWarnings({"UnusedDeclaration"})
  19. private static final Logger log = Logger.getLogger(ViewAgentWallboard.class);
  20. private static final List<RemoteElasticInstanceState> ELASTIC_INSTANCE_PENDING =
  21. Collections.unmodifiableList(Arrays.asList(RemoteElasticInstanceState.INITIAL, RemoteElasticInstanceState.STARTING, RemoteElasticInstanceState.IDENTIFIED));
  22. private static final List<RemoteElasticInstanceState> ELASTIC_INSTANCE_SHUTTING_DOWN =
  23. Collections.unmodifiableList(Arrays.asList(RemoteElasticInstanceState.STOPPING, RemoteElasticInstanceState.STOPPED, RemoteElasticInstanceState.SHUTTING_DOWN));
  24. private static final int DEFAULT_SECONDS_BEFORE_NEXT_REFRESH = 15;
  25. private static final int DEFAULT_MINUTES_BEFORE_QUEUE_ITEM_OVERDUE = 1;
  26. private static final int DEFAULT_MINUTES_BEFORE_PENDING_AGENT_OVERDUE = 5;
  27. private static final Comparator<Map> QUEUE_LIST_COMPARATOR = new Comparator<Map>() {
  28. public int compare(Map left, Map right) {
  29. return ((Long) right.get("elapsedTime")).compareTo((Long) left.get("elapsedTime"));
  30. }
  31. };
  32. // ---------------------------------------------------------------------------------------------------- Dependencies
  33. private AgentManager agentManager;
  34. private ElasticInstanceManager elasticInstanceManager;
  35. private BuildExecutionManager buildExecutionManager;
  36. private BuildQueueManager buildQueueManager;
  37. private ElasticUIBean elasticUIBean;
  38. // ------------------------------------------------------------------------------------------------- Type Properties
  39. private boolean showLegend = true;
  40. private int secondsBeforeNextRefresh = DEFAULT_SECONDS_BEFORE_NEXT_REFRESH;
  41. private int minutesBeforeQueueItemOverdue = DEFAULT_MINUTES_BEFORE_QUEUE_ITEM_OVERDUE;
  42. private int minutesBeforePendingAgentOverdue = DEFAULT_MINUTES_BEFORE_PENDING_AGENT_OVERDUE;
  43. private List<Map> agentStats = new ArrayList<Map>();
  44. private List<Map> queueList = new ArrayList<Map>();
  45. private Map<String, Long> queuetimes = new HashMap<String, Long>();
  46. private Map createStatistics(List<BuildAgent> buildAgents, String agentType) {
  47. int idle = 0;
  48. int busy = 0;
  49. int hung = 0;
  50. int disabled = 0;
  51. for (BuildAgent buildAgent : buildAgents) {
  52. if (buildAgent.isActive() && buildAgent.isEnabled()) {
  53. if (buildAgent.isBusy()) {
  54. CurrentlyBuilding currentlyBuilding = buildExecutionManager.getBuildRunningOnAgent(buildAgent.getId());
  55. if (currentlyBuilding != null && currentlyBuilding.getBuildHangDetails() != null) {
  56. hung++;
  57. } else {
  58. busy++;
  59. }
  60. } else {
  61. idle++;
  62. }
  63. } else if (buildAgent.isActive()) {
  64. disabled++;
  65. }
  66. }
  67. int total = idle + busy + hung + disabled;
  68. Map result = new HashMap();
  69. result.put("agentType", agentType);
  70. result.put("i18nKeyName", "agent.smith." + agentType + ".agents");
  71. result.put("idle", idle);
  72. result.put("busy", busy);
  73. result.put("hung", hung);
  74. result.put("disabled", disabled);
  75. result.put("total", total);
  76. int idlePercent = getPercentage(idle, total);
  77. int hungPercent = getPercentage(hung, total);
  78. int disabledPercent = getPercentage(disabled, total);
  79. //fix the percentages so they add to 100
  80. int missingPercent = 100 - (idlePercent + hungPercent + disabledPercent);
  81. if (busy == 0 && missingPercent != 0) {
  82. if (idlePercent != 0) {
  83. idlePercent += missingPercent;
  84. } else if (disabledPercent != 0) {
  85. disabledPercent += missingPercent;
  86. } else {
  87. hungPercent += missingPercent;
  88. }
  89. }
  90. result.put("idlePercent", idlePercent);
  91. result.put("hungPercent", hungPercent);
  92. result.put("disabledPercent", disabledPercent);
  93. if (idlePercent == 100) {
  94. result.put("idlePercentCssClass", "idle curveball");
  95. } else if (idle != 0 && hung == 0 && busy == 0){
  96. result.put("idlePercentCssClass", "idle curveball");
  97. }else {
  98. result.put("idlePercentCssClass", "idle");
  99. }
  100. if (hungPercent == 100) {
  101. result.put("hungPercentCssClass", "hung curveball");
  102. } else {
  103. result.put("hungPercentCssClass", "hung");
  104. }
  105. if (disabledPercent == 100) {
  106. result.put("disabledPercentCssClass", "disabled curveball");
  107. //this makes sure the idle bar doesn't stick out past the round corners
  108. result.put("idlePercentCssClass", "idle curveball");
  109. } else {
  110. result.put("disabledPercentCssClass", "disabled");
  111. }
  112. result.put("instanceStatus", Collections.emptyList());
  113. result.put("show", (total > 0));
  114. result.put("agentCssClass", "agent");
  115. if (hung > 0) {
  116. result.put("qtyCssClass", "qty hung");
  117. } else {
  118. result.put("qtyCssClass", "qty");
  119. }
  120. result.put("totalInstanceCost", 0d);
  121. return result;
  122. }
  123. private int getPercentage(int value, int total) {
  124. float percentage = 0;
  125. if (value > 0) {
  126. percentage = (float) value / (float) total * 100;
  127. }
  128. return (int) percentage;
  129. }
  130. @Override
  131. public String doDefault() throws Exception {
  132. if (elasticInstanceManager == null) {
  133. throw new UnsupportedOperationException("Bamboo 3.1.1 or higher is required for this plugin");
  134. }
  135. List<BuildAgent> localAgents = new ArrayList<BuildAgent>(agentManager.getAllLocalAgents());
  136. List<BuildAgent> elasticAgents = agentManager.getOnlineElasticAgents();
  137. List<BuildAgent> remoteAgents = new ArrayList<BuildAgent>(agentManager.getActiveAndEnabledAgents());
  138. remoteAgents.removeAll(localAgents);
  139. remoteAgents.removeAll(elasticAgents);
  140. agentStats.add(createStatistics(localAgents, "local"));
  141. agentStats.add(createStatistics(remoteAgents, "remote"));
  142. //ELASTIC
  143. List<String> instanceStatus = new ArrayList<String>();
  144. Map elasticAgentStatistics = createStatistics(elasticAgents, "elastic");
  145. agentStats.add(elasticAgentStatistics);
  146. Double totalInstanceCost = 0d;
  147. Integer elasticPendingCount = 0;
  148. Integer elasticShuttingCount = 0;
  149. Integer elasticFailedCount = 0;
  150. Long maxElasticWaitTime = 0L;
  151. Long totalElasticWaitTime = 0L;
  152. for (RemoteElasticInstance elasticAgent : elasticInstanceManager.getElasticRemoteAgents()) {
  153. RemoteElasticInstanceState status = elasticAgent.getState();
  154. String currentStatus = "";
  155. if (ELASTIC_INSTANCE_PENDING.contains(status) ||
  156. (status == RemoteElasticInstanceState.RUNNING && elasticAgent.isAgentLoading())) {
  157. currentStatus = "pending";
  158. elasticPendingCount++;
  159. } else if (ELASTIC_INSTANCE_SHUTTING_DOWN.contains(status)) {
  160. currentStatus = "shutting";
  161. elasticShuttingCount++;
  162. } else if (status.isFinal()) {
  163. currentStatus = "failed";
  164. elasticFailedCount++;
  165. }
  166. //elasticUIBean is only available from Bamboo 3.2
  167. if (elasticUIBean != null) {
  168. Double instanceCost = elasticUIBean.getInstancePrice(elasticAgent);
  169. if (instanceCost != null) {
  170. totalInstanceCost += instanceCost;
  171. }
  172. }
  173. //check time data for pending agents
  174. if (currentStatus.equals("pending")) {
  175. Long elapsed = getCurrentSystemTime() - elasticAgent.getInstance().getLaunchTime().getTime();
  176. totalElasticWaitTime += elapsed;
  177. if (elapsed > maxElasticWaitTime) {
  178. maxElasticWaitTime = elapsed;
  179. }
  180. if (elapsed > getMinutesBeforePendingAgentOverdue()*60*1000) {
  181. currentStatus = "overdue";
  182. }
  183. }
  184. instanceStatus.add(currentStatus);
  185. }
  186. elasticAgentStatistics.put("totalInstanceCost", totalInstanceCost);
  187. elasticAgentStatistics.put("pendingCount", elasticPendingCount);
  188. elasticAgentStatistics.put("shuttingCount", elasticShuttingCount);
  189. elasticAgentStatistics.put("failedCount", elasticFailedCount);
  190. elasticAgentStatistics.put("maxElasticWaitTime", maxElasticWaitTime);
  191. elasticAgentStatistics.put("totalElasticWaitTime", totalElasticWaitTime);
  192. Collections.sort(instanceStatus);
  193. elasticAgentStatistics.put("instanceStatus", instanceStatus);
  194. if (!instanceStatus.isEmpty()) {
  195. elasticAgentStatistics.put("show", true);
  196. elasticAgentStatistics.put("agentCssClass", "agent has-misc");
  197. }
  198. //QUEUE TIMES
  199. Long maxElapsed = 0L;
  200. Long totalQueueTime = 0L;
  201. for (BuildContext buildContext : buildQueueManager.getBuildQueue()) {
  202. CurrentlyBuilding currentlyBuilding = buildExecutionManager.getCurrentlyBuildingByBuildResult(buildContext);
  203. Long elapsed = getCurrentSystemTime() - currentlyBuilding.getQueueTime().getTime();
  204. if (maxElapsed < elapsed) {
  205. maxElapsed = elapsed;
  206. }
  207. totalQueueTime += elapsed;
  208. Map queueItem = new HashMap();
  209. queueItem.put("elapsedTime", elapsed);
  210. boolean isOverdue = elapsed > getMinutesBeforeQueueItemOverdue() * 60 * 1000;
  211. queueItem.put("isOverdue", isOverdue);
  212. queueItem.put("cssClass", isOverdue ? "overdue" : "");
  213. queueItem.put("planName", buildContext.getPlanName());
  214. String readableElapsedTimei18nKeyName = "agent.smith.queue.time.none";
  215. long readableElapsedTime = 0;
  216. if (elapsed > 0) {
  217. readableElapsedTime = elapsed / 1000;
  218. if (readableElapsedTime > 60) {
  219. readableElapsedTime /= 60;
  220. if (readableElapsedTime > 60) {
  221. readableElapsedTime /= 60;
  222. readableElapsedTimei18nKeyName = "agent.smith.queue.time.hour";
  223. } else {
  224. readableElapsedTimei18nKeyName = "agent.smith.queue.time.minute";
  225. }
  226. } else {
  227. readableElapsedTimei18nKeyName = "agent.smith.queue.time.second";
  228. }
  229. }
  230. queueItem.put("readableElapsedTime", readableElapsedTime);
  231. queueItem.put("readableElapsedTimei18nKeyName", readableElapsedTimei18nKeyName);
  232. queueList.add(queueItem);
  233. }
  234. queuetimes.put("maxQueueTime", maxElapsed);
  235. queuetimes.put("totalQueueTime", totalQueueTime);
  236. for (Map queueItem : queueList) {
  237. int width = (int) ((float) (Long) queueItem.get("elapsedTime") / (float) maxElapsed * 100);
  238. if (width == 0) {
  239. width = 1;
  240. }
  241. queueItem.put("width", width);
  242. }
  243. Collections.sort(queueList, QUEUE_LIST_COMPARATOR);
  244. return SUCCESS;
  245. }
  246. // -------------------------------------------------------------------------------------------------- Public Methods
  247. public List<Map> getQueueList() {
  248. return queueList;
  249. }
  250. public List<Map> getAgentStats() {
  251. return agentStats;
  252. }
  253. public Map getQueueTimes() {
  254. return this.queuetimes;
  255. }
  256. public int getMinutesBeforeQueueItemOverdue() {
  257. if (minutesBeforeQueueItemOverdue < DEFAULT_MINUTES_BEFORE_QUEUE_ITEM_OVERDUE) {
  258. return DEFAULT_MINUTES_BEFORE_QUEUE_ITEM_OVERDUE;
  259. }
  260. return minutesBeforeQueueItemOverdue;
  261. }
  262. public void setMinutesBeforeQueueItemOverdue(int minutesBeforeQueueItemOverdue) {
  263. this.minutesBeforeQueueItemOverdue = minutesBeforeQueueItemOverdue;
  264. }
  265. public int getMinutesBeforePendingAgentOverdue() {
  266. if (minutesBeforePendingAgentOverdue < DEFAULT_MINUTES_BEFORE_PENDING_AGENT_OVERDUE) {
  267. return DEFAULT_MINUTES_BEFORE_PENDING_AGENT_OVERDUE;
  268. }
  269. return minutesBeforePendingAgentOverdue;
  270. }
  271. public void setMinutesBeforePendingAgentOverdue(int minutesBeforePendingAgentOverdue) {
  272. this.minutesBeforePendingAgentOverdue = minutesBeforePendingAgentOverdue;
  273. }
  274. public boolean getShowLegend() {
  275. return showLegend;
  276. }
  277. public void setShowLegend(boolean showLegend) {
  278. this.showLegend = showLegend;
  279. }
  280. public int getSecondsBeforeNextRefresh() {
  281. if (secondsBeforeNextRefresh < DEFAULT_SECONDS_BEFORE_NEXT_REFRESH) {
  282. return DEFAULT_SECONDS_BEFORE_NEXT_REFRESH;
  283. }
  284. return secondsBeforeNextRefresh;
  285. }
  286. public void setSecondsBeforeNextRefresh(int secondsBeforeNextRefresh) {
  287. this.secondsBeforeNextRefresh = secondsBeforeNextRefresh;
  288. }
  289. // -------------------------------------------------------------------------------------- Basic accessors & mutators
  290. public void setAgentManager(AgentManager agentManager) {
  291. this.agentManager = agentManager;
  292. }
  293. public void setElasticInstanceManager(ElasticInstanceManager elasticInstanceManager) {
  294. this.elasticInstanceManager = elasticInstanceManager;
  295. }
  296. public void setBuildExecutionManager(BuildExecutionManager buildExecutionManager) {
  297. this.buildExecutionManager = buildExecutionManager;
  298. }
  299. public void setBuildQueueManager(BuildQueueManager buildQueueManager) {
  300. this.buildQueueManager = buildQueueManager;
  301. }
  302. public void setElasticUIBean(ElasticUIBean elasticUIBean) {
  303. this.elasticUIBean = elasticUIBean;
  304. }
  305. }