diff options
Diffstat (limited to 'web/js')
-rw-r--r-- | web/js/nms-admin-pane.js | 19 | ||||
-rw-r--r-- | web/js/nms-color-util.js | 48 | ||||
-rw-r--r-- | web/js/nms-data.js | 3 | ||||
-rw-r--r-- | web/js/nms-info-box.js | 298 | ||||
-rw-r--r-- | web/js/nms-map-handlers.js | 336 | ||||
-rw-r--r-- | web/js/nms-map.js | 34 | ||||
-rw-r--r-- | web/js/nms-oplog.js | 48 | ||||
-rw-r--r-- | web/js/nms-search.js | 159 | ||||
-rw-r--r-- | web/js/nms-time.js | 150 | ||||
-rw-r--r-- | web/js/nms.js | 364 |
10 files changed, 821 insertions, 638 deletions
diff --git a/web/js/nms-admin-pane.js b/web/js/nms-admin-pane.js index 9edf9f9..235da8d 100644 --- a/web/js/nms-admin-pane.js +++ b/web/js/nms-admin-pane.js @@ -78,3 +78,22 @@ nmsAdmin.updateConfigPane = function() { } } } + +nmsAdmin.addLinknet = function() { + var myData = { + "switch1": document.getElementById("admin-input-linknet1").value, + "switch2": document.getElementById("admin-input-linknet2").value + }; + myData = JSON.stringify(myData); + $.ajax({ + type: "POST", + url: "/api/write/linknet-add", + dataType: "text", + data:myData, + success: function (data, textStatus, jqXHR) { + nmsData.invalidate("switches"); + document.getElementById("admin-input-linknet1").value = ""; + document.getElementById("admin-input-linknet2").value = ""; + } + }); +} diff --git a/web/js/nms-color-util.js b/web/js/nms-color-util.js index 6b5a4b5..ec7f619 100644 --- a/web/js/nms-color-util.js +++ b/web/js/nms-color-util.js @@ -1,34 +1,31 @@ - +"use strict"; /* * Some stolen colors that look OK. * * PS: Stolen from boostrap, because we use bootstrap and these look good * and match. */ -var lightblue = "#d9edf7"; -var lightgreen = "#dff0d8"; -var lightred = "#f2dede"; -var lightorange = "#fcf8e3"; -var blue = "#337ab7"; -var green = "#5cb85c"; -var teal = "#5bc0de"; // Or whatever the hell that is -var orange = "#f0ad4e"; -var red = "#d9534f"; -var white = "#ffffff"; -function gradient_from_latency(latency_ms, latency_secondary_ms) -{ - if (latency_ms == undefined) - return blue; - return getColorStop(parseInt(latency_ms) * 10); +var nmsColor = nmsColor || { + _cache: [], + lightblue: "#d9edf7", + lightgreen: "#dff0d8", + lightred: "#f2dede", + lightorange: "#fcf8e3", + blue: "#337ab7", + green: "#5cb85c", + teal: "#5bc0de", + orange: "#f0ad4e", + red: "#d9534f", + white: "#ffffff" } /* * Return a random-ish color (for testing) */ -function getRandomColor() +nmsColor.random = function() { - var colors = [ "white", red, teal, orange, green, blue ]; + var colors = [ "white", nmsColor.red, nmsColor.teal, nmsColor.orange, nmsColor.green, nmsColor.blue ]; var i = Math.round(Math.random() * (colors.length-1)); return colors[i]; } @@ -47,11 +44,11 @@ function getRandomColor() * resize for the moment, because this canvas is also re-sized (which isn't * really necessary, but avoids special handling). */ -function drawGradient(gradients) -{ +nmsColor.drawGradient = function(gradients) { var ctx = nmsMap._c.hidden.ctx; // FIXME: Move it away... var gradient = ctx.createLinearGradient(0,0,1000,0); var stops = gradients.length - 1; + nmsColor._cache = []; nms.gradients = gradients; for (var color in gradients) { var i = color / stops; @@ -70,24 +67,27 @@ function drawGradient(gradients) /* * Get the color of a gradient, range is from 0 to 999 (inclusive). */ -function getColorStop(x) { +nmsColor.getColorStop = function(x) { x = parseInt(x); if (x > 999) x = 999; if (x < 0) x = 0; - return getColor(x,0); + return nmsColor._getColor(x,0); } /* * Get the color on the hidden canvas at a specific point. Could easily be * made generic. */ -function getColor(x,y) { +nmsColor._getColor = function(x,y) { + if (nmsColor._cache[x] != undefined) + return nmsColor._cache[x]; var ctx = nmsMap._c.hidden.ctx; // FIXME: Move it away... var imageData = ctx.getImageData(x, y, 1, 1); var data = imageData.data; if (data.length < 4) return false; - return 'rgb(' + data[0] + ',' + data[1] + ',' + data[2] + ')'; + nmsColor._cache[x] = 'rgb(' + data[0] + ',' + data[1] + ',' + data[2] + ')'; + return nmsColor._cache[x]; } diff --git a/web/js/nms-data.js b/web/js/nms-data.js index 14e5fed..e3c4106 100644 --- a/web/js/nms-data.js +++ b/web/js/nms-data.js @@ -231,7 +231,8 @@ nmsData._genericUpdater = function(name, cacheok) { dataType: "json", success: function (data, textStatus, jqXHR) { if (nmsData[name] == undefined || nmsData[name]['hash'] != data['hash']) { - nmsData._last = data['time']; + if (name == "ping") + nmsData._last = data['time']; nmsData.old[name] = nmsData[name]; nmsData[name] = data; nmsMap.drawNow(); diff --git a/web/js/nms-info-box.js b/web/js/nms-info-box.js index e6817dc..cd5594e 100644 --- a/web/js/nms-info-box.js +++ b/web/js/nms-info-box.js @@ -13,15 +13,11 @@ /* * * Currently broken or needs reimplementing: - * - Possibly: Comment CRUD, did not want to test writes on our "historic db copy" - * - Comments popover * - Handler unloading is not working correctly, and many are never removed * - SSH-management link, this should propably be a custom "view" of sorts - * - inventoryListing window is partially broken when first opened. Since it's - * both a window type and a panel with different modes it has some conflicts. * * General TODO: - * - Fix broken stuff + * - Move inventory into a separate tab. * - Add external windows (timetravel, etc) * - Take a critical look at what methods/variables should be marked as "_" * - Currently argument is assumed to be a switch, this should not be the case @@ -44,13 +40,9 @@ var nmsInfoBox = nmsInfoBox || { 'title': 'Switch info', 'views': { 'initial': { - 'name': 'Switch summary', + 'name': 'Summary', 'panels': ['switchSummary','switchComments'] }, - 'details': { - 'name': 'Switch details', - 'panels': ['switchDetails'] - }, 'ports': { 'name': 'SNMP - Ports', 'panels': ['switchSNMP:ports'] @@ -59,9 +51,9 @@ var nmsInfoBox = nmsInfoBox || { 'name': 'SNMP - Misc', 'panels': ['switchSNMP:misc'] }, - 'comments': { - 'name': 'Comments', - 'panels': ['switchComments'] + 'details': { + 'name': 'Settings', + 'panels': ['switchDetails'] }, 'edit': { 'name': 'Edit', @@ -176,8 +168,10 @@ nmsInfoBox.addPanelType = function (id, obj) { */ nmsInfoBox.hide = function() { this._sw = false; - this._windowHandler.hide(); - this._windowHandler.unloadWindow(); + if (this._windowHandler != undefined && this._windowHandler.hide != undefined) { + this._windowHandler.hide(); + this._windowHandler.unloadWindow(); + } }; /* @@ -194,7 +188,7 @@ nmsInfoBox.click = function(sw) * Is based on a hierarchy of objects: Window (itself) > Views > Panels. Where * any object should not interact with any other directly. Panels are special * nmsInfoPanel-objects that handle their own rendering, refreshing, etc. The - * window handler only makes shure these panels are loaded and unloaded when + * window handler only makes sure these panels are loaded and unloaded when * needed in a window or view. * * Does primarily rely on an imported list of panel types and window types to @@ -590,13 +584,7 @@ var searchHelpPanel = function() { nmsInfoPanel.call(this,"searchHelp"); this.refresh = function(reason) { var x = document.createElement("div"); - var searchHelp = [ - "The search box can be used to identify switches in several ways. The simplest is by name.", - "If you are using the non-public version of Gondul, you can also perform smart searches.", - "Distro search: Type the name of a distro-switch and all access switches registered to that distro switch will also be hilighted.", - 'Active ports: Type "active>x", "active<x" or "active=x" to identify switch with "x" amount of active gigabit ethernet (ge) ports. E.g.: "active>30".', - 'IP search: Start typing an IP and any switch with that IP registered either as management IP or part of its subnet will be identified', - 'SNMP search: Type anything found in the "sysDescr" SNMP OID to hilight a switch matching that. Practical examples include version numbers for firmware (e.g.: "JUNOS 12." vs "JUNOS 14.").']; + var searchHelp = nmsSearch.helpText; for (var a in searchHelp) { var c = document.createElement("p"); c.innerText = searchHelp[a]; @@ -704,7 +692,7 @@ var inventoryListingPanel = function() { for(var sw in nmsData.switches.switches) { var value = ''; if(this.filter != '') { - if(sw.toLowerCase().indexOf(this.filter) == -1 && !nmsInfoBox._searchSmart(this.filter,sw)) + if(sw.toLowerCase().indexOf(this.filter) == -1 && !nmsSearch.searchTest(this.filter,sw)) continue; } try { @@ -774,7 +762,7 @@ var switchEditPanel = function () { var tmpsw = '\'' + this.sw + '\''; var tmpv = '\'' + v + '\''; var tmphandler = '"nmsInfoBox._editChange(' + tmpsw + ',' + tmpv + ');"'; - var html = '<input type="text" class="form-control" value="' + template[v] + '" id="edit-' + this.sw + '-' + v + '" onchange=' + tmphandler + ' oninput=' + tmphandler + '>'; + var html = '<input type="text" class="form-control" value="' + template[v] + '" id="edit-' + this.sw + '-' + v + '" onchange=' + tmphandler + ' oninput=' + tmphandler + ' ' + (v == 'sysname' ? "readonly" : "") + '>'; content.push([v, html]); } @@ -794,6 +782,7 @@ var switchEditPanel = function () { output.id = "edit-output"; domObj.appendChild(output); + this._render(domObj); if (place) { var pval = document.getElementById("edit-" + this.sw + "-placement"); if (pval) { @@ -801,7 +790,6 @@ var switchEditPanel = function () { } } - this._render(domObj); }; this.save = function () { var myData = nmsInfoBox._editStringify(this.sw); @@ -828,8 +816,6 @@ nmsInfoBox.addPanelType("switchEdit",switchEditPanel); * * Displays the current comments and lets you interact with them or add new ones * - * TODO: Test with a dummy-db to make sure everything still works properly - * */ var switchCommentsPanel = function () { nmsInfoPanel.call(this,"switchComments"); @@ -837,28 +823,25 @@ var switchCommentsPanel = function () { this.refresh = function (reason) { var domObj = document.createElement("div"); var comments = []; - var logs = nmsOplog.getSwitchLogs(this.sw); - // We have data - var table = document.createElement("table"); - var tr; - var td1; - var td2; - var td3; - table.className = "table"; - table.classList.add("table"); - table.classList.add("table-condensed"); - for (var v in logs) { - tr = table.insertRow(-1); - tr.className = - td1 = tr.insertCell(0); - td2 = tr.insertCell(1); - td1.textContent = logs[v]['timestamp']; - td2.textContent = "[" + logs[v]['username'] + "] " + logs[v]['log']; - } - domObj.appendChild(table); - - + var table = document.createElement("table"); + var tr; + var td1; + var td2; + var td3; + table.className = "table"; + table.classList.add("table"); + table.classList.add("table-condensed"); + for (var v in logs) { + tr = table.insertRow(-1); + tr.className = + td1 = tr.insertCell(0); + td2 = tr.insertCell(1); + var date = new Date(logs[v]['timestamp']); + td1.textContent = date.toString(); + td2.textContent = "[" + logs[v]['username'] + "] " + logs[v]['log']; + } + domObj.appendChild(table); this._render(domObj); }; }; @@ -878,97 +861,14 @@ var switchSummaryPanel = function() { }; this.refresh = function(reason) { var content = []; - - //Get DHCP info - var lastDhcp = undefined; - try { - var tempDhcp = nmsData.dhcp.dhcp[this.sw]; - var now = Date.now(); - now = Math.floor(now / 1000); - tempDhcp = now - parseInt(tempDhcp); - tempDhcp = tempDhcp + " s"; - } catch(e) {} - - //Get SNMP status - var snmpStatus = undefined; - try { - if (nmsData.snmp.snmp[this.sw].misc.sysName[0] != sw) { - snmpStatus = "Sysname mismatch"; - } else { - snmpStatus = "OK"; - } - } catch(e) {} - - //Get CPU usage - var cpuUsage = undefined; - try { - var cpu = 0; - for (var u in nmsData.snmp.snmp[this.sw].misc.jnxOperatingCPU) { - var local = nmsData.snmp.snmp[this.sw].misc['jnxOperatingCPU'][u]; - cpu = Math.max(nmsData.snmp.snmp[this.sw].misc.jnxOperatingCPU[u],cpu); - } - cpuUsage = cpu + " %"; - } catch (e) {} - - //Get traffic data - var uplinkTraffic = undefined; - try { - var speed = 0; - var t = parseInt(nmsData.switchstate.then[this.sw].uplinks.ifHCOutOctets); - var n = parseInt(nmsData.switchstate.switches[this.sw].uplinks.ifHCOutOctets); - var tt = parseInt(nmsData.switchstate.then[this.sw].time); - var nt = parseInt(nmsData.switchstate.switches[this.sw].time); - var tdiff = nt - tt; - var diff = n - t; - speed = diff / tdiff; - if(!isNaN(speed)) { - uplinkTraffic = byteCount(speed*8,0); + for ( var h in handlers ) { + if (handlers[h].getInfo != undefined) { + var tmp = handlers[h].getInfo(this.sw); + for (var x in tmp.data) { + content.push([tmp.data[x].description, tmp.data[x].value]); + } } - } catch (e) {}; - - //Get uptime data - var uptime = ""; - try { - uptime = nmsData.snmp.snmp[this.sw]["misc"]["sysUpTimeInstance"][""] / 60 / 60 / 100; - uptime = Math.floor(uptime) + " t"; - } catch(e) {} - - //Get temperature data - var temp = ""; - try { - temp = nmsData.switchstate.switches[this.sw].temp + " °C"; - } catch(e) {} - - //Get management data - var mgmtV4 = ""; - var mgmtV6 = ""; - var subnetV4 = ""; - var subnetV6 = ""; - try { - mgmtV4 = nmsData.smanagement.switches[this.sw].mgmt_v4_addr; - mgmtV6 = nmsData.smanagement.switches[this.sw].mgmt_v6_addr; - subnetV4 = nmsData.smanagement.switches[this.sw].subnet4; - subnetV6 = nmsData.smanagement.switches[this.sw].subnet6; - } catch(e) {} - - //Get ping data - var ping = undefined; - try { - nmsData.ping.switches[this.sw].latency + " ms"; - } catch (e) {} - - - content.push(["Ping latency:",ping]); - content.push(["Last DHCP lease:",lastDhcp]); - content.push(["SNMP status:",snmpStatus]); - content.push(["CPU usage:",cpuUsage]); - content.push(["Uplink traffic:",uplinkTraffic]); - content.push(["System uptime:",uptime]); - content.push(["Temperature",temp]); - content.push(["Management (v4):",mgmtV4]); - content.push(["Management (v6):",mgmtV6]); - content.push(["Subnet (v4):",subnetV4]); - content.push(["Subnet (v6):",subnetV6]); + } var contentCleaned = []; for(var i in content) { @@ -978,7 +878,6 @@ var switchSummaryPanel = function() { content[i][1] == "No data"; contentCleaned.push(content[i]); } - var table = nmsInfoBox._makeTable(contentCleaned); this._render(table); @@ -1008,7 +907,7 @@ nmsInfoBox._makeTable = function(content, caption) { } for (var v in content) { tr = table.insertRow(-1); - tr.className = content[v][0].toLowerCase(); + tr.className = content[v][0].toLowerCase().replace(/[^a-z0-9_]/g,""); td1 = tr.insertCell(0); td2 = tr.insertCell(1); td1.innerHTML = content[v][0]; @@ -1017,123 +916,6 @@ nmsInfoBox._makeTable = function(content, caption) { return table; }; -nmsInfoBox.searchSmart = function(id, sw) { - if (id == "") - return false; - return nmsInfoBox._searchSmart(id, sw); -} - -nmsInfoBox._searchSmart = function(id, sw) { - try { - if(sw.toLowerCase().indexOf(id) > -1) { - return true; - } - try { - if (nmsData.switches.switches[sw].distro_name.toLowerCase() == id) { - return true; - } - } catch (e) {} - try { - if (id.match("active")) { - var limit = id; - limit = limit.replace("active>",""); - limit = limit.replace("active<",""); - limit = limit.replace("active=",""); - var operator = id.replace("active","")[0]; - if (limit == parseInt(limit)) { - var ports = parseInt(nmsData.switchstate.switches[sw].ifs.ge.live); - limit = parseInt(limit); - if (operator == ">" ) { - if (ports > limit) { - return true; - } - } else if (operator == "<") { - if (ports < limit) { - return true; - } - } else if (operator == "=") { - if (ports == limit) { - return true; - } - } - } - } - } catch (e) {} - try { - if (nmsData.smanagement.switches[sw].mgmt_v4_addr.match(id)) { - return true; - } - if (nmsData.smanagement.switches[sw].mgmt_v6_addr.match(id)) { - return true; - } - } catch (e) {} - try { - if (nmsData.smanagement.switches[sw].subnet4.match(id)) { - return true; - } - if (nmsData.smanagement.switches[sw].subnet6.match(id)) { - return true; - } - } catch (e) {} - if (nmsData.snmp.snmp[sw].misc.sysDescr[0].toLowerCase().match(id)) { - return true; - } - } catch (e) { - return false; - } - return false; -}; - -/* - * FIXME: Not sure this belongs here, it's really part of the "Core" ui, - * not just the infobox. - */ -nmsInfoBox._search = function() { - var el = document.getElementById("searchbox"); - var id = false; - var matches = []; - if (el) { - id = el.value.toLowerCase(); - } - if(id) { - nmsMap.enableHighlights(); - for(var sw in nmsData.switches.switches) { - if (nmsInfoBox._searchSmart(id,sw)) { - matches.push(sw); - nmsMap.setSwitchHighlight(sw,true); - } else { - nmsMap.setSwitchHighlight(sw,false); - } - } - } else { - nmsMap.disableHighlights(); - } - if(matches.length == 1) { - document.getElementById("searchbox-submit").classList.add("btn-primary"); - document.getElementById("searchbox").dataset.match = matches[0]; - } else { - document.getElementById("searchbox-submit").classList.remove("btn-primary"); - document.getElementById("searchbox").dataset.match = ''; - } -}; - -nmsInfoBox._searchKeyListener = function(e) { - switch (e.keyCode) { - case 13: - var sw = document.getElementById("searchbox").dataset.match; - if(sw != '') { - nmsInfoBox.showWindow("switchInfo",sw); - } - break; - case 27: - document.getElementById("searchbox").dataset.match = ''; - document.getElementById("searchbox").value = ''; - nmsInfoBox._search(); - nmsInfoBox.hide(); - break; - } -}; - nmsInfoBox._nullBlank = function(x) { if (x == null || x == false || x == undefined) return ""; diff --git a/web/js/nms-map-handlers.js b/web/js/nms-map-handlers.js index f4aba57..61a5a05 100644 --- a/web/js/nms-map-handlers.js +++ b/web/js/nms-map-handlers.js @@ -11,12 +11,6 @@ * utilize information from any aspect of NMS, and thus opens NMS up to the * world of intelligent maps base don multiple data sources. * - * Warning: This paradigm will change. Handlers will be expected to - * register their own callbacks for nmsData. Work in progress. - * - */ - -/* */ var handler_uplinks = { @@ -33,8 +27,9 @@ var handler_temp = { var handler_ping = { init:pingInit, + getInfo:pingInfo, tag:"ping", - name:"IPv4 Ping" + name:"Ping" }; var handler_traffic = { @@ -63,6 +58,7 @@ var handler_disco = { var handler_snmp = { init:snmpInit, + getInfo:snmpInfo, tag:"snmp", name:"SNMP state" }; @@ -73,7 +69,51 @@ var handler_cpu = { name:"CPU utilization" }; +var handler_health = { + init:healthInit, + getInfo:healthInfo, + tag:"health", + name:"Health" +}; + +var handler_mgmt = { + getInfo:mgmtInfo, + name:"Management info" +}; + +var handlerInfo = function(tag,desc) { + /* + * Short name, typically matching the url anchor. + */ + this.tag = tag; + this.description = desc || "Unknown"; + this.data = [ + { + value: undefined, + description: desc || "Generic info" + }]; + /* + * 0: all good. + * 1000: messed up. + * + * This can be "intelligent". E.g.: pingInfo() takes latency and + * the age of the reply into account in addition to having a + * special handling of lack of a result. + */ + this.score = 0; + /* + * Why the score is what it is. E.g.: If you have multiple + * conditions that set the score, what is the final value based on? + */ + this.why = "0 score is the default"; +}; + +/* + * Order matches what's seen in the infobox + */ var handlers = [ + handler_health, + handler_mgmt, handler_uplinks, handler_temp, handler_ping, @@ -110,13 +150,13 @@ function uplinkUpdater() if (uplinks == 0) { nmsMap.setSwitchColor(sw,"white"); } else if (uplinks == 1) { - nmsMap. setSwitchColor(sw, red); + nmsMap. setSwitchColor(sw, nmsColor.red); } else if (uplinks == 2) { - nmsMap.setSwitchColor(sw, orange); + nmsMap.setSwitchColor(sw, nmsColor.orange); } else if (uplinks == 3) { - nmsMap.setSwitchColor(sw,green); + nmsMap.setSwitchColor(sw, nmsColor.green); } else if (uplinks > 3) { - nmsMap.setSwitchColor(sw, blue); + nmsMap.setSwitchColor(sw, nmsColor.blue); } } } @@ -129,10 +169,10 @@ function uplinkInit() nmsData.addHandler("switches","mapHandler",uplinkUpdater); nmsData.addHandler("switchstate","mapHandler",uplinkUpdater); setLegend(1,"white","0 uplinks"); - setLegend(2,red,"1 uplink"); - setLegend(3,orange,"2 uplinks"); - setLegend(4,green,"3 uplinks"); - setLegend(5,blue,"4 uplinks"); + setLegend(2,nmsColor.red,"1 uplink"); + setLegend(3,nmsColor.orange,"2 uplinks"); + setLegend(4,nmsColor.green,"3 uplinks"); + setLegend(5,nmsColor.blue,"4 uplinks"); } /* @@ -143,7 +183,7 @@ function trafficInit() nmsData.addHandler("switches","mapHandler",trafficUpdater); nmsData.addHandler("switchstate","mapHandler",trafficUpdater); var m = 1024 * 1024 / 8; - drawGradient([lightgreen,green,orange,red]); + nmsColor.drawGradient([nmsColor.lightgreen, nmsColor.green, nmsColor.orange, nmsColor.red]); setLegend(1,colorFromSpeed(0),"0 (N/A)"); setLegend(5,colorFromSpeed(1100 * m) , "1100Mb/s"); setLegend(4,colorFromSpeed(600 * m),"600Mb/s"); @@ -178,7 +218,7 @@ function trafficTotInit() nmsData.addHandler("switches","mapHandler",trafficTotUpdater); nmsData.addHandler("switchstate","mapHandler",trafficTotUpdater); var m = 1024 * 1024 / 8; - drawGradient([lightgreen,green,orange,red]); + nmsColor.drawGradient([nmsColor.lightgreen, nmsColor.green, nmsColor.orange, nmsColor.red]); setLegend(1,colorFromSpeed(0),"0 (N/A)"); setLegend(5,colorFromSpeed(5000 * m,5) , "5000Mb/s"); setLegend(4,colorFromSpeed(3000 * m,5),"3000Mb/s"); @@ -212,9 +252,9 @@ function colorFromSpeed(speed,factor) if (factor == undefined) factor = 1.1; if (speed == 0) - return blue; + return nmsColor.blue; speed = speed < 0 ? 0 : speed; - return getColorStop( 1000 * (speed / (factor * (1000 * m)))); + return nmsColor.getColorStop( 1000 * (speed / (factor * (1000 * m)))); } /* @@ -226,11 +266,11 @@ function temp_color(t) { if (t == undefined) { console.log("Temp_color, but temp is undefined"); - return blue; + return nmsColor.blue; } t = parseInt(t) - 12; t = Math.floor((t / 23) * 1000); - return getColorStop(t); + return nmsColor.getColorStop(t); } function tempUpdater() @@ -257,7 +297,7 @@ function tempUpdater() function tempInit() { //Padded the gradient with extra colors for the upper unused values - drawGradient(["black",blue,lightblue,lightgreen,green,orange,red]); + nmsColor.drawGradient(["black", nmsColor.blue, nmsColor.lightblue, nmsColor.lightgreen, nmsColor.green, nmsColor.orange, nmsColor.red]); setLegend(1,temp_color(15),"15 °C"); setLegend(2,temp_color(20),"20 °C"); setLegend(3,temp_color(25),"25 °C"); @@ -272,28 +312,68 @@ function pingUpdater() return; } for (var sw in nmsData.switches.switches) { - try { - var c; - if (nmsData.ping.switches[sw].age > 0) { - c = red; - } else { - c = gradient_from_latency(nmsData.ping.switches[sw].latency); - } + var c = nmsColor.getColorStop(pingInfo(sw).score); + if (c == 1000) { + nmsMap.setSwitchColor(sw, nmsColor.blue); + } else { nmsMap.setSwitchColor(sw, c); - } catch (e) { - nmsMap.setSwitchColor(sw, blue); } } } +function pingInfo(sw) +{ + var ret = new handlerInfo("ping","Latency(ms)"); + ret.why = "Latency"; + if (testTree(nmsData,['ping','switches',sw])) { + var v4 = nmsData.ping.switches[sw].latency4; + var v6 = nmsData.ping.switches[sw].latency6; + if (v4 == undefined) + v4 = undefined; + if (v6 == undefined) + v6 = undefined; + ret.data[0].value = v4; + ret.data[0].description = "IPv4 latency(ms)"; + ret.data[1] = {}; + ret.data[1].value = v6; + ret.data[1].description = "IPv6 latency(ms)"; + if (v4 == undefined && v6 == undefined) { + ret.score = 1000; + ret.why = "No IPv4 or IPv6 ping reply"; + } else if(v6 == undefined) { + ret.score = 200; + ret.why = "No IPv6 ping reply"; + } else if (v4 == undefined) { + ret.score = 199; + ret.why = "No IPv4 ping reply"; + } + + v4 = parseFloat(v4) * 10; + v6 = parseFloat(v6) * 10; + if (v4 > ret.score || v6 > ret.score) { + ret.why = "Latency"; + ret.score = parseInt(v4 > v6 ? v4 : v6); + } + if (nmsData.ping.switches[sw].age4 > 5 || nmsData.ping.switches[sw].age6 > 5) { + ret.why = "Old ping"; + ret.score = 900; + } + } else { + ret.data[0].value = "N/A - no ping replies"; + ret.why = "No ping replies"; + ret.score = 999; + } + return ret; +} + function pingInit() { - drawGradient([green,lightgreen,orange,red]); - 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(100),"100ms"); - setLegend(5,gradient_from_latency(undefined) ,"No response"); + nmsColor.drawGradient([nmsColor.green,nmsColor.lightgreen, nmsColor.orange, nmsColor.red]); + setLegend(1,nmsColor.getColorStop(10),"1ms"); + setLegend(2,nmsColor.getColorStop(300),"30ms"); + setLegend(3,nmsColor.getColorStop(600),"60ms"); + setLegend(4,nmsColor.getColorStop(1000),"100ms"); + setLegend(5,nmsColor.blue,"No response"); nmsData.addHandler("ping","mapHandler",pingUpdater); nmsData.addHandler("switches","mapHandler",pingUpdater); nmsData.addHandler("ticker", "mapHandler", pingUpdater); @@ -307,38 +387,31 @@ function getDhcpColor(stop) stop = 1000; if (stop > 1000) stop = 1000; - return getColorStop(stop); + return nmsColor.getColorStop(stop); } function dhcpUpdater() { - if (nmsData.dhcp == undefined || nmsData.dhcp.dhcp == undefined) { + if (!testTree(nmsData,['dhcp','dhcp']) || !testTree(nmsData,['switches','switches'])) { return } - if (nmsData.switches == undefined || nmsData.switches.switches == undefined) { - return; - } var now = nmsData.dhcp.time; - try { for (var sw in nmsData.switches.switches) { - var c = blue; - if (nmsData.dhcp.dhcp[sw] == undefined) { + var c = nmsColor.blue; + var s = nmsData.dhcp.dhcp[sw]; + if (s == undefined) { nmsMap.setSwitchColor(sw,c); continue; } - var s = nmsData.dhcp.dhcp[sw]; var then = parseInt(s); c = getDhcpColor(now - then); nmsMap.setSwitchColor(sw, c); } - } catch(e) { - console.log(e); - } } function dhcpInit() { - drawGradient([green,lightgreen,orange,red]); + nmsColor.drawGradient([nmsColor.green, nmsColor.lightgreen, nmsColor.orange, nmsColor.red]); nmsData.addHandler("dhcp","mapHandler",dhcpUpdater); setLegend(1,"white","Undefined"); setLegend(2,getDhcpColor(1),"1 Second old"); @@ -360,47 +433,96 @@ function randomizeColors() return; } for (var sw in nmsData.switches.switches) { - nmsMap.setSwitchColor(sw, getRandomColor()); + nmsMap.setSwitchColor(sw, nmsColor.random()); } } function discoDo() { randomizeColors(); - setTimeout(randomizeColors,500); } function discoInit() { nmsData.addHandler("ticker", "mapHandler", discoDo); setNightMode(true); - setLegend(1,blue,"Y"); - setLegend(2,red, "M"); - setLegend(3,orange,"C"); - setLegend(4,green, "A"); + setLegend(1,nmsColor.blue,"Y"); + setLegend(2,nmsColor.red, "M"); + setLegend(3,nmsColor.orange,"C"); + setLegend(4,nmsColor.green, "A"); setLegend(5,"white","!"); } function snmpUpdater() { for (var sw in nmsData.switches.switches) { if (nmsData.snmp.snmp[sw] == undefined || nmsData.snmp.snmp[sw].misc == undefined) { - nmsMap.setSwitchColor(sw, red); + nmsMap.setSwitchColor(sw, nmsColor.red); } else if (nmsData.snmp.snmp[sw].misc.sysName[0] != sw) { - nmsMap.setSwitchColor(sw, orange); + nmsMap.setSwitchColor(sw, nmsColor.orange); } else { - nmsMap.setSwitchColor(sw, green); + nmsMap.setSwitchColor(sw, nmsColor.green); + } + } +} + +function secondsToTime(input) { + var h, m, s; + h = Math.floor(input / 60 / 60); + m = Math.floor((input%3600)/60); + s = Math.floor(input%60); + var string = ""; + if (h > 0) + string = h + " hours "; + if (h > 0 || m > 0) + string += m + " minutes "; + if (string == "") + string += s + " seconds"; + return string; +} + +function snmpInfo(sw) { + var ret = new handlerInfo("snmp","SNMP data"); + ret.why = "No data"; + if (!testTree(nmsData,['snmp','snmp',sw,'misc'])) { + ret.score = 800; + ret.why = "No data"; + ret.data[0].value = "No data"; + } else if (nmsData.snmp.snmp[sw].misc.sysName[0] != sw) { + ret.score = 200; + ret.why = "SNMP sysName doesn't match Gondul sysname"; + ret.data[0].value = ret.why; + ret.data[1] = { description: "SNMP sysName", value: nmsData.snmp.snmp[sw].misc.sysName[0] }; + } else { + ret.score = 0; + ret.data[0].value = "SNMP freshly updated"; + ret.why = "SNMP all good"; + } + if (testTree(nmsData,['snmp','snmp',sw,'misc','sysUpTimeInstance',''])) { + var uptime = parseInt(nmsData.snmp.snmp[sw]["misc"]["sysUpTimeInstance"][""]) / 100; + var upstring = secondsToTime(uptime); + ret.data.push({value: upstring, description: "System uptime"}); + if (uptime < 60*5 && ret.score < 500) { + ret.score = 500; + ret.why = "System rebooted last 5 minutes"; + } + if (uptime < 60*15 && ret.score < 250) { + ret.score = 250; + ret.why = "System rebooted last 15 minutes"; } } + return ret; } + function snmpInit() { nmsData.addHandler("snmp", "mapHandler", snmpUpdater); - setLegend(1,green,"OK"); - setLegend(2,orange, "Sysname mismatch"); - setLegend(3,red,"No SNMP data"); - setLegend(4,green, ""); - setLegend(5,green,""); + setLegend(1,nmsColor.green,"OK"); + setLegend(2,nmsColor.orange, "Sysname mismatch"); + setLegend(3,nmsColor.red,"No SNMP data"); + setLegend(4,nmsColor.green, ""); + setLegend(5,nmsColor.green,""); } + function cpuUpdater() { for (var sw in nmsData.switches.switches) { try { @@ -409,7 +531,7 @@ function cpuUpdater() { var local = nmsData.snmp.snmp[sw].misc['jnxOperatingCPU'][u]; cpu = Math.max(nmsData.snmp.snmp[sw].misc.jnxOperatingCPU[u],cpu); } - nmsMap.setSwitchColor(sw, getColorStop(cpu * 10)); + nmsMap.setSwitchColor(sw, nmsColor.getColorStop(cpu * 10)); nmsMap.setSwitchInfo(sw, cpu + " % "); } catch (e) { nmsMap.setSwitchColor(sw, "white"); @@ -418,12 +540,94 @@ function cpuUpdater() { } } +function mgmtInfo(sw) { + var ret = new handlerInfo("mgmt","Management info"); + ret.score = 0; + ret.why = "All good"; + if (testTree(nmsData,['smanagement','switches',sw])) { + var mg = nmsData.smanagement.switches[sw]; + ret.data = + [{ + value: mg.mgmt_v4_addr || "", + description: "Management IP (v4)" + }, { + value: mg.mgmt_v6_addr || "", + description: "Management IP (v6)" + }, { + value: mg.subnet4 || "", + description: "Subnet (v4)" + }, { + value: mg.subnet6 || "", + description: "Subnet (v6)" + }]; + if ((mg.mgmt_v4_addr == undefined || mg.mgmt_v4_addr == "") && (mg.mgmt_v6_addr == undefined || mg.mgmt_v6_addr == "")) { + ret.why = "No IPv4 or IPv6 mamagement IP"; + ret.score = 1000; + } else if (mg.mgmt_v4_addr == undefined || mg.mgmt_v4_addr == "") { + ret.why = "No IPv4 management IP"; + ret.score = 240; + } else if (mg.mgmt_v6_addr == undefined || mg.mgmt_v6_addr == "") { + ret.why = "No IPv6 management IP"; + ret.score = 239; + } + } else { + ret.score = 1000; + ret.why = "No management info"; + ret.data = [{}]; + ret.data[0].value = "N/A"; + ret.data[0].description = "Management info"; + }; + return ret; + +} + function cpuInit() { nmsData.addHandler("snmp", "mapHandler", cpuUpdater); - drawGradient([green,orange,red]); + nmsColor.drawGradient([nmsColor.green,nmsColor.orange,nmsColor.red]); setLegend(1,getColorStop(0),"0 %"); setLegend(2,getColorStop(250),"25 %"); setLegend(3,getColorStop(600),"60 %"); setLegend(4,getColorStop(1000),"100 %"); setLegend(5,"white","N/A"); } + +function healthInfo(sw) { + var worst = new handlerInfo("health", "Health"); + worst.score = 0; + worst.why = "All good"; + for (var h in handlers) { + if (handlers[h].tag== "health") + continue; + if (handlers[h].getInfo == undefined) + continue; + var ret = handlers[h].getInfo(sw); + if (ret.score > worst.score) { + worst = ret; + } + } + worst.data = [{ + description: "Health (lower is better): ", + value: worst.score + " (" + worst.why + ")" + }]; + return worst; +} + +function healthUpdater() { + if (nmsData.switches == undefined || nmsData.switches.switches == undefined) + return; + for (var sw in nmsData.switches.switches) { + var worst = healthInfo(sw); + nmsMap.setSwitchColor(sw, nmsColor.getColorStop(worst.score)); + nmsMap.setSwitchInfo(sw, worst.tag); + } +} + +function healthInit() { + nmsData.addHandler("ping", "mapHandler", healthUpdater); + nmsColor.drawGradient([nmsColor.green,nmsColor.orange,nmsColor.red]); + setLegend(1,nmsColor.getColorStop(0),"All good"); + setLegend(2,nmsColor.getColorStop(250),"Ok-ish"); + setLegend(3,nmsColor.getColorStop(600),"Ick-ish"); + setLegend(4,nmsColor.getColorStop(800),"Nasty"); + setLegend(5,nmsColor.getColorStop(1000),"WTF?"); +} diff --git a/web/js/nms-map.js b/web/js/nms-map.js index 224b3db..26782f8 100644 --- a/web/js/nms-map.js +++ b/web/js/nms-map.js @@ -180,6 +180,19 @@ nmsMap._resizeEvent = function() { nmsMap.drawNow = function () { var now = nmsData.now; + var ctx = nmsMap._c.top.ctx; + if (nmsTime.isRealTime()) { + if (!nmsMap._nowCleared) { + ctx.save(); + ctx.scale(this.scale, this.scale); + ctx.clearRect(0,0,800,100); + ctx.restore(); + nmsMap._nowCleared = true; + nmsMap._lastNow = undefined; + } + return true; + } + nmsMap._nowCleared = false; if(String(now).indexOf('T') == -1) { //If now does not contain 'T' we assume its in epoch format now = new Date(nmsData.now * 1000); } else { @@ -192,7 +205,6 @@ nmsMap.drawNow = function () } nmsMap.stats.nows++; - var ctx = nmsMap._c.top.ctx; ctx.save(); ctx.scale(this.scale, this.scale); ctx.font = (2 * this._settings.fontSize) + "px " + this._settings.fontFace; @@ -273,13 +285,13 @@ nmsMap._drawSwitch = function(sw) var color = nmsMap._color[sw]; if(this._highlightActive) { if(nmsMap._highlight[sw]) { - color = green; + color = nmsColor.green; } else { - color = white; + color = nmsColor.white; } } if (color == undefined) { - color = blue; + color = nmsColor.blue; } this._c.switch.ctx.fillStyle = color; this._drawBox(this._c.switch.ctx, box['x'],box['y'],box['width'],box['height']); @@ -311,7 +323,7 @@ nmsMap._drawText = function(ctx, text, box, align) { if ((box['width'] + 10 )< box['height']) rotate = true; - + this._clearBox(ctx,box); ctx.save(); ctx.scale(this.scale, this.scale); @@ -320,7 +332,7 @@ nmsMap._drawText = function(ctx, text, box, align) { ctx.fillStyle = "white"; ctx.strokeStyle = "black"; ctx.translate(box.x + this._settings.textMargin, box.y + box.height - this._settings.textMargin); - + if (rotate) { ctx.translate(box.width - this._settings.textMargin * 2,0); ctx.rotate(Math.PI * 3/2); @@ -361,8 +373,8 @@ nmsMap._setLinknetColor = function(l, color1, color2) nmsMap._drawLinknet = function(l) { try { - var color1 = blue; - var color2 = blue; + var color1 = nmsColor.blue; + var color2 = nmsColor.blue; try { color1 = nmsMap._linknets[l].sysname1; color2 = nmsMap._linknets[l].sysname2; @@ -408,7 +420,7 @@ nmsMap._drawBox = function(ctx, x, y, boxw, boxh) { }; nmsMap._connectSwitches = function(sw1, sw2, color1, color2) { - nmsMap._connectBoxes(this._getBox(sw1), this._getBox(sw2), + nmsMap._connectBoxes(nmsMap._getBox(sw1), nmsMap._getBox(sw2), color1, color2); }; @@ -419,9 +431,9 @@ nmsMap._connectSwitches = function(sw1, sw2, color1, color2) { nmsMap._connectBoxes = function(box1, box2,color1, color2) { var ctx = nmsMap._c.link.ctx; if (color1 == undefined) - color1 = blue; + color1 = nmsColor.blue; if (color2 == undefined) - color2 = blue; + color2 = nmsColor.blue; var x0 = Math.floor(box1.x + box1.width/2); var y0 = Math.floor(box1.y + box1.height/2); var x1 = Math.floor(box2.x + box2.width/2); diff --git a/web/js/nms-oplog.js b/web/js/nms-oplog.js index 5da7893..83bda69 100644 --- a/web/js/nms-oplog.js +++ b/web/js/nms-oplog.js @@ -8,11 +8,38 @@ nmsOplog.init = function() { nmsData.addHandler("oplog", "nmsOplogHandler", nmsOplog.updateComments); } +nmsOplog._reset = function() { + document.getElementById('logbox-id').value = ""; + document.getElementById('logbox').value = ""; + document.getElementById('searchbox').value = ""; + document.getElementById('searchbox').oninput(); +} + +nmsOplog.getUser = function(force = false) { + var user = nms.user; + if (user == undefined || force) { + user = prompt("Who are you? Short nick for the record."); + if (user == null || user == undefined || user == "") { + console.log("empty prompt"); + alert("No cake for you."); + return false; + } + nms.user = user; + saveSettings(); + } + return nms.user; +} + nmsOplog.commit = function() { var s = document.getElementById('logbox-id').value; var d = document.getElementById('logbox').value; + var user = nmsOplog.getUser(); + if (user == undefined) { + nmsOplog._reset(); + return; + } - var myData = {"systems": s, "log": d}; + var myData = {"user": user, "systems": s, "log": d}; myData = JSON.stringify(myData); $.ajax({ type: "POST", @@ -23,11 +50,7 @@ nmsOplog.commit = function() { nmsData.invalidate("oplog"); } }); - document.getElementById('logbox-id').value = ""; - document.getElementById('logbox').value = ""; - document.getElementById('searchbox').value = ""; - document.getElementById('searchbox').oninput(); - + nmsOplog._reset(); } nmsOplog.updateComments = function() { @@ -37,9 +60,11 @@ nmsOplog.updateComments = function() { nmsOplog.getSwitchLogs = function(sw) { var logs = []; + if (nmsData.oplog == undefined || nmsData['oplog']['oplog'] == undefined) + return []; for (var v in nmsData['oplog']['oplog']) { var log = nmsData['oplog']['oplog'][v]; - if (nmsInfoBox.searchSmart(log['systems'],sw)) { + if (nmsSearch.searchTest(log['systems'],sw)) { logs.push(log); } } @@ -60,10 +85,15 @@ nmsOplog._updateComments = function(limit,prefix,timefield) { tr = table.insertRow(-1); td1 = tr.insertCell(0); td2 = tr.insertCell(1); - td1.textContent = nmsData['oplog']['oplog'][v][timefield]; + var date = new Date(nmsData.oplog.oplog[v]['timestamp']); + if (timefield == "time") { + td1.textContent = date.toTimeString().replace(/:\d\d .*$/,""); + } else { + td1.textContent = date.toString(); + } td2.textContent = "[" + nmsData['oplog']['oplog'][v]['username'] + "] " + nmsData['oplog']['oplog'][v]['log']; td2.hiddenthing = v; - td2.onclick = function(e){ console.log(e); var x = document.getElementById("searchbox"); var v = e.path[0].hiddenthing; console.log("KEK" + v); x.value = nmsData['oplog']['oplog'][v]['systems']; x.oninput(); } + td2.onclick = function(e){ var x = document.getElementById("searchbox"); var v = e.path[0].hiddenthing; x.value = nmsData['oplog']['oplog'][v]['systems']; x.oninput(); } if (++i == limit) break; } diff --git a/web/js/nms-search.js b/web/js/nms-search.js new file mode 100644 index 0000000..6d61536 --- /dev/null +++ b/web/js/nms-search.js @@ -0,0 +1,159 @@ +"use strict"; + +var nmsSearch = nmsSearch || { + _handler: false, + _lastId: false +}; + +nmsSearch.helpText = [ + "The search box can be used to identify switches in several ways. The simplest is by name.", + "Searching by name can be done by just entering text, or if you want to match \"foobar1\" but not \"foobar15\" you can enclose the name in quotation marks. E.g.: foobar1 matches foobar1 and foobar1123123123, while \"foobar1\" only matches exactly foobar1.", + "All text-oriented searches are regular expressions. ^row\\d-2$ matches row1-2, row2-2, etc, but not row13-2 or rowx-2.", + "If you are using the non-public version of Gondul, you can also perform smart searches.", + "Distro search: Type the name of a distro-switch and all access switches registered to that distro switch will also be hilighted.", + 'Active ports: Type "active>x", "active<x" or "active=x" to identify switch with "x" amount of active gigabit ethernet (ge) ports. E.g.: "active>30".', + 'IP search: Start typing an IP and any switch with that IP registered either as management IP or part of its subnet will be identified', + 'SNMP search: Type anything found in the "sysDescr" SNMP OID to hilight a switch matching that. Practical examples include version numbers for firmware (e.g.: "JUNOS 12." vs "JUNOS 14.").']; + +/* + * Test if the search expression "id" matches the switch "sw" + * + * Return true if it does. + */ +nmsSearch.searchTest = function(id, sw) { + try { + var re = new RegExp(id,"i"); + if(re.test(sw)) { + return true; + } + if (id[0] == "\"") { + if (("\"" + sw.toLowerCase() + "\"") == id.toLowerCase()) { + return true; + } else { + return false; + } + } + try { + if (re.test(nmsData.switches.switches[sw].distro_name)) { + return true; + } + } catch (e) {} + try { + if (id.match("active")) { + var limit = id; + limit = limit.replace("active>",""); + limit = limit.replace("active<",""); + limit = limit.replace("active=",""); + var operator = id.replace("active","")[0]; + if (limit == parseInt(limit)) { + var ports = parseInt(nmsData.switchstate.switches[sw].ifs.ge.live); + limit = parseInt(limit); + if (operator == ">" ) { + if (ports > limit) { + return true; + } + } else if (operator == "<") { + if (ports < limit) { + return true; + } + } else if (operator == "=") { + if (ports == limit) { + return true; + } + } + } + } + } catch (e) {} + try { + if (re.test(nmsData.smanagement.switches[sw].mgmt_v4_addr)) { + return true; + } + if (re.test(nmsData.smanagement.switches[sw].mgmt_v6_addr)) { + return true; + } + } catch (e) {} + try { + if (re.test(nmsData.smanagement.switches[sw].subnet4)) { + return true; + } + if (re.test(nmsData.smanagement.switches[sw].subnet6)) { + return true; + } + } catch (e) {} + if (re.test(nmsData.snmp.snmp[sw].misc.sysDescr[0])) { + return true; + } + } catch (e) { + return false; + } + return false; +}; + +nmsSearch.reset = function() { + document.getElementById("searchbox").dataset.match = ''; + document.getElementById("searchbox").value = ''; + nmsSearch.search(); +} + +nmsSearch._enableTimer = function() { + if (nmsSearch._handler == false) { + nmsSearch._handler = setInterval(nmsSearch.search,1000); + } +} + +nmsSearch._disableTimer = function() { + if (nmsSearch._handler != false) { + clearInterval(nmsSearch.search); + } +} + +nmsSearch.search = function() { + var el = document.getElementById("searchbox"); + var id = false; + var matches = []; + if (el) { + id = el.value.toLowerCase(); + } + if(id) { + nmsMap.enableHighlights(); + for(var sw in nmsData.switches.switches) { + if (nmsSearch.searchTest(id,sw)) { + matches.push(sw); + nmsMap.setSwitchHighlight(sw,true); + } else { + nmsMap.setSwitchHighlight(sw,false); + } + } + nmsSearch._enableTimer(); + } else { + nmsSearch._disableTimer(); + nmsMap.disableHighlights(); + } + if(matches.length == 1) { + document.getElementById("searchbox-submit").classList.add("btn-primary"); + document.getElementById("searchbox").dataset.match = matches[0]; + } else { + document.getElementById("searchbox-submit").classList.remove("btn-primary"); + document.getElementById("searchbox").dataset.match = ''; + } +}; + +nmsSearch._searchKeyListener = function(e) { + switch (e.keyCode) { + case 13: + var sw = document.getElementById("searchbox").dataset.match; + if(sw != '') { + nmsInfoBox.showWindow("switchInfo",sw); + } + break; + case 27: + nmsSearch.reset(); + break; + } +} + +nmsSearch.init = function() { + $("#searchbox").keyup(function(e) { + nmsSearch._searchKeyListener(e); + }); +} diff --git a/web/js/nms-time.js b/web/js/nms-time.js new file mode 100644 index 0000000..315ac79 --- /dev/null +++ b/web/js/nms-time.js @@ -0,0 +1,150 @@ +"use strict"; + +/* + * Deals with controlling time. + * + * More specifically: replaying of past events, fast forwarding, pausing, + * etc. + * + * The interface is a bit bloated at the moment, though. + */ +var nmsTime = nmsTime || { + _now: undefined, + _handle: undefined +} + +nmsTime.replayEvent = function() { + throw "Not yet implemented."; +} + +nmsTime.isRealTime = function() { + if (nmsTime._now == undefined && nmsTime._handle == undefined) + return true; + return false; +} + +nmsTime.startNowPicker = function () { + $.datetimepicker.setLocale('no'); + $('#nowPicker').datetimepicker('destroy'); + var now; + if (nmsTime._now == undefined) + now = new Date(); + else + now = nmsTime._now; + now.setSeconds(0); + now.setMilliseconds(0); + var datepicker = $('#nowPicker').datetimepicker({ + value: now, + step: 5, + mask:false, + inline:true, + todayButton: true, + validateOnBlur:false, + dayOfWeekStart:1, + maxDate:'+1970/01/01', + onSelectDate: function(ct,$i){ + document.getElementById('nowPicker').dataset.iso = new Date(ct.valueOf()); + }, + onSelectTime: function(ct,$i){ + document.getElementById('nowPicker').dataset.iso = new Date(ct.valueOf()); + }, + onGenerate: function(ct,$i){ + document.getElementById('nowPicker').dataset.iso = new Date(ct.valueOf()); + } + }); +} + +nmsTime.setNow = function(now) { + var newDate = new Date(now); + newDate.setSeconds(0); + newDate.setMilliseconds(0); + newDate.setMinutes(newDate.getMinutes() - newDate.getMinutes()%5); + nmsTime._now = newDate; + nmsTime._updateData(); +} + +nmsTime._updateData = function() { + nmsData.now = nmsTime._now.getTime() / 1000; +} + +nmsTime.realTime = function() { + nmsTime.stopPlayback(); + nmsTime._now = undefined; + nmsData.now = undefined; +} + +/* + * Step a fixed amount of time, measured in minutes. + * + * Try to align this to whole 5 minutes. It will be enforced in future + * backend versions to avoid bloating the cache and thus also stressing the + * database + */ +nmsTime.step = function(amount) { + if (nmsTime._now == null) + throw "Stepping without nmsTime._now"; + if (amount == 0 || amount == undefined) + throw "Invalid step"; + if (nmsTime._now.getTime() + (amount * 1000 * 60 ) > Date.now()) { + nmsTime.realTime(); + return; + } + nmsTime._now.setMinutes(nmsTime._now.getMinutes() + amount); + nmsTime._updateData(); +} + +/* + * Step based on key-press. Same as step() but stops playback if it's + * active and allows you to rewind from a "live" map. + */ +nmsTime.stepKey = function(amount) { + nmsTime.stopPlayback(); + if (nmsTime._now == undefined) { + nmsTime.setNow(Date.now()); + } + nmsTime.step(amount); +} + +/* + * Target of setInterval() when replaying. + */ +nmsTime._tick = function() { + nmsTime.step(nmsTime._speed); +} + +/* + * We now have a time (presumably), start playback. + * + * Aborts if the time provided is greater than real time. + * + * Gondul does not _yet_ support fast forwarding into the future. + */ +nmsTime.startPlayback = function(speed) { + if (nmsTime._handle) + nmsTime.stopPlayback(); + if (nmsTime._now.getTime() > Date.now()) { + nmsTime.stopPlayback(); + return; + } + nmsTime._speed = speed; + nmsTime._handle = setInterval(nmsTime._tick,1000); +} + +nmsTime.togglePause = function() { + if (nmsTime._handle) { + nmsTime.stopPlayback(); + } else { + if (nmsTime.isRealTime()) { + nmsTime.setNow(Date.now()); + } else { + nmsTime.startPlayback(nmsTime._speed ? nmsTime._speed : 5); + } + } +} + +nmsTime.stopPlayback = function() { + if (nmsTime._handle) + clearInterval(nmsTime._handle); + nmsTime._handle = undefined; +} + diff --git a/web/js/nms.js b/web/js/nms.js index b979a32..9727c09 100644 --- a/web/js/nms.js +++ b/web/js/nms.js @@ -11,8 +11,6 @@ * - Move all pure UI stuff into nmsUi: nightMode, vertical mode, * menushowing, * - Get rid of "tvmode". As in: complete the merge - * - Move all time-travel related code out into a separate entity. - * - Remove nms.now: it belongs in nmsData. * - nms.timers probably also deserves to die. It used to do a lot more, * now it's just leftovers. */ @@ -21,23 +19,19 @@ var nms = { get nightMode() { return this._nightMode; }, set nightMode(val) { if (val != this._nightMode) { this._nightMode = val; setNightMode(val); } }, /* - * FIXME: This should be slightly smarter. - */ - _now: false, - get now() { return this._now }, - set now(v) { this._now = v; nmsData.now = v; }, - /* * Various setInterval() handlers. See nmsTimer() for how they are * used. * * FIXME: Should just stop using these. */ timers: { - playback:false, tvmode: false }, menuShowing:true, + get uptime() { + return (Date.now() - this._startTime)/1000; + }, _vertical: 0, get vertical() { return this._vertical }, set vertical(v) { @@ -49,9 +43,14 @@ var nms = { saveSettings(); }, - interval: 0, - tvmodeToggle: false, - views: "ping", + interval: 10, + _user: undefined, + get user() { return this._user; }, + set user(u) { + this._user = u; + document.getElementById('logbook-name').textContent = u; + saveSettings(); + }, /* * This is a list of nms[x] variables that we store in our * settings-cookie when altered and restore on load. @@ -59,10 +58,9 @@ var nms = { settingsList:[ 'nightMode', 'menuShowing', - 'tvmodeToggle', 'vertical', - 'views', - 'interval' + 'interval', + 'user' ], keyBindings:{ '-':toggleMenu, @@ -86,16 +84,6 @@ var nms = { 'Escape':hideWindow, '?':toggleHelp }, - /* - * Playback controllers and variables - */ - playback:{ - startTime: false, - stopTime: false, - playing: false, - replayTime: 0, - replayIncrement: 60 * 60 - }, tvmode: { handlers: [], currentIndex: 0, @@ -108,8 +96,8 @@ var nms = { /* * Returns a handler object. * - * This might seem a bit much for 'setInterval()' etc, but it's really more - * about self-documentation and predictable ways of configuring timers. + * FIXME: This is legacy-stuff, should get rid of it. DO NOT use this for + * new code. */ function nmsTimer(handler, interval, name, description) { this.handler = handler; @@ -166,193 +154,8 @@ function toggleNightMode() } /* - * Parse 'now' from user-input. - * - * Should probably just use stringToEpoch() instead, but alas, not yet. - */ -function parseNow(now) -{ - if (Date.parse(now)) { - // Adjust for timezone when converting from epoch (UTC) to string (local) - var d = new Date(now); - var timezoneOffset = d.getTimezoneOffset() * -60000; - var d = new Date(Date.parse(now) - timezoneOffset); - var str = d.getFullYear() + "-" + ("00" + (parseInt(d.getMonth())+1)).slice(-2) + "-" + ("00" + d.getDate()).slice(-2) + "T"; - str += ("00" + d.getHours()).slice(-2) + ":" + ("00" + d.getMinutes()).slice(-2) + ":" + ("00" + d.getSeconds()).slice(-2); - return str; - - } - if (now == "") - return ""; - return false; -} - -/* - * Convert back and forth between epoch. - * - * There's no particular reason why I use seconds instead of javascript - * microseconds, except to leave the mark of a C coder on this javascript - * project. - */ -function stringToEpoch(t) -{ - var foo = t.toString(); -// foo = foo.replace('T',' '); - var ret = new Date(Date.parse(foo)); - return parseInt(parseInt(ret.valueOf()) / 1000); -} - -/* - * Have to pad with zeroes to avoid "17:5:0" instead of the conventional - * and more readable "17:05:00". I'm sure there's a better way, but this - * works just fine. - */ -function epochToString(t) -{ - // Adjust for timezone when converting from epoch (UTC) to string (local) - var date = new Date(parseInt(t) * parseInt(1000)); - var timezoneOffset = date.getTimezoneOffset() * -60; - t = t - timezoneOffset; - - date = new Date(parseInt(t) * parseInt(1000)); - var str = date.getFullYear() + "-"; - if (parseInt(date.getMonth()) < 9) - str += "0"; - str += (parseInt(date.getMonth())+1) + "-"; - if (date.getDate() < 10) - str += "0"; - str += date.getDate() + "T"; - if (date.getHours() < 10) - str += "0"; - str += date.getHours() + ":"; - if (date.getMinutes() < 10) - str += "0"; - str += date.getMinutes() + ":"; - if (date.getSeconds() < 10) - str += "0"; - str += date.getSeconds(); - - return str; -} - -function localEpochToString(t) { - var d = new Date(parseInt(t) * parseInt(1000)); - var timezoneOffset = d.getTimezoneOffset() * -60; - t = t + timezoneOffset; - - return epochToString(t); -} - -/* - * Start replaying historical data. - */ -nms.playback.startReplay = function(startTime,stopTime) { - if(!startTime || !stopTime) - return false; - - nms.playback.pause(); - nms.playback.startTime = stringToEpoch(startTime); - nms.playback.stopTime = stringToEpoch(stopTime); - nms.now = epochToString(nms.playback.startTime); - nms.playback.play(); -}; - -/* - * Pause playback - */ -nms.playback.pause = function() { - nms.timers.playback.stop(); - nms.playback.playing = false; -}; - -/* - * Start playback - */ -nms.playback.play = function() { - nms.playback.tick(); - nms.timers.playback.start(); - nms.playback.playing = true; -}; - -/* - * Toggle playback - */ -nms.playback.toggle = function() { - if(nms.playback.playing) { - nms.playback.pause(); - } else { - nms.playback.play(); - } -}; - -/* - * Jump to place in time - */ -nms.playback.setNow = function(now) { - nms.now = parseNow(now); - - nms.playback.stopTime = false; - nms.playback.startTime = false; - nms.playback.tick(); -}; - -/* - * Step forwards or backwards in timer - */ -nms.playback.stepTime = function(n) -{ - var now = getNowEpoch(); - var newtime = parseInt(now) + parseInt(n); - nms.now = epochToString(parseInt(newtime)); - - if(!nms.playback.playing) - nms.playback.tick(); -}; - -/* - * Ticker to trigger updates, and advance time if replaying - * - * This is run on a timer (nms.timers.tick) every second while unpaused - */ -nms.playback.tick = function() -{ - nms.playback.replayTime = getNowEpoch(); - - // If outside start-/stopTime, remove limits and pause playback - if (nms.playback.stopTime && (nms.playback.replayTime >= nms.playback.stopTime || nms.playback.replayTime < nms.playback.startTime)) { - nms.playback.stopTime = false; - nms.playback.startTime = false; - nms.playback.pause(); - return; - } - - // If past actual datetime, go live - if (nms.playback.replayTime > parseInt(Date.now() / 1000)) { - nms.now = false; - } - - // If we are still replaying, advance time - if(nms.now !== false && nms.playback.playing) { - nms.playback.stepTime(nms.playback.replayIncrement); - } -}; - -/* - * Helper function for safely getting a valid now-epoch - */ -function getNowEpoch() { - if (nms.now && nms.now != 0) - return stringToEpoch(nms.now); - else - return parseInt(Date.now() / 1000); -} - -/* * 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. - * - * FIXME: Should be smarter, possibly use a canvas-writer so we can get - * proper text (e.g.: not black text on dark blue). */ function setLegend(x,color,name) { @@ -371,7 +174,10 @@ function setLegend(x,color,name) * Start TV-mode * * Loops trough a list of views/updaters at a set interval. - * Arguments: array of views, interval in seconds, use nightmode, hide menus + * Arguments: array of views, interval in seconds + * + * FIXME: this is getting gradually stripped down from the original, so far + * we're not quite there yet with merging it with the regular code paths. */ nms.tvmode.start = function(views,interval) { nms.tvmode.handlers = []; @@ -395,21 +201,43 @@ nms.tvmode.tick = function() { if(nms.tvmode.currentIndex > nms.tvmode.handlers.length - 1) { nms.tvmode.currentIndex = 0; } - setUpdater(nms.tvmode.handlers[nms.tvmode.currentIndex]); + setUpdater(nms.tvmode.handlers[nms.tvmode.currentIndex],false); nms.tvmode.currentIndex++; } nms.tvmode.stop = function() { - nms.timers.tvmode.stop(); - document.body.classList.remove("tvmode"); - document.body.classList.remove("vertical"); - nms.tvmode.active = false; + if (nms.tvmode.active) { + nms.timers.tvmode.stop(); + nms.tvmode.active = false; + } +} + +/* + * Used when changing handler to ensure that the new handler is listed in + * the anchor. The anchor can contain a comma-separated list of views and + * we only overwrite it if the new view isn't present. + */ +function ensureAnchorHas(view) { + try { + var views = document.location.hash.slice(1); + views = views.split(","); + if (views.includes(view)) { + return true; + } + } catch(e) { } + document.location.hash = view; + return false; } /* * Change map handler (e.g., change from uplink map to ping map) + * + * stopTv esnures that we don't conflict with the tvmode thing. If a + * user-initiated map is selected, tvmode is disabled. */ -function setUpdater(fo) +function setUpdater(fo, stopTv = true) { + if (stopTv) + nms.tvmode.stop(); nmsMap.reset(); nmsData.unregisterHandlerWildcard("mapHandler"); try { @@ -425,7 +253,7 @@ function setUpdater(fo) } var foo = document.getElementById("map-mode-title"); foo.innerHTML = fo.name; - document.location.hash = fo.tag; + ensureAnchorHas(fo.tag); } function toggleLayer(layer) { @@ -436,6 +264,12 @@ function toggleLayer(layer) { l.style.display = 'none'; } +function hideLayer(layer) { + var l = document.getElementById(layer); + l.style.display = 'none'; +} + + function toggleConnect() { toggleLayer("linkCanvas"); } @@ -498,6 +332,10 @@ function setNightMode(toggle) { * * If you add a configuration setting, use nmsData['config'] as much as * possible. Avoid adding to this function. + * + * FIXME: If anyone has a way to remove the deprecation warnings, either by + * just silencing them or by moving this off the main thread, then go + * ahead and fix it. I don't consider it a real problem, though. */ function getInitialConfig() { $.ajax({ @@ -510,7 +348,6 @@ function getInitialConfig() { nms._public = true; document.body.classList.add("gondul-public"); } else { - console.log("Private"); nms._public = false; document.body.classList.add("gondul-private"); } @@ -526,7 +363,8 @@ function getInitialConfig() { * yet. */ function initNMS() { - nms.timers.playback = new nmsTimer(nms.playback.tick, 1000, "Playback ticker", "Handler used to advance time"); + // Only used for dev-purposes now. Accessible through nms.uptime. + nms._startTime = Date.now(); // Public nmsData.registerSource("config","/api/public/config"); @@ -558,23 +396,23 @@ function initNMS() { restoreSettings(); nmsMap.init(); detectHandler(); - nms.playback.play(); setupKeyhandler(); - setupSearchKeyHandler(); + nmsSearch.init(); } function detectHandler() { - if (nms.tvmodeToggle) { - var views = nms.views; - var interval = nms.interval; - - views = views.split(","); - + var views = document.location.hash.slice(1); + var interval = nms.interval; + if (views == undefined || views == "") + views = "ping"; + views = views.split(","); + + if (views.length > 1) { nms.tvmode.start(views,interval); return; } else { for (var i in handlers) { - if (('#' + handlers[i].tag) == document.location.hash) { + if (handlers[i].tag == views[0]) { setUpdater(handlers[i]); return; } @@ -623,6 +461,9 @@ function setMapModeFromN(e,key) case '3': setUpdater(handler_dhcp); break; + case '4': + setUpdater(handler_health); + break; case '5': setUpdater(handler_temp); break; @@ -646,23 +487,22 @@ function moveTimeFromKey(e,key) { switch(key) { case 'h': - nms.playback.stepTime(-3600); + nmsTime.stepKey(-60); break; case 'j': - nms.playback.stepTime(-300); + nmsTime.stepKey(-5); break; case 'k': - nms.playback.stepTime(300); + nmsTime.stepKey(5); break; case 'l': - nms.playback.stepTime(3600); + nmsTime.stepKey(60); break; case 'p': - nms.playback.toggle(); + nmsTime.togglePause(); break; case 'r': - nms.playback.setNow(); - nms.playback.play(); + nmsTime.realTime(); break; } return true; @@ -708,13 +548,6 @@ function setupKeyhandler() }); } -function setupSearchKeyHandler() -{ - $("#searchbox").keyup(function(e) { - nmsInfoBox._searchKeyListener(e); - }); -} - function getCookie(cname) { var name = cname + "="; @@ -773,29 +606,22 @@ function restoreSettings() } /* - * Time travel gui + * Test if the entire path specified in the arrary "ar" exists under the + * specified root. + * + * E.g.: + * if (!testTree(nmsData,['snmp','snmp',sw,'misc'])) { + * do stuff with nmsData.snmp.snmp[sw].misc + * } + * */ -function startNowPicker(now) { - $.datetimepicker.setLocale('no'); - $('#nowPicker').datetimepicker('destroy'); - if(!now && nms.now) - now = nms.now; - var datepicker = $('#nowPicker').datetimepicker({ - value: now, - mask:false, - inline:true, - todayButton: false, - validateOnBlur:false, - dayOfWeekStart:1, - maxDate:'+1970/01/01', - onSelectDate: function(ct,$i){ - document.getElementById('nowPicker').dataset.iso = localEpochToString(ct.valueOf()/1000); - }, - onSelectTime: function(ct,$i){ - document.getElementById('nowPicker').dataset.iso = localEpochToString(ct.valueOf()/1000); - }, - onGenerate: function(ct,$i){ - document.getElementById('nowPicker').dataset.iso = localEpochToString(ct.valueOf()/1000); - } - }); +function testTree(root, ar) { + if (ar == undefined || root == undefined) + return false; + for (var i in ar) { + root = root[ar[i]]; + if (root == undefined) + return false; + } + return true; } |