mirror of
https://github.com/ntop/ntopng.git
synced 2026-04-29 15:39:33 +00:00
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:
parent
0793d8666a
commit
8f87f11b26
4 changed files with 568 additions and 485 deletions
|
|
@ -1,5 +1,7 @@
|
|||
require "lua_utils"
|
||||
|
||||
local json = require ("dkjson")
|
||||
|
||||
local pcap_status_url = ntop.getHttpPrefix().."/lua/get_nbox_data.lua?action=status"
|
||||
local pcap_request_url = ntop.getHttpPrefix().."/lua/get_nbox_data.lua?action=schedule"
|
||||
local favourites_url = ntop.getHttpPrefix().."/lua/get_historical_favourites.lua"
|
||||
|
|
@ -304,7 +306,8 @@ function historicalDownloadButtonsBar(button_id, pcap_request_data_container_div
|
|||
|
||||
|
||||
print[[
|
||||
if($('#tab-ipv4').length > 0){
|
||||
|
||||
if($('#tab-ipv4').length > 0 || true /* TODO: disable if no flows are available */){
|
||||
$('#download_flows_v4_]] print(button_id) print[[').click(function (event){
|
||||
window.location.assign("]] print(flows_download_url) print [[?version=4&format=txt&" + $.param(buildRequestData(']] print(pcap_request_data_container_div_id) print[[')));
|
||||
return false;
|
||||
|
|
@ -313,7 +316,7 @@ print[[
|
|||
$('#download_flows_v4_]] print(button_id) print[[').attr("style", "display:none;");
|
||||
}
|
||||
|
||||
if($('#tab-ipv6').length > 0){
|
||||
if($('#tab-ipv6').length > 0 || true /* TODO: disable if no flows are available */){
|
||||
$('#download_flows_v6_]] print(button_id) print[[').click(function (event){
|
||||
window.location.assign("]] print(flows_download_url) print [[?version=6&format=txt&" + $.param(buildRequestData(']] print(pcap_request_data_container_div_id) print[[')));
|
||||
return false;
|
||||
|
|
@ -1416,6 +1419,512 @@ end
|
|||
|
||||
-- ##########################################
|
||||
|
||||
function historicalFlowsTab(ifId, host, epoch_begin, epoch_end, l7proto, l4proto, port, info)
|
||||
-- prepare some attributes that will be attached to divs
|
||||
local div_data = ""
|
||||
|
||||
if ifId ~= "" and ifId ~= nil then
|
||||
_GET["ifId"] = ifId
|
||||
div_data = div_data..' ifId="'..tostring(ifId)..'" '
|
||||
end
|
||||
if epoch_begin ~= "" and epoch_begin ~= nil then
|
||||
_GET["epoch_begin"] = epoch_begin
|
||||
div_data = div_data..' epoch_begin="'..tostring(epoch_begin)..'" '
|
||||
end
|
||||
if epoch_end ~= "" and epoch_end ~= nil then
|
||||
_GET["epoch_end"] = epoch_end
|
||||
div_data = div_data..' epoch_end="'..tostring(epoch_end)..'" '
|
||||
end
|
||||
if host ~= "" and host ~= nil then
|
||||
_GET["host"] = host
|
||||
div_data = div_data..' host="'..tostring(host)..'" '
|
||||
end
|
||||
if l7proto ~= "" and l7proto ~= nil then
|
||||
_GET["protocol"] = l7proto
|
||||
div_data = div_data..' l7_proto_id="'..l7proto..'" '
|
||||
end
|
||||
if l4proto ~= "" and l4proto ~= nil then
|
||||
_GET["l4proto"] = l4proto
|
||||
div_data = div_data..' l4_proto_id="'..l4proto..'" '
|
||||
end
|
||||
if port ~= "" and port ~= nil then
|
||||
_GET["port"] = port
|
||||
div_data = div_data..' port="'..port..'" '
|
||||
end
|
||||
|
||||
print[[
|
||||
|
||||
<br>
|
||||
<div class="container-fluid" id="historical-flows-container">
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="active"> <a href="#historical-flows-summary" role="tab" data-toggle="tab"> Summary </a> </li>
|
||||
<li class="disabled"> <a href="#tab-ipv4" role="tab"> IPv4 </a> </li>
|
||||
<li class="disabled"> <a href="#tab-ipv6" role="tab"> IPv6 </a> </li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane fade in active" id="historical-flows-summary">
|
||||
<br>
|
||||
<div class="panel panel-default" id="historical-flows-summary-div">
|
||||
<div class="panel-heading"> <h3 class="panel-title"> Search Results </h3> </div>
|
||||
<div class="panel-body" id="historical-flows-summary-body" style="display:true;">
|
||||
<div id="flows-summary-too-slow" style="display:none;" class="alert alert-warning"></div>
|
||||
<div id="flows-summary-wait" style="display:true;">
|
||||
<img src="]] print(ntop.getHttpPrefix()) print[[/img/loading.gif"\>
|
||||
Database query in progress, waiting to receive results...
|
||||
<button class="btn btn-danger btn-xs" type="button" onclick="abortQuery();">Abort</button>
|
||||
</div>
|
||||
</div>
|
||||
<table border=0 class="table table-bordered table-striped" id="flows-summary-table" style="display:none;">
|
||||
<tr>
|
||||
<th> </th><th>Total Flows</th><th>Traffic Volume</th>
|
||||
<th>Total Packets</th><th>Traffic Rate</th><th>Packet Rate</th>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-pane fade" id="tab-ipv4" num_flows=0 ]] print(div_data) print[[>
|
||||
<div id="table-flows4"></div>
|
||||
]] historicalDownloadButtonsBar('flows_v4', 'tab-ipv4',
|
||||
true,
|
||||
true) print[[
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="tab-ipv6" num_flows=0 ]] print(div_data) print[[>
|
||||
<div id="table-flows6"></div>
|
||||
]] historicalDownloadButtonsBar('flows_v6', 'tab-ipv6',
|
||||
true,
|
||||
true
|
||||
) print[[
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var xhr;
|
||||
|
||||
var abortQuery = function(){
|
||||
if(xhr && xhr.readyState != 4){
|
||||
xhr.abort();
|
||||
}
|
||||
// error message is populated in the ajax error callback
|
||||
}
|
||||
|
||||
$('a[href="#historical-flows"]').on('shown.bs.tab', function (e) {
|
||||
if ($('a[href="#historical-flows"]').attr("loaded") == 1){
|
||||
enableAllDropdownsAndTabs();
|
||||
// do nothing if the tabs have already been computed and populated
|
||||
return;
|
||||
}
|
||||
|
||||
var target = $(e.target).attr("href"); // activated tab
|
||||
$('a[href="#historical-flows"]').attr("loaded", 1);
|
||||
|
||||
// disable all tabs
|
||||
$("#historical-flows-container").find("li").addClass("disabled").find("a").removeAttr("data-toggle");
|
||||
|
||||
xhr = $.ajax({
|
||||
type: 'GET',]]
|
||||
print("url: '"..ntop.getHttpPrefix().."/lua/get_db_data.lua?ifname="..tostring(_GET["ifId"]).."', ")
|
||||
print("data: "..json.encode(_GET, nil)..",")
|
||||
print[[
|
||||
complete: function() {
|
||||
$("#flows-summary-wait").hide()
|
||||
},
|
||||
error: function() {
|
||||
var err_msg = "."
|
||||
|
||||
if(xhr.responseText && xhr.responseText !== "")
|
||||
err_msg = ": " + xhr.responseText + err_msg
|
||||
|
||||
if(xhr.statusText === "error")
|
||||
err_msg = "An error occurred. Check database connections status" + err_msg
|
||||
else if(xhr.statusText === "abort")
|
||||
err_msg = "Query aborted" + err_msg
|
||||
else
|
||||
err_msg = "Query failed with an unknown status " + xhr.statusText + err_msg
|
||||
|
||||
$("#historical-flows-summary-body").html(err_msg).show()
|
||||
},
|
||||
success: function(msg){
|
||||
if(msg.status !== "ok") {
|
||||
$("#historical-flows-summary-body").html('<H5><i class="fa fa-exclamation-triangle fa-2x"></i> ' + msg.statusText + '</H5>').show()
|
||||
return;
|
||||
} else if(msg.count.IPv4.tot_flows <= 0 && msg.count.IPv6.tot_flows <= 0) {
|
||||
$("#historical-flows-summary-body").html('<H5><i class="fa fa-exclamation-triangle fa-2x"></i> No results found. Please modify your search criteria.</H5>').show()
|
||||
return;
|
||||
}
|
||||
|
||||
// re-enable all tabs
|
||||
$("#historical-flows-container").find("li").removeClass("disabled").find("a").attr("data-toggle", "tab");
|
||||
|
||||
// populate the number of flows
|
||||
$("#tab-ipv4").attr("num_flows", msg.count.IPv4.tot_flows)
|
||||
$("#tab-ipv6").attr("num_flows", msg.count.IPv6.tot_flows)
|
||||
|
||||
var tr=""
|
||||
$.each(msg.count, function(ipvers, item){
|
||||
tr += "<tr><th>" + ipvers + "</th>"
|
||||
tr += "<td align='right'>" + item.tot_flows + " Flows</td>"
|
||||
tr += "<td align='right'>" + bytesToVolume(item.tot_bytes) + "</td>"
|
||||
tr += "<td align='right'>" + formatPackets(item.tot_packets) + "</td>"
|
||||
tr += "<td align='right'>" + fbits(item.tot_bytes * 8 / msg.timespan) + "</td>"
|
||||
tr += "<td align='right'>" + fpackets(item.tot_packets / msg.timespan) + "</td>"
|
||||
tr += "</tr>"
|
||||
});
|
||||
$("#flows-summary-table").append(tr)
|
||||
$("#historical-flows-summary-body").remove()
|
||||
$("#flows-summary-table").show();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
setInterval(function() {
|
||||
var too_slow = "The database is taking too long to produce results. Consider narrowing down the scope of the query"
|
||||
too_slow += " or tune the database for performance."
|
||||
$("#flows-summary-too-slow").html(too_slow).show()
|
||||
}, 15000)
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
]]
|
||||
historicalFlowsTabTables(ifId, host, epoch_begin, epoch_end, l7proto, l4proto, port, info)
|
||||
|
||||
end
|
||||
|
||||
-- ##########################################
|
||||
|
||||
function historicalFlowsTabTables(ifId, host, epoch_begin, epoch_end, l7proto, l4proto, port, info, limitv4, limitv6)
|
||||
local 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
|
||||
|
||||
print [[
|
||||
|
||||
<script type="text/javascript">
|
||||
$('a[href="#tab-ipv4"]').on('shown.bs.tab', function (e) {
|
||||
if ($('a[href="#tab-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="#tab-ipv4"]').attr("loaded", 1);
|
||||
|
||||
]]
|
||||
|
||||
print [[
|
||||
var url_update4 = "]] print(url_update) print [[&version=4&limit=" + $("#tab-ipv4").attr("num_flows") ;
|
||||
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);
|
||||
]]
|
||||
|
||||
print[[
|
||||
}); // closes the event handler on shown.bs.tab
|
||||
]]
|
||||
|
||||
print [[
|
||||
$('a[href="#tab-ipv6"]').on('shown.bs.tab', function (e) {
|
||||
if ($('a[href="#tab-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="#tab-ipv6"]').attr("loaded", 1);
|
||||
|
||||
|
||||
var url_update6 = "]] print(url_update) print [[&version=6&limit=" + $("#tab-ipv6").attr("num_flows") ;
|
||||
|
||||
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>
|
||||
]]
|
||||
|
||||
end
|
||||
|
||||
-- ##########################################
|
||||
|
||||
function historicalProtoHostHref(ifId, host, l4_proto, ndpi_proto_id, info)
|
||||
if ntop.isPro() and ntop.getPrefs().is_dump_flows_to_mysql_enabled == true then
|
||||
local hist_url = ntop.getHttpPrefix().."/lua/pro/db_explorer.lua?search=true&ifId="..ifId
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue