ntopng/scripts/lua/hosts_interaction.lua
Arianna Avanzini fddd7a0315 lua/hosts_interaction.lua: show most relevant hosts first
This commit lets the hosts interaction map show most relevant
hosts first. This plays along well with the limit introduced
with commit 7d3bbc1. This addresses issue #42.
2015-07-11 18:26:46 +02:00

580 lines
15 KiB
Lua

--
-- (C) 2013-15 - ntop.org
--
dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path
require "lua_utils"
-- host_info = url2hostinfo(_GET)
if(host_ip == nil) then
host_info = url2hostinfo(_GET)
else
-- print("host_ip:"..host_ip.."<br>")
host_info = {}
host_info = hostkey2hostinfo(host_ip)
end
if(mode == nil) then
mode = _GET["mode"]
end
if(host_name == nil) then
host_name = _GET["name"]
end
if(mode ~= "embed") then
sendHTTPHeader('text/html; charset=iso-8859-1')
ntop.dumpFile(dirs.installdir .. "/httpdocs/inc/header.inc")
active_page = "hosts"
dofile(dirs.installdir .. "/scripts/lua/inc/menu.lua")
end
num_top_hosts = 10
if(host_info["host"] ~= nil) then
num = 1
else
interface.select(ifname)
hosts_stats = interface.getHostsInfo()
num = 0
for key, value in pairs(hosts_stats) do
num = num + 1
end
end
if(num > 0) then
if(mode ~= "embed") then
if(host_info["host"] == nil) then
print("<hr><h2>Top Hosts Interaction</H2>")
else
name = host_name
if(name == nil) then name = host_info["host"] end
print("<hr><h2>"..name.." Interactions</H2><i class=\"fa fa-chevron-left fa-lg\"></i><small><A onClick=\"javascript:history.back()\">Back</A></small>")
end
end
print [[
<style>
svg {
font: 10px sans-serif;
}
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
sup, sub {
line-height: 0;
}
q:before, blockquote:before {
content: "?";
}
q:after, blockquote:after {
content: "?";
}
blockquote:before {
position: absolute;
left: 2em;
}
blockquote:after {
position: absolute;
}
</style>
<style>
#chart {
height:
]]
if(mode ~= "embed") then
print("600")
elseif(interface.getNumAggregatedHosts() > 0) then
print("400")
else
print("300")
end
print [[
px;
}
.node rect {
cursor: move;
fill-opacity: .9;
shape-rendering: crispEdges;
}
.node text {
pointer-events: none;
text-shadow: 0 1px 0 #fff;
}
.link {
fill: none;
stroke: #000;
stroke-opacity: .2;
}
.link:hover {
stroke-opacity: .5;
}
circle.node-dot {
fill: DarkSlateGray;
stroke: SlateGray;
stroke-width: 1px;
}
path.link {
fill: none;
stroke: SlateGray;
stroke-width: 1.5px;
}
marker#defaultMarker {
fill: SlateGray;
}
path.link.defaultMarker {
stroke: SlateGray;
}
circle {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
}
text {
pointer-events: none;
}
text.shadow {
stroke: #fff;
stroke-width: 3px;
stroke-opacity: .8;
}
</style><style>path.link.proposer{stroke:red;}
marker#bus{fill:blue;}
marker#manual{fill:red;}
path.link.direct{stroke:green; }
path.link.bus{stroke:blue;}
path.link.manual{stroke:red;stroke-dasharray: 0, 2 1;} </style><script>
/**
* do the force vizualization
* @param {string} divName name of the div to hold the tree
* @param {object} inData the source data
*/
function doTheTreeViz(divName, inData) {
// tweak the options
var options = $.extend({
stackHeight : 12,
radius : 5,
fontSize : 12,
labelFontSize : 8,
nodeLabel : null,
markerWidth : 0,
markerHeight : 0,
width : $(divName).outerWidth(),
gap : 1.5,
nodeResize : "",
linkDistance : 30,
charge : -120,
styleColumn : null,
styles : null,
linkName : null,
height : $(divName).outerHeight()
}, inData.d3.options);
// set up the parameters
options.gap = options.gap * options.radius;
var width = options.width;
var height = options.height;
var data = inData.d3.data;
var nodes = data.nodes;
var links = data.links;
var color = d3.scale.category10();
color["local"] = "#aec7e8";
color["remote"] = "#bcbd22";
color["sun"] = "#fd8d3c";
color["aggregation"] = "#008d3c";
var force = d3.layout.force().nodes(nodes).links(links).size([width, height]).linkDistance(options.linkDistance).charge(options.charge).on("tick", tick).start();
var main = d3.select(divName).append("svg:svg").attr("width", width).attr("height", height);
var svg = main.append('svg:g')
.call(d3.behavior.zoom().on("zoom", rescale))
.on("dblclick.zoom", null);
function rescale() {
trans=d3.event.translate;
scale=d3.event.scale;
svg.attr("transform",
"translate(" + trans + ")"
+ " scale(" + scale + ")");
}
// get list of unique values in stylecolumn
linkStyles = [];
if (options.styleColumn) {
var x;
for (var i = 0; i < links.length; i++) {
if (linkStyles.indexOf( x = links[i][options.styleColumn].toLowerCase()) == -1)
linkStyles.push(x);
}
} else
linkStyles[0] = "defaultMarker";
// do we need a marker?
if (options.markerWidth) {
svg.append("svg:defs").selectAll("marker").data(linkStyles).enter().append("svg:marker").attr("id", String).attr("viewBox", "0 -5 10 10").attr("refX", 15).attr("refY", -1.5).attr("markerWidth", options.markerWidth).attr("markerHeight", options.markerHeight).attr("orient", "auto").append("svg:path").attr("d", "M0,-5L10,0L0,5");
}
var path = svg.append("svg:g").selectAll("path").data(force.links()).enter().append("svg:path").attr("class", function(d) {
return "link " + (options.styleColumn ? d[options.styleColumn].toLowerCase() : linkStyles[0]);
}).attr("marker-end", function(d) {
return "url(#" + (options.styleColumn ? d[options.styleColumn].toLowerCase() : linkStyles[0] ) + ")";
});
var circle = svg.append("svg:g").selectAll("circle")
.data(force.nodes())
.enter()
.append("svg:circle")
.attr("r", function(d) {
return getRadius(d);
})
.style("fill", function(d) {
return color[d.group];
})
.call(force.drag)
//.on("mousedown",
// function(d) {
// disable zoom
// svg.call(d3.behavior.zoom().on("zoom"), null);
// })
//.on("mouseup",
// function(d) {
// enable zoom
// svg.call(d3.behavior.zoom().on("zoom"), rescale);
// })
;
if (options.nodeLabel) {
circle.append("title").html(function(d) {
return d[options.nodeLabel];
});
}
circle.on("dblclick", function(d) { if(d.link.length > 0) { window.location.href = d.link; } } );
if (options.linkName) {
path.append("title").text(function(d) {
return d[options.linkName];
});
}
var text = svg.append("svg:g").selectAll("g").data(force.nodes()).enter().append("svg:g");
// A copy of the text with a thick white stroke for legibility.
text.append("svg:text").attr("x", options.labelFontSize).attr("y", ".31em").attr("class", "shadow").text(function(d) {
return d[options.nodeLabel];
});
text.append("svg:text").attr("x", options.labelFontSize).attr("y", ".31em").text(function(d) {
return d[options.nodeLabel];
});
function getRadius(d) {
return options.radius * (options.nodeResize ? Math.sqrt(d[options.nodeResize]) / Math.PI : 1);
}
// Use elliptical arc path segments to doubly-encode directionality.
function tick() {
path.attr("d", function(d) {
var dx = d.target.x - d.source.x, dy = d.target.y - d.source.y, dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});
circle.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
text.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
}
</script><script>
window['ntopData'] = {"d3":{"options":
{"radius":"16","fontSize":"30","labelFontSize":"30","charge":"-2000","nodeResize":"count","nodeLabel":"label","markerHeight":"6","markerWidth":"6","styleColumn":"styleColumn","linkName":"group"},
"data":{"links":[
]]
-- Nodes
interface.select(ifname)
if(host_info["host"] == nil) then
hosts_stats = getTopInterfaceHosts(num_top_hosts, true)
else
hosts_stats = {}
hosts_stats[host_info["host"]] = interface.getHostInfo(host_info["host"],host_info["vlan"])
end
hosts_ctx = {}
hosts_id = {}
ids = {}
num = 0
links = 0
local host
local max_num_hosts = 50
local host_idx = 0
local num_contacts
-- Enumerate contacts of each host
for key, values in pairs(hosts_stats) do
host = interface.getHostInfo(key)
if (host ~= nil) then
num_contacts = 0
if (host["contacts"]["client"] ~= nil) then
for k,v in pairs(host["contacts"]["client"]) do
num_contacts = num_contacts + 1
end
end
if (host["contacts"]["server"] ~= nil) then
for k,v in pairs(host["contacts"]["server"]) do
num_contacts = num_contacts + 1
end
end
end
hosts_ctx[key] = num_contacts
end
for key, values in pairsByValues(hosts_ctx, rev) do
host = interface.getHostInfo(key)
if(host ~= nil) then
-- init host
if(hosts_id[key] == nil) then
hosts_id[key] = { }
hosts_id[key]['count'] = 0
hosts_id[key]['id'] = num
ids[num] = key
key_id = num
num = num + 1
else
key_id = hosts_id[key]['id']
end
-- client contacts
if(host["contacts"]["client"] ~= nil) then
for k,v in pairs(host["contacts"]["client"]) do
if(hosts_id[k] == nil) then
hosts_id[k] = { }
hosts_id[k]['count'] = 0
hosts_id[k]['id'] = num
ids[num] = k
peer_id = num
num = num + 1
else
peer_id = hosts_id[k]['id']
end
hosts_id[key]['count'] = hosts_id[key]['count'] + v
if(links > 0) then print(",") end
print('\n{"source":'..key_id..',"target":'..peer_id..',"depth":6,"count":'..v..',"styleColumn":"client","linkName":""}')
links = links + 1
end
end
-- server contacts
if(host["contacts"]["server"] ~= nil) then
for k,v in pairs(host["contacts"]["server"]) do
if(hosts_id[k] == nil) then
hosts_id[k] = { }
hosts_id[k]['count'] = 0
hosts_id[k]['id'] = num
ids[num] = k
peer_id = num
num = num + 1
else
peer_id = hosts_id[k]['id']
end
hosts_id[key]['count'] = hosts_id[key]['count'] + v
if(links > 0) then print(",") end
print('\n{"source":'..key_id..',"target":'..peer_id..',"depth":6,"count":'..v..',"styleColumn":"server"}')
links = links + 1
end
end
end
host_idx = host_idx + 1
if (host_idx > max_num_hosts) then
break
end
end
aggregation_ids = {}
if(host_info["host"] ~= nil) then
aggregations = interface.getAggregationsForHost(host_info["host"])
else
aggregations = {}
end
for name,num_contacts in pairs(aggregations) do
aggregation_ids[name] = num
hosts_id[name] = { }
hosts_id[name]['count'] = num_contacts
hosts_id[name]['id'] = num
ids[num] = name
if(links > 0) then print(",") end
print('\n{"source":'..num..',"target": 0,"depth":6,"count":'..num_contacts..',"styleColumn":"aggregation"}')
links = links + 1
num = num + 1
end
tot_hosts = num
print [[
],"nodes":[
]]
-- Nodes
min_size = 5
maxval = 0
for k,v in pairs(hosts_id) do
if(v['count'] > maxval) then maxval = v['count'] end
end
num = 0
for i=0,tot_hosts-1 do
k = ids[i]
v = hosts_id[k]
k_info = hostkey2hostinfo(k)
target_host = interface.getHostInfo(k)
if(target_host ~= nil) then
name = target_host["name"]
if(name ~= nil) then
name = name
else
name = ntop.getResolvedAddress(k_info["host"])
target_host["name"] = name
end
if(target_host['localhost'] ~= nil) then label = "local" else label = "remote" end
else
name = k
if(aggregations[k] ~= nil) then label = "aggregation" else label = "remote" end
end
if ((host_info["host"] ~= nil) and (host_info["host"] == k_info["host"])) then label = "sun" end
-- f(name == k) then name = ntop.getResolvedAddress(k) end
if(name == nil) then name = k end
if(maxval == 0) then
tot = maxval
else
tot = math.floor(0.5+(v['count']*100)/maxval)
if(tot < min_size) then tot = min_size end
end
if(num > 0) then print(",") end
print('\n{"name":"'.. name ..'","count":'.. tot ..',"group":"' .. label .. '","linkCount": '.. tot .. ',"label":"'.. name..'"')
if(target_host ~= nil) then
-- Host still in memory
if((host_info["host"] == nil) or (k_info["host"] ~= host_info["host"])) then
print(', "link": "'..ntop.getHttpPrefix()..'/lua/hosts_interaction.lua?"}')
else
print(', "link": "'..ntop.getHttpPrefix()..'/lua/host_details.lua?host='.. k.. '"}')
end
else
-- print('->>'..k..'<<-\n')
if(aggregations[k] ~= nil) then
print(', "link": "'..ntop.getHttpPrefix()..'/lua/aggregated_host_details.lua?host='.. k .. '"}')
else
-- Host purged ?
print(', "link": ""}')
end
end
num = num + 1
end
if ((num == 0) and (host_info["host"] ~= nil)) then
tot = 1
label = ""
name = host_info["host"]
print('\n{"name":"'.. name ..'","count":'.. tot ..',"group":"' .. label .. '","linkCount": '.. tot .. ',"label":"'.. name..'", "link": "'..ntop.getHttpPrefix()..'/lua/host_details.lua?host='.. hostinfo2hostkey(host_info).. '"}')
end
print [[
]
}}};
</script>
<div id="chart"></div>
<p>&nbsp;<p><small><b>NOTE</b></small>
<ol>
]]
if(host_info["host"] ~= nil) then
print('<li><small>This map is centered on host <font color="#fd8d3c">'.. hostinfo2hostkey(host_info))
if(host_name ~= nil) then print('('.. host_name .. ')') end
print('</font>. Clicking on this host you will visualize its details.</small></li>\n')
else
print('<li><small>This map depicts the interactions of the top '.. num_top_hosts .. ' hosts.</small></li>\n')
end
print('<li><small>Color map: <font color=#aec7e8><b>local</b></font>, <font color=#bcbd22><b>remote</b></font>, <font color=#008d3c><b>aggregation</b></font>, <font color=#fd8d3c><b>focus</b></font> host.</small></li>\n')
print [[
<li> <small>Click is enabled only for hosts that have not been purged from memory.</small></li>
</ol>
<!-- http://ramblings.mcpher.com/Home/excelquirks/d3 -->
<script type="text/javascript">
doTheTreeViz("#chart", ntopData);
</script>
]]
else
print("<div class=\"alert alert-danger\"><img src=".. ntop.getHttpPrefix() .. "/img/warning.png> No results found</div>")
end
if(mode ~= "embed") then
dofile(dirs.installdir .. "/scripts/lua/inc/footer.lua")
end