xref: /aosp_15_r20/external/autotest/frontend/client/src/autotest/afe/HostDetailView.java (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
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 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 
HostDetailView(HostDetailListener hostDetailListener, JobCreateListener jobCreateListener)171     public HostDetailView(HostDetailListener hostDetailListener,
172                           JobCreateListener jobCreateListener) {
173         this.hostDetailListener = hostDetailListener;
174         this.jobCreateListener = jobCreateListener;
175     }
176 
177     @Override
getElementId()178     public String getElementId() {
179         return "view_host";
180     }
181 
182     @Override
getFetchControlsElementId()183     protected String getFetchControlsElementId() {
184         return "view_host_fetch_controls";
185     }
186 
getFetchByHostnameControlsElementId()187     private String getFetchByHostnameControlsElementId() {
188         return "view_host_fetch_by_hostname_controls";
189     }
190 
191     @Override
getDataElementId()192     protected String getDataElementId() {
193         return "view_host_data";
194     }
195 
196     @Override
getTitleElementId()197     protected String getTitleElementId() {
198         return "view_host_title";
199     }
200 
201     @Override
getNoObjectText()202     protected String getNoObjectText() {
203         return "No host selected";
204     }
205 
206     @Override
getObjectId()207     protected String getObjectId() {
208         return hostId;
209     }
210 
getHostname()211     protected String getHostname() {
212         return hostname;
213     }
214 
215     @Override
setObjectId(String id)216     protected void setObjectId(String id) {
217         if (id.length() == 0) {
218             throw new IllegalArgumentException();
219         }
220         this.hostId = id;
221     }
222 
223     @Override
fetchData()224     protected void fetchData() {
225         JSONObject params = new JSONObject();
226         params.put("id", new JSONString(getObjectId()));
227         fetchDataCommmon(params);
228     }
229 
fetchDataByHostname(String hostname)230     private void fetchDataByHostname(String hostname) {
231         JSONObject params = new JSONObject();
232         params.put("hostname", new JSONString(hostname));
233         fetchDataCommmon(params);
234     }
235 
fetchDataCommmon(JSONObject params)236     private void fetchDataCommmon(JSONObject params) {
237         params.put("valid_only", JSONBoolean.getInstance(false));
238         hostDataSource.query(params, this);
239     }
240 
fetchByHostname(String hostname)241     private void fetchByHostname(String hostname) {
242         fetchDataByHostname(hostname);
243         updateHistory();
244     }
245 
246     @Override
handleTotalResultCount(int totalCount)247     public void handleTotalResultCount(int totalCount) {}
248 
249     @Override
onQueryReady(Query query)250     public void onQueryReady(Query query) {
251         query.getPage(null, null, null, this);
252     }
253 
handlePage(List<JSONObject> data)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
initialize()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 
onError(JSONObject errorObject)440     public void onError(JSONObject errorObject) {
441         // RPC handler will display error
442     }
443 
getActionMenu()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 
abortSelectedQueueEntriesAndSpecialTasks()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 
updateLockButton()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 
updateLockReasonInput()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 
changeLock(final boolean lock)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 
isJobRow(JSONObject row)514     private boolean isJobRow(JSONObject row) {
515         String type = Utils.jsonToString(row.get("type"));
516         return type.equals("Job");
517     }
518 
isRowSelectable(JSONObject row)519     public boolean isRowSelectable(JSONObject row) {
520         return isJobRow(row);
521     }
522 
523     @Override
handleHistoryArguments(Map<String, String> arguments)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