mirror of
https://github.com/ntop/ntopng.git
synced 2026-04-29 23:49:33 +00:00
ARP Map (#2540)
* arp matrix graph and fix * bug fix * graph fix * bug fix * tests on arp graph * merged heatmap.js and map.js * graph dinamic resize * minor fix * graph dinamic width * graph performance improvement * clean code * fix manual refresh * message for empty graph * clean code * more clean * update arpMap and host details * migrate to v4.min version of d3.js
This commit is contained in:
parent
94b143c8a3
commit
1d9b35be42
13 changed files with 1247 additions and 224 deletions
|
|
@ -1,252 +1,199 @@
|
|||
--
|
||||
-- (C) 2013-19 - ntop.org
|
||||
-- (C) 2018 - ntop.org
|
||||
--
|
||||
|
||||
local dirs = ntop.getDirs()
|
||||
dirs = ntop.getDirs()
|
||||
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path
|
||||
require "lua_utils"
|
||||
if((dirs.scriptdir ~= nil) and (dirs.scriptdir ~= "")) then package.path = dirs.scriptdir .. "/lua/modules/?.lua;" .. package.path end
|
||||
ignore_post_payload_parse = 1
|
||||
require "lua_utils"
|
||||
local json = require("dkjson")
|
||||
|
||||
local matrix = interface.getArpStatsMatrixInfo()
|
||||
|
||||
sendHTTPContentTypeHeader('application/json')
|
||||
--sendHTTPContentTypeHeader('text/html')
|
||||
|
||||
--g: the type og graph ( 1 -sigma graph, 2 -heb graph )
|
||||
--t: the type of data visualized (1-broadcast,2-replies,3-requests)
|
||||
--local g,t = _GET["g"], _GET["t"]
|
||||
--print(g..t)
|
||||
--MISSING VALIDATION!
|
||||
sendHTTPContentTypeHeader('Application/json')
|
||||
|
||||
|
||||
--========UTILS=======(but not currently used)==============================
|
||||
--[[
|
||||
--chack if inside "t" there is a mac named "name", if true return the index, nil otherwise
|
||||
local function containName(t,name)
|
||||
for i,v in pairs(t) do
|
||||
if v.labels == name then return i end
|
||||
--------------------------------------------------------------------------
|
||||
---------------------------------------------------------------------------
|
||||
local ga_module = {}
|
||||
|
||||
local request = {}
|
||||
local response = {}
|
||||
|
||||
--"suggestions_strings" must be a string array, and "card" must be created with create_card()
|
||||
local function fill_response(speech_text, display_text, expect_response, suggestions_strings, card)
|
||||
|
||||
if display_text == nil or display_text == "" then display_text = speech_text end
|
||||
if expect_response == nil then expect_response = true end
|
||||
|
||||
local mysuggestions = {}--MAX 10 (imposed by google)
|
||||
|
||||
if suggestions_strings then
|
||||
for i = 1, #suggestions_strings do
|
||||
table.insert( mysuggestions, {title = suggestions_strings[i]} )
|
||||
end
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
--split the string "s" with the "sep" separator
|
||||
local function split(s,sep)
|
||||
local sep, fields = sep, {}
|
||||
local pattern = string.format("([^%s]+)", sep)
|
||||
s:gsub(pattern, function(c) fields[#fields+1] = c end)
|
||||
return fields
|
||||
end
|
||||
local myitems = {}
|
||||
|
||||
function tableLength(t)
|
||||
local count = 0
|
||||
for _ in pairs(t) do count = count + 1 end
|
||||
return count
|
||||
end
|
||||
--]]
|
||||
--=======================================================================
|
||||
if card then
|
||||
--tprint(card)
|
||||
myitems = {
|
||||
{
|
||||
simpleResponse = {
|
||||
textToSpeech = speech_text,
|
||||
displayText = display_text
|
||||
}
|
||||
},
|
||||
{basicCard = card}
|
||||
}
|
||||
else
|
||||
myitems[1] = {
|
||||
simpleResponse = {
|
||||
textToSpeech = speech_text,
|
||||
displayText = display_text,
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local r = {}
|
||||
--if a context was created, consume it
|
||||
local mycontext = ga_module.getContext()
|
||||
if mycontext then
|
||||
|
||||
--[[ JSON SCHEMA for sigma.js graph
|
||||
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "chr1",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"label": "Bob",
|
||||
"size": 8.75
|
||||
},
|
||||
{
|
||||
"id": "chr10",
|
||||
"label": "Alice",
|
||||
"x": 3,
|
||||
"y": 1,
|
||||
"size": 14.75
|
||||
r = {
|
||||
fulfillmentText = display_text,
|
||||
payload = {
|
||||
google = {
|
||||
expectUserResponse = expect_response,
|
||||
richResponse = {
|
||||
items = myitems,
|
||||
suggestions = mysuggestions
|
||||
}
|
||||
}
|
||||
},
|
||||
outputContexts = mycontext
|
||||
}
|
||||
],
|
||||
"edges": [{
|
||||
"id": "1",
|
||||
"source": "chr1",
|
||||
"target": "chr10"
|
||||
}]
|
||||
|
||||
]]--
|
||||
ga_module.deleteContext()
|
||||
else
|
||||
r = {
|
||||
fulfillmentText = display_text,
|
||||
payload = {
|
||||
google = {
|
||||
expectUserResponse = expect_response,
|
||||
richResponse = {
|
||||
items = myitems,
|
||||
suggestions = mysuggestions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
--TODO: normalize pkt size (eg: size = (pkt_snt+rcv / tot_pkt_seen) * max_node_size ) ? )
|
||||
--but maybe is not necessary, sigma.js already make an average
|
||||
end
|
||||
|
||||
--the value of a t_nodes element is the size (not normalized) of the node (size = #request_pkt_sent )
|
||||
|
||||
--if "broadcast" is false all the broadcast requests will be ignored
|
||||
|
||||
--AT THE MOMENT THE CREATED GRAPH REPRESENT ONLY THE ARP REQUESTS (not replies)
|
||||
local function createNodesAndEdges(matrix, broadcast)
|
||||
local t_nodes = {}
|
||||
local x,y = 10,10
|
||||
local num, e_id = 0, 0
|
||||
local t = { nodes = {}, edges = {} }
|
||||
local source, target
|
||||
|
||||
for _, m_elem in ipairs(matrix) do
|
||||
for src_mac, s_elem in pairs(m_elem)do
|
||||
for dst_mac, stats in pairs(s_elem) do
|
||||
|
||||
--add dst_mac node and edges if broadcast is true or the pkt isn't broadcast
|
||||
if broadcast or (dst_mac ~= "FF:FF:FF:FF:FF:FF") then
|
||||
|
||||
if t_nodes[src_mac] then
|
||||
t_nodes[src_mac] = t_nodes[src_mac] + stats["src2dst.requests"]
|
||||
else
|
||||
t_nodes[src_mac] = stats["src2dst.requests"]
|
||||
end
|
||||
|
||||
if t_nodes[dst_mac] then
|
||||
t_nodes[dst_mac] = t_nodes[dst_mac] + stats["dst2src.requests"]
|
||||
else
|
||||
t_nodes[dst_mac] = stats["dst2src.requests"]
|
||||
end
|
||||
|
||||
if stats["src2dst.requests"] > 0 then
|
||||
table.insert( t.edges,
|
||||
{ id = e_id,
|
||||
source = src_mac,
|
||||
target = dst_mac,
|
||||
size = stats["src2dst.requests"],
|
||||
label = stats["src2dst.requests"].." req snt"
|
||||
}
|
||||
)
|
||||
end
|
||||
e_id = e_id + 1
|
||||
|
||||
if stats["dst2src.requests"] > 0 then
|
||||
table.insert( t.edges,
|
||||
{ id = e_id,
|
||||
source = dst_mac,
|
||||
target = src_mac,
|
||||
size = stats["dst2src.requests"],
|
||||
label = stats["dst2src.requests"].." req snt"
|
||||
}
|
||||
)
|
||||
end
|
||||
e_id = e_id + 1
|
||||
|
||||
end--end if
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for i,v in pairs(t_nodes) do
|
||||
x = math.floor(math.random(0,500))
|
||||
y = math.floor(math.random(0,350))
|
||||
table.insert( t.nodes, { id = i, label = i, x = x , y = y, size = v })
|
||||
end
|
||||
|
||||
return t
|
||||
return json.encode(r)
|
||||
end
|
||||
|
||||
----WIP--------WIP--------WIP--------WIP--------WIP--------WIP--------WIP--------WIP--------
|
||||
--------------------------------------------------------------------------------------------
|
||||
----------------------------Hierarchical Edge Bundling--------------------------------------
|
||||
--------------------------------------------------------------------------------------------
|
||||
|
||||
--PROBLEMA: le "foglie", cioè l'ultima parola dopo il punto. devono essere uniche
|
||||
-- ma se A invia una req a B e poi B invia una req ad A, ciò viene meno
|
||||
--IDEA: creo una finta gerarchia in base alle comunicazioni:
|
||||
--se A invia a B, allora il nome di B diventa "A.B". e così per ogni comunicazione
|
||||
--TODO: cards allow many things (like buttons), more info ---> [ https://dialogflow.com/docs/rich-messages#card ]
|
||||
function ga_module.create_card(card_title, card_url_image, accessibility_image_text, button_title, button_open_url_action )
|
||||
|
||||
--TODO: unire i 3 cici dove possibile
|
||||
local function createHierarchyAndImport(matrix,broadcast)
|
||||
local t_names = {}
|
||||
local tbl = {}
|
||||
local pkt_num = 0
|
||||
local myButton = {}
|
||||
myButton = {
|
||||
{
|
||||
title = button_title,
|
||||
openUrlAction = { url = button_open_url_action}
|
||||
}
|
||||
}
|
||||
|
||||
--creo i nodi del grafo
|
||||
for _, m_elem in ipairs(matrix) do
|
||||
for src_mac, s_elem in pairs(m_elem)do
|
||||
for dst_mac, stats in pairs(s_elem) do
|
||||
local myCard = {}
|
||||
myCard = {
|
||||
title = card_title,
|
||||
image = { url = card_url_image, accessibilityText = accessibility_image_text },
|
||||
buttons = myButton
|
||||
}
|
||||
|
||||
--i due punti separatori dei byte del mac danno noia allo script js, metto il trattino
|
||||
src_mac = string.gsub(src_mac, ":", "-")
|
||||
dst_mac = string.gsub(dst_mac, ":", "-")
|
||||
return myCard
|
||||
end
|
||||
|
||||
t_names[src_mac] = {name = src_mac, imports = {} }
|
||||
--To set an arbitrary context (and overwrite the old one) call setContext()
|
||||
--To cancel an existing/outgoing context ---> set the lifespan to 0
|
||||
--For complex structures use as many prefs as there are fields to save
|
||||
function ga_module.setContext(name, lifespan, parameter) --TODO: support for more parameters
|
||||
|
||||
if dst_mac ~= "FF-FF-FF-FF-FF-FF" then
|
||||
t_names[dst_mac] = {name = dst_mac, imports = {} }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--ho la mappa dei mac dentro t_names, ora compongo la gerarchia fittizia
|
||||
for _, m_elem in ipairs(matrix) do
|
||||
for src_mac, s_elem in pairs(m_elem)do
|
||||
for dst_mac, stats in pairs(s_elem) do
|
||||
|
||||
src_mac = string.gsub(src_mac, ":", "-")
|
||||
dst_mac = string.gsub(dst_mac, ":", "-")
|
||||
|
||||
pkt_num = stats["src2dst.requests"]
|
||||
if pkt_num > 0 then
|
||||
|
||||
if dst_mac ~= "FF-FF-FF-FF-FF-FF" then
|
||||
t_names[dst_mac].name = src_mac.."."..t_names[dst_mac].name
|
||||
end
|
||||
end
|
||||
|
||||
pkt_num = stats["dst2src.requests"]
|
||||
if pkt_num > 0 then
|
||||
t_names[src_mac].name = dst_mac.."."..t_names[src_mac].name
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--ho i nomi "lunghi", aggiungo gli import
|
||||
for _, m_elem in ipairs(matrix) do
|
||||
for src_mac, s_elem in pairs(m_elem)do
|
||||
for dst_mac, stats in pairs(s_elem) do
|
||||
|
||||
src_mac = string.gsub(src_mac, ":", "-")
|
||||
dst_mac = string.gsub(dst_mac, ":", "-")
|
||||
if name then
|
||||
ntop.setCache("context_name", name, 60 * 20) --(max context lifespan: 20 min)
|
||||
end
|
||||
if lifespan then
|
||||
ntop.setCache("context_lifespan", tostring(lifespan), 60 * 20)
|
||||
end
|
||||
if parameter then
|
||||
ntop.setCache("context_param", parameter, 60*20)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
pkt_num = stats["src2dst.requests"]
|
||||
if pkt_num > 0 then
|
||||
if dst_mac ~= "FF-FF-FF-FF-FF-FF" and (t_names[dst_mac].name ~= t_names[src_mac].name) then
|
||||
|
||||
table.insert( t_names[src_mac].imports, t_names[dst_mac].name )
|
||||
end
|
||||
end
|
||||
|
||||
pkt_num = stats["dst2src.requests"]
|
||||
if pkt_num > 0 then
|
||||
|
||||
if t_names[dst_mac].name ~= t_names[src_mac].name then
|
||||
table.insert( t_names[dst_mac].imports, t_names[src_mac].name )
|
||||
end
|
||||
end
|
||||
function ga_module.deleteContext()
|
||||
ntop.delCache("context_name")
|
||||
ntop.delCache("context_lifespan")
|
||||
ntop.delCache("context_param")
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
function ga_module.getContext()
|
||||
|
||||
--genero la tabella pronta per divenire il file json
|
||||
for i,v in pairs(t_names) do
|
||||
--tprint(v)
|
||||
--"size" non viene preso in considerazione per lo spessore dell'arco
|
||||
table.insert(tbl, { name = v.name, size = math.floor(math.random(100,10000)), imports = v.imports } )
|
||||
end
|
||||
local name = ntop.getCache("context_name")
|
||||
if name == "" then return nil end
|
||||
|
||||
local lifespan = ntop.getCache("context_lifespan")
|
||||
|
||||
return tbl
|
||||
if lifespan == "" then lifespan = 2 end
|
||||
|
||||
local mycontext = {
|
||||
{
|
||||
name = name,
|
||||
lifespanCount = lifespan,
|
||||
parameters = {param = ntop.getCache("context_param") }
|
||||
}
|
||||
}
|
||||
|
||||
return mycontext
|
||||
end
|
||||
|
||||
function ga_module.send(speech_text, display_text, expect_response, suggestions_strings, card )
|
||||
|
||||
res = fill_response(speech_text, display_text,expect_response, suggestions_strings, card)
|
||||
print(res.."\n")
|
||||
|
||||
io.write("\n")
|
||||
io.write("NTOPNG RESPONSE\n")
|
||||
tprint(res)
|
||||
io.write("\n---------------------------------------------------------\n")
|
||||
|
||||
end
|
||||
|
||||
--print( json.encode( createNodesAndEdges(matrix, false), {indent = true} ) )
|
||||
|
||||
print( json.encode( createHierarchyAndImport(matrix, false), {indent = true} ) )
|
||||
function ga_module.receive()
|
||||
|
||||
--print( json.encode( matrix, {indent = true} ) )
|
||||
local info, pos, err = json.decode(_POST["payload"], 1, nil)--I assume only ONE outputContext
|
||||
|
||||
response["responseId"] = info.responseId
|
||||
response["queryText"] = info.queryResult.queryText
|
||||
if info.queryResult.parameters ~= nil then response["parameters"] = info.queryResult.parameters end
|
||||
if info.queryResult.outputContexts and info.queryResult.outputContexts[1].name then response["context"] = info.queryResult.outputContexts[1].name end
|
||||
---response["outputContext_name"] = info.queryResult.outputContexts[1].name
|
||||
--response["outputContext_parameters"] = info.queryResult.outputContexts[1].parameters.number
|
||||
response["intent_name"] = info.queryResult.intent.displayName
|
||||
response["session"] = info.session
|
||||
|
||||
ntop.setCache("session_id", info.session )
|
||||
|
||||
io.write("\n")
|
||||
io.write("DIALOGFLOW REQUEST")
|
||||
tprint(response)
|
||||
io.write("\n")
|
||||
|
||||
return response
|
||||
end
|
||||
|
||||
return ga_module
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue