EVOLUTION-MANAGER
Edit File: predicates_defined_toasts.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_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.flows_limit_reached", { 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('double_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('double_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 or 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 -- ############################################### --- @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 local is_clickhouse_enable = ntop.isClickHouseEnabled() if (not is_clickhouse_enable) then local body = i18n("hosts_stats.page_scan_hosts.enable_clickhouse_toast_label", { link = "https://www.ntop.org/guides/ntopng/vulnerability_scan/index.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 --- 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/scripts/alert_endpoints.html" }) 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/scripts/alert_endpoints.html" }) 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 local is_dump_flows_enabled = prefs.is_dump_flows_enabled if is_dump_flows_enabled then local ifstats = interface.getStats() local total_flows = ifstats.stats_since_reset.flow_export_count local flow_export_drops = ifstats.stats_since_reset.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 -- ############################################### --- Create an instance for the obsolete nindex support function predicates.obsolete_nindex(toast, container) if not IS_ADMIN then return end if prefs.is_nindex_enabled then local title = i18n("obsolete_nindex") local body = i18n("obsolete_nindex_message") local hint = toast_ui:new(toast.id, title, body, ToastLevel.WARNING, nil, toast.dismissable) table.insert(container, hint) end end -- ############################################### --- Create an instance for the obsolete MySQL support function predicates.obsolete_mysql(toast, container) if not IS_ADMIN then return end if prefs.is_dump_flows_to_mysql_enabled and not ntop.isClickHouseEnabled() then local title = i18n("obsolete_mysql") local body = i18n("obsolete_mysql_message") local hint = toast_ui:new(toast.id, title, body, ToastLevel.WARNING, nil, toast.dismissable) table.insert(container, hint) end end -- ############################################### return predicates