with popup menu
This commit is contained in:
31
ChangeLog
31
ChangeLog
@@ -1,3 +1,34 @@
|
|||||||
|
2015-11-26 15:51 marc
|
||||||
|
|
||||||
|
* html/manage.php: table fixed
|
||||||
|
|
||||||
|
2015-11-25 15:26 marc
|
||||||
|
|
||||||
|
* html/images.php, html/index.html.in, html/overview.php,
|
||||||
|
html/rotate.svg, html/servicedock.css, html/servicedock.js,
|
||||||
|
html/zoom.svg: allow image zoom and rotate
|
||||||
|
|
||||||
|
2015-11-20 15:14 marc
|
||||||
|
|
||||||
|
* html/images.php, html/servicedock.css: better image overview
|
||||||
|
|
||||||
|
2015-11-20 13:49 marc
|
||||||
|
|
||||||
|
* html/makefile.am: typo
|
||||||
|
|
||||||
|
2015-11-19 13:13 marc
|
||||||
|
|
||||||
|
* ax_init_standard_project.m4, configure.ac, html/about.php.in,
|
||||||
|
html/images.php, html/index.html.in, html/makefile.am,
|
||||||
|
html/overview.php, html/servicedock.css, html/servicedock.js:
|
||||||
|
added about and images
|
||||||
|
|
||||||
|
2015-11-18 15:54 marc
|
||||||
|
|
||||||
|
* ChangeLog, README, html/details.php, html/manage.php,
|
||||||
|
html/overview.php, html/servicedock.css: added doku for
|
||||||
|
installatin in README
|
||||||
|
|
||||||
2015-11-18 13:24 marc
|
2015-11-18 13:24 marc
|
||||||
|
|
||||||
* configure.ac, html/details.php, html/index.html.in,
|
* configure.ac, html/details.php, html/index.html.in,
|
||||||
|
@@ -12,8 +12,9 @@ EXTRA_DIST = index.html.in about.php.in
|
|||||||
|
|
||||||
wwwdir = ${pkgdatadir}/html
|
wwwdir = ${pkgdatadir}/html
|
||||||
www_DATA = index.html about.php
|
www_DATA = index.html about.php
|
||||||
dist_www_DATA = servicedock.css servicedock.js jquery.js viz.js \
|
dist_www_DATA = servicedock.css servicedock.js jquery.js viz.js \
|
||||||
menu.svg overview.php details.php manage.php \
|
menu.svg overview.php details.php manage.php \
|
||||||
action.php jquery-ui.js jquery-ui.css images.php
|
action.php jquery-ui.js jquery-ui.css images.php \
|
||||||
|
zoom.svg rotate.svg
|
||||||
|
|
||||||
MAINTAINERCLEANFILES = makefile.in
|
MAINTAINERCLEANFILES = makefile.in
|
||||||
|
@@ -124,14 +124,26 @@ function zoom(incr = 0) {
|
|||||||
case 1: {
|
case 1: {
|
||||||
$("#main svg").css("width", "100%");
|
$("#main svg").css("width", "100%");
|
||||||
$("#main svg").css("height", "auto");
|
$("#main svg").css("height", "auto");
|
||||||
$("#main svg").css("max-width", "100%");
|
$("#main svg").css("max-width", "none");
|
||||||
$("#main svg").css("max-height", "none");
|
$("#main svg").css("max-height", "none");
|
||||||
} break;
|
} break;
|
||||||
case 2: {
|
case 2: {
|
||||||
$("#main.svg").css("width", "auto");
|
$("#main.svg").css("width", "auto");
|
||||||
$("#main.svg").css("height", "100%");
|
$("#main.svg").css("height", "100%");
|
||||||
$("#main.svg").css("max-width", "none");
|
$("#main.svg").css("max-width", "none");
|
||||||
$("#main.svg").css("max-height", "100%");
|
$("#main.svg").css("max-height", "none");
|
||||||
|
} break;
|
||||||
|
case 3: {
|
||||||
|
$("#main svg").css("width", "200%");
|
||||||
|
$("#main svg").css("height", "auto");
|
||||||
|
$("#main svg").css("max-width", "none");
|
||||||
|
$("#main svg").css("max-height", "none");
|
||||||
|
} break;
|
||||||
|
case 4: {
|
||||||
|
$("#main.svg").css("width", "auto");
|
||||||
|
$("#main.svg").css("height", "200%");
|
||||||
|
$("#main.svg").css("max-width", "none");
|
||||||
|
$("#main.svg").css("max-height", "none");
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,66 +9,219 @@
|
|||||||
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
||||||
|
|
||||||
var socket = io.connect();
|
var socket = io.connect();
|
||||||
|
var focused = null;
|
||||||
|
|
||||||
function DockerContainers() {
|
function DockerContainers() {
|
||||||
var Status = Object.freeze({
|
var Status = Object.freeze({
|
||||||
Error: "red",
|
Error: {color: "red", action1: "start", action2: "remove"},
|
||||||
Terminated: "yellow",
|
Terminated: {color: "yellow", action1: "start", action2: "remove"},
|
||||||
Restarting: "lightblue",
|
Restarting: {color: "lightblue", action1: "start", action2: "remove"},
|
||||||
Paused: "lightgrey",
|
Paused: {color: "lightgrey", action1: "unpause", action2: null},
|
||||||
Running: "lightgreen"
|
Running: {color: "lightgreen", action1: "pause", action2: "stop"}
|
||||||
});
|
});
|
||||||
var containers = [];
|
var containers = [];
|
||||||
var nodes = [];
|
var nodes = [];
|
||||||
this.graph = function() {
|
function protocol(port) {
|
||||||
var res = "";
|
if (port.toString().match("443")) return "https://";
|
||||||
console.log("nodes["+nodes.length+"]=", nodes);
|
if (port.toString().match("3304")) return "mysql://";
|
||||||
for (name in nodes) {
|
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 n = nodes[name];
|
||||||
var label = n.name+'\\n'+n.image;
|
$(selector).prepend('<div id="popup"></div>')
|
||||||
res += '"'+n.name+'"'
|
$("#popup").empty();
|
||||||
+' [label="'+label
|
if (n.status.action1) {
|
||||||
+'",URL="details('+"'"+n.name+"'"
|
$("#popup").append('<button id="popup1">'+n.status.action1+'</button>');
|
||||||
+')",style=filled,fillcolor='+n.status+"];\n";
|
$("#popup1").click(function() {
|
||||||
}
|
socket.emit(n.status.action1, name);
|
||||||
res += "{rank=same;\n";
|
});
|
||||||
for (name in nodes) {
|
}
|
||||||
var n = nodes[name];
|
$("#popup").append('<button id="popup2">'+(focused?"overview":"focus")+'</button>');
|
||||||
n.volumes.forEach(function(v) {
|
$("#popup2").click(function() {
|
||||||
res += '"'+v.id+'" [label="'+v.inside+'",shape=box];\n';
|
if (focused) overview(); else details(name);
|
||||||
});
|
});
|
||||||
}
|
if (n.status.action2) {
|
||||||
res+="}\n";
|
$("#popup").append('<button id="popup3">'+n.status.action2+'</button>');
|
||||||
res += "{rank=same;\n";
|
$("#popup3").click(function() {
|
||||||
for (name in nodes) {
|
socket.emit(n.status.action2, name);
|
||||||
var n = nodes[name];
|
|
||||||
n.volumes.forEach(function(v) {
|
|
||||||
if (v.host)
|
|
||||||
res += '"'+v.outside+'" [label="'+v.host+'",shape=box];\n';
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
res+="}\n";
|
$("#popup").append('<br/>');
|
||||||
for (name in nodes) {
|
$("#popup").append('<button id="popup4">download</button>');
|
||||||
var n = nodes[name];
|
$("#popup").css("position", "fixed");
|
||||||
n.volumes.forEach(function(v) {
|
$("#popup").css("top", e.pageY-$("#popup").height()/4);
|
||||||
if (v.host)
|
$("#popup").css("left", e.pageX-$("#popup").width()/2);
|
||||||
res += '"'+v.id+'" -> "'+v.outside+'"\n';
|
$("#popup").mouseleave(function() {
|
||||||
|
$("#popup").hide();
|
||||||
|
}).click(function() {
|
||||||
|
$("#popup").hide();
|
||||||
});
|
});
|
||||||
}
|
$("#popup").show();
|
||||||
for (name in nodes) {
|
})
|
||||||
var n = nodes[name];
|
}
|
||||||
n.volumes.forEach(function(v) {
|
this.details = function(name) {
|
||||||
res += '"'+n.name+'" -> "'+v.id+'"\n';
|
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;
|
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 (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 n) res += graphVolumesInside(n[name]);
|
||||||
|
res+="}\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() {
|
function setup() {
|
||||||
delete nodes; nodes=[];
|
delete nodes; nodes = [];
|
||||||
containers.forEach(function(c) {
|
containers.forEach(function(c, i) {
|
||||||
var name = c.Name.replace(/^\//, "");
|
var name = c.Name.replace(/^\//, "");
|
||||||
nodes[name] = {};
|
if (!nodes[name]) nodes[name] = {};
|
||||||
console.log("container: "+name);
|
nodes[name].id = i;
|
||||||
nodes[name].name = name;
|
nodes[name].name = name;
|
||||||
nodes[name].image = c.Config.Image;
|
nodes[name].image = c.Config.Image;
|
||||||
nodes[name].ports = [];
|
nodes[name].ports = [];
|
||||||
@@ -78,43 +231,80 @@ function DockerContainers() {
|
|||||||
if (ports[port])
|
if (ports[port])
|
||||||
for (var expose in ports[port]) {
|
for (var expose in ports[port]) {
|
||||||
var ip = ports[port][expose].HostIp;
|
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({
|
nodes[name].ports.push({
|
||||||
internal: port,
|
internal: port,
|
||||||
external: ports[port][expose].HostPort,
|
external: ports[port][expose].HostPort,
|
||||||
ip: ip
|
ip: ip
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (c.State.Running) nodes[name].status = Status.Running;
|
if (c.State.Paused) nodes[name].status = Status.Paused;
|
||||||
else 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.Restarting) nodes[name].status = Status.Restarting;
|
||||||
else if (c.State.ExitCode == 0) nodes[name].status = Status.Terminated;
|
else if (c.State.ExitCode == 0) nodes[name].status = Status.Terminated;
|
||||||
else nodes[name].status = Status.Error;
|
else nodes[name].status = Status.Error;
|
||||||
|
console.log("STATUS", name, c.State, nodes[name].status);
|
||||||
nodes[name].volumes = [];
|
nodes[name].volumes = [];
|
||||||
var volumes = c.Volumes || c.Config.Volumes;
|
var volumes = c.Volumes || c.Config.Volumes;
|
||||||
nodes[name].volumes = [];
|
nodes[name].volumes = [];
|
||||||
if (volumes)
|
if (volumes)
|
||||||
for (var volume in volumes) {
|
for (var volume in volumes) {
|
||||||
|
var rw = "rw";
|
||||||
var outside = (typeof volumes[volume]=="string")?volumes[volume]:null;
|
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({
|
nodes[name].volumes.push({
|
||||||
id: volume+':'+(outside?outside:name),
|
id: volume+':'+(outside?outside:name),
|
||||||
|
rw:rw,
|
||||||
inside: volume,
|
inside: volume,
|
||||||
outside: outside,
|
outside: outside,
|
||||||
host: outside && !outside.match(/^\/var\/lib\/docker/)
|
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 = [];
|
nodes[name].links = [];
|
||||||
|
if (!nodes[name].usedby) nodes[name].usedby = [];
|
||||||
if (c.HostConfig && c.HostConfig.Links)
|
if (c.HostConfig && c.HostConfig.Links)
|
||||||
c.HostConfig.Links.forEach(function(l) {
|
c.HostConfig.Links.forEach(function(l) {
|
||||||
nodes[name].links.push({
|
var target = {
|
||||||
to: l.replace(/^\/?([^:]*).*$/, "$1"),
|
to: l.replace(/^\/?([^:]*).*$/, "$1"),
|
||||||
link: l.replace(new RegExp("^.*:/?"+name+"/"), "")
|
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) {
|
this.setContainers = function(c) {
|
||||||
if (typeof c == "string") c = JSON.parse(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.
|
@param data (optional) The error can be a string or any structure.
|
||||||
Strings are shown to the user, structures are logged only.
|
Strings are shown to the user, structures are logged only.
|
||||||
@param stay (optional) If not given as @c true, reloads page after 5s. */
|
@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").fadeOut("slow", function() {
|
||||||
$("#status").addClass("error")
|
$("#status").addClass("error")
|
||||||
$("#status").removeClass("notice")
|
$("#status").removeClass("notice")
|
||||||
@@ -149,7 +339,6 @@ function error(data, stay) {
|
|||||||
console.log("error");
|
console.log("error");
|
||||||
}
|
}
|
||||||
$("#status").fadeIn("slow");
|
$("#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 text Text is a message or some complex HTML from the server.
|
||||||
@param msg The success message text */
|
@param msg The success message text */
|
||||||
function status(text, msg) {
|
function status(text, msg) {
|
||||||
$("#main").fadeOut("slow", function() {
|
$("#main").hide();
|
||||||
$("#main").html(text);
|
$("#main").html(text);
|
||||||
if (msg) success(msg);
|
$("#popup").hide();
|
||||||
else setTimeout("$('#status').fadeOut('slow')", 5000);
|
if (msg) success(msg);
|
||||||
$("#main").fadeIn("slow", function() {
|
else setTimeout("$('#status').fadeOut('slow')", 5000);
|
||||||
$("form input:first-child").focus();
|
$("#main").show();
|
||||||
})
|
$("form input:first-child").focus();
|
||||||
});
|
dc.contextmenu("#main");
|
||||||
}
|
}
|
||||||
|
|
||||||
function emit(signal, data) {
|
function emit(signal, data) {
|
||||||
@@ -278,6 +467,7 @@ function zoom(incr = 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var viz = null;
|
var viz = null;
|
||||||
|
var vizmore = null;
|
||||||
var rankdir = "LR";
|
var rankdir = "LR";
|
||||||
function rotateviz() {
|
function rotateviz() {
|
||||||
if (!viz) return;
|
if (!viz) return;
|
||||||
@@ -285,16 +475,21 @@ function rotateviz() {
|
|||||||
rankdir = "TB";
|
rankdir = "TB";
|
||||||
else
|
else
|
||||||
rankdir = "LR";
|
rankdir = "LR";
|
||||||
showviz(viz);
|
showviz();
|
||||||
}
|
}
|
||||||
function showviz(vizpath) {
|
function showviz(vizpath, more) {
|
||||||
$("#imagetools").show();
|
$("#imagetools").show();
|
||||||
viz = vizpath;
|
if (!vizpath) {
|
||||||
console.log("DRAW: "+viz);
|
vizpath = viz;
|
||||||
|
more = vizmore;
|
||||||
|
} else {
|
||||||
|
viz = vizpath;
|
||||||
|
vizmore = more;
|
||||||
|
}
|
||||||
res = "digraph {\n"+" rankdir="+rankdir+";\n"+viz+"\n}";
|
res = "digraph {\n"+" rankdir="+rankdir+";\n"+viz+"\n}";
|
||||||
try {
|
try {
|
||||||
zoomlevel = 0;
|
zoomlevel = 0;
|
||||||
status(Viz(res));
|
status(more?Viz(res)+more:Viz(res));
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
(res = res.split("\n")).forEach(function(v, i, a) {
|
(res = res.split("\n")).forEach(function(v, i, a) {
|
||||||
a[i] = ("000"+(i+1)).slice(-3)+": "+v;
|
a[i] = ("000"+(i+1)).slice(-3)+": "+v;
|
||||||
@@ -303,18 +498,10 @@ function showviz(vizpath) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function details(c) {
|
function details(name) {
|
||||||
$("#imagetools").hide();
|
if (name) focused = name;
|
||||||
$.ajax({url: "details.php?container="+c, success: function(res) {
|
else if (!focused) return overview();
|
||||||
try {
|
showviz(dc.subgraph(focused));
|
||||||
status(res);
|
|
||||||
} catch(e) {
|
|
||||||
status("<pre>"+res+"</pre>");
|
|
||||||
error("Exception Caught: "+e);
|
|
||||||
}
|
|
||||||
}}).fail(function() {
|
|
||||||
error("offline");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function action(container, action) {
|
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 */
|
/** Show an Overview of all Docker Images */
|
||||||
function imgs() {
|
function imgs() {
|
||||||
$("#imagetools").hide();
|
$("#imagetools").hide();
|
||||||
@@ -362,6 +544,14 @@ function imgs() {
|
|||||||
function containers(c) {
|
function containers(c) {
|
||||||
console.log("->rcv containers");
|
console.log("->rcv containers");
|
||||||
dc.setContainers(c);
|
dc.setContainers(c);
|
||||||
|
if (focused && dc.exists(focused))
|
||||||
|
details(focused);
|
||||||
|
else
|
||||||
|
overview();
|
||||||
|
}
|
||||||
|
|
||||||
|
function overview() {
|
||||||
|
focused = null;
|
||||||
showviz(dc.graph());
|
showviz(dc.graph());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,11 +570,14 @@ function start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
socket.io.on("connect", connected);
|
socket.io
|
||||||
socket.io.on("reconnect", connected);
|
.on("connect", connected)
|
||||||
socket.io.on("disconnect", disconnected);
|
.on("reconnect", connected)
|
||||||
socket.io.on("error", disconnected);
|
.on("disconnect", disconnected)
|
||||||
socket.on("containers", containers);
|
.on("error", disconnected);
|
||||||
|
socket
|
||||||
|
.on("fail", error)
|
||||||
|
.on("containers", containers);
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,6 +22,8 @@ svg {
|
|||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
width: auto;
|
width: auto;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
z-index: -1;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
form {
|
||||||
@@ -238,6 +240,13 @@ table.docker li+li {
|
|||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#popup {
|
||||||
|
position: fixed;
|
||||||
|
background-color: lightblue;
|
||||||
|
border: .1ex solid blue;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.clear {
|
.clear {
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
@@ -4,13 +4,13 @@ module.exports = function() {
|
|||||||
|
|
||||||
module.connection = function(socket) {
|
module.connection = function(socket) {
|
||||||
|
|
||||||
var sys = require('sys');
|
//var sys = require('sys');
|
||||||
var exec = require('child_process').exec;
|
var proc = require('child_process');
|
||||||
|
|
||||||
console.log("new client");
|
console.log("new client");
|
||||||
|
|
||||||
function emit(signal, data, info) {
|
function emit(signal, data, info) {
|
||||||
if (typeof data == 'string') {
|
if (typeof data == 'string' && !data.match("\n")) {
|
||||||
console.log("<- signal: "+signal+"("+data+")");
|
console.log("<- signal: "+signal+"("+data+")");
|
||||||
} else {
|
} else {
|
||||||
console.log("<- signal: "+signal);
|
console.log("<- signal: "+signal);
|
||||||
@@ -24,31 +24,86 @@ module.exports = function() {
|
|||||||
socket.broadcast.emit(signal, data);
|
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) {
|
function containerinspect(error, stdout, stderr) {
|
||||||
console.log(error);
|
if (error || stderr)
|
||||||
if (!error && !stderr) {
|
return fail("inspect docker containers failed", {
|
||||||
// var res = {};
|
error: error, stderr: stderr, stdout: stdout
|
||||||
// JSON.parse(stdout).forEach(function(c) {
|
});
|
||||||
// res[c.Id] = c;
|
emit("containers", stdout);
|
||||||
// });
|
|
||||||
emit("containers", stdout);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function containerlist(error, stdout, stderr) {
|
function containerlist(error, stdout, stderr) {
|
||||||
console.log(error);
|
if (error || stderr)
|
||||||
console.log("docker inspect "+stdout.trim().replace(/\n/g, " "));
|
return fail("list docker containers failed", {
|
||||||
if (!error && !stderr)
|
error: error, stderr: stderr, stdout: stdout
|
||||||
exec("docker inspect "+stdout.trim().replace(/\n/g, " "),
|
});
|
||||||
containerinspect);
|
exec("docker inspect "+stdout.trim().replace(/\n/g, " "), containerinspect);
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.on("containers", function() {
|
function updatecontainers(error, stdout, stderr) {
|
||||||
|
if (error || stderr)
|
||||||
|
return fail("update docker container failed", {
|
||||||
|
error: error, stderr: stderr, stdout: stdout
|
||||||
|
});
|
||||||
|
exec("docker ps -aq", containerlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
function modify(cmd, name) {
|
||||||
|
if (!name.match(/^[a-z0-9][-_:.+a-z0-9]*$/i))
|
||||||
|
return fail("illegal instance name", {
|
||||||
|
error: error, stderr: stderr, stdout: stdout
|
||||||
|
});
|
||||||
|
exec("docker "+cmd+" "+name, updatecontainers);
|
||||||
|
}
|
||||||
|
|
||||||
|
function containers() {
|
||||||
console.log("-> containers");
|
console.log("-> containers");
|
||||||
exec("docker ps -aq",
|
updatecontainers();
|
||||||
containerlist);
|
}
|
||||||
});
|
|
||||||
|
|
||||||
|
function start(name) {
|
||||||
|
console.log("-> start("+name+")");
|
||||||
|
modify("start", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop(name) {
|
||||||
|
console.log("-> stop("+name+")");
|
||||||
|
modify("stop", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pause(name) {
|
||||||
|
console.log("-> pause("+name+")");
|
||||||
|
modify("pause", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unpause(name) {
|
||||||
|
console.log("-> unpause("+name+")");
|
||||||
|
modify("unpause", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(name) {
|
||||||
|
console.log("-> remove("+name+")");
|
||||||
|
modify("rm", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
socket
|
||||||
|
.on("containers", containers)
|
||||||
|
.on("start", start)
|
||||||
|
.on("stop", stop)
|
||||||
|
.on("pause", pause)
|
||||||
|
.on("unpause", unpause)
|
||||||
|
.on("remove", remove);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return module;
|
return module;
|
||||||
|
@@ -43,9 +43,12 @@
|
|||||||
<p>start up engine, please wait ...</p>
|
<p>start up engine, please wait ...</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="status">
|
||||||
|
|
||||||
|
<noscript>JavaScript is required for the interface.</noscript>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="status">
|
</body>
|
||||||
|
</html>
|
||||||
<noscript>JavaScript is required for the interface.</noscript>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
Reference in New Issue
Block a user