Add ability to disable specific alert types on alertables

This commit is contained in:
emanuele-f 2019-07-22 23:36:30 +02:00
parent 8ee923e100
commit 09fb8667e2
16 changed files with 432 additions and 84 deletions

View file

@ -12,6 +12,7 @@ require "flow_utils"
local format_utils = require "format_utils"
local json = require "dkjson"
local alerts_api = require "alerts_api"
local alert_consts = require "alert_consts"
sendHTTPHeader('application/json')
@ -27,6 +28,21 @@ if _GET["ifid"] ~= nil then
end
local ifid = interface.getId()
local entities_bitmaps = {}
local function getEntityAlertDisabledBitmap(entity, entity_val)
if((entities_bitmaps[entity] ~= nil) and (entities_bitmaps[entity][entity_val] ~= nil)) then
return entities_bitmaps[entity][entity_val]
end
local bitmap = alerts_api.getEntityAlertsDisabled(ifid, entity, entity_val)
entities_bitmaps[entity] = entities_bitmaps[entity] or {}
entities_bitmaps[entity][entity_val] = bitmap
return(bitmap)
end
--~ function alerts_api.getEntityAlertsDisabled(ifid, entity, entity_val)
if(tonumber(_GET["currentPage"]) == nil) then _GET["currentPage"] = 1 end
if(tonumber(_GET["perPage"]) == nil) then _GET["perPage"] = getDefaultTableSize() end
@ -108,7 +124,7 @@ for _key,_value in ipairs(alerts) do
local alert_id = _value["rowid"]
if _value["alert_entity"] ~= nil then
alert_entity = alertEntityLabel(_value["alert_entity"])
alert_entity = tonumber(_value["alert_entity"])
else
alert_entity = "flow" -- flow alerts page doesn't have an entity
end
@ -168,14 +184,22 @@ for _key,_value in ipairs(alerts) do
end
if status ~= "historical-flows" then
local bitmap = getEntityAlertDisabledBitmap(_value["alert_entity"], _value["alert_entity_val"])
record["column_entity_formatted"] = alert_consts.formatAlertEntity(ifid, alertEntityRaw(_value["alert_entity"]), _value["alert_entity_val"])
record["column_alert_disabled"] = ntop.bitmapIsSet(bitmap, tonumber(_value["alert_type"]))
end
record["column_key"] = column_id
record["column_date"] = column_date
record["column_duration"] = column_duration
record["column_severity"] = column_severity
record["column_count"] = column_count
record["column_type"] = column_type
record["column_type_id"] = tonumber(_value["alert_type"])
record["column_msg"] = column_msg
record["column_entity"] = alert_entity
record["column_entity_id"] = alert_entity
record["column_entity_val"] = alert_entity_val
record["column_chart"] = column_chart

View file

@ -73,6 +73,7 @@ local function formatAlertEntity(ifid, entity_type, entity_value)
require "flow_utils"
local value
local epoch_begin, epoch_end = getAlertTimeBounds({alert_tstamp = os.time()})
local label = string.lower(alert_consts.alert_entities[entity_type].label)
if entity_type == "host" then
local host_info = hostkey2hostinfo(entity_value)
@ -93,6 +94,9 @@ local function formatAlertEntity(ifid, entity_type, entity_value)
value = "<a href='"..ntop.getHttpPrefix().."/lua/network_details.lua?network_cidr="..
entity_value.."&page=historical&epoch_begin=".. epoch_begin
.."&epoch_end=".. epoch_end .."'>" ..value.."</a>"
elseif entity_type == "host_pool" then
host_pools_utils = require("host_pools_utils")
value = host_pools_utils.getPoolName(ifid, entity_value)
else
-- fallback
value = entity_value
@ -105,7 +109,7 @@ local function formatAlertEntity(ifid, entity_type, entity_value)
return localized
else
-- fallback
return entity_type.." "..value
return label.." "..value
end
end

View file

@ -522,6 +522,24 @@ end
-- #################################
local function checkDisableAlerts()
if(_POST["action"] == "disable_alert") then
local entity = _POST["entity"]
local entity_val = _POST["entity_val"]
local alert_type = _POST["alert_type"]
alerts.disableEntityAlert(interface.getId(), entity, entity_val, alert_type)
elseif(_POST["action"] == "enable_alert") then
local entity = _POST["entity"]
local entity_val = _POST["entity_val"]
local alert_type = _POST["alert_type"]
alerts.enableEntityAlert(interface.getId(), entity, entity_val, alert_type)
end
end
-- #################################
function checkDeleteStoredAlerts()
_GET["status"] = _GET["status"] or _POST["status"]
@ -549,6 +567,8 @@ function checkDeleteStoredAlerts()
_GET["alert_type"] = nil
end
end
checkDisableAlerts()
end
-- #################################
@ -1454,6 +1474,76 @@ end
-- #################################
local function printDisabledAlerts(ifid)
local entitites = alerts.listEntitiesWithAlertsDisabled(ifid)
print(
template.gen("modal_confirm_dialog.html", {
dialog={
id = "enable_alert_type",
action = "toggleAlert(false)",
title = i18n("show_alerts.enable_alerts_title"),
message = i18n("show_alerts.enable_alerts_message", {
type = "<span class='toggle-alert-id'></span>",
entity_value = "<span class='toggle-alert-entity-value'></span>"
}),
confirm = i18n("show_alerts.enable_alerts"),
}
})
)
print[[
<div id="#table-disabled-alerts"></div>
<script>
$("#table-disabled-alerts").datatable({
url: "]] print(ntop.getHttpPrefix()) print [[/lua/get_disabled_alerts.lua",
showPagination: true,
title: "]] print(i18n("show_alerts.disabled_alerts")) print[[",
columns: [
{
title: "]]print(i18n("show_alerts.alarmable"))print[[",
field: "column_entity_formatted",
sortable: true,
css: {
textAlign: 'center',
whiteSpace: 'nowrap',
width: '35%',
}
},{
title: "]]print(i18n("show_alerts.alert_type"))print[[",
field: "column_type",
sortable: true,
css: {
textAlign: 'center',
whiteSpace: 'nowrap',
}
},{
title: "]]print(i18n("show_alerts.num_ignored_alerts"))print[[",
field: "column_count",
sortable: true,
css: {
textAlign: 'center',
whiteSpace: 'nowrap',
}
},{
title: "]]print(i18n("show_alerts.alert_actions")) print[[",
css: {
textAlign: 'center',
}
}], tableCallback: function() {
var enable_alerts_dialog = "#enable_alert_type";
datatableForEachRow("#table-disabled-alerts", function(row_id) {
datatableAddActionButtonCallback.bind(this)(4, "prepareToggleAlertsDialog('table-disabled-alerts',"+ row_id +"); $('"+enable_alerts_dialog+"').modal('show');", "]] print(i18n("show_alerts.enable_alerts")) print[[");
})
}
});
</script>]]
end
-- #################################
function drawAlertTables(num_past_alerts, num_engaged_alerts, num_flow_alerts, get_params, hide_extended_title, alt_nav_tabs, options)
local alert_items = {}
local url_params = {}
@ -1467,6 +1557,22 @@ function drawAlertTables(num_past_alerts, num_engaged_alerts, num_flow_alerts, g
title = i18n("show_alerts.delete_alert"),
message = i18n("show_alerts.confirm_delete_alert").."?",
confirm = i18n("delete"),
confirm_button = "btn-danger",
}
})
)
print(
template.gen("modal_confirm_dialog.html", {
dialog={
id = "disable_alert_type",
action = "toggleAlert(true)",
title = i18n("show_alerts.disable_alerts_title"),
message = i18n("show_alerts.disable_alerts_message", {
type = "<span class='toggle-alert-id'></span>",
entity_value = "<span class='toggle-alert-entity-value'></span>"
}),
confirm = i18n("show_alerts.disable_alerts"),
}
})
)
@ -1598,6 +1704,10 @@ function getCurrentStatus() {
status = nil; status_reset = 1
end
if alerts.hasEntitiesWithAlertsDisabled(ifId) then
alert_items[#alert_items +1] = {["label"] = i18n("show_alerts.disabled_alerts"), ["div-id"] = "table-disabled-alerts", ["status"] = "disabled-alerts"}
end
for k, t in ipairs(alert_items) do
local clicked = "0"
if((not alt_nav_tabs) and ((k == 1 and status == nil) or (status ~= nil and status == t["status"]))) then
@ -1608,6 +1718,17 @@ function getCurrentStatus() {
<div id="]] print(t["div-id"]) print[["></div>
</div>
<script type="text/javascript">
$("#]] print(nav_tab_id) print[[").append('<li class="]] print(ternary(options.engaged_only, 'hidden', '')) print[["><a href="#tab-]] print(t["div-id"]) print[[" clicked="]] print(clicked) print[[" role="tab" data-toggle="tab">]] print(t["label"]) print[[</a></li>')
</script>
]]
if t["status"] == "disabled-alerts" then
printDisabledAlerts(ifId)
goto next_menu_item
end
print[[
<script type="text/javascript">
function deleteAlertById(alert_id) {
var params = {};
@ -1619,7 +1740,30 @@ function getCurrentStatus() {
form.appendTo('body').submit();
}
$("#]] print(nav_tab_id) print[[").append('<li class="]] print(ternary(options.engaged_only, 'hidden', '')) print[["><a href="#tab-]] print(t["div-id"]) print[[" clicked="]] print(clicked) print[[" role="tab" data-toggle="tab">]] print(t["label"]) print[[</a></li>')
var alert_to_toggle = null;
function prepareToggleAlertsDialog(table_id, idx) {
var table_data = $("#" + table_id ).data("datatable").resultset.data;
var row = table_data[idx];
alert_to_toggle = row;
$(".toggle-alert-id").html(noHtml(row.column_type).trim());
$(".toggle-alert-entity-value").html(noHtml(row.column_entity_formatted).trim())
}
function toggleAlert(disable) {
var row = alert_to_toggle;
var params = {
"action": disable ? "disable_alert" : "enable_alert",
"entity": row.column_entity_id,
"entity_val": row.column_entity_val,
"alert_type": row.column_type_id,
"csrf": "]] print(ntop.getRandomCSRFValue()) print[[",
};
var form = paramsToForm('<form method="post"></form>', params);
form.appendTo('body').submit();
}
$('a[href="#tab-]] print(t["div-id"]) print[["]').on('shown.bs.tab', function (e) {
// append the li to the tabs
@ -1759,24 +1903,28 @@ function getCurrentStatus() {
{
title: "]]print(i18n("show_alerts.alert_actions")) print[[",
]]
if t["status"] == "engaged" then
print("hidden: true,")
end
print[[
css: {
textAlign: 'center',
}
}
], tableCallback: function() {
datatableForEachRow("#]] print(t["div-id"]) print[[", function(row_id) {
var table_data = $("#]] print(t["div-id"]) print[[").data("datatable").resultset.data;
datatableForEachRow("#]] print(t["div-id"]) print[[", function(row_id) {
var alert_key = $("td:nth(7)", this).html().split("|");
var alert_id = alert_key[0];
var historical_url = alert_key[1];
var disable_alerts_dialog = "#disable_alert_type";
var data = table_data[row_id];
if (typeof(historical_url) === "string")
if(typeof(historical_url) === "string") {
datatableAddLinkButtonCallback.bind(this)(9, historical_url, "]] print(i18n("show_alerts.explorer")) print[[");
datatableAddDeleteButtonCallback.bind(this)(9, "delete_alert_id ='" + alert_id + "'; $('#delete_alert_dialog').modal('show');", "]] print(i18n('delete')) print[[");
disable_alerts_dialog = "#disable_flows_alerts";
} else if(!data.column_alert_disabled)
datatableAddActionButtonCallback.bind(this)(9, "prepareToggleAlertsDialog(']] print(t["div-id"]) print[[',"+ row_id +"); $('"+disable_alerts_dialog+"').modal('show');", "]] print(i18n("show_alerts.disable_alerts")) print[[");
if(]] print(ternary(t["status"] ~= "engaged", "true", "false")) print[[)
datatableAddDeleteButtonCallback.bind(this)(9, "delete_alert_id ='" + alert_id + "'; $('#delete_alert_dialog').modal('show');", "]] print(i18n('delete')) print[[");
$("form", this).submit(function() {
// add "status" parameter to the form
@ -1812,6 +1960,7 @@ function getCurrentStatus() {
</script>
]]
::next_menu_item::
end
local zoom_vals = {
@ -1965,6 +2114,7 @@ function drawAlerts(options)
end
checkDeleteStoredAlerts()
checkDisableAlert()
return drawAlertTables(num_past_alerts, num_engaged_alerts, num_flow_alerts, _GET, true, nil, options)
end

View file

@ -52,6 +52,46 @@ end
-- ##############################################
local function getEntityDisabledAlertsCountersKey(ifid, entity, entity_val)
return(string.format("ntopng.cache.alerts.ifid_%d.%d_%s", ifid, entity, entity_val))
end
local function incDisabledAlertsCount(ifid, granularity_id, entity, entity_val, alert_type)
local key = getEntityDisabledAlertsCountersKey(ifid, entity, entity_val)
-- NOTE: using separate keys based on granularity to avoid concurrency issues
counter_key = string.format("%d_%d", granularity_id, alert_type)
local val = tonumber(ntop.getHashCache(key, counter_key)) or 0
val = val + 1
ntop.setHashCache(key, counter_key, string.format("%d", val))
return(val)
end
-- ##############################################
function alerts_api.getEntityDisabledAlertsCounters(ifid, entity, entity_val)
local key = getEntityDisabledAlertsCountersKey(ifid, entity, entity_val)
local entity_counters = ntop.getHashAllCache(key) or {}
local by_alert_type = {}
for what, counter in pairs(entity_counters) do
local parts = string.split(what, "_")
if((parts) and (#parts == 2)) then
local granularity_id = tonumber(parts[1])
local alert_type = tonumber(parts[2])
by_alert_type[alert_type] = by_alert_type[alert_type] or 0
by_alert_type[alert_type] = by_alert_type[alert_type] + counter
end
end
return(by_alert_type)
end
-- ##############################################
--! @brief Creates an alert object
--! @param metadata the information about the alert type and severity
--! @return an alert object on success, nil on error
@ -105,12 +145,18 @@ end
function alerts_api:trigger(entity_value, alert_message, when)
local force = false
local msg = alert_message
local ifid = interface.getId()
when = when or os.time()
if(type(alert_message) == "table") then
msg = json.encode(alert_message)
end
if alerts_api.isEntityAlertDisabled(ifid, self.entity_type_id, entity_value, self.type_id) then
incDisabledAlertsCount(ifid, -1, self.entity_type_id, entity_value, self.type_id)
return(false)
end
local rv = interface.storeAlert(when, when, self.periodicity,
self.type_id, self.subtype or "", self.severity_id,
self.entity_type_id, entity_value, msg)
@ -280,17 +326,19 @@ end
--! @return true on success, false otherwise
function alerts_api.new_trigger(entity_info, type_info, when)
when = when or os.time()
local ifid = interface.getId()
local granularity_sec = type_info.alert_granularity and type_info.alert_granularity.granularity_seconds or 0
local granularity_id = type_info.alert_granularity and type_info.alert_granularity.granularity_id or nil
local subtype = type_info.alert_subtype or ""
local alert_json = json.encode(type_info.alert_type_params)
local is_disabled = alerts_api.isEntityAlertDisabled(ifid, entity_info.alert_entity.entity_id, entity_info.alert_entity_val, type_info.alert_type.alert_id)
if(granularity_id ~= nil) then
local triggered = true
local alert_key_name = get_alert_triggered_key(type_info)
local params = {alert_key_name, granularity_id,
type_info.alert_type.severity.severity_id, type_info.alert_type.alert_id,
subtype, alert_json
subtype, alert_json, is_disabled
}
if((host.storeTriggeredAlert) and (entity_info.alert_entity.entity_id == alertEntity("host"))) then
@ -305,13 +353,18 @@ function alerts_api.new_trigger(entity_info, type_info, when)
if(do_trace) then print("[DON'T Trigger alert (already triggered?) @ "..granularity_sec.."] "..
entity_info.alert_entity_val .."@"..type_info.alert_type.i18n_title.."\n") end
return(false)
elseif(is_disabled) then
if(do_trace) then print("[COUNT Disabled alert @ "..granularity_sec.."] "..
entity_info.alert_entity_val .."@"..type_info.alert_type.i18n_title.."\n") end
incDisabledAlertsCount(ifid, granularity_id, entity_info.alert_entity.entity_id, entity_info.alert_entity_val, type_info.alert_type.alert_id)
end
end
local action = ternary((granularity_id ~= nil), "engaged", "stored")
local alert_event = {
ifid = interface.getId(),
ifid = ifid,
granularity = granularity_sec,
entity_type = entity_info.alert_entity.entity_id,
entity_value = entity_info.alert_entity_val,
@ -617,4 +670,91 @@ end
-- ##############################################
local function getEntityDisabledAlertsBitmapKey(ifid, entity, entity_val)
return string.format("ntopng.prefs.alerts.ifid_%d.disabled_alerts.__%s__%s", ifid, entity, entity_val)
end
-- ##############################################
function alerts_api.getEntityAlertsDisabled(ifid, entity, entity_val)
local bitmap = tonumber(ntop.getPref(getEntityDisabledAlertsBitmapKey(ifid, entity, entity_val))) or 0
return(bitmap)
end
-- ##############################################
function alerts_api.setEntityAlertsDisabled(ifid, entity, entity_val, bitmap)
local key = getEntityDisabledAlertsBitmapKey(ifid, entity, entity_val)
if(bitmap == 0) then
ntop.delCache(key)
else
ntop.setPref(key, string.format("%u", bitmap))
end
end
-- ##############################################
local function toggleEntityAlert(ifid, entity, entity_val, alert_type, disable)
alert_type = tonumber(alert_type)
bitmap = alerts_api.getEntityAlertsDisabled(ifid, entity, entity_val)
if(disable) then
bitmap = ntop.bitmapSet(bitmap, alert_type)
else
bitmap = ntop.bitmapClear(bitmap, alert_type)
end
alerts_api.setEntityAlertsDisabled(ifid, entity, entity_val, bitmap)
return(bitmap)
end
-- ##############################################
function alerts_api.disableEntityAlert(ifid, entity, entity_val, alert_type)
return(toggleEntityAlert(ifid, entity, entity_val, alert_type, true))
end
-- ##############################################
function alerts_api.enableEntityAlert(ifid, entity, entity_val, alert_type)
return(toggleEntityAlert(ifid, entity, entity_val, alert_type, false))
end
-- ##############################################
function alerts_api.isEntityAlertDisabled(ifid, entity, entity_val, alert_type)
local bitmap = alerts_api.getEntityAlertsDisabled(ifid, entity, entity_val)
return(ntop.bitmapIsSet(bitmap, tonumber(alert_type)))
end
-- ##############################################
function alerts_api.hasEntitiesWithAlertsDisabled(ifid)
return(table.len(ntop.getKeysCache(getEntityDisabledAlertsBitmapKey(ifid, "*", "*"))) > 0)
end
-- ##############################################
function alerts_api.listEntitiesWithAlertsDisabled(ifid)
local keys = ntop.getKeysCache(getEntityDisabledAlertsBitmapKey(ifid, "*", "*")) or {}
local res = {}
for key in pairs(keys) do
local parts = string.split(key, "__")
if((parts) and (#parts == 3)) then
local entity = tonumber(parts[2])
local entity_val = parts[3]
res[entity] = res[entity] or {}
res[entity][entity_val] = true
end
end
return(res)
end
-- ##############################################
return(alerts_api)