18947: Remove errant uses of runsu.sh.
[arvados.git] / apps / workbench / app / assets / javascripts / log_viewer.js
index c9e871bfc145cffb34c009ffe65f3634da97fbc1..b201ed7f105229488763c64d49c2761eb17312a0 100644 (file)
@@ -1,10 +1,25 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+function newTaskState() {
+    return {"complete_count": 0,
+            "failure_count": 0,
+            "task_count": 0,
+            "incomplete_count": 0,
+            "nodes": []};
+}
+
 function addToLogViewer(logViewer, lines, taskState) {
     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+)? (.*)/;
+
+    var items = [];
+    var count = logViewer.items.length;
     for (var a in lines) {
         var v = lines[a].match(re);
         if (v != null) {
 
-            var ts = new Date(Date.UTC(v[2], v[3], v[4], v[6], v[7], v[8]));
+            var ts = new Date(Date.UTC(v[2], v[3]-1, v[4], v[6], v[7], v[8]));
 
             v11 = v[11];
             if (typeof v[11] === 'undefined') {
@@ -16,9 +31,11 @@ function addToLogViewer(logViewer, lines, taskState) {
             var message = v[12];
             var type = "";
             var node = "";
+            var slot = "";
             if (v11 !== "") {
                 if (!taskState.hasOwnProperty(v11)) {
                     taskState[v11] = {};
+                    taskState.task_count += 1;
                 }
 
                 if (/^stderr /.test(message)) {
@@ -29,28 +46,34 @@ function addToLogViewer(logViewer, lines, taskState) {
                     } else if (/^srun: /.test(message) || /^slurmd/.test(message)) {
                         type = "task-dispatch";
                     } else {
-                        type = "task-output";
+                        type = "task-print";
                     }
                 } else {
-                    if (/^success /.test(message)) {
+                    var m;
+                    if (m = /^success in (\d+) second/.exec(message)) {
                         taskState[v11].outcome = "success";
-                        taskState.success_count += 1;
+                        taskState[v11].runtime = Number(m[1]);
+                        taskState.complete_count += 1;
                     }
-                    else if (/^failure /.test(message)) {
+                    else if (m = /^failure \(\#\d+, (temporary|permanent)\) after (\d+) second/.exec(message)) {
                         taskState[v11].outcome = "failure";
+                        taskState[v11].runtime = Number(m[2]);
                         taskState.failure_count += 1;
+                        if (m[1] == "permanent") {
+                            taskState.incomplete_count += 1;
+                        }
                     }
-                    else {
-                        var child = message.match(/^child \d+ started on (.*)/);
-                        if (child != null) {
-                            taskState[v11].node = child[1];
-                            for (var i in logViewer.items) {
-                                if (i > 0) {
-                                    var val = logViewer.items[i].values();
-                                    if (val.taskid === v11) {
-                                        val.node = child[1];
-                                        logViewer.items[i].values(val);
-                                    }
+                    else if (m = /^child \d+ started on ([^.]*)\.(\d+)/.exec(message)) {
+                        taskState[v11].node = m[1];
+                        taskState[v11].slot = m[2];
+                        if (taskState.nodes.indexOf(m[1], 0) == -1) {
+                            taskState.nodes.push(m[1]);
+                        }
+                        for (var i in items) {
+                            if (i > 0) {
+                                if (items[i].taskid === v11) {
+                                    items[i].node = m[1];
+                                    items[i].slot = m[2];
                                 }
                             }
                         }
@@ -58,30 +81,27 @@ function addToLogViewer(logViewer, lines, taskState) {
                     type = "task-dispatch";
                 }
                 node = taskState[v11].node;
+                slot = taskState[v11].slot;
             } else {
-                if (/^status: /.test(message)) {
-                    type = "job-status";
-                    message = message.substr(8);
-                } else {
-                    type = "crunch";
-                }
+                type = "crunch";
             }
 
-            logViewer.add({
-                id: logViewer.items.length,
+            items.push({
+                id: count,
                 ts: ts,
                 timestamp: ts.toLocaleDateString() + " " + ts.toLocaleTimeString(),
                 taskid: v11,
                 node: node,
-                message: message,
+                slot: slot,
+                message: message.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'),
                 type: type
             });
-
+            count += 1;
         } else {
-            console.log("Did not parse: " + lines[a]);
+            console.log("Did not parse line " + a + ": " + lines[a]);
         }
     }
-    logViewer.update();
+    logViewer.add(items);
 }
 
 function sortById(a, b, opt) {
@@ -140,6 +160,15 @@ function sortByNode(a, b, opt) {
         }
     }
 
+    if (aa["slot"] !== "" && bb["slot"] !== "") {
+        if (aa["slot"] > bb["slot"]) {
+            return 1;
+        }
+        if (aa["slot"] < bb["slot"]) {
+            return -1;
+        }
+    }
+
     return sortById(a, b, opt);
 }
 
@@ -149,22 +178,18 @@ function dumbPluralize(n, s, p) {
         p = "s";
     }
     if (n == 0 || n > 1) {
-        return (s + p);
+        return n + " " + (s + p);
     } else {
-        return s;
+        return n + " " + s;
     }
 }
 
 function generateJobOverview(id, logViewer, taskState) {
     var html = "";
 
-    var first = logViewer.items[1];
-    var last = logViewer.items[logViewer.items.length-1];
-
-    {
-        html += "<div>";
-        html += "Started at " + first.values().timestamp;
-
+    if (logViewer.items.length > 2) {
+        var first = logViewer.items[1];
+        var last = logViewer.items[logViewer.items.length-1];
         var duration = (last.values().ts.getTime() - first.values().ts.getTime()) / 1000;
 
         var hours = 0;
@@ -181,24 +206,81 @@ function generateJobOverview(id, logViewer, taskState) {
         }
         seconds = duration;
 
-        var tcount = taskState.success_count + taskState.failure_count;
+        var tcount = taskState.task_count;
 
-        html += ".  " + tcount + dumbPluralize(tcount, " task") + " completed in ";
+        html += "<p>";
+        html += "Started at " + first.values().timestamp + ".  ";
+        html += "Ran " + dumbPluralize(tcount, " task") + " over ";
         if (hours > 0) {
-            html += hours + dumbPluralize(hours, " hour");
+            html += dumbPluralize(hours, " hour");
         }
         if (minutes > 0) {
-            html += " " + minutes + dumbPluralize(minutes, " minute");
+            html += " " + dumbPluralize(minutes, " minute");
         }
         if (seconds > 0) {
-            html += " " + seconds + dumbPluralize(seconds, " second");
+            html += " " + dumbPluralize(seconds, " second");
         }
-        html += ".  " + taskState.success_count + dumbPluralize(taskState.success_count, " success", "es");
-        html += ", " + taskState.failure_count + dumbPluralize(taskState.failure_count, " failure");
 
-        html += ".  Completed at " + last.values().timestamp;
-        html += "</div>";
+        html += " using " + dumbPluralize(taskState.nodes.length, " node");
+
+        html += ".  " + dumbPluralize(taskState.complete_count, "task") + " completed";
+        html += ",  " + dumbPluralize(taskState.incomplete_count, "task") +  " incomplete";
+        html += " (" + dumbPluralize(taskState.failure_count, " failure") + ")";
+
+        html += ".  Finished at " + last.values().timestamp + ".";
+        html += "</p>";
+    } else {
+       html = "<p>Job log is empty or failed to load.</p>";
     }
 
     $(id).html(html);
-}
\ No newline at end of file
+}
+
+function gotoPage(n, logViewer, page, id) {
+    if (n < 0) { return; }
+    if (n*page > logViewer.matchingItems.length) { return; }
+    logViewer.page_offset = n;
+    logViewer.show(n*page, page);
+}
+
+function updatePaging(id, logViewer, page) {
+    var p = "";
+    var i = logViewer.matchingItems.length;
+    var n;
+    for (n = 0; (n*page) < i; n += 1) {
+        if (n == logViewer.page_offset) {
+            p += "<span class='log-viewer-page-num'>" + (n+1) + "</span> ";
+        } else {
+            p += "<a href=\"#\" class='log-viewer-page-num log-viewer-page-" + n + "'>" + (n+1) + "</a> ";
+        }
+    }
+    $(id).html(p);
+    for (n = 0; (n*page) < i; n += 1) {
+        (function(n) {
+            $(".log-viewer-page-" + n).on("click", function() {
+                gotoPage(n, logViewer, page, id);
+                return false;
+            });
+        })(n);
+    }
+
+    if (logViewer.page_offset == 0) {
+        $(".log-viewer-page-up").addClass("text-muted");
+    } else {
+        $(".log-viewer-page-up").removeClass("text-muted");
+    }
+
+    if (logViewer.page_offset == (n-1)) {
+        $(".log-viewer-page-down").addClass("text-muted");
+    } else {
+        $(".log-viewer-page-down").removeClass("text-muted");
+    }
+}
+
+function nextPage(logViewer, page, id) {
+    gotoPage(logViewer.page_offset+1, logViewer, page, id);
+}
+
+function prevPage(logViewer, page, id) {
+    gotoPage(logViewer.page_offset-1, logViewer, page, id);
+}