mirror of
https://github.com/ntop/ntopng.git
synced 2026-04-29 07:29:32 +00:00
Added incoming and outgoing edges in alerts graph (#9169)
This commit is contained in:
parent
da72706c6d
commit
2308fb1906
3 changed files with 713 additions and 358 deletions
|
|
@ -2,7 +2,8 @@
|
|||
-- (C) 2013-24 - ntop.org
|
||||
--
|
||||
local dirs = ntop.getDirs()
|
||||
package.path = dirs.installdir .. "/scripts/lua/modules/pools/?.lua;" .. package.path
|
||||
package.path = dirs.installdir .. "/scripts/lua/modules/pools/?.lua;" ..
|
||||
package.path
|
||||
|
||||
require "lua_utils"
|
||||
require "db_utils"
|
||||
|
|
@ -21,14 +22,125 @@ local graph_utils = {}
|
|||
|
||||
-- ########################################################
|
||||
|
||||
graph_utils.graph_colors = { '#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f',
|
||||
'#bcbd22', '#17becf', -- https://github.com/mbostock/d3/wiki/Ordinal-Scales
|
||||
'#ffbb78', '#1f77b4', '#aec7e8', '#2ca02c', '#98df8a', '#d62728', '#ff9896', '#9467bd', '#c5b0d5', '#8c564b',
|
||||
'#c49c94', '#e377c2', '#f7b6d2', '#7f7f7f', '#c7c7c7', '#bcbd22', '#dbdb8d', '#17becf',
|
||||
'#9edae5' }
|
||||
graph_utils.graph_colors_old = {
|
||||
'#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2',
|
||||
'#7f7f7f', '#bcbd22', '#17becf', -- https://github.com/mbostock/d3/wiki/Ordinal-Scales
|
||||
'#ffbb78', '#1f77b4', '#aec7e8', '#2ca02c', '#98df8a', '#d62728', '#ff9896',
|
||||
'#9467bd', '#c5b0d5', '#8c564b', '#c49c94', '#e377c2', '#f7b6d2', '#7f7f7f',
|
||||
'#c7c7c7', '#bcbd22', '#dbdb8d', '#17becf', '#9edae5'
|
||||
}
|
||||
|
||||
-- ########################################################
|
||||
|
||||
-- "#0A84FF", -- Blue (information)
|
||||
|
||||
local function hue2rgb(p, q, t)
|
||||
if t < 0 then t = t + 1 end
|
||||
if t > 1 then t = t - 1 end
|
||||
if t < 1 / 6 then return p + (q - p) * 6 * t end
|
||||
if t < 1 / 2 then return q end
|
||||
if t < 2 / 3 then
|
||||
return p + (q - p) * (2 / 3 - t) * 6
|
||||
end
|
||||
return p
|
||||
end
|
||||
|
||||
local function addAlphaToHex(hex, alpha)
|
||||
local alphaHex = string.format("%02x", math.floor(alpha * 255 + 0.5))
|
||||
return hex .. alphaHex
|
||||
end
|
||||
|
||||
|
||||
function generateColorPalette(n, alpha)
|
||||
local colors = {}
|
||||
local alpha = 0.8
|
||||
|
||||
local baseColors = {
|
||||
"#00A3FF", -- Electric blue (primary)
|
||||
"#FF3B5C", -- Neon red (critical)
|
||||
"#FF9E00", -- Vivid orange (warning)
|
||||
"#FFD600", -- Bright yellow (alerts)
|
||||
"#3DFAFF", -- Cyan (monitoring)
|
||||
"#5232FF", -- Deep purple (intelligence)
|
||||
"#00E676", -- Neon green (secure)
|
||||
"#B94FFF", -- Magenta (anomalies)
|
||||
"#0F2B46", -- Dark blue (protected)
|
||||
"#56CFE1", -- Light blue (information)
|
||||
"#FF5864", -- Coral (threat)
|
||||
"#3F51B5", -- Indigo (defense)
|
||||
"#01C8AA", -- Teal (normal)
|
||||
"#6874E8", -- Periwinkle (analysis)
|
||||
"#FFB627" -- Amber
|
||||
}
|
||||
|
||||
-- Function to convert hex to RGBA
|
||||
local function hexToRgba(hex, alpha)
|
||||
local hex = hex:gsub("#","")
|
||||
local r = tonumber("0x"..hex:sub(1,2))
|
||||
local g = tonumber("0x"..hex:sub(3,4))
|
||||
local b = tonumber("0x"..hex:sub(5,6))
|
||||
return string.format("rgba(%d, %d, %d, %.2f)", r, g, b, alpha)
|
||||
end
|
||||
|
||||
-- Function to convert hex to hex with alpha
|
||||
local function hexToHexAlpha(hex, alpha)
|
||||
local alphaHex = string.format("%02x", math.floor(alpha * 255 + 0.5))
|
||||
return hex .. alphaHex
|
||||
end
|
||||
|
||||
-- If requested colors are fewer than our base set, return a subset
|
||||
if n <= #baseColors then
|
||||
for i = 1, n do
|
||||
table.insert(colors, hexToRgba(baseColors[i], alpha))
|
||||
end
|
||||
return colors
|
||||
end
|
||||
|
||||
-- Get base colors
|
||||
for i = 1, #baseColors do
|
||||
table.insert(colors, hexToRgba(baseColors[i], alpha))
|
||||
end
|
||||
|
||||
local saturation = 75
|
||||
local lightness = 55
|
||||
|
||||
for i = #baseColors, n - 1 do
|
||||
-- Use golden angle to distribute hues
|
||||
local hue = (i * 137.508) % 360
|
||||
|
||||
-- Convert HSL to RGB
|
||||
local h = hue / 360
|
||||
local s = saturation / 100
|
||||
local l = lightness / 100
|
||||
|
||||
local r, g, b
|
||||
|
||||
if s == 0 then
|
||||
r, g, b = l, l, l
|
||||
else
|
||||
|
||||
local q = l < 0.5 and l * (1 + s) or l + s - l * s
|
||||
local p = 2 * l - q
|
||||
|
||||
r = hue2rgb(p, q, h + 1 / 3)
|
||||
g = hue2rgb(p, q, h)
|
||||
b = hue2rgb(p, q, h - 1 / 3)
|
||||
end
|
||||
|
||||
local rgba = string.format("rgba(%d, %d, %d, %.2f)",
|
||||
math.floor(r * 255 + 0.5),
|
||||
math.floor(g * 255 + 0.5),
|
||||
math.floor(b * 255 + 0.5),
|
||||
alpha)
|
||||
|
||||
table.insert(colors, rgba)
|
||||
end
|
||||
|
||||
return colors
|
||||
end
|
||||
|
||||
graph_utils.graph_colors = generateColorPalette(30)
|
||||
|
||||
function graph_utils.get_html_color(index)
|
||||
return graph_utils.graph_colors[(index % #graph_utils.graph_colors) + 1]
|
||||
end
|
||||
|
|
@ -78,7 +190,8 @@ function graph_utils.normalizeSeriesPoints(series)
|
|||
-- ending up in having a much smaller step.
|
||||
-- TODO: adjust timeseries times.
|
||||
-- TODO: handle series with different start and end times.
|
||||
serie_data.data = ts_common.upsampleSerie(serie_data.data, max_count)
|
||||
serie_data.data = ts_common.upsampleSerie(serie_data.data,
|
||||
max_count)
|
||||
-- The new step needs to be adjusted as well. The new step is smaller
|
||||
-- than the new step. To calculate it, multiply the old step by the fraction
|
||||
-- of old vs new points.
|
||||
|
|
@ -97,15 +210,14 @@ end
|
|||
|
||||
function graph_utils.getProtoVolume(ifName, start_time, end_time, ts_options)
|
||||
ifId = getInterfaceId(ifName)
|
||||
local series = ts_utils.listSeries("iface:ndpi", {
|
||||
ifid = ifId
|
||||
}, start_time)
|
||||
local series = ts_utils.listSeries("iface:ndpi", {ifid = ifId}, start_time)
|
||||
|
||||
ret = {}
|
||||
|
||||
for _, tags in ipairs(series or {}) do
|
||||
-- NOTE: this could be optimized via a dedicated driver call
|
||||
local data = ts_utils.query("iface:ndpi", tags, start_time, end_time, ts_options)
|
||||
local data = ts_utils.query("iface:ndpi", tags, start_time, end_time,
|
||||
ts_options)
|
||||
|
||||
if (data ~= nil) and (data.statistics.total > 0) then
|
||||
ret[tags.protocol] = data.statistics.total
|
||||
|
|
@ -117,7 +229,8 @@ end
|
|||
|
||||
-- ########################################################
|
||||
|
||||
function graph_utils.breakdownBar(sent, sentLabel, rcvd, rcvdLabel, thresholdLow, thresholdHigh)
|
||||
function graph_utils.breakdownBar(sent, sentLabel, rcvd, rcvdLabel,
|
||||
thresholdLow, thresholdHigh)
|
||||
if ((sent + rcvd) > 0) then
|
||||
sent2rcvd = round((sent * 100) / (sent + rcvd), 0)
|
||||
-- io.write("****>> "..sent.."/"..rcvd.."/"..sent2rcvd.."\n")
|
||||
|
|
@ -129,16 +242,22 @@ function graph_utils.breakdownBar(sent, sentLabel, rcvd, rcvdLabel, thresholdLow
|
|||
end
|
||||
|
||||
if (sent2rcvd < thresholdLow) then
|
||||
sentLabel = '<i class="fas fa-exclamation-triangle fa-lg"></i> ' .. sentLabel
|
||||
sentLabel = '<i class="fas fa-exclamation-triangle fa-lg"></i> ' ..
|
||||
sentLabel
|
||||
elseif (sent2rcvd > thresholdHigh) then
|
||||
rcvdLabel = '<i class="fas fa-exclamation-triangle fa-lg""></i> ' .. rcvdLabel
|
||||
rcvdLabel = '<i class="fas fa-exclamation-triangle fa-lg""></i> ' ..
|
||||
rcvdLabel
|
||||
end
|
||||
|
||||
print('<div class="progress"><div class="progress-bar bg-warning" aria-valuenow="' .. sent2rcvd ..
|
||||
'" aria-valuemin="0" aria-valuemax="100" style="width: ' .. sent2rcvd .. '%;">' .. sentLabel)
|
||||
print('</div><div class="progress-bar bg-success" aria-valuenow="' .. (100 - sent2rcvd) ..
|
||||
'" aria-valuemin="0" aria-valuemax="100" style="width: ' .. (100 - sent2rcvd) .. '%;">' .. rcvdLabel ..
|
||||
'</div></div>')
|
||||
print(
|
||||
'<div class="progress"><div class="progress-bar bg-warning" aria-valuenow="' ..
|
||||
sent2rcvd ..
|
||||
'" aria-valuemin="0" aria-valuemax="100" style="width: ' ..
|
||||
sent2rcvd .. '%;">' .. sentLabel)
|
||||
print('</div><div class="progress-bar bg-success" aria-valuenow="' ..
|
||||
(100 - sent2rcvd) ..
|
||||
'" aria-valuemin="0" aria-valuemax="100" style="width: ' ..
|
||||
(100 - sent2rcvd) .. '%;">' .. rcvdLabel .. '</div></div>')
|
||||
else
|
||||
print(' ')
|
||||
end
|
||||
|
|
@ -150,8 +269,10 @@ function graph_utils.percentageBar(total, value, valueLabel)
|
|||
-- io.write("****>> "..total.."/"..value.."\n")
|
||||
if ((total ~= nil) and (total > 0)) then
|
||||
pctg = round((value * 100) / total, 0)
|
||||
print('<div class="progress"><div class="progress-bar bg-warning" aria-valuenow="' .. pctg ..
|
||||
'" aria-valuemin="0" aria-valuemax="100" style="width: ' .. pctg .. '%;">' .. valueLabel)
|
||||
print(
|
||||
'<div class="progress"><div class="progress-bar bg-warning" aria-valuenow="' ..
|
||||
pctg .. '" aria-valuemin="0" aria-valuemax="100" style="width: ' ..
|
||||
pctg .. '%;">' .. valueLabel)
|
||||
print('</div></div>')
|
||||
else
|
||||
print(' ')
|
||||
|
|
@ -162,16 +283,15 @@ end
|
|||
|
||||
function graph_utils.makeProgressBar(percentage)
|
||||
-- nan check
|
||||
if percentage ~= percentage then
|
||||
return ""
|
||||
end
|
||||
if percentage ~= percentage then return "" end
|
||||
|
||||
local perc_int = round(percentage)
|
||||
return
|
||||
'<span style="width: 70%; float:left"><div class="progress"><div class="progress-bar bg-warning" aria-valuenow="' ..
|
||||
perc_int .. '" aria-valuemin="0" aria-valuemax="100" style="width: ' .. perc_int ..
|
||||
'%;"></div></div></span><span style="width: 30%; margin-left: 15px;">' .. round(percentage, 1) ..
|
||||
' %</span>'
|
||||
perc_int .. '" aria-valuemin="0" aria-valuemax="100" style="width: ' ..
|
||||
perc_int ..
|
||||
'%;"></div></div></span><span style="width: 30%; margin-left: 15px;">' ..
|
||||
round(percentage, 1) .. ' %</span>'
|
||||
end
|
||||
|
||||
-- ########################################################
|
||||
|
|
@ -187,24 +307,21 @@ end
|
|||
-- ! @param css_class an optional css class to apply to the progress div
|
||||
-- ! @skip_zero_values don't display values containing only zero
|
||||
-- ! @return html for the bar
|
||||
function graph_utils.stackedProgressBars(total, bars, other_label, formatter, css_class, skip_zero_values)
|
||||
function graph_utils.stackedProgressBars(total, bars, other_label, formatter,
|
||||
css_class, skip_zero_values)
|
||||
local res = {}
|
||||
local cumulative = 0
|
||||
local cumulative_perc = 0
|
||||
local skip_zero_values = skip_zero_values or false
|
||||
formatter = formatter or (function(x)
|
||||
return x
|
||||
end)
|
||||
formatter = formatter or (function(x) return x end)
|
||||
|
||||
-- The bars
|
||||
res[#res + 1] = [[<div class=']] .. (css_class or "ntop-progress-stacked") .. [['><div class="progress">]]
|
||||
res[#res + 1] =
|
||||
[[<div class=']] .. (css_class or "ntop-progress-stacked") ..
|
||||
[['><div class="progress">]]
|
||||
|
||||
for _, bar in ipairs(bars) do
|
||||
cumulative = cumulative + bar.value
|
||||
end
|
||||
if cumulative > total then
|
||||
total = cumulative
|
||||
end
|
||||
for _, bar in ipairs(bars) do cumulative = cumulative + bar.value end
|
||||
if cumulative > total then total = cumulative end
|
||||
|
||||
for _, bar in ipairs(bars) do
|
||||
local percentage = round(bar.value * 100 / total, 2)
|
||||
|
|
@ -212,23 +329,21 @@ function graph_utils.stackedProgressBars(total, bars, other_label, formatter, cs
|
|||
percentage = 100 - cumulative_perc
|
||||
end
|
||||
cumulative_perc = cumulative_perc + percentage
|
||||
if bar.class == nil then
|
||||
bar.class = "primary"
|
||||
end
|
||||
if bar.style == nil then
|
||||
bar.style = ""
|
||||
end
|
||||
if bar.class == nil then bar.class = "primary" end
|
||||
if bar.style == nil then bar.style = "" end
|
||||
if bar.link ~= nil then
|
||||
res[#res + 1] = [[<a href="]] .. bar.link .. [[" style="width:]] .. percentage .. [[%;]] .. bar.style ..
|
||||
[[" class="progress-bar bg-]] .. (bar.class) .. [[" role="progressbar"></a>]]
|
||||
res[#res + 1] = [[<a href="]] .. bar.link .. [[" style="width:]] ..
|
||||
percentage .. [[%;]] .. bar.style ..
|
||||
[[" class="progress-bar bg-]] .. (bar.class) ..
|
||||
[[" role="progressbar"></a>]]
|
||||
else
|
||||
res[#res + 1] = [[
|
||||
<div class="progress-bar bg-]] .. (bar.class) .. [[" role="progressbar" style="width:]] .. percentage ..
|
||||
[[%;]] .. bar.style .. [["></div></a>]]
|
||||
end
|
||||
if bar.link ~= nil then
|
||||
res[#res + 1] = [[</a>]]
|
||||
<div class="progress-bar bg-]] .. (bar.class) ..
|
||||
[[" role="progressbar" style="width:]] ..
|
||||
percentage .. [[%;]] .. bar.style ..
|
||||
[["></div></a>]]
|
||||
end
|
||||
if bar.link ~= nil then res[#res + 1] = [[</a>]] end
|
||||
end
|
||||
|
||||
res[#res + 1] = [[
|
||||
|
|
@ -252,29 +367,27 @@ function graph_utils.stackedProgressBars(total, bars, other_label, formatter, cs
|
|||
|
||||
num = 0
|
||||
for _, bar in ipairs(legend_items) do
|
||||
if skip_zero_values and bar.value == 0 then
|
||||
goto continue
|
||||
end
|
||||
if skip_zero_values and bar.value == 0 then goto continue end
|
||||
|
||||
res[#res + 1] = [[<span>]]
|
||||
if (num > 0) then
|
||||
res[#res + 1] = [[<br>]]
|
||||
end
|
||||
if (num > 0) then res[#res + 1] = [[<br>]] end
|
||||
if bar.link ~= nil then
|
||||
res[#res + 1] = [[<a href="]] .. bar.link .. [[">]]
|
||||
end
|
||||
res[#res + 1] = [[<span class="badge bg-]] .. (bar.class) .. [[" style="]] .. bar.style .. [["> </span>]]
|
||||
if bar.link ~= nil then
|
||||
res[#res + 1] = [[</a>]]
|
||||
end
|
||||
res[#res + 1] = [[<span> ]] .. bar.title .. " (" .. formatter(bar.value) .. ")</span></span>"
|
||||
res[#res + 1] = [[<span class="badge bg-]] .. (bar.class) ..
|
||||
[[" style="]] .. bar.style .. [["> </span>]]
|
||||
if bar.link ~= nil then res[#res + 1] = [[</a>]] end
|
||||
res[#res + 1] =
|
||||
[[<span> ]] .. bar.title .. " (" .. formatter(bar.value) ..
|
||||
")</span></span>"
|
||||
num = num + 1
|
||||
|
||||
::continue::
|
||||
end
|
||||
|
||||
res[#res + 1] = [[<span style="margin-left: 0"><span></span><span> - ]] .. i18n("total") ..
|
||||
": " .. formatter(total) .. "</span></span>"
|
||||
res[#res + 1] =
|
||||
[[<span style="margin-left: 0"><span></span><span> - ]] ..
|
||||
i18n("total") .. ": " .. formatter(total) .. "</span></span>"
|
||||
|
||||
return table.concat(res)
|
||||
end
|
||||
|
|
@ -308,28 +421,45 @@ function graph_utils.drawNewGraphs(source_value_object)
|
|||
local ifid = ifstats.id
|
||||
|
||||
-- Check extraction permissions
|
||||
local traffic_extraction_permitted = recording_utils.isActive(ifid) or recording_utils.isExtractionActive(ifid)
|
||||
local traffic_extraction_permitted =
|
||||
recording_utils.isActive(ifid) or
|
||||
recording_utils.isExtractionActive(ifid)
|
||||
|
||||
if source_value_object == nil then
|
||||
source_value_object = {}
|
||||
end
|
||||
if source_value_object == nil then source_value_object = {} end
|
||||
|
||||
-- Checking the available timeseries
|
||||
local interface_ts_enabled = ntop.getCache("ntopng.prefs.interface_ndpi_timeseries_creation") == "1"
|
||||
local host_ts_creation = ntop.getPref("ntopng.prefs.hosts_ts_creation") ~= nil
|
||||
local host_ts_enabled = ntop.getCache("ntopng.prefs.host_ndpi_timeseries_creation")
|
||||
local l2_ts_enabled = ntop.getPref("ntopng.prefs.l2_device_rrd_creation") == "1"
|
||||
local interface_ts_enabled = ntop.getCache(
|
||||
"ntopng.prefs.interface_ndpi_timeseries_creation") ==
|
||||
"1"
|
||||
local host_ts_creation = ntop.getPref("ntopng.prefs.hosts_ts_creation") ~=
|
||||
nil
|
||||
local host_ts_enabled = ntop.getCache(
|
||||
"ntopng.prefs.host_ndpi_timeseries_creation")
|
||||
local l2_ts_enabled = ntop.getPref("ntopng.prefs.l2_device_rrd_creation") ==
|
||||
"1"
|
||||
local network_ts_enabled = true -- always enabled
|
||||
local asn_ts_enabled = ntop.getPref("ntopng.prefs.asn_rrd_creation") == "1"
|
||||
local country_ts_enabled = ntop.getPref("ntopng.prefs.country_rrd_creation") == "1"
|
||||
local country_ts_enabled =
|
||||
ntop.getPref("ntopng.prefs.country_rrd_creation") == "1"
|
||||
local os_ts_enabled = ntop.getPref("ntopng.prefs.os_rrd_creation") == "1"
|
||||
local vlan_ts_enabled = ntop.getPref("ntopng.prefs.vlan_rrd_creation") == "1"
|
||||
local host_pools_ts_enabled = ntop.getPref("ntopng.prefs.host_pools_rrd_creation") == "1"
|
||||
local system_probes_ts_enabled = ntop.getPref("ntopng.prefs.system_probes_rrd_creation") == "1"
|
||||
local am_ts_enabled = ntop.getPref("ntopng.prefs.system_probes_timeseries") == "1"
|
||||
local snmp_ts_enabled = ntop.getPref("ntopng.prefs.snmp_devices_rrd_creation") == "1"
|
||||
local flow_device_ts_enabled = ntop.getPref("ntopng.prefs.flow_device_port_rrd_creation") == "1"
|
||||
local obs_point_ts_enabled = ntop.getPref("ntopng.prefs.observation_points_rrd_creation") == "1"
|
||||
local vlan_ts_enabled = ntop.getPref("ntopng.prefs.vlan_rrd_creation") ==
|
||||
"1"
|
||||
local host_pools_ts_enabled = ntop.getPref(
|
||||
"ntopng.prefs.host_pools_rrd_creation") ==
|
||||
"1"
|
||||
local system_probes_ts_enabled = ntop.getPref(
|
||||
"ntopng.prefs.system_probes_rrd_creation") ==
|
||||
"1"
|
||||
local am_ts_enabled =
|
||||
ntop.getPref("ntopng.prefs.system_probes_timeseries") == "1"
|
||||
local snmp_ts_enabled = ntop.getPref(
|
||||
"ntopng.prefs.snmp_devices_rrd_creation") == "1"
|
||||
local flow_device_ts_enabled = ntop.getPref(
|
||||
"ntopng.prefs.flow_device_port_rrd_creation") ==
|
||||
"1"
|
||||
local obs_point_ts_enabled = ntop.getPref(
|
||||
"ntopng.prefs.observation_points_rrd_creation") ==
|
||||
"1"
|
||||
|
||||
local topk_heuristic = ntop.getPref("ntopng.prefs.topk_heuristic_precision")
|
||||
local ts_driver = ntop.getPref("ntopng.prefs.timeseries_driver")
|
||||
|
|
@ -339,12 +469,17 @@ function graph_utils.drawNewGraphs(source_value_object)
|
|||
local container_ts_enabled = ifstats.has_seen_containers
|
||||
|
||||
-- Checking which top timeseries are available
|
||||
local interface_has_top_protocols = (interface_ts_enabled == "both" or interface_ts_enabled == "per_protocol" or
|
||||
interface_ts_enabled == "full")
|
||||
local interface_has_top_categories = (interface_ts_enabled == "both" or interface_ts_enabled == "per_category" or
|
||||
interface_ts_enabled == "full")
|
||||
local host_has_top_protocols = (host_ts_enabled == "both" or host_ts_enabled == "per_protocol")
|
||||
local host_has_top_categories = (host_ts_enabled == "both" or host_ts_enabled == "per_category")
|
||||
local interface_has_top_protocols = (interface_ts_enabled == "both" or
|
||||
interface_ts_enabled ==
|
||||
"per_protocol" or
|
||||
interface_ts_enabled == "full")
|
||||
local interface_has_top_categories =
|
||||
(interface_ts_enabled == "both" or interface_ts_enabled ==
|
||||
"per_category" or interface_ts_enabled == "full")
|
||||
local host_has_top_protocols =
|
||||
(host_ts_enabled == "both" or host_ts_enabled == "per_protocol")
|
||||
local host_has_top_categories = (host_ts_enabled == "both" or
|
||||
host_ts_enabled == "per_category")
|
||||
|
||||
local sources_types_enabled = {
|
||||
interface = true, -- always enabled
|
||||
|
|
@ -383,14 +518,10 @@ function graph_utils.drawNewGraphs(source_value_object)
|
|||
},
|
||||
host = {
|
||||
top_protocols = host_has_top_protocols,
|
||||
top_categories = host_has_top_categories,
|
||||
top_categories = host_has_top_categories
|
||||
},
|
||||
snmp = {
|
||||
top_snmp_ifaces = true
|
||||
},
|
||||
flowdevice = {
|
||||
top_flowdev_ifaces = true
|
||||
}
|
||||
snmp = {top_snmp_ifaces = true},
|
||||
flowdevice = {top_flowdev_ifaces = true}
|
||||
}
|
||||
|
||||
local context = {
|
||||
|
|
@ -398,9 +529,11 @@ function graph_utils.drawNewGraphs(source_value_object)
|
|||
sources_types_enabled = json.encode(sources_types_enabled),
|
||||
source_value_object = json.encode(source_value_object),
|
||||
sources_types_top_enabled = json.encode(sources_types_top_enabled),
|
||||
is_dark_mode = ntop.getPref("ntopng.user." .. _SESSION["user"] .. ".theme") == "dark"
|
||||
is_dark_mode = ntop.getPref("ntopng.user." .. _SESSION["user"] ..
|
||||
".theme") == "dark"
|
||||
}
|
||||
template_utils.render("pages/components/historical_interface.template", context)
|
||||
template_utils.render("pages/components/historical_interface.template",
|
||||
context)
|
||||
end
|
||||
|
||||
-- #################################################
|
||||
|
|
@ -417,7 +550,8 @@ end
|
|||
-- bool traffic
|
||||
-- bool time
|
||||
--
|
||||
function graph_utils.printProtocolQuota(proto, ndpi_stats, category_stats, quotas_to_show, show_td, hide_limit)
|
||||
function graph_utils.printProtocolQuota(proto, ndpi_stats, category_stats,
|
||||
quotas_to_show, show_td, hide_limit)
|
||||
local total_bytes = 0
|
||||
local total_duration = 0
|
||||
local output = {}
|
||||
|
|
@ -439,83 +573,118 @@ function graph_utils.printProtocolQuota(proto, ndpi_stats, category_stats, quota
|
|||
end
|
||||
|
||||
if quotas_to_show.traffic then
|
||||
local bytes_exceeded = ((proto.traffic_quota ~= "0") and (total_bytes >= tonumber(proto.traffic_quota)))
|
||||
local bytes_exceeded = ((proto.traffic_quota ~= "0") and
|
||||
(total_bytes >= tonumber(proto.traffic_quota)))
|
||||
local lb_bytes = bytesToSize(total_bytes)
|
||||
local lb_bytes_quota = ternary(proto.traffic_quota ~= "0", bytesToSize(tonumber(proto.traffic_quota)),
|
||||
i18n("unlimited"))
|
||||
local traffic_taken = ternary(proto.traffic_quota ~= "0", math.min(total_bytes, tonumber(proto.traffic_quota)),
|
||||
0)
|
||||
local traffic_remaining = math.max(tonumber(proto.traffic_quota) - traffic_taken, 0)
|
||||
local lb_bytes_quota = ternary(proto.traffic_quota ~= "0", bytesToSize(
|
||||
tonumber(proto.traffic_quota)),
|
||||
i18n("unlimited"))
|
||||
local traffic_taken = ternary(proto.traffic_quota ~= "0", math.min(
|
||||
total_bytes,
|
||||
tonumber(proto.traffic_quota)), 0)
|
||||
local traffic_remaining = math.max(
|
||||
tonumber(proto.traffic_quota) -
|
||||
traffic_taken, 0)
|
||||
local traffic_quota_ratio = 0
|
||||
if traffic_taken + traffic_remaining > 0 then
|
||||
traffic_quota_ratio = round(traffic_taken * 100 / (traffic_taken + traffic_remaining), 0) or 0
|
||||
traffic_quota_ratio = round(traffic_taken * 100 /
|
||||
(traffic_taken + traffic_remaining),
|
||||
0) or 0
|
||||
end
|
||||
|
||||
if show_td then
|
||||
output[#output + 1] = [[<td class='text-end']] .. ternary(bytes_exceeded, ' style=\'color:red;\'', '') ..
|
||||
"><span>" .. lb_bytes .. ternary(hide_limit, "", " / " .. lb_bytes_quota) ..
|
||||
"</span>"
|
||||
output[#output + 1] = [[<td class='text-end']] ..
|
||||
ternary(bytes_exceeded,
|
||||
' style=\'color:red;\'', '') ..
|
||||
"><span>" .. lb_bytes ..
|
||||
ternary(hide_limit, "",
|
||||
" / " .. lb_bytes_quota) ..
|
||||
"</span>"
|
||||
end
|
||||
|
||||
local progress_bar_with_inside_value = [[<div class='progress' style=']] .. (quotas_to_show.traffic_style or "text-align: center;") .. [['>
|
||||
<div class='progress-bar bg-warning' aria-valuenow=']] .. traffic_quota_ratio ..
|
||||
'\' aria-valuemin=\'0\' aria-valuemax=\'100\' style=\'width: ' .. traffic_quota_ratio ..
|
||||
'%;color:black;\'>' ..
|
||||
ternary(traffic_quota_ratio == traffic_quota_ratio --[[nan check]],
|
||||
traffic_quota_ratio, 0) .. [[%
|
||||
local progress_bar_with_inside_value =
|
||||
[[<div class='progress' style=']] ..
|
||||
(quotas_to_show.traffic_style or "text-align: center;") .. [['>
|
||||
<div class='progress-bar bg-warning' aria-valuenow=']] ..
|
||||
traffic_quota_ratio ..
|
||||
'\' aria-valuemin=\'0\' aria-valuemax=\'100\' style=\'width: ' ..
|
||||
traffic_quota_ratio .. '%;color:black;\'>' ..
|
||||
ternary(traffic_quota_ratio == traffic_quota_ratio --[[nan check]] ,
|
||||
traffic_quota_ratio, 0) .. [[%
|
||||
</div>
|
||||
</div>]]
|
||||
|
||||
|
||||
local progress_bar_with_outside_value = [[
|
||||
<div class='progress' style=']] .. (quotas_to_show.traffic_style or "text-align: center;") .. [['>
|
||||
<div class='progress' style=']] ..
|
||||
(quotas_to_show.traffic_style or
|
||||
"text-align: center;") ..
|
||||
[['>
|
||||
|
||||
<div class='progress-bar bg-warning' aria-valuenow=']] .. traffic_quota_ratio ..
|
||||
'\' aria-valuemin=\'0\' aria-valuemax=\'100\' style=\'width: ' .. traffic_quota_ratio ..
|
||||
'%;\'>' .. [[
|
||||
<div class='progress-bar bg-warning' aria-valuenow=']] ..
|
||||
traffic_quota_ratio ..
|
||||
'\' aria-valuemin=\'0\' aria-valuemax=\'100\' style=\'width: ' ..
|
||||
traffic_quota_ratio ..
|
||||
'%;\'>' .. [[
|
||||
</div>]] ..
|
||||
ternary(traffic_quota_ratio == traffic_quota_ratio --[[nan check]],
|
||||
traffic_quota_ratio, 0) .. [[%
|
||||
ternary(
|
||||
traffic_quota_ratio ==
|
||||
traffic_quota_ratio --[[nan check]] ,
|
||||
traffic_quota_ratio, 0) ..
|
||||
[[%
|
||||
</div>]]
|
||||
|
||||
output[#output + 1] = ternary( traffic_quota_ratio < 50,
|
||||
progress_bar_with_outside_value,
|
||||
progress_bar_with_inside_value)
|
||||
if show_td then
|
||||
output[#output + 1] = ("</td>")
|
||||
end
|
||||
|
||||
output[#output + 1] = ternary(traffic_quota_ratio < 50,
|
||||
progress_bar_with_outside_value,
|
||||
progress_bar_with_inside_value)
|
||||
if show_td then output[#output + 1] = ("</td>") end
|
||||
end
|
||||
|
||||
if quotas_to_show.time then
|
||||
local time_exceeded = ((proto.time_quota ~= "0") and (total_duration >= tonumber(proto.time_quota)))
|
||||
local time_exceeded = ((proto.time_quota ~= "0") and
|
||||
(total_duration >= tonumber(proto.time_quota)))
|
||||
local lb_duration = secondsToTime(total_duration)
|
||||
local lb_duration_quota = ternary(proto.time_quota ~= "0", secondsToTime(tonumber(proto.time_quota)),
|
||||
i18n("unlimited"))
|
||||
local lb_duration_quota = ternary(proto.time_quota ~= "0",
|
||||
secondsToTime(
|
||||
tonumber(proto.time_quota)),
|
||||
i18n("unlimited"))
|
||||
|
||||
local duration_taken = ternary(proto.time_quota ~= "0", math.min(total_duration, tonumber(proto.time_quota)), 0)
|
||||
local duration_remaining = math.max(proto.time_quota - duration_taken, 0)
|
||||
local duration_taken = ternary(proto.time_quota ~= "0", math.min(
|
||||
total_duration,
|
||||
tonumber(proto.time_quota)), 0)
|
||||
local duration_remaining =
|
||||
math.max(proto.time_quota - duration_taken, 0)
|
||||
local duration_quota_ratio = 0
|
||||
if duration_taken + duration_remaining > 0 then
|
||||
duration_quota_ratio = round(duration_taken * 100 / (duration_taken + duration_remaining), 0) or 0
|
||||
duration_quota_ratio = round(
|
||||
duration_taken * 100 /
|
||||
(duration_taken + duration_remaining),
|
||||
0) or 0
|
||||
end
|
||||
|
||||
if show_td then
|
||||
output[#output + 1] = [[<td class='text-end']] .. ternary(time_exceeded, ' style=\'color:red;\'', '') ..
|
||||
"><span>" .. lb_duration .. ternary(hide_limit, "", " / " .. lb_duration_quota) ..
|
||||
"</span>"
|
||||
output[#output + 1] = [[<td class='text-end']] ..
|
||||
ternary(time_exceeded,
|
||||
' style=\'color:red;\'', '') ..
|
||||
"><span>" .. lb_duration ..
|
||||
ternary(hide_limit, "",
|
||||
" / " .. lb_duration_quota) ..
|
||||
"</span>"
|
||||
end
|
||||
|
||||
output[#output + 1] = ([[
|
||||
<div class='progress' style=']] .. (quotas_to_show.time_style or "") .. [['>
|
||||
<div class='progress-bar bg-warning align-items-center justify-content-center' aria-valuenow=']] .. duration_quota_ratio ..
|
||||
'\' aria-valuemin=\'0\' aria-valuemax=\'100\' style=\'width: ' .. duration_quota_ratio ..
|
||||
'%;\'>' ..
|
||||
ternary(duration_quota_ratio == duration_quota_ratio --[[nan check]],
|
||||
duration_quota_ratio, 0) .. [[%
|
||||
<div class='progress' style=']] .. (quotas_to_show.time_style or "") ..
|
||||
[['>
|
||||
<div class='progress-bar bg-warning align-items-center justify-content-center' aria-valuenow=']] ..
|
||||
duration_quota_ratio ..
|
||||
'\' aria-valuemin=\'0\' aria-valuemax=\'100\' style=\'width: ' ..
|
||||
duration_quota_ratio .. '%;\'>' ..
|
||||
ternary(
|
||||
duration_quota_ratio ==
|
||||
duration_quota_ratio --[[nan check]] ,
|
||||
duration_quota_ratio, 0) .. [[%
|
||||
</div>
|
||||
</div>]])
|
||||
if show_td then
|
||||
output[#output + 1] = ("</td>")
|
||||
end
|
||||
if show_td then output[#output + 1] = ("</td>") end
|
||||
end
|
||||
|
||||
return table.concat(output, '')
|
||||
|
|
@ -558,8 +727,10 @@ function graph_utils.poolDropdown(ifId, pool_id, exclude)
|
|||
end
|
||||
|
||||
output[#output + 1] = '>' .. pool.name ..
|
||||
ternary(limit_reached, " (" .. i18n("host_pools.members_limit_reached") .. ")", "") ..
|
||||
'</option>'
|
||||
ternary(limit_reached, " (" ..
|
||||
i18n(
|
||||
"host_pools.members_limit_reached") ..
|
||||
")", "") .. '</option>'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -572,20 +743,23 @@ function graph_utils.printPoolChangeDropdown(ifId, pool_id, have_nedge)
|
|||
local output = {}
|
||||
|
||||
output[#output + 1] = [[<tr>
|
||||
<th>]] .. i18n(ternary(have_nedge, "nedge.user", "host_config.host_pool")) .. [[</th>
|
||||
<th>]] .. i18n(ternary(have_nedge, "nedge.user", "host_config.host_pool")) ..
|
||||
[[</th>
|
||||
<td>
|
||||
<select name="pool" class="form-select" style="width:20em; display:inline;">]]
|
||||
|
||||
output[#output + 1] = graph_utils.poolDropdown(ifId, pool_id)
|
||||
|
||||
local edit_pools_link = ternary(have_nedge, "/lua/pro/nedge/admin/nf_list_users.lua",
|
||||
"/lua/admin/manage_pools.lua?page=host")
|
||||
local edit_pools_link = ternary(have_nedge,
|
||||
"/lua/pro/nedge/admin/nf_list_users.lua",
|
||||
"/lua/admin/manage_pools.lua?page=host")
|
||||
|
||||
output[#output + 1] = [[
|
||||
</select>
|
||||
<a class='ms-1' href="]] .. ntop.getHttpPrefix() .. edit_pools_link ..
|
||||
[["><i class="fas fa-edit" aria-hidden="true" title="]] ..
|
||||
(have_nedge and i18n("edit") or '') .. [["></i></a>
|
||||
[["><i class="fas fa-edit" aria-hidden="true" title="]] ..
|
||||
(have_nedge and i18n("edit") or '') ..
|
||||
[["></i></a>
|
||||
</tr>]]
|
||||
|
||||
print(table.concat(output, ''))
|
||||
|
|
@ -593,8 +767,9 @@ end
|
|||
|
||||
-- #################################################
|
||||
|
||||
function graph_utils.printCategoryDropdownButton(by_id, cat_id_or_name, base_url, page_params, count_callback,
|
||||
skip_unknown)
|
||||
function graph_utils.printCategoryDropdownButton(by_id, cat_id_or_name,
|
||||
base_url, page_params,
|
||||
count_callback, skip_unknown)
|
||||
local function count_all(cat_id, cat_name)
|
||||
local cat_protos = interface.getnDPIProtocols(tonumber(cat_id))
|
||||
return table.len(cat_protos)
|
||||
|
|
@ -604,16 +779,15 @@ function graph_utils.printCategoryDropdownButton(by_id, cat_id_or_name, base_url
|
|||
count_callback = count_callback or count_all
|
||||
|
||||
-- 'Category' button
|
||||
print('\'<div class="btn-group float-right"><div class="btn btn-link dropdown-toggle" data-bs-toggle="dropdown">' ..
|
||||
i18n("category") .. ternary(not isEmptyString(cat_id_or_name), '<span class="fas fa-filter"></span>', '') ..
|
||||
'<span class="caret"></span></div> <ul class="dropdown-menu scrollable-dropdown" role="menu" style="min-width: 90px;">')
|
||||
print(
|
||||
'\'<div class="btn-group float-right"><div class="btn btn-link dropdown-toggle" data-bs-toggle="dropdown">' ..
|
||||
i18n("category") .. ternary(not isEmptyString(cat_id_or_name),
|
||||
'<span class="fas fa-filter"></span>',
|
||||
'') ..
|
||||
'<span class="caret"></span></div> <ul class="dropdown-menu scrollable-dropdown" role="menu" style="min-width: 90px;">')
|
||||
|
||||
-- 'Category' dropdown menu
|
||||
local entries = { {
|
||||
text = i18n("all"),
|
||||
id = "",
|
||||
cat_id = ""
|
||||
} }
|
||||
local entries = {{text = i18n("all"), id = "", cat_id = ""}}
|
||||
entries[#entries + 1] = ""
|
||||
for cat_name, cat_id in pairsByKeys(interface.getnDPICategories()) do
|
||||
local cat_count = count_callback(cat_id, cat_name)
|
||||
|
|
@ -636,11 +810,17 @@ function graph_utils.printCategoryDropdownButton(by_id, cat_id_or_name, base_url
|
|||
|
||||
for _, entry in pairs(entries) do
|
||||
if entry ~= "" then
|
||||
page_params["category"] = ternary(by_id, ternary(entry.cat_id ~= "", "cat_" .. entry.cat_id, ""), entry.id)
|
||||
page_params["category"] = ternary(by_id,
|
||||
ternary(entry.cat_id ~= "",
|
||||
"cat_" .. entry.cat_id, ""),
|
||||
entry.id)
|
||||
|
||||
print('<li><a class="dropdown-item ' ..
|
||||
ternary(cat_id_or_name == ternary(by_id, entry.cat_id, entry.id), 'active', '') .. '" href="' ..
|
||||
getPageUrl(base_url, page_params) .. '">' .. (entry.icon or "") .. entry.text .. '</a></li>')
|
||||
ternary(
|
||||
cat_id_or_name ==
|
||||
ternary(by_id, entry.cat_id, entry.id), 'active',
|
||||
'') .. '" href="' .. getPageUrl(base_url, page_params) ..
|
||||
'">' .. (entry.icon or "") .. entry.text .. '</a></li>')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -657,9 +837,7 @@ end
|
|||
-- Output format:
|
||||
-- { labels = [ 'xxx', ...], series = [ yyy, ... ], colors = [ ... ], ... }
|
||||
function graph_utils.convert_pie_data(res, new_charts, js_formatter)
|
||||
if not new_charts then
|
||||
return res
|
||||
end
|
||||
if not new_charts then return res end
|
||||
|
||||
local labels = {}
|
||||
local series = {}
|
||||
|
|
@ -684,17 +862,8 @@ function graph_utils.convert_pie_data(res, new_charts, js_formatter)
|
|||
labels = labels,
|
||||
series = series,
|
||||
colors = colors,
|
||||
yaxis = {
|
||||
show = false,
|
||||
labels = {
|
||||
formatter = js_formatter
|
||||
}
|
||||
},
|
||||
tooltip = {
|
||||
y = {
|
||||
formatter = js_formatter
|
||||
}
|
||||
},
|
||||
yaxis = {show = false, labels = {formatter = js_formatter}},
|
||||
tooltip = {y = {formatter = js_formatter}},
|
||||
urls = urls,
|
||||
extra_x_tooltip_label = 'None'
|
||||
}
|
||||
|
|
@ -711,9 +880,7 @@ end
|
|||
-- Output format:
|
||||
-- { series = [{ data = [ yyy, ... ] }], xaxis = { categories = [ 'xxx'] } }
|
||||
function graph_utils.convert_bar_data(res, new_charts, js_formatter)
|
||||
if not new_charts then
|
||||
return res
|
||||
end
|
||||
if not new_charts then return res end
|
||||
|
||||
local labels = {}
|
||||
local series = {}
|
||||
|
|
@ -733,18 +900,10 @@ function graph_utils.convert_bar_data(res, new_charts, js_formatter)
|
|||
end
|
||||
|
||||
res = {
|
||||
series = {{
|
||||
data = series
|
||||
}},
|
||||
xaxis = {
|
||||
categories = labels,
|
||||
},
|
||||
series = {{data = series}},
|
||||
xaxis = {categories = labels},
|
||||
urls = urls,
|
||||
tooltip = {
|
||||
y = {
|
||||
formatter = js_formatter
|
||||
}
|
||||
},
|
||||
tooltip = {y = {formatter = js_formatter}}
|
||||
}
|
||||
return res
|
||||
end
|
||||
|
|
@ -753,37 +912,35 @@ end
|
|||
|
||||
-- Merge pie data from 2 data sources (compute top of tops)
|
||||
function graph_utils.merge_pie_data(aggregated_data, data, max_values)
|
||||
-- Merge in temporary table
|
||||
-- Note: aggregated_data may be nil
|
||||
local items = {}
|
||||
if aggregated_data then
|
||||
for _, l in ipairs(aggregated_data.labels or {}) do
|
||||
items[l] = aggregated_data.series[_]
|
||||
end
|
||||
end
|
||||
for _, l in ipairs(data.labels or {}) do
|
||||
if items[l] then
|
||||
items[l] = items[l] + data.series[_]
|
||||
else
|
||||
items[l] = data.series[_]
|
||||
end
|
||||
end
|
||||
|
||||
-- Compute tops
|
||||
data.labels = {}
|
||||
data.series = {}
|
||||
data.colors = {}
|
||||
for l, v in pairsByValues(items or {}, rev) do
|
||||
data.labels[#data.labels+1] = l
|
||||
data.series[#data.series+1] = v
|
||||
data.colors[#data.colors+1] = graph_utils.get_html_color(#data.colors)
|
||||
if #data.labels >= max_values then
|
||||
goto done
|
||||
end
|
||||
end
|
||||
::done::
|
||||
-- Merge in temporary table
|
||||
-- Note: aggregated_data may be nil
|
||||
local items = {}
|
||||
if aggregated_data then
|
||||
for _, l in ipairs(aggregated_data.labels or {}) do
|
||||
items[l] = aggregated_data.series[_]
|
||||
end
|
||||
end
|
||||
for _, l in ipairs(data.labels or {}) do
|
||||
if items[l] then
|
||||
items[l] = items[l] + data.series[_]
|
||||
else
|
||||
items[l] = data.series[_]
|
||||
end
|
||||
end
|
||||
|
||||
return data
|
||||
-- Compute tops
|
||||
data.labels = {}
|
||||
data.series = {}
|
||||
data.colors = {}
|
||||
for l, v in pairsByValues(items or {}, rev) do
|
||||
data.labels[#data.labels + 1] = l
|
||||
data.series[#data.series + 1] = v
|
||||
data.colors[#data.colors + 1] = graph_utils.get_html_color(#data.colors)
|
||||
if #data.labels >= max_values then goto done end
|
||||
end
|
||||
::done::
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
-- #################################################
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue