2883: Distinguish between complete/incomplete and success/failure.
[arvados.git] / apps / workbench / app / assets / javascripts / log_viewer.js
1 function newTaskState() {
2     return {"complete_count": 0,
3             "failure_count": 0,
4             "task_count": 0,
5             "incomplete_count": 0,
6             "nodes": []};
7 }
8
9 function addToLogViewer(logViewer, lines, taskState) {
10     var re = /((\d\d\d\d)-(\d\d)-(\d\d))_((\d\d):(\d\d):(\d\d)) ([a-z0-9]{5}-[a-z0-9]{5}-[a-z0-9]{15}) (\d+) (\d+)? (.*)/;
11     for (var a in lines) {
12         var v = lines[a].match(re);
13         if (v != null) {
14
15             var ts = new Date(Date.UTC(v[2], v[3], v[4], v[6], v[7], v[8]));
16
17             v11 = v[11];
18             if (typeof v[11] === 'undefined') {
19                 v11 = "";
20             } else {
21                 v11 = Number(v11);
22             }
23
24             var message = v[12];
25             var type = "";
26             var node = "";
27             var slot = "";
28             if (v11 !== "") {
29                 if (!taskState.hasOwnProperty(v11)) {
30                     taskState[v11] = {};
31                     taskState.task_count += 1;
32                 }
33
34                 if (/^stderr /.test(message)) {
35                     message = message.substr(7);
36                     if (/^crunchstat: /.test(message)) {
37                         type = "crunchstat";
38                         message = message.substr(12);
39                     } else if (/^srun: /.test(message) || /^slurmd/.test(message)) {
40                         type = "task-dispatch";
41                     } else {
42                         type = "task-output";
43                     }
44                 } else {
45                     var m;
46                     if (m = /^success in (\d+) second/.exec(message)) {
47                         taskState[v11].outcome = "success";
48                         taskState[v11].runtime = Number(m[1]);
49                         taskState.complete_count += 1;
50                         console.log(taskState[v11].runtime);
51                     }
52                     else if (m = /^failure \(\#\d+, (temporary|permanent)\) after (\d+) second/.exec(message)) {
53                         taskState[v11].outcome = "failure";
54                         taskState[v11].runtime = Number(m[2]);
55                         taskState.failure_count += 1;
56                         if (m[1] == "permanent") {
57                             taskState.incomplete_count += 1;
58                         }
59                         console.log(taskState[v11].runtime);
60                     }
61                     else if (m = /^child \d+ started on ([^.]*)\.(\d+)/.exec(message)) {
62                         taskState[v11].node = m[1];
63                         taskState[v11].slot = m[2];
64                         if (taskState.nodes.indexOf(m[1], 0) == -1) {
65                             taskState.nodes.push(m[1]);
66                         }
67                         for (var i in logViewer.items) {
68                             if (i > 0) {
69                                 var val = logViewer.items[i].values();
70                                 if (val.taskid === v11) {
71                                     val.node = m[1];
72                                     val.slot = m[2];
73                                     logViewer.items[i].values(val);
74                                 }
75                             }
76                         }
77                     }
78                     type = "task-dispatch";
79                 }
80                 node = taskState[v11].node;
81                 slot = taskState[v11].slot;
82             } else {
83                 if (/^status: /.test(message)) {
84                     type = "job-status";
85                     message = message.substr(8);
86                 } else {
87                     type = "crunch";
88                 }
89             }
90
91             logViewer.add({
92                 id: logViewer.items.length,
93                 ts: ts,
94                 timestamp: ts.toLocaleDateString() + " " + ts.toLocaleTimeString(),
95                 taskid: v11,
96                 node: node,
97                 slot: slot,
98                 message: message,
99                 type: type
100             });
101
102         } else {
103             console.log("Did not parse: " + lines[a]);
104         }
105     }
106     logViewer.update();
107 }
108
109 function sortById(a, b, opt) {
110     a = a.values();
111     b = b.values();
112
113     if (a["id"] > b["id"]) {
114         return 1;
115     }
116     if (a["id"] < b["id"]) {
117         return -1;
118     }
119     return 0;
120 }
121
122 function sortByTask(a, b, opt) {
123     var aa = a.values();
124     var bb = b.values();
125
126     if (aa["taskid"] === "" && bb["taskid"] !== "") {
127         return -1;
128     }
129     if (aa["taskid"] !== "" && bb["taskid"] === "") {
130         return 1;
131     }
132
133     if (aa["taskid"] !== "" && bb["taskid"] !== "") {
134         if (aa["taskid"] > bb["taskid"]) {
135             return 1;
136         }
137         if (aa["taskid"] < bb["taskid"]) {
138             return -1;
139         }
140     }
141
142     return sortById(a, b, opt);
143 }
144
145 function sortByNode(a, b, opt) {
146     var aa = a.values();
147     var bb = b.values();
148
149     if (aa["node"] === "" && bb["node"] !== "") {
150         return -1;
151     }
152     if (aa["node"] !== "" && bb["node"] === "") {
153         return 1;
154     }
155
156     if (aa["node"] !== "" && bb["node"] !== "") {
157         if (aa["node"] > bb["node"]) {
158             return 1;
159         }
160         if (aa["node"] < bb["node"]) {
161             return -1;
162         }
163     }
164
165     if (aa["slot"] !== "" && bb["slot"] !== "") {
166         if (aa["slot"] > bb["slot"]) {
167             return 1;
168         }
169         if (aa["slot"] < bb["slot"]) {
170             return -1;
171         }
172     }
173
174     return sortById(a, b, opt);
175 }
176
177
178 function dumbPluralize(n, s, p) {
179     if (typeof p === 'undefined') {
180         p = "s";
181     }
182     if (n == 0 || n > 1) {
183         return n + " " + (s + p);
184     } else {
185         return n + " " + s;
186     }
187 }
188
189 function generateJobOverview(id, logViewer, taskState) {
190     var html = "";
191
192     var first = logViewer.items[1];
193     var last = logViewer.items[logViewer.items.length-1];
194
195     {
196         html += "<div>";
197         html += "Started at " + first.values().timestamp;
198
199         var duration = (last.values().ts.getTime() - first.values().ts.getTime()) / 1000;
200
201         var hours = 0;
202         var minutes = 0;
203         var seconds;
204
205         if (duration >= 3600) {
206             hours = Math.floor(duration / 3600);
207             duration -= (hours * 3600);
208         }
209         if (duration >= 60) {
210             minutes = Math.floor(duration / 60);
211             duration -= (minutes * 60);
212         }
213         seconds = duration;
214
215         var tcount = taskState.task_count;
216
217         html += ".  " + dumbPluralize(tcount, " task") + " run over ";
218         if (hours > 0) {
219             html += dumbPluralize(hours, " hour");
220         }
221         if (minutes > 0) {
222             html += " " + dumbPluralize(minutes, " minute");
223         }
224         if (seconds > 0) {
225             html += " " + dumbPluralize(seconds, " second");
226         }
227
228         html += " using " + dumbPluralize(taskState.nodes.length, " node");
229
230         html += ".  " + dumbPluralize(taskState.complete_count, "task") + " complete";
231         html += ",  " + dumbPluralize(taskState.incomplete_count, "task") +  " incomplete";
232         html += " (" + dumbPluralize(taskState.failure_count, " failure") + ")";
233
234         html += ".  Finished at " + last.values().timestamp;
235         html += "</div>";
236     }
237
238     $(id).html(html);
239 }