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 | ||||
|  | ||||
| 	* configure.ac, html/details.php, html/index.html.in, | ||||
|   | ||||
| @@ -12,8 +12,9 @@ EXTRA_DIST = index.html.in about.php.in | ||||
|  | ||||
| wwwdir = ${pkgdatadir}/html | ||||
| www_DATA = index.html about.php | ||||
| dist_www_DATA = servicedock.css servicedock.js jquery.js viz.js	\ | ||||
|                 menu.svg overview.php details.php manage.php	\ | ||||
|                 action.php jquery-ui.js jquery-ui.css images.php | ||||
| dist_www_DATA = servicedock.css servicedock.js jquery.js viz.js		\ | ||||
|                 menu.svg overview.php details.php manage.php		\ | ||||
|                 action.php jquery-ui.js jquery-ui.css images.php	\ | ||||
|                 zoom.svg rotate.svg | ||||
|  | ||||
| MAINTAINERCLEANFILES = makefile.in | ||||
|   | ||||
| @@ -124,14 +124,26 @@ function zoom(incr = 0) { | ||||
|         case 1: { | ||||
|             $("#main svg").css("width", "100%"); | ||||
|             $("#main svg").css("height", "auto"); | ||||
|             $("#main svg").css("max-width", "100%"); | ||||
|             $("#main svg").css("max-width", "none"); | ||||
|             $("#main svg").css("max-height", "none"); | ||||
|         } break; | ||||
|         case 2: { | ||||
|             $("#main.svg").css("width", "auto"); | ||||
|             $("#main.svg").css("height", "100%"); | ||||
|             $("#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; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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"; | ||||
|         } | ||||
|         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'; | ||||
|             $(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); | ||||
|             }); | ||||
|         } | ||||
|         res+="}\n"; | ||||
|         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'; | ||||
|             if (n.status.action2) { | ||||
|                 $("#popup").append('<button id="popup3">'+n.status.action2+'</button>'); | ||||
|                 $("#popup3").click(function() { | ||||
|                     socket.emit(n.status.action2, 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'; | ||||
|             } | ||||
|             $("#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(); | ||||
|             }); | ||||
|         } | ||||
|         for (name in nodes) { | ||||
|             var n = nodes[name]; | ||||
|             n.volumes.forEach(function(v) { | ||||
|                 res += '"'+n.name+'" -> "'+v.id+'"\n'; | ||||
|             }); | ||||
|         } | ||||
|             $("#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 (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() { | ||||
|         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(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -22,6 +22,8 @@ svg { | ||||
|   max-height: 100%; | ||||
|   width: auto; | ||||
|   height: auto; | ||||
|   z-index: -1; | ||||
|   position: relative; | ||||
| } | ||||
|  | ||||
| form { | ||||
| @@ -238,6 +240,13 @@ table.docker li+li { | ||||
|   z-index: 0; | ||||
| } | ||||
|  | ||||
| #popup { | ||||
|   position: fixed; | ||||
|   background-color: lightblue; | ||||
|   border: .1ex solid blue; | ||||
|   text-align: center; | ||||
| } | ||||
|  | ||||
| .clear { | ||||
|   clear: both; | ||||
| } | ||||
|   | ||||
| @@ -4,13 +4,13 @@ module.exports = function() { | ||||
|  | ||||
|     module.connection = function(socket) { | ||||
|  | ||||
|         var sys = require('sys'); | ||||
|         var exec = require('child_process').exec; | ||||
|         //var sys = require('sys'); | ||||
|         var proc = require('child_process'); | ||||
|  | ||||
|         console.log("new client"); | ||||
|  | ||||
|         function emit(signal, data, info) { | ||||
|             if (typeof data == 'string') { | ||||
|             if (typeof data == 'string' && !data.match("\n")) { | ||||
|                 console.log("<- signal: "+signal+"("+data+")"); | ||||
|             } else { | ||||
|                 console.log("<- signal: "+signal); | ||||
| @@ -24,30 +24,85 @@ module.exports = function() { | ||||
|             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) { | ||||
|             console.log(error); | ||||
|             if (!error && !stderr) { | ||||
|                 // var res = {}; | ||||
|                 // JSON.parse(stdout).forEach(function(c) { | ||||
|                 //     res[c.Id] = c; | ||||
|                 // }); | ||||
|                 emit("containers", stdout); | ||||
|             } | ||||
|             if (error || stderr) | ||||
|                 return fail("inspect docker containers failed", { | ||||
|                     error: error, stderr: stderr, stdout: stdout | ||||
|                 }); | ||||
|             emit("containers", stdout); | ||||
|         } | ||||
|          | ||||
|         function containerlist(error, stdout, stderr) { | ||||
|             console.log(error); | ||||
|             console.log("docker inspect "+stdout.trim().replace(/\n/g, " ")); | ||||
|             if (!error && !stderr) | ||||
|                 exec("docker inspect "+stdout.trim().replace(/\n/g, " "), | ||||
|                      containerinspect); | ||||
|             if (error || stderr) | ||||
|                 return fail("list docker containers failed", { | ||||
|                     error: error, stderr: stderr, stdout: stdout | ||||
|                 }); | ||||
|             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"); | ||||
|             exec("docker ps -aq", | ||||
|                  containerlist); | ||||
|         }); | ||||
|             updatecontainers(); | ||||
|         } | ||||
|          | ||||
|         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); | ||||
|                  | ||||
|     } | ||||
|      | ||||
|   | ||||
| @@ -44,8 +44,11 @@ | ||||
|        | ||||
|     </div> | ||||
|  | ||||
| <div id="status"> | ||||
|     <div id="status"> | ||||
|        | ||||
|   <noscript>JavaScript is required for the interface.</noscript> | ||||
|       <noscript>JavaScript is required for the interface.</noscript> | ||||
|  | ||||
| </div> | ||||
|     </div> | ||||
|      | ||||
|   </body> | ||||
| </html> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user