diff options
-rw-r--r-- | static/status-dnbd3.html | 223 |
1 files changed, 144 insertions, 79 deletions
diff --git a/static/status-dnbd3.html b/static/status-dnbd3.html index 5e81657..5f02fa3 100644 --- a/static/status-dnbd3.html +++ b/static/status-dnbd3.html @@ -6,6 +6,7 @@ <script type="text/javascript" src="jquery-1.7.1.min.js"></script> <script src="smoothie.js"></script> <style type="text/css"> + #graph, #statistics, #traffic {position: absolute } .circle, .link { stroke: #56f; stroke-width: 3px; stroke-opacity: 0.75; } .nodetext { pointer-events: none; font: 10px sans-serif; } body { width:100%; height:100%; margin:0; padding:0; overflow: hidden; } @@ -14,53 +15,61 @@ </head> <body> <div id="graph"></div> +<div id="statistics"> </div> <div> <canvas id="traffic" width="100%" height="150"></canvas></div> </body> <script type="text/javascript"> +// Initialize the statistics chart at the bottom. var smoothie = new SmoothieChart({'millisPerPixel': 300, 'grid': {'millisPerLine': 30000}, timestampFormatter:SmoothieChart.timeFormatter}); smoothie.streamTo(document.getElementById("traffic"), 2000); -//var line1 = new TimeSeries(); -//smoothie.addTimeSeries(line1); +// For coloring the servers and the chart-lines. var colorList = d3.scale.category20(); +// IDs need to begin with a letter. +function makeId(prefix, text) { + return prefix + text.replace(/\./g, "-"); +} + function myGraph(el) { - var changed = false; + var changed = false; // Add and remove elements on the graph object this.addNode = function (id, radius, color, distance, x, y) { title = id; - id = 'a' + id.replace(/\./g, "-"); + id = makeId("a", id); if (!distance) distance = 5; distance *= w / 2000; if (!radius) radius = 5; var node = findNode(id); + // Add node, if it doesn't exist if (!node) { - nodes.push({"id":id, "title": title, "distance":distance, "radius":radius, "active":2, "x":x, "y":y, "color":color}); + nodes.push({"id":id, "title": title, "distance":distance, "radius":radius, "lifetime":2, "x":x, "y":y, "color":color}); changed = true; } else { var visNode = vis.select("#" + id); if (visNode) { - visNode.select("title").text(id); visNode.select("circle").attr("r", radius); } + node.distance = distance; node.radius = radius; - node.active = 2; + node.lifetime = 2; node.color = color; + var s = servers[title]; if (s && visNode) { visNode.select("circle").style("fill", s.color); - //visNode.select("#" + id).select("circle").style("stroke", color); } } } + // Remove nodes/edges that haven't reported for a while. this.decay = function () { for (var i = nodes.length - 1; i >= 0; --i) { - if ( nodes[i].active <= 0 ) { + if ( nodes[i].lifetime <= 0 ) { for (var j = links.length - 1; j >= 0; --j) { if ((links[j].source === nodes[i]) || (links[j].target === nodes[i])) links.splice(j,1); } @@ -68,64 +77,63 @@ function myGraph(el) { changed = true; continue; } - nodes[i].active--; + nodes[i].lifetime--; } for (var i = links.length - 1; i >= 0; --i) { - if (links[i].active <= 0) { + if (links[i].lifetime <= 0) { links.splice(i, 1); changed = true; continue; } - links[i].active--; + links[i].lifetime--; } } - this.removeNode = function (id) { - var i = 0; - var n = findNode(id); - while (i < links.length) { - if ((links[i]['source'] === n)||(links[i]['target'] == n)) links.splice(i,1); - else i++; - } - var index = findNodeIndex(id); - if(index !== undefined) { - nodes.splice(index, 1); - changed = true; - //update(); - } - } +// this.removeNode = function (id) { +// var i = 0; +// var n = findNode(id); +// while (i < links.length) { +// if ((links[i]['source'] === n)||(links[i]['target'] == n)) links.splice(i,1); +// else i++; +// } +// var index = findNodeIndex(id); +// if(index !== undefined) { +// nodes.splice(index, 1); +// changed = true; +// } +// } this.addLink = function (sourceId, targetId) { - sourceId = 'a' + sourceId.replace(/\./g, "-"); - targetId = 'a' + targetId.replace(/\./g, "-"); + + sourceId = makeId("a", sourceId); + targetId = makeId("a", targetId); var sourceNode = findNode(sourceId); var targetNode = findNode(targetId); if ((sourceNode !== undefined) && (targetNode !== undefined)) { - var link = findLink(sourceNode, targetNode); - if (!link) { - links.push({"source": sourceNode, "target": targetNode, "active":5}); - //update(); - changed = true; - } else { - link.active = 5; - } + var link = findLink(sourceNode, targetNode); + if (!link) { + links.push({"source": sourceNode, "target": targetNode, "lifetime":5}); + changed = true; + } else { + link.lifetime = 5; + } } } var findNode = function (id) { - for (var i=0; i < nodes.length; i++) { + for (var i = 0; i < nodes.length; i++) { if (nodes[i].id === id) return nodes[i] } } var findLink = function (sourceId, targetId) { - for (var i = 0; i < links.length; ++i) { - if ( (links[i].source === sourceId && links[i].target === targetId) - || (links[i].source === targetId && links[i].target === sourceId) ) - return links[i]; - } + for (var i = 0; i < links.length; ++i) { + if ( (links[i].source === sourceId && links[i].target === targetId) + || (links[i].source === targetId && links[i].target === sourceId) ) + return links[i]; + } } var findNodeIndex = function (id) { @@ -138,34 +146,46 @@ function myGraph(el) { var w = 100, h = 100; var updateBounds = function() { - w = window.innerWidth; - h = window.innerHeight - 150 - 10; + var width = window.innerWidth; + // Width for the graph and the statistics div + w = width/2; + // Chart needs to fit under the graph and the statistics + h = window.innerHeight - 150; vis.attr("width", w); vis.attr("height", h); force.size([w, h]); $(el).attr("width", w); $(el).attr("height", h); - $("#traffic").attr("width", w); + // Positions for statistics and the traffic graph + $("#statistics").attr("width", w).css("width", w + "px").css("left", w + "px"); + $("#statistics").attr("height", h).css("height", h + "px"); + $("#traffic").attr("width", width).css("top", h + "px"); } var vis = this.vis = d3.select(el).append("svg:svg") .attr("width", w) .attr("height", h); + // Settings for movement of the graph var force = d3.layout.force() + // Force of center gravitation .gravity(.02) + // Distance of the links .distance(function(bla) { return bla.source.distance * bla.target.distance; }) + // Strength of the links .linkStrength(0.5) + // Force with which the links "pull" .charge(function(bla) { return -bla.distance * 10 * bla.distance; }) + // Friction for the graph nodes .friction(0.6) .size([w, h]); + var nodes = force.nodes(), links = force.links(); this.update = function () { - if (!changed) - return; + if (!changed) return; update(); } @@ -175,15 +195,30 @@ function myGraph(el) { var node = vis.selectAll("g.node") .data(nodes, function(d) { return d.id;}); + var nodeEnter = node.enter().append("g") .attr("class", "node") .attr("id", function(d) { return d.id;}) .on("mouseover", function(d) { + // Highlight statistics div + var statDiv = document.getElementById(makeId("b", d.title)) + if (statDiv){ + statDiv.setAttribute("style", "background-color:blue; color:white;"); + } + + // Increase line width of server in chart var s = servers[d.title]; if (!s) return; smoothie.seriesSet[s.index].options.lineWidth = 4; }) .on("mouseout", function(d) { + // Make statistics div normal again + var statDiv = document.getElementById(makeId("b", d.title)) + if (statDiv){ + statDiv.setAttribute("style", "background-color:white; color:black;"); + } + + // Reset line width var s = servers[d.title]; if (!s) return; smoothie.seriesSet[s.index].options.lineWidth = 2; @@ -194,7 +229,6 @@ function myGraph(el) { .attr("r", function(d) { return d.radius; }) .style("fill", function(d) { return d.color; }); - nodeEnter.append("title"); nodeEnter.append("text") .attr("class", "nodetext") @@ -261,39 +295,70 @@ graph = new myGraph("#graph"); var servers = {}; var serverCount = 0; +// Get new data setInterval( function() { - $.get('/data.json', function(data) { - var g = data.graph; - var stats = data.servers; - if (g) { - for (var i = 0; i < g.nodes.length; ++i) { - graph.addNode(g.nodes[i].name, g.nodes[i].radius, g.nodes[i].color, g.nodes[i].distance, g.nodes[i].x, g.nodes[i].y); - } - for (var i = 0; i < g.edges.length; ++i) { - graph.addLink(g.edges[i].source, g.edges[i].target); - } - } - if (stats) { - for (var i = 0; i < stats.length; ++i) { - var server = servers[stats[i].address]; - if (!server) { - servers[stats[i].address] = server = { 'lastTime': 0, 'lastSent': 0, 'line': new TimeSeries(), 'index': serverCount++ }; - server.color = colorList(server.index); - smoothie.addTimeSeries(server['line'], {lineWidth:2, strokeStyle: server.color}); - } - if (server['lastTime'] != 0) { - server['line'].append(new Date().getTime(), (stats[i].bytesSent - server['lastSent'])/1000/(data.timestamp - server.lastTime)); - //smoothie.seriesSet[server.index].options.strokeStyle = 'rgba(0,255,0,0.93)'; - } - server['lastTime'] = data.timestamp; - server['lastSent'] = stats[i].bytesSent; - } + $.get('/data.json', function(data) { + var g = data.graph; + var stats = data.servers; + updateGraph(g, data); + updateTrafficGraph(stats, data); + updateTextStatistics(stats); + }, 'json').always(function() { + graph.decay(); + graph.update(); + }); + }, 2000); + +// Update data of the graph +function updateGraph(g, data){ + if (g) { + for (var i = 0; i < g.nodes.length; ++i) { + graph.addNode(g.nodes[i].name, g.nodes[i].radius, g.nodes[i].color, g.nodes[i].distance, g.nodes[i].x, g.nodes[i].y); + } + for (var i = 0; i < g.edges.length; ++i) { + graph.addLink(g.edges[i].source, g.edges[i].target); + } + } +} + +// Update data of the statistics divs +function updateTextStatistics(stats){ + if (stats) { + for (var i = 0; i < stats.length; ++i) { + var server = document.getElementById(makeId("b", stats[i].address)); + if (!server){ + $("#statistics").append("<div id=" + makeId ("b", stats[i].address) + "></div>"); + server = document.getElementById(makeId("b", stats[i].address)); + } + server.innerHTML = "<p><b> Server: " + stats[i].address + "</b><br>" + + "Number of clients: " + + stats[i].clientCount + "<br>" + + "uptime: " + Math.floor(stats[i].uptime / (3600 * 24)) + "d " + + Math.floor(stats[i].uptime / 3600) % 24 + "h " + Math.floor((stats[i].uptime) / 60) % 60 + "min" + "<br>" + + "Sent: " + Math.floor(stats[i].bytesSent / Math.pow(1024, 3)) + "GiB <br>" + + "Received: " + Math.floor(stats[i].bytesReceived / Math.pow(1024, 3)) + "GiB </p>"; + } + } +} - }, 'json').always(function() { - graph.decay(); - graph.update(); - }); - }, 2000); +// Update the traffic graph +function updateTrafficGraph(stats, data){ + if (stats) { + for (var i = 0; i < stats.length; ++i) { + var server = servers[stats[i].address]; + if (!server) { + servers[stats[i].address] = server = { 'lastTime': 0, 'lastSent': 0, 'line': new TimeSeries(), 'index': serverCount++ }; + server.color = colorList(server.index); + smoothie.addTimeSeries(server['line'], {lineWidth:2, strokeStyle: server.color}); + } + if (server['lastTime'] != 0) { + server['line'].append(new Date().getTime(), (stats[i].bytesSent - server['lastSent'])/1000/(data.timestamp - server.lastTime)); + } + server['lastTime'] = data.timestamp; + server['lastSent'] = stats[i].bytesSent; + } + } +} </script> |