diff options
author | Nicolai Tellefsen <niccofyren@gmail.com> | 2016-04-05 22:56:39 +0200 |
---|---|---|
committer | Nicolai Tellefsen <niccofyren@gmail.com> | 2016-04-05 22:56:39 +0200 |
commit | d1d1e3c9cbf405eae86e5a46fc90765cd429acff (patch) | |
tree | 6f6d219d5d2441d1d9003373e43996636b87243e /web/nms.gathering.org/js/nms-info-box.js | |
parent | 6dc215efd24c79c06c9d7f572f5c79218fdc2f18 (diff) |
NMS: Rebuild nmsInfoBox
Basically the old model forced each window to handle all the logic needed for
showing and managing a window. The new model takes care of the basic window-
management and lets us focus on "panels" that are reusable, independent of each
other, and can re-render themselves without concern for any other objects. We
are also able to define windows and views that use existing panels just by
providing a basic template to the windowHandler.
Does still have som bugs and missing functionality that needs to be addressed.
Diffstat (limited to 'web/nms.gathering.org/js/nms-info-box.js')
-rw-r--r-- | web/nms.gathering.org/js/nms-info-box.js | 1283 |
1 files changed, 787 insertions, 496 deletions
diff --git a/web/nms.gathering.org/js/nms-info-box.js b/web/nms.gathering.org/js/nms-info-box.js index 8ef2611..f4dcb49 100644 --- a/web/nms.gathering.org/js/nms-info-box.js +++ b/web/nms.gathering.org/js/nms-info-box.js @@ -1,469 +1,502 @@ "use strict"; /* - * NMS info window controller + * NMS info-window controller * - * Interface: nmsInfoBox.showWindow(windowType,optionalParameter), nmsInfoBox.hide(), nmsInfoBox.refresh() + * Interface: nmsInfoBox.showWindow(windowType,optionalParameter), nmsInfoBox.hide() * - * Any windowTypes should at a minimum implement load, update, unload, getTitle, getContent, getChildContent + * Uses a basic hierarchy of window > views > panels, where each panel can work + * independently, but gets loaded/unloaded when needed by window or view. * */ +/* + * + * 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 + * - Test comments + * - 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 + * - Add some basic styling to separate panels visually when in the same view + * - Add some movement/placement/size options to info window + * + */ + +/* + * Basic configuration + */ var nmsInfoBox = nmsInfoBox || { stats: {}, _container: false, //Container window - _window: false, //Active window (reference to _windowTypes object or false) - _windowTypes: [] //List of all avaliable window types + _windowHandler: false, //Window handler + _sw: false, //Name of last switch opened, used for toggle-click + _windowTypes: [ + { + 'id': 'switchInfo', + 'title': 'Switch info', + 'views': { + 'initial': { + 'name': 'Switch summary', + 'panels': ['switchComments','switchSummary'] + }, + 'details': { + 'name': 'Switch details', + 'panels': ['switchDetails'] + }, + 'ports': { + 'name': 'SNMP - Ports', + 'panels': ['switchSNMP:ports'] + }, + 'misc': { + 'name': 'SNMP - Misc', + 'panels': ['switchSNMP:misc'] + }, + 'comments': { + 'name': 'Comments', + 'panels': ['switchComments'] + }, + 'edit': { + 'name': 'Edit', + 'panels': ['switchEdit'] + } + } + }, + { + 'id': 'addSwitch', + 'title': 'Add new switch', + 'views': { + 'initial': { + 'name': 'Add switch', + 'panels': ['switchAdd'] + } + } + }, + { + 'id': 'inventoryListing', + 'title': 'Inventory listing', + 'views': { + 'initial': { + 'name': 'Distro names', + 'panels': ['inventoryListing:distro_name'] + }, + 'sysDescr': { + 'name': 'System description', + 'panels': ['inventoryListing:sysDescr'] + }, + 'jnxBoxSerialNo': { + 'name': 'Serial numbers', + 'panels': ['inventoryListing:jnxBoxSerialNo'] + } + } + } + ], + _panelTypes: {} //Populate by using the nmsInfoBox.addPanelType method }; /* - * Shows a window from the _windowTypes list + * Shows a window, and triggers initial load if needed */ nmsInfoBox.showWindow = function (windowName,argument) { - if(windowName == "switchInfo" && argument != '' && argument == this._window.sw) { + if(windowName == "switchInfo" && argument != '' && argument == this._sw) { nmsInfoBox.hide(); return; } - nmsInfoBox.hide(); - for(var win in this._windowTypes) { - if(windowName == win) { - this._window = this._windowTypes[win]; - this._show(argument); - return; - } - } -}; -/* - * Refresh the active window - */ -nmsInfoBox.refresh = function(argument) { - if(!nmsInfoBox._window) - return; - nmsInfoBox._show(argument); + if(!this._container) + this._load(); + if(!windowName) + windowName = 'switchInfo'; + + this._sw = argument; + + this._windowHandler.showWindow(windowName,argument) + this._container.style.display = "block"; }; -nmsInfoBox.update = function(argument) { - if(!nmsInfoBox._window) - return; - nmsInfoBox._window.update(argument); -} /* - * Internal function to show the active _window and pass along any arguments + * Internal function to load and register the initial html objects */ -nmsInfoBox._show = function(argument) { - nmsData.addHandler("comments","switchshower",nmsInfoBox.update,'comments'); - nmsData.addHandler("switches","switchshower",nmsInfoBox.update,'switches'); - nmsData.addHandler("smanagement","switchshower",nmsInfoBox.update,'smanagement'); - nmsData.addHandler("snmp","switchshower",nmsInfoBox.update,'snmp'); - - if(argument != "soft") - this._window.load(argument); - - this._container = document.getElementById("info-panel-container"); - var panel = document.createElement("div"); - panel.classList.add("panel", "panel-default"); +nmsInfoBox._load = function() { + var infoBox = document.createElement("div"); + infoBox.classList.add("panel", "panel-default"); var title = document.createElement("div"); + title.id = "info-box-title"; title.classList.add("panel-heading"); + var nav = document.createElement("div"); + nav.id = "info-box-nav"; + nav.classList.add("panel-body"); var body = document.createElement("div"); + body.id = "info-box-body"; body.classList.add("panel-body"); - title.innerHTML = this._window.getTitle() + '<button type="button" class="close" aria-label="Close" onclick="nmsInfoBox.hide();" style="float: right;"><span aria-hidden="true">×</span></button>'; - var content = this._window.getContent(); - if(!content.nodeName) { - body.innerHTML = this._window.content; - } else { - body.appendChild(content); - } - var childContent = this._window.getChildContent(); - if(childContent != false) { - body.appendChild(childContent); - } + infoBox.appendChild(title); + infoBox.appendChild(nav); + infoBox.appendChild(body); - panel.appendChild(title); - panel.appendChild(body); - while(this._container.firstChild) { - this._container.removeChild(this._container.firstChild); - } - this._container.appendChild(panel); - this._container.style.display = "block"; - $('[data-toggle="popover"]').popover({placement:"top",container:'body'}); - $(".collapse-controller").on("click", function(e) { - $(e.target.dataset.target).collapse('toggle'); - }); + this._container = document.getElementById("info-box-container"); + this._container.appendChild(infoBox); + + this._windowHandler = new windowHandler(); + this._windowHandler.setContainerObj(document.getElementById("info-box-container")); + this._windowHandler.setTitleObj(document.getElementById("info-box-title")); + this._windowHandler.setBodyObj(document.getElementById("info-box-body")); + this._windowHandler.setNavObj(document.getElementById("info-box-nav")); + this._windowHandler.setPanelTypes(this._panelTypes); + this._windowHandler.setWindowTypes(this._windowTypes); +}; + +/* + * Adds a panel type to _panelTypes for usage in windows and views + */ +nmsInfoBox.addPanelType = function (id, obj) { + this._panelTypes[id] = obj; }; + /* * Hide the active window and tell it to unload */ nmsInfoBox.hide = function() { - if(!this._container || !this._window) - return; - this._container.style.display = "none"; - this._window.unload(); - this._window = false; - nmsData.unregisterHandler("comments","switchshower"); - nmsData.unregisterHandler("switches","switchshower"); - nmsData.unregisterHandler("smanagement","switchshower"); - nmsData.unregisterHandler("snmp","switchshower"); + this._sw = false; + this._windowHandler.hide(); + this._windowHandler.unloadWindow(); }; /* - * Window type: Add Switch - * - * Basic window that lets you create a new switch - * + * Click a switch and display it. */ -nmsInfoBox._windowTypes.addSwitch = { - title: 'Add new switch', - content: '<input type="text" class="form-control" id="create-sysname" placeholder="Sysname id"><button class="btn btn-default" onclick="nmsInfoBox._windowTypes.addSwitch.save();">Add switch</button>', - childContent: false, - getTitle: function() { - return this.title; - }, - getContent: function() { - return this.content; - }, - getChildContent: function() { - return this.childContent; - }, - load: function(argument) { - }, - update: function(type) { - }, - unload: function() { - }, - save: function() { - var sysname = document.getElementById('create-sysname').value; - var myData = JSON.stringify([{'sysname':sysname}]); - $.ajax({ - type: "POST", - url: "/api/write/switch-add", - dataType: "text", - data:myData, - success: function (data, textStatus, jqXHR) { - var result = JSON.parse(data); - if(result.switches_addded.length > 0) { // FIXME unresolved variable switches_addded - nmsInfoBox.hide(); - } - nmsData.invalidate("switches"); - nmsData.invalidate("smanagement"); - } - }); - } +nmsInfoBox.click = function(sw) +{ + this.showWindow("switchInfo",sw); }; /* - * Window type: Switch info + * Window handler * - * Advanced window with information about a specific switch, and basic editing options + * 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 + * needed in a window or view. * - * Custom interfaces: showSummary, showInfoTable, showComments, showSNMP, showEdit, save + * Does primarily rely on an imported list of panel types and window types to + * display stuff, but has methods for manual overrides if needed. + * + * Panels can use the doInPanel(panelId,functionName,arguments) method to pass + * actions to themselves if needed. * */ -nmsInfoBox._windowTypes.switchInfo = { - title: '', - content: '', - childContent: false, - sw: '', - swi: '', - swm: '', - commentsHash: false, - activeView: '', - load: function(sw) { - if(sw) { - this.sw = sw; - } - if(!this.swi) { - try { - this.swi = nmsData.switches["switches"][this.sw]; - } catch(e) { - this.swi = []; - } +var windowHandler = function () { + this.containerObj = false; + this.titleObj = false; + this.navObj = false; + this.bodyObj = false; + this._panels = {}; + this._view = ""; + this._window = {}; + this.windowTypes = false; + this.panelTypes = false; + this.argument = false; + this.show = function () { + this.containerObj.classList.remove("hidden"); + }; + this.hide = function () { + this.containerObj.classList.add("hidden"); + }; + this.setContainerObj = function (containerObj) { + this.containerObj = containerObj; + }; + this.setTitleObj = function (titleObj) { + this.titleObj = titleObj; + }; + this.setNavObj = function (navObj) { + this.navObj = navObj; + }; + this.setBodyObj = function (bodyObj) { + this.bodyObj = bodyObj; + }; + this.setPanelTypes = function (panelTypes) { + this.panelTypes = panelTypes; + }; + this.setWindowTypes = function (windowTypes) { + this.windowTypes = {}; + for(var i in windowTypes) { + this.windowTypes[windowTypes[i].id] = windowTypes[i]; } - if(!this.swm) { - try { - this.swm = nmsData.smanagement.switches[this.sw]; - } catch(e) { - this.swm = []; - } + }; + this.setArgument = function (argument) { + if(this.argument != argument) { + this.argument = argument; + this.showView(this._view); } - nmsInfoBox._windowTypes.switchInfo.showSummary(); - nmsData.addHandler("ticker","switchInfo",nmsInfoBox._windowTypes.switchInfo.update,"tick"); - }, - update: function(type) { - switch (type) { - case 'comments': - if(this.activeView == "summary" && this.commentsHash != nmsData.comments.hash) { - nmsInfoBox._windowTypes.switchInfo.showComments(); - } - break; + }; + this.showPanel = function (panelName) { + var panelArray = panelName.split(":"); + var panelName = panelArray[0]; + var panelMode = panelArray[1]; + if(this.panelTypes[panelName]) { + var id = (Math.random().toString(36)+'00000000000000000').slice(2, 10+2); + var panel = new this.panelTypes[panelName]; + panel.setId(id); + if(!!panelMode) + panel.setMode(panelMode); + panel.load(this.bodyObj,this.argument); + this._panels[id] = panel; } - }, - getTitle: function() { - var sshButton = ''; - try { - var mgmt = nmsInfoBox._window.swm.mgmt_v4_addr; - sshButton = mgmt.split("/")[0]; - } catch(e) {} - if(sshButton != null && sshButton != undefined && sshButton != '') { - sshButton = ' <button type="button" class="ssh btn btn-xs btn-default"><a href="ssh://' + sshButton + '">SSH</a></button>'; + }; + this.showTitle = function (title) { + this.titleObj.innerHTML = '<button type="button" class="close" aria-label="Close" onclick="nmsInfoBox.hide();" style="float: right;"><span aria-hidden="true">×</span></button><h4>' + title + '</h4>'; + }; + this.showNav = function () { + if(!this._window.views) + this.navObj.innerHTML = ''; + var output = '<ul class="nav nav-pills small">'; + var i = 0; + for(var view in this._window.views) { + var viewObj = this._window.views[view]; + var active = ''; + if(this._view == view) + active = ' class="active" '; + output += '<li' + active + '><a class="' + view + '" aria-label="' + viewObj.name + '" onclick="nmsInfoBox._windowHandler.showView(\'' + view + '\');">' + viewObj.name + '</a></li> '; + i++; } - return '<h4>' + this.sw + '</h4><button type="button" class="edit btn btn-xs btn-warning" onclick="nmsInfoBox._windowTypes.switchInfo.showEdit();">Edit</button> <button type="button" class="summary btn btn-xs btn-default" onclick="nmsInfoBox._windowTypes.switchInfo.showSummary();">Summary</button> <button type="button" class="details btn btn-xs btn-default" onclick="nmsInfoBox._windowTypes.switchInfo.showInfoTable();">Details</button> <button type="button" class="edit btn btn-xs btn-default" onclick="nmsInfoBox._windowTypes.switchInfo.showSNMP(\'ports\');">Ports</button> <button type="button" class="edit btn btn-xs btn-default" onclick="nmsInfoBox._windowTypes.switchInfo.showSNMP(\'misc\');">Misc</button>' + sshButton; - }, - getContent: function() { - return this.content; - }, - getChildContent: function() { - return this.childContent; - }, - showSummary: function(argument) { - this.activeView = "summary"; - 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); - } - } 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) {} - - content.push(["Ping latency:",(nmsData.ping.switches[this.sw].latency + " ms" || undefined)]); - 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):",(this.swm.mgmt_v4_addr || '')]); - content.push(["Management (v6):",(this.swm.mgmt_v6_addr || '')]); - content.push(["Subnet (v4):",(this.swm.subnet4 || '')]); - content.push(["Subnet (v6):",(this.swm.subnet6 || '')]); - - var contentCleaned = []; - for(var i in content) { - if(content[i][1] == '' || content[i][1] == null) - continue; - if(content[i][1] == undefined || content[i][1]) - content[i][1] == "No data"; - contentCleaned.push(content[i]); + output += '</ul>'; + if(i < 2) { + this.navObj.innerHTML = ''; + } else { + this.navObj.innerHTML = output; } - - var table = nmsInfoBox._makeTable(contentCleaned); - table.id = this.sw + "-summary"; - - this.content = table; - - if(argument == "tick") { - var myObj = document.getElementById(this.sw + "-summary"); - var oldObj = myObj.parentNode.replaceChild(this.content,myObj); + }; + this.addWindow = function (windowObj) { + this.windowTypes[windowObj.id] = windowObj; + }; + this.showWindow = function (windowName,argument) { + if(!this.windowTypes[windowName]) return; + this.unloadWindow(); + this.argument = argument; + this._window = this.windowTypes[windowName]; + this.showTitle(this._window.title); + this.showView(); + this.show(); + }; + this.showView = function (viewId) { + if(!viewId || viewId == '') + viewId = "initial"; + if(!this._window.views || !this._window.views[viewId]) + return; + this.unloadView(); + for(var panel in this._window.views[viewId].panels) { + this.showPanel(this._window.views[viewId].panels[panel]); } - - this.childContent = ''; - nmsInfoBox._windowTypes.switchInfo.showComments(); - nmsInfoBox.refresh("soft"); - }, - showInfoTable: function() { - this.activeView = "infotable"; - var content = []; - - for (var v in this.swi) { - if (v == "placement") { - var place = JSON.stringify(this.swi[v]); - content.push([v,place]); - continue; + this._view = viewId; + this.showNav(); + }; + this.removePanel = function (panelId) { + if(!!panelId) + this.unloadPanel(panelId); + }; + this.unloadView = function () { + this.unloadPanel(); + }; + this.unloadWindow = function() { + this.hide(); + this.unloadPanel(); + }; + this.unloadPanel = function (panelId) { + if(!panelId) { + for(var i in this._panels) { + this._panels[i].unload(); } - content.push([v, this.swi[v]]); - } - - for (var v in this.swm) { - content.push([v, this.swm[v]]); - } - content.sort(); - - var infotable = nmsInfoBox._makeTable(content); - infotable.id = "info-switch-table"; - - this.content = infotable; - this.childContent = ''; - nmsInfoBox.refresh("soft"); - }, - update: function(type) { - switch (type) { - case 'comments': - if(nmsInfoBox._windowTypes.switchInfo.activeView == "summary" && this.commentsHash != nmsData.comments.hash) { - nmsInfoBox._windowTypes.switchInfo.showComments(); - } - break; - case 'tick': - if(nmsInfoBox._windowTypes.switchInfo.activeView == "summary") - nmsInfoBox._windowTypes.switchInfo.showSummary("tick"); - break; + this._panels = {}; + } else { + try { + this._panels[panelId].unload(); + } catch (e) {} + delete this._panels[panelId]; } - }, - showComments: function() { - var domObj = document.createElement("div"); - var comments = []; - - var commentbox = document.createElement("div"); - commentbox.id = "commentbox"; - commentbox.className = "panel-body"; - commentbox.style.width = "100%"; - commentbox.innerHTML = '<div class="input-group"><input type="text" class="form-control" placeholder="Comment" id="' + this.sw + '-comment"><span class=\"input-group-btn\"><button class="btn btn-default" onclick="addComment(\'' + this.sw + '\',document.getElementById(\'' + this.sw + '-comment\').value); document.getElementById(\'' + this.sw + '-comment\').value = \'\'; document.getElementById(\'' + this.sw + '-comment\').placeholder = \'Comment added. Wait for next refresh.\';">Add comment</button></span></div>'; - - // If we have no switch data, so just show comment form - if(!nmsData.comments || !nmsData.comments.comments) { - this.commentsHash = false; - - // We have data, refresh - } else if(nmsData.comments.comments[this.sw]) { - this.commentsHash = nmsData.comments.hash; - for (var c in nmsData.comments.comments[this.sw]["comments"]) { - var comment = nmsData.comments.comments[this.sw]["comments"][c]; - if (comment["state"] == "active" || comment["state"] == "persist" || comment["state"] == "inactive") { - comments.push(comment); - } - } - - if (comments.length > 0) { - var commenttable = nmsInfoBox._makeCommentTable(comments); - commenttable.id = "info-switch-comments-table"; - domObj.appendChild(commenttable); + }; + //Method for triggering a function in an active panel + this.doInPanel = function (panelId, action, argument) { + if(!this._panels[panelId]) + return; + if(typeof this._panels[panelId][action] === "function") { + if(!argument) { + this._panels[panelId][action](); + } else { + this._panels[panelId][action](argument); } - - // We have no data for this switch, but its still correct - } else { - this.commentsHash = nmsData.comments.hash; } + }; +}; - domObj.appendChild(commentbox); - this.childContent = domObj; - nmsInfoBox.refresh("soft"); - }, - showEdit: function() { - this.activeView = "edit"; - var domObj = document.createElement("div"); - var template = {}; - - nmsInfoBox._editValues = {}; - var place; - for (var v in this.swi) { - if (v == "placement") { - place = JSON.stringify(this.swi[v]); - template[v] = place; - continue; - } - template[v] = nmsInfoBox._nullBlank(this.swi[v]); +/* + * Basic info-panel object + * + * Has a basic model of a stand alone panel which is intended to be extended + * upon to implement specific panels: + * + * var myNewPanel = function () { + * nmsInfoPanel.call(this,"myNewPanelId"); + * this.refresh = function (reason) { + * //My custom window function + * this._render(htmlObj); + * }; + * }; + * + */ +var nmsInfoPanel = function nmsInfoPanel(name,id) { + this.name = name; + this.id = id; + this.sw = false; + this.container = false; + this.classList = ['info-panel']; + this.me = false; + this.handlers = []; + this.mode = "initial"; + + if(!this.id) + this.id = (Math.random().toString(36)+'00000000000000000').slice(2, 10+2); + + //Method for loading the general panel properties + this.load = function (container,argument) { + this.container = container; + this.sw = argument; + this.me = document.createElement("div"); + this.me.id = this.id; + for(var i in this.classList) + this.me.classList.add(this.classList[i]); + this.container.appendChild(this.me); + this.init(argument); + }; + + //Method for making this specific panel-instance ready for first refresh + //Override in children when any custom init-functionality is needed + this.init = function (argument) { + this.refresh("init"); + }; + + //Methods for getting and setting panel id + this.setId = function (id) { + this.id = id; + }; + this.getId = function () { + return this.id; + }; + + //Methods for setting and getting mode (default "initial") + this.setMode = function (mode) { + this.mode = mode; + }; + this.getMode = function () { + return this.mode; + }; + + //Internal method for rendering content + this._render = function (newContent) { + if(!newContent || !this.me) + return; + this.me.innerHTML = newContent.outerHTML; + }; + + //Helper method for rendering error messages in a unified way + this._renderError = function (message) { + var error = document.createElement("div"); + error.className = "alert alert-info"; + error.innerHTML = message; + this._render(error); + }; + + //Method for unloading any local data + this.unload = function () { + if(!this.me) + return; + this.me.remove(); + this.sw = false; + this.container = false; + this.me = false; + this.id = false; + this.removeHandlers(); + }; + + //Method for loading new data and triggering a _render if needed + //Implemented in children only + this.refresh = function (reason) {}; + + //Methods for adding and removing classes + this.addClass = function (className) { + if(this.classList.indexOf(className) == -1) { + this.classList.push(className); + this.me.classList.add(className); } - for (var v in this.swm) { - template[v] = nmsInfoBox._nullBlank(this.swm[v]); + }; + this.removeClass = function (className) { + var index = this.classList.indexOf(className); + if(index != -1) { + this.classList.splice(index,1); + this.me.classList.remove(className); } - var content = []; - for (v in template) { - 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 + '/>'; - content.push([v, html]); + }; + + //Method for registering a data handler (lets us clean them up later) + this.addHandler = function (dataType,targetFunction) { + if(!targetFunction) + targetFunction = "refresh"; + nmsData.addHandler(dataType,this.id,(this[targetFunction]).bind(this),"handler-"+dataType); + this.handlers.push(dataType); + }; + + //Method for removing all handlers we have registered + this.removeHandlers = function () { + for(var i in this.handlers) { + nmsData.unregisterHandler(this.handlers[i],this.id); } + }; - content.sort(); - - var table = nmsInfoBox._makeTable(content, "edit"); - domObj.appendChild(table); - - var submit = document.createElement("button"); - submit.innerHTML = "Save changes"; - submit.classList.add("btn", "btn-primary"); - submit.id = "edit-submit-" + this.sw; - submit.onclick = function(e) { nmsInfoBox._windowTypes.switchInfo.save(); }; - domObj.appendChild(submit); - - var output = document.createElement("output"); - output.id = "edit-output"; - domObj.appendChild(output); - - if (place) { - var pval = document.getElementById("edit-" + this.sw + "-placement"); - if (pval) { - pval.value = place; - } + //Method for checking if we have handlers registered + this.hasHandler = function (dataType) { + for(var i in this.handlers) { + if(this.handlers[i] == dataType) + return true; } + return false; + }; +}; - this.content = domObj; - this.childContent = ''; - nmsInfoBox.refresh("soft"); - }, - showSNMP: function(tree) { - this.activeView = "snmp"; +/* + * Panel type: Switch SNMP information + * + * Displays a list of available SNMP data from a set SNMP-group (Ex. "Ports", "Misc") + * + * TODO: Clean up html-generator code. + * + */ +var switchSNMPPanel = function () { + nmsInfoPanel.call(this,"switchSNMP"); + this.init = function() { + this.addHandler("snmp"); + this.refresh(); + }; + this.refresh = function(reason) { var domObj = document.createElement("div"); domObj.classList.add("panel-group"); try { - var snmpJson = nmsData.snmp.snmp[this.sw][tree]; + var snmpJson = nmsData.snmp.snmp[this.sw][this.mode]; } catch(e) { - this.content = "(no recent data (yet)?)"; + this._renderError("Waiting for data."); return; } - /* - * This html-generation code seems unnecessary complex. Must be a - * cleaner way to do this. But not today. - */ for(var obj in snmpJson) { var cleanObj = obj.replace(/\W+/g, ""); @@ -497,127 +530,151 @@ nmsInfoBox._windowTypes.switchInfo = { domObj.appendChild(groupObj); } - this.content = domObj; - this.childContent = ''; - nmsInfoBox.refresh("soft"); - }, - unload: function() { - this.title = ''; - this.content = ''; - this.childContent = false; - this.sw = ''; - this.swi = ''; - this.swm = ''; - this.commentsHash = false; - this.activeView = ''; - nmsData.unregisterHandler("ticker","switchInfo"); - }, - save: function() { - var myData = nmsInfoBox._editStringify(this.sw); + this._render(domObj); + }; +}; +nmsInfoBox.addPanelType("switchSNMP",switchSNMPPanel); + +/* + * Panel type: Switch details + * + * Displays a table of switch information + * + */ +var switchDetailsPanel = function() { + nmsInfoPanel.call(this,"switchDetails"); + this.refresh = function(reason) { + var swi = []; + var swm = []; + try { + swi = nmsData.switches["switches"][this.sw]; + } catch(e) {} + try { + swm = nmsData.smanagement.switches[this.sw]; + } catch(e) {} + + var content = []; + + for (var v in swi) { + if (v == "placement") { + var place = JSON.stringify(swi[v]); + content.push([v,place]); + continue; + } + content.push([v, swi[v]]); + } + + for (var v in swm) { + content.push([v, swm[v]]); + } + content.sort(); + + var infotable = nmsInfoBox._makeTable(content); + + this._render(infotable); + }; +}; +nmsInfoBox.addPanelType("switchDetails",switchDetailsPanel); + +/* + * Panel type: Add switch + * + * Lets you add a new switch using the switch-add api + * + */ +var switchAddPanel = function() { + nmsInfoPanel.call(this,"switchAdd"); + this.refresh = function(reason) { + var domObj = document.createElement("div"); + domObj.innerHTML = '<input type="text" class="form-control" id="create-sysname" placeholder="Sysname id"><button class="btn btn-default" onclick="nmsInfoBox._windowHandler.doInPanel(\'' + this.id +'\',\'save\');">Add switch</button>' + this._render(domObj); + }; + this.save = function () { + var sysname = document.getElementById('create-sysname').value; + var myData = JSON.stringify([{'sysname':sysname}]); $.ajax({ type: "POST", - url: "/api/write/switch-update", + url: "/api/write/switch-add", dataType: "text", data:myData, success: function (data, textStatus, jqXHR) { var result = JSON.parse(data); - if(result.switches_updated.length > 0) { // FIXME unresolved variable switches_addded + if(result.switches_addded.length > 0) { // FIXME unresolved variable switches_addded nmsInfoBox.hide(); } nmsData.invalidate("switches"); nmsData.invalidate("smanagement"); } }); - } + }; }; +nmsInfoBox.addPanelType("switchAdd",switchAddPanel); /* - * Window type: Show inventory listing + * Panel type: Inventory listing * - * Basic window that displays a list of all devices with simple summary information + * Displays a filterable table with switch data, based on a selected mode * - * TODO: Set up more complex views with more columns, sorting, etc. + * TODO: + * - Add support for multiple columns with data + * - Add sorting + * - Add live filtering + * - Add export options? * */ -nmsInfoBox._windowTypes.inventoryListing = { - content: '', - childContent: false, - activeView: '', - activeFilter: '', - getTitle: function() { - return '<h4>Inventory listing</h4><button type="button" class="distro-name btn btn-xs btn-default" onclick="nmsInfoBox.showWindow(\'inventoryListing\',\'distro_name\');">Distro name</button> <button type="button" class="distro-name btn btn-xs btn-default" onclick="nmsInfoBox.showWindow(\'inventoryListing\',\'sysDescr\');">System Description</button><button type="button" class="distro-name btn btn-xs btn-default" onclick="nmsInfoBox.showWindow(\'inventoryListing\',\'jnxBoxSerialNo\');">Serial Numbers</button>'; - }, - getContent: function() { - return this.content; - }, - getChildContent: function() { - return this.childContent; - }, - setFilter: function(filter) { - this.activeFilter = filter.toLowerCase(); - nmsInfoBox._windowTypes.inventoryListing.load("refresh"); - }, - getFilter: function() { - return this.activeFilter; - }, - load: function(list) { - var hasSnmp = false; +var inventoryListingPanel = function() { + nmsInfoPanel.call(this,"inventoryListing"); + this.filter = ""; + this.init = function (mode) { + if(!nmsData.snmp || !nmsData.snmp.snmp) { + if(!this.hasHandler("snmp")) { + this.addHandler("snmp","init"); + this._renderError("Waiting for SNMP data."); + } + return; + } else { + this.removeHandlers(); + if(!!mode && this.mode == "initial") + this.setMode(mode); + this.refresh("init"); + } + }; + this.setFilter = function (filter) { + this.filter = filter; + this.refresh(); + }; + this.refresh = function (reason) { var targetArray = []; var listTitle = ''; - var needRefresh = false; - var needSnmp = false; var contentObj = document.createElement("div"); var inputObj = document.createElement("div"); - inputObj.innerHTML = '<div class="input-group"><input type="text" class="form-control" placeholder="Filter" id="inventorylisting-filter" value="' + this.activeFilter + '" onkeyup="if (event.keyCode == 13) {nmsInfoBox._windowTypes.inventoryListing.setFilter(document.getElementById(\'inventorylisting-filter\').value);}"><span class=\"input-group-btn\"><button class="btn btn-default" onclick="nmsInfoBox._windowTypes.inventoryListing.setFilter(document.getElementById(\'inventorylisting-filter\').value);">Filtrer</button></span></div>'; + inputObj.innerHTML = '<div class="input-group"><input type="text" class="form-control" placeholder="Filter" id="inventorylisting-filter" value="' + this.filter + '" onkeyup="if (event.keyCode == 13) {nmsInfoBox._windowHandler.doInPanel(\'' + this.id + '\',\'setFilter\',document.getElementById(\'inventorylisting-filter\').value);}"><span class=\"input-group-btn\"><button class="btn btn-default" onclick="nmsInfoBox._windowHandler.doInPanel(\'' + this.id + '\',\'setFilter\',document.getElementById(\'inventorylisting-filter\').value);">Filtrer</button></span></div>'; contentObj.appendChild(inputObj); - - if(!nmsData.switches || !nmsData.switches.switches) - return; - if(!(!nmsData.snmp || !nmsData.snmp.snmp)) { - hasSnmp = true; - } - if(list == "refresh") { - list = this.activeView; - needRefresh = true; - } - - switch (list) { + switch (this.mode) { case 'distro_name': listTitle = 'Distro names'; break; case 'sysDescr': - if(hasSnmp) - listTitle = 'System description'; - needSnmp = true; + listTitle = 'System description'; break; case 'jnxBoxSerialNo': - if(hasSnmp) - listTitle = 'Serial Numbers'; - needSnmp = true; + listTitle = 'Serial Numbers'; break; default: listTitle = 'Distro names'; - list = 'distro_name'; - } - this.activeView = list; - - if(needSnmp && !hasSnmp) { - this.content = "No SNMP data loaded. Reloading shortly."; - nmsData.addHandler("snmp","inventoryListing",nmsInfoBox._windowTypes.inventoryListing.update,"snmp-request"); - return; } var resultArray = []; for(var sw in nmsData.switches.switches) { var value = ''; - if(this.activeFilter != '') { - if(sw.toLowerCase().indexOf(this.activeFilter) == -1 && !nmsInfoBox._searchSmart(this.activeFilter,sw)) + if(this.filter != '') { + if(sw.toLowerCase().indexOf(this.filter) == -1 && !nmsInfoBox._searchSmart(this.filter,sw)) continue; } try { - switch (list) { + switch (this.mode) { case 'distro_name': value = nmsData.switches.switches[sw]["distro_name"]; break; @@ -628,9 +685,7 @@ nmsInfoBox._windowTypes.inventoryListing = { value = nmsData.snmp.snmp[sw]["misc"]["jnxBoxSerialNo"][0]; break; } - } catch (e) { - //console.log(e); - } + } catch (e) {} resultArray.push([sw, value]); } @@ -640,34 +695,270 @@ nmsInfoBox._windowTypes.inventoryListing = { infotable.id = "inventory-table"; contentObj.appendChild(infotable); - this.content = contentObj; - if(needRefresh) - nmsInfoBox.refresh("soft"); - }, - update: function(type) { - if(type == "snmp-request") { - nmsData.unregisterHandler("snmp","inventoryListing"); - nmsInfoBox._windowTypes.inventoryListing.load("refresh"); + this._render(contentObj); + }; +}; +nmsInfoBox.addPanelType("inventoryListing",inventoryListingPanel); + +/* + * Panel type: Edit switch + * + * Lets you edit basic switch and switch management data through the switch-update api + * + */ +var switchEditPanel = function () { + nmsInfoPanel.call(this,"switchEdit"); + this.refresh = function (reason) { + var swi = []; + var swm = []; + try { + swi = nmsData.switches["switches"][this.sw]; + } catch(e) {} + try { + swm = nmsData.smanagement.switches[this.sw]; + } catch(e) {} + + var domObj = document.createElement("div"); + var template = {}; + + nmsInfoBox._editValues = {}; + var place; + for (var v in swi) { + if (v == "placement") { + place = JSON.stringify(swi[v]); + template[v] = place; + continue; + } + template[v] = nmsInfoBox._nullBlank(swi[v]); } - }, - unload: function() { - nmsData.unregisterHandler("snmp","inventoryListing"); - this.content = ''; - this.activeView = ''; - this.activeFilter = ''; - }, - save: function() { - } + for (var v in swm) { + template[v] = nmsInfoBox._nullBlank(swm[v]); + } + var content = []; + for (v in template) { + 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 + '>'; + content.push([v, html]); + } + + content.sort(); + + var table = nmsInfoBox._makeTable(content, "edit"); + domObj.appendChild(table); + + var submit = document.createElement("button"); + submit.innerHTML = "Save changes"; + submit.classList.add("btn", "btn-primary"); + submit.id = "edit-submit-" + this.sw; + submit.setAttribute("onclick","nmsInfoBox._windowHandler.doInPanel('" + this.id + "','save');"); + domObj.appendChild(submit); + + var output = document.createElement("output"); + output.id = "edit-output"; + domObj.appendChild(output); + + if (place) { + var pval = document.getElementById("edit-" + this.sw + "-placement"); + if (pval) { + pval.value = place; + } + } + + this._render(domObj); + }; + this.save = function () { + var myData = nmsInfoBox._editStringify(this.sw); + $.ajax({ + type: "POST", + url: "/api/write/switch-update", + dataType: "text", + data:myData, + success: function (data, textStatus, jqXHR) { + var result = JSON.parse(data); + if(result.switches_updated.length > 0) { // FIXME unresolved variable switches_addded + nmsInfoBox.hide(); + } + nmsData.invalidate("switches"); + nmsData.invalidate("smanagement"); + } + }); + }; }; +nmsInfoBox.addPanelType("switchEdit",switchEditPanel); /* - * Click a switch and display it - * it. + * Panel type: Switch comments + * + * 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 + * */ -nmsInfoBox.click = function(sw) -{ - this.showWindow("switchInfo",sw); +var switchCommentsPanel = function () { + nmsInfoPanel.call(this,"switchComments"); + this.commentsHash = false; + this.refresh = function (reason) { + var domObj = document.createElement("div"); + var comments = []; + + var commentbox = document.createElement("div"); + commentbox.id = "commentbox"; + commentbox.className = "panel-body"; + commentbox.style.width = "100%"; + commentbox.innerHTML = '<div class="input-group"><input type="text" class="form-control" placeholder="Comment" id="' + this.sw + '-comment"><span class=\"input-group-btn\"><button class="btn btn-default" onclick="addComment(\'' + this.sw + '\',document.getElementById(\'' + this.sw + '-comment\').value); document.getElementById(\'' + this.sw + '-comment\').value = \'\'; document.getElementById(\'' + this.sw + '-comment\').placeholder = \'Comment added. Wait for next refresh.\';">Add comment</button></span></div>'; + + // We have data + if(!(!nmsData.comments || !nmsData.comments.comments)) { + this.commentsHash = nmsData.comments.hash; + + // We have data for this switch + if(nmsData.comments.comments[this.sw]) { + this.commentsHash = nmsData.comments.hash; + for (var c in nmsData.comments.comments[this.sw]["comments"]) { + var comment = nmsData.comments.comments[this.sw]["comments"][c]; + if (comment["state"] == "active" || comment["state"] == "persist" || comment["state"] == "inactive") { + comments.push(comment); + } + } + + if (comments.length > 0) { + var commenttable = nmsInfoBox._makeCommentTable(comments); + commenttable.id = "info-switch-comments-table"; + domObj.appendChild(commenttable); + } + + } + } + + domObj.appendChild(commentbox); + this._render(domObj); + }; +}; +nmsInfoBox.addPanelType("switchComments",switchCommentsPanel); + +/* + * Panel type: Switch summary + * + * Display a live summary of key metrics + * + */ +var switchSummaryPanel = function() { + nmsInfoPanel.call(this,"switchSummary"); + this.init = function() { + this.addHandler("ticker"); + this.refresh(); + }; + 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); + } + } 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) { + if(content[i][1] == '' || content[i][1] == null) + continue; + if(content[i][1] == undefined || content[i][1]) + content[i][1] == "No data"; + contentCleaned.push(content[i]); + } + + var table = nmsInfoBox._makeTable(contentCleaned); + + this._render(table); + }; }; +nmsInfoBox.addPanelType("switchSummary",switchSummaryPanel); /* * General-purpose table-maker? |