diff options
Diffstat (limited to 'static/status-dnbd3.html')
-rw-r--r-- | static/status-dnbd3.html | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/static/status-dnbd3.html b/static/status-dnbd3.html new file mode 100644 index 0000000..5e81657 --- /dev/null +++ b/static/status-dnbd3.html @@ -0,0 +1,299 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<html> +<head> + <script src="d3.v3.min.js"></script> + <script type="text/javascript" src="jquery-1.7.1.min.js"></script> + <script src="smoothie.js"></script> + <style type="text/css"> + .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; } + svg, div, canvas { margin:0; padding:0; } + </style> +</head> +<body> +<div id="graph"></div> +<div> <canvas id="traffic" width="100%" height="150"></canvas></div> +</body> +<script type="text/javascript"> + +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); +var colorList = d3.scale.category20(); + +function myGraph(el) { + + 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, "-"); + if (!distance) distance = 5; + distance *= w / 2000; + if (!radius) radius = 5; + var node = findNode(id); + if (!node) { + nodes.push({"id":id, "title": title, "distance":distance, "radius":radius, "active":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.color = color; + var s = servers[title]; + if (s && visNode) { + visNode.select("circle").style("fill", s.color); + //visNode.select("#" + id).select("circle").style("stroke", color); + } + } + } + + this.decay = function () { + for (var i = nodes.length - 1; i >= 0; --i) { + if ( nodes[i].active <= 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); + } + nodes.splice(i, 1); + changed = true; + continue; + } + nodes[i].active--; + } + for (var i = links.length - 1; i >= 0; --i) { + if (links[i].active <= 0) { + links.splice(i, 1); + changed = true; + continue; + } + links[i].active--; + } + } + + 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.addLink = function (sourceId, targetId) { + sourceId = 'a' + sourceId.replace(/\./g, "-"); + targetId = 'a' + targetId.replace(/\./g, "-"); + 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 findNode = function (id) { + 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]; + } + } + + var findNodeIndex = function (id) { + for (var i=0; i < nodes.length; i++) { + if (nodes[i].id === id) + return i + }; + } + + var w = 100, h = 100; + + var updateBounds = function() { + w = window.innerWidth; + h = window.innerHeight - 150 - 10; + vis.attr("width", w); + vis.attr("height", h); + force.size([w, h]); + $(el).attr("width", w); + $(el).attr("height", h); + $("#traffic").attr("width", w); + } + + var vis = this.vis = d3.select(el).append("svg:svg") + .attr("width", w) + .attr("height", h); + + var force = d3.layout.force() + .gravity(.02) + .distance(function(bla) { return bla.source.distance * bla.target.distance; }) + .linkStrength(0.5) + .charge(function(bla) { return -bla.distance * 10 * bla.distance; }) + .friction(0.6) + .size([w, h]); + + var nodes = force.nodes(), + links = force.links(); + + this.update = function () { + if (!changed) + return; + update(); + } + + var update = function () { + changed = false; + + 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) { + var s = servers[d.title]; + if (!s) return; + smoothie.seriesSet[s.index].options.lineWidth = 4; + }) + .on("mouseout", function(d) { + var s = servers[d.title]; + if (!s) return; + smoothie.seriesSet[s.index].options.lineWidth = 2; + }); + + nodeEnter.append("circle") + .attr("class", "circle") + .attr("r", function(d) { return d.radius; }) + .style("fill", function(d) { return d.color; }); + + nodeEnter.append("title"); + + nodeEnter.append("text") + .attr("class", "nodetext") + .attr("dx", -32) + .attr("dy", "-1em") + .text(function(d) {return d.title}); + + node.exit().remove(); + + var link = vis.selectAll("line.link") + .data(links, function(d) { return d.source.id + "-" + d.target.id; }); + + link.enter().insert("line") + .attr("class", "link"); + + link.exit().remove(); + + vis.selectAll('g.node').forEach(function(e){$('#graph').find('svg').append(e)}); + + var render = function() { + var fixX = function(x) { + return (x - lx) * scale + 20 + offX; + } + var fixY = function(y) { + return (y - ly) * scale + 20 + offY; + } + var lx = 1000, ly = 1000, ux = -1000, uy = -1000; + for (var i = 0; i < nodes.length; ++i) { + if (nodes[i].x < lx) lx = nodes[i].x; + if (nodes[i].x > ux) ux = nodes[i].x; + if (nodes[i].y < ly) ly = nodes[i].y; + if (nodes[i].y > uy) uy = nodes[i].y; + } + var width = (ux - lx), height = (uy - ly); + var scale; + var offX = 0, offY = 0; + if ( (width / w) > (height / h) ) { + scale = (w - 40) / width; + offY = (h - (height * scale + 20)) / 2; + } else { + scale = (h - 40) / height; + offX = (w - (width * scale + 20)) / 2; + } + link.attr("x1", function(d) { return fixX(d.source.x); }) + .attr("y1", function(d) { return fixY(d.source.y); }) + .attr("x2", function(d) { return fixX(d.target.x); }) + .attr("y2", function(d) { return fixY(d.target.y); }); + + node.attr("transform", function(d) { return "translate(" + fixX(d.x) + "," + fixY(d.y) + ")"; }); + }; + + force.on("tick", render); + + updateBounds(); + force.start(); + } + + update(); + $( window ).resize( update ); +} + +graph = new myGraph("#graph"); + +var servers = {}; +var serverCount = 0; + +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; + } + } + + }, 'json').always(function() { + graph.decay(); + graph.update(); + }); + }, 2000); + +</script> |