Implements L7 application statistics drill-down

L7 Statistics can be drilled-down starting at the interface
or at the host level.
This commit is contained in:
Simone Mainardi 2016-02-12 18:49:03 +01:00
parent 3d2df7e64e
commit 5f4eee79a3
4 changed files with 384 additions and 43 deletions

View file

@ -1,5 +1,32 @@
require "lua_utils"
function commonJsUtils()
print[[
function hideAll(cla){
$('.' + cla).hide();
}
function showOne(cla, id){
$('.' + cla).not('#' + id).hide();
$('#' + id).show();
}
function hostkey2hostid(host_key) {
var info;
var hostinfo = [];
host_key = host_key.replace(/\:/g, "____");
host_key = host_key.replace(/\//g, "___");
host_key = host_key.replace(/\./g, "__");
info = host_key.split("@");
return(info);
}
]]
end
function historicalTopTalkersTable(ifid, epoch_begin, epoch_end, host)
local breadcrumb_root = "interface"
local host_talkers_url_params = ""
@ -28,27 +55,7 @@ function historicalTopTalkersTable(ifid, epoch_begin, epoch_end, host)
</div>
<script type="text/javascript">
function hideAll(cla){
$('.' + cla).hide();
}
function showOne(cla, id){
$('.' + cla).not('#' + id).hide();
$('#' + id).show();
}
function hostkey2hostInfo(host_key) {
var info;
var hostinfo = [];
host_key = host_key.replace(/\:/g, "____");
host_key = host_key.replace(/\//g, "___");
host_key = host_key.replace(/\./g, "__");
info = host_key.split("@");
return(info);
}
]] commonJsUtils() print[[
var emptyBreadCrumb = function(){
$('#bc-talkers').empty();
@ -111,12 +118,10 @@ var populateInterfaceTopTalkersTable = function(){
}
};
var populateHostTopTalkersTable = function(host){
refreshBreadCrumbHost(host);
var div_id = 'host-' + hostkey2hostInfo(host)[0];
var div_id = 'host-' + hostkey2hostid(host)[0];
if ($('#'+div_id).length == 0) // create the div only if it does not exist
$('#hosts-container').append('<div class="host-talkers" id="' + div_id + '" total_rows=-1 loaded=0></div>');
@ -163,8 +168,8 @@ var populateHostTopTalkersTable = function(host){
var populateAppsPerHostsPairTable = function(peer1, peer2){
refreshBreadCrumbPairs(peer1, peer2);
var kpeer1 = hostkey2hostInfo(peer1)[0];
var kpeer2 = hostkey2hostInfo(peer2)[0];
var kpeer1 = hostkey2hostid(peer1)[0];
var kpeer2 = hostkey2hostid(peer2)[0];
if (kpeer2 > kpeer1){
var tmp = kpeer1;
kpeer2 = kpeer1;
@ -232,50 +237,293 @@ $('a[href="#historical-top-talkers"]').on('shown.bs.tab', function (e) {
end
function historicalTopApplicationsTable(ifid, epoch_begin, epoch_end, host)
local breadcrumb_root = "interface"
local top_apps_url_params=""
top_apps_url_params = top_apps_url_params.."&epoch_start="..epoch_begin
top_apps_url_params = top_apps_url_params.."&epoch_end="..epoch_end
if host and host ~= "" then
top_apps_url_params = top_apps_url_params.."&peer1="..host
breadcrumb_root="host"
end
local preference = tablePreferences("rows_number",_GET["perPage"])
local sort_order = getDefaultTableSortOrder("historical_stats_top_applications")
local sort_column= getDefaultTableSort("historical_stats_top_applications")
if not sort_column or sort_column == "column_" then sort_column = "column_tot_bytes" end
if not sort_column or sort_column == "column_" then sort_column = "column_bytes" end
print[[
<div id="historical-top-applications-table" total_rows=-1>
<ol class="breadcrumb" id="bc-apps" style="margin-bottom: 5px;"]] print('root="'..breadcrumb_root..'"') print [[>
</ol>
<div id="historical-apps-container">
<div id="historical-interface-top-apps-table" class="historical-interface-apps" total_rows=-1 loaded=0> </div>
<div id="apps-container"> </div>
<div id="peers-per-host-by-app-container"> </div>
</div>
<script type="text/javascript">
var totalRows = -1;
$('a[href="#historical-top-apps"]').on('shown.bs.tab', function (e) {
var target = $(e.target).attr("href"); // activated tab
$('#historical-top-applications-table').datatable({
var emptyAppsBreadCrumb = function(){
$('#bc-apps').empty();
};
var refreshHostPeersByAppBreadCrumb = function(peer1, proto_id){
emptyAppsBreadCrumb();
var root = $("#bc-apps").attr("root");
var app = $('#historical-interface-top-apps-table').attr("proto");
if (root === "interface"){
$("#bc-apps").append('<li><a onclick="populateInterfaceTopAppsTable();">Interface ]] print(getInterfaceName(ifid)) print [[</a></li>');
$("#bc-apps").append('<li><a onclick="populateAppTopTalkersTable(\'' + proto_id + '\');">Talkers speaking ' + app + '</a></li>');
$("#bc-apps").append('<li>Talkers speaking ' + app + ' with ' + peer1 + ' </li>');
} else if (root == "host"){
var host = $('#historical-interface-top-apps-table').attr("host");
$("#bc-apps").append('<li><a onclick="populateHostTopAppsTable(host);">Protocols spoken by ' + peer1 + '</a></li>');
$("#bc-apps").append('<li>Talkers speaking ' + app + ' with ' + peer1 + ' </li>');
}
}
var populateInterfaceTopAppsTable = function(){
emptyAppsBreadCrumb();
$("#bc-apps").append('<li>Interface ]] print(getInterfaceName(ifid)) print [[</li>');
hideAll("app-talkers");
hideAll("peers-by-app");
showOne('historical-interface-apps', 'historical-interface-top-apps-table');
if ($('#historical-interface-top-apps-table').attr("loaded") != 1) {
$('#historical-interface-top-apps-table').attr("loaded", 1);
$('#historical-interface-top-apps-table').datatable({
title: "",]]
print("url: '"..ntop.getHttpPrefix().."/lua/get_historical_data.lua?stats_type=top_applications"..top_apps_url_params.."',")
if preference ~= "" then print ('perPage: '..preference.. ",") end
-- Automatic default sorted. NB: the column must be exists.
print ('sort: [ ["'..sort_column..'","'..sort_order..'"] ],')
print [[
post: {totalRows: function(){ return $('#historical-top-applications-table').attr("total_rows");} },
post: {totalRows: function(){ return $('#historical-interface-top-apps-table').attr("total_rows");} },
showFilter: true,
showPagination: true,
tableCallback: function(){$('#historical-top-applications-table').attr("total_rows", this.options.totalRows);},
tableCallback: function(){$('#historical-interface-top-apps-table').attr("total_rows", this.options.totalRows);},
rowCallback: function(row){
var proto_id_td = $("td:eq(0)", row[0]);
var proto_label_td = $("td:eq(1)", row[0]);
var proto_id = proto_id_td.text();
var proto_label = proto_label_td.text();
proto_label_td.append('&nbsp;<a onclick="$(\'#historical-interface-top-apps-table\').attr(\'proto\', \'' + proto_label + '\');populateAppTopTalkersTable(\'' + proto_id +'\');"><i class="fa fa-pie-chart" title="Get Talkers using this protocol"></i></a>');
return row;
},
columns:
[
{title: "Protocol id", field: "column_application", hidden: true},
{title: "Application", field: "column_label", sortable: false},
{title: "Avg. Flow Duration", field: "column_avg_flow_duration", sortable: true, css: {textAlign:'center'}},
{title: "Traffic Volume", field: "column_bytes", sortable: true, css: {textAlign:'right'}},
{title: "Packets", field: "column_packets", sortable: true, css: {textAlign:'right'}},
{title: "Flows", field: "column_flows", sortable: true, css: {textAlign:'right'}}
]
});
});
}
};
var populateAppTopTalkersTable = function(proto_id){
emptyAppsBreadCrumb();
var app = $('#historical-interface-top-apps-table').attr("proto");
$("#bc-apps").append('<li><a onclick="populateInterfaceTopAppsTable();">Interface ]] print(getInterfaceName(ifid)) print [[</a></li>');
$("#bc-apps").append('<li>Talkers speaking ' + app + ' </li>');
var div_id = 'app-' + proto_id;
if ($('#'+div_id).length == 0) // create the div only if it does not exist
$('#apps-container').append('<div class="app-talkers" id="' + div_id + '" total_rows=-1 loaded=0></div>');
hideAll('historical-interface-apps');
hideAll('peers-by-app');
showOne('app-talkers', div_id);
// load the table only if it is the first time we've been called
div_id='#'+div_id;
if ($(div_id).attr("loaded") != 1) {
$(div_id).attr("loaded", 1);
$(div_id).attr("l7_proto", proto_id);
$(div_id).datatable({
title: "",]]
print("url: '"..ntop.getHttpPrefix().."/lua/get_historical_data.lua?stats_type=top_talkers"..top_apps_url_params.."&l7_proto_id=' + proto_id ,")
if preference ~= "" then print ('perPage: '..preference.. ",\n") end
-- Automatic default sorted. NB: the column must be exists.
print ('sort: [ ["'..sort_column..'","'..sort_order..'"] ],\n')
print [[
post: {totalRows: function(){ return $(div_id).attr("total_rows");} },
showFilter: true,
showPagination: true,
tableCallback: function(){$(div_id).attr("total_rows", this.options.totalRows);},
rowCallback: function(row){
var addr_td = $("td:eq(0)", row[0]);
var label_td = $("td:eq(1)", row[0]);
var addr = addr_td.text();
label_td.append('&nbsp;<a onclick="populatePeersPerHostByApplication(\'' + addr +'\',\'' + proto_id +'\');"><i class="fa fa-exchange" title="Hosts talking ' + proto_id + ' with ' + addr + '"></i></a>');
return row;
},
columns:
[
{title: "Address", field: "column_addr", hidden: true},
{title: "Host Name", field: "column_label", sortable: true},
{title: "Avg. Flow Duration", field: "column_avg_flow_duration", sortable: true, css: {textAlign:'center'}},
{title: "Traffic Volume", field: "column_bytes", sortable: true,css: {textAlign:'right'}},
{title: "Packets", field: "column_packets", sortable: true, css: {textAlign:'right'}},
{title: "Flows", field: "column_flows", sortable: true, css: {textAlign:'right'}}
]
});
}
};
var populatePeersPerHostByApplication = function(host, proto_id){
refreshHostPeersByAppBreadCrumb(host, proto_id);
var div_id = 'app-' + proto_id + '-host-' + hostkey2hostid(host)[0];
if ($('#'+div_id).length == 0) // create the div only if it does not exist
$('#peers-per-host-by-app-container').append('<div class="peers-by-app" id="' + div_id + '" total_rows=-1 loaded=0></div>');
hideAll('historical-interface-apps');
hideAll('app-talkers');
showOne('peers-by-app', div_id);
// load the table only if it is the first time we've been called
div_id='#'+div_id;
if ($(div_id).attr("loaded") != 1) {
$(div_id).attr("loaded", 1);
$(div_id).attr("l7_proto", proto_id);
$(div_id).attr("host", host);
$(div_id).datatable({
title: "",]]
print("url: '"..ntop.getHttpPrefix().."/lua/get_historical_data.lua?stats_type=top_talkers"..top_apps_url_params.."&l7_proto_id=' + proto_id + '&peer1=' + host ,")
if preference ~= "" then print ('perPage: '..preference.. ",\n") end
-- Automatic default sorted. NB: the column must be exists.
print ('sort: [ ["'..sort_column..'","'..sort_order..'"] ],\n')
print [[
post: {totalRows: function(){ return $(div_id).attr("total_rows");} },
showFilter: true,
showPagination: true,
tableCallback: function(){$(div_id).attr("total_rows", this.options.totalRows);},
/*
rowCallback: function(row){
var addr_td = $("td:eq(0)", row[0]);
var label_td = $("td:eq(1)", row[0]);
var addr = addr_td.text();
var label = addr_td.text();
label_td.append('&nbsp;<a onclick="populateAppsPerHostsPairTable(\'' + host +'\',\'' + addr +'\');"><i class="fa fa-exchange" title="Hosts talking ' + label + ' with ' + host + '"></i></a>');
return row;
},
*/
columns:
[
{title: "Address", field: "column_addr", hidden: true},
{title: "Host Name", field: "column_label", sortable: true},
{title: "Avg. Flow Duration", field: "column_avg_flow_duration", sortable: true, css: {textAlign:'center'}},
{title: "Traffic Volume", field: "column_bytes", sortable: true,css: {textAlign:'right'}},
{title: "Packets", field: "column_packets", sortable: true, css: {textAlign:'right'}},
{title: "Flows", field: "column_flows", sortable: true, css: {textAlign:'right'}}
]
});
}
};
// this is the entry point for the navigation that starts at hosts
var populateHostTopAppsTable = function(host){
emptyAppsBreadCrumb();
$("#bc-apps").append('<li>Protocols spoken by ]] print(ntop.getResolvedAddress(host)) print [[</li>');
hideAll("app-talkers");
hideAll("peers-by-app");
showOne('historical-interface-apps', 'historical-interface-top-apps-table');
if ($('#historical-interface-top-apps-table').attr("loaded") != 1) {
$('#historical-interface-top-apps-table').attr("loaded", 1);
$('#historical-interface-top-apps-table').attr("host", host);
$('#historical-interface-top-apps-table').datatable({
title: "",]]
print("url: '"..ntop.getHttpPrefix().."/lua/get_historical_data.lua?stats_type=top_applications"..top_apps_url_params.."&peer1=' + host,")
if preference ~= "" then print ('perPage: '..preference.. ",") end
-- Automatic default sorted. NB: the column must be exists.
print ('sort: [ ["'..sort_column..'","'..sort_order..'"] ],')
print [[
post: {totalRows: function(){ return $('#historical-interface-top-apps-table').attr("total_rows");} },
showFilter: true,
showPagination: true,
tableCallback: function(){$('#historical-interface-top-apps-table').attr("total_rows", this.options.totalRows);},
rowCallback: function(row){
var proto_id_td = $("td:eq(0)", row[0]);
var proto_label_td = $("td:eq(1)", row[0]);
var proto_id = proto_id_td.text();
var proto_label = proto_label_td.text();
proto_label_td.append('&nbsp;<a onclick="$(\'#historical-interface-top-apps-table\').attr(\'proto\', \'' + proto_label + '\');populatePeersPerHostByApplication(\'' + host +'\',\'' + proto_id +'\');"><i class="fa fa-exchange" title="Hosts talking ' + proto_id + ' with ' + host + '"></i></a>');
return row;
},
columns:
[
{title: "Protocol id", field: "column_application", hidden: true},
{title: "Application", field: "column_label", sortable: false},
{title: "Avg. Flow Duration", field: "column_avg_flow_duration", sortable: true, css: {textAlign:'center'}},
{title: "Traffic Volume", field: "column_bytes", sortable: true, css: {textAlign:'right'}},
{title: "Packets", field: "column_packets", sortable: true, css: {textAlign:'right'}},
{title: "Flows", field: "column_flows", sortable: true, css: {textAlign:'right'}}
]
});
}
};
/*
This event is triggered every time the user focuses the "Protocols" tab.
The Protocols tab can be focused from two different pages:
- from the historical interface chart (e.g., http://localhost:3000/lua/if_stats.lua?if_name=en4&page=historical), and;
- from the historical host chart (e.g., http://localhost:3000/lua/host_details.lua?ifname=0&host=192.168.2.130&page=historical)
Depending on the page that triggers the event, there is a slightly different behavior
of the ajax navigation.
Historical interface chart
==========================
The navigation follows this path:
0. Interface en4 (populateInterfaceTopAppsTable)
1. Talkers speaking SSL (populateAppTopTalkersTable)
2. Talkers speaking SSL with 192.168.x.x (populatePeersPerHostByApplication)
The entry point is at 0. and then it is possible to go back and forth.
Historical host chart
========================
The navigation follows this path:
0. Protocols spoken by 192.168.y.y (populateHostTopAppsTable)
1. Talkers speaking SSL with 192.168.y.y (populatePeersPerHostByApplication)
Code Re-Use
========================
Interface function 2. and host function 1. are the same function that
is used in two different contex. There is a breadcrumb function that
adapts the breadcrumb depending on the page.
*/
$('a[href="#historical-top-apps"]').on('shown.bs.tab', function (e) {
if ($('a[href="#historical-top-apps"]').attr("loaded") == 1){
// do nothing if the tabs have already been computed and populated
return;
}
var target = $(e.target).attr("href"); // activated tab
var root = $("#bc-apps").attr("root");
if (root === "interface"){
populateInterfaceTopAppsTable();
} else if (root === "host"){
populateHostTopAppsTable(']] print(host) print[[');
}
// Finally set a loaded flag for the current tab
$('a[href="#historical-top-apps"]').attr("loaded", 1);
});
</script>
]]