"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:
* - Handler unloading is not working correctly, and many are never removed
* - SSH-management link, this should propably be a custom "view" of sorts
*
* General TODO:
* - Move inventory into a separate tab.
* - Add external windows (timetravel, etc)
* - Take a critical look at what methods/variables should be marked as "_"
* - Currently argument is assumed to be a switch, this should not be the case
* - Add some basic styling to separate panels visually when in the same view
*
*/
/*
* 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': 'Summary',
'panels': ['switchSummary','switchComments']
},
'ports': {
'name': 'Ports',
'panels': ['switchPorts']
},
'misc': {
'name': 'SNMP',
'panels': ['switchSNMP:misc']
},
'details': {
'name': 'Settings',
'panels': ['switchDetails']
},
'edit': {
'name': 'Edit',
'panels': ['switchEdit']
}
}
},
{
'id': 'addSwitch',
'title': 'Add new switch(es)',
'views': {
'initial': {
'name': 'Add switch',
'panels': ['switchAdd']
}
}
},
{
'id': 'searchHelp',
'title': 'Search help',
'views': {
'initial': {
'name': 'Search help',
'panels': ['searchHelp']
}
}
},
{
'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";
$(this._windowHandler.getTitleObj()).on('mousedown', function(e) {
var cont = $(nmsInfoBox._container);
var dimensions = nmsInfoBox._container.getBoundingClientRect();
var startLeft = dimensions.left;
var startTop = dimensions.top;
nmsInfoBox.startDrag(e.screenX, e.screenY, startLeft, startTop);
});
$(this._windowHandler.getTitleObj()).on('mouseup',nmsInfoBox.stopDrag);
};
/*
* 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;
if (this._windowHandler != undefined && this._windowHandler.hide != undefined) {
this._windowHandler.hide();
this._windowHandler.unloadWindow();
}
nmsInfoBox.resetDrag();
};
/*
* Start window drag
*/
nmsInfoBox.startDrag = function(mouseStartX, mouseStartY, startLeft, startTop) {
document.onmousemove = function(e) {
var mouseOffsetX = e.screenX - mouseStartX;
var mouseOffsetY = e.screenY - mouseStartY;
$(nmsInfoBox._container).css('left', startLeft + mouseOffsetX + 'px');
$(nmsInfoBox._container).css('top', startTop + mouseOffsetY + 'px');
};
};
/*
* Reset drag
*/
nmsInfoBox.resetDrag = function() {
nmsInfoBox.stopDrag();
$(nmsInfoBox._container).css('left','');
$(nmsInfoBox._container).css('top','');
};
/*
* Stop window drag
*/
nmsInfoBox.stopDrag = function() {
document.onmousemove = null;
};
/*
* 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 sure these panels are loaded and unloaded when
* needed in a window or view.
*
* Does primarily rely on an imported list of panel types and window types to
* 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._viewId = {};
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.getTitleObj = function (titleObj) {
return this.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 = '×
' + title + ' ';
};
this.showNav = function () {
if(!this._window.views)
this.navObj.innerHTML = '';
var output = '';
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 += '' + viewObj.name + ' ';
i++;
}
output += ' ';
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 + " " + (argument ? argument : ""));
this.showView();
this.show();
};
this.showView = function (viewId) {
if(!viewId || viewId == '') {
if (this._viewId[this._window.id] == undefined)
viewId = "initial";
else
viewId = this._viewId[this._window.id];
} else {
this._viewId[this._window.id] = viewId;
}
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.removeHandlers();
this.sw = false;
this.container = false;
this.me = false;
this.id = false;
};
//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.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","nms-panel-small");
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._render(domObj);
};
};
nmsInfoBox.addPanelType("switchSNMP",switchSNMPPanel);
var switchPortsPanel = function () {
nmsInfoPanel.call(this,"switchPorts");
this.init = function() {
this.refresh();
};
this.refresh = function(reason) {
var domObj = document.createElement("div");
domObj.classList.add("panel-group");
try {
var snmpOldJson;
var snmpJson = nmsData.snmp.snmp[this.sw]['ports'];
if (nmsData.old.snmp != undefined) {
snmpOldJson = nmsData.old.snmp.snmp[this.sw]['ports'];
}
} catch(e) {
this._renderError("Waiting for data.");
return;
}
var img = document.createElement("img");
var i = "totals";
img.src = '/render/?target=cactiStyle(group(aliasByMetric(perSecond(sum(snmp.' + this.sw + '.*.ifHCOutOctets))),aliasByMetric(perSecond(sum(snmp.' + this.sw + '.*.ifHCInOctets)))),"binary")' + nmsInfoBox._graphDefaults(this.sw + "totals");
img.classList.add("graph");
var expanderButton = document.createElement("a");
expanderButton.innerHTML = "Toggle all";
expanderButton.setAttribute("onclick","$('.collapse-top').collapse('toggle');");
expanderButton.setAttribute("role","button");
domObj.appendChild(img);
domObj.appendChild(expanderButton);
var indicies = [];
for (var obj in snmpJson) {
indicies.push(obj);
}
indicies.sort(function(a,b) {
return snmpJson[a].ifIndex - snmpJson[b].ifIndex;
});
for(var obji in indicies) {
var obj = indicies[obji];
var cleanObj = obj.replace(/\W+/g, "");
var groupObj = document.createElement("div");
groupObj.classList.add("panel","panel-default","nms-panel-small");
var glyphicon = "glyphicon-remove";
var button = "btn-danger";
var title = "link down";
if (snmpJson[obj].ifOperStatus == "up") {
glyphicon = "glyphicon-ok";
button = "btn-success";
title = "link up";
}
if (snmpJson[obj].ifAdminStatus == "down") {
glyphicon = "glyphicon-ban-circle";
title = "admin down";
button = "btn-info";
}
var traffic = "";
try {
var nowin = parseInt(snmpJson[obj].ifHCInOctets);
var nowout = parseInt(snmpJson[obj].ifHCOutOctets);
if (!isNaN(nowin) && !isNaN(nowout)) {
traffic = "" + byteCount(nowin) + "B in | " + byteCount(nowout) + "B out ";
}
} catch(e) {};
groupObj.innerHTML = '' + snmpJson[obj].ifDescr + ' ' + snmpJson[obj].ifAlias + ' ' + traffic + ' ';
var groupObjCollapse = document.createElement("div");
groupObjCollapse.id = cleanObj + "-group";
groupObjCollapse.classList.add("collapse");
groupObjCollapse.classList.add("collapse-top");
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");
var props = [];
for (var prop in snmpJson[obj]) {
props.push(prop);
}
props.sort();
for(var index in props) {
var prop = props[index];
var propObj = document.createElement("tr");
var append = "";
var value = snmpJson[obj][prop];
if (!isNaN(parseInt(value))) {
append = byteCount(value, 2);
if (append != value)
append = " (" + append + ")";
else
append = "";
}
propObj.innerHTML = '' + prop + ' ' + value + append + ' ';
tbody.appendChild(propObj);
}
tableObj.appendChild(tbody);
if (snmpJson[obj].ifHCInOctets != 0
|| snmpJson[obj].ifHCOutOctets != 0) {
var img = document.createElement("img");
img.src = '/render/?target=cactiStyle(aliasByMetric(perSecond(snmp.' + this.sw + '.' + obj + '.{ifHCOutOctets,ifHCInOctets})),"binary")&target=cactiStyle(aliasByMetric(secondYAxis(snmp.' + this.sw + '.' + obj + '.{ifInDiscards,ifInErrors,ifOutDiscards,ifOutErrors})),"binary")' + nmsInfoBox._graphDefaults();
img.classList.add("graph");
panelBodyObj.appendChild(img);
}
var tableTopObj = document.createElement("div");
tableTopObj.innerHTML = 'Details ';
var tableTopObjCollapse = document.createElement("div");
tableTopObjCollapse.id = cleanObj + "-table-group";
tableTopObjCollapse.classList.add("collapse");
tableTopObjCollapse.classList.add("collapse-detail");
tableTopObjCollapse.appendChild(tableObj);
tableTopObj.appendChild(tableTopObjCollapse);
panelBodyObj.appendChild(tableTopObj);
//panelBodyObj.appendChild(tableObj);
groupObjCollapse.appendChild(panelBodyObj);
groupObj.appendChild(groupObjCollapse);
domObj.appendChild(groupObj);
}
this._render(domObj);
};
};
nmsInfoBox.addPanelType("switchPorts",switchPortsPanel);
/*
* 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);
var searchHelpPanel = function() {
nmsInfoPanel.call(this,"searchHelp");
this.refresh = function(reason) {
var x = document.createElement("div");
var searchHelp = nmsSearch.helpText;
for (var a in searchHelp) {
var c = document.createElement("p");
c.innerText = searchHelp[a];
x.appendChild(c);
}
this._render(x);
};
};
nmsInfoBox.addPanelType("searchHelp",searchHelpPanel);
/*
* 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 = 'Add switch '
this._render(domObj);
};
this.save = function () {
var sysname = document.getElementById('create-sysname').value.split(" ");
var myData = [];
for (var v in sysname) {
myData.push({"sysname":sysname[v]});
}
var myData = JSON.stringify(myData);
$.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 = 'Filtrer
';
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 && !nmsSearch.searchTest(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;
var tags;
for (var v in swi) {
if (v == "placement") {
place = JSON.stringify(swi[v]);
template[v] = place;
continue;
}
if (v == "tags") {
tags = JSON.stringify(swi[v]);
template[v] = tags;
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 = ' ';
content.push([v, html]);
}
content.sort();
var table = nmsInfoBox._makeTable(content);
domObj.appendChild(table);
var outputCont = document.createElement("div");
outputCont.id = "edit-output-cont";
outputCont.classList.add("collapse");
outputCont.innerHTML = "Request preview ";
var output = document.createElement("output");
output.id = "edit-output";
outputCont.appendChild(output);
domObj.appendChild(outputCont);
var nav = document.createElement("nav");
nav.classList.add("nav","nav-pills");
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');");
nav.appendChild(submit);
var toggleDetails = document.createElement("button");
toggleDetails.innerHTML = '';
toggleDetails.classList.add("btn", "btn-default", "pull-right");
toggleDetails.dataset.toggle = "collapse";
toggleDetails.dataset.target = "#edit-output-cont";
toggleDetails.title = "Show request preview";
toggleDetails.id = "edit-toggle-details-" + this.sw;
nav.appendChild(toggleDetails);
domObj.appendChild(nav);
this._render(domObj);
if (place) {
var pval = document.getElementById("edit-" + this.sw + "-placement");
if (pval) {
pval.value = place;
}
}
if (tags) {
var ptags = document.getElementById("edit-" + this.sw + "-tags");
if (ptags) {
ptags.value = tags;
}
}
};
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
*
*/
var switchCommentsPanel = function () {
nmsInfoPanel.call(this,"switchComments");
this.commentsHash = false;
this.refresh = function (reason) {
var domObj = document.createElement("div");
var comments = [];
var logs = nmsOplog.getSwitchLogs(this.sw);
var table = document.createElement("table");
var tr;
var td1;
var td2;
var td3;
table.className = "table";
table.classList.add("table");
table.classList.add("table-condensed");
var cap = document.createElement("caption");
cap.textContent = "Relevant log entries";
table.appendChild(cap);
for (var v in logs) {
tr = table.insertRow(-1);
tr.className =
td1 = tr.insertCell(0);
td2 = tr.insertCell(1);
var date = new Date(logs[v]['timestamp']);
var month = date.getMonth() + 1;
var day = date.getDate();
var tmp = (date.getYear() + 1900) + "-" + (month < 10 ? "0": "") + month + "-" + (day < 10 ? "0" : "") + day + " " + date.toTimeString().replace(/:\d\d .*$/,"");
td1.textContent = tmp;
td1.classList.add("left");
td2.textContent = "[" + logs[v]['username'] + "] " + logs[v]['log'];
}
domObj.appendChild(table);
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 = [];
if (this.sw == false) {
console.log("ugh, cleanup failed?");
return;
}
var topper = document.createElement("div");
for ( var h in handlers ) {
if (handlers[h].getInfo != undefined) {
var tmp = handlers[h].getInfo(this.sw);
for (var x in tmp.data) {
if (tmp.data[x].value != undefined) {
var d = "" + tmp.data[x].value + '
';
content.push([tmp.data[x].description, d]);
}
}
}
}
var contentCleaned = [];
for(var i in content) {
if(content[i][1] == '' || content[i][1] == null)
continue;
contentCleaned.push(content[i]);
}
var table = nmsInfoBox._makeTable(contentCleaned);
var latency = document.createElement("img");
latency.src = '/render/?target=alias(movingAverage(ping.' + this.sw + '.ipv4,60),"Latency (ms)")&target=alias(secondYAxis(perSecond(sum(snmp.' + this.sw + '.*.{ifInDiscards,ifInErrors}))),"Input errors and discards")&target=alias(secondYAxis(perSecond(sum(snmp.' + this.sw + '.*.{ifOutDiscards,ifOutErrors}))),"Output errors and discards")' + nmsInfoBox._graphDefaults("Click to cycle");
latency.classList.add("graph");
latency.setAttribute("onclick","nmsInfoBox._graphZoom();");
latency.setAttribute("role","button");
topper.appendChild(latency);
topper.appendChild(table);
this._render(topper);
};
};
nmsInfoBox.setLegendPick = function(tag,id) {
if (nms.legendPick != undefined) {
if (nms.legendPick.handler == tag && nms.legendPick.idx == id) {
nms.legendPick = undefined;
return;
}
}
nms.legendPick = {handler: tag, idx: id};
}
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().replace(/[^a-z0-9_]/g,"");
td1 = tr.insertCell(0);
td1.classList.add("left");
td2 = tr.insertCell(1);
td1.innerHTML = content[v][0];
td2.innerHTML = content[v][1];
}
return table;
};
nmsInfoBox._nullBlank = function(x) {
if (x == null || x == false || x == undefined)
return "";
return x;
};
/*
* Provide common defaults for graph renders.
*
* Kept on the URL to avoid having to manage templates since we need to
* manage night mode anyway for now.
*/
nmsInfoBox._graphZoom = function() {
if (nmsInfoBox._graphFrom == "-60min") {
nmsInfoBox._graphFrom = "-6h";
} else if (nmsInfoBox._graphFrom == "-6h") {
nmsInfoBox._graphFrom = "-24h";
} else if (nmsInfoBox._graphFrom == "-24h") {
nmsInfoBox._graphFrom = "-7days";
} else {
nmsInfoBox._graphFrom = "-60min";
}
}
nmsInfoBox._graphFrom = "-60min";
nmsInfoBox._graphUntil = "now";
nmsInfoBox._graphDefaults = function(title) {
// Copied from nms-color-util.js. Could do with expanding.
var colorlist = "337ab7,5cb85c,5bc0de,f0ad4e,d9534f,ffffff";
if (title != undefined) {
title = "From " + nmsInfoBox._graphFrom + " until " + nmsInfoBox._graphUntil + " (" + title + ")";
} else {
title = "From " + nmsInfoBox._graphFrom + " until " + nmsInfoBox._graphUntil;
}
var base = "&yMinLeft=0&yMinRight=0&yMin=0&fontName=courier&width=600&height=240&yUnitSystem=binary&format=svg&colorList=" + colorlist + "&from=" + nmsInfoBox._graphFrom + "&until=" + nmsInfoBox._graphUntil + '&title=' + title;
if (nms.nightMode) {
return "&bgcolor=222222&fgcolor=dddddd&minorGridLineColor=%233d3d3d&majorGridLineColor=%23666666" + base;
} else {
return base;
}
}
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]);
};