mirror of
https://github.com/ntop/ntopng.git
synced 2026-04-29 23:49:33 +00:00
368 lines
13 KiB
Lua
368 lines
13 KiB
Lua
--
|
|
-- (C) 2019 - ntop.org
|
|
--
|
|
|
|
-- Check modules provide a scriptable way to interact with the ntopng
|
|
-- core. Users can provide their own modules to trigger custom alerts,
|
|
-- export data, or perform periodic tasks.
|
|
|
|
local os_utils = require("os_utils")
|
|
local json = require("dkjson")
|
|
|
|
local user_scripts = {}
|
|
|
|
-- ##############################################
|
|
|
|
user_scripts.field_units = {
|
|
seconds = "field_units.seconds",
|
|
bytes = "field_units.bytes",
|
|
flows = "field_units.flows",
|
|
packets = "field_units.packets",
|
|
mbits = "field_units.mbits",
|
|
hosts = "field_units.hosts",
|
|
syn_sec = "field_units.syn_sec",
|
|
flow_sec = "field_units.flow_sec",
|
|
percentage = "field_units.percentage",
|
|
}
|
|
|
|
local CHECK_MODULES_BASEDIR = dirs.installdir .. "/scripts/callbacks/interface"
|
|
local CHECK_MODULES_PRO_BASEDIR = dirs.installdir .. "/pro/scripts/callbacks/interface"
|
|
|
|
-- Hook points for flow/periodic modules
|
|
-- NOTE: keep in sync with the Documentation
|
|
local FLOW_HOOKS = {"protocolDetected", "statusChanged", "flowEnd", "periodicUpdate"}
|
|
local PERIODIC_HOOKS = {"min", "5mins", "hour", "day"}
|
|
|
|
-- ##############################################
|
|
|
|
function user_scripts.getSubdirectoryPath(subdir)
|
|
return os_utils.fixPath(CHECK_MODULES_BASEDIR .. "/" .. subdir)
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
local function getCheckModuleConfHash(ifid, subdir, module_key)
|
|
return string.format("ntopng.prefs.user_scripts.conf.%s.ifid_%d.%s", subdir, ifid, module_key)
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
-- @brief Enables a check module
|
|
function user_scripts.enableModule(ifid, subdir, module_key)
|
|
local hkey = getCheckModuleConfHash(ifid, subdir, module_key)
|
|
ntop.delHashCache(hkey, "disabled")
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
-- @brief Disables a check module
|
|
function user_scripts.disableModule(ifid, subdir, module_key)
|
|
local hkey = getCheckModuleConfHash(ifid, subdir, module_key)
|
|
ntop.setHashCache(hkey, "disabled", "1")
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
-- @brief Checks if a check module is enabled.
|
|
-- @return true if disabled, false otherwise
|
|
-- @notes Modules are neabled by default. The user can manually turn them off.
|
|
function user_scripts.isEnabled(ifid, subdir, module_key)
|
|
local hkey = getCheckModuleConfHash(ifid, subdir, module_key)
|
|
return(ntop.getHashCache(hkey, "disabled") ~= "1")
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
local function flow_user_scripts_benchmarks_key(mod_k)
|
|
local ifid = interface.getId()
|
|
|
|
return string.format("ntopng.cache.ifid_%d.flow_user_scripts_benchmarks.mod_%s", ifid, mod_k)
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
-- Load previous benchmark infor
|
|
local function getFlowBenchmarks(mod_k)
|
|
local k = flow_user_scripts_benchmarks_key(mod_k)
|
|
-- ntop.delCache(k)
|
|
local res = ntop.getHashAllCache(k)
|
|
|
|
for mod_fn, benchmark in pairs(res or {}) do
|
|
res[mod_fn] = json.decode(benchmark)
|
|
end
|
|
|
|
return res
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
-- @brief Save flow.lua benchmarks results
|
|
function user_scripts.storeFlowBenchmarks(benchmarks)
|
|
for mod_k, modules in pairs(benchmarks or {}) do
|
|
local k = flow_user_scripts_benchmarks_key(mod_k)
|
|
|
|
|
|
for mod_fn, mod_benchmark in pairs(modules) do
|
|
ntop.setHashCache(k, mod_fn, json.encode(mod_benchmark))
|
|
end
|
|
end
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
-- @brief Get the default configuration value for the given check module
|
|
-- and granularity.
|
|
-- @param check_module a check_module returned by user_scripts.load
|
|
-- @param granularity_str the target granularity
|
|
-- @return nil if there is not default value, the given value otherwise
|
|
function user_scripts.getDefaultConfigValue(check_module, granularity_str)
|
|
if((check_module.default_values ~= nil) and (check_module.default_values[granularity_str] ~= nil)) then
|
|
-- granularity specific default
|
|
return(check_module.default_values[granularity_str])
|
|
end
|
|
|
|
-- global default
|
|
return(check_module.default_value)
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
-- @brief Load the check modules.
|
|
-- @params ifid the interface ID
|
|
-- @params subdir the modules subdir
|
|
-- @params hook_filter if non nil, only load the check modules for the specified hook
|
|
-- @params ignore_disabled if true, also returns disabled check modules
|
|
-- @return {modules = key->check_module, hooks = check_module->function}
|
|
function user_scripts.load(ifid, subdir, hook_filter, ignore_disabled)
|
|
local rv = {modules = {}, hooks = {}}
|
|
local is_nedge = ntop.isnEdge()
|
|
|
|
local check_dirs = {
|
|
CHECK_MODULES_BASEDIR .. "/" .. subdir,
|
|
CHECK_MODULES_BASEDIR .. "/" .. subdir .. "/alerts",
|
|
}
|
|
|
|
if ntop.isPro() then
|
|
check_dirs[#check_dirs + 1] = CHECK_MODULES_PRO_BASEDIR .. "/" .. subdir
|
|
check_dirs[#check_dirs + 1] = CHECK_MODULES_PRO_BASEDIR .. "/" .. subdir .. "/alerts"
|
|
end
|
|
|
|
-- Load hook table keys
|
|
local available_hooks = ternary(subdir == "flow", FLOW_HOOKS, PERIODIC_HOOKS)
|
|
|
|
for _, hook in pairs(available_hooks) do
|
|
rv.hooks[hook] = {}
|
|
end
|
|
|
|
for _, checks_dir in pairs(check_dirs) do
|
|
checks_dir = os_utils.fixPath(checks_dir)
|
|
package.path = checks_dir .. "/?.lua;" .. package.path
|
|
|
|
local is_alert_path = string.ends(checks_dir, "alerts")
|
|
|
|
for fname in pairs(ntop.readdir(checks_dir)) do
|
|
if ends(fname, ".lua") then
|
|
local mod_fname = string.sub(fname, 1, string.len(fname) - 4)
|
|
local check_module = require(mod_fname)
|
|
local setup_ok = true
|
|
|
|
traceError(TRACE_DEBUG, TRACE_CONSOLE, string.format("Loading check module '%s'", mod_fname))
|
|
|
|
if(check_module == nil) then
|
|
traceError(TRACE_ERROR, TRACE_CONSOLE, string.format("Loading '%s' failed", checks_dir.."/"..fname))
|
|
end
|
|
|
|
if(check_module.key == nil) then
|
|
traceError(TRACE_ERROR, TRACE_CONSOLE, string.format("Missing 'key' in check module '%s'", mod_fname))
|
|
goto next_module
|
|
end
|
|
|
|
if(rv.modules[check_module.key]) then
|
|
traceError(TRACE_ERROR, TRACE_CONSOLE, string.format("Skipping duplicate module '%s'", check_module.key))
|
|
goto next_module
|
|
end
|
|
|
|
if(check_module.nedge_exclude and is_nedge) then
|
|
goto next_module
|
|
end
|
|
|
|
if(table.empty(check_module.hooks)) then
|
|
traceError(TRACE_WARNING, TRACE_CONSOLE, string.format("No 'hooks' defined in check module '%s'", check_module.key))
|
|
-- This guarantees that the "hooks" field is always available
|
|
check_module.hooks = {}
|
|
end
|
|
|
|
-- Augument with additional attributes
|
|
check_module.enabled = user_scripts.isEnabled(ifid, subdir, check_module.key)
|
|
check_module.is_alert = is_alert_path
|
|
|
|
if((not check_module.enabled) and (not ignore_disabled)) then
|
|
traceError(TRACE_DEBUG, TRACE_CONSOLE, string.format("Skipping disabled module '%s'", check_module.key))
|
|
goto next_module
|
|
end
|
|
|
|
if(hook_filter ~= nil) then
|
|
-- Only return modules which should be called for the specified hook
|
|
if((check_module.hooks[hook_filter] == nil) and (check_module.hooks["all"] == nil)) then
|
|
traceError(TRACE_DEBUG, TRACE_CONSOLE, string.format("Skipping module '%s' for hook '%s'", check_module.key, hook_filter))
|
|
goto next_module
|
|
end
|
|
end
|
|
|
|
-- If a setup function is available, call it
|
|
if(check_module.setup ~= nil) then
|
|
setup_ok = check_module.setup()
|
|
end
|
|
|
|
if(not setup_ok) then
|
|
traceError(TRACE_DEBUG, TRACE_CONSOLE, string.format("Skipping module '%s' as setup() returned %s", check_module.key, setup_ok))
|
|
goto next_module
|
|
end
|
|
|
|
check_module["benchmark"] = getFlowBenchmarks(check_module.key)
|
|
|
|
-- Populate hooks fast lookup table
|
|
for hook, hook_fn in pairs(check_module.hooks) do
|
|
if(hook == "all") then
|
|
-- Register for all the hooks
|
|
for _, hook in pairs(available_hooks) do
|
|
rv.hooks[hook][check_module.key] = hook_fn
|
|
end
|
|
|
|
-- no more hooks allowed
|
|
break
|
|
elseif(rv.hooks[hook] == nil) then
|
|
traceError(TRACE_WARNING, TRACE_CONSOLE, string.format("Unkown hook '%s' in module '%s'", hook, check_module.key))
|
|
else
|
|
rv.hooks[hook][check_module.key] = hook_fn
|
|
end
|
|
end
|
|
|
|
rv.modules[check_module.key] = check_module
|
|
end
|
|
|
|
::next_module::
|
|
end
|
|
end
|
|
|
|
return(rv)
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
function user_scripts.runPeriodicScripts(granularity)
|
|
if(granularity == "min") then
|
|
interface.checkInterfaceAlertsMin()
|
|
interface.checkHostsAlertsMin()
|
|
interface.checkNetworksAlertsMin()
|
|
elseif(granularity == "5mins") then
|
|
interface.checkInterfaceAlerts5Min()
|
|
interface.checkHostsAlerts5Min()
|
|
interface.checkNetworksAlerts5Min()
|
|
elseif(granularity == "hour") then
|
|
interface.checkInterfaceAlertsHour()
|
|
interface.checkHostsAlertsHour()
|
|
interface.checkNetworksAlertsHour()
|
|
elseif(granularity == "day") then
|
|
interface.checkInterfaceAlertsDay()
|
|
interface.checkHostsAlertsDay()
|
|
interface.checkNetworksAlertsDay()
|
|
else
|
|
traceError(TRACE_ERROR, TRACE_CONSOLE, "Unknown granularity " .. granularity)
|
|
end
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
local function build_on_off_toggle(submit_field, active)
|
|
local on_value = "on"
|
|
local off_value = "off"
|
|
local value
|
|
local on_color = "success"
|
|
local off_color = "danger"
|
|
|
|
local on_active
|
|
local off_active
|
|
|
|
if active then
|
|
|
|
value = on_value
|
|
on_active = "btn-"..on_color.." active"
|
|
off_active = "btn-default"
|
|
else
|
|
value = off_value
|
|
on_active = "btn-default"
|
|
off_active = "btn-"..off_color.." active"
|
|
end
|
|
|
|
return [[
|
|
<div class="btn-group btn-toggle">
|
|
<button type="button" onclick="]]..submit_field..[[_on_fn()" id="]]..submit_field..[[_on_id" class="btn btn-sm ]]..on_active..[[">On</button>
|
|
<button type="button" onclick="]]..submit_field..[[_off_fn()" id="]]..submit_field..[[_off_id" class="btn btn-sm ]]..off_active..[[">Off</button>
|
|
</div>
|
|
<input type=hidden id="]]..submit_field..[[_input" name="]]..submit_field..[[" value="]]..value..[["/>
|
|
<script>
|
|
|
|
|
|
function ]]..submit_field..[[_on_fn() {
|
|
var class_on = document.getElementById("]]..submit_field..[[_on_id");
|
|
var class_off = document.getElementById("]]..submit_field..[[_off_id");
|
|
class_on.removeAttribute("class");
|
|
class_off.removeAttribute("class");
|
|
class_on.setAttribute("class", "btn btn-sm btn-]]..on_color..[[ active");
|
|
class_off.setAttribute("class", "btn btn-sm btn-default");
|
|
$("#]]..submit_field..[[_input").val("]]..on_value..[[").trigger('change');
|
|
}
|
|
|
|
function ]]..submit_field..[[_off_fn() {
|
|
var class_on = document.getElementById("]]..submit_field..[[_on_id");
|
|
var class_off = document.getElementById("]]..submit_field..[[_off_id");
|
|
class_on.removeAttribute("class");
|
|
class_off.removeAttribute("class");
|
|
class_on.setAttribute("class", "btn btn-sm btn-default");
|
|
class_off.setAttribute("class", "btn btn-sm btn-]]..off_color..[[ active");
|
|
$("#]]..submit_field..[[_input").val("]]..off_value..[[").trigger('change');
|
|
}
|
|
</script>
|
|
]]
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
function user_scripts.checkbox_input_builder(gui_conf, input_id, value)
|
|
local built = build_on_off_toggle(input_id, value == 1)
|
|
|
|
return built
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
function user_scripts.flow_checkbox_input_builder(check_module)
|
|
local input_id = string.format("enabled_%s", check_module.key)
|
|
local built = build_on_off_toggle(input_id, check_module.enabled)
|
|
|
|
return built
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
function user_scripts.threshold_cross_input_builder(gui_conf, input_id, value)
|
|
value = value or {}
|
|
local gt_selected = ternary((value.operator or gui_conf.field_operator) == "gt", ' selected="selected"', '')
|
|
local lt_selected = ternary((value.operator or gui_conf.field_operator) == "lt", ' selected="selected"', '')
|
|
local input_op = "op_" .. input_id
|
|
local input_val = "value_" .. input_id
|
|
|
|
return(string.format([[<select name="%s">
|
|
<option value="gt"%s ]] .. (ternary(gui_conf.field_operator == "lt", "hidden", "")) .. [[>></option>
|
|
<option value="lt"%s ]] .. (ternary(gui_conf.field_operator == "gt", "hidden", "")) .. [[><</option>
|
|
</select> <input type="number" class="text-right form-control" min="%s" max="%s" step="%s" style="display:inline; width:12em;" name="%s" value="%s"/> <span>%s</span>]],
|
|
input_op, gt_selected, lt_selected,
|
|
gui_conf.field_min or "0", gui_conf.field_max or "", gui_conf.field_step or "1",
|
|
input_val, value.edge, i18n(gui_conf.i18n_field_unit))
|
|
)
|
|
end
|
|
|
|
-- ##############################################
|
|
|
|
return(user_scripts)
|