ntopng/scripts/lua/modules/graph_utils.lua
Luca Deri 4bf10f4fce Added user role in About page
Fixed SQL when exploring subnets
Modified preferences to specify that when nDPI RRDs are created, they do for both local hosts and networks
2015-09-14 14:16:11 +02:00

1310 lines
No EOL
35 KiB
Lua

--
-- (C) 2013-15 - ntop.org
--
top_rrds = {
["bytes.rrd"] = "Traffic",
["packets.rrd"] = "Packets",
["drops.rrd"] = "Packet Drops",
["num_flows.rrd"] = "Active Flows",
["num_hosts.rrd"] = "Active Hosts",
["num_http_hosts.rrd"] = "Active HTTP Servers"
}
-- ########################################################
if(ntop.isPro()) then
package.path = dirs.installdir .. "/pro/scripts/lua/modules/?.lua;" .. package.path
require "nv_graph_utils"
end
-- ########################################################
function navigatedir(url, label, base, path, go_deep, print_html)
local shown = false
local to_skip = false
local ret = { }
local do_debug = false
local printed = false
rrds = ntop.readdir(path)
table.sort(rrds)
for k,v in pairsByKeys(rrds, asc) do
if(v ~= nil) then
p = fixPath(path .. "/" .. v)
if(ntop.isdir(p)) then
if(go_deep) then
r = navigatedir(url, label.."/"..v, base, p, print_html)
for k,v in pairs(r) do
ret[k] = v
if(do_debug) then print(v.."<br>\n") end
end
end
else
if(top_rrds[v] == nil) then
if(label == "*") then
to_skip = true
else
if(not(shown) and not(to_skip)) then
if(print_html) then
if(not(printed)) then print('<li class="divider"></li>\n') printed = true end
print('<li class="dropdown-submenu"><a tabindex="-1" href="#">'..label..'</a>\n<ul class="dropdown-menu">\n')
end
shown = true
end
end
what = string.sub(path.."/"..v, string.len(base)+2)
label = string.sub(v, 1, string.len(v)-4)
label = l4Label(string.gsub(label, "_", " "))
ret[label] = what
if(do_debug) then print(what.."<br>\n") end
if(print_html) then
if(not(printed)) then print('<li class="divider"></li>\n') printed = true end
print("<li> <A HREF="..url..what..">"..label.."</A> </li>\n")
end
end
end
end
end
if(shown) then
if(print_html) then print('</ul></li>\n') end
end
return(ret)
end
-- ########################################################
function breakdownBar(sent, sentLabel, rcvd, rcvdLabel)
if((sent+rcvd) > 0) then
sent2rcvd = round((sent * 100) / (sent+rcvd), 0)
print('<div class="progress"><div class="progress-bar progress-bar-warning" aria-valuenow="'.. sent2rcvd..'" aria-valuemin="0" aria-valuemax="100" style="width: ' .. sent2rcvd.. '%;">'..sentLabel)
print('</div><div class="progress-bar progress-bar-info" aria-valuenow="'.. (100 -sent2rcvd)..'" aria-valuemin="0" aria-valuemax="100" style="width: ' .. (100-sent2rcvd) .. '%;">' .. rcvdLabel .. '</div></div>')
else
print('&nbsp;')
end
end
-- ########################################################
function percentageBar(total, value, valueLabel)
if(total > 0) then
pctg = round((value * 100) / total, 0)
print('<div class="progress"><div class="progress-bar progress-bar-warning" aria-valuenow="'.. pctg..'" aria-valuemin="0" aria-valuemax="100" style="width: ' .. pctg.. '%;">'..valueLabel)
print('</div></div>')
else
print('&nbsp;')
end
end
-- ########################################################
function getRRDName(ifid, host, rrdFile)
rrdname = fixPath(dirs.workingdir .. "/" .. ifid .. "/rrd/")
if(host ~= nil) then
rrdname = rrdname .. getPathFromKey(host) .. "/"
end
return(rrdname .. rrdFile)
end
-- ########################################################
zoom_vals = {
{ "1m", "now-60s", 60 },
{ "5m", "now-300s", 60*5 },
{ "10m", "now-600s", 60*10 },
{ "1h", "now-1h", 60*60*1 },
{ "3h", "now-3h", 60*60*3 },
{ "6h", "now-6h", 60*60*6 },
{ "12h", "now-12h", 60*60*12 },
{ "1d", "now-1d", 60*60*24 },
{ "1w", "now-1w", 60*60*24*7 },
{ "2w", "now-2w", 60*60*24*14 },
{ "1M", "now-1mon", 60*60*24*31 },
{ "6M", "now-6mon", 60*60*24*31*6 },
{ "1Y", "now-1y", 60*60*24*366 }
}
function getZoomAtPos(cur_zoom, pos_offset)
local pos = 1
local new_zoom_level = cur_zoom
for k,v in pairs(zoom_vals) do
if(zoom_vals[k][1] == cur_zoom) then
if (pos+pos_offset >= 1 and pos+pos_offset < 13) then
new_zoom_level = zoom_vals[pos+pos_offset][1]
break
end
end
pos = pos + 1
end
return new_zoom_level
end
-- ########################################################
function getZoomDuration(cur_zoom)
for k,v in pairs(zoom_vals) do
if(zoom_vals[k][1] == cur_zoom) then
return(zoom_vals[k][3])
end
end
return(180)
end
-- ########################################################
function drawPeity(ifid, host, rrdFile, zoomLevel, selectedEpoch)
rrdname = getRRDName(ifid, host, rrdFile)
if(zoomLevel == nil) then
zoomLevel = "1h"
end
nextZoomLevel = zoomLevel;
epoch = tonumber(selectedEpoch);
for k,v in ipairs(zoom_vals) do
if(zoom_vals[k][1] == zoomLevel) then
if(k > 1) then
nextZoomLevel = zoom_vals[k-1][1]
end
if(epoch) then
start_time = epoch - zoom_vals[k][3]/2
end_time = epoch + zoom_vals[k][3]/2
else
start_time = zoom_vals[k][2]
end_time = "now"
end
end
end
--print("=> Found "..rrdname.."<p>\n")
if(ntop.notEmptyFile(rrdname)) then
--io.write("=> Found ".. start_time .. "|" .. end_time .. "<p>\n")
local fstart, fstep, fnames, fdata = ntop.rrd_fetch(rrdname, 'AVERAGE', start_time..", end_time..")
local max_num_points = 512 -- This is to avoid having too many points and thus a fat graph
local num_points_found = table.getn(fdata)
local sample_rate = round(num_points_found / max_num_points)
local num_points = 0
local step = 1
local series = {}
if(sample_rate < 1) then
sample_rate = 1
end
-- print("=> "..num_points_found.."[".. sample_rate .."]["..fstart.."]<p>")
id = 0
num = 0
total = 0
sample_rate = sample_rate-1
points = {}
for i, v in ipairs(fdata) do
timestamp = fstart + (i-1)*fstep
num_points = num_points + 1
local elemId = 1
for _, w in ipairs(v) do
if(w ~= w) then
-- This is a NaN
v = 0
else
v = tonumber(w)
if(v < 0) then
v = 0
end
end
value = v*8 -- bps
total = total + value
if(id == sample_rate) then
points[num] = round(value)..""
num = num+1
id = 0
else
id = id + 1
end
elemId = elemId + 1
end
end
end
print("<td class=\"text-right\">"..round(total).."</td><td> <span class=\"peity-line\">")
for i=0,10 do
if(i > 0) then print(",") end
print(points[i])
end
print("</span>\n")
end
-- ########################################################
function drawRRD(ifid, host, rrdFile, zoomLevel, baseurl, show_timeseries,
selectedEpoch, selected_epoch_sanitized, topArray)
local debug_rrd = false
if(zoomLevel == nil) then zoomLevel = "1h" end
if((selectedEpoch == nil) or (selectedEpoch == "")) then
-- Refresh the page every minute unless a specific epoch has been selected
print("<script>setInterval(function() { window.location.reload();}, 60*1000); </script>\n");
end
if(ntop.isPro()) then
drawProGraph(ifid, host, rrdFile, zoomLevel, baseurl, show_timeseries, selectedEpoch, selected_epoch_sanitized, topArray)
return
end
dirs = ntop.getDirs()
rrdname = getRRDName(ifid, host, rrdFile)
names = {}
series = {}
if(zoomLevel == nil) then
zoomLevel = "1h"
end
nextZoomLevel = zoomLevel;
epoch = tonumber(selectedEpoch);
for k,v in ipairs(zoom_vals) do
if(zoom_vals[k][1] == zoomLevel) then
if(k > 1) then
nextZoomLevel = zoom_vals[k-1][1]
end
if(epoch) then
start_time = epoch - zoom_vals[k][3]/2
end_time = epoch + zoom_vals[k][3]/2
else
start_time = zoom_vals[k][2]
end_time = "now"
end
end
end
local maxval_bits_time = 0
local maxval_bits = 0
local minval_bits = 0
local minval_bits_time = 0
local lastval_bits = 0
local lastval_bits_time = 0
local total_bytes = 0
local num_points = 0
local step = 1
prefixLabel = l4Label(string.gsub(rrdFile, ".rrd", ""))
-- io.write(prefixLabel.."\n")
if(prefixLabel == "Bytes") then
prefixLabel = "Traffic"
end
if(ntop.notEmptyFile(rrdname)) then
-- print("=> Found "..rrdname.."<p>\n")
-- print("=> "..rrdname)
-- io.write("=> *** ".. start_time .. "|" .. end_time .. "<p>\n")
local fstart, fstep, fnames, fdata = ntop.rrd_fetch(rrdname, 'AVERAGE', start_time, end_time)
--print("=> here we go")
local max_num_points = 600 -- This is to avoid having too many points and thus a fat graph
local num_points_found = table.getn(fdata)
local sample_rate = round(num_points_found / max_num_points)
if(sample_rate < 1) then
sample_rate = 1
end
-- DEBUG
--tprint(fdata, 1)
step = fstep
num = 0
names_cache = {}
for i, n in ipairs(fnames) do
-- handle duplicates
if (names_cache[n] == nil) then
names_cache[n] = true
names[num] = prefixLabel
if(host ~= nil) then names[num] = names[num] .. " (" .. firstToUpper(n)..")" end
num = num + 1
--io.write(prefixLabel.."\n")
--print(num.."\n")
end
end
id = 0
sampling = 0
--sample_rate = 1
sample_rate = sample_rate-1
accumulated = 0
for i, v in ipairs(fdata) do
s = {}
s[0] = fstart + (i-1)*fstep
num_points = num_points + 1
local elemId = 1
for _, w in ipairs(v) do
if(w ~= w) then
-- This is a NaN
v = 0
else
--io.write(w.."\n")
v = tonumber(w)
if(v < 0) then
v = 0
end
end
if(v > 0) then
lastval_bits_time = s[0]
lastval_bits = v
end
s[elemId] = v*8 -- bps
--if(s[elemId] > 0) then io.write("[".. elemId .. "]=" .. s[elemId] .."\n") end
elemId = elemId + 1
end
total_bytes = total_bytes + v*fstep
--if((v*fstep) > 0) then io.write(" | " .. (v*fstep) .." | [sampling: ".. sampling .. "/" .. sample_rate.."]\n") end
if(sampling == sample_rate) then
if(sample_rate > 0) then
s[1] = accumulated / sample_rate
end
series[id] = s
id = id + 1
sampling = 0
accumulated = 0
else
accumulated = accumulated + s[1]
sampling = sampling + 1
end
end
for key, value in pairs(series) do
local t = 0
for elemId=0,(num-1) do
--io.write(key.."="..value[elemId+1].. "\n")
t = t + value[elemId+1] -- bps
end
t = t * step
if(((minval_bits_time == 0) or (minval_bits >= t)) and (value[0] < lastval_bits_time)) then
--io.write(value[0].."\t".. t .. "\t".. lastval_bits_time .. "\n")
minval_bits_time = value[0]
minval_bits = t
end
if((maxval_bits_time == 0) or (maxval_bits <= t)) then
maxval_bits_time = value[0]
maxval_bits = t
end
end
print [[
<style>
#chart_container {
display: inline-block;
font-family: Arial, Helvetica, sans-serif;
}
#chart {
float: left;
}
#legend {
float: left;
margin-left: 15px;
color: black;
background: white;
}
#y_axis {
float: left;
width: 40px;
}
</style>
<div>
<table border=0>
<tr><td valign=top>
]]
if(show_timeseries == 1) then
print [[
<div class="btn-group">
<button class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">Timeseries <span class="caret"></span></button>
<ul class="dropdown-menu">
]]
for k,v in pairs(top_rrds) do
rrdname = getRRDName(ifid, host, k)
if(ntop.notEmptyFile(rrdname)) then
print('<li><a href="'..baseurl .. '&rrd_file=' .. k .. '&graph_zoom=' .. zoomLevel .. '&epoch=' .. (selectedEpoch or '') .. '">'.. v ..'</a></li>\n')
end
end
--print('<li class="divider"></li>\n')
dirs = ntop.getDirs()
p = dirs.workingdir .. "/" .. purifyInterfaceName(ifid) .. "/rrd/"
if(host ~= nil) then
p = p .. getPathFromKey(host)
go_deep = true
else
go_deep = false
end
d = fixPath(p)
navigatedir(baseurl .. '&graph_zoom=' .. zoomLevel .. '&epoch=' .. (selectedEpoch or '')..'&rrd_file=', "*", d, d, go_deep, true)
print [[
</ul>
</div><!-- /btn-group -->
]]
end -- show_timeseries == 1
print('&nbsp;Timeframe: <div class="btn-group" data-toggle="buttons" id="graph_zoom">\n')
for k,v in ipairs(zoom_vals) do
print('<label class="btn btn-link ')
if(zoom_vals[k][1] == zoomLevel) then
print("active")
end
print('">')
print('<input type="radio" name="options" id="zoom_level_'..k..'" value="'..baseurl .. '&rrd_file=' .. rrdFile .. '&graph_zoom=' .. zoom_vals[k][1] .. '&epoch=' .. (selectedEpoch or '') ..'">'.. zoom_vals[k][1] ..'</input></label>\n')
end
print [[
</div>
</div>
<script>
$('input:radio[id^=zoom_level_]').change( function() {
window.open(this.value,'_self',false);
});
</script>
<br />
<p>
<div id="legend"></div>
<div id="chart_legend"></div>
<div id="chart" style="margin-right: 50px; margin-left: 10px; display: table-cell"></div>
<p><font color=lightgray><small>NOTE: Click on the graph to zoom.</small></font>
</td>
<td rowspan=2>
<div id="y_axis"></div>
<div style="margin-left: 10px; display: table">
<div id="chart_container" style="display: table-row">
]]
if(string.contains(rrdFile, "num_")) then
formatter_fctn = "fint"
else
formatter_fctn = "fpackets"
end
if (topArray ~= nil) then
print [[
<table class="table table-bordered table-striped" style="border: 0; margin-right: 10px; display: table-cell">
]]
print(' <tr><th>&nbsp;</th><th>Time</th><th>Value</th></tr>\n')
if(string.contains(rrdFile, "num_") or string.contains(rrdFile, "packets") or string.contains(rrdFile, "drops")) then
print(' <tr><th>Min</th><td>' .. os.date("%x %X", minval_bits_time) .. '</td><td>' .. formatValue(round(minval_bits/step), 1) .. '</td></tr>\n')
print(' <tr><th>Max</th><td>' .. os.date("%x %X", maxval_bits_time) .. '</td><td>' .. formatValue(round(maxval_bits/step), 1) .. '</td></tr>\n')
print(' <tr><th>Last</th><td>' .. os.date("%x %X", last_time) .. '</td><td>' .. formatValue(round(lastval_bits/step), 1) .. '</td></tr>\n')
print(' <tr><th>Average</th><td colspan=2>' .. formatValue(round(total_bytes*8/(step*num_points), 2)) .. '</td></tr>\n')
print(' <tr><th>Total Number</th><td colspan=2>' .. formatValue(round(total_bytes)) .. '</td></tr>\n')
else
formatter_fctn = "fbits"
print(' <tr><th>Min</th><td>' .. os.date("%x %X", minval_bits_time) .. '</td><td>' .. bitsToSize(minval_bits/step) .. '</td></tr>\n')
print(' <tr><th>Max</th><td>' .. os.date("%x %X", maxval_bits_time) .. '</td><td>' .. bitsToSize(maxval_bits/step) .. '</td></tr>\n')
print(' <tr><th>Last</th><td>' .. os.date("%x %X", last_time) .. '</td><td>' .. bitsToSize(lastval_bits/step) .. '</td></tr>\n')
print(' <tr><th>Average</th><td colspan=2>' .. bitsToSize(total_bytes*8/(step*num_points)) .. '</td></tr>\n')
print(' <tr><th>Total Traffic</th><td colspan=2>' .. bytesToSize(total_bytes) .. '</td></tr>\n')
end
print(' <tr><th>Selection Time</th><td colspan=2><div id=when></div></td></tr>\n')
print(' <tr><th>Minute<br>Top Talkers</th><td colspan=2><div id=talkers></div></td></tr>\n')
print [[
</table>
]]
end -- topArray ~= nil
print[[</div></td></tr>]]
printGraphTopFlows(ifid, (host or ''), _GET["epoch"], zoomLevel, rrdFile)
print [[
</table>
]]
print [[
<script>
var palette = new Rickshaw.Color.Palette();
var graph = new Rickshaw.Graph( {
element: document.getElementById("chart"),
width: 600,
height: 300,
renderer: 'area',
series: [
]]
if(names ~= nil) then
for elemId=0,(num-1) do
if(elemId > 0) then
print ","
end
--name = strsplit(names[elemId], "/")
--name = name[#name]
name = names[elemId]
print ("{\nname: '".. name .. "',\n")
print("color: palette.color(),\ndata: [\n")
n = 0
for key, value in pairs(series) do
if(n > 0) then
print(",\n")
end
print ("\t{ x: ".. value[0] .. ", y: ".. value[elemId+1] .. " }")
n = n + 1
end
print("\n]}\n")
end
end
print [[
]
} );
graph.render();
var chart_legend = document.querySelector('#chart_legend');
function fdate(when) {
var epoch = when*1000;
var d = new Date(epoch);
return(d);
}
function fbits(bits) {
var sizes = ['bps', 'Kbit/s', 'Mbit/s', 'Gbit/s', 'Tbit/s'];
if(bits == 0) return 'n/a';
var i = parseInt(Math.floor(Math.log(bits) / Math.log(1024)));
return Math.round(bits / Math.pow(1024, i), 2) + ' ' + sizes[i];
}
function capitaliseFirstLetter(string)
{
return string.charAt(0).toUpperCase() + string.slice(1);
}
/**
* Convert number of bytes into human readable format
*
* @param integer bytes Number of bytes to convert
* @param integer precision Number of digits after the decimal separator
* @return string
*/
function formatBytes(bytes, precision)
{
var kilobyte = 1024;
var megabyte = kilobyte * 1024;
var gigabyte = megabyte * 1024;
var terabyte = gigabyte * 1024;
if((bytes >= 0) && (bytes < kilobyte)) {
return bytes + ' B';
} else if((bytes >= kilobyte) && (bytes < megabyte)) {
return (bytes / kilobyte).toFixed(precision) + ' KB';
} else if((bytes >= megabyte) && (bytes < gigabyte)) {
return (bytes / megabyte).toFixed(precision) + ' MB';
} else if((bytes >= gigabyte) && (bytes < terabyte)) {
return (bytes / gigabyte).toFixed(precision) + ' GB';
} else if(bytes >= terabyte) {
return (bytes / terabyte).toFixed(precision) + ' TB';
} else {
return bytes + ' B';
}
}
var Hover = Rickshaw.Class.create(Rickshaw.Graph.HoverDetail, {
graph: graph,
xFormatter: function(x) { return new Date( x * 1000 ); },
yFormatter: function(bits) { return(]] print(formatter_fctn) print [[(bits)); },
render: function(args) {
var graph = this.graph;
var points = args.points;
var point = points.filter( function(p) { return p.active } ).shift();
if(point.value.y === null) return;
var formattedXValue = fdate(point.value.x); // point.formattedXValue;
var formattedYValue = ]]
print(formatter_fctn)
print [[(point.value.y); // point.formattedYValue;
var infoHTML = "";
]]
if(topArray ~= nil) then
print[[
var seconds;
$.ajax ({
type: 'GET',
url: ']]
print(ntop.getHttpPrefix().."/lua/modules/get_real_epochs.lua?epoch='+point.value.x,")
print[[
data: { epoch: point.value.x },
async: false,
success: function(content) {
var res = content.split(" ");
seconds = parseInt(res[0]) - parseInt(res[1]);
}
});
infoHTML += "<ul>";
]]
for n,v in pairs(topArray) do
modulename = n
sectionname = v["name"]
levels = v["levels"]
scriptname = v["script"]
key = v["key"]
if (string.lower(sectionname) ~= "top talkers") then
goto continue
end
-- Support only 1 or 2 levels by now
if (levels < 1 or levels > 2) then goto continue end
print [[
$.ajax({
type: 'GET',
url: ']]
print(ntop.getHttpPrefix().."/lua/top_generic.lua?m="..modulename.."&epoch='+point.value.x+'&addvlan=true")
if (levels == 2) then
print [[',
data: { epoch: point.value.x },
async: false,
success: function(content) {
var info = jQuery.parseJSON(content);]]
print [[
var elements = 0;
$.each(info, function(i, n) {
elements++;
return false;
});
]]
print[[
$.each(info, function(i, n) {
var nonempty = 0;
$.each(n, function(j, m) {
nonempty++;
return false;
});
if (nonempty != 0)
infoHTML += "<li>"+capitaliseFirstLetter(i)+" [Avg Traffic/sec]<ol>";
var items = 0;
var other_traffic = 0;
$.each(n, function(j, m) {
if(items < 3) {
infoHTML += "<li><a href=']]
print(scriptname.."?"..key.."=")
print[["+m.address+"'>"+m.label; if ("]]print(sectionname)print[[".toLowerCase() == "Operating Systems") infoHTML += getOSIcon(m.label); if ("]]print(sectionname)print[[".toLowerCase() == "countries") infoHTML += " <img src=']] print(ntop.getHttpPrefix()) print [[/img/blank.gif' class='flag flag-"+m.label.toLowerCase()+"'>"; infoHTML += "</a>"; if (m.vlan != "0") infoHTML += " ("+m.vlanm+")"; infoHTML += " ("+]] print(formatter_fctn) print [[((m.value*8)/seconds)+")</li>";
items++;
} else
other_traffic += m.value;
});
if (other_traffic > 0)
infoHTML += "<li>Other ("+]]print(formatter_fctn)print[[((other_traffic*8)/seconds)+")</li>";
if (nonempty != 0)
infoHTML += "</ol></li>";
});
infoHTML += "</ul></li></li>";
}
});
]]
elseif (levels == 1) then
print [[',
data: { epoch: point.value.x },
async: false,
success: function(content) {
var info = jQuery.parseJSON(content);
var items = 0;
$.each(info, function(i, n) {
]]
print('if(items == 0) infoHTML += "<li>'..sectionname..' [Avg Traffic/sec]<ol>";')
print[[
if(items < 3)
infoHTML += "<li><a href=']]
print(scriptname.."?"..key.."=")
print[["+n.label+"'>"+n.name+"</a>";]]
if (sectionname ~= "VLANs") then
print[[if (n.vlan != "0") infoHTML += " ("+n.vlanm+")"+]]
else
print[[infoHTML +=]]
end
print[[" ("+]] print(formatter_fctn) print [[((n.value*8)/seconds)+")</li>";
items++;
});
if(items > 0)
infoHTML += "</ol></li></ul>";
}
});
]]
end -- levels
::continue::
end -- for
print[[infoHTML += "</ul>";]]
end -- topArray
print [[
this.element.innerHTML = '';
this.element.style.left = graph.x(point.value.x) + 'px';
/*var xLabel = document.createElement('div');
xLabel.setAttribute("style", "opacity: 0.5; background-color: #EEEEEE; filter: alpha(opacity=0.5)");
xLabel.className = 'x_label';
xLabel.innerHTML = formattedXValue + infoHTML;
this.element.appendChild(xLabel);
*/
$('#when').html(formattedXValue);
$('#talkers').html(infoHTML);
var item = document.createElement('div');
item.className = 'item';
item.innerHTML = this.formatter(point.series, point.value.x, point.value.y, formattedXValue, formattedYValue, point);
item.style.top = this.graph.y(point.value.y0 + point.value.y) + 'px';
this.element.appendChild(item);
var dot = document.createElement('div');
dot.className = 'dot';
dot.style.top = item.style.top;
dot.style.borderColor = point.series.color;
this.element.appendChild(dot);
if(point.active) {
item.className = 'item active';
dot.className = 'dot active';
}
this.show();
if(typeof this.onRender == 'function') {
this.onRender(args);
}
// Put the selected graph epoch into the legend
//chart_legend.innerHTML = point.value.x; // Epoch
this.selected_epoch = point.value.x;
//event
}
} );
var hover = new Hover( { graph: graph } );
var legend = new Rickshaw.Graph.Legend( {
graph: graph,
element: document.getElementById('legend')
} );
//var axes = new Rickshaw.Graph.Axis.Time( { graph: graph } ); axes.render();
var yAxis = new Rickshaw.Graph.Axis.Y({
graph: graph,
tickFormat: ]] print(formatter_fctn) print [[
});
yAxis.render();
$("#chart").click(function() {
if(hover.selected_epoch)
window.location.href = ']]
print(baseurl .. '&rrd_file=' .. rrdFile .. '&graph_zoom=' .. nextZoomLevel .. '&epoch=')
print[['+hover.selected_epoch;
});
</script>
]]
else
print("<div class=\"alert alert-danger\"><img src=".. ntop.getHttpPrefix() .. "/img/warning.png> File "..rrdname.." cannot be found</div>")
end
end
-- ########################################################
function create_rrd(name, step, ds)
if(not(ntop.exists(name))) then
if(enable_second_debug == 1) then io.write('Creating RRD ', name, '\n') end
local prefs = ntop.getPrefs()
ntop.rrd_create(
name,
step, -- step
'DS:' .. ds .. ':DERIVE:5:U:U',
'RRA:AVERAGE:0.5:1:'..tostring(prefs.intf_rrd_raw_days*24*60*60), -- raw: 1 day = 86400
'RRA:AVERAGE:0.5:60:'..tostring(prefs.intf_rrd_1min_days*24*60), -- 1 min resolution = 1 month
'RRA:AVERAGE:0.5:3600:'..tostring(prefs.intf_rrd_1h_days*24), -- 1h resolution (3600 points) 2400 hours = 100 days
'RRA:AVERAGE:0.5:86400:'..tostring(prefs.intf_rrd_1d_days) -- 1d resolution (86400 points) 365 days
-- 'RRA:HWPREDICT:1440:0.1:0.0035:20'
)
end
end
function create_rrd_num(name, ds)
if(not(ntop.exists(name))) then
if(enable_second_debug == 1) then io.write('Creating RRD ', name, '\n') end
local prefs = ntop.getPrefs()
ntop.rrd_create(
name,
1, -- step
'DS:' .. ds .. ':GAUGE:5:0:U',
'RRA:AVERAGE:0.5:1:'..tostring(prefs.intf_rrd_raw_days*24*60*60), -- raw: 1 day = 86400
'RRA:AVERAGE:0.5:3600:'..tostring(prefs.intf_rrd_1h_days*24), -- 1h resolution (3600 points) 2400 hours = 100 days
'RRA:AVERAGE:0.5:86400:'..tostring(prefs.intf_rrd_1d_days) -- 1d resolution (86400 points) 365 days
-- 'RRA:HWPREDICT:1440:0.1:0.0035:20'
)
end
end
function makeRRD(basedir, ifname, rrdname, step, value)
name = fixPath(basedir .. "/" .. rrdname .. ".rrd")
if(string.contains(rrdname, "num_")) then
create_rrd_num(name, rrdname)
else
create_rrd(name, 1, rrdname)
end
ntop.rrd_update(name, "N:".. toint(value))
if(enable_second_debug == 1) then io.write('Updating RRD ['.. ifname..'] '.. name .. " " .. value ..'\n') end
end
function createRRDcounter(path, step, verbose)
if(not(ntop.exists(path))) then
if(verbose) then print('Creating RRD ', path, '\n') end
local prefs = ntop.getPrefs()
ntop.rrd_create(
path,
step, -- step
'DS:sent:DERIVE:600:U:U',
'DS:rcvd:DERIVE:600:U:U',
'RRA:AVERAGE:0.5:1:'..tostring(prefs.other_rrd_raw_days*24*300), -- raw: 1 day = 1 * 24 = 24 * 300 sec = 7200
'RRA:AVERAGE:0.5:12:'..tostring(prefs.other_rrd_1h_days*24), -- 1h resolution (12 points) 2400 hours = 100 days
'RRA:AVERAGE:0.5:288:'..tostring(prefs.other_rrd_1d_days) -- 1d resolution (288 points) 365 days
--'RRA:HWPREDICT:1440:0.1:0.0035:20'
)
end
end
-- ########################################################
function createSingleRRDcounter(path, verbose)
if(not(ntop.exists(path))) then
if(verbose) then print('Creating RRD ', path, '\n') end
local prefs = ntop.getPrefs()
ntop.rrd_create(
path,
300, -- step
'DS:num:DERIVE:600:U:U',
'RRA:AVERAGE:0.5:1:'..tostring(prefs.other_rrd_raw_days*24*300), -- raw: 1 day = 1 * 24 = 24 * 300 sec = 7200
'RRA:AVERAGE:0.5:12:'..tostring(prefs.other_rrd_1h_days*24), -- 1h resolution (12 points) 2400 hours = 100 days
'RRA:AVERAGE:0.5:288:'..tostring(prefs.other_rrd_1d_days), -- 1d resolution (288 points) 365 days
'RRA:HWPREDICT:1440:0.1:0.0035:20')
end
end
-- ########################################################
function dumpSingleTreeCounters(basedir, label, host, verbose)
what = host[label]
if(what ~= nil) then
for k,v in pairs(what) do
for k1,v1 in pairs(v) do
-- print("-->"..k1.."/".. type(v1).."<--\n")
if(type(v1) == "table") then
for k2,v2 in pairs(v1) do
dname = fixPath(basedir.."/"..label.."/"..k.."/"..k1)
if(not(ntop.exists(dname))) then
ntop.mkdir(dname)
end
fname = dname..fixPath("/"..k2..".rrd")
createSingleRRDcounter(fname, verbose)
ntop.rrd_update(fname, "N:"..toint(v2))
if(verbose) then print("\t"..fname.."\n") end
end
else
dname = fixPath(basedir.."/"..label.."/"..k)
if(not(ntop.exists(dname))) then
ntop.mkdir(dname)
end
fname = dname..fixPath("/"..k1..".rrd")
createSingleRRDcounter(fname, verbose)
ntop.rrd_update(fname, "N:"..toint(v1))
if(verbose) then print("\t"..fname.."\n") end
end
end
end
end
end
function printGraphTopFlows(ifId, host, epoch, zoomLevel, l7proto)
-- Check if the DB is enabled
rsp = interface.execSQLQuery("show tables")
if(rsp == nil) then return end
if((epoch == nil) or (epoch == "")) then epoch = os.time() end
local d = getZoomDuration(zoomLevel)/2
epoch_end = epoch+d
epoch_begin = epoch-d
printTopFlows(ifId, host, epoch_begin, epoch_end, l7proto, '', '', 5, 5)
end
function printTopFlows(ifId, host, epoch_begin, epoch_end, l7proto, l4proto, port, limitv4, limitv6)
url_update = "/lua/get_db_flows.lua?ifId="..ifId.. "&host="..(host or '') .. "&epoch_begin="..epoch_begin.."&epoch_end="..epoch_end.."&l4proto="..l4proto.."&port="..port
if(l7proto ~= "") then
if(not(isnumber(l7proto))) then
local id
-- io.write(l7proto.."\n")
l7proto = string.gsub(l7proto, "%.rrd", "")
if(string.ends(l7proto, ".rrd")) then l7proto = string.sub(l7proto, 1, -5) end
id = interface.getnDPIProtoId(l7proto)
if(id ~= -1) then
l7proto = id
title = "Top "..l7proto.." Flows"
else
l7proto = ""
end
end
if(l7proto ~= "") then
url_update = url_update.."&l7proto="..l7proto
end
end
if((host == "") and (l4proto == "") and (port == "")) then
title = "Top Flows ["..formatEpoch(epoch_begin).." - "..formatEpoch(epoch_end).."]"
else
title = ""
end
print [[
<div class="container-fluid">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
]]
if(host ~= nil) then
local chunks = {host:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")}
if(#chunks == 4) then
limitv6="0"
end
end
selected = false
if(not((limitv4 == nil) or (limitv4 == "") or (limitv4 == "0"))) then
print('<li class="active"> <a href="#ipv4" role="tab" data-toggle="tab"> IPv4 </a> </li>\n')
selected = true
end
if(not((limitv6 == nil) or (limitv6 == "") or (limitv6 == "0"))) then
if(selected == false) then print('<li class="active">\n') else print('<li>\n') end
print('<a href="#ipv6" role="tab" data-toggle="tab"> IPv6 </a> </li>\n')
end
print [[
</ul>
<!-- Tab panes -->
<div class="tab-content">
]]
if(not((limitv4 == nil) or (limitv4 == "") or (limitv4 == "0"))) then
print [[
<div class="tab-pane fade active in" id="ipv4"> <div id="table-flows4"></div> </div>
]]
end
if(not((limitv6 == nil) or (limitv6 == "") or (limitv6 == "0"))) then
if(selected == false) then print('<div class="tab-pane fade active in" id="ipv6">\n') else print('<div class="tab-pane fade" id="ipv6">\n') end
print('<div id="table-flows6"></div> </div>')
end
print [[
</div>
</div>
<script>
]]
if(not((limitv4 == nil) or (limitv4 == "") or (limitv4 == "0"))) then
print [[
var url_update4 = "]] print(url_update.."&limit="..limitv4) print [[&version=4";
var graph_options4 = {
url: url_update4,
perPage: 5, ]]
if(title ~= "") then print('title: "IPv4 '..title..'",\n') else print("title: '',\n") end
print [[
showFilter: true,
showPagination: true,
sort: [ [ "BYTES","desc"] ],
columns: [
{
title: "Key",
field: "idx",
hidden: true,
},
]]
if(ntop.isPro()) then
print [[
{
title: "",
field: "FLOW_URL",
sortable: false,
css: {
textAlign: 'center'
}
},
]]
end
print [[
{
title: "Application",
field: "L7_PROTO",
sortable: true,
css: {
textAlign: 'center'
}
},
{
title: "L4 Proto",
field: "PROTOCOL",
sortable: true,
css: {
textAlign: 'center'
}
},
{
title: "Client",
field: "CLIENT",
sortable: false,
},
{
title: "Server",
field: "SERVER",
sortable: false,
},
{
title: "Begin",
field: "FIRST_SWITCHED",
sortable: true,
css: {
textAlign: 'center'
}
},
{
title: "End",
field: "LAST_SWITCHED",
sortable: true,
css: {
textAlign: 'center'
}
},
{
title: "Bytes",
field: "BYTES",
sortable: true,
css: {
textAlign: 'right'
}
},
{
title: "Avg Thpt",
field: "AVG_THROUGHPUT",
sortable: false,
css: {
textAlign: 'right'
}
}
]
};
var table4 = $("#table-flows4").datatable(graph_options4);
]]
end
if((limitv6 == nil) or (limitv6 == "") or (limitv6 == "0")) then print("</script>") return end
print [[
var url_update6 = "]] print(url_update.."&limit="..limitv6) print [[&version=6";
var graph_options6 = {
url: url_update6,
perPage: 5, ]]
if(title ~= "") then print('title: "IPv6 '..title..'",\n') else print("title: '',\n") end
print [[
showFilter: true,
showPagination: true,
sort: [ [ "BYTES","desc"] ],
columns: [
{
title: "Key",
field: "idx",
hidden: true,
},
]]
if(ntop.isPro()) then
print [[
{
title: "",
field: "FLOW_URL",
sortable: false,
css: {
textAlign: 'center'
}
},
]]
end
print [[
{
title: "Application",
field: "L7_PROTO",
sortable: true,
css: {
textAlign: 'center'
}
},
{
title: "L4 Proto",
field: "PROTOCOL",
sortable: true,
css: {
textAlign: 'center'
}
},
{
title: "Client",
field: "CLIENT",
sortable: false,
},
{
title: "Server",
field: "SERVER",
sortable: false,
},
{
title: "Begin",
field: "FIRST_SWITCHED",
sortable: true,
css: {
textAlign: 'center'
}
},
{
title: "End",
field: "LAST_SWITCHED",
sortable: true,
css: {
textAlign: 'center'
}
},
{
title: "Bytes",
field: "BYTES",
sortable: true,
css: {
textAlign: 'right'
}
},
{
title: "Avg Thpt",
field: "AVG_THROUGHPUT",
sortable: false,
css: {
textAlign: 'right'
}
}
]
};
var table6 = $("#table-flows6").datatable(graph_options6);
</script>
]]
end