ntopng/scripts/plugins/endpoints/slack_alert_endpoint/alert_endpoints/slack.lua
2021-07-22 10:47:05 +02:00

173 lines
5 KiB
Lua

--
-- (C) 2021 - ntop.org
--
require "lua_utils"
local json = require "dkjson"
local alert_consts = require("alert_consts")
local alert_utils = require "alert_utils"
local slack = {
name = "Slack",
endpoint_params = {
{ param_name = "slack_sender_username" },
{ param_name = "slack_webhook" },
-- TODO: add severity (Errors, Errors and Warnings, All)
},
endpoint_template = {
plugin_key = "slack_alert_endpoint",
template_name = "slack_endpoint.template"
},
recipient_params = {
-- TODO: add channel
},
recipient_template = {
plugin_key = "slack_alert_endpoint",
template_name = "slack_recipient.template" -- TODO: add template
},
}
slack.EXPORT_FREQUENCY = 5
slack.prio = 500
local MAX_ALERTS_PER_MESSAGE = 5
local alert_severity_to_emoji = {
["info"] = ":information_source:",
["warning"] = ":warning:",
["error"] = ":exclamation:",
default = ":warning:",
}
-- ##############################################
-- @brief Returns the desided formatted output for recipient params
function slack.format_recipient_params(recipient_params)
return string.format("(%s)", slack.name)
end
-- ##############################################
local function recipient2sendMessageSettings(recipient)
local settings = {
webhook = recipient.endpoint_conf.slack_webhook,
sender_username = recipient.endpoint_conf.slack_sender_username,
}
return settings
end
-- ##############################################
function slack.sendMessage(entity_type, severity, text, settings)
local channel_name = entity_type
if isEmptyString(settings.webhook) or isEmptyString(settings.sender_username) then
return false
end
local message = {
icon_emoji = alert_severity_to_emoji[severity] or alert_severity_to_emoji.default,
username = settings.sender_username .. " [" .. string.upper(severity) .. "]",
text = text,
}
local json_message = json.encode(message)
return ntop.postHTTPJsonData("", "", settings.webhook, json_message)
end
-- ##############################################
-- This will try to send the most recent MAX_ALERTS_PER_MESSAGE messages for every
-- channel and severity (no more than one message for each, budget is ignored).
-- On success, it clears the queue.
-- On error, it leaves the queue unchagned to retry on next round.
function slack.dequeueRecipientAlerts(recipient, budget, high_priority)
local notifications = {}
for i = 1, budget do
local notification = ntop.recipient_dequeue(recipient.recipient_id, high_priority)
if notification then
notifications[#notifications + 1] = notification.alert
else
break
end
end
if not notifications or #notifications == 0 then
return {success = true, more_available = false}
end
local settings = recipient2sendMessageSettings(recipient)
-- Separate by severity and channel
local alerts_by_types = {}
for _, json_message in ipairs(notifications) do
local notif = json.decode(json_message)
notif.severity = ntop.mapScoreToSeverity(notif.score)
if notif.entity_id then
if not alerts_by_types[notif.entity_id] then
alerts_by_types[notif.entity_id] = {}
end
if not alerts_by_types[notif.entity_id][notif.severity] then
alerts_by_types[notif.entity_id][notif.severity] = {}
end
table.insert(alerts_by_types[notif.entity_id][notif.severity], notif)
end
end
for entity_type, by_severity in pairs(alerts_by_types) do
for severity, notifications in pairs(by_severity) do
local messages = {}
entity_type = alert_consts.alertEntityRaw(entity_type)
severity = alert_consts.alertSeverityRaw(severity)
-- Most recent notifications first
for _, notif in pairsByValues(notifications, alert_utils.notification_timestamp_rev) do
local msg = alert_utils.formatAlertNotification(notif, {nohtml=true, show_severity=false, show_entity=true})
table.insert(messages, msg)
if #messages >= MAX_ALERTS_PER_MESSAGE then
break
end
end
local missing = #notifications - #messages
if missing > 0 then
table.insert(messages, "NOTE: " .. missing .. " older messages have been suppressed")
end
messages = table.concat(messages, "\n")
if not slack.sendMessage(entity_type, severity, messages, settings) then
-- Note: upon failure we'll possibly resend already sent messages
return {success=false, error_message="Unable to send slack messages"}
end
end
end
return {success = true, more_available = true}
end
-- ##############################################
function slack.runTest(recipient)
local message_info
local settings = recipient2sendMessageSettings(recipient)
local success = slack.sendMessage("interface", "info", "Slack notification is working", settings)
if success then
message_info = i18n("prefs.slack_sent_successfully", {channel="info"})
else
message_info = i18n("prefs.slack_send_error")
end
return success, message_info
end
-- ##############################################
return slack