ntopng/scripts/lua/modules/flow_data_historical.lua
2026-06-05 08:27:00 +00:00

206 lines
6.6 KiB
Lua

--
-- (C) 2013-26 - ntop.org
--
local dirs = ntop.getDirs()
package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path
local TABLE_NAME = "hourly_asn"
local flow_data_historical = {}
-- ###################################################
-- Helper used just to correctly format the Limit clause
local function buildLimitClause(query_info)
local limit_keys = query_info.limit_query or {}
local formatted_limit_query = "LIMIT "
if not limit_keys or (table.len(limit_keys) == 0) then
formatted_limit_query = ""
end
-- Multiple limit allowed only in Historical, just skip all except the first limit
if not isHistorical then
limit_keys = { limit_keys[1] }
end
for index, num_limit in pairs(limit_keys) do
formatted_limit_query = string.format("%s%s", formatted_limit_query, num_limit)
if index ~= #limit_keys then
-- Add the separator
formatted_limit_query = string.format("%s, ", formatted_limit_query)
end
end
return formatted_limit_query
end
-- ###################################################
-- Helper used just to correctly format the order by clause
local function buildOrderByClause(query_info)
local order_by_keys = query_info.order_by_query or {}
local formatted_order_by_query = "ORDER BY "
if not order_by_keys or (table.len(order_by_keys) == 0) then
formatted_order_by_query = ""
end
-- Multiple order by allowed only in Historical, just skip all except the first order by
if not isHistorical then
order_by_keys = { order_by_keys[1] }
end
-- ###### ORDER BY ######
for index, order_by_info in pairs(order_by_keys) do
formatted_order_by_query =
string.format("%s%s %s", formatted_order_by_query, order_by_info.key, tostring(order_by_info.value))
if index ~= #order_by_keys then
-- Add the separator
formatted_order_by_query = string.format("%s, ", formatted_order_by_query)
end
end
return formatted_order_by_query
end
-- ###################################################
-- Helper used just to correctly format the where clause
local function buildSelectClause(query_info)
local select_keys = query_info.select_query
local formatted_select_query = "SELECT "
-- ###### SELECT ######
for index, select_info in pairs(select_keys) do
formatted_select_query = string.format("%s%s", formatted_select_query, select_info.key)
if select_info.rename then
-- Add the AS if requested
formatted_select_query = string.format("%s AS %s", formatted_select_query, select_info.rename)
end
if index ~= #select_keys then
-- Add the separator
formatted_select_query = string.format("%s, ", formatted_select_query)
end
end
return formatted_select_query
end
-- ###################################################
-- Helper used just to correctly format the group by clause
local function buildGroupByClause(query_info)
local select_keys = query_info.select_query
local formatted_group_by = "GROUP BY "
-- ###### GROUP BY ######
for index, select_keys_info in pairs(select_keys) do
if select_keys_info.is_key then
formatted_group_by =
string.format("%s%s", formatted_group_by, (select_keys_info.rename or select_keys_info.key))
-- Add the separator
formatted_group_by = string.format("%s, ", formatted_group_by)
end
end
formatted_group_by = formatted_group_by:sub(1, -3)
return formatted_group_by
end
-- ###################################################
-- Helper used just to correctly format the where clause
local function buildWhereClause(query_info)
local where_keys = query_info.where_query or {}
local formatted_where_query = "WHERE "
-- First of all add the basic filters (ifid and epoch) to the where
if query_info.basic_filters then
local basic_filters = query_info.basic_filters
-- IFID
if basic_filters.ifid then
formatted_where_query =
string.format("%sINTERFACE_ID=%s AND ", formatted_where_query, tostring(basic_filters.ifid))
end
if basic_filters.epoch_begin then
formatted_where_query =
string.format("%sFIRST_SEEN>=%s AND ", formatted_where_query, tostring(basic_filters.epoch_begin))
end
if basic_filters.epoch_end then
formatted_where_query = string.format(
"%sFIRST_SEEN<=%u AND LAST_SEEN<=%s AND ",
formatted_where_query,
tostring(basic_filters.epoch_end),
tostring(basic_filters.epoch_end)
)
end
end
-- Now format the others where
for index, where_info in pairs(where_keys) do
-- It means that there is an array of keys that needs to be put in an OR condition
if where_info.or_condition then
formatted_where_query = formatted_where_query .. "("
for _, info in pairs(where_info.key) do
formatted_where_query =
string.format("%s%s%s%s OR ", formatted_where_query, info.key, info.operator, tostring(info.value))
end
formatted_where_query = formatted_where_query:sub(1, -4)
formatted_where_query = formatted_where_query .. ")"
else
formatted_where_query = string.format(
"%s%s%s%s AND ",
formatted_where_query,
where_info.key,
where_info.operator,
tostring(where_info.value)
)
end
end
-- Remove the last "AND " string alwais added
if not isEmptyString(formatted_where_query) and formatted_where_query:sub(-5) == " AND " then
formatted_where_query = formatted_where_query:sub(1, -5)
end
return formatted_where_query
end
-- ###################################################
function flow_data_historical.retrieveFlowData(query_info)
local results = nil
local error_code = nil
-- Handle the select first
local isHistorical = not query_info.basic_info
or isEmptyString(query_info.basic_info.epoch_begin)
or isEmptyString(query_info.basic_info.epoch_end)
-- In case no select keys are requested return empty -> error
if not query_info.select_query then
traceError(TRACE_ERROR, TRACE_CONSOLE, "No SELECT keys provided\n")
return {}
end
local select_query = buildSelectClause(query_info)
local group_by_query = buildGroupByClause(query_info)
local where_query = buildWhereClause(query_info)
local order_by_query = buildOrderByClause(query_info)
local limit_query = buildLimitClause(query_info)
local query =
string.format("%s FROM %s %s %s %s %s", select_query, TABLE_NAME, where_query, group_by_query, order_by_query, limit_query)
if isHistorical and hasClickHouseSupport() then
results, error_code = interface.execSQLQuery(query)
else
results, error_code = interface.execInMemoryQuery(query)
end
return results or {}
end
-- ###################################################
return flow_data_historical