diff options
author | Kristian Lyngstol <kristian@bohemians.org> | 2015-04-04 01:51:04 +0200 |
---|---|---|
committer | Kristian Lyngstol <kristian@bohemians.org> | 2015-04-04 01:51:04 +0200 |
commit | 694d52bc57adc19ee13047dce213d469d07070aa (patch) | |
tree | bb87c654757b2ce5c89e98b1efaa0a874cb4a43c | |
parent | f496d5413912e461eb7a4deb34e2f770310e37d4 (diff) |
NMS2: Working map
-rw-r--r-- | web/nms.gathering.org/nms2/map.html | 126 | ||||
-rw-r--r-- | web/nms.gathering.org/nms2/map.js | 433 |
2 files changed, 559 insertions, 0 deletions
diff --git a/web/nms.gathering.org/nms2/map.html b/web/nms.gathering.org/nms2/map.html new file mode 100644 index 0000000..ec4ce43 --- /dev/null +++ b/web/nms.gathering.org/nms2/map.html @@ -0,0 +1,126 @@ +<!DOCTYPE html> +<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <meta charset="utf-8"> + <title>NMS2</title> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <meta name="description" content=""> + <meta name="author" content=""> + + <script src="js/jquery-1.10.2.min.js"></script> + <!--<script src="http://cdn.jsdelivr.net/qtip2/2.2.1/jquery.qtip.min.js"></script>--> + + <link href="css/bootstrap.min.css" rel="stylesheet"> + <link href="css/bootstrap-responsive.min.css" rel="stylesheet"> + <link href="css/slider.css" rel="stylesheet"> + <!--<link src="http://cdn.jsdelivr.net/qtip2/2.2.1/jquery.qtip.min.css" rel="stylesheet">--> + <style type="text/css"> + body { + padding-top: 60px; + padding-bottom: 40px; + } + .sidebar-nav { + padding: 9px 0; + } + + #canvas { + background-image: url('http://nms.tg15.gathering.org/tg15-salkart.png'); + } + + #tooltip { + position: absolute; + left: 110px; + top: 110px; + border: 1px solid #FF0000; + } + </style> + + <!-- HTML5 shim, for IE6-8 support of HTML5 elements --> + <!--[if lt IE 9]> + <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> + <![endif]--> + + <style type="text/css"></style></head> + <body> + <div class="navbar navbar-inverse navbar-fixed-top"> + <div class="navbar-inner"> + <div class="container-fluid"> + <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> + <span class="icon-bar"> + </span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </a> + <div class="nav-collapse collapse"> + <p class="navbar-text pull-right" id="speed">Foobar</p> + <ul class="nav"> + <li><a href="#uplink" onclick="setUpdater(handler_uplinks)" >Uplink map</a></li> + <li><a href="#temp" onclick="setUpdater(handler_temp)" >Temperature map</a></li> + <li><a href="#ping" onclick="setUpdater(handler_ping)" >Ping map</a></li> + </ul> + <p id="info-content" class="navbar-text"> </p> + <p id="info-switch" class="navbar-text pull-left"> </p> + </div><!--/.nav-collapse --> + </div> + </div> + </div> + + <div class="container-fluid"> + <div class="row-fluid"> + <div class="span12"> + <link rel="stylesheet" href="/ping.css"> + <p id="playground"> + <svg id="lines" width="1920" height="1032" style="position: absolute; top: 0; left: 0; z-index: 1"> + </svg> + + <img src="/tg15-salkart.png" alt="" id="map" /> + </p> + </div> + </div> + <div class="row-fluid"> + <div class="span12"> + <div id="info-legend-box"> + <button id="legend-1"> </button> + <button id="legend-2"> </button> + <button id="legend-3"> </button> + <button id="legend-4"> </button> + <button id="legend-5"> </button> + </div> + <div id="info-switch-parent"> + <table class="table" id="info-switch-table"> + </table> + </div> + </div> + </div><!--/row--> + </div><!--/.fluid-container--> + </div> + <script src="js/bootstrap.min.js"></script> + <script src="js/nms2-lib.js"></script> + <script src="js/nms2.js"></script> + <script> + // These are used by ping.js, below. + var draw_linknets = true; + var can_edit = false; + </script> + <script type="text/javascript" src="map.js"></script> + <script> + updatePorts(); + updatePing(); + setInterval(updatePorts,5000); + setInterval(updateInfo,5000); + setInterval(updatePing,1000); + setInterval(updateMap,1000); + setInterval(updateSpeed,3000); + url = document.URL; + if (/#ping/.exec(url)) { + setUpdater(handler_ping); + }else if (/#uplink/.exec(url)) { + setUpdater(handler_uplinks); + } else if (/#temp/.exec(url)) { + setUpdater(handler_temp); + } else { + setUpdater(handler_ping); + } + </script> + </body> +</html> diff --git a/web/nms.gathering.org/nms2/map.js b/web/nms.gathering.org/nms2/map.js new file mode 100644 index 0000000..a51f03d --- /dev/null +++ b/web/nms.gathering.org/nms2/map.js @@ -0,0 +1,433 @@ +var nms = { + updater:undefined, // Active updater + switches_now:undefined, // Most recent data + switches_then:undefined, // 2 minutes old + speed:0, // Current aggregated speed + full_speed:false, // Set to 'true' to include ALL interfaces + ping_data:undefined, + globalmap:[], // DOM objects for switches + drawn:false, // Set to 'true' when switches are drawn + did_update:false // Set to 'true' after we've done some basic updating +}; + +/* + * Handlers. "updater" is run periodically when the handler is active, and + * "init" is run once when it's activated. + */ + +var handler_uplinks = { + updater:uplinkUpdater, + init:uplinkInit +}; + +var handler_temp = { + updater:tempUpdater, + init:tempInit +}; + +var handler_ping = { + updater:pingUpdater, + init:pingInit +}; + +/* + * Display info on switch "x" in the info-box + */ +function switchInfo(x) +{ + var switchele = document.getElementById("info-switch-table"); + var sw = nms.switches_now["switches"][x]; + var swtop = document.getElementById("info-switch-parent"); + var tr; + var td1; + var td2; + + swtop.removeChild(switchele); + + switchele = document.createElement("table"); + switchele.id = "info-switch-table"; + switchele.style.zIndex = 100; + switchele.className = "table table-bordered"; + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Sysname"; + td2.innerHTML = x; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Management IP"; + td2.innerHTML = sw["management"]["ip"]; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Temperature"; + td2.innerHTML = sw["temp"]; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Temperature age"; + td2.innerHTML = sw["temp_time"]; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Type"; + td2.innerHTML = sw["switchtype"]; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Last Updated"; + td2.innerHTML = sw["management"]["last_updated"]; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + tr = document.createElement("tr"); td1 = document.createElement("td"); td2 = document.createElement("td"); + td1.innerHTML = "Poll frequency"; + td2.innerHTML = sw["management"]["poll_frequency"]; + tr.appendChild(td1); tr.appendChild(td2); switchele.appendChild(tr); + + swtop.appendChild(switchele); +} + +/* + * Draw/create a specific switch + */ +function createSwitch(sysname, x, y, zorder, width, height) +{ + var s = document.createElement("div"); + var map = document.getElementById('map'); + var top_offset = map.getBoundingClientRect().top; + var left_offset = map.getBoundingClientRect().left; + + var onclick = "console.log(\"foo\");"; + s.style.position = 'absolute'; + s.style.left = (left_offset + x) + 'px'; + s.style.top = (top_offset + y) + 'px'; + s.style.width = width + 'px'; + s.style.height = height + 'px'; + s.style.backgroundColor = '#0000ff'; + s.style.border = '1px solid black'; + s.style.padding = "0"; + s.style.zIndex = zorder + 100; + s.addEventListener("click", function(){ switchInfo(sysname); }); + s.title = sysname + " - " + nms.switches_now["switches"][sysname]["management"]["ip"]; + nms.globalmap[sysname] = s; + + var span = document.createElement("div"); + span.className = "switchname"; + if (width < 1.5 * height) { + span.className = "switchname rot"; + } + span.style.border = "0"; + span.style.padding = "0"; + s.appendChild(span); + + var text = document.createTextNode(sysname); + span.appendChild(text); + + s.setAttribute("data-sysname", sysname); + + document.body.appendChild(s); +} + +/* + * Draw/add switches. + */ +function drawSwitches() +{ + for (var sysname in nms.switches_now["switches"]) { + var s = nms.switches_now['switches'][sysname]["placement"]; + createSwitch(sysname, + parseInt(s['x']), + parseInt(s['y']), + parseInt(s['zorder']), + parseInt(s['width']), + parseInt(s['height'])); + } + nms.drawn = true; +} + +/* + * Update various info elements periodically. + */ +function updateInfo() +{ + if (!nms.drawn && nms.switches_now != undefined) { + drawSwitches(); + } + var speedele = document.getElementById("speed"); + speedele.innerHTML = (8 * parseInt(nms.speed) / 1024 / 1024 / 1024 ).toPrecision(5) + " Gbit/s"; +} + +/* + * Short hand for setting color on a switch in the map + */ +function colorSwitch(sysname, color) +{ + nms.globalmap[sysname].style.background = color; +} + +/* + * Update function for uplink map + * Run periodically when uplink map is active. + */ +function uplinkUpdater() +{ + for (sw in nms.switches_now["switches"]) { + var uplinks=0; + for (port in nms.switches_now["switches"][sw]["ports"]) { + if (/ge-0\/0\/44$/.exec(port) || + /ge-0\/0\/45$/.exec(port) || + /ge-0\/0\/46$/.exec(port) || + /ge-0\/0\/47$/.exec(port)) + { + if (parseInt(nms.switches_then["switches"][sw]["ports"][port]["ifhcoutoctets"]) != parseInt(nms.switches_now["switches"][sw]["ports"][port]["ifhcoutoctets"])) { + uplinks += 1; + } + } + } + if (uplinks == 0) { + colorSwitch(sw,"blue"); + } else if (uplinks == 1) { + colorSwitch(sw,"red"); + } else if (uplinks == 2) { + colorSwitch(sw, "yellow"); + } else if (uplinks == 3) { + colorSwitch(sw, "green"); + } else if (uplinks > 3) { + colorSwitch(sw, "white"); + } + } +} + +/* + * Init-function for uplink map + */ +function uplinkInit() +{ + setLegend(1,"blue","0 uplinks"); + setLegend(2,"red","1 uplink"); + setLegend(3,"yellow","2 uplinks"); + setLegend(4,"green","3 uplinks"); + setLegend(5,"white","4 uplinks"); +} + +function rgb_from_max(x) +{ + x = x/100; + var colorred = 255 * x; + var colorblue = 255 - colorred; + + return 'rgb(' + Math.round(colorred) + ", 0, " + Math.round(colorblue) + ')'; +} + +function temp_color(t) +{ + t = Math.round((t / 60) * 100); + return rgb_from_max(t); +} + +/* + * There are 4 legend-bars. This is a helper-function to set the color and + * description/name for each one. Used from handler init-functions. + */ +function setLegend(x,color,name) +{ + var el = document.getElementById("legend-" + x); + el.style.background = color; + el.innerHTML = name; +} + +function tempUpdater() +{ + for (sw in nms.switches_now["switches"]) { + var t = nms.switches_now["switches"][sw]["temp"]; + + colorSwitch(sw, temp_color(t)); + } +} + +function tempInit() +{ + setLegend(1,temp_color(10),"10 °C"); + setLegend(2,temp_color(20),"20 °C"); + setLegend(3,temp_color(30),"30 °C"); + setLegend(4,temp_color(40),"40 °C"); + setLegend(5,temp_color(50),"50 °C"); +} + +function gradient_from_latency(latency_ms, latency_secondary_ms) +{ + if (latency_secondary_ms === undefined) { + return rgb_from_latency(latency_ms); + } + return 'linear-gradient(' + + rgb_from_latency(latency_ms) + ', ' + + rgb_from_latency(latency_secondary_ms) + ')'; +} + +function rgb_from_latency(latency_ms) +{ + if (latency_ms === null || latency_ms === undefined) { + return '#0000ff'; + } + + var l = latency_ms / 40.0; + if (l >= 2.0) { + return 'rgb(255, 0, 0)'; + } else if (l >= 1.0) { + l = 2.0 - l; + l = Math.pow(l, 1.0/2.2); + l = Math.round(l * 255.0); + return 'rgb(255, ' + l + ', 0)'; + } else { + l = Math.pow(l, 1.0/2.2); + l = Math.round(l * 255.0); + return 'rgb(' + l + ', 255, 0)'; + } +} + +function pingUpdater() +{ + for (var sw in nms.ping_data["switches"]) { + colorSwitch(sw, gradient_from_latency(nms.ping_data["switches"][sw]["latency"])); + } +} + +function pingInit() +{ + setLegend(1,gradient_from_latency(1),"1ms"); + setLegend(2,gradient_from_latency(30),"30ms"); + setLegend(3,gradient_from_latency(60),"60ms"); + setLegend(4,gradient_from_latency(80),"80ms"); + setLegend(5,"#0000ff" ,"No response"); +} + +/* + * Run periodically to trigger map updates when a handler is active + */ +function updateMap() +{ + if (nms.updater != undefined) { + nms.updater(); + } +} + +/* + * Change map handler (e.g., change from uplink map to ping map) + */ +function setUpdater(fo) +{ + nms.updater = undefined; + fo.init(); + nms.updater = fo.updater; + initialUpdate(); + if (nms.ping_data && nms.switches_then && nms.switches_now) { + nms.updater(); + } +} + + +/* + * Convenience function to avoid waiting for pollers when data is available + * for the first time. + */ +function initialUpdate() +{ + if (nms.ping_data && nms.switches_then && nms.switches_now && nms.updater != undefined && nms.did_update == false ) { + if (!nms.drawn) { + drawSwitches(); + } + nms.updater(); + nms.did_update = true; + } +} + +/* + * Update nms.ping_data + */ +function updatePing() +{ + $.ajax({ + type: "GET", + url: "/ping-json2.pl", + dataType: "text", + success: function (data, textStatus, jqXHR) { + nms.ping_data = JSON.parse(data); + initialUpdate(); + } + }); +} + +/* + * Update nms.switches_now and nms.switches_then + */ +function updatePorts() +{ + $.ajax({ + type: "GET", + url: "/port-state.pl", + dataType: "text", + success: function (data, textStatus, jqXHR) { + var switchdata = JSON.parse(data); + nms.switches_now = switchdata; + initialUpdate(); + } + }); + $.ajax({ + type: "GET", + url: "/port-state.pl?time=5m", + dataType: "text", + success: function (data, textStatus, jqXHR) { + var switchdata = JSON.parse(data); + nms.switches_then = switchdata; + initialUpdate(); + } + }) +} + +/* + * Use nms.switches_now and nms.switches_then to update 'nms.speed'. + * + * nms.speed is a total of ifHCInOctets across all interfaces. + * + * if nms.full_speed is true: Include ALL interfaces + * if nms.full_speed is false: Include only e* switches and exclude + * uplinks. + */ +function updateSpeed() +{ + var speed_in = parseInt(0); + var speed_kant = parseInt(0); + var counter=0; + var sw; + for (sw in nms.switches_now["switches"]) { + for (port in nms.switches_now["switches"][sw]["ports"]) { + if (!nms.switches_now["switches"][sw]["ports"][port]) { + console.log("ops"); + continue; + } + if (!nms.switches_then["switches"][sw]["ports"][port]) { + console.log("ops"); + continue; + } + var diff = parseInt(parseInt(nms.switches_now["switches"][sw]["ports"][port]["time"]) - parseInt(nms.switches_then["switches"][sw]["ports"][port]["time"])); + var then = parseInt(nms.switches_then["switches"][sw]["ports"][port]["ifhcinoctets"]) ; + var now = parseInt(nms.switches_now["switches"][sw]["ports"][port]["ifhcinoctets"]) ; + var diffval = (now - then); + if (then == 0 || now == 0 || diffval == 0 || diffval == NaN) { + continue; + } + if (nms.full_speed || (( /e\d-\d/.exec(sw) || /e\d\d-\d/.exec(sw)) && ( /ge-\d\/\d\/\d$/.exec(port) || /ge-\d\/\d\/\d\d$/.exec(port)))) { + if (nms.full_speed || !( + /ge-0\/0\/44$/.exec(port) || + /ge-0\/0\/45$/.exec(port) || + /ge-0\/0\/46$/.exec(port) || + /ge-0\/0\/47$/.exec(port))) { + speed_in += parseInt(diffval/diff) ; + counter++; + } + } + //speed_in += parseInt(diffval/diff) / 1024 ; + } + } + nms.speed = speed_in; +} |