mirror of
https://github.com/ntop/ntopng.git
synced 2026-07-10 00:14:24 +00:00
246 lines
8.7 KiB
Lua
246 lines
8.7 KiB
Lua
--
|
|
-- (C) 2013-26 - ntop.org
|
|
--
|
|
local dirs = ntop.getDirs()
|
|
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path
|
|
|
|
require "lua_utils_gui"
|
|
|
|
local flow_data = {}
|
|
local callback_utils = require "callback_utils"
|
|
local flow_data_preset = require "flow_data_preset"
|
|
local trace_stats = false
|
|
local flow_data_historical = nil
|
|
local node_key_id_separator = ";"
|
|
|
|
if ntop.isEnterpriseL() then
|
|
package.path = dirs.installdir .. "/scripts/lua/pro/modules/?.lua;" ..
|
|
package.path
|
|
flow_data_historical = require "flow_data_historical"
|
|
end
|
|
|
|
-- ###################################################
|
|
|
|
-- @brief Given a list of columns and a flow, create a unique key to pair the values,
|
|
-- using the columns, basically simulating a group by
|
|
-- @param data List, generate by formatEmptyStats
|
|
-- @return a unique key composed by the elements requested
|
|
local function formatKey(data, query_info)
|
|
local key = ""
|
|
local separator = "|"
|
|
-- Iterate all the select and create a unique ID, so if multiple queries for example
|
|
-- have the same result, merge all these results
|
|
for _, select_info in pairs(query_info.select_query) do
|
|
if select_info.is_key then
|
|
local node_key = nil
|
|
local value = nil
|
|
-- Find the info inside the preformatted empty data
|
|
if (select_info.key) and (data[select_info.key]) then
|
|
node_key = select_info.key
|
|
value = data[select_info.key]
|
|
elseif (select_info.rename) and (data[select_info.rename]) then
|
|
node_key = select_info.rename
|
|
value = data[select_info.rename]
|
|
end
|
|
|
|
if select_info.depends_on then
|
|
value = string.format("%s_%s", (data[select_info.depends_on] or select_info.depends_on), value)
|
|
end
|
|
|
|
if value then
|
|
key = string.format("%s%s%s%s%s", key, node_key, node_key_id_separator, value, separator)
|
|
end
|
|
end
|
|
end
|
|
|
|
key = key:sub(1, -2)
|
|
|
|
return key
|
|
end
|
|
|
|
-- ###################################################
|
|
|
|
local function formatEmptyStats(flow, query_info)
|
|
local empty_stats = {}
|
|
|
|
-- IMPORTANT: Here the "duplicated" values are removed, if requested,
|
|
-- see remove_if_equal_to; means that if the value is equal to the given one
|
|
-- then that value is removed from the statistics, for example the ASN and PEER ASN
|
|
-- must be different, otherwise there is no use in the PEER ASN and it has to be removed
|
|
-- from the returned flow
|
|
for _, select_info in pairs(query_info.select_query or {}) do
|
|
local key
|
|
if (select_info.rename) and (flow[select_info.rename]) then
|
|
key = select_info.rename
|
|
empty_stats[key] = flow[key]
|
|
else
|
|
key = select_info.key
|
|
empty_stats[key] = flow[key]
|
|
end
|
|
|
|
-- In case of the values that needs to be added (e.g. Bytes)
|
|
-- Start from 0, otherwise the same value maybe be added multiple times
|
|
if not select_info.is_key then
|
|
empty_stats[key] = 0
|
|
end
|
|
end
|
|
|
|
-- Now remove the remove_if_equal_to
|
|
for _, select_info in pairs(query_info.select_query or {}) do
|
|
local condition = select_info.remove_if_equal_to
|
|
if condition then
|
|
local key = nil
|
|
if (select_info.rename) then
|
|
key = select_info.rename
|
|
else
|
|
key = select_info.key
|
|
end
|
|
-- The condition is a table, multiple possibilities are available
|
|
for _, tmp in pairs(condition) do
|
|
-- Two possibilities, condition is a value or condition is a key of the data
|
|
if (tostring(empty_stats[key]) == tostring(tmp)) or (tostring(empty_stats[key]) == tostring(empty_stats[tmp])) then
|
|
empty_stats[key] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return empty_stats
|
|
end
|
|
|
|
-- ###################################################
|
|
|
|
-- @brief Given a list of columns and a flow, create a unique key using the columns
|
|
-- @param columns List, as key an id and as value a boolean, telling if the element
|
|
-- has to be used as an id or not (e.g. bytes_sent are NOT ids)
|
|
-- @param invert_direction Boolean, true if traffic directions needs to be inverted
|
|
-- @param flow List, containing all the elements
|
|
-- @param current_element List, containing the statistics previously collected, needs to be updated
|
|
-- @return an updated list of stats, contained in current_element
|
|
local function updateStats(stats, flow, query_info)
|
|
-- Iterate the requested fields
|
|
for _, select_info in pairs(query_info.select_query or {}) do
|
|
if (not select_info.is_key) then -- Not a key, so a value to be updated (e.g. bytes)
|
|
local key = nil
|
|
if (select_info.rename) then
|
|
key = select_info.rename
|
|
else
|
|
key = select_info.key
|
|
end
|
|
|
|
stats[key] = tonumber(stats[key]) + (tonumber(flow[key]) or 0)
|
|
end
|
|
end
|
|
|
|
return stats
|
|
end
|
|
|
|
-- ###################################################
|
|
|
|
function flow_data.getStats(queries)
|
|
local results = {}
|
|
|
|
for _, query_info in pairs(queries or {}) do
|
|
-- Function used to, given a flow, merge all the same data togheter
|
|
local function formatData(_, flow)
|
|
-- Create an empty table, composed only by key values then create a unique key, where only
|
|
-- flows with the same exact requested data are going to have the same key
|
|
-- e.g. key = 1.1.1.1|2222|
|
|
-- table = ip: 1.1.1.1
|
|
-- asn: 2222
|
|
-- bytes_sent: 0
|
|
-- bytes_rcvd: 0
|
|
local empty = formatEmptyStats(flow, query_info)
|
|
if not empty then goto skip_flow end
|
|
local key = formatKey(empty, query_info)
|
|
if not results[key] then -- Entry still not created
|
|
results[key] = empty
|
|
end
|
|
|
|
-- Now update the data (e.g. bytes_sent and bytes_rcvd)
|
|
results[key] = updateStats(results[key], flow, query_info)
|
|
::skip_flow::
|
|
end
|
|
|
|
local query_result = {}
|
|
if flow_data_historical then
|
|
query_result = flow_data_historical.retrieveFlowData(query_info)
|
|
end
|
|
|
|
for _, flow in pairs(query_result or {}) do formatData(_, flow) end
|
|
end
|
|
|
|
-- Now we have equal table for live and historical, so now format the data and run the checks
|
|
return results
|
|
end
|
|
|
|
-- ###################################################
|
|
|
|
-- @brief Given a list of columns and a flow, create a unique key using the columns
|
|
-- @param columns Array of stats to format
|
|
-- @return an Array of formatted elements
|
|
function flow_data.formatStats(stats_to_format, queries)
|
|
local formatted_stats = {}
|
|
local formatters_linkers = {}
|
|
|
|
-- Get the formatters and linkers
|
|
for _, info in pairs(queries or {}) do
|
|
local select_query = info.select_query or {}
|
|
for _, select_info in pairs(select_query) do
|
|
local key = (select_info.rename or select_info.key)
|
|
formatters_linkers[key] = {
|
|
linker = select_info.linker,
|
|
formatter = select_info.formatter,
|
|
depends_on = select_info.depends_on
|
|
}
|
|
end
|
|
end
|
|
|
|
-- For each element, see if there is a defined formatter in
|
|
-- scripts/lua/modules/flow_data_preset.lua:format_functions
|
|
-- if there is, split the value in { id = id, name = formattedName }
|
|
-- otherwise keep that as it is
|
|
for _, values in pairs(stats_to_format) do
|
|
local formatted_element = {}
|
|
for key, value in pairs(values or {}) do
|
|
-- Format the data
|
|
if formatters_linkers[key] and type(value) == "string" then
|
|
local label = value
|
|
local url_link = nil
|
|
if formatters_linkers[key].depends_on then
|
|
local depend_value = (values[formatters_linkers[key].depends_on] or formatters_linkers[key].depends_on)
|
|
if depend_value then
|
|
label = formatters_linkers[key]["formatter"](depend_value, value)
|
|
url_link = formatters_linkers[key]["linker"](depend_value, value)
|
|
end
|
|
else
|
|
label = formatters_linkers[key]["formatter"](value)
|
|
url_link = formatters_linkers[key]["linker"](value)
|
|
end
|
|
|
|
formatted_element[key] = {
|
|
id = value,
|
|
name = label,
|
|
url = url_link
|
|
}
|
|
else
|
|
formatted_element[key] = value
|
|
end
|
|
end
|
|
|
|
formatted_stats[#formatted_stats + 1] = formatted_element
|
|
end
|
|
|
|
return formatted_stats
|
|
end
|
|
|
|
-- ###################################################
|
|
|
|
function flow_data.getOperators()
|
|
local flowfilter_utils = require "flowfilter_utils"
|
|
return flowfilter_utils.flowfilter_operators_sql
|
|
end
|
|
|
|
-- ###################################################
|
|
|
|
return flow_data
|