1package autotest.afe; 2 3import autotest.afe.create.CreateJobViewPresenter.JobCreateListener; 4import autotest.common.CustomHistory.HistoryToken; 5import autotest.common.SimpleCallback; 6import autotest.common.Utils; 7import autotest.common.table.DataSource; 8import autotest.common.table.DataSource.DataCallback; 9import autotest.common.table.DataSource.Query; 10import autotest.common.table.DataSource.SortDirection; 11import autotest.common.table.DataTable; 12import autotest.common.table.DatetimeSegmentFilter; 13import autotest.common.table.DynamicTable; 14import autotest.common.table.DynamicTable.DynamicTableListener; 15import autotest.common.table.JSONObjectSet; 16import autotest.common.table.RpcDataSource; 17import autotest.common.table.SelectionManager; 18import autotest.common.table.SimpleFilter; 19import autotest.common.table.TableDecorator; 20import autotest.common.ui.ContextMenu; 21import autotest.common.ui.DetailView; 22import autotest.common.ui.NotifyManager; 23import autotest.common.ui.TableActionsPanel.TableActionsListener; 24 25import com.google.gwt.event.dom.client.ClickEvent; 26import com.google.gwt.event.dom.client.ClickHandler; 27import com.google.gwt.event.dom.client.KeyCodes; 28import com.google.gwt.event.dom.client.KeyPressEvent; 29import com.google.gwt.event.dom.client.KeyPressHandler; 30import com.google.gwt.event.logical.shared.ValueChangeEvent; 31import com.google.gwt.event.logical.shared.ValueChangeHandler; 32import com.google.gwt.json.client.JSONArray; 33import com.google.gwt.json.client.JSONBoolean; 34import com.google.gwt.json.client.JSONNumber; 35import com.google.gwt.json.client.JSONObject; 36import com.google.gwt.json.client.JSONString; 37import com.google.gwt.json.client.JSONValue; 38import com.google.gwt.user.client.Command; 39import com.google.gwt.user.client.ui.Button; 40import com.google.gwt.user.client.ui.CheckBox; 41import com.google.gwt.user.client.ui.TextBox; 42 43import java.util.List; 44import java.util.Map; 45import java.util.Set; 46 47public class HostDetailView extends DetailView implements DataCallback, TableActionsListener { 48 private static final String[][] HOST_JOBS_COLUMNS = { 49 {DataTable.WIDGET_COLUMN, ""}, {"type", "Type"}, {"job__id", "Job ID"}, 50 {"job_owner", "Job Owner"}, {"job_name", "Job Name"}, {"started_on", "Time started"}, 51 {"status", "Status"} 52 }; 53 public static final int JOBS_PER_PAGE = 20; 54 55 public interface HostDetailListener { 56 public void onJobSelected(int jobId); 57 } 58 59 private static class HostQueueEntryDataSource extends RpcDataSource { 60 public HostQueueEntryDataSource() { 61 super("get_host_queue_entries", "get_num_host_queue_entries"); 62 } 63 64 @Override 65 protected List<JSONObject> handleJsonResult(JSONValue result) { 66 List<JSONObject> resultArray = super.handleJsonResult(result); 67 for (JSONObject row : resultArray) { 68 // get_host_queue_entries() doesn't return type, so fill it in for consistency with 69 // get_host_queue_entries_and_special_tasks() 70 row.put("type", new JSONString("Job")); 71 } 72 return resultArray; 73 } 74 } 75 76 private static class HostJobsTable extends DynamicTable { 77 private static final DataSource normalDataSource = new HostQueueEntryDataSource(); 78 private static final DataSource dataSourceWithSpecialTasks = 79 new RpcDataSource("get_host_queue_entries_and_special_tasks", 80 "get_num_host_queue_entries_and_special_tasks"); 81 82 private SimpleFilter hostFilter = new SimpleFilter(); 83 private String hostId; 84 private String startTime; 85 private String endTime; 86 87 public HostJobsTable() { 88 super(HOST_JOBS_COLUMNS, normalDataSource); 89 addFilter(hostFilter); 90 } 91 92 public void setHostId(String hostId) { 93 this.hostId = hostId; 94 updateFilter(); 95 } 96 97 public void setStartTime(String startTime) { 98 this.startTime = startTime; 99 updateFilter(); 100 } 101 102 public void setEndTime(String endTime) { 103 this.endTime = endTime; 104 updateFilter(); 105 } 106 107 private void updateFilter() { 108 if (getDataSource() == normalDataSource) { 109 sortOnColumn("job__id", SortDirection.DESCENDING); 110 } else { 111 clearSorts(); 112 } 113 114 hostFilter.clear(); 115 hostFilter.setParameter("host", 116 new JSONNumber(Double.parseDouble(hostId))); 117 if (startTime != null && startTime != "") 118 hostFilter.setParameter("start_time", new JSONString(startTime)); 119 if (endTime != null && endTime != "") 120 hostFilter.setParameter("end_time", new JSONString(endTime)); 121 } 122 123 public void setSpecialTasksEnabled(boolean enabled) { 124 if (enabled) { 125 setDataSource(dataSourceWithSpecialTasks); 126 } else { 127 setDataSource(normalDataSource); 128 } 129 130 updateFilter(); 131 } 132 133 @Override 134 protected void preprocessRow(JSONObject row) { 135 JSONObject job = row.get("job").isObject(); 136 JSONString blank = new JSONString(""); 137 JSONString jobId = blank, owner = blank, name = blank; 138 if (job != null) { 139 int id = (int) job.get("id").isNumber().doubleValue(); 140 jobId = new JSONString(Integer.toString(id)); 141 owner = job.get("owner").isString(); 142 name = job.get("name").isString(); 143 } 144 145 row.put("job__id", jobId); 146 row.put("job_owner", owner); 147 row.put("job_name", name); 148 } 149 } 150 151 private String hostname = ""; 152 private String hostId = ""; 153 private DataSource hostDataSource = new HostDataSource(); 154 private HostJobsTable jobsTable = new HostJobsTable(); 155 private TableDecorator tableDecorator = new TableDecorator(jobsTable); 156 private HostDetailListener hostDetailListener = null; 157 private JobCreateListener jobCreateListener = null; 158 private SelectionManager selectionManager; 159 160 private JSONObject currentHostObject; 161 162 private Button lockButton = new Button(); 163 private Button reverifyButton = new Button("Reverify"); 164 private Button repairButton = new Button("Repair"); 165 private CheckBox showSpecialTasks = new CheckBox(); 166 private DatetimeSegmentFilter startedTimeFilter = new DatetimeSegmentFilter(); 167 private TextBox hostnameInput = new TextBox(); 168 private Button hostnameFetchButton = new Button("Go"); 169 private TextBox lockReasonInput = new TextBox(); 170 171 public HostDetailView(HostDetailListener hostDetailListener, 172 JobCreateListener jobCreateListener) { 173 this.hostDetailListener = hostDetailListener; 174 this.jobCreateListener = jobCreateListener; 175 } 176 177 @Override 178 public String getElementId() { 179 return "view_host"; 180 } 181 182 @Override 183 protected String getFetchControlsElementId() { 184 return "view_host_fetch_controls"; 185 } 186 187 private String getFetchByHostnameControlsElementId() { 188 return "view_host_fetch_by_hostname_controls"; 189 } 190 191 @Override 192 protected String getDataElementId() { 193 return "view_host_data"; 194 } 195 196 @Override 197 protected String getTitleElementId() { 198 return "view_host_title"; 199 } 200 201 @Override 202 protected String getNoObjectText() { 203 return "No host selected"; 204 } 205 206 @Override 207 protected String getObjectId() { 208 return hostId; 209 } 210 211 protected String getHostname() { 212 return hostname; 213 } 214 215 @Override 216 protected void setObjectId(String id) { 217 if (id.length() == 0) { 218 throw new IllegalArgumentException(); 219 } 220 this.hostId = id; 221 } 222 223 @Override 224 protected void fetchData() { 225 JSONObject params = new JSONObject(); 226 params.put("id", new JSONString(getObjectId())); 227 fetchDataCommmon(params); 228 } 229 230 private void fetchDataByHostname(String hostname) { 231 JSONObject params = new JSONObject(); 232 params.put("hostname", new JSONString(hostname)); 233 fetchDataCommmon(params); 234 } 235 236 private void fetchDataCommmon(JSONObject params) { 237 params.put("valid_only", JSONBoolean.getInstance(false)); 238 hostDataSource.query(params, this); 239 } 240 241 private void fetchByHostname(String hostname) { 242 fetchDataByHostname(hostname); 243 updateHistory(); 244 } 245 246 @Override 247 public void handleTotalResultCount(int totalCount) {} 248 249 @Override 250 public void onQueryReady(Query query) { 251 query.getPage(null, null, null, this); 252 } 253 254 public void handlePage(List<JSONObject> data) { 255 try { 256 currentHostObject = Utils.getSingleObjectFromList(data); 257 } 258 catch (IllegalArgumentException exc) { 259 NotifyManager.getInstance().showError("No such host found"); 260 resetPage(); 261 return; 262 } 263 264 setObjectId(currentHostObject.get("id").toString()); 265 266 String lockedText = Utils.jsonToString(currentHostObject.get(HostDataSource.LOCKED_TEXT)); 267 if (currentHostObject.get("locked").isBoolean().booleanValue()) { 268 String lockedBy = Utils.jsonToString(currentHostObject.get("locked_by")); 269 String lockedTime = Utils.jsonToString(currentHostObject.get("lock_time")); 270 String lockReasonText = Utils.jsonToString(currentHostObject.get("lock_reason")); 271 lockedText += ", by " + lockedBy + " on " + lockedTime; 272 lockedText += ", reason: " + lockReasonText; 273 } 274 275 showField(currentHostObject, "status", "view_host_status"); 276 showField(currentHostObject, "platform", "view_host_platform"); 277 showField(currentHostObject, HostDataSource.HOST_ACLS, "view_host_acls"); 278 showField(currentHostObject, HostDataSource.OTHER_LABELS, "view_host_labels"); 279 showText(lockedText, "view_host_locked"); 280 String shard_url = Utils.jsonToString(currentHostObject.get("shard")).trim(); 281 String host_id = Utils.jsonToString(currentHostObject.get("id")).trim(); 282 if (shard_url.equals("<null>")){ 283 shard_url = ""; 284 } else { 285 shard_url = "http://" + shard_url; 286 } 287 shard_url = shard_url + "/afe/#tab_id=view_host&object_id=" + host_id; 288 showField(currentHostObject, "shard", "view_host_shard"); 289 getElementById("view_host_shard").setAttribute("href", shard_url); 290 291 String job_id = Utils.jsonToString(currentHostObject.get("current_job")).trim(); 292 if (!job_id.equals("<null>")){ 293 String job_url = "#tab_id=view_job&object_id=" + job_id; 294 showField(currentHostObject, "current_job", "view_host_current_job"); 295 getElementById("view_host_current_job").setAttribute("href", job_url); 296 } 297 hostname = currentHostObject.get("hostname").isString().stringValue(); 298 299 String task = Utils.jsonToString(currentHostObject.get("current_special_task")).trim(); 300 if (!task.equals("<null>")){ 301 String task_url = Utils.getRetrieveLogsUrl("hosts/" + hostname + "/" + task); 302 showField(currentHostObject, "current_special_task", "view_host_current_special_task"); 303 getElementById("view_host_current_special_task").setAttribute("href", task_url); 304 } 305 306 showField(currentHostObject, "protection", "view_host_protection"); 307 String pageTitle = "Host " + hostname; 308 hostnameInput.setText(hostname); 309 hostnameInput.setWidth("240px"); 310 updateLockButton(); 311 updateLockReasonInput(); 312 displayObjectData(pageTitle); 313 314 jobsTable.setHostId(getObjectId()); 315 jobsTable.refresh(); 316 } 317 318 @Override 319 public void initialize() { 320 super.initialize(); 321 322 // Replace fetch by id with fetch by hostname 323 addWidget(hostnameInput, getFetchByHostnameControlsElementId()); 324 addWidget(hostnameFetchButton, getFetchByHostnameControlsElementId()); 325 326 hostnameInput.addKeyPressHandler(new KeyPressHandler() { 327 public void onKeyPress (KeyPressEvent event) { 328 if (event.getCharCode() == (char) KeyCodes.KEY_ENTER) 329 fetchByHostname(hostnameInput.getText()); 330 } 331 }); 332 hostnameFetchButton.addClickHandler(new ClickHandler() { 333 public void onClick(ClickEvent event) { 334 fetchByHostname(hostnameInput.getText()); 335 } 336 }); 337 338 jobsTable.setRowsPerPage(JOBS_PER_PAGE); 339 jobsTable.setClickable(true); 340 jobsTable.addListener(new DynamicTableListener() { 341 public void onRowClicked(int rowIndex, JSONObject row, boolean isRightClick) { 342 if (isJobRow(row)) { 343 JSONObject job = row.get("job").isObject(); 344 int jobId = (int) job.get("id").isNumber().doubleValue(); 345 hostDetailListener.onJobSelected(jobId); 346 } else { 347 String resultsPath = Utils.jsonToString(row.get("execution_path")); 348 Utils.openUrlInNewWindow(Utils.getRetrieveLogsUrl(resultsPath)); 349 } 350 } 351 352 public void onTableRefreshed() {} 353 }); 354 tableDecorator.addPaginators(); 355 selectionManager = tableDecorator.addSelectionManager(false); 356 jobsTable.setWidgetFactory(selectionManager); 357 tableDecorator.addTableActionsPanel(this, true); 358 tableDecorator.addControl("Show verifies, repairs, cleanups and resets", 359 showSpecialTasks); 360 tableDecorator.addFilter("Filter by time started:", 361 startedTimeFilter); 362 addWidget(tableDecorator, "view_host_jobs_table"); 363 364 showSpecialTasks.addClickHandler(new ClickHandler() { 365 public void onClick(ClickEvent event) { 366 selectionManager.deselectAll(); 367 jobsTable.setSpecialTasksEnabled(showSpecialTasks.getValue()); 368 jobsTable.refresh(); 369 } 370 }); 371 372 startedTimeFilter.addValueChangeHandler( 373 new ValueChangeHandler() { 374 public void onValueChange(ValueChangeEvent event) { 375 String value = (String) event.getValue(); 376 jobsTable.setStartTime(value); 377 if (value == "") 378 startedTimeFilter.setStartTimeToPlaceHolderValue(); 379 jobsTable.refresh(); 380 } 381 }, 382 new ValueChangeHandler() { 383 public void onValueChange(ValueChangeEvent event) { 384 String value = (String) event.getValue(); 385 jobsTable.setEndTime(value); 386 if (value == "") 387 startedTimeFilter.setEndTimeToPlaceHolderValue(); 388 jobsTable.refresh(); 389 } 390 } 391 ); 392 393 addWidget(lockButton, "view_host_lock_button"); 394 addWidget(lockReasonInput, "view_host_lock_reason_input"); 395 396 lockButton.addClickHandler(new ClickHandler() { 397 public void onClick(ClickEvent event) { 398 boolean locked = currentHostObject.get("locked").isBoolean().booleanValue(); 399 changeLock(!locked); 400 } 401 }); 402 lockReasonInput.addKeyPressHandler(new KeyPressHandler() { 403 public void onKeyPress (KeyPressEvent event) { 404 if (event.getCharCode() == (char) KeyCodes.KEY_ENTER) { 405 boolean locked = currentHostObject.get("locked").isBoolean().booleanValue(); 406 changeLock(!locked); 407 } 408 } 409 }); 410 411 reverifyButton.addClickHandler(new ClickHandler() { 412 public void onClick(ClickEvent event) { 413 JSONObject params = new JSONObject(); 414 415 params.put("id", currentHostObject.get("id")); 416 AfeUtils.callReverify(params, new SimpleCallback() { 417 public void doCallback(Object source) { 418 refresh(); 419 } 420 }, "Host " + hostname); 421 } 422 }); 423 addWidget(reverifyButton, "view_host_reverify_button"); 424 425 repairButton.addClickHandler(new ClickHandler() { 426 public void onClick(ClickEvent event) { 427 JSONObject params = new JSONObject(); 428 429 params.put("id", currentHostObject.get("id")); 430 AfeUtils.callRepair(params, new SimpleCallback() { 431 public void doCallback(Object source) { 432 refresh(); 433 } 434 }, "Host " + hostname); 435 } 436 }); 437 addWidget(repairButton, "view_host_repair_button"); 438 } 439 440 public void onError(JSONObject errorObject) { 441 // RPC handler will display error 442 } 443 444 public ContextMenu getActionMenu() { 445 ContextMenu menu = new ContextMenu(); 446 menu.addItem("Abort", new Command() { 447 public void execute() { 448 abortSelectedQueueEntriesAndSpecialTasks(); 449 } 450 }); 451 if (selectionManager.isEmpty()) 452 menu.setEnabled(false); 453 return menu; 454 } 455 456 private void abortSelectedQueueEntriesAndSpecialTasks() { 457 Set<JSONObject> selectedEntries = selectionManager.getSelectedObjects(); 458 Set<JSONObject> selectedQueueEntries = new JSONObjectSet<JSONObject>(); 459 JSONArray selectedSpecialTaskIds = new JSONArray(); 460 for (JSONObject entry : selectedEntries) { 461 if (isJobRow(entry)) 462 selectedQueueEntries.add(entry); 463 else 464 selectedSpecialTaskIds.set(selectedSpecialTaskIds.size(), 465 entry.get("oid")); 466 } 467 if (!selectedQueueEntries.isEmpty()) { 468 AfeUtils.abortHostQueueEntries(selectedQueueEntries, new SimpleCallback() { 469 public void doCallback(Object source) { 470 refresh(); 471 } 472 }); 473 } 474 if (selectedSpecialTaskIds.size() > 0) { 475 AfeUtils.abortSpecialTasks(selectedSpecialTaskIds, new SimpleCallback() { 476 public void doCallback(Object source) { 477 refresh(); 478 } 479 }); 480 } 481 } 482 483 private void updateLockButton() { 484 boolean locked = currentHostObject.get("locked").isBoolean().booleanValue(); 485 if (locked) { 486 lockButton.setText("Unlock"); 487 } else { 488 lockButton.setText("Lock"); 489 } 490 } 491 492 private void updateLockReasonInput() { 493 boolean locked = currentHostObject.get("locked").isBoolean().booleanValue(); 494 if (locked) { 495 lockReasonInput.setText(""); 496 lockReasonInput.setEnabled(false); 497 } else { 498 lockReasonInput.setEnabled(true); 499 } 500 } 501 502 private void changeLock(final boolean lock) { 503 JSONArray hostIds = new JSONArray(); 504 hostIds.set(0, currentHostObject.get("id")); 505 506 AfeUtils.changeHostLocks(hostIds, lock, lockReasonInput.getText(), 507 "Host " + hostname, new SimpleCallback() { 508 public void doCallback(Object source) { 509 refresh(); 510 } 511 }); 512 } 513 514 private boolean isJobRow(JSONObject row) { 515 String type = Utils.jsonToString(row.get("type")); 516 return type.equals("Job"); 517 } 518 519 public boolean isRowSelectable(JSONObject row) { 520 return isJobRow(row); 521 } 522 523 @Override 524 public void handleHistoryArguments(Map<String, String> arguments) { 525 String hostname = arguments.get("hostname"); 526 String objectId = arguments.get("object_id"); 527 528 if (objectId != null) { 529 try { 530 updateObjectId(objectId); 531 } 532 catch (IllegalArgumentException exc) { 533 return; 534 } 535 } else if (hostname != null) { 536 fetchDataByHostname(hostname); 537 } else { 538 resetPage(); 539 } 540 } 541} 542