|
|
|
@ -9,66 +9,219 @@ |
|
|
|
|
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
|
|
|
|
|
|
|
|
|
var socket = io.connect(); |
|
|
|
|
var focused = null; |
|
|
|
|
|
|
|
|
|
function DockerContainers() { |
|
|
|
|
var Status = Object.freeze({ |
|
|
|
|
Error: "red", |
|
|
|
|
Terminated: "yellow", |
|
|
|
|
Restarting: "lightblue", |
|
|
|
|
Paused: "lightgrey", |
|
|
|
|
Running: "lightgreen" |
|
|
|
|
Error: {color: "red", action1: "start", action2: "remove"}, |
|
|
|
|
Terminated: {color: "yellow", action1: "start", action2: "remove"}, |
|
|
|
|
Restarting: {color: "lightblue", action1: "start", action2: "remove"}, |
|
|
|
|
Paused: {color: "lightgrey", action1: "unpause", action2: null}, |
|
|
|
|
Running: {color: "lightgreen", action1: "pause", action2: "stop"} |
|
|
|
|
}); |
|
|
|
|
var containers = []; |
|
|
|
|
var nodes = []; |
|
|
|
|
this.graph = function() { |
|
|
|
|
var res = ""; |
|
|
|
|
console.log("nodes["+nodes.length+"]=", nodes); |
|
|
|
|
for (name in nodes) { |
|
|
|
|
function protocol(port) { |
|
|
|
|
if (port.toString().match("443")) return "https://"; |
|
|
|
|
if (port.toString().match("3304")) return "mysql://"; |
|
|
|
|
if (port.toString().match("22")) return "ssh://"; |
|
|
|
|
return "http://"; |
|
|
|
|
} |
|
|
|
|
this.exists = function(name) { |
|
|
|
|
if (nodes[name]) return true; |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
this.contextmenu = function(selector) { |
|
|
|
|
$('a[xlink\\:href^=#]').click(function(e) { |
|
|
|
|
name = $(this).attr("xlink:href").replace(/^#/, ""); |
|
|
|
|
var n = nodes[name]; |
|
|
|
|
var label = n.name+'\\n'+n.image; |
|
|
|
|
res += '"'+n.name+'"' |
|
|
|
|
+' [label="'+label |
|
|
|
|
+'",URL="details('+"'"+n.name+"'" |
|
|
|
|
+')",style=filled,fillcolor='+n.status+"];\n"; |
|
|
|
|
$(selector).prepend('<div id="popup"></div>') |
|
|
|
|
$("#popup").empty(); |
|
|
|
|
if (n.status.action1) { |
|
|
|
|
$("#popup").append('<button id="popup1">'+n.status.action1+'</button>'); |
|
|
|
|
$("#popup1").click(function() { |
|
|
|
|
socket.emit(n.status.action1, name); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
$("#popup").append('<button id="popup2">'+(focused?"overview":"focus")+'</button>'); |
|
|
|
|
$("#popup2").click(function() { |
|
|
|
|
if (focused) overview(); else details(name); |
|
|
|
|
}); |
|
|
|
|
if (n.status.action2) { |
|
|
|
|
$("#popup").append('<button id="popup3">'+n.status.action2+'</button>'); |
|
|
|
|
$("#popup3").click(function() { |
|
|
|
|
socket.emit(n.status.action2, name); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
$("#popup").append('<br/>'); |
|
|
|
|
$("#popup").append('<button id="popup4">download</button>'); |
|
|
|
|
$("#popup").css("position", "fixed"); |
|
|
|
|
$("#popup").css("top", e.pageY-$("#popup").height()/4); |
|
|
|
|
$("#popup").css("left", e.pageX-$("#popup").width()/2); |
|
|
|
|
$("#popup").mouseleave(function() { |
|
|
|
|
$("#popup").hide(); |
|
|
|
|
}).click(function() { |
|
|
|
|
$("#popup").hide(); |
|
|
|
|
}); |
|
|
|
|
$("#popup").show(); |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
this.details = function(name) { |
|
|
|
|
var res = ` |
|
|
|
|
<div id="tabs"> |
|
|
|
|
<ul> |
|
|
|
|
<li><a href="#tabs-1">Overview</a></li> |
|
|
|
|
<li><a href="#tabs-2">Logs</a></li> |
|
|
|
|
<li><a href="#tabs-3">Dump</a></li> |
|
|
|
|
</ul> |
|
|
|
|
<div id="tabs-1"> |
|
|
|
|
<table class="details docker"> |
|
|
|
|
<thead> |
|
|
|
|
<tr> |
|
|
|
|
<th>Name</th> |
|
|
|
|
<th>Ports</th> |
|
|
|
|
<th>Volumes</th> |
|
|
|
|
<th>Links</th> |
|
|
|
|
<th>Environments</th> |
|
|
|
|
<th>Image</th> |
|
|
|
|
<th>Command</th> |
|
|
|
|
</tr> |
|
|
|
|
</thead> |
|
|
|
|
<tbody> |
|
|
|
|
`;
|
|
|
|
|
var n = nodes[name]; |
|
|
|
|
res += ` |
|
|
|
|
</tbody> |
|
|
|
|
</table> |
|
|
|
|
</div> |
|
|
|
|
<div id="tabs-2"> |
|
|
|
|
</div> |
|
|
|
|
<div id="tabs-3"> |
|
|
|
|
<pre>`;
|
|
|
|
|
res += JSON.stringify(containers[nodes[name].id], null, 4); |
|
|
|
|
res += ` |
|
|
|
|
</pre> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
<script> |
|
|
|
|
$(function() {$("#tabs").tabs();}); |
|
|
|
|
</script> |
|
|
|
|
`;
|
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
function getIps(n, ips) { |
|
|
|
|
n.ports.forEach(function(p) { |
|
|
|
|
if (!ips[p.ip]) ips[p.ip] = []; |
|
|
|
|
ips[p.ip].push(p); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
function graphIpClusters(ips) { |
|
|
|
|
var res = "newrank=true;\n"; |
|
|
|
|
var i = 0; |
|
|
|
|
for (ip in ips) { |
|
|
|
|
res += "subgraph clusterIp"+(++i)+' {\nlabel="'+ip+'";\n'; |
|
|
|
|
ips[ip].forEach(function(p) { |
|
|
|
|
res += '"'+p.ip+":"+p.external |
|
|
|
|
+'" [label="'+p.external+'",URL="' |
|
|
|
|
+protocol(p.internal)+p.ip+':'+p.external+'",shape=box];\n'; |
|
|
|
|
}); |
|
|
|
|
res+="}\n"; |
|
|
|
|
} |
|
|
|
|
res += "{rank=same;\n"; |
|
|
|
|
for (name in nodes) { |
|
|
|
|
var n = nodes[name]; |
|
|
|
|
n.volumes.forEach(function(v) { |
|
|
|
|
res += '"'+v.id+'" [label="'+v.inside+'",shape=box];\n'; |
|
|
|
|
for (ip in ips) { |
|
|
|
|
ips[ip].forEach(function(p) { |
|
|
|
|
res += '"'+p.ip+":"+p.external+'";\n'; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
res+="}\n"; |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
function graphNode(n) { |
|
|
|
|
var res = ""; |
|
|
|
|
var label = n.name+'\\n'+n.image; |
|
|
|
|
res += '"'+n.name+'"' |
|
|
|
|
+' [label="'+label |
|
|
|
|
+'",URL="#'+n.name |
|
|
|
|
+'",style=filled,fillcolor='+n.status.color+"];\n"; |
|
|
|
|
n.ports.forEach(function(p) { |
|
|
|
|
res += '"'+(p.ip?p.ip+":":"")+p.external+'" -> "'+n.name |
|
|
|
|
+'" [label="'+p.internal+'"];\n'; |
|
|
|
|
}); |
|
|
|
|
n.links.forEach(function(l) { |
|
|
|
|
res += '"'+n.name+'" -> "'+l.to+'" [label="link: '+l.link+'"];\n' |
|
|
|
|
}); |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
function graphVolumesInside(n) { |
|
|
|
|
var res = ""; |
|
|
|
|
n.volumes.forEach(function(v) { |
|
|
|
|
res += '"'+v.id+'" [label="'+v.inside+'",shape=box];\n'; |
|
|
|
|
}); |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
function graphVolumesOutside(n) { |
|
|
|
|
var res = ""; |
|
|
|
|
n.volumes.forEach(function(v) { |
|
|
|
|
if (v.host) |
|
|
|
|
res += '"'+v.outside+'" [label="'+v.host+'",shape=box];\n'; |
|
|
|
|
}); |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
function graphVolumesConnections(n) { |
|
|
|
|
var res = ""; |
|
|
|
|
n.volumes.forEach(function(v) { |
|
|
|
|
if (v.host) |
|
|
|
|
res += '"'+v.id+'" -> "'+v.outside+'" [label="mounted from"]\n'; |
|
|
|
|
res += '"'+n.name+'" -> "'+v.id+'" [label="volume/'+v.rw+'"]\n'; |
|
|
|
|
}); |
|
|
|
|
n.volumesfrom.forEach(function(o) { |
|
|
|
|
res += '"'+n.name+'" -> "'+nodes[o].name+'" [label="volumes from"]\n'; |
|
|
|
|
}); |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
this.graph = function(n) { |
|
|
|
|
var res = ""; |
|
|
|
|
var ips = []; |
|
|
|
|
n = n || nodes; |
|
|
|
|
for (name in n) getIps(n[name], ips); |
|
|
|
|
res += graphIpClusters(ips); |
|
|
|
|
for (name in n) res += graphNode(n[name]);
|
|
|
|
|
res += "{rank=same;\n"; |
|
|
|
|
for (name in nodes) { |
|
|
|
|
var n = nodes[name]; |
|
|
|
|
n.volumes.forEach(function(v) { |
|
|
|
|
if (v.host) |
|
|
|
|
res += '"'+v.outside+'" [label="'+v.host+'",shape=box];\n'; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
for (name in n) res += graphVolumesInside(n[name]); |
|
|
|
|
res+="}\n"; |
|
|
|
|
for (name in nodes) { |
|
|
|
|
var n = nodes[name]; |
|
|
|
|
n.volumes.forEach(function(v) { |
|
|
|
|
if (v.host) |
|
|
|
|
res += '"'+v.id+'" -> "'+v.outside+'"\n'; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
for (name in nodes) { |
|
|
|
|
var n = nodes[name]; |
|
|
|
|
n.volumes.forEach(function(v) { |
|
|
|
|
res += '"'+n.name+'" -> "'+v.id+'"\n'; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
res += "{rank=same;\n"; |
|
|
|
|
for (name in n) res += graphVolumesOutside(n[name]); |
|
|
|
|
res+="}\n"; |
|
|
|
|
for (name in n) res += graphVolumesConnections(n[name]); |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
function addNodes(ns, name) { |
|
|
|
|
var n = nodes[name]; |
|
|
|
|
ns[name] = n; |
|
|
|
|
n.links.forEach(function(peer) { |
|
|
|
|
if (!ns[peer.to]) addNodes(ns, peer.to); |
|
|
|
|
}); |
|
|
|
|
n.usedby.forEach(function(peer) { |
|
|
|
|
if (!ns[peer]) addNodes(ns, peer); |
|
|
|
|
}); |
|
|
|
|
n.volumesfrom.forEach(function(peer) { |
|
|
|
|
if (!ns[peer]) addNodes(ns, peer); |
|
|
|
|
}); |
|
|
|
|
n.volumesto.forEach(function(peer) { |
|
|
|
|
if (!ns[peer]) addNodes(ns, peer); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
this.subgraph = function(name) { |
|
|
|
|
var ns = []; |
|
|
|
|
addNodes(ns, name); |
|
|
|
|
return this.graph(ns); |
|
|
|
|
} |
|
|
|
|
function setup() { |
|
|
|
|
delete nodes; nodes=[]; |
|
|
|
|
containers.forEach(function(c) { |
|
|
|
|
delete nodes; nodes = []; |
|
|
|
|
containers.forEach(function(c, i) { |
|
|
|
|
var name = c.Name.replace(/^\//, ""); |
|
|
|
|
nodes[name] = {}; |
|
|
|
|
console.log("container: "+name); |
|
|
|
|
if (!nodes[name]) nodes[name] = {}; |
|
|
|
|
nodes[name].id = i; |
|
|
|
|
nodes[name].name = name; |
|
|
|
|
nodes[name].image = c.Config.Image; |
|
|
|
|
nodes[name].ports = []; |
|
|
|
@ -78,43 +231,80 @@ function DockerContainers() { |
|
|
|
|
if (ports[port]) |
|
|
|
|
for (var expose in ports[port]) { |
|
|
|
|
var ip = ports[port][expose].HostIp; |
|
|
|
|
if (ip==""||ip=="127.0.0.1"||ip=="0.0.0.0") ip=null; |
|
|
|
|
if (!ip||ip==""||ip=="0.0.0.0"||ip==0) ip=window.location.hostname; |
|
|
|
|
nodes[name].ports.push({ |
|
|
|
|
internal: port, |
|
|
|
|
external: ports[port][expose].HostPort, |
|
|
|
|
ip: ip |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
if (c.State.Running) nodes[name].status = Status.Running; |
|
|
|
|
else if (c.State.Paused) nodes[name].status = Status.Paused; |
|
|
|
|
if (c.State.Paused) nodes[name].status = Status.Paused; |
|
|
|
|
else if (c.State.Running) nodes[name].status = Status.Running; |
|
|
|
|
else if (c.State.Restarting) nodes[name].status = Status.Restarting; |
|
|
|
|
else if (c.State.ExitCode == 0) nodes[name].status = Status.Terminated; |
|
|
|
|
else nodes[name].status = Status.Error; |
|
|
|
|
console.log("STATUS", name, c.State, nodes[name].status); |
|
|
|
|
nodes[name].volumes = []; |
|
|
|
|
var volumes = c.Volumes || c.Config.Volumes; |
|
|
|
|
nodes[name].volumes = []; |
|
|
|
|
if (volumes) |
|
|
|
|
for (var volume in volumes) { |
|
|
|
|
var rw = "rw"; |
|
|
|
|
var outside = (typeof volumes[volume]=="string")?volumes[volume]:null; |
|
|
|
|
if (c.Mounts) c.Mounts.forEach(function(mnt) { |
|
|
|
|
if (mnt.Destination==volume) { |
|
|
|
|
outside = mnt.Source; |
|
|
|
|
rw = mnt.RW ? "rw" : "ro"; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
nodes[name].volumes.push({ |
|
|
|
|
id: volume+':'+(outside?outside:name), |
|
|
|
|
rw:rw, |
|
|
|
|
inside: volume, |
|
|
|
|
outside: outside, |
|
|
|
|
host: outside && !outside.match(/^\/var\/lib\/docker/) |
|
|
|
|
? volumes[volume] : null |
|
|
|
|
? outside : null |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
nodes[name].volumesfrom = c.VolumesFrom; |
|
|
|
|
nodes[name].volumesfrom = []; |
|
|
|
|
if (!nodes[name].volumesto) nodes[name].volumesto = []; |
|
|
|
|
if (c.HostConfig.VolumesFrom) c.HostConfig.VolumesFrom.forEach(function(id) { |
|
|
|
|
containers.forEach(function(c) { |
|
|
|
|
if (c.Id == id || c.Name == "/"+id || c.Name == id) { |
|
|
|
|
var src = c.Name.replace(/^\//, ""); |
|
|
|
|
nodes[name].volumesfrom.push(src); |
|
|
|
|
if (!nodes[src]) nodes[src] = {}; |
|
|
|
|
if (!nodes[src].volumesto) nodes[src].volumesto = []; |
|
|
|
|
nodes[src].volumesto.push(name); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
nodes[name].links = []; |
|
|
|
|
if (!nodes[name].usedby) nodes[name].usedby = []; |
|
|
|
|
if (c.HostConfig && c.HostConfig.Links) |
|
|
|
|
c.HostConfig.Links.forEach(function(l) { |
|
|
|
|
nodes[name].links.push({ |
|
|
|
|
var target = { |
|
|
|
|
to: l.replace(/^\/?([^:]*).*$/, "$1"), |
|
|
|
|
link: l.replace(new RegExp("^.*:/?"+name+"/"), "") |
|
|
|
|
}); |
|
|
|
|
}; |
|
|
|
|
nodes[name].links.push(target); |
|
|
|
|
if (!nodes[target.to]) nodes[target.to] = {}; |
|
|
|
|
if (!nodes[target.to].usedby) nodes[target.to].usedby = []; |
|
|
|
|
nodes[target.to].usedby.push(name); |
|
|
|
|
}); |
|
|
|
|
console.log(nodes[name]); |
|
|
|
|
}); |
|
|
|
|
for (name in nodes) { // cleanup duplicate links to volumes when using volumes-from
|
|
|
|
|
var n = nodes[name]; |
|
|
|
|
n.volumesfrom.forEach(function(other) { |
|
|
|
|
var o = nodes[other]; |
|
|
|
|
o.volumes.forEach(function(ovol) { |
|
|
|
|
n.volumes.reduceRight(function(x, nvol, i, arr) { |
|
|
|
|
if (nvol.id == ovol.id) |
|
|
|
|
arr.splice(i, 1); |
|
|
|
|
}, []) |
|
|
|
|
}) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
this.setContainers = function(c) { |
|
|
|
|
if (typeof c == "string") c = JSON.parse(c); |
|
|
|
@ -131,7 +321,7 @@ var dc = new DockerContainers(); |
|
|
|
|
@param data (optional) The error can be a string or any structure. |
|
|
|
|
Strings are shown to the user, structures are logged only. |
|
|
|
|
@param stay (optional) If not given as @c true, reloads page after 5s. */ |
|
|
|
|
function error(data, stay) { |
|
|
|
|
function error(data) { |
|
|
|
|
$("#status").fadeOut("slow", function() { |
|
|
|
|
$("#status").addClass("error") |
|
|
|
|
$("#status").removeClass("notice") |
|
|
|
@ -149,7 +339,6 @@ function error(data, stay) { |
|
|
|
|
console.log("error"); |
|
|
|
|
} |
|
|
|
|
$("#status").fadeIn("slow"); |
|
|
|
|
if (!stay) setTimeout(start, 5000); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -195,14 +384,14 @@ function success(text) { |
|
|
|
|
/** @param text Text is a message or some complex HTML from the server. |
|
|
|
|
@param msg The success message text */ |
|
|
|
|
function status(text, msg) { |
|
|
|
|
$("#main").fadeOut("slow", function() { |
|
|
|
|
$("#main").html(text); |
|
|
|
|
if (msg) success(msg); |
|
|
|
|
else setTimeout("$('#status').fadeOut('slow')", 5000); |
|
|
|
|
$("#main").fadeIn("slow", function() { |
|
|
|
|
$("form input:first-child").focus(); |
|
|
|
|
}) |
|
|
|
|
}); |
|
|
|
|
$("#main").hide(); |
|
|
|
|
$("#main").html(text); |
|
|
|
|
$("#popup").hide(); |
|
|
|
|
if (msg) success(msg); |
|
|
|
|
else setTimeout("$('#status').fadeOut('slow')", 5000); |
|
|
|
|
$("#main").show(); |
|
|
|
|
$("form input:first-child").focus(); |
|
|
|
|
dc.contextmenu("#main"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function emit(signal, data) { |
|
|
|
@ -278,6 +467,7 @@ function zoom(incr = 0) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var viz = null; |
|
|
|
|
var vizmore = null; |
|
|
|
|
var rankdir = "LR"; |
|
|
|
|
function rotateviz() { |
|
|
|
|
if (!viz) return; |
|
|
|
@ -285,16 +475,21 @@ function rotateviz() { |
|
|
|
|
rankdir = "TB"; |
|
|
|
|
else |
|
|
|
|
rankdir = "LR"; |
|
|
|
|
showviz(viz); |
|
|
|
|
showviz(); |
|
|
|
|
} |
|
|
|
|
function showviz(vizpath) { |
|
|
|
|
function showviz(vizpath, more) { |
|
|
|
|
$("#imagetools").show(); |
|
|
|
|
viz = vizpath; |
|
|
|
|
console.log("DRAW: "+viz); |
|
|
|
|
if (!vizpath) { |
|
|
|
|
vizpath = viz; |
|
|
|
|
more = vizmore; |
|
|
|
|
} else { |
|
|
|
|
viz = vizpath; |
|
|
|
|
vizmore = more; |
|
|
|
|
} |
|
|
|
|
res = "digraph {\n"+" rankdir="+rankdir+";\n"+viz+"\n}"; |
|
|
|
|
try { |
|
|
|
|
zoomlevel = 0; |
|
|
|
|
status(Viz(res)); |
|
|
|
|
status(more?Viz(res)+more:Viz(res)); |
|
|
|
|
} catch(e) { |
|
|
|
|
(res = res.split("\n")).forEach(function(v, i, a) { |
|
|
|
|
a[i] = ("000"+(i+1)).slice(-3)+": "+v; |
|
|
|
@ -303,18 +498,10 @@ function showviz(vizpath) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function details(c) { |
|
|
|
|
$("#imagetools").hide(); |
|
|
|
|
$.ajax({url: "details.php?container="+c, success: function(res) { |
|
|
|
|
try { |
|
|
|
|
status(res); |
|
|
|
|
} catch(e) { |
|
|
|
|
status("<pre>"+res+"</pre>"); |
|
|
|
|
error("Exception Caught: "+e); |
|
|
|
|
} |
|
|
|
|
}}).fail(function() { |
|
|
|
|
error("offline"); |
|
|
|
|
}); |
|
|
|
|
function details(name) { |
|
|
|
|
if (name) focused = name; |
|
|
|
|
else if (!focused) return overview(); |
|
|
|
|
showviz(dc.subgraph(focused)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function action(container, action) { |
|
|
|
@ -337,11 +524,6 @@ function manage() { |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Show an Overview of all Docker Services */ |
|
|
|
|
function overview() { |
|
|
|
|
$("#imagetools").hide(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Show an Overview of all Docker Images */ |
|
|
|
|
function imgs() { |
|
|
|
|
$("#imagetools").hide(); |
|
|
|
@ -362,6 +544,14 @@ function imgs() { |
|
|
|
|
function containers(c) { |
|
|
|
|
console.log("->rcv containers"); |
|
|
|
|
dc.setContainers(c); |
|
|
|
|
if (focused && dc.exists(focused)) |
|
|
|
|
details(focused); |
|
|
|
|
else |
|
|
|
|
overview(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function overview() { |
|
|
|
|
focused = null; |
|
|
|
|
showviz(dc.graph()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -380,11 +570,14 @@ function start() { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function init() { |
|
|
|
|
socket.io.on("connect", connected); |
|
|
|
|
socket.io.on("reconnect", connected); |
|
|
|
|
socket.io.on("disconnect", disconnected); |
|
|
|
|
socket.io.on("error", disconnected); |
|
|
|
|
socket.on("containers", containers); |
|
|
|
|
socket.io |
|
|
|
|
.on("connect", connected) |
|
|
|
|
.on("reconnect", connected) |
|
|
|
|
.on("disconnect", disconnected) |
|
|
|
|
.on("error", disconnected); |
|
|
|
|
socket |
|
|
|
|
.on("fail", error) |
|
|
|
|
.on("containers", containers); |
|
|
|
|
start(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|