Improves the responsiveness and interactivity of historical exploration

Uses ajax to handle (possibly) long queries on historical data
with the aim of improving user experience.
The user is notified when a query is taking too long so that
he/she can get feedback of what is going on.
This commit is contained in:
Simone Mainardi 2016-10-31 15:56:11 +01:00
parent 0793d8666a
commit 8f87f11b26
4 changed files with 568 additions and 485 deletions

View file

@ -454,8 +454,7 @@ if ntop.getPrefs().is_dump_flows_to_mysql_enabled
-- hide historical tabs for networks
and not string.starts(host, 'net:')
then
print('<li><a href="#ipv4" role="tab" data-toggle="tab" id="tab-ipv4"> IPv4 Flows </a> </li>\n')
print('<li><a href="#ipv6" role="tab" data-toggle="tab" id="tab-ipv6"> IPv6 Flows </a> </li>\n')
print('<li><a href="#historical-flows" role="tab" data-toggle="tab" id="tab-flows-summary"> Flows </a> </li>\n')
end
print[[
@ -608,12 +607,14 @@ if ntop.getPrefs().is_dump_flows_to_mysql_enabled
-- hide historical tabs for networks and profiles
and not string.starts(host, 'net:')
then
if tonumber(start_time) ~= nil and tonumber(end_time) ~= nil then
-- if both start_time and end_time are vaid epoch we can print finer-grained top flows
printTopFlows(ifid, (host or ''), start_time, end_time, rrdFile, '', '', '', 5, 5)
else
printGraphTopFlows(ifid, (host or ''), _GET["epoch"], zoomLevel, rrdFile)
end
print('<div class="tab-pane fade" id="historical-flows">')
if tonumber(start_time) ~= nil and tonumber(end_time) ~= nil then
-- if both start_time and end_time are vaid epoch we can print finer-grained top flows
historicalFlowsTab(ifid, (host or ''), start_time, end_time, rrdFile, '', '', '', 5, 5)
else
printGraphTopFlows(ifid, (host or ''), _GET["epoch"], zoomLevel, rrdFile)
end
print('</div>')
end
print[[
@ -649,13 +650,6 @@ function fdate(when) {
return(d);
}
function fbits(bits) {
var sizes = ['bps', 'Kbit/s', 'Mbit/s', 'Gbit/s', 'Tbit/s'];
if(bits == 0) return '';
var i = parseInt(Math.floor(Math.log(bits) / Math.log(1000)));
return Math.round(bits / Math.pow(1000, i), 2) + ' ' + sizes[i];
}
function capitaliseFirstLetter(string)
{
return string.charAt(0).toUpperCase() + string.slice(1);
@ -1005,403 +999,7 @@ function printGraphTopFlows(ifId, host, epoch, zoomLevel, l7proto)
epoch_end = epoch
epoch_begin = epoch-d
local tot_flows = { }
local versions = { ['IPv4']=4, ['IPv6']=6 }
for k,v in pairs(versions) do
local res = getNumFlows(ifId, v, (host or ''), '', '', (l7proto or ''), '', epoch_begin, epoch_end)
for _,flow in pairs(res) do
tot_flows[v] = flow['TOT_FLOWS']
end
end
printTopFlows(ifId, host, epoch_begin, epoch_end, l7proto, '', '', '', tot_flows[4], tot_flows[6])
end
function printTopFlows(ifId, host, epoch_begin, epoch_end, l7proto, l4proto, port, info, limitv4, limitv6)
url_update = ntop.getHttpPrefix().."/lua/get_db_flows.lua?ifId="..ifId.. "&host="..(host or '') .. "&epoch_begin="..(epoch_begin or '').."&epoch_end="..(epoch_end or '').."&l4proto="..(l4proto or '').."&port="..(port or '').."&info="..(info or '')
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
if(host ~= nil) then
local chunks = {host:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")}
if(#chunks == 4) then
limitv6="0"
end
end
-- prepare some attributes that will be attached to divs
local div_data = ""
if epoch_begin ~= "" and epoch_begin ~= nil then
div_data = div_data..' epoch_begin="'..tostring(epoch_begin)..'" '
end
if epoch_end ~= "" and epoch_end ~= nil then
div_data = div_data..' epoch_end="'..tostring(epoch_end)..'" '
end
if host ~= "" and host ~= nil then
div_data = div_data..' host="'..tostring(host)..'" '
end
if l7proto ~= "" and l7proto ~= nil then
div_data = div_data..' l7_proto_id="'..l7proto..'" '
end
if l4proto ~= "" and l4proto ~= nil then
div_data = div_data..' l4_proto_id="'..l4proto..'" '
end
if port ~= "" and port ~= nil then
div_data = div_data..' port="'..port..'" '
end
local ipv4_enabled = not(limitv4 == nil or limitv4 == "" or limitv4 == "0")
local ipv6_enabled = not(limitv6 == nil or limitv6 == "" or limitv6 == "0")
if ipv4_enabled == true then
print [[
<div class="tab-pane fade" id="ipv4" ]] print(div_data) print[[">
<div id="table-flows4"></div>
]] historicalDownloadButtonsBar('flows_v4', 'ipv4',
ipv4_enabled,
ipv6_enabled) print[[
</div>
]]
else
print[[
<script>
$('#tab-ipv4').remove()
</script>
]]
end
if ipv6_enabled == true then
print[[
<div class="tab-pane fade" id="ipv6" ]] print(div_data) print[[">
<div id="table-flows6"></div>
]] historicalDownloadButtonsBar('flows_v6', 'ipv6',
ipv4_enabled,
ipv6_enabled
) print[[
</div>
]]
else
print[[
<script>
$('#tab-ipv6').remove()
</script>
]]
end
print [[
<script>
$('a[href="#ipv4"]').on('shown.bs.tab', function (e) {
if ($('a[href="#ipv4"]').attr("loaded") == 1){
// do nothing if the tab has already been computed and populated
enableAllDropdownsAndTabs();
return;
}
// if here, then we actually have to load the datatable
disableAllDropdownsAndTabs();
$('a[href="#ipv4"]').attr("loaded", 1);
]]
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,
tableCallback: function(){enableAllDropdownsAndTabs();},
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: "Traffic Sent",
field: "IN_BYTES",
sortable: true,
css: {
textAlign: 'right'
}
},
{
title: "Traffic Received",
field: "OUT_BYTES",
sortable: true,
css: {
textAlign: 'right'
}
},
{
title: "Total Traffic",
field: "BYTES",
sortable: true,
css: {
textAlign: 'right'
}
},
{
title: "Info",
field: "INFO",
sortable: true,
css: {
textAlign: 'left'
}
},
{
title: "Avg Thpt",
field: "AVG_THROUGHPUT",
sortable: false,
css: {
textAlign: 'right'
}
}
]
};
var table4 = $("#table-flows4").datatable(graph_options4);
]]
end
print[[
}); // closes the event handler on shown.bs.tab
]]
if((limitv6 == nil) or (limitv6 == "") or (limitv6 == "0")) then print("</script>") return end
print [[
$('a[href="#ipv6"]').on('shown.bs.tab', function (e) {
if ($('a[href="#ipv6"]').attr("loaded") == 1){
// do nothing if the tab has already been computed and populated
enableAllDropdownsAndTabs();
return;
}
// if here, then we actually have to load the datatable
disableAllDropdownsAndTabs();
$('a[href="#ipv6"]').attr("loaded", 1);
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,
tableCallback: function(){enableAllDropdownsAndTabs();},
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: "Traffic Sent",
field: "IN_BYTES",
sortable: true,
css: {
textAlign: 'right'
}
},
{
title: "Traffic Received",
field: "OUT_BYTES",
sortable: true,
css: {
textAlign: 'right'
}
},
{
title: "Total Traffic",
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);
}); // closes the event handler on shown.bs.tab
</script>
]]
historicalFlowsTab(ifId, host, epoch_begin, epoch_end, l7proto, '', '', '')
end
-- ########################################################