mirror of
https://github.com/ntop/ntopng.git
synced 2026-04-29 07:29:32 +00:00
This commit is contained in:
parent
1a99e709a1
commit
a3f64be34b
11 changed files with 153 additions and 1071 deletions
|
|
@ -64,48 +64,6 @@ function graph_common.getZoomDuration(cur_zoom)
|
|||
|
||||
local graph_menu_entries = {}
|
||||
|
||||
-- Menu entries are either populated by printSeries (optimized) or directly by
|
||||
-- calling this function. In the latter case it is mandatory to check that the
|
||||
-- series actually exist before calling this function.
|
||||
--
|
||||
-- The rule which determines how an entry is show is:
|
||||
-- - If no timeseries exist at all for the entry, the entry will not be shown
|
||||
-- - If the visualized interval is less then the entry timseries step, then
|
||||
-- the entry will be shown but will be grayed out (disabled state)
|
||||
-- - If timeseries exist for the entry in the visualized interval, the
|
||||
-- entry will be shown and will be clickable
|
||||
function graph_common.populateGraphMenuEntry(label, base_url, params, tab_id, needs_separator, separator_label, pending, extra_params, serie)
|
||||
local url = getPageUrl(base_url, params)
|
||||
local step = nil
|
||||
|
||||
-- table.clone needed as entry_params is modified below
|
||||
local entry_params = table.clone(params)
|
||||
for k, v in pairs(splitUrl(base_url).params) do
|
||||
entry_params[k] = v
|
||||
end
|
||||
|
||||
if(params.ts_schema ~= nil) then
|
||||
step = graph_common.getEntryStep(params.ts_schema)
|
||||
end
|
||||
|
||||
local entry = {
|
||||
label = label,
|
||||
schema = params.ts_schema,
|
||||
params = entry_params, -- for graph_common.graphMenuGetActive
|
||||
url = url,
|
||||
tab_id = tab_id,
|
||||
needs_separator = needs_separator,
|
||||
separator_label = separator_label,
|
||||
pending = pending, -- true for batched operations
|
||||
step = step,
|
||||
extra_params = extra_params,
|
||||
graph_options = serie,
|
||||
}
|
||||
|
||||
graph_menu_entries[#graph_menu_entries + 1] = entry
|
||||
return entry
|
||||
end
|
||||
|
||||
-- ########################################################
|
||||
|
||||
function graph_common.makeMenuDivider()
|
||||
|
|
@ -180,26 +138,6 @@ function graph_common.getMinGraphEntriesStep()
|
|||
return(min_step)
|
||||
end
|
||||
|
||||
-- ##############################################
|
||||
|
||||
function graph_common.getEntryStep(schema_name)
|
||||
if(starts(schema_name, "custom:") and (getCustomSchemaStep ~= nil)) then
|
||||
return(getCustomSchemaStep(schema_name))
|
||||
end
|
||||
|
||||
if(starts(schema_name, "top:")) then
|
||||
schema_name = split(schema_name, "top:")[2]
|
||||
end
|
||||
|
||||
local schema_obj = ts_utils.getSchema(schema_name)
|
||||
|
||||
if(schema_obj) then
|
||||
return(schema_obj.options.step)
|
||||
end
|
||||
|
||||
return(nil)
|
||||
end
|
||||
|
||||
-- ########################################################
|
||||
|
||||
function graph_common.printEntry(idx, entry)
|
||||
|
|
@ -216,523 +154,6 @@ function graph_common.printEntry(idx, entry)
|
|||
print(table.concat(parts, ""))
|
||||
end
|
||||
|
||||
-- ########################################################
|
||||
|
||||
-- Prints the menu from the populated graph_menu_entries.
|
||||
-- The entry_print_callback is called to print the actual entries.
|
||||
function graph_common.printGraphMenuEntries(entry_print_callback, active_entry, start_time, end_time, light_config)
|
||||
local active_entries = {}
|
||||
local active_idx = 1 -- index in active_entries
|
||||
local tdiff = (end_time - start_time)
|
||||
|
||||
-- Sort entries based on label, preserving groups
|
||||
local graph_menu_entries_sorted = {}
|
||||
local sort_table = {}
|
||||
local needs_separator = false
|
||||
local separator_label = nil
|
||||
local first
|
||||
|
||||
for _, entry in ipairs(graph_menu_entries) do
|
||||
if entry.needs_separator
|
||||
or entry.label == nil -- divider
|
||||
then
|
||||
-- sort group
|
||||
first = true
|
||||
for k,v in pairsByKeys(sort_table) do
|
||||
if first then
|
||||
v.needs_separator = needs_separator
|
||||
v.separator_label = separator_label
|
||||
first = false
|
||||
end
|
||||
graph_menu_entries_sorted[#graph_menu_entries_sorted+1] = v
|
||||
end
|
||||
|
||||
-- backup group separator, if any
|
||||
needs_separator = entry.needs_separator or false
|
||||
separator_label = entry.separator_label
|
||||
-- reset group separator on this item
|
||||
entry.needs_separator = false
|
||||
entry.separator_label = nil
|
||||
|
||||
-- append
|
||||
sort_table = {}
|
||||
if entry.label == nil then -- divider
|
||||
graph_menu_entries_sorted[#graph_menu_entries_sorted+1] = entry
|
||||
else
|
||||
sort_table[entry.label] = entry
|
||||
end
|
||||
else
|
||||
-- append
|
||||
sort_table[entry.label] = entry
|
||||
end
|
||||
end
|
||||
-- sort group
|
||||
first = true
|
||||
for k,v in pairsByKeys(sort_table) do
|
||||
if (light_config == true)
|
||||
and (v["params"]["host"])
|
||||
and (v["params"]["dscp_class"]) then
|
||||
graph_menu_entries_sorted[#graph_menu_entries_sorted] = nil
|
||||
v["pending"] = 1 -- Skip the record
|
||||
goto continue
|
||||
end
|
||||
|
||||
if first then
|
||||
v.needs_separator = needs_separator
|
||||
v.separator_label = separator_label
|
||||
first = false
|
||||
end
|
||||
::continue::
|
||||
|
||||
graph_menu_entries_sorted[#graph_menu_entries_sorted+1] = v
|
||||
end
|
||||
|
||||
-- Print entries
|
||||
needs_separator = false
|
||||
separator_label = nil
|
||||
for _, entry in ipairs(graph_menu_entries_sorted) do
|
||||
|
||||
if active_idx ~= 1 then
|
||||
needs_separator = needs_separator or entry.needs_separator
|
||||
separator_label = separator_label or entry.separator_label
|
||||
end
|
||||
|
||||
if(entry.step) then
|
||||
entry.disabled = (tdiff <= entry.step)
|
||||
end
|
||||
|
||||
if(active_entry == entry) then
|
||||
-- Always consider the selected entry as active
|
||||
entry.pending = 0
|
||||
end
|
||||
|
||||
if(ignoreEntry(entry)) then
|
||||
-- not verified, act like it does not exist
|
||||
goto continue
|
||||
end
|
||||
|
||||
if(needs_separator) then
|
||||
print(graph_common.makeMenuDivider())
|
||||
needs_separator = false
|
||||
end
|
||||
if(separator_label) then
|
||||
print(graph_common.makeMenuHeader(separator_label))
|
||||
separator_label = nil
|
||||
end
|
||||
|
||||
if entry.html then
|
||||
print(entry.html)
|
||||
else
|
||||
entry_print_callback(active_idx, entry)
|
||||
active_entries[#active_entries + 1] = entry
|
||||
active_idx = active_idx + 1
|
||||
end
|
||||
|
||||
::continue::
|
||||
end
|
||||
|
||||
-- NOTE: only return the graph_menu_entries which are non-pending
|
||||
return active_entries
|
||||
end
|
||||
|
||||
-- ##############################################
|
||||
|
||||
function graph_common.printSeries(options, tags, start_time, end_time, base_url, params)
|
||||
local series = options.timeseries
|
||||
local needs_separator = false
|
||||
local separator_label = nil
|
||||
local batch_id_to_entry = {}
|
||||
local device_timeseries_mac = options.device_timeseries_mac
|
||||
local mac_tags = nil
|
||||
local mac_params = nil
|
||||
local mac_baseurl = ntop.getHttpPrefix() .. "/lua/mac_details.lua?page=historical"
|
||||
local is_pro = ntop.isPro()
|
||||
local is_enterprise = ntop.isEnterpriseM()
|
||||
local tdiff = (end_time - start_time)
|
||||
|
||||
if params.tskey then
|
||||
-- this can contain a MAC address for local broadcast domain hosts
|
||||
-- table.clone needed as tags is modified below
|
||||
tags = table.clone(tags)
|
||||
tags.host = params.tskey
|
||||
end
|
||||
|
||||
if(device_timeseries_mac ~= nil) then
|
||||
-- table.clone needed as mac_tags is modified below
|
||||
mac_tags = table.clone(tags)
|
||||
mac_tags.host = nil
|
||||
mac_tags.mac = device_timeseries_mac
|
||||
-- table.clone needed as mac_params is modified below
|
||||
mac_params = table.clone(params)
|
||||
mac_params.host = device_timeseries_mac
|
||||
end
|
||||
|
||||
for _, serie in ipairs(series) do
|
||||
if ((have_nedge and serie.nedge_exclude) or (not have_nedge and serie.nedge_only)) or
|
||||
(serie.pro_skip and is_pro) or
|
||||
(serie.skip) or
|
||||
(serie.enterprise_only and (not is_enterprise)) then
|
||||
goto continue
|
||||
end
|
||||
|
||||
local query_start = start_time
|
||||
|
||||
if(serie.schema ~= nil) then
|
||||
local step = graph_common.getEntryStep(serie.schema)
|
||||
|
||||
if step and (tdiff <= step) then
|
||||
-- This entry will not be clickable but maybe it will be
|
||||
-- shown in disabled state if any data for it exists, so
|
||||
-- remove the time constraint
|
||||
query_start = 0
|
||||
end
|
||||
end
|
||||
|
||||
if serie.separator then
|
||||
needs_separator = true
|
||||
separator_label = serie.label
|
||||
else
|
||||
local k = serie.schema
|
||||
local v = serie.label
|
||||
local exists = false
|
||||
local entry_tags = tags
|
||||
local entry_params = table.merge(params, serie.extra_params)
|
||||
local entry_baseurl = base_url
|
||||
local override_link = nil
|
||||
|
||||
-- Contains the list of batch_ids to be associated to this menu entry.
|
||||
-- The entry can only be shown when all the batch_ids have been confirmed
|
||||
-- in getBatchedListSeriesResult
|
||||
local batch_ids = {}
|
||||
|
||||
if starts(k, "custom:") then
|
||||
if not ntop.isPro() then
|
||||
goto continue
|
||||
end
|
||||
|
||||
-- exists by default, otherwise specify a serie.check below
|
||||
exists = true
|
||||
|
||||
if(serie.custom_schema == nil) then
|
||||
serie.custom_schema = graph_common.getCustomSchemaOptions(k)
|
||||
end
|
||||
end
|
||||
|
||||
local to_check = serie.check or (serie.custom_schema and serie.custom_schema.bases)
|
||||
|
||||
if(to_check ~= nil) then
|
||||
exists = true
|
||||
|
||||
-- In the case of custom series, the serie can only be shown if all
|
||||
-- the component series exists
|
||||
for idx, serie in pairs(to_check) do
|
||||
local exist_tags = tags
|
||||
|
||||
if starts(k, "custom:") then
|
||||
exist_tags = graph_common.getCustomSchemaTags(k, exist_tags, idx)
|
||||
end
|
||||
|
||||
local batch_id = ts_utils.batchListSeries(serie, table.merge(exist_tags, serie.extra_params), query_start)
|
||||
|
||||
if batch_id == nil then
|
||||
exists = false
|
||||
break
|
||||
end
|
||||
|
||||
batch_ids[#batch_ids +1] = batch_id
|
||||
end
|
||||
elseif not exists then
|
||||
if(mac_tags ~= nil) and (starts(k, "mac:")) then
|
||||
-- This is a mac timeseries shown under the host
|
||||
entry_tags = mac_tags
|
||||
entry_params = mac_params
|
||||
entry_baseurl = mac_baseurl
|
||||
end
|
||||
|
||||
-- only show if there has been an update within the specified time frame
|
||||
local batch_id = ts_utils.batchListSeries(k, table.merge(entry_tags, serie.extra_params), query_start)
|
||||
|
||||
if batch_id ~= nil then
|
||||
-- assume it exists for now, will verify in getBatchedListSeriesResult
|
||||
exists = true
|
||||
batch_ids[#batch_ids +1] = batch_id
|
||||
end
|
||||
end
|
||||
|
||||
if exists then
|
||||
local entry = graph_common.populateGraphMenuEntry(v, entry_baseurl, table.merge(entry_params, {ts_schema=k}), nil,
|
||||
needs_separator, separator_label, #batch_ids --[[ pending ]], serie.extra_params, serie)
|
||||
|
||||
if entry then
|
||||
for _, batch_id in pairs(batch_ids) do
|
||||
batch_id_to_entry[batch_id] = entry
|
||||
end
|
||||
end
|
||||
|
||||
needs_separator = false
|
||||
separator_label = nil
|
||||
end
|
||||
end
|
||||
|
||||
::continue::
|
||||
end
|
||||
|
||||
-- DSCP
|
||||
if options.dscp_classes then
|
||||
local schema = options.dscp_classes
|
||||
-- table.clone needed as dscp_tags is modified below
|
||||
local dscp_tags = table.clone(tags)
|
||||
dscp_tags.dscp_class = nil
|
||||
|
||||
local series = ts_utils.listSeries(schema, dscp_tags, start_time)
|
||||
|
||||
if not table.empty(series) then
|
||||
graph_common.graphMenuDivider()
|
||||
graph_common.graphMenuHeader(i18n("dscp"))
|
||||
|
||||
local by_class = {}
|
||||
for _, serie in pairs(series) do
|
||||
local sortkey = serie.dscp_class
|
||||
|
||||
if sortkey == "unknown" then
|
||||
-- place at the end
|
||||
sortkey = "z" .. sortkey
|
||||
end
|
||||
|
||||
by_class[sortkey] = serie.dscp_class
|
||||
end
|
||||
|
||||
for _, class in pairsByKeys(by_class, asc) do
|
||||
local label = dscp_consts.ds_class_descr(class)
|
||||
graph_common.populateGraphMenuEntry(label, base_url, table.merge(params, {ts_schema=schema, dscp_class=class}))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- nDPI applications
|
||||
if options.top_protocols then
|
||||
local schema = split(options.top_protocols, "top:")[2]
|
||||
-- table.clone needed as proto_tags is modified below
|
||||
local proto_tags = table.clone(tags)
|
||||
proto_tags.protocol = nil
|
||||
|
||||
local series = ts_utils.listSeries(schema, proto_tags, start_time)
|
||||
|
||||
if not table.empty(series) then
|
||||
graph_common.graphMenuDivider()
|
||||
graph_common.graphMenuHeader(i18n("applications"))
|
||||
|
||||
local by_protocol = {}
|
||||
|
||||
for _, serie in pairs(series) do
|
||||
by_protocol[serie.protocol] = 1
|
||||
end
|
||||
|
||||
for protocol in pairsByKeys(by_protocol, asc_insensitive) do
|
||||
local proto_id = protocol
|
||||
graph_common.populateGraphMenuEntry(protocol, base_url, table.merge(params, {ts_schema=schema, protocol=proto_id}))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- L4 protocols
|
||||
if options.l4_protocols then
|
||||
local schema = options.l4_protocols
|
||||
-- table.clone needed as l4_tags is modified below
|
||||
local l4_tags = table.clone(tags)
|
||||
l4_tags.l4proto = nil
|
||||
|
||||
local series = ts_utils.listSeries(schema, l4_tags, start_time)
|
||||
|
||||
if not table.empty(series) then
|
||||
graph_common.graphMenuDivider()
|
||||
graph_common.graphMenuHeader(i18n("protocols"))
|
||||
|
||||
local by_protocol = {}
|
||||
|
||||
for _, serie in pairs(series) do
|
||||
local sortkey = serie.l4proto
|
||||
|
||||
if sortkey == "other_ip" then
|
||||
-- place at the end
|
||||
sortkey = "z" .. sortkey
|
||||
end
|
||||
|
||||
by_protocol[sortkey] = serie.l4proto
|
||||
end
|
||||
|
||||
for _, protocol in pairsByKeys(by_protocol, asc_insensitive) do
|
||||
local proto_id = protocol
|
||||
local label
|
||||
|
||||
if proto_id == "other_ip" then
|
||||
label = i18n("other")
|
||||
else
|
||||
label = string.upper(protocol)
|
||||
end
|
||||
|
||||
graph_common.populateGraphMenuEntry(label, base_url, table.merge(params, {ts_schema=schema, l4proto=proto_id}))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- nDPI application categories
|
||||
if options.top_categories then
|
||||
local schema = split(options.top_categories, "top:")[2]
|
||||
-- table.clone needed as cat_tags is modified below
|
||||
local cat_tags = table.clone(tags)
|
||||
cat_tags.category = nil
|
||||
local series = ts_utils.listSeries(schema, cat_tags, start_time)
|
||||
|
||||
if not table.empty(series) then
|
||||
graph_common.graphMenuDivider()
|
||||
graph_common.graphMenuHeader(i18n("categories"))
|
||||
|
||||
local by_category = {}
|
||||
|
||||
for _, serie in pairs(series) do
|
||||
by_category[getCategoryLabel(serie.category, interface.getnDPICategoryId(serie.category))] = serie.category
|
||||
end
|
||||
|
||||
for label, category in pairsByKeys(by_category, asc_insensitive) do
|
||||
graph_common.populateGraphMenuEntry(label, base_url, table.merge(params, {ts_schema=schema, category=category}))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Perform the batched operations
|
||||
local result = ts_utils.getBatchedListSeriesResult()
|
||||
|
||||
for batch_id, res in pairs(result) do
|
||||
local entry = batch_id_to_entry[batch_id]
|
||||
|
||||
if entry and not table.empty(res) and entry.pending then
|
||||
-- entry exists, decrement the number of pending requests
|
||||
entry.pending = entry.pending - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ###############################################
|
||||
|
||||
--- Load additiona custom schemas
|
||||
-- See README.charts for an explanation
|
||||
local locally_defined_custom_schemas = {
|
||||
["custom:flows_vs_local_hosts"] = {
|
||||
bases = {"iface:flows", "iface:local_hosts"},
|
||||
types = {"line", "bar"},
|
||||
axis = {1,2},
|
||||
}, ["custom:flows_vs_traffic"] = {
|
||||
bases = {"iface:traffic", "iface:flows"},
|
||||
types = {"line", "bar"},
|
||||
axis = {1,2},
|
||||
}, ["custom:memory_vs_flows_hosts"] = {
|
||||
bases = {"process:resident_memory", "iface:flows", "iface:hosts"},
|
||||
types = {"line", "bar", "bar"},
|
||||
axis = {1,2,2},
|
||||
exclude = {virtual_bytes=1},
|
||||
tags_override = {{ifid=getSystemInterfaceId()},},
|
||||
}, ["custom:score_vs_flows_hosts"] = {
|
||||
bases = {"iface:score", "iface:flows", "iface:hosts"},
|
||||
types = {"line", "bar", "bar"},
|
||||
axis = {1,2,2},
|
||||
exclude = {virtual_bytes=1},
|
||||
}, ["custom:snmp_traffic_vs_errors"] = {
|
||||
bases = {"snmp_if:traffic", "snmp_if:errors"},
|
||||
types = {"line", "bar"},
|
||||
axis = {1,2},
|
||||
}, ["custom:host_ndpi_and_flows"] = {
|
||||
bases = {"host:ndpi", "host:ndpi_flows"},
|
||||
types = {"line", "bar"},
|
||||
axis = {1,2},
|
||||
}, ["custom:iface_ndpi_and_flows"] = {
|
||||
bases = {"iface:ndpi", "iface:ndpi_flows"},
|
||||
types = {"line", "bar"},
|
||||
axis = {1,2},
|
||||
}, ["custom:zmq_msg_rcvd_vs_drops"] = {
|
||||
bases = {"iface:zmq_rcvd_msgs", "iface:zmq_msg_drops"},
|
||||
types = {"area", "area"},
|
||||
axis = {1,2},
|
||||
}, ["custom:iface_tcp_syn_vs_tcp_synack"] = {
|
||||
bases = {"iface:tcp_syn", "iface:tcp_synack"},
|
||||
types = {"area", "area"},
|
||||
axis = {1,1},
|
||||
}, ["custom:flow_script:stats"] = {
|
||||
bases = {"flow_script:skipped_calls", "flow_script:pending_calls", "flow_script:successful_calls"},
|
||||
types = {"area", "area", "line"},
|
||||
axis = {1, 1, 1},
|
||||
}, ["custom:flow_check:vs_total"] = {
|
||||
bases = {"flow_check:duration", "flow_check:total_stats", "flow_check:num_calls"},
|
||||
types = {"line", "line", "bar"},
|
||||
axis = {1, 1, 2},
|
||||
tags_ignore = {nil, {check=1}},
|
||||
exclude = {nil, {num_calls=1}},
|
||||
}, ["custom:elem_check:vs_total"] = {
|
||||
bases = {"elem_check:duration", "elem_check:total_stats", "elem_check:num_calls"},
|
||||
types = {"line", "line", "bar"},
|
||||
axis = {1, 1, 2},
|
||||
tags_ignore = {nil, {check=1}},
|
||||
exclude = {nil, {num_calls=1}},
|
||||
}, ["custom:flow_check:total_stats"] = {
|
||||
bases = {"flow_check:total_stats"},
|
||||
types = {"line"},
|
||||
axis = {1},
|
||||
tags_ignore = {nil, {check=1}},
|
||||
exclude = {num_calls=1},
|
||||
},
|
||||
}
|
||||
|
||||
-- ##############################################
|
||||
|
||||
function graph_common.getCustomSchemaOptions(schema_id)
|
||||
return(locally_defined_custom_schemas[schema_id] or ts_utils.custom_schemas[schema_id])
|
||||
end
|
||||
|
||||
-- ##############################################
|
||||
|
||||
local function getCustomSchemaStep(schema_id)
|
||||
local schema_options = graph_common.getCustomSchemaOptions(schema_id)
|
||||
|
||||
if((schema_options ~= nil) and (schema_options.bases[1])) then
|
||||
local schema_obj = ts_utils.getSchema(schema_options.bases[1])
|
||||
|
||||
if(schema_obj ~= nil) then
|
||||
return(schema_obj.options.step)
|
||||
end
|
||||
end
|
||||
|
||||
return(nil)
|
||||
end
|
||||
|
||||
-- ##############################################
|
||||
|
||||
function graph_common.getCustomSchemaTags(schema_id, query_tags, base_idx)
|
||||
local tags = query_tags
|
||||
local schema_options = graph_common.getCustomSchemaOptions(schema_id)
|
||||
|
||||
if(not schema_options) then
|
||||
return(tags)
|
||||
end
|
||||
|
||||
if schema_options.tags_override and schema_options.tags_override[base_idx] then
|
||||
-- table.clone needed tags is modified below
|
||||
tags = table.clone(tags)
|
||||
|
||||
for tag, override in pairs(schema_options.tags_override[base_idx]) do
|
||||
tags[tag] = override
|
||||
end
|
||||
end
|
||||
|
||||
if schema_options.tags_ignore and schema_options.tags_ignore[base_idx] then
|
||||
-- table.clone needed tags is modified below
|
||||
tags = table.clone(tags)
|
||||
|
||||
for tag in pairs(schema_options.tags_ignore[base_idx]) do
|
||||
tags[tag] = nil
|
||||
end
|
||||
end
|
||||
|
||||
return(tags)
|
||||
end
|
||||
|
||||
return graph_common
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue