Implements checkmk timeseries alert integration (#5269)

This commit is contained in:
MatteoBiscosi 2021-10-19 10:12:27 +02:00
parent bc4fb9040b
commit bdb4ceb7e0
3 changed files with 124 additions and 3 deletions

View file

@ -913,7 +913,7 @@ function alert_store:count_by_severity_and_time_historical()
-- Group by according to the timeslot, that is, the alert timestamp MODULO the slot width
local q = string.format("SELECT severity, (tstamp - tstamp %% %u) as slot, count(*) count FROM %s WHERE %s GROUP BY severity, slot ORDER BY severity, slot ASC",
time_slot_width, self._table_name, where_clause)
time_slot_width, self._table_name, where_clause)
local q_res = interface.alert_store_query(q) or {}
@ -930,13 +930,78 @@ function alert_store:count_by_severity_and_time_historical()
local cur_slot = tonumber(p.slot)
local cur_count = tonumber(p.count)
if cur_slot >= min_slot and cur_slot <= max_slot then
all_severities[severity_id].all_slots[cur_slot] = cur_count
all_severities[severity_id].all_slots[cur_slot] = cur_count
end
end
return self:_prepare_count_by_severity_and_time_series(all_severities, min_slot, max_slot, time_slot_width)
end
-- ##############################################
--@brief Performs a query and counts the number of records in multiple time slots using the old response format (CheckMK integration)
function alert_store:old_timeseries_query_historical()
local group_by = "hour"
local time_slot_width = "3600"
local where_clause = self:build_where_clause()
if severity then
where_clause = string.format("severity = %u", severity) .. " AND " .. where_clause
end
-- Group by according to the timeslot, that is, the alert timestamp MODULO the slot width
local q = string.format("SELECT (tstamp - tstamp %% %u) as hour, count(*) count FROM %s WHERE %s GROUP BY hour",
time_slot_width, self._table_name, where_clause)
local q_res = interface.alert_store_query(q) or {}
local res = alert_utils.formatOldTimeseries(q_res, self._epoch_begin, self._epoch_end)
return res
end
-- ##############################################
--@brief Performs a query and counts the number of records in multiple time slots using the old response format (CheckMK integration)
function alert_store:old_timeseries_query_engaged(filter, severity)
local group_by = "hour"
local time_slot_width = "3600"
local where_clause = self:build_where_clause()
local entity_id_filter = tonumber(self._alert_entity and self._alert_entity.entity_id) -- Possibly set in subclasses constructor
local entity_value_filter = filter
local alert_id_filter = nil
local severity_filter = nil
local role_filter = nil
local alerts = interface.getEngagedAlerts(entity_id_filter, entity_value_filter, alert_id_filter, severity_filter)
q_res = self:filter_alerts(alerts)
-- Query done, now format the array
local res = alert_utils.formatOldTimeseries(q_res, self._epoch_begin, self._epoch_end)
return res
end
-- ##############################################
-- Old timeseries --
--@brief Count from memory (engaged) or database (historical)
--@return Alert counters divided into severity and time slots
function alert_store:count_by_severity_and_time_old_ts()
-- Add filters
self:add_request_filters()
-- Add limits and sort criteria
self:add_request_ranges()
if self._status == alert_consts.alert_status.engaged.alert_status_id then -- Engaged
return self:old_timeseries_query_engaged() or {}
else -- Historical
return self:old_timeseries_query_historical() or {}
end
end
-- ##############################################
--@brief Count from memory (engaged) or database (historical)
@ -947,6 +1012,7 @@ function alert_store:count_by_severity_and_time()
-- Add limits and sort criteria
self:add_request_ranges()
-- old queries, integration with CheckMK
if self._status == alert_consts.alert_status.engaged.alert_status_id then -- Engaged
return self:count_by_severity_and_time_engaged() or 0
else -- Historical
@ -1006,7 +1072,14 @@ function alert_store:count_by_severity_and_time_request()
colors = {}
}
local count_data = self:count_by_severity_and_time()
local count_data = 0
local old_ts = toboolean(_GET["old_alert_ts"]) or false
if old_ts and old_ts == true then
return self:count_by_severity_and_time_old_ts()
else
count_data = self:count_by_severity_and_time()
end
for _, severity in pairsByField(alert_severities, "severity_id", rev) do
if(count_data[severity.severity_id] ~= nil) then

View file

@ -947,4 +947,51 @@ function alert_utils.formatBehaviorAlert(params, anomalies, stats, id, subtype,
end
end
-- ##############################################
local function addDayEpoch(res, day_epoch)
res[day_epoch] = {
0, --[[ Counter for alerts between 00:00 and 00:59 UTC --]]
0, --[[ Counter for alerts between 01:00 and 01:59 UTC --]]
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 --[[ Counter for alerts in other hours, until 23:00 to 23:59 --]]
}
return res
end
-- ##############################################
--@brief Function used to format the old timeseries format (CheckMK integration)
function alert_utils.formatOldTimeseries(q_res, _epoch_begin, _epoch_end)
local hour_secs = 60*60
local day_secs = 60*60*24
local epoch_begin = _epoch_begin - (_epoch_begin % day_secs) -- Round begin to start of day
local epoch_end = _epoch_end - (_epoch_end % day_secs) + day_secs -- Round end to end of day
local res = {}
tprint(q_res)
for _, v in ipairs(q_res) do
local tstamp = v.hour or v.tstamp
-- Midnight UTC of the day containing v.hour
local day_epoch = tstamp - (tstamp % day_secs)
-- Hour of the day containing v.hour, from 0 to 23, inclusive
-- NOTE: Use the `floor` to make sure hour is an integer as it will be used to index a Lua array
local hour = math.floor((tstamp - day_epoch) / hour_secs)
-- Here we add 1 to the hour as Lua array are indexed starting from 1, whereas `hour` is an integer starting from zero
if not res[day_epoch] then
res = addDayEpoch(res, day_epoch)
end
-- This is done for both, enganged and historical alert, historical have v.hour and v.count, instead engaged
-- There is one entry per engaged, reporting the starting time (tstamp), so just add +1 to the hour having that alert
res[day_epoch][hour + 1] = tonumber(--[[ Historical ]]v.count or --[[ Engaged ]]((res[day_epoch][hour + 1] or 0) + 1))
end -- for
return res
end
-- ##############################################
return alert_utils

View file

@ -1539,6 +1539,7 @@ local known_parameters = {
["severity"] = validateListOfTypeInline(validateFilters(validateNumber)), -- Same as alert_severity
["alert_granularity"] = validateNumber, -- An alert granularity
["entity"] = validateNumber, -- An alert entity type
["old_alert_ts"] = validateBool, -- Used to know if the new or the old timeseries format is requested
["role"] = validateListOfTypeInline(validateFilters(validateSingleWord)), -- attacker/victim
["role_cli_srv"] = validateListOfTypeInline(validateFilters(validateSingleWord)), -- client/server
["acknowledged"] = validateListOfTypeInline(validateFilters(validateSingleWord)), -- acknowledged