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