From ecd96901f4911d70f0544fdcd4f59ebc44b89581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=A4ckerlin?= Date: Fri, 22 Jan 2016 15:50:21 +0000 Subject: [PATCH] stats displayed; about to rework bash --- nodejs/public/javascripts/servicedock.js | 138 ++++++++---- nodejs/servicedock.js | 6 +- nodejs/sockets/index.js | 268 ++++++++++++----------- nodejs/views/index.ejs | 6 +- 4 files changed, 247 insertions(+), 171 deletions(-) diff --git a/nodejs/public/javascripts/servicedock.js b/nodejs/public/javascripts/servicedock.js index 5750e43..4221227 100644 --- a/nodejs/public/javascripts/servicedock.js +++ b/nodejs/public/javascripts/servicedock.js @@ -131,7 +131,7 @@ function Docker() { } function graphNode(n) { var res = ""; - var label = n.name+'\\n'+n.image.name; + var label = n.image.name+'\\n'+n.name+'\\ncpu: ????? mem: ?????'; res += '"'+n.name+'"' +' [label="'+label +'",URL="#'+n.name @@ -372,7 +372,7 @@ function Docker() { } this.contextmenu = function(selector) { $('a[xlink\\:href^=#]').click(function(e) { - name = $(this).attr("xlink:href").replace(/^#/, ""); + var name = $(this).attr("xlink:href").replace(/^#/, ""); var n = nodes[name]; $(selector).prepend('') $("#popup").empty(); @@ -403,10 +403,17 @@ function Docker() { $("#popup5").click(function() { showConsole(); emit("bash-start", name); - $("#bash").submit(function() { - emit("bash-input", {name: name, text: $("#command").val()+"\n"}); - $("#command").val(""); - }) + $("#screen").focus(); + $("#screen").keypress(function(e) { + console.log("keypress", e); + if (e.keyCode) emit("bash-input", {name: name, text: String.fromCharCode(e.keyCode)}); + else if (e.charCode) emit("bash-input", {name: name, text: String.fromCharCode(e.charCode)}); + $("#screen").focus(); + }); + // $("#bash").submit(function() { + // emit("bash-input", {name: name, text: $("#command").val()+"\n"}); + // $("#command").val(""); + // }) }); } $("#popup").append(''); @@ -528,17 +535,17 @@ function success(text) { function status(text, msg) { $("#main").hide(); $("#main").html(text); - $("#popup").hide(); if (msg) success(msg); else setTimeout("$('#status').fadeOut('slow')", 5000); zoom(0); + stats(); $("#main").show(); $("form input:first-child").focus(); docker.containers.contextmenu("#main"); } function emit(signal, data) { - console.log("<-snd "+signal); + console.log("<-snd "+signal, data); socket.emit(signal, data); } @@ -565,25 +572,6 @@ function togglemenu() { $("#menu").toggle(); } -function about(c) { - $("#imagetools").hide(); - $.ajax({url: "about.php", success: function(res) { - try { - var a = JSON.parse(res); - status("

"+a.description+"

"+ - "

"+a.project+"-"+a.version+"

"+ - "

"+a.docker+"

"+ - "

README

"+ - "
"+a.readme+"
"); - } catch(e) { - status("
"+res+"
"); - error("Exception Caught: "+e); - } - }}).fail(function() { - error("offline"); - }); -} - var zoomlevel = 0; function zoom(incr = 0) { zoomlevel = (zoomlevel+incr)%2; @@ -621,7 +609,6 @@ function rotateviz() { showviz(); } function showviz(vizpath, more) { - $("#imagetools").show(); if (!vizpath) { vizpath = viz; more = vizmore; @@ -632,6 +619,12 @@ function showviz(vizpath, more) { res = "digraph {\n"+" rankdir="+rankdir+";\n"+viz+"\n}"; try { status(more?Viz(res)+more:Viz(res)); + $('a > ellipse + text').attr('font-size', '12'); + $('a > ellipse + text + text') + .attr('font-weight', 'bold') + .attr('font-size', '16') + .each(function() {$(this).attr('y', parseFloat($(this).attr('y'))+1.0)}); + $('a > ellipse + text + text + text').attr('font-size', '12'); } catch(e) { (res = res.split("\n")).forEach(function(v, i, a) { a[i] = ("000"+(i+1)).slice(-3)+": "+v; @@ -646,8 +639,51 @@ function details(name) { showviz(docker.containers.subgraph(focused)); } +/// Convert number of bytes to readable text +function size(num) { + if (num>0.6*1024) { + if (num>0.6*1024*1024) { + if (num>0.6*1024*1024*1024) { + if (num>0.6*1024*1024*1024*1024) { + return Math.round(num/1024/1024/1024/1024)+"TB"; + } else { + return Math.round(num/1024/1024/1024)+"GB"; + } + } else { + return Math.round(num/1024/1024)+"MB"; + } + } else { + return Math.round(num/1024)+"kB"; + } + } else { + return num+"B"; + } +} + +var oldoldstats = null; +var oldstats = null; function stats(data) { - console.log("->rcv stats") + console.log("->rcv stats"); + if (!data && oldstats && oldoldstats) { + data = oldstats; + oldstats = oldoldstats; + } + if (oldstats) for (name in data) { + var s = data[name]; + var o = oldstats[name]; + if (!o|| !s) continue; + $('text:contains("'+name+'") + text + text').css('background-color: red'); + $('text:contains("'+name+'") + text + text') + .html('cpu: ' + +(Math.round((s.cpuacct.usage.data-o.cpuacct.usage.data) + /(s.cpuacct.usage.date-o.cpuacct.usage.date) + /100) + /100) + +'% mem: ' + +size(s.memory.usage_in_bytes.data)); + } + oldoldstats = oldstats; + oldstats = data; } function images(i) { @@ -658,7 +694,6 @@ function images(i) { function containers(c) { console.log("->rcv containers"); docker.containers.set(c); - showImage(); if (focused && docker.containers.exists(focused)) details(focused); else @@ -666,27 +701,30 @@ function containers(c) { } function showImage() { - $("#close").hide(); $("#logs").hide(); $("#console").hide(); + $("#close").hide(); + $("#imagetools").show(); $("#main").show(); } function showConsole() { $("#main").hide(); $("#logs").hide(); + $("#imagetools").hide(); $("#console").show(); $("#close").show(); - $("#command").focus(); - $("#command").val(""); - if ($("#screen").val()!="") $("#screen").append("\n"); + // $("#command").focus(); + // $("#command").val(""); + // if ($("#screen").val()!="") $("#screen").append("\n"); } function showLogs() { $("#main").hide(); - $("#logs").show(); $("#console").hide(); + $("#imagetools").hide(); $("#close").show(); + $("#logs").show(); } function logs(data) { @@ -777,16 +815,39 @@ function ansifilter(data) { return res.replace(/\r\r\n/g, '\n'); } +function ascii(txt) { + var res = ""; + for (i=0; ircv bash-data("+data.name+")", data); if (data.type=='done') { $("#screen").append('\nDONE'); } else { - $("#screen").append(''+ansifilter(htmlenc(data.text))+''); + var done = false; + console.log("ASCII: ", ascii(data.text)); + if (data.text.length==1) { + switch (data.text.charCodeAt(0)) { + case 7: + $('#screen').text(function (_,txt) { + return txt.slice(0, -1); + }); + done = true; + break; + } + } + if (!done) $("#screen").append(ansifilter(htmlenc(data.text))); } $("#screen").animate({ scrollTop: $("#screen").prop('scrollHeight') - }, 500);} + }, 500); + $("#screen").focus(); +} function overview() { focused = null; @@ -797,12 +858,15 @@ function overview() { /** Decide whether to login or to create a new user */ function start() { $("#imagetools").hide(); + $("#close").hide(); + $("#popup").hide(); $("#menu").hide(); $("#username").html(window.location.hostname) try { status("Starting up ..."); emit("images"); emit("containers"); + showImage(); } catch (m) { error(m); } diff --git a/nodejs/servicedock.js b/nodejs/servicedock.js index cf62673..fe2206a 100644 --- a/nodejs/servicedock.js +++ b/nodejs/servicedock.js @@ -8,7 +8,7 @@ var express = require('express') var app = module.exports = express.createServer(); var io = require('socket.io').listen(app); -var sockets = require(__dirname+'/sockets')(); +var sockets = require(__dirname+'/sockets')(io); // Configuration @@ -30,10 +30,6 @@ app.configure('production', function(){ app.use(express.errorHandler()); }); -// Sockets - -io.sockets.on('connection', sockets.connection); - // Routes app.get('/', routes.index); diff --git a/nodejs/sockets/index.js b/nodejs/sockets/index.js index 1611260..bf40ea7 100644 --- a/nodejs/sockets/index.js +++ b/nodejs/sockets/index.js @@ -1,61 +1,80 @@ -module.exports = function() { +module.exports = function(io) { + + var pty = require('pty.js'); + var proc = require('child_process'); var module={}; + var idtoname = {}; + + function broadcast(signal, data) { + console.log("<= signal: "+signal); + io.sockets.emit(signal, data); + } + + function exec(cmd, callback) { + console.log("== "+cmd); + proc.exec(cmd, callback); + } - module.connection = function(socket) { + var oldcontainer = null; + function containerinspect(error, stdout, stderr) { + if (error || stderr) + return fail("inspect docker containers failed", { + error: error, stderr: stderr, stdout: stdout + }); + JSON.parse(stdout).forEach(function(n) { + idtoname[n.Id] = n.Name.replace(/^\//, '');; + }); + if (oldcontainer && oldcontainer==stdout) return; // do not resend same containers + oldcontainer = stdout; + broadcast("containers", stdout); + } + + function containerlist(error, stdout, stderr) { + if (error || stderr) + return fail("list docker containers failed", { + error: error, stderr: stderr, stdout: stdout + }); + exec("docker inspect "+stdout.trim().replace(/\n/g, " "), containerinspect); + } - //var sys = require('sys'); - var pty = require('pty.js'); - var proc = require('child_process'); + function updatecontainers(error, stdout, stderr) { + if (error || stderr) + return fail("update docker container failed", { + error: error, stderr: stderr, stdout: stdout + }); + exec("docker ps -aq --no-trunc ", containerlist); + } - function stats(ids) { - var res = {}; - var fs = require('fs'); - var async = require('async'); - var base = "/sys/fs/cgroup/"; - var dataPoints = { - "cpuacct": [ - "stat" - ], - "memory": [ - "usage_in_bytes", - "max_usage_in_bytes" - ] - }; - async.each(ids, function(id, cb1) { - res[id] = {}; - async.forEachOf(dataPoints, function(val1, category, cb2) { - res[id][category] = {}; - async.each(val1, function(element, cb3) { - var file = base + category + '/docker/' + id + '/' + category + '.' + element; - res[id][category][element] = {}; - fs.readFile(file, 'utf8', function(err, data) { - if (err) {cb3(err); return} - data.trim().split('\n').forEach(function(v, i) { - var vals = v.split(' '); - switch (vals.length) { - case 1: - res[id][category][element] = vals[0]; - break; - case 2: - res[id][category][element][vals[0]] = vals[1]; - break; - } - }); - cb3(); - }); - }, function(err) { - cb2(); - }); - }, function(err) { - cb1(); - }); - }, function(err) { - if (err) return; - broadcast("stats", res); + var oldimage = null; + function imageinspect(error, stdout, stderr) { + if (error || stderr) + return fail("inspect docker images failed", { + error: error, stderr: stderr, stdout: stdout }); - } - + if (oldimage && oldimage==stdout) return; // do not resend same images + oldimage = stdout; + broadcast("images", stdout); + } + + function imagelist(error, stdout, stderr) { + if (error || stderr) + return fail("list docker images failed", { + error: error, stderr: stderr, stdout: stdout + }); + exec("docker inspect "+stdout.trim().replace(/\n/g, " "), imageinspect); + } + + function updateimages(error, stdout, stderr) { + if (error || stderr) + return fail("update docker images failed", { + error: error, stderr: stderr, stdout: stdout + }); + exec("docker images -q --no-trunc", imagelist); + } + + function connection(socket) { + console.log("new client"); function emit(signal, data, info) { @@ -68,80 +87,17 @@ module.exports = function() { socket.emit(signal, data); } - function broadcast(signal, data) { - console.log("<= signal: "+signal); - socket.broadcast.emit(signal, data); - } - - function exec(cmd, callback) { - console.log("== "+cmd); - proc.exec(cmd, callback); - } - function fail(txt, data) { console.log("** "+txt, data); emit("fail", txt); } - function containerinspect(error, stdout, stderr) { - if (error || stderr) - return fail("inspect docker containers failed", { - error: error, stderr: stderr, stdout: stdout - }); - emit("containers", stdout); - } - - function containerlist(error, stdout, stderr) { - if (error || stderr) - return fail("list docker containers failed", { - error: error, stderr: stderr, stdout: stdout - }); - exec("docker inspect "+stdout.trim().replace(/\n/g, " "), containerinspect); - } - - function updatecontainers(error, stdout, stderr) { - if (error || stderr) - return fail("update docker container failed", { - error: error, stderr: stderr, stdout: stdout - }); - exec("docker ps -aq --no-trunc ", containerlist); - } - function modify(cmd, name) { if (!name.match(/^[a-z0-9][-_:.+a-z0-9]*$/i)) return fail("illegal instance name"); exec("docker "+cmd+" "+name, updatecontainers); } - function containers() { - console.log("-> containers"); - updatecontainers(); - } - - function imageinspect(error, stdout, stderr) { - if (error || stderr) - return fail("inspect docker images failed", { - error: error, stderr: stderr, stdout: stdout - }); - emit("images", stdout); - } - - function imagelist(error, stdout, stderr) { - if (error || stderr) - return fail("list docker images failed", { - error: error, stderr: stderr, stdout: stdout - }); - exec("docker inspect "+stdout.trim().replace(/\n/g, " "), imageinspect); - } - - function updateimages(error, stdout, stderr) { - if (error || stderr) - return fail("update docker images failed", { - error: error, stderr: stderr, stdout: stdout - }); - exec("docker images -q --no-trunc", imagelist); - } - function modify(cmd, name) { if (!name.match(/^[a-z0-9][-_:.+a-z0-9]*$/i)) return fail("illegal instance name"); @@ -150,14 +106,16 @@ module.exports = function() { function containers() { console.log("-> containers"); - updatecontainers(); + if (oldcontainer) emit("containers", oldcontainer); + else updatecontainers(); } function images() { console.log("-> images"); - updateimages(); + if (oldimage) emit("images", oldimage); + else updateimages(); } - + function start(name) { console.log("-> start("+name+")"); modify("start", name); @@ -242,15 +200,73 @@ module.exports = function() { .on('bash-input', bash_input) .on('bash-end', bash_end); - setInterval(function() { - exec("docker ps -q --no-trunc ", function(error, stdout, stderr) { - if (error || stderr) return; // ignore - stats(stdout.trim().split('\n')); - }) - }, - 1000); - } + function stats() { + var res = {}; + var fs = require('fs'); + var async = require('async'); + var base = "/sys/fs/cgroup/"; + var dataPoints = { + "cpuacct": [ + "usage" + ], + "memory": [ + "usage_in_bytes", + "limit_in_bytes", + "max_usage_in_bytes" + ] + }; + async.forEachOf(idtoname, function(name, id, cb1) { + res[name] = {}; + async.forEachOf(dataPoints, function(val1, category, cb2) { + res[name][category] = {}; + async.each(val1, function(element, cb3) { + var file = base + category + '/docker/' + id + '/' + category + '.' + element; + fs.readFile(file, 'utf8', function(err, data) { + res[name][category][element] = { + date: (new Date()).getTime(), + data: {} + }; + if (err) {cb3(err); return} + data.trim().split('\n').forEach(function(v, i) { + var vals = v.split(' '); + switch (vals.length) { + case 1: + res[name][category][element].data = parseInt(vals[0]); + break; + case 2: + res[name][category][element].data[vals[0]] = parseInt(vals[1]); + break; + } + }); + cb3(); + }); + }, function(err) { + cb2(); + }); + }, function(err) { + cb1(); + }); + }, function(err) { + if (err) return; + broadcast("stats", res); + }); + } + + // Handle Connection + io.sockets.on('connection', connection); + + // Regular Update of Stats + setInterval(function() { + stats(); + }, 1000); + + // Regular Update of Images and Containers + setInterval(function() { + updateimages(); + updatecontainers(); + }, 10000); + return module; } diff --git a/nodejs/views/index.ejs b/nodejs/views/index.ejs index 8db0b8e..c6a69a6 100644 --- a/nodejs/views/index.ejs +++ b/nodejs/views/index.ejs @@ -48,11 +48,11 @@