aboutsummaryrefslogtreecommitdiffstats
path: root/web/js/nms-info-box.js
diff options
context:
space:
mode:
Diffstat (limited to 'web/js/nms-info-box.js')
-rw-r--r--web/js/nms-info-box.js1177
1 files changed, 1177 insertions, 0 deletions
diff --git a/web/js/nms-info-box.js b/web/js/nms-info-box.js
new file mode 100644
index 0000000..f4dcb49
--- /dev/null
+++ b/web/js/nms-info-box.js
@@ -0,0 +1,1177 @@
+"use strict";
+
+/*
+ * NMS info-window controller
+ *
+ * Interface: nmsInfoBox.showWindow(windowType,optionalParameter), nmsInfoBox.hide()
+ *
+ * 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
+ _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, and triggers initial load if needed
+ */
+nmsInfoBox.showWindow = function (windowName,argument) {
+ if(windowName == "switchInfo" && argument != '' && argument == this._sw) {
+ nmsInfoBox.hide();
+ return;
+ }
+
+ if(!this._container)
+ this._load();
+ if(!windowName)
+ windowName = 'switchInfo';
+
+ this._sw = argument;
+
+ this._windowHandler.showWindow(windowName,argument)
+ this._container.style.display = "block";
+};
+
+/*
+ * Internal function to load and register the initial html objects
+ */
+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");
+
+ infoBox.appendChild(title);
+ infoBox.appendChild(nav);
+ infoBox.appendChild(body);
+
+ 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() {
+ this._sw = false;
+ this._windowHandler.hide();
+ this._windowHandler.unloadWindow();
+};
+
+/*
+ * Click a switch and display it.
+ */
+nmsInfoBox.click = function(sw)
+{
+ this.showWindow("switchInfo",sw);
+};
+
+/*
+ * Window handler
+ *
+ * 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.
+ *
+ * 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.
+ *
+ */
+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];
+ }
+ };
+ this.setArgument = function (argument) {
+ if(this.argument != argument) {
+ this.argument = argument;
+ this.showView(this._view);
+ }
+ };
+ 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;
+ }
+ };
+ 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">&times;</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++;
+ }
+ output += '</ul>';
+ if(i < 2) {
+ this.navObj.innerHTML = '';
+ } else {
+ this.navObj.innerHTML = output;
+ }
+ };
+ 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._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();
+ }
+ this._panels = {};
+ } else {
+ try {
+ this._panels[panelId].unload();
+ } catch (e) {}
+ delete this._panels[panelId];
+ }
+ };
+ //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);
+ }
+ }
+ };
+};
+
+/*
+ * 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);
+ }
+ };
+ this.removeClass = function (className) {
+ var index = this.classList.indexOf(className);
+ if(index != -1) {
+ this.classList.splice(index,1);
+ this.me.classList.remove(className);
+ }
+ };
+
+ //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);
+ }
+ };
+
+ //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;
+ };
+};
+
+/*
+ * 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][this.mode];
+ } catch(e) {
+ this._renderError("Waiting for data.");
+ return;
+ }
+
+ for(var obj in snmpJson) {
+
+ var cleanObj = obj.replace(/\W+/g, "");
+
+ var groupObj = document.createElement("div");
+ groupObj.classList.add("panel","panel-default");
+ groupObj.innerHTML = '<a class="panel-heading collapse-controller" style="display:block;" role="button" data-toggle="collapse" href="#'+cleanObj+'-group">' + obj + '</a>';
+
+ var groupObjCollapse = document.createElement("div");
+ groupObjCollapse.id = cleanObj + "-group";
+ groupObjCollapse.classList.add("collapse");
+
+ var panelBodyObj = document.createElement("div");
+ panelBodyObj.classList.add("panel-body");
+
+ var tableObj = document.createElement("table");
+ tableObj.classList.add("table","table-condensed");
+
+ var tbody = document.createElement("tbody");
+
+ for(var prop in snmpJson[obj]) {
+ var propObj = document.createElement("tr");
+ propObj.innerHTML = '<td>' + prop + '</td><td>' + snmpJson[obj][prop] + '</td>';
+ tbody.appendChild(propObj);
+ }
+
+ tableObj.appendChild(tbody);
+ panelBodyObj.appendChild(tableObj);
+ groupObjCollapse.appendChild(panelBodyObj);
+ groupObj.appendChild(groupObjCollapse);
+ domObj.appendChild(groupObj);
+
+ }
+
+ 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-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.addPanelType("switchAdd",switchAddPanel);
+
+/*
+ * Panel type: Inventory listing
+ *
+ * Displays a filterable table with switch data, based on a selected mode
+ *
+ * TODO:
+ * - Add support for multiple columns with data
+ * - Add sorting
+ * - Add live filtering
+ * - Add export options?
+ *
+ */
+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 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.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);
+
+ switch (this.mode) {
+ case 'distro_name':
+ listTitle = 'Distro names';
+ break;
+ case 'sysDescr':
+ listTitle = 'System description';
+ break;
+ case 'jnxBoxSerialNo':
+ listTitle = 'Serial Numbers';
+ break;
+ default:
+ listTitle = 'Distro names';
+ }
+
+ var resultArray = [];
+ for(var sw in nmsData.switches.switches) {
+ var value = '';
+ if(this.filter != '') {
+ if(sw.toLowerCase().indexOf(this.filter) == -1 && !nmsInfoBox._searchSmart(this.filter,sw))
+ continue;
+ }
+ try {
+ switch (this.mode) {
+ case 'distro_name':
+ value = nmsData.switches.switches[sw]["distro_name"];
+ break;
+ case 'sysDescr':
+ value = nmsData.snmp.snmp[sw]["misc"]["sysDescr"][0];
+ break;
+ case 'jnxBoxSerialNo':
+ value = nmsData.snmp.snmp[sw]["misc"]["jnxBoxSerialNo"][0];
+ break;
+ }
+ } catch (e) {}
+ resultArray.push([sw, value]);
+ }
+
+ resultArray.sort();
+
+ var infotable = nmsInfoBox._makeTable(resultArray,listTitle);
+ infotable.id = "inventory-table";
+
+ contentObj.appendChild(infotable);
+ 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]);
+ }
+ 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);
+
+/*
+ * 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
+ *
+ */
+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?
+ *
+ * Takes an array of arrays as input, and an optional caption.
+ *
+ * E.g.: _makeTable([["name","Kjell"],["Age","five"]], "Age list");
+ */
+nmsInfoBox._makeTable = function(content, caption) {
+ var table = document.createElement("table");
+ var tr;
+ var td1;
+ var td2;
+ table.className = "table";
+ table.classList.add("table");
+ table.classList.add("table-condensed");
+ if (caption != undefined) {
+ var cap = document.createElement("caption");
+ cap.textContent = caption;
+ table.appendChild(cap);
+ }
+ for (var v in content) {
+ tr = table.insertRow(-1);
+ tr.className = content[v][0].toLowerCase();
+ td1 = tr.insertCell(0);
+ td2 = tr.insertCell(1);
+ td1.innerHTML = content[v][0];
+ td2.innerHTML = content[v][1];
+ }
+ return table;
+};
+
+/*
+ * Create and return a table for comments.
+ *
+ * Input is an array of comments.
+ */
+nmsInfoBox._makeCommentTable = function(content) {
+ var table = document.createElement("table");
+ table.className = "table";
+ table.classList.add("table");
+ table.classList.add("table-condensed");
+ var cap = document.createElement("caption");
+ cap.textContent = "Comments"
+ table.appendChild(cap);
+ for (var commentid in content) {
+ var tr;
+ var td1;
+ var td2;
+ var comment = content[commentid];
+ var col;
+ if (comment["state"] == "active")
+ col = "danger";
+ else if (comment["state"] == "inactive")
+ col = false;
+ else
+ col = "info";
+ tr = table.insertRow(-1);
+ tr.id = "commentRow" + comment["id"];
+ tr.className = col;
+
+ td1 = tr.insertCell(0);
+ td1.style.whiteSpace = "nowrap";
+ td1.style.width = "8em";
+ td2 = tr.insertCell(1);
+ var txt = '<div class="btn-group" role="group" aria-label="..."><button type="button" class="btn btn-xs btn-default" data-trigger="focus" data-toggle="popover" title="Info" data-content="Comment added ' + comment["time"] + " by user " + comment["username"] + ' and listed as ' + comment["state"] + '"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span></button>';
+ txt += '<button type="button" class="btn btn-xs btn-danger" data-trigger="focus" data-toggle="tooltip" title="Mark as deleted" onclick="commentDelete(' + comment["id"] + ');"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button>';
+ txt += '<button type="button" class="btn btn-xs btn-success" data-trigger="focus" data-toggle="tooltip" title="Mark as inactive/fixed" onclick="commentInactive(' + comment["id"] + ');"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>';
+ txt += '<button type="button" class="btn btn-xs btn-info" data-trigger="focus" data-toggle="tooltip" title="Mark as persistent" onclick="commentPersist(' + comment["id"] + ');"><span class="glyphicon glyphicon-star" aria-hidden="true"></span></button></div>';
+ td1.innerHTML = txt;
+ td2.innerHTML = comment["comment"];
+ }
+ return table;
+};
+
+nmsInfoBox._searchSmart = function(id, sw) {
+ try {
+ 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(sw.toLowerCase().indexOf(id) > -1) {
+ matches.push(sw);
+ nmsMap.setSwitchHighlight(sw,true);
+ } else 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 "";
+ return x;
+};
+
+nmsInfoBox._editChange = function(sw, v) {
+ var el = document.getElementById("edit-" + sw + "-" + v);
+ var val = el.value;
+ if (v == "placement") {
+ try {
+ val = JSON.parse(val);
+ el.parentElement.classList.remove("has-error");
+ el.parentElement.classList.add("has-success");
+ } catch (e) {
+ el.parentElement.classList.add("has-error");
+ return;
+ }
+ }
+ nmsInfoBox._editValues[v] = val;
+ el.classList.add("has-warning");
+ var myData = nmsInfoBox._editStringify(sw);
+ var out = document.getElementById("edit-output");
+ out.value = myData;
+};
+
+nmsInfoBox._editStringify = function(sw) {
+ nmsInfoBox._editValues['sysname'] = sw;
+ return JSON.stringify([nmsInfoBox._editValues]);
+};