summaryrefslogblamecommitdiffstats
path: root/static/status-dnbd3.html
blob: 0a9556e30b8cb905def890c7d5e7b7cc2b18e311 (plain) (tree)
1
2
3
4
5
6
7
8
9







                                                                      
                                                           







                                                                                 
                            



                                                                    
                                                 
                                                                                                                                                                                                                                                                                                              

                                                            
                                                




                                      


               

                       
                                               



                                              
 

                                   
                                             

 
                                    



                                     
                      
                        
                                                        
                                      
 

                                                         

              
                   
                             


                                      

                                 

                              


                         
                             
                                
 
                                        
                    



                                  
                                                                                                                     



                                               
                                                     

                                       
                                                               

                                                                    
             
                              


         
                                                            

                                                     
                                           






                                                                                                            
                                

                                                     
                                         



                                   
                                


         

                                                  

                                   

                               


                                                                              

                                         



                                                                       
                                                        
                                     
                        

                                                                         
                                                                                                                                                                                                              



                                                       
                                  
                                  
 
                                                                       
                                              

                                                  
                                                
                                                                                                                      
                                                                                                                                  

                                                                        
                                        
                     





                                                     
             


         















                                                                                    
                                                     







                                                                   
                                                                              










                                                                                                        







                                                                                       
                                  
                                                
                                   
                                


         



                                              
                         



                         
                               

                                   
                                      

                                                                




                                                     




                                
                                                         
                                                                                           

                                                                   





                                                        
                                         
                                 
                                      
                     
                                
                                              
                                
                          
                                            
                                               
                                       
                      

                      
 

                          

                               
                 

     

                        


                                                                                  
 
                                   
                                                                                    
 

                                                              
 

                            
 
                                              
                                                           
 

                                                    
                                                         

                                               
                                                                                


                                                                                             
 
                                                                     





                                                                      
                                                                                
                                 
                                                                                              
                     
 




                                                                      
 



                                                                
 




                                                    
 
                                 
 
                                 
 
                                                                                            
 

                                        





















                                                                             












                                                                            


                 




                                     

     







                                  
 
 

                              
 

                    

                 
               
                         
                                        

                                                                 


                                 
                                                                                              

                                        





                                  
 

 



                                                  
                                              

                                                  
                                                         
         


                                


     
 
 



                                                                      
                                  

                                                                            
                                      

                                                                         
                                   

                                                                      
                                 
                                                                   







                                           



                                                


                                                      
                                                                                                     
                                            

             

                                                              
                                       
                                                                          



                                                                                                                           

                                                                               


                                                                                                       


         
 
 





                                                   
                                                                                                                                                                                                                                           
                                                           

                                                                                                 


                                                                                    
             
 
                                                                             
                                            
                                         

                                                                                                                                    
                                
                                                                                  

                                         
             


                                                    
                                                         
         

     

         
<!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">
        #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; }
        svg, div, canvas { margin:0; padding:0; }
    </style>
</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, yMaxFormatter: bytesToString, yMinFormatter: bytesToString, valueTransformFunction: logarithmicScaling, minValue: 0, maxValue: 262144000, labels: {fontSize: 13}});
smoothie.streamTo(document.getElementById("traffic"), 2000);

// For coloring the servers and the chart-lines.
var colorList = d3.scale.category10();

var pendingLinks = [];
var intLinks = [];
var newLinks = [];
var links = {};
var nodes = {};

// color for links
// color for idle links
var idleColor = { "r": 85, "g": 102, "b": 255};
// links that are active
var activeColor = { "r": 0, "g": 255, "b": 0};
// links with peaking download
var peakColor = { "r": 255, "g": 0, "b": 0};

// IDs need to begin with a letter.
function makeId(prefix, text) {
    return prefix + text.replace(/\./g, "-");
}

// Log scaling for the traffic graph
function logarithmicScaling(value) {
    return Math.log(value/1000000+1);
}

function myGraph(el) {
    var changed = false;
    // to spawn new clients near already existing nodes.
    var ccX = 0, ccY = 0, ccCount = 0;

    // Add/update and remove elements on the graph object
    this.updateNode = function (id) {
        x = 1;
        y = 1;
        title = id;
        id = makeId("a", id);
        var distance = 2;
        var radius = 4;
        var color = "#0000FF"; // Blue

        // Servers look different
        var s = servers[title]
        if(s) {
            distance = 6;
            radius = 15;
        }
        distance *= w / 2000;
        var node = findNode(id);

        // Add node, if it doesn't exist
        if (!node) {
            if (ccCount != 0) {
                x = ccX / ccCount;
                y = ccY / ccCount;
            }
            nodes.push({"id":id, "title": title, "distance":distance, "radius":radius, "x":x, "y":y, "color":color});
            changed = true;
        } else {
            var visNode = vis.select("#" + id);
            if (visNode) {
                // Change color and radius if server.
                var s = servers[title];
                if (s) {
                    visNode.select("circle").attr("r", radius);
                    visNode.select("circle").style("fill", s.color);
                }
            }
            node.lifetime = 2;
        }
    }

    // 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].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);
                }
                nodes.splice(i, 1);
                changed = true;
                continue;
            }
            nodes[i].lifetime--;
        }
        for (var i = links.length - 1; i >= 0; --i) {
            if (links[i].lifetime <= 0) {
                links.splice(i, 1);
                changed = true;
                continue;
            }
            links[i].lifetime--;
        }
    }

    // Add/update links in the graph
    this.updateLink = function (edge, timestamp) {
        var sourceId = edge.source;
        var targetId = edge.target;
        var stroke = idleColor;

        // Change color to red for links between servers
        if (servers[sourceId] && servers[targetId]) stroke = "rgb(255, 0, 0)";

        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);
            // Add non existing links
            if (!link) {
                var index = $.inArray(sourceId + targetId, pendingLinks);
                if (index !== -1) {
                    links.push({"source": sourceNode, "target": targetNode, "lifetime":2, "color": stroke, "downloadRate" : 0, "lastReceived": 0, "lastTimestamp": 0, "lastUptime": 0, "colorIntensity": 0 });
                    changed = true;
                } else {
                    newLinks.push(sourceId + targetId);
                }
            } else {              
                link.lifetime = 2;

                // Calculate download rate for clients and color links.
                if (link.lastTimestamp != 0) {

                    if(timestamp - lastTime > 0) {
                        // Download rate in KB/s
                        link.downloadRate = ((edge.received - link.lastReceived) / (timestamp - lastTime)) * 4 * 1000;
                        // Clients may have multiple connections with different received data -> prevent negative download rate...
                        if(link.downloadRate < 0) link.downloadRate = 0;

                        colorLink(link);
                    }

                } else {
                    link['downloadRate'] = 0;
                }
                link['lastReceived'] = edge.received;
                link['lastTimestamp'] = timestamp;
            }
        }
    }

    // Color a link according to its intensity
    function colorLink(link) {
        // Max intensity ~ 50MB/s
        var maxIntensity = 50000;
        // Fading rate of colored links
        var fadingValue = (link.colorIntensity*0.15) + 100;
        link.colorIntensity -= fadingValue;
        if (link.colorIntensity < 0) link.colorIntensity = 0;

        link.colorIntensity += link.downloadRate;
        if (link.colorIntensity >= maxIntensity) link.colorIntensity = maxIntensity;

        var red, green, blue;
        // Green colors between 0 - 1 MB/s
        if( link.colorIntensity <= 1000 ) {
            // Blending idle (blue) and active (green) color
            var factor =  link.colorIntensity / 1000;
            var cFactor = 1 - factor;

            red = cFactor * idleColor.r + factor * activeColor.r;
            green = cFactor * idleColor.g + factor * activeColor.g;
            blue = cFactor * idleColor.b + factor * activeColor.b;

        } else { // Red over 1MB/s
            // Blending active (green) and peak (red) color
            var factor =  (link.colorIntensity - 1000)/ (maxIntensity - 1000);
            var cFactor = 1 - factor;

            red = cFactor * activeColor.r + factor * peakColor.r;
            green = cFactor * activeColor.g + factor * peakColor.g;
            blue = cFactor * activeColor.b + factor * peakColor.b;
            //console.log(" " + red + " " + " " + green + " " +  " " + blue)
        }
        //console.log(" " + red + " " + " " + green + " " +  " " + blue)
        link.color = "rgb("+ Math.floor(red) + ", " + Math.floor(green) + ", " + Math.floor(blue) + ")";
    }

    function findLink(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 findNode = function (id) {
        for (var i = 0; i < nodes.length; i++) {
            if (nodes[i].id === id)
                return nodes[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 lastW = -1, lastH = -1;

    var updateBounds = function() {
        var width = window.innerWidth;
        // Chart needs to fit under the graph and the statistics
        h = window.innerHeight - 150;
        // Width for the graph and the statistics div
        w = width/3*2;
        if (lastW === w && lastH === h) return;
        lastW = w;
        lastH = h;
        vis.attr("width", w);
        vis.attr("height", h);
        force.size([w, h]);
        $(el).attr("width", w);
        $(el).attr("height", h);
        // Positions for statistics and the traffic graph
        $("#statistics").attr("width", w/2).css("width", w/2 + "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(.12)
        // Distance of the links
        .distance(function(bla) { return 4; })
        // Strength of the links
        .linkStrength(0.3)
        // Force with which the links "pull"
        .charge(function(bla) { return -0.9; })
        // Friction for the graph nodes
        .friction(0.5)
        .size([w, h]);


    nodes = force.nodes();
    links = force.links();

    this.update = function () {
        update();
    }


    function update () {

        var link = vis.selectAll("line.link")
            .data(links, function(d) { return d.source.id + "-" + d.target.id; });

        link.enter().insert("line")
            .attr("class", "link").style({stroke: function(d) { return d.color; }});

        // change color of exitsting nodes
        link.style({stroke: function(d) { return d.color; }});

        if (changed){
            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) {
                    // 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 traffic 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;
                });

            nodeEnter.append("circle")
                .attr("class", "circle")
                .attr("r", function(d) { return d.radius; })
                .style("fill", function(d) { return d.color; });

            nodeEnter.append("text")
                .attr("class", "nodetext")
                .attr("dx", -32)
                .attr("dy", "-1em")
                .text(function(d) {return d.title});

            node.exit().remove();

            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 = 1000000, ly = 1000000, ux = -1000000, uy = -1000000;
                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) {
                    if (ccCount > 10) {
                      ccX = ccY = ccCount = 0;
                    }
                    ccCount++;
                    ccX += d.x;
                    ccY += d.y;
                    return "translate(" + fixX(d.x) + "," + fixY(d.y) + ")";
              });
            };

            force.on("tick", render);

            updateBounds();
            force.start();
        }
    }


    window.onresize = forceUpdate;

    function forceUpdate () {
        changed = true;
        update();
    }
    forceUpdate();
}

graph = new myGraph("#graph");


var servers = {};
var serverCount = 0;
var lastTime = 0;

// Get new data
setInterval( function() {
    $.get('/data.json', function(data) {
        if (data.timestamp < lastTime) lastTime = data.timestamp;
        if(data.timestamp === lastTime) return;
        var g = data.graph;
        var stats = data.servers;
        updateGraph(g, data);
        // updateTrafficGraph has to be called before updateTextStatistics to populate servers
        updateTrafficGraph(stats, data);
        updateTextStatistics(stats);
        lastTime = data.timestamp;
}, '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.updateNode(g.nodes[i].name);
        }
        for (var i = 0; i < g.edges.length; ++i) {
            graph.updateLink(g.edges[i], data.timestamp);
        }
        pendingLinks = intLinks;
        intLinks = newLinks;
        newLinks = [];
    }
}



// Convert bytes to GiB or TiB and return a string in form "10,23 GiB"
function bytesToString( bytes ) {
    var convertedValue;
    var unit;
    if (bytes >= 1099511627776 ) {
        convertedValue = Math.round( (bytes / 1099511627776) * 100 ) / 100 ;
        unit = " TiB";
    } else if (bytes >= 1073741824 ) {
        convertedValue = Math.round( (bytes / 1073741824) * 100 ) / 100 ;
        unit = " GiB";
    } else if (bytes >= 1048576 ) {
        convertedValue = Math.round( (bytes / 1048576) * 100 ) / 100 ;
        unit = " MiB";
    } else if ( bytes >= 1024 ) {
        convertedValue = Math.round( (bytes / 1024) * 100 ) / 100 ;
        unit = " KiB";
    } else {
        convertedValue = Math.round(bytes);
        unit = " B";
    }
    return convertedValue + unit;
}

// Update data of the statistics divs
function updateTextStatistics(stats){
    if (stats) {
        for (var i = 0; i < stats.length; ++i) {
            var divId = makeId("b", stats[i].address);
            var server = $('#' + divId);
            if (server.length == 0){
                    $("#statistics").append("<div id=" + makeId ("b", stats[i].address) + "></div>");
                    server = $("#" + divId);
            }

            var upload = servers[stats[i].address].uploadRate;
            upload = upload ? upload : 0;
            // Generate the HTML string
            server.html( "<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: " + bytesToString(stats[i].bytesSent)  + "<br>"
                + "Received: " + bytesToString(stats[i].bytesReceived) + "<br>"
                + "Upload: " + bytesToString(servers[stats[i].address].uploadRate) + "/s<br>"
                + "Upload Peak: " + bytesToString(servers[stats[i].address].uploadPeak) + "/s" + "</p>"
                );
        }
    }
}


// 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 = { 'lastUptime': 0, 'lastTimestamp': 0, 'lastSent': 0, 'line': new TimeSeries({logarithmicScale: true}), 'index': serverCount++ , 'uploadRate': 0, 'downloadRate': 0, 'uploadPeak': 0 }
                server.color = colorList(stats[i].address);
                smoothie.addTimeSeries(server['line'], {lineWidth:2, strokeStyle: server.color});
            }
            // Server seems to have rebootet, redo values but add no point to chart.
            if (server['lastUptime'] > stats[i].uptime) {
                server['lastUptime'] = 0;
            }

            // Add points to graph and set the upload rate for the statistics
            if (server['lastUptime'] != 0) {
                // Upload rate in bytes/s
                server['uploadRate'] = (stats[i].bytesSent - server['lastSent'])/(stats[i].timestamp - server.lastTimestamp) * 1000;
                if (server['uploadRate'] > server['uploadPeak']) server['uploadPeak'] = server['uploadRate'];
                // Rate in MiB/s
                server['line'].append(new Date().getTime(), server['uploadRate']);
            } else {
                server['uploadRate'] = 0;
            }

            server['lastUptime'] = stats[i].uptime;
            server['lastSent'] = stats[i].bytesSent;
            server['lastTimestamp'] = stats[i].timestamp;
        }
    }
}

</script>