mirror of
https://github.com/ntop/ntopng.git
synced 2026-04-29 07:29:32 +00:00
950 lines
33 KiB
Lua
950 lines
33 KiB
Lua
--
|
|
-- (C) 2020 - ntop.org
|
|
--
|
|
local dirs = ntop.getDirs()
|
|
package.path = dirs.installdir .. "/scripts/lua/modules/pools/?.lua;" .. package.path
|
|
package.path = dirs.installdir .. "/scripts/lua/modules/notifications/?.lua;" .. package.path
|
|
|
|
local pools = require("pools")
|
|
local check_utils = require("checks")
|
|
local endpoint_configs = require("endpoints")
|
|
local recipients_manager = require("recipients")
|
|
local page_utils = require('page_utils')
|
|
local toast_ui = require("toast_ui")
|
|
local stats_utils = require("stats_utils")
|
|
local delete_data_utils = require("delete_data_utils")
|
|
local prefs_factory_reset_utils = require("prefs_factory_reset_utils")
|
|
local configuration_utils = require "configuration_utils"
|
|
local recipients = require "recipients"
|
|
local alert_severities = require "alert_severities"
|
|
|
|
local info = ntop.getInfo()
|
|
local prefs = ntop.getPrefs()
|
|
|
|
local ToastLevel = toast_ui.ToastLevels
|
|
|
|
-- Constants
|
|
local ALARM_THRESHOLD_LOW = 80
|
|
local ALARM_THRESHOLD_HIGH = 90
|
|
local IS_ADMIN = isAdministrator()
|
|
local IS_SYSTEM_INTERFACE = page_utils.is_system_view()
|
|
local IS_PCAP_DUMP = interface.isPcapDumpInterface()
|
|
local IS_PACKET_INTERFACE = interface.isPacketInterface()
|
|
local UNEXPECTED_SCRIPTS_ENABLED_CACHE_KEY = "ntopng.cache.checks.unexpected_scripts_enabled"
|
|
|
|
local predicates = {}
|
|
|
|
-- ###############################################################
|
|
|
|
local function create_DHCP_range_missing_notification(toast)
|
|
local title = i18n("about.configure_dhcp_range")
|
|
local description = i18n("about.dhcp_range_missing_warning", {
|
|
name = i18n("prefs.toggle_host_tskey_title"),
|
|
url = ntop.getHttpPrefix() .. "/lua/if_stats.lua?page=config",
|
|
dhcp_url = ntop.getHttpPrefix() .. "/lua/if_stats.lua?page=dhcp"
|
|
})
|
|
|
|
return toast_ui:new(toast.id, title, description, ToastLevel.WARNING, nil, toast.dismissable)
|
|
|
|
end
|
|
|
|
-- ###############################################################
|
|
|
|
local function create_DCHP_monitoring_toast(toast)
|
|
local title = i18n("about.dhcp_monitoring_title")
|
|
local description = i18n("about.host_identifier_warning", {
|
|
name = i18n("prefs.toggle_host_tskey_title"),
|
|
url = ntop.getHttpPrefix() .. "/lua/if_stats.lua?page=config"
|
|
})
|
|
|
|
local action = {
|
|
url = ntop.getHttpPrefix() .. "/lua/if_stats.lua?page=config",
|
|
title = i18n("configure")
|
|
}
|
|
|
|
return toast_ui:new(toast.id, title, description, ToastLevel.WARNING, action, toast.dismissable)
|
|
end
|
|
|
|
-- ###############################################################
|
|
|
|
local function create_DHCP_range_missing_toast(toast)
|
|
local title = i18n("about.dhcp_monitoring_title")
|
|
local description = i18n("about.dhcp_range_missing_warning", {
|
|
name = i18n("prefs.toggle_host_tskey_title"),
|
|
url = ntop.getHttpPrefix() .. "/lua/if_stats.lua?page=config",
|
|
dhcp_url = ntop.getHttpPrefix() .. "/lua/if_stats.lua?page=dhcp"
|
|
})
|
|
|
|
local action = {
|
|
url = ntop.getHttpPrefix() .. "/lua/if_stats.lua?page=dhcp",
|
|
title = i18n("configure")
|
|
}
|
|
|
|
return toast_ui:new(toast.id, title, description, ToastLevel.WARNING, action, toast.dismissable)
|
|
|
|
end
|
|
|
|
-- ###############################################################
|
|
|
|
local function create_geo_ip_toast_ui(toast)
|
|
local title = i18n("geolocation_unavailable_title")
|
|
local description = i18n("geolocation_unavailable", {
|
|
url = "https://github.com/ntop/ntopng/blob/dev/doc/README.geolocation.md",
|
|
target = "_blank",
|
|
icon = "fas fa-external-link-alt"
|
|
})
|
|
|
|
return toast_ui:new(toast.id, title, description, ToastLevel.WARNING, nil, toast.dismissable)
|
|
end
|
|
|
|
-- ###############################################################
|
|
|
|
local function create_forced_community_toast(toast)
|
|
local title = i18n("about.licence")
|
|
local description = i18n("about.forced_community_notification")
|
|
|
|
return toast_ui:new(toast.id, title, description, ToastLevel.WARNING, nil --[[ no action --]] , toast.dismissable)
|
|
end
|
|
|
|
-- ###############################################################
|
|
|
|
local function create_tempdir_toast_ui(toast)
|
|
local title = i18n("warning")
|
|
local description = i18n("about.datadir_warning")
|
|
local action = {
|
|
url = "https://www.ntop.org/support/faq/migrate-the-data-directory-in-ntopng/",
|
|
title = i18n("details.details")
|
|
}
|
|
|
|
return toast_ui:new(toast.id, title, description, ToastLevel.WARNING, action, toast.dismissable)
|
|
end
|
|
|
|
-- ###############################################################
|
|
|
|
local function create_update_ntopng_toast(toast, body)
|
|
|
|
local title = i18n("update")
|
|
return toast_ui:new(toast.id, title, body, ToastLevel.INFO, nil, toast.dismissable)
|
|
end
|
|
|
|
-- ###############################################################
|
|
|
|
local function create_mirrored_traffic_toast(toast, body)
|
|
local title = i18n("mirrored_traffic")
|
|
return toast_ui:new(toast.id, title, body, ToastLevel.INFO, nil, toast.dismissable)
|
|
end
|
|
|
|
-- ###############################################################
|
|
|
|
local function create_flows_limit_exceeded_toast(toast, level)
|
|
local info = ntop.getInfo()
|
|
local title = i18n("about.flows_limit_exceeded")
|
|
local desc = i18n("about.flows_limit_exceeded_descr", {
|
|
product = info["product"]
|
|
})
|
|
|
|
return toast_ui:new(toast.id, title, desc, level, action, toast.dismissable)
|
|
end
|
|
|
|
-- ###############################################################
|
|
|
|
local function create_hosts_limit_exceeded_toast(toast, level)
|
|
local info = ntop.getInfo()
|
|
local title = i18n("about.hosts_limit_exceeded")
|
|
local desc = i18n("about.hosts_limit_exceeded_descr", {
|
|
product = info["product"]
|
|
})
|
|
|
|
return toast_ui:new(toast.id, title, desc, level, action, toast.dismissable)
|
|
end
|
|
|
|
-- ###############################################################
|
|
|
|
local function create_too_many_flows_toast(toast, level, is_db_view)
|
|
local info = ntop.getInfo()
|
|
local title = i18n("too_many_flows")
|
|
|
|
local desc
|
|
if is_db_view then
|
|
desc = i18n("about.you_have_too_many_flows_db", { product = info["product"] })
|
|
else
|
|
desc = i18n("about.you_have_too_many_flows", { product = info["product"] })
|
|
end
|
|
|
|
local action = {
|
|
url = "#",
|
|
additional_classes = "toast-config-change-flows",
|
|
title = i18n("alert_messages.too_many_flows_title"),
|
|
js = "toast-config-change-flows.js",
|
|
dialog = {
|
|
id = 'toast-config-change-modal-flows',
|
|
action = 'toastConfigFlowChanges()',
|
|
title = i18n("too_many_flows"),
|
|
err_msg = i18n("alert_messages.too_many_flows_err"),
|
|
message = i18n("alert_messages.too_many_flows_details"),
|
|
custom_alert_class = 'alert alert-danger',
|
|
confirm = i18n('increase_num_flows_hosts'),
|
|
confirm_button = 'btn-danger'
|
|
}
|
|
}
|
|
|
|
return toast_ui:new(toast.id, title, desc, level, action, toast.dismissable)
|
|
end
|
|
|
|
-- ###############################################################
|
|
|
|
local function create_too_many_hosts_toast(toast, level)
|
|
local info = ntop.getInfo()
|
|
local title = i18n("too_many_hosts")
|
|
local desc = i18n("about.you_have_too_many_hosts", {
|
|
product = info["product"]
|
|
})
|
|
local action = {
|
|
url = "#",
|
|
additional_classes = "toast-config-change-hosts",
|
|
title = i18n("alert_messages.too_many_hosts_title"),
|
|
js = "toast-config-change-hosts.js",
|
|
dialog = {
|
|
id = 'toast-config-change-modal-hosts',
|
|
action = 'toastConfigHostChanges()',
|
|
title = i18n("too_many_hosts"),
|
|
err_msg = i18n("alert_messages.too_many_hosts_err"),
|
|
message = i18n("alert_messages.too_many_hosts_details"),
|
|
custom_alert_class = 'alert alert-danger',
|
|
confirm = i18n('increase_num_flows_hosts'),
|
|
confirm_button = 'btn-danger'
|
|
}
|
|
}
|
|
|
|
return toast_ui:new(toast.id, title, desc, level, action, toast.dismissable)
|
|
end
|
|
|
|
-- ###############################################################
|
|
|
|
local function create_remote_probe_clock_drift_toast(toast, level, local_time, remote_time)
|
|
local title = i18n("remote_probe_clock_drift")
|
|
local desc = i18n("about.you_need_to_sync_remote_probe_time", {
|
|
url = ntop.getHttpPrefix() .. "/lua/if_stats.lua"
|
|
})
|
|
local desc_times = i18n("about.remote_probe_times", {
|
|
local_time = formatEpoch(local_time),
|
|
remote_time = formatEpoch(remote_time)
|
|
})
|
|
desc = string.format("%s<br>%s.", desc, desc_times)
|
|
|
|
return toast_ui:new(toast.id, title, desc, level, nil, toast.dismissable)
|
|
end
|
|
|
|
-- ##################################################################
|
|
|
|
local function create_flow_dump_toast_ui(toast)
|
|
local title = i18n("flow_dump_not_working_title")
|
|
local description = i18n("flow_dump_not_working", {
|
|
icon = "fas fa-external-link-alt"
|
|
})
|
|
|
|
return toast_ui:new(toast.id, title, description, ToastLevel.WARNING, nil, toast.dismissable)
|
|
end
|
|
|
|
-- ##################################################################
|
|
|
|
---
|
|
local function create_restart_required_toast(toast, description)
|
|
local title = i18n("restart.restart_required")
|
|
local action = nil
|
|
-- only the ntop packed can be restarted
|
|
if IS_ADMIN and ntop.isPackage() and not ntop.isWindows() then
|
|
action = {
|
|
title = i18n("restart.restart_now"),
|
|
additional_classes = "restart-service",
|
|
url = "#"
|
|
}
|
|
end
|
|
|
|
return toast_ui:new(toast.id, title, description, ToastLevel.DANGER, action, toast.dismissable)
|
|
|
|
end
|
|
|
|
-- ##################################################################
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.restart_required(toast, container)
|
|
|
|
if not IS_ADMIN then
|
|
return
|
|
end
|
|
|
|
if prefs.active_monitoring_pref and not prefs.active_monitoring then
|
|
table.insert(container, create_restart_required_toast(toast, i18n("prefs.restart_needed", { product = info.product })))
|
|
end
|
|
|
|
if not ntop.isPackage() or ntop.isWindows() then
|
|
return
|
|
end
|
|
|
|
-- ifname is defined globally
|
|
local delete_active_interface_requested = delete_data_utils.delete_active_interface_data_requested(ifname)
|
|
if delete_active_interface_requested then
|
|
table.insert(container, create_restart_required_toast(toast, i18n("delete_data.restart_product_toast", {
|
|
product = ntop.getInfo()["product"]
|
|
})))
|
|
end
|
|
if prefs_factory_reset_utils.is_prefs_factory_reset_requested() then
|
|
table.insert(container, create_restart_required_toast(toast, i18n("manage_configurations.after_reset_request", {
|
|
product = ntop.getInfo()["product"]
|
|
})))
|
|
end
|
|
if configuration_utils.restart_required() then
|
|
table.insert(container, create_restart_required_toast(toast, i18n("manage_configurations.restart_request", {
|
|
product = ntop.getInfo()["product"]
|
|
})))
|
|
end
|
|
end
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.DHCP(toast, container)
|
|
if not IS_ADMIN then
|
|
return
|
|
end
|
|
|
|
-- In System Interface we can't collect the required data
|
|
if (IS_SYSTEM_INTERFACE) then
|
|
return false
|
|
end
|
|
|
|
local ifs = interface.getStats()
|
|
|
|
if (not (ifs.has_seen_dhcp_addresses and IS_ADMIN and (not IS_PCAP_DUMP) and IS_PACKET_INTERFACE)) then
|
|
return
|
|
end
|
|
|
|
local lbd_serialize_by_mac = (_POST["lbd_hosts_as_macs"] == "1") or
|
|
(ntop.getPref(
|
|
string.format("ntopng.prefs.ifid_%u.serialize_local_broadcast_hosts_as_macs", ifs.id)) == "1")
|
|
|
|
if (not lbd_serialize_by_mac) and
|
|
(ntop.getPref(string.format("ntopng.prefs.ifid_%u.disable_host_identifier_message", ifs.id)) ~= "1") then
|
|
|
|
table.insertIfNotPresent(container, create_DCHP_monitoring_toast(toast), function(n)
|
|
return n.id == toast.id
|
|
end)
|
|
|
|
elseif isEmptyString(_POST["dhcp_ranges"]) then
|
|
|
|
local dhcp_utils = require("dhcp_utils")
|
|
local ranges = dhcp_utils.listRanges(ifs.id)
|
|
|
|
if (table.empty(ranges)) then
|
|
table.insertIfNotPresent(container, create_DHCP_range_missing_toast(toast), function(n)
|
|
return n.id == toast.id
|
|
end)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
--- Create an instance for the geoip alert toast
|
|
--- if the user doesn't have geoIP installed
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.geo_ip(toast, container)
|
|
if IS_ADMIN and not ntop.hasGeoIP() then
|
|
table.insert(container, create_geo_ip_toast_ui(toast))
|
|
end
|
|
end
|
|
|
|
--- Create an instance for the temp working directory alert
|
|
--- if ntopng is running inside /var/tmp
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.temp_working_dir(toast, container)
|
|
if not IS_ADMIN or ntop.isWindows() then
|
|
return
|
|
end
|
|
|
|
if (dirs.workingdir == "/var/tmp/ntopng") then
|
|
table.insert(container, create_tempdir_toast_ui(toast))
|
|
end
|
|
end
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.update_ntopng(toast, container)
|
|
if not IS_ADMIN or not ntop.isPackage() or ntop.isWindows() then
|
|
return
|
|
end
|
|
|
|
-- check if ntopng is oem and the user is an Administrator
|
|
local is_not_oem_and_administrator = IS_ADMIN and not info.oem
|
|
local message = check_latest_major_release()
|
|
|
|
if is_not_oem_and_administrator and not isEmptyString(message) then
|
|
table.insert(container, create_update_ntopng_toast(toast, message))
|
|
end
|
|
end
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.mirrored_traffic(toast, container)
|
|
if not IS_ADMIN or not IS_PACKET_INTERFACE or ntop.isWindows() then
|
|
return
|
|
end
|
|
|
|
-- check if ntopng is oem and the user is an Administrator
|
|
local is_not_oem_and_administrator = IS_ADMIN and not info.oem
|
|
local ifstats = interface.getStats()
|
|
local message = i18n('check_mirrored_traffic', {
|
|
id = ifstats.id
|
|
})
|
|
local upload_traffic = 0
|
|
local download_traffic = 0
|
|
local num_tot_traffic_points = 0
|
|
|
|
for _, traffic in pairs(ifstats.download_stats) do
|
|
download_traffic = download_traffic + traffic
|
|
if traffic > 0 then
|
|
num_tot_traffic_points = num_tot_traffic_points + 1
|
|
end
|
|
end
|
|
|
|
for _, traffic in pairs(ifstats.upload_stats) do
|
|
upload_traffic = upload_traffic + traffic
|
|
end
|
|
|
|
local is_mirrored_traffic_pref = ntop.getPref(string.format("ntopng.prefs.ifid_%d.is_traffic_mirrored",
|
|
interface.getId())) == "0"
|
|
|
|
if is_not_oem_and_administrator and not isEmptyString(message) and (num_tot_traffic_points > 10) and
|
|
(upload_traffic == 0 and download_traffic > 0 and is_mirrored_traffic_pref) then
|
|
table.insert(container, create_mirrored_traffic_toast(toast, message))
|
|
end
|
|
end
|
|
|
|
-- ###############################################
|
|
|
|
local function create_emergency_notifications_toast(toast, message)
|
|
local title = i18n("emergency_notifications")
|
|
return toast_ui:new(toast.id, title, message, ToastLevel.INFO, nil, toast.dismissable)
|
|
end
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.emergency_recipient(toast, container)
|
|
if not IS_ADMIN then
|
|
return
|
|
end
|
|
|
|
-- check if ntopng is oem and the user is an Administrator
|
|
local is_not_oem_and_administrator = IS_ADMIN and not info.oem
|
|
local message = i18n('check_emergency_recipient', {
|
|
http_prefix = ntop.getHttpPrefix()
|
|
})
|
|
local emergency_recipient_configured = false
|
|
|
|
for _, recipient in pairs(recipients.get_all_recipients()) do
|
|
if recipients.isAlertsRecipient(recipient) and
|
|
not recipients.isBuiltinRecipient(recipient) and
|
|
recipient.minimum_severity >= alert_severities.notice.severity_id then
|
|
emergency_recipient_configured = true
|
|
break
|
|
end
|
|
end
|
|
|
|
if is_not_oem_and_administrator and not isEmptyString(message) and not emergency_recipient_configured then
|
|
table.insert(container, create_emergency_notifications_toast(toast, message))
|
|
return true
|
|
end
|
|
end
|
|
|
|
-- ###############################################
|
|
|
|
local function hide_warn_on_limit_exceeeded()
|
|
return ntop.getCache("ntopng.prefs.ignore_limit_exceeded") == "1"
|
|
end
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.flows_limit_exceeded(toast, container)
|
|
if not IS_ADMIN then
|
|
return
|
|
end
|
|
|
|
-- In System Interface we can't get the flows number from `interface.getNumFlows()`
|
|
if (IS_SYSTEM_INTERFACE) then
|
|
return
|
|
end
|
|
|
|
if hide_warn_on_limit_exceeeded() then
|
|
return
|
|
end
|
|
|
|
local level = nil
|
|
local flows = interface.getNumFlows()
|
|
|
|
if flows > prefs.flows_limit then
|
|
level = ToastLevel.WARNING
|
|
end
|
|
|
|
if (level ~= nil) then
|
|
table.insert(container, create_flows_limit_exceeded_toast(toast, level))
|
|
end
|
|
end
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.hosts_limit_exceeded(toast, container)
|
|
if not IS_ADMIN then
|
|
return
|
|
end
|
|
|
|
-- In System Interface we can't get the hosts number from `interface.getNumHosts()`
|
|
if (IS_SYSTEM_INTERFACE) then
|
|
return
|
|
end
|
|
|
|
if hide_warn_on_limit_exceeeded() then
|
|
return
|
|
end
|
|
|
|
local level = nil
|
|
local hosts = interface.getNumLocalHosts()
|
|
|
|
local rxonly_hosts = interface.getNumLocalRxOnlyHosts()
|
|
if hosts >= rxonly_hosts then -- Safety check
|
|
hosts = hosts - rxonly_hosts -- Do not consider non-existing hosts
|
|
end
|
|
|
|
if hosts > prefs.hosts_limit then
|
|
level = ToastLevel.WARNING
|
|
end
|
|
|
|
if (level ~= nil) then
|
|
table.insert(container, create_hosts_limit_exceeded_toast(toast, level))
|
|
end
|
|
end
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.too_many_hosts(toast, container)
|
|
if not IS_ADMIN or not ntop.isPackage() or ntop.isWindows() then
|
|
return
|
|
end
|
|
|
|
-- In System Interface we can't get the hosts number from `interface.getNumHosts()`
|
|
if (IS_SYSTEM_INTERFACE) then
|
|
return
|
|
end
|
|
|
|
local level = nil
|
|
local hosts = interface.getNumHosts()
|
|
local hosts_pctg = math.floor(1 + ((hosts * 100) / prefs.max_num_hosts))
|
|
|
|
if (hosts_pctg >= ALARM_THRESHOLD_LOW and hosts_pctg <= ALARM_THRESHOLD_HIGH) then
|
|
level = ToastLevel.WARNING
|
|
elseif (hosts_pctg > ALARM_THRESHOLD_HIGH) then
|
|
level = ToastLevel.DANGER
|
|
end
|
|
|
|
if (level ~= nil) then
|
|
table.insert(container, create_too_many_hosts_toast(toast, level))
|
|
end
|
|
end
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.too_many_flows(toast, container)
|
|
if not IS_ADMIN or not ntop.isPackage() or ntop.isWindows() then
|
|
return
|
|
end
|
|
|
|
-- In System Interface we can't get the flows number from `interface.getNumFlows()`
|
|
if (IS_SYSTEM_INTERFACE) then
|
|
return
|
|
end
|
|
|
|
local level = nil
|
|
local is_db_view = false
|
|
local flows = interface.getNumFlows()
|
|
local max_flows = prefs.max_num_flows
|
|
|
|
local ifstats = interface.getStats()
|
|
if ifstats['type'] == 'db' then
|
|
is_db_view = true
|
|
if flows >= max_flows then
|
|
level = ToastLevel.WARNING
|
|
end
|
|
else
|
|
local flows_pctg = math.floor(1 + ((flows * 100) / prefs.max_num_flows))
|
|
|
|
if (flows_pctg >= ALARM_THRESHOLD_LOW and flows_pctg <= ALARM_THRESHOLD_HIGH) then
|
|
level = ToastLevel.WARNING
|
|
elseif (flows_pctg > ALARM_THRESHOLD_HIGH) then
|
|
level = ToastLevel.DANGER
|
|
end
|
|
end
|
|
|
|
if (level ~= nil) then
|
|
table.insert(container, create_too_many_flows_toast(toast, level, is_db_view))
|
|
end
|
|
end
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.remote_probe_clock_drift(toast, container)
|
|
if not IS_ADMIN then
|
|
return
|
|
end
|
|
|
|
-- In System Interface we can't collect the stats from `interface.getStats()`
|
|
if (IS_SYSTEM_INTERFACE) then
|
|
return
|
|
end
|
|
|
|
local ifstats = interface.getStats()
|
|
|
|
if ifstats["probe.remote_time"] ~= nil and ifstats["probe.local_time"] ~= nil then
|
|
|
|
local tdiff = math.abs(ifstats["probe.local_time"] - ifstats["probe.remote_time"])
|
|
local level = nil
|
|
|
|
if (tdiff >= 10 and tdiff <= 30) then
|
|
level = ToastLevel.WARNING
|
|
elseif (tdiff > 30) then
|
|
level = ToastLevel.DANGER
|
|
end
|
|
|
|
if (level ~= nil) then
|
|
table.insert(container, create_remote_probe_clock_drift_toast(toast, level, ifstats["probe.local_time"],
|
|
ifstats["probe.remote_time"]))
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Create an instance for the flow dump alert toast
|
|
--- if flow dump is not able to start/run/dump
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.flow_dump(toast, container)
|
|
if not IS_ADMIN or not ntop.isPackage() or ntop.isWindows() then
|
|
return
|
|
end
|
|
|
|
-- In System Interface we can't collect the stats from `interface.getStats()`
|
|
if (IS_SYSTEM_INTERFACE) then
|
|
return
|
|
end
|
|
|
|
local ifstats = interface.getStats()
|
|
|
|
if IS_ADMIN and prefs.is_dump_flows_enabled and prefs.is_dump_flows_runtime_enabled and
|
|
not ifstats.isFlowDumpDisabled and not ifstats.isFlowDumpRunning and not ifstats.isViewed then
|
|
|
|
table.insert(container, create_flow_dump_toast_ui(toast))
|
|
end
|
|
end
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.about_page(toast, container)
|
|
if not IS_ADMIN then
|
|
return
|
|
end
|
|
|
|
if (_POST["ntopng_license"] ~= nil) then
|
|
|
|
ntop.setCache('ntopng.license', trimSpace(_POST["ntopng_license"]))
|
|
ntop.checkLicense()
|
|
|
|
info = ntop.getInfo()
|
|
|
|
if (info["version.enterprise_l_edition"] and info["pro.license"] ~= "") then
|
|
table.insert(container,
|
|
toast_ui:new(toast.id, i18n("info"), i18n("about.create_license_l"), ToastLevel.INFO, {
|
|
url = "https://www.ntop.org/support/faq/what-is-included-in-ntopng-enterprise-l/",
|
|
title = i18n("details.details")
|
|
}))
|
|
elseif (not info["version.enterprise_l_edition"] and info["pro.license"] ~= "") then
|
|
table.insert(container, toast_ui:new(toast.id, i18n("info"), i18n("about.create_license"), ToastLevel.INFO))
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.hosts_geomap(toast, container)
|
|
if not IS_ADMIN then
|
|
return
|
|
end
|
|
|
|
local hosts_stats = interface.getHostsInfo()
|
|
local num = hosts_stats["numHosts"]
|
|
if (num > 0) then
|
|
table.insert(container, toast_ui:new(toast.id, i18n("warning"), i18n("geo_map.warning_accuracy"),
|
|
ToastLevel.WARNING, nil, toast.dismissable))
|
|
end
|
|
end
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.vulnerability_scan(toast, container)
|
|
if not IS_ADMIN then
|
|
return
|
|
end
|
|
|
|
if not ntop.isClickHouseEnabled() then
|
|
local body = i18n("hosts_stats.page_scan_hosts.enable_clickhouse_toast_label", {
|
|
link = "https://www.ntop.org/guides/ntopng/user_interface/network_interface/monitoring/vulnerability_scan.html#scan-reports"
|
|
})
|
|
table.insert(container, toast_ui:new(toast.id, i18n("info"),body,
|
|
ToastLevel.INFO, nil, toast.dismissable))
|
|
end
|
|
end
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.service_map(toast, container)
|
|
local service_map_status = interface.serviceMapLearningStatus()
|
|
if ntop.isEnterprise() and service_map_status.service_map_learning_status == true then
|
|
local body = i18n("map_page.learning")
|
|
table.insert(container, toast_ui:new(toast.id, i18n("info"),body,
|
|
ToastLevel.INFO, nil, toast.dismissable))
|
|
end
|
|
end
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.enable_exporters_timeseries(toast, container)
|
|
if ntop.isEnterprise() and not areFlowdevTimeseriesEnabled() then
|
|
local body = i18n("prefs.enable_exporters_timeseries", {
|
|
link = ntop.getHttpPrefix() .. "/lua/admin/prefs.lua?tab=on_disk_ts"
|
|
})
|
|
table.insert(container, toast_ui:new(toast.id, i18n("info"),body,
|
|
ToastLevel.INFO, nil, toast.dismissable))
|
|
end
|
|
end
|
|
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.configured_exporters_timeseries_resolution(toast, container)
|
|
local service_map_status = interface.serviceMapLearningStatus()
|
|
if ntop.isEnterprise() and areFlowdevTimeseriesEnabled() and not highExporterTimeseriesResolution() then
|
|
local body = i18n("prefs.change_exporter_timeseries_resolution", {
|
|
link = ntop.getHttpPrefix() .. "/lua/admin/prefs.lua?tab=on_disk_ts"
|
|
})
|
|
table.insert(container, toast_ui:new(toast.id, i18n("info"),body,
|
|
ToastLevel.INFO, nil, toast.dismissable))
|
|
end
|
|
end
|
|
|
|
|
|
--- This is a simple placeholder for new toasts
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.empty(toast, container)
|
|
-- do nothing!
|
|
end
|
|
|
|
--- Generate two toasts if the SNMP ratio is not available for probes
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.exporters_SNMP_ratio_column(toast, container)
|
|
if not IS_ADMIN or not ntop.isPro() then
|
|
return
|
|
end
|
|
|
|
local snmp_utils = require "snmp_utils"
|
|
local snmp_cached_dev = require "snmp_cached_dev"
|
|
|
|
local flow_device_ip = _GET["ip"]
|
|
if (isEmptyString(flow_device_ip)) then
|
|
return
|
|
end
|
|
|
|
local cached_device = snmp_cached_dev:create(flow_device_ip)
|
|
|
|
local is_ratio_available = snmp_utils.is_snmp_ratio_available(cached_device)
|
|
|
|
if (is_ratio_available) then
|
|
return
|
|
end
|
|
|
|
local title = i18n("flow_devices.enable_flow_ratio")
|
|
local body
|
|
local action = nil
|
|
|
|
-- Did the user add the device to the SNMP overview page?
|
|
local device_exists = not (cached_device == nil)
|
|
-- Are SNMP Timeseries enabled?
|
|
local snmp_dev_creation = ntop.getPref("ntopng.prefs.snmp_devices_rrd_creation") == "1"
|
|
-- Are Flow Probes Timseries enabled?
|
|
local flow_dev_creation = ntop.getPref("ntopng.prefs.flow_device_port_rrd_creation") == "1"
|
|
|
|
if not device_exists then
|
|
-- Build the message to show
|
|
local message_snmp = i18n("flow_devices.flow_ratio_snmp_instructions", {
|
|
href = ntop.getHttpPrefix() .. "/lua/pro/enterprise/snmpdevices_stats.lua"
|
|
})
|
|
|
|
body = message_snmp
|
|
|
|
elseif not flow_dev_creation or not snmp_dev_creation then
|
|
-- Build the message to show
|
|
local message_timeseries = i18n("flow_devices.flow_ratio_timeseries_instructions")
|
|
|
|
body = message_timeseries
|
|
action = {
|
|
url = ntop.getHttpPrefix() .. "/lua/admin/prefs.lua?tab=on_disk_ts",
|
|
title = i18n("enable_them")
|
|
}
|
|
end
|
|
|
|
if body then
|
|
table.insert(container, toast_ui:new(toast.id, title, body, ToastLevel.INFO, action, toast.dismissable))
|
|
end
|
|
|
|
end
|
|
|
|
-- ###############################################
|
|
|
|
--- Create an instance for forced community toast
|
|
--- @param toast table The toast is the logic model defined in defined_toasts
|
|
--- @param container table Is the table where to put the new toast ui
|
|
function predicates.forced_community(toast, container)
|
|
if (ntop.getInfo()["pro.forced_community"] and ntop.exists("/etc/ntopng.license")) then
|
|
table.insert(container, create_forced_community_toast(toast))
|
|
end
|
|
end
|
|
|
|
-- ###############################################
|
|
|
|
--- Did the user create an non builtin endpoint?
|
|
local function user_has_created_endpoint()
|
|
return ntop.getCache(endpoint_configs.FIRST_ENDPOINT_CREATED_CACHE_KEY) == "1"
|
|
end
|
|
|
|
--- Did the user create a recipient?
|
|
local function user_has_created_recipient()
|
|
return ntop.getCache(recipients_manager.FIRST_RECIPIENT_CREATED_CACHE_KEY) == "1"
|
|
end
|
|
|
|
--- Generate a toast to adive the user about toast endpoints
|
|
function predicates.create_endpoint(toast, container)
|
|
if (not IS_ADMIN) then
|
|
return
|
|
end
|
|
if (user_has_created_endpoint()) then
|
|
return
|
|
end
|
|
if IS_PCAP_DUMP then
|
|
return
|
|
end
|
|
|
|
local title = i18n("endpoint_notifications.hints.create_endpoint.title")
|
|
local body = i18n("endpoint_notifications.hints.create_endpoint.body", {
|
|
link = "https://www.ntop.org/guides/ntopng/user_interface/shared/alerts/available_endpoints.html#available-endpoints"
|
|
})
|
|
local action = {
|
|
title = i18n("endpoint_notifications.hints.create_endpoint.action"),
|
|
url = ntop.getHttpPrefix() .. "/lua/admin/endpoint_notifications_list.lua"
|
|
}
|
|
|
|
local hint = toast_ui:new(toast.id, title, body, ToastLevel.INFO, action, toast.dismissable)
|
|
|
|
table.insert(container, hint)
|
|
|
|
end
|
|
|
|
-- Generate a second toast to inform the user to create a recipient for the new endpoint
|
|
function predicates.create_recipients_for_endpoint(toast, container)
|
|
if (not IS_ADMIN) then
|
|
return
|
|
end
|
|
if IS_PCAP_DUMP then
|
|
return
|
|
end
|
|
|
|
-- Did the user created a new endpoint? If not then return
|
|
if (not user_has_created_endpoint()) then
|
|
return
|
|
end
|
|
-- Did the user created the new recipient? If yes then return
|
|
if (user_has_created_recipient()) then
|
|
return
|
|
end
|
|
|
|
local title = i18n("endpoint_notifications.hints.create_recipients.title")
|
|
local body = i18n("endpoint_notifications.hints.create_recipients.body", {
|
|
link = "https://www.ntop.org/guides/ntopng/user_interface/shared/alerts/available_endpoints.html#available-endpoints"
|
|
})
|
|
local action = {
|
|
url = ntop.getHttpPrefix() .. "/lua/admin/endpoint_notifications_list.lua?subdir=recipient",
|
|
title = i18n("create")
|
|
}
|
|
|
|
local hint = toast_ui:new(toast.id, title, body, ToastLevel.INFO, action, toast.dismissable)
|
|
table.insert(container, hint)
|
|
end
|
|
|
|
--- Check if unexpected scripts are disabled and notifiy the user
|
|
--- about their existance
|
|
function predicates.unexpected_scripts(toast, container)
|
|
if (not IS_ADMIN) then
|
|
return
|
|
end
|
|
if not isEmptyString(ntop.getCache(UNEXPECTED_SCRIPTS_ENABLED_CACHE_KEY)) then
|
|
return
|
|
end
|
|
|
|
local url = ntop.getHttpPrefix() .. "/lua/admin/edit_configset.lua?subdir=flow&search_script=unexpected#disabled"
|
|
|
|
-- TODO: missing documentation links
|
|
local title = i18n("checks.hint.title")
|
|
local body = i18n("checks.hint.body", {
|
|
link_DHCP = "https://ntop.org",
|
|
link_SMTP = "https://ntop.org",
|
|
link_DNS = "https://ntop.org",
|
|
link_NTP = "https://ntop.org",
|
|
product = info["product"]
|
|
})
|
|
local action = {
|
|
url = url,
|
|
title = i18n("configure")
|
|
}
|
|
|
|
local hint = toast_ui:new(toast.id, title, body, ToastLevel.INFO, action, toast.dismissable)
|
|
table.insert(container, hint)
|
|
end
|
|
|
|
-- ###############################################
|
|
|
|
function predicates.export_drops(toast, container)
|
|
if (IS_SYSTEM_INTERFACE) then
|
|
return
|
|
end
|
|
|
|
if ntop.isClickHouseEnabled() then
|
|
local ifstats = interface.getStats()
|
|
local total_flows = ifstats.stats_since_reset.db.flow_export_count
|
|
local flow_export_drops = ifstats.stats_since_reset.db.flow_export_drops
|
|
local severity = ToastLevels[stats_utils.get_severity_by_export_drops(flow_export_drops, total_flows)]
|
|
|
|
-- for the info severity don't show anything
|
|
if severity == ToastLevels.INFO then
|
|
return
|
|
end
|
|
|
|
local body = i18n("about.too_many_exports", {
|
|
product = "<b>" .. info.product .. "</b>"
|
|
})
|
|
local hint = toast_ui:new(toast.id, i18n("too_many_exports"), body, severity, nil, toast.dismissable)
|
|
table.insert(container, hint)
|
|
end
|
|
|
|
end
|
|
|
|
-- ###############################################
|
|
|
|
return predicates
|