"use strict";
/*
* NMS info window controller
*
* Interface: nmsInfoBox.showWindow(windowType,optionalParameter), nmsInfoBox.hide(), nmsInfoBox.refresh()
*
* Any windowTypes should at a minimum implement load, update, unload, getTitle, getContent, getChildContent
*
*/
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
};
/*
* Shows a window from the _windowTypes list
*/
nmsInfoBox.showWindow = function (windowName,argument) {
if(windowName == "switchInfo" && argument != '' && argument == this._window.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);
};
nmsInfoBox.update = function(argument) {
if(!nmsInfoBox._window)
return;
nmsInfoBox._window.update(argument);
}
/*
* Internal function to show the active _window and pass along any arguments
*/
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");
var title = document.createElement("div");
title.classList.add("panel-heading");
var body = document.createElement("div");
body.classList.add("panel-body");
title.innerHTML = this._window.getTitle() + '';
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);
}
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');
});
};
/*
* 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");
};
/*
* Window type: Add Switch
*
* Basic window that lets you create a new switch
*
*/
nmsInfoBox._windowTypes.addSwitch = {
title: 'Add new switch',
content: '',
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");
}
});
}
};
/*
* Window type: Switch info
*
* Advanced window with information about a specific switch, and basic editing options
*
* Custom interfaces: showSummary, showInfoTable, showComments, showSNMP, showEdit, save
*
*/
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 = [];
}
}
if(!this.swm) {
try {
this.swm = nmsData.smanagement.switches[this.sw];
} catch(e) {
this.swm = [];
}
}
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;
}
},
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 = ' ';
}
return '
' + this.sw + '
' + 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]);
}
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);
return;
}
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;
}
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;
}
},
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 = '
';
// 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);
}
// 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]);
}
for (var v in this.swm) {
template[v] = nmsInfoBox._nullBlank(this.swm[v]);
}
var content = [];
for (v in template) {
var tmpsw = '\'' + this.sw + '\'';
var tmpv = '\'' + v + '\'';
var tmphandler = '"nmsInfoBox._editChange(' + tmpsw + ',' + tmpv + ');"';
var html = "';
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.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;
}
}
this.content = domObj;
this.childContent = '';
nmsInfoBox.refresh("soft");
},
showSNMP: function(tree) {
this.activeView = "snmp";
var domObj = document.createElement("div");
domObj.classList.add("panel-group");
try {
var snmpJson = nmsData.snmp.snmp[this.sw][tree];
} catch(e) {
this.content = "(no recent data (yet)?)";
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, "");
var groupObj = document.createElement("div");
groupObj.classList.add("panel","panel-default");
groupObj.innerHTML = '' + obj + '';
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 = '
' + prop + '
' + snmpJson[obj][prop] + '
';
tbody.appendChild(propObj);
}
tableObj.appendChild(tbody);
panelBodyObj.appendChild(tableObj);
groupObjCollapse.appendChild(panelBodyObj);
groupObj.appendChild(groupObjCollapse);
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);
$.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");
}
});
}
};
/*
* Window type: Show inventory listing
*
* Basic window that displays a list of all devices with simple summary information
*
* TODO: Set up more complex views with more columns, sorting, etc.
*
*/
nmsInfoBox._windowTypes.inventoryListing = {
content: '',
childContent: false,
activeView: '',
activeFilter: '',
getTitle: function() {
return '
Inventory listing
';
},
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 targetArray = [];
var listTitle = '';
var needRefresh = false;
var needSnmp = false;
var contentObj = document.createElement("div");
var inputObj = document.createElement("div");
inputObj.innerHTML = '
';
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) {
case 'distro_name':
listTitle = 'Distro names';
break;
case 'sysDescr':
if(hasSnmp)
listTitle = 'System description';
needSnmp = true;
break;
case 'jnxBoxSerialNo':
if(hasSnmp)
listTitle = 'Serial Numbers';
needSnmp = true;
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))
continue;
}
try {
switch (list) {
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) {
//console.log(e);
}
resultArray.push([sw, value]);
}
resultArray.sort();
var infotable = nmsInfoBox._makeTable(resultArray,listTitle);
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");
}
},
unload: function() {
nmsData.unregisterHandler("snmp","inventoryListing");
this.content = '';
this.activeView = '';
this.activeFilter = '';
},
save: function() {
}
};
/*
* Click a switch and display it
* it.
*/
nmsInfoBox.click = function(sw)
{
this.showWindow("switchInfo",sw);
};
/*
* 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 = '
';
txt += '';
txt += '';
txt += '
';
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]);
};