447 lines
11 KiB
JavaScript
447 lines
11 KiB
JavaScript
|
angular.module("histogramApp")
|
||
|
.directive("nagiosHistogram", function() {
|
||
|
return {
|
||
|
templateUrl: "histogram-graph.html",
|
||
|
restrict: "AE",
|
||
|
scope: {
|
||
|
cgiurl: "@cgiurl",
|
||
|
reporttype: "@reporttype",
|
||
|
host: "@host",
|
||
|
service: "@service",
|
||
|
timeperiod: "@timeperiod",
|
||
|
t1: "@t1",
|
||
|
t2: "@t2",
|
||
|
breakdown: "@breakdown",
|
||
|
graphevents: "@graphevents",
|
||
|
graphstatetypes: "@graphstatetypes",
|
||
|
assumestateretention: "@assumestateretention",
|
||
|
initialstateslogged: "@initialstateslogged",
|
||
|
ignorerepeatedstates: "@ignorerepeatedstates",
|
||
|
lastUpdate: "=lastUpdate",
|
||
|
reload: "@reload",
|
||
|
build: "&build"
|
||
|
},
|
||
|
controller: function($scope, $element, $attrs, $http,
|
||
|
histogramEventsService, statisticsBreakdown) {
|
||
|
|
||
|
// Layout variables
|
||
|
$scope.fontSize = 11;
|
||
|
$scope.graphMargin = {
|
||
|
top: 40,
|
||
|
right: 290,
|
||
|
bottom: 84,
|
||
|
left: 60
|
||
|
};
|
||
|
$scope.svgHeight = 320;
|
||
|
$scope.svgWidth = 900;
|
||
|
$scope.dateFormat = d3.time.format("%a %b %d %H:%M:%S %Y");
|
||
|
|
||
|
// Application state variables
|
||
|
$scope.fetchingAlerts = false;
|
||
|
|
||
|
$scope.$watch("reload", function(newValue) {
|
||
|
// Remove any previous graphs
|
||
|
d3.select("g#grid").selectAll("path").remove();
|
||
|
// Set the start and end times
|
||
|
$scope.startTime = $scope.t1 * 1000;
|
||
|
$scope.endTime = $scope.t2 * 1000;
|
||
|
// Show the histogram
|
||
|
showHistogram();
|
||
|
});
|
||
|
|
||
|
// Get the statistics breakdown label
|
||
|
$scope.statisticsBreakdownLabel = function(value) {
|
||
|
for (var i = 0; i < statisticsBreakdown.length; i++) {
|
||
|
var bd = statisticsBreakdown[i];
|
||
|
if (bd.value == value) {
|
||
|
return bd.label;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Get the x-axis scale domain
|
||
|
var getXScaleDomain = function() {
|
||
|
|
||
|
// Set up the x-axis scale domain
|
||
|
var domain = [];
|
||
|
|
||
|
switch($scope.breakdown) {
|
||
|
case "monthly":
|
||
|
domain = d3.range(0,13);
|
||
|
break;
|
||
|
case "dayofweek":
|
||
|
domain = d3.range(0,8);
|
||
|
break;
|
||
|
case "hourly":
|
||
|
domain = d3.range(0,25);
|
||
|
break;
|
||
|
case "dayofmonth":
|
||
|
default:
|
||
|
domain = d3.range(0,32);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return domain;
|
||
|
};
|
||
|
|
||
|
// Get the x-axis tick values
|
||
|
var getXAxisTickValue = function(d) {
|
||
|
|
||
|
// Set up the x-axis tick values
|
||
|
var values = [];
|
||
|
|
||
|
switch($scope.breakdown) {
|
||
|
case "monthly":
|
||
|
var months = ["January", "February", "March",
|
||
|
"April", "May", "June", "July", "August",
|
||
|
"September", "October", "November",
|
||
|
"December", "January"];
|
||
|
return months[d % 12];
|
||
|
break;
|
||
|
case "dayofweek":
|
||
|
var days = ["Sunday", "Monday", "Tuesday",
|
||
|
"Wednesday", "Thursday", "Friday",
|
||
|
"Saturday", "Sunday"];
|
||
|
return days[d % 7];
|
||
|
break;
|
||
|
case "hourly":
|
||
|
var hourFormat = d3.format("02d");
|
||
|
return hourFormat(d % 24) + ":00";
|
||
|
break;
|
||
|
case "dayofmonth":
|
||
|
default:
|
||
|
var value = ((d + 1) % 32);
|
||
|
return (value == 0 ? 1 : value);
|
||
|
break;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Get the number of periods for the breakdown
|
||
|
$scope.getBreakdownPeriods = function() {
|
||
|
switch($scope.breakdown) {
|
||
|
case "monthly":
|
||
|
return 12;
|
||
|
break;
|
||
|
case "dayofweek":
|
||
|
return 7;
|
||
|
break;
|
||
|
case "hourly":
|
||
|
return 24;
|
||
|
break;
|
||
|
case "dayofmonth":
|
||
|
default:
|
||
|
return 31;
|
||
|
break;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Determine the y-axis maximum
|
||
|
getYMax = function() {
|
||
|
|
||
|
var states = ["up", "down", "unreachable", "ok",
|
||
|
"warning", "unknown", "critical" ];
|
||
|
var yMax = 0;
|
||
|
|
||
|
states.forEach(function(e, i, a) {
|
||
|
yMax = Math.max($scope.summary[$scope.breakdown].maxima[e], yMax);
|
||
|
});
|
||
|
|
||
|
return yMax;
|
||
|
};
|
||
|
|
||
|
// Display the graph
|
||
|
var displayGraph = function() {
|
||
|
|
||
|
// Local variables
|
||
|
var graphHeight = $scope.svgHeight -
|
||
|
($scope.graphMargin.bottom +
|
||
|
$scope.graphMargin.top);
|
||
|
var graphWidth = $scope.svgWidth -
|
||
|
($scope.graphMargin.right +
|
||
|
$scope.graphMargin.left);
|
||
|
var gridCenter = $scope.graphMargin.left +
|
||
|
graphWidth / 2;
|
||
|
|
||
|
// Get the header group
|
||
|
var gridGroup = d3.select("svg#histogram")
|
||
|
.select("g#grid")
|
||
|
.attr({
|
||
|
transform: function() {
|
||
|
return "translate(" +
|
||
|
$scope.graphMargin.left + "," +
|
||
|
$scope.graphMargin.top + ")";
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Build the x-axis scale
|
||
|
var xScale = d3.scale.ordinal()
|
||
|
.domain(getXScaleDomain())
|
||
|
.rangePoints([0, graphWidth]);
|
||
|
|
||
|
// Build the x-axis
|
||
|
var xAxis = d3.svg.axis()
|
||
|
.scale(xScale)
|
||
|
.orient("bottom")
|
||
|
.tickSize(graphHeight)
|
||
|
.tickFormat(function(d) {
|
||
|
return getXAxisTickValue(d);
|
||
|
});
|
||
|
|
||
|
// Display the x-axis
|
||
|
d3.select("g#xaxis").call(xAxis);
|
||
|
|
||
|
// Rotate the value labels for the x-axis
|
||
|
var translate = -(graphHeight + ($scope.fontSize / 2));
|
||
|
d3.select("g#xaxis")
|
||
|
.selectAll("text")
|
||
|
.attr({
|
||
|
transform: function() {
|
||
|
return "rotate(-90) translate(" +
|
||
|
translate + "," + translate + ")";
|
||
|
}
|
||
|
})
|
||
|
.style({
|
||
|
"text-anchor": "end"
|
||
|
});
|
||
|
|
||
|
// Build the y-axis scale
|
||
|
var yScale = d3.scale.linear()
|
||
|
.domain([getYMax(), 0])
|
||
|
.rangeRound([0, graphHeight]);
|
||
|
|
||
|
// Build the y-axis
|
||
|
var yAxis = d3.svg.axis()
|
||
|
.scale(yScale)
|
||
|
.orient("left")
|
||
|
.tickSize(graphWidth);
|
||
|
|
||
|
// Display the y-axis
|
||
|
d3.select("g#yaxis").call(yAxis);
|
||
|
|
||
|
// Generate the lines
|
||
|
var states = [];
|
||
|
if($scope.reporttype == "hosts") {
|
||
|
states = ["up", "down", "unreachable"];
|
||
|
}
|
||
|
else {
|
||
|
states = ["ok", "warning", "unknown", "critical"];
|
||
|
}
|
||
|
states.forEach(function(e, i, a) {
|
||
|
var line = d3.svg.line()
|
||
|
.x(function(d, i) { return xScale(i); })
|
||
|
.y(function(d) { return yScale(d[e]); })
|
||
|
.interpolate("linear");
|
||
|
|
||
|
gridGroup.append("path")
|
||
|
.attr({
|
||
|
"d": line($scope.summary[$scope.breakdown].details),
|
||
|
"class": e
|
||
|
});
|
||
|
});
|
||
|
};
|
||
|
|
||
|
// Initialize the data for display
|
||
|
var initializeData = function(obj, size) {
|
||
|
|
||
|
var states = ["up", "down", "unreachable", "ok",
|
||
|
"warning", "unknown", "critical" ];
|
||
|
|
||
|
obj.maxima = new Object;
|
||
|
obj.minima = new Object;
|
||
|
obj.totals = new Object;
|
||
|
states.forEach(function(e, i, a) {
|
||
|
obj.maxima[e] = 0;
|
||
|
obj.minima[e] = -1;
|
||
|
obj.totals[e] = 0;
|
||
|
});
|
||
|
|
||
|
obj.details = new Array;
|
||
|
for(var i = 0; i < size; i++) {
|
||
|
obj.details.push({
|
||
|
"up": 0,
|
||
|
"down": 0,
|
||
|
"unreachable": 0,
|
||
|
"ok": 0,
|
||
|
"warning": 0,
|
||
|
"unknown": 0,
|
||
|
"critical": 0
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Update the summary data
|
||
|
var updateData = function(obj, state, index) {
|
||
|
|
||
|
obj.details[index][state]++;
|
||
|
obj.maxima[state] = Math.max(obj.maxima[state],
|
||
|
obj.details[index][state]);
|
||
|
obj.totals[state]++;
|
||
|
if(index == 0) {
|
||
|
// If this is the first entry in the breakdown,
|
||
|
// also update the last because the graph
|
||
|
// "wraps around"
|
||
|
obj.details[obj.details.length - 1][state]++;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Calculate the minima
|
||
|
var calculateMinima = function(obj) {
|
||
|
for(var key in obj.minima) {
|
||
|
obj.minima[key] = obj.details[0][key];
|
||
|
for(var j = 1; j < obj.details.length; j++) {
|
||
|
obj.minima[key] = Math.min(obj.minima[key],
|
||
|
obj.details[j][key]);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Summarize the data for display
|
||
|
var summarizeData = function() {
|
||
|
|
||
|
// Initialize the data
|
||
|
$scope.summary = {
|
||
|
monthly: new Object,
|
||
|
dayofmonth: new Object,
|
||
|
dayofweek: new Object,
|
||
|
hourly: new Object,
|
||
|
};
|
||
|
initializeData($scope.summary.monthly, 13);
|
||
|
initializeData($scope.summary.dayofmonth, 32);
|
||
|
initializeData($scope.summary.dayofweek, 8);
|
||
|
initializeData($scope.summary.hourly, 25);
|
||
|
|
||
|
$scope.json.data.alertlist.forEach(function(e, i, a) {
|
||
|
// Create a Javascript date object
|
||
|
var ts = new Date(e.timestamp);
|
||
|
|
||
|
// Update the monthly data
|
||
|
updateData($scope.summary.monthly, e.state,
|
||
|
ts.getMonth());
|
||
|
|
||
|
// Update the day of month data
|
||
|
updateData($scope.summary.dayofmonth, e.state,
|
||
|
ts.getDate() - 1);
|
||
|
|
||
|
// Update the day of week data
|
||
|
updateData($scope.summary.dayofweek, e.state,
|
||
|
ts.getDay());
|
||
|
|
||
|
// Update the hourly data
|
||
|
updateData($scope.summary.hourly, e.state,
|
||
|
ts.getHours());
|
||
|
|
||
|
});
|
||
|
|
||
|
// Calculate the minima
|
||
|
calculateMinima($scope.summary.monthly);
|
||
|
calculateMinima($scope.summary.dayofmonth);
|
||
|
calculateMinima($scope.summary.dayofweek);
|
||
|
calculateMinima($scope.summary.hourly);
|
||
|
};
|
||
|
|
||
|
// Show the graph
|
||
|
var showHistogram = function() {
|
||
|
|
||
|
if(!$scope.build()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Build the list of parameters for the JSON query
|
||
|
var parameters = {
|
||
|
query: "alertlist",
|
||
|
formatoptions: "enumerate bitmask",
|
||
|
objecttypes: ($scope.reporttype == "hosts" ?
|
||
|
"host" : "service"),
|
||
|
hostname: $scope.host,
|
||
|
starttime: $scope.t1,
|
||
|
endtime: $scope.t2,
|
||
|
statetypes: function() {
|
||
|
switch($scope.graphstatetypes) {
|
||
|
case 1:
|
||
|
return "soft";
|
||
|
break;
|
||
|
case 2:
|
||
|
return "hard";
|
||
|
break;
|
||
|
case 3:
|
||
|
default:
|
||
|
return "hard soft";
|
||
|
break;
|
||
|
}
|
||
|
}(),
|
||
|
backtrackedarchives: $scope.backtracks
|
||
|
};
|
||
|
|
||
|
switch($scope.reporttype) {
|
||
|
case "hosts":
|
||
|
var events = histogramEventsService.hostEvents.filter(function(e) {
|
||
|
return e.value == $scope.graphevents;
|
||
|
});
|
||
|
if(events.length > 0) {
|
||
|
parameters.hoststates = events[0].states;
|
||
|
}
|
||
|
else {
|
||
|
parameters.hoststates = "up down unreachable";
|
||
|
}
|
||
|
break;
|
||
|
case "services":
|
||
|
var events = histogramEventsService.serviceEvents.filter(function(e) {
|
||
|
return e.value == $scope.graphevents;
|
||
|
});
|
||
|
parameters.servicedescription = $scope.service;
|
||
|
if(events.length > 0) {
|
||
|
parameters.servicestates = events[0].states;
|
||
|
}
|
||
|
else {
|
||
|
parameters.servicestates =
|
||
|
"ok warning unknown critical";
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
var getConfig = {
|
||
|
params: parameters,
|
||
|
withCredentials: true
|
||
|
};
|
||
|
|
||
|
// Where to place the spinner
|
||
|
var spinnerdiv = d3.select("div#spinner");
|
||
|
var spinner = null;
|
||
|
|
||
|
// Send the JSON query
|
||
|
$http.get($scope.cgiurl + "archivejson.cgi", getConfig)
|
||
|
.error(function(err) {
|
||
|
console.warn(err);
|
||
|
// Stop the spinner
|
||
|
$scope.fetchingAlerts = false;
|
||
|
spinner.stop();
|
||
|
})
|
||
|
.success(function(json) {
|
||
|
// Stop the spinner
|
||
|
$scope.fetchingAlerts = false;
|
||
|
spinner.stop();
|
||
|
|
||
|
// Save the json results
|
||
|
$scope.json = json;
|
||
|
|
||
|
// Record the query time
|
||
|
$scope.lastUpdate = new Date(json.result.query_time);
|
||
|
|
||
|
// Summarize the results
|
||
|
summarizeData();
|
||
|
|
||
|
// Display the graph
|
||
|
displayGraph();
|
||
|
|
||
|
// Display the summary
|
||
|
});
|
||
|
|
||
|
// Start the spinner
|
||
|
$scope.fetchingAlerts = true;
|
||
|
spinner = new Spinner($scope.spinnerOpts).spin(spinnerdiv[0][0]);
|
||
|
};
|
||
|
|
||
|
}
|
||
|
};
|
||
|
});
|