From 532cd6dbdb416fe7d6c128f8ebdd4be5c31d5948 Mon Sep 17 00:00:00 2001 From: omercier Date: Mon, 28 Apr 2025 09:40:32 +0200 Subject: [PATCH 01/27] wip: things to improve? --- modules/centreon-stream-connectors-lib/sc_metrics.lua | 2 +- modules/centreon-stream-connectors-lib/sc_params.lua | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/centreon-stream-connectors-lib/sc_metrics.lua b/modules/centreon-stream-connectors-lib/sc_metrics.lua index 3fc65356..515e9419 100644 --- a/modules/centreon-stream-connectors-lib/sc_metrics.lua +++ b/modules/centreon-stream-connectors-lib/sc_metrics.lua @@ -269,7 +269,7 @@ end function ScMetrics:build_metric(format_metric) local metrics_info = self.metrics_info - for metric, metric_data in pairs(self.metrics_info) do + for metric, metric_data in pairs(metrics_info) do if string.match(metric_data.metric_name, self.params.accepted_metrics) then metrics_info[metric].metric_name = string.gsub(metric_data.metric_name, self.params.metric_name_regex, self.params.metric_replacement_character) -- use stream connector method to format the metric event diff --git a/modules/centreon-stream-connectors-lib/sc_params.lua b/modules/centreon-stream-connectors-lib/sc_params.lua index d4a1f8f8..88f2f4dc 100644 --- a/modules/centreon-stream-connectors-lib/sc_params.lua +++ b/modules/centreon-stream-connectors-lib/sc_params.lua @@ -1011,6 +1011,7 @@ function ScParams:check_params() -- self.params.allow_insecure_connection = self.common:number_to_boolean(self.common:check_boolean_number_option_syntax(not self.params.allow_insecure_connection, 0)) self.params.verify_certificate = self.common:number_to_boolean(self.common:check_boolean_number_option_syntax(self.params.verify_certificate, 0)) self.params.logfile = self.common:ifnil_or_empty(self.params.logfile, "/var/log/centreon-broker/stream-connector.log") + -- FIXME: Add a control that log_level is a number or convert string to number self.params.log_level = self.common:ifnil_or_empty(self.params.log_level, 1) self.params.log_curl_commands = self.common:check_boolean_number_option_syntax(self.params.log_curl_commands, 0) self.params.use_long_output = self.common:check_boolean_number_option_syntax(self.params.use_longoutput, 1) @@ -1242,4 +1243,4 @@ function ScParams:build_and_validate_filters_pattern(param_list) end end -return sc_params \ No newline at end of file +return sc_params From c153bbf361428b854290f2090809b4eddf8a599b Mon Sep 17 00:00:00 2001 From: omercier Date: Mon, 28 Apr 2025 09:41:18 +0200 Subject: [PATCH 02/27] feat(prometheus-metrics-v2): new connector for metrics --- .../prometheus-pushgateway-metrics-apiv2.lua | 613 ++++++++++++++++++ 1 file changed, 613 insertions(+) create mode 100644 centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua diff --git a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua new file mode 100644 index 00000000..35170d25 --- /dev/null +++ b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua @@ -0,0 +1,613 @@ +#!/usr/bin/lua +-------------------------------------------------------------------------------- +-- Centreon Broker Datadog Connector Events +-------------------------------------------------------------------------------- + + +-- Libraries +local curl = require "cURL" +local base64 = require("base64") +local sc_common = require("centreon-stream-connectors-lib.sc_common") +local sc_logger = require("centreon-stream-connectors-lib.sc_logger") +local sc_broker = require("centreon-stream-connectors-lib.sc_broker") +local sc_event = require("centreon-stream-connectors-lib.sc_event") +local sc_params = require("centreon-stream-connectors-lib.sc_params") +local sc_macros = require("centreon-stream-connectors-lib.sc_macros") +local sc_flush = require("centreon-stream-connectors-lib.sc_flush") +local sc_metrics = require("centreon-stream-connectors-lib.sc_metrics") + + +-------------------------------------------------------------------------------- +-- Local functions +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- convert_to_openmetric: [for Prometheus] replace unwanted characters in order to comply with the open metrics format +-- @param {string} string, the string to convert +-- @return {string} string, a string that matches [a-zA-Z0-9_\.]+ +-------------------------------------------------------------------------------- +local function convert_to_openmetric (string) + if string == nil or string == '' or type(string) ~= 'string' then + return false + end + + return string.gsub(string, '[^a-zA-Z0-9_:]', '_') +end + +-------------------------------------------------------------------------------- +-- unit_mapping: convert perfdata units to openmetrics standard +-- @param {string} unit, the unit value +-- @return {string} unit, the openmetrics unit name +-- @return {boolean}, true if the unit is found in the mapping or empty +-------------------------------------------------------------------------------- +local function unit_mapping (unit) + local unitMapping = { + s = 'seconds', + m = 'meters', + B = 'bytes', + g = 'grams', + V = 'volts', + A = 'amperes', + K = 'kelvins', + ratio = 'ratios', + degres = 'celsius' + } + + local unhandledUnit = nil + + if unit == nil or unit == '' or type(unit) ~= 'string' then + unit = '' + end + + if unit == '%' then + unit = unitMapping['ratio'] + elseif unit == '°' then + unit = unitMapping['degres'] + else + if (unitMapping[unit] ~= nil) then + unit = unitMapping[unit] + end + end + + return unit, true +end + +-------------------------------------------------------------------------------- +-- Classe event_queue +-------------------------------------------------------------------------------- + +local EventQueue = {} +EventQueue.__index = EventQueue + +-------------------------------------------------------------------------------- +---- Constructor +---- @param conf The table given by the init() function and returned from the GUI +---- @return the new EventQueue +---------------------------------------------------------------------------------- + +function EventQueue.new(params) + local self = {} + + local mandatory_parameters = { + } + + self.fail = false + + -- set up log configuration + local logfile = params.logfile or "/var/log/centreon-broker/prometheus-pushgateway-v2-metrics.log" + local log_level = params.log_level or 1 + + -- initiate mandatory objects + self.sc_logger = sc_logger.new(logfile, log_level) + self.sc_common = sc_common.new(self.sc_logger) + self.sc_broker = sc_broker.new(self.sc_logger) + self.sc_params = sc_params.new(self.sc_common, self.sc_logger) + + -- checking mandatory parameters and setting a fail flag + if not self.sc_params:is_mandatory_config_set(mandatory_parameters, params) then + self.fail = true + end + + params.max_buffer_size = 1 + + -- overriding default parameters for this stream connector if the default values doesn't suit the basic needs + self.sc_params.params.accepted_categories = params.accepted_categories or "neb" + self.sc_params.params.accepted_elements = params.accepted_elements or "host_status,service_status" + + -- prometheus specific parameters + self.sc_params.params.prometheus_url = params.prometheus_url or "http://127.0.0.1:9091" + self.sc_params.params.http_timeout = params.http_timeout or 30 + self.sc_params.params.prometheus_gateway_job = params.prometheus_gateway_job or "monitoring" + + -- apply users params and check syntax of standard ones + self.sc_params:param_override(params) + self.sc_params:check_params() + + -- in order to have the proper use of that max_buffer_size param, we need to separate queues for hosts and services + self.sc_params.params.send_mixed_events = 0 + + self.sc_macros = sc_macros.new(self.sc_params.params, self.sc_logger) + + -- only load the custom code file, not executed yet + if self.sc_params.load_custom_code_file and not self.sc_params:load_custom_code_file(self.sc_params.params.custom_code_file) then + self.sc_logger:error("[EventQueue:new]: couldn't successfully load the custom code file: " .. tostring(self.sc_params.params.custom_code_file)) + end + + self.sc_params:build_accepted_elements_info() + self.sc_flush = sc_flush.new(self.sc_params.params, self.sc_logger) + + local categories = self.sc_params.params.bbdo.categories + local elements = self.sc_params.params.bbdo.elements + + -- it is not possible to have a payload containing metrics from different hosts or services. + -- therefore, we need to check if the metric that we are working on belongs to the same host/service than the previous metric + -- that's why we initiate a structure to store this info + self.previous_info = { + [categories.neb.id] = { + [elements.host_status.id] = { + host_id = "", + flush_success = false + }, + [elements.service_status.id] = { + host_id = "", + service_id = "", + flush_success = false + } + } + } + + self.format_event = { + [categories.neb.id] = { + [elements.host_status.id] = function () return self:format_event_host() end, + [elements.service_status.id] = function () return self:format_event_service() end + } + } + + self.format_metric = { + [categories.neb.id] = { + [elements.host_status.id] = function (metric) return self:format_metric_host(metric) end, + [elements.service_status.id] = function (metric) return self:format_metric_service(metric) end + } + } + + self.send_data_method = { + [1] = function (payload, queue_metadata) return self:send_data(payload, queue_metadata) end + } + + self.build_payload_method = { + [1] = function (payload, event) return self:build_payload(payload, event) end + } + + -- those sleep counters will avoid log spam and connection spam + self.send_data_sleep_counter = self.sc_common:create_sleep_counter_table({}, 0, 300, 10) + self.init_fail_sleep_counter = self.sc_common:create_sleep_counter_table({}, 0, 300, 10) + + -- return EventQueue object + setmetatable(self, { __index = EventQueue }) + return self +end + +-------------------------------------------------------------------------------- +---- EventQueue:format_accepted_event method +-------------------------------------------------------------------------------- +function EventQueue:format_accepted_event() + local category = self.sc_event.event.category + local element = self.sc_event.event.element + + self.sc_logger:debug("[EventQueue:format_accepted_event]: starting format event") + + -- can't format event if stream connector is not handling this kind of event and that it is not handled with a template file + if not self.format_event[category][element] then + self.sc_logger:error("[format_accepted_event]: You are trying to format an event with category: " + .. tostring(self.sc_params.params.reverse_category_mapping[category]) .. " and element: " + .. tostring(self.sc_params.params.reverse_element_mapping[category][element]) + .. ". If it is a not a misconfiguration, you should create a format file to handle this kind of element") + else + self.format_event[category][element]() + end + + self.sc_logger:debug("[EventQueue:format_accepted_event]: event formatting is finished") +end + +-------------------------------------------------------------------------------- +---- EventQueue:format_event_host method +-------------------------------------------------------------------------------- +function EventQueue:format_event_host() + local event = self.sc_event.event + self.previous_info[event.category][event.element].flush_success = false + + -- this is the first time we receive a metric from a host, we store host id in the table + if self.previous_info[event.category][event.element].host_id == "" then + self.previous_info[event.category][event.element].host_id = event.host_id + else + -- the event is linked to a new host, we can't send payload with data from different hosts so we force a data flush + -- we store the new host id and then we continue working on metrics from said host + if self.previous_info[event.category][event.element].host_id ~= event.host_id then + while not self.previous_info[event.category][event.element].flush_success do + if self.sc_flush:flush_all_queues(self.build_payload_method[1], self.send_data_method[1]) then + self.previous_info[event.category][event.element].flush_success = true + self.send_data_sleep_counter:reset() + else + self.send_data_sleep_counter:sleep() + end + end + + self.previous_info[event.category][event.element].host_id = event.host_id + end + end + self.sc_logger:debug("[EventQueue:format_event_host]: call build_metric ") + self.sc_metrics:build_metric(self.format_metric[event.category][event.element]) +end + +-------------------------------------------------------------------------------- +---- EventQueue:format_event_service method +-------------------------------------------------------------------------------- +function EventQueue:format_event_service() + self.sc_logger:debug("[EventQueue:format_event_service]: starting format event service.") + local event = self.sc_event.event + + self.previous_info[event.category][event.element].flush_success = false + + -- this is the first time we receive a metric from a servuce, we store host id and service id in the table + if self.previous_info[event.category][event.element].host_id == "" + or self.previous_info[event.category][event.element].service_id == "" + then + self.previous_info[event.category][event.element].host_id = event.host_id + self.previous_info[event.category][event.element].service_id = event.service_id + else + if self.previous_info[event.category][event.element].host_id ~= event.host_id + or self.previous_info[event.category][event.element].service_id ~= event.service_id + then + -- the event is linked to a new service, we can't send payload with data from different services so we force a data flush + -- we store the new host and service id and then we continue working on metrics from said service + while not self.previous_info[event.category][event.element].flush_success do + if self.sc_flush:flush_all_queues(self.build_payload_method[1], self.send_data_method[1]) then + self.previous_info[event.category][event.element].flush_success = true + self.send_data_sleep_counter:reset() + else + self.send_data_sleep_counter:sleep() + end + end + + self.previous_info[event.category][event.element].host_id = event.host_id + self.previous_info[event.category][event.element].service_id = event.service_id + end + end + self.sc_logger:debug("[EventQueue:format_event_service]: call build_metric ") + self.sc_metrics:build_metric(self.format_metric[event.category][event.element]) + self.sc_logger:debug("[EventQueue:format_event_service]: format metric service is finished ") +end + +-------------------------------------------------------------------------------- +---- EventQueue:format_metric_host method +-- @param metric {table} a single metric data +-------------------------------------------------------------------------------- +function EventQueue:format_metric_host(metric) + self.sc_logger:debug("[EventQueue:format_metric_host]: starting format event host.") + local event = self.sc_event.event + local sdesc = "host" + + event.formated_event = { + prom_hname = event.cache.host.name, + prom_sdesc = sdesc, + prom_sdesc_url = base64.encode(sdesc) + } + self.sc_logger:debug("[EventQueue:format_metric_host]: call format_metric ") + self:format_metric_event(metric) + self.sc_logger:debug("[EventQueue:format_metric_host]: format metric host is finished ") +end + +-------------------------------------------------------------------------------- +---- EventQueue:format_metric_service method +-- @param metric {table} a single metric data +-------------------------------------------------------------------------------- +function EventQueue:format_metric_service(metric) + self.sc_logger:debug("[EventQueue:format_metric_service]: starting format event service.") + local event = self.sc_event.event + local sdesc = event.cache.service.description + + event.formated_event = { + prom_hname = event.cache.host.name, + prom_sdesc = sdesc, + prom_sdesc_url = base64.encode(sdesc) + } + self.sc_logger:debug("[EventQueue:format_metric_service]: call format_metric ") + self:format_metric_event(metric) + self.sc_logger:debug("[EventQueue:format_metric_service]: format metric service is finished ") +end + +-------------------------------------------------------------------------------- +-- add_unit_info: add unit metadata to match openmetrics standard +-- @param {string} label, the name of the metric +-- @param {string} unit, the unit name +-- @param {string} name, the name of the metric +-- @return {string} data, the unit metadata information +-------------------------------------------------------------------------------- +function EventQueue:add_unit_info (label, unit, name) + local data = '' + + if (unit ~= '' and unit ~= nil) then + data = '# UNIT ' .. name .. '\n' + end + + return data +end + +-------------------------------------------------------------------------------- +-- create_metric_name: concatenates data to create the metric name +-- @param {string} label, the name of the perfdata +-- @param {string} unit, the unit name +-- @return {string} name, the prometheus metric name (open metric format) +-------------------------------------------------------------------------------- +function EventQueue:create_metric_name (label, unit) + local name = '' + local sdesc = 'host' + if (self.sc_event.event.service_description) then + sdesc = self.sc_event.event.service_description + end + local hname = self.sc_event.event.cache.host.name + + if (unit ~= '') then + if (self.enable_extended_metric_name == 0) then + name = label .. '_' .. unit + else + name = hname .. '_' .. sdesc .. ':' .. label .. '_' .. unit + end + else + if (self.enable_extended_metric_name == 0) then + name = label + else + name = hname .. '_' .. sdesc .. ':' .. label + end + end + + return convert_to_openmetric(name) +end + +-------------------------------------------------------------------------------- +---- EventQueue:format_metric_service method +-- @param metric {table} a single metric data +------------------------------------------------------------------------------- +function EventQueue:format_metric_event(metric) + self.sc_logger:debug("[EventQueue:format_metric]: start real format metric ") + local event = self.sc_event.event + local type = self:get_metric_type(metric) + local unit = unit_mapping(metric.uom) + local label = metric.metric_name + local name = self:create_metric_name(label, unit) + local data = '' + local sdesc = event.formated_event.prom_sdesc + + data = '# TYPE ' .. name .. ' ' .. type .. '\n' + data = data .. self:add_unit_info(label, unit, name) + + if not event.hostgroupsLabel then + data = data .. name .. '{label="' .. label .. '", host="' .. event.cache.host.name .. '", service="' .. sdesc .. '"} ' .. metric.value .. '\n' + else + data = data .. name .. '{label="' .. label .. '", host="' .. event.cache.host.name .. '", service="' .. sdesc .. '", ' .. event.hostgroupsLabel .. '} ' .. metric.value .. '\n' + end + + if (self.enable_threshold_metrics == 1) then + data = data .. self:threshold_metrics(metric, label, unit, type) + end + + event.formated_event.payload = data + + self:add() + self.sc_logger:debug("[EventQueue:format_metric]: end real format metric ") +end + +-------------------------------------------------------------------------------- +-- ifnumber_not_nan: [for Prometheus] check if a number is a number (and not a NaN) +-- @param {number} number, the number to check +-- @return {boolean} +-------------------------------------------------------------------------------- +local function ifnumber_not_nan (number) + if (number ~= number) then + return false + elseif (type(number) ~= 'number') then + return false + else + return true + end +end + +-------------------------------------------------------------------------------- +-- get_metric_type: [for Prometheus] find out the metric type to match openmetrics standard +-- @param {table} perfdata, the perfdata informations +-- @return {string} metricType, the type of the metric +-------------------------------------------------------------------------------- +function EventQueue:get_metric_type (perfdata) + local metricType = nil; + if (ifnumber_not_nan(perfdata.max)) then + metricType = 'gauge' + else + metricType = 'counter' + end + + return metricType +end + +-------------------------------------------------------------------------------- +-- EventQueue:add, add an event to the sending queue +-------------------------------------------------------------------------------- +function EventQueue:add() + -- store event in self.events lists + local category = self.sc_event.event.category + local element = self.sc_event.event.element + + self.sc_logger:debug("[EventQueue:add]: add event in queue category: " .. tostring(self.sc_params.params.reverse_category_mapping[category]) + .. " element: " .. tostring(self.sc_params.params.reverse_element_mapping[category][element])) + + self.sc_logger:debug("[EventQueue:add]: queue size before adding event: " .. tostring(#self.sc_flush.queues[category][element].events)) + self.sc_flush.queues[category][element].events[#self.sc_flush.queues[category][element].events + 1] = self.sc_event.event.formated_event + + self.sc_logger:info("[EventQueue:add]: queue size is now: " .. tostring(#self.sc_flush.queues[category][element].events) + .. ", max is: " .. tostring(self.sc_params.params.max_buffer_size)) +end + +-------------------------------------------------------------------------------- +-- EventQueue:build_payload, concatenate data so it is ready to be sent +-- @param payload {string} json encoded string +-- @param event {table} the event that is going to be added to the payload +-- @return payload {string} json encoded string +-------------------------------------------------------------------------------- +function EventQueue:build_payload(payload, event) + + if not payload then -- FIXME: voir obsidian + payload = event + else + table.insert(payload, event) + end + + return payload +end + +function EventQueue:send_data(payload, queue_metadata) + self.sc_logger:debug("[EventQueue:send_data]: Starting to send data") + local httpPostData = payload.payload + local httpResponseBody = "" + + local httpRequest = curl.easy() + :setopt_url(self.sc_params.params.prometheus_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance/' .. payload.prom_hname .. '/service@base64/' .. payload.prom_sdesc_url) + :setopt_writefunction( + function (response) + httpResponseBody = httpResponseBody .. tostring(response) + end + ) + :setopt(curl.OPT_TIMEOUT, self.sc_params.params.http_timeout) + :setopt( + curl.OPT_HTTPHEADER, + { + "content-type: application/openmetrics-text" + } + ) + + -- set proxy address configuration + if (self.sc_params.params.proxy_address and self.sc_params.params.proxy_address ~= '') then + if (self.sc_params.params.proxy_port and self.sc_params.params.proxy_port ~= '') then + httpRequest:setopt(curl.OPT_PROXY, self.sc_params.params.proxy_address .. ':' .. self.sc_params.params.proxy_port) + else + self.sc_logger:error("EventQueue:send_data: proxy_port parameter is not set but proxy_address is used") + end + end + + -- set proxy user configuration + if (self.sc_params.params.proxy_username ~= '') then + if (self.sc_params.params.proxy_password ~= '') then + httpRequest:setopt(curl.OPT_PROXYUSERPWD, self.sc_params.params.proxy_username .. ':' .. self.sc_params.params.proxy_password) + else + self.sc_logger:error("EventQueue:send_data: proxy_password parameter is not set but proxy_username is used") + end + end + + -- adding the HTTP POST data + self.sc_logger:debug("EventQueue:send_data: POST data: '" .. httpPostData .. "'") + httpRequest:setopt_postfields(httpPostData) + + -- performing the HTTP request + httpRequest:perform() + + -- collecting results + local httpResponseCode = httpRequest:getinfo(curl.INFO_RESPONSE_CODE) + + httpRequest:close() + + -- Handling the return code + local retval = false + if httpResponseCode == 200 then + self.sc_logger:info("EventQueue:send_data: HTTP POST request successful: return code is " .. httpResponseCode) + -- now that the data has been sent, we empty the events array + self.events = {} + retval = true + else + self.sc_logger:error("EventQueue:send_data: HTTP POST request FAILED, return code is " .. httpResponseCode .. " message is:\n\"" .. httpResponseBody .. "\n\"\n") + self.sc_logger:error("the body request " .. httpPostData) + end + + -- and update the timestamp + self.__internal_ts_last_flush = os.time() + + self.sc_logger:debug("[EventQueue:send_data]: End") + return retval +end + +-------------------------------------------------------------------------------- +-- Required functions for Broker StreamConnector +-------------------------------------------------------------------------------- + +local queue + +-- Fonction init() +function init(conf) + queue = EventQueue.new(conf) +end + +-- -------------------------------------------------------------------------------- +-- write, +-- @param {table} event, the event from broker +-- @return {boolean} +-------------------------------------------------------------------------------- +function write(event) + -- skip event if a mandatory parameter is missing + if queue.fail then + queue.sc_logger:error("Skipping event because a mandatory parameter is not set") + queue.init_fail_sleep_counter:sleep() + return false + end + + queue.init_fail_sleep_counter:reset() + + -- initiate event object + queue.sc_metrics = sc_metrics.new(event, queue.sc_params.params, queue.sc_common, queue.sc_broker, queue.sc_logger) + queue.sc_event = queue.sc_metrics.sc_event + + if queue.sc_event:is_valid_category() then + if queue.sc_metrics:is_valid_bbdo_element() then + -- format event if it is validated + if queue.sc_metrics:is_valid_metric_event() then + queue:format_accepted_event() + end + --- log why the event has been dropped + else + queue.sc_logger:debug("dropping event because element is not valid. Event element is: " + .. tostring(queue.sc_params.params.reverse_element_mapping[queue.sc_event.event.category][queue.sc_event.event.element])) + end + else + queue.sc_logger:debug("dropping event because category is not valid. Event category is: " + .. tostring(queue.sc_params.params.reverse_category_mapping[queue.sc_event.event.category])) + end + + return flush() +end + +-- flush method is called by broker every now and then (more often when broker has nothing else to do) +function flush() + local queues_size = queue.sc_flush:get_queues_size() + + -- nothing to flush + if queues_size == 0 then + return true + end + + -- flush all queues because last global flush is too old + if queue.sc_flush.last_global_flush < os.time() - queue.sc_params.params.max_all_queues_age then + if not queue.sc_flush:flush_all_queues(queue.build_payload_method[1], queue.send_data_method[1]) then + return false + end + + return true + end + + -- flush queues because too many events are stored in them + if queues_size > queue.sc_params.params.max_buffer_size then + if not queue.sc_flush:flush_all_queues(queue.build_payload_method[1], queue.send_data_method[1]) then + return false + end + + return true + end + + -- there are events in the queue but they were not ready to be send + return false +end From 107b0d75d816042831c79ec43f04c4422e317b5d Mon Sep 17 00:00:00 2001 From: omercier Date: Mon, 28 Apr 2025 16:51:01 +0200 Subject: [PATCH 03/27] fix CTOR-1626 --- modules/centreon-stream-connectors-lib/sc_common.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/centreon-stream-connectors-lib/sc_common.lua b/modules/centreon-stream-connectors-lib/sc_common.lua index 835be9c8..b23220dc 100644 --- a/modules/centreon-stream-connectors-lib/sc_common.lua +++ b/modules/centreon-stream-connectors-lib/sc_common.lua @@ -374,7 +374,7 @@ function ScCommon:sleep(seconds) if type(seconds) == "number" then os.execute("sleep " .. seconds) else - self.sc_logger:error("[sc_common:sleep]: given parameter is not a valid second value. Parameter value: " .. tostrin(seconds) + self.sc_logger:error("[sc_common:sleep]: given parameter is not a valid second value. Parameter value: " .. tostring(seconds) .. ". This will default to: " .. tostring(default_value)) os.execute("sleep " .. default_value) end From 06a73031cdabf0beb1c963a0ce665bfa438882b5 Mon Sep 17 00:00:00 2001 From: omercier Date: Tue, 29 Apr 2025 10:00:04 +0200 Subject: [PATCH 04/27] enh(libs): log an error when log_level is ignored because of its type --- modules/centreon-stream-connectors-lib/sc_params.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/centreon-stream-connectors-lib/sc_params.lua b/modules/centreon-stream-connectors-lib/sc_params.lua index 88f2f4dc..0d5cf141 100644 --- a/modules/centreon-stream-connectors-lib/sc_params.lua +++ b/modules/centreon-stream-connectors-lib/sc_params.lua @@ -1011,7 +1011,11 @@ function ScParams:check_params() -- self.params.allow_insecure_connection = self.common:number_to_boolean(self.common:check_boolean_number_option_syntax(not self.params.allow_insecure_connection, 0)) self.params.verify_certificate = self.common:number_to_boolean(self.common:check_boolean_number_option_syntax(self.params.verify_certificate, 0)) self.params.logfile = self.common:ifnil_or_empty(self.params.logfile, "/var/log/centreon-broker/stream-connector.log") - -- FIXME: Add a control that log_level is a number or convert string to number + + if type(self.params.log_level) ~= "number" then + self.logger:error("[sc_params:check_params]: log_level parameter given as a " .. type(self.params.log_level) .. " (" .. self.params.log_level .. ") instead of a number. Ignored.") + end + self.params.log_level = self.common:ifnil_or_empty(self.params.log_level, 1) self.params.log_curl_commands = self.common:check_boolean_number_option_syntax(self.params.log_curl_commands, 0) self.params.use_long_output = self.common:check_boolean_number_option_syntax(self.params.use_longoutput, 1) From 232b312ad1cd93887c8ed0565b93b317f16d64f0 Mon Sep 17 00:00:00 2001 From: omercier Date: Tue, 29 Apr 2025 18:02:27 +0200 Subject: [PATCH 05/27] feat(prometheus-events-v2): new connector for events --- .../prometheus-pushgateway-events-apiv2.lua | 378 ++++++++++++++++++ 1 file changed, 378 insertions(+) create mode 100644 centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua diff --git a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua new file mode 100644 index 00000000..9df5352c --- /dev/null +++ b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua @@ -0,0 +1,378 @@ +#!/usr/bin/lua +-------------------------------------------------------------------------------- +-- Centreon Broker Splunk Connector Events +-------------------------------------------------------------------------------- + + +-- Libraries +local curl = require "cURL" +local base64 = require("base64") +local sc_common = require("centreon-stream-connectors-lib.sc_common") +local sc_logger = require("centreon-stream-connectors-lib.sc_logger") +local sc_broker = require("centreon-stream-connectors-lib.sc_broker") +local sc_event = require("centreon-stream-connectors-lib.sc_event") +local sc_params = require("centreon-stream-connectors-lib.sc_params") +local sc_macros = require("centreon-stream-connectors-lib.sc_macros") +local sc_flush = require("centreon-stream-connectors-lib.sc_flush") + + +-------------------------------------------------------------------------------- +-- Local functions +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- convert_to_openmetric: [for Prometheus] replace unwanted characters in order to comply with the open metrics format +-- @param {string} string, the string to convert +-- @return {string} string, a string that matches [a-zA-Z0-9_\.]+ +-------------------------------------------------------------------------------- +local function convert_to_openmetric (string) + if string == nil or string == '' or type(string) ~= 'string' then + return false + end + + return string.gsub(string, '[^a-zA-Z0-9_:]', '_') +end + +-------------------------------------------------------------------------------- +-- Classe event_queue +-------------------------------------------------------------------------------- + +local EventQueue = {} +EventQueue.__index = EventQueue + +-------------------------------------------------------------------------------- +---- Constructor +---- @param conf The table given by the init() function and returned from the GUI +---- @return the new EventQueue +---------------------------------------------------------------------------------- + +function EventQueue.new(params) + local self = {} + + local mandatory_parameters = { + } + + self.fail = false + + -- set up log configuration + local logfile = params.logfile or "/var/log/centreon-broker/prometheus-pushgateway-v2-events.log" + local log_level = params.log_level or 1 + + -- initiate mandatory objects + self.sc_logger = sc_logger.new(logfile, log_level) + self.sc_common = sc_common.new(self.sc_logger) + self.sc_broker = sc_broker.new(self.sc_logger) + self.sc_params = sc_params.new(self.sc_common, self.sc_logger) + + -- checking mandatory parameters and setting a fail flag + if not self.sc_params:is_mandatory_config_set(mandatory_parameters, params) then + self.fail = true + end + + -- overriding default parameters for this stream connector if the default values doesn't suit the basic needs + self.sc_params.params.accepted_categories = params.accepted_categories or "neb" + self.sc_params.params.accepted_elements = params.accepted_elements or "host_status,service_status" + + -- prometheus specific parameters + self.sc_params.params.prometheus_url = params.prometheus_url or "http://127.0.0.1:9091" + self.sc_params.params.http_timeout = params.http_timeout or 30 + self.sc_params.params.prometheus_gateway_job = params.prometheus_gateway_job or "monitoring" + + -- apply users params and check syntax of standard ones + self.sc_params:param_override(params) + self.sc_params:check_params() + + self.sc_macros = sc_macros.new(self.sc_params.params, self.sc_logger) + self.format_template = self.sc_params:load_event_format_file() + + -- only load the custom code file, not executed yet + if self.sc_params.load_custom_code_file and not self.sc_params:load_custom_code_file(self.sc_params.params.custom_code_file) then + self.sc_logger:error("[EventQueue:new]: couldn't successfully load the custom code file: " .. tostring(self.sc_params.params.custom_code_file)) + end + + self.sc_params:build_accepted_elements_info() + self.sc_flush = sc_flush.new(self.sc_params.params, self.sc_logger) + + local categories = self.sc_params.params.bbdo.categories + local elements = self.sc_params.params.bbdo.elements + + self.format_event = { + [categories.neb.id] = { + [elements.host_status.id] = function () return self:format_event_host() end, + [elements.service_status.id] = function () return self:format_event_service() end + }, + [categories.bam.id] = {} + } + + self.send_data_method = { + [1] = function (payload, queue_metadata) return self:send_data(payload, queue_metadata) end + } + + self.build_payload_method = { + [1] = function (payload, event) return self:build_payload(payload, event) end + } + + -- return EventQueue object + setmetatable(self, { __index = EventQueue }) + return self +end + +-------------------------------------------------------------------------------- +---- EventQueue:format_event method +---------------------------------------------------------------------------------- +function EventQueue:format_accepted_event() + local category = self.sc_event.event.category + local element = self.sc_event.event.element + local template = self.sc_params.params.format_template[category][element] + self.sc_logger:debug("[EventQueue:format_event]: starting format event") + self.sc_event.event.formated_event = {} + + if self.format_template and template ~= nil and template ~= "" then + for index, value in pairs(template) do + self.sc_event.event.formated_event[index] = self.sc_macros:replace_sc_macro(value, self.sc_event.event) + end + else + -- can't format event if stream connector is not handling this kind of event and that it is not handled with a template file + if not self.format_event[category][element] then + self.sc_logger:error("[format_event]: You are trying to format an event with category: " + .. tostring(self.sc_params.params.reverse_category_mapping[category]) .. " and element: " + .. tostring(self.sc_params.params.reverse_element_mapping[category][element]) + .. ". If it is a not a misconfiguration, you should create a format file to handle this kind of element") + else + self.format_event[category][element]() + end + end + + self:add() + self.sc_logger:debug("[EventQueue:format_event]: event formatting is finished") +end + +function EventQueue:format_event_host() + self.sc_logger:debug("[EventQueue:format_event_host]: starting format event host.") + + local event = self.sc_event.event + local sdesc = "host" + + self.sc_event.event.formated_event = { + event_type = "host", + prom_hname = event.cache.host.name, + prom_sdesc = sdesc, + prom_sdesc_url = base64.encode(sdesc), + state = event.state, + state_type = event.state_type, + hostname = event.cache.host.name, + output = event.output, + } +end + +function EventQueue:format_event_service() + self.sc_logger:debug("[EventQueue:format_event_service]: starting format event service.") + + local event = self.sc_event.event + local sdesc = event.cache.service.description + + event.formated_event = { + event_type = "service", + prom_hname = event.cache.host.name, + prom_sdesc = sdesc, + prom_sdesc_url = base64.encode(sdesc), + state = event.state, + state_type = event.state_type, + hostname = event.cache.host.name, + service_description = sdesc, + output = event.output, + } +end + +-------------------------------------------------------------------------------- +-- EventQueue:add, add an event to the sending queue +-------------------------------------------------------------------------------- +function EventQueue:add() + -- store event in self.events lists + local category = self.sc_event.event.category + local element = self.sc_event.event.element + + self.sc_logger:debug("[EventQueue:add]: add event in queue category: " .. tostring(self.sc_params.params.reverse_category_mapping[category]) + .. " element: " .. tostring(self.sc_params.params.reverse_element_mapping[category][element])) + + self.sc_logger:debug("[EventQueue:add]: queue size before adding event: " .. tostring(#self.sc_flush.queues[category][element].events)) + self.sc_flush.queues[category][element].events[#self.sc_flush.queues[category][element].events + 1] = self.sc_event.event.formated_event + + self.sc_logger:info("[EventQueue:add]: queue size is now: " .. tostring(#self.sc_flush.queues[category][element].events) + .. ", max is: " .. tostring(self.sc_params.params.max_buffer_size)) +end + +-------------------------------------------------------------------------------- +-- EventQueue:build_payload, concatenate data so it is ready to be sent +-- @param payload {string} json encoded string +-- @param event {table} the event that is going to be added to the payload +-- @return payload {string} json encoded string +-------------------------------------------------------------------------------- +function EventQueue:build_payload(payload, event) + if not payload then + payload = event --TBD + else + self.sc_logger:error("[EventQueue:build_payload]: payload should be nil at this point.") + table.insert(payload, event) --TBD + end + + return payload +end + + +function EventQueue:send_data(payload, queue_metadata) + self.sc_logger:debug("[EventQueue:send_data]: Starting to send data") + --self.sc_logger:warning("[EventQueue:send_data]: payload: " .. self.sc_common:dumper(payload)) + local data = "" + local httpResponseBody = "" + local label = "status" + + local name = convert_to_openmetric(payload.prom_hname .. '_' .. payload.prom_sdesc .. ':' .. label .. ':monitoring_status') + data = data .. '# TYPE ' .. name .. ' counter\n' + data = data .. '# HELP ' .. name .. ' 0 is OK, 1 is WARNING, 2 is CRITICAL, 3 is UNKNOWN\n' + if not payload.hostgroupsLabel then + data = data .. name .. '{label="monitoring_status", host="' .. payload.prom_hname .. '", service="' .. payload.prom_sdesc .. '"} ' .. payload.state .. '\n' + else + data = data .. name .. '{label="monitoring_status", host="' .. payload.prom_hname .. '", service="' .. payload.prom_sdesc .. '", ' .. payload.hostgroupsLabel .. '} ' .. payload.state .. '\n' + end + + + local httpRequest = curl.easy() + :setopt_url(self.sc_params.params.prometheus_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance/' .. payload.prom_hname .. '/service@base64/' .. payload.prom_sdesc_url) + :setopt_writefunction( + function (response) + httpResponseBody = httpResponseBody .. tostring(response) + end + ) + :setopt(curl.OPT_TIMEOUT, self.sc_params.params.http_timeout) + :setopt( + curl.OPT_HTTPHEADER, + { + "content-type: application/openmetrics-text" + } + ) + + -- set proxy address configuration + if (self.sc_params.params.proxy_address and self.sc_params.params.proxy_address ~= '') then + if (self.sc_params.params.proxy_port and self.sc_params.params.proxy_port ~= '') then + httpRequest:setopt(curl.OPT_PROXY, self.sc_params.params.proxy_address .. ':' .. self.sc_params.params.proxy_port) + else + self.sc_logger:error("EventQueue:send_data: proxy_port parameter is not set but proxy_address is used") + end + end + + -- set proxy user configuration + if (self.sc_params.params.proxy_username ~= '') then + if (self.sc_params.params.proxy_password ~= '') then + httpRequest:setopt(curl.OPT_PROXYUSERPWD, self.sc_params.params.proxy_username .. ':' .. self.sc_params.params.proxy_password) + else + self.sc_logger:error("EventQueue:send_data: proxy_password parameter is not set but proxy_username is used") + end + end + + -- adding the HTTP POST data + self.sc_logger:debug("EventQueue:send_data: POST data: '" .. data .. "'") + httpRequest:setopt_postfields(data) + + -- performing the HTTP request + httpRequest:perform() + + -- collecting results + local httpResponseCode = httpRequest:getinfo(curl.INFO_RESPONSE_CODE) + + httpRequest:close() + + -- Handling the return code + local retval = false + if httpResponseCode == 200 then + self.sc_logger:info("EventQueue:send_data: HTTP POST request successful: return code is " .. httpResponseCode) + -- now that the data has been sent, we empty the events array + self.events = {} + retval = true + else + self.sc_logger:error("EventQueue:send_data: HTTP POST request FAILED, return code is " .. httpResponseCode .. " message is:\n\"" .. httpResponseBody .. "\n\"\n") + self.sc_logger:error("the body request " .. data) + end + + -- and update the timestamp + self.__internal_ts_last_flush = os.time() + + self.sc_logger:debug("[EventQueue:send_data]: End") + + return retval +end + +-------------------------------------------------------------------------------- +-- Required functions for Broker StreamConnector +-------------------------------------------------------------------------------- + +local queue + +-- Fonction init() +function init(conf) + queue = EventQueue.new(conf) +end + +-------------------------------------------------------------------------------- +-- write, +-- @param {table} event, the event from broker +-- @return {boolean} +-------------------------------------------------------------------------------- +function write (event) + -- skip event if a mandatory parameter is missing + if queue.fail then + queue.sc_logger:error("Skipping event because a mandatory parameter is not set") + return false + end + + -- initiate event object + queue.sc_event = sc_event.new(event, queue.sc_params.params, queue.sc_common, queue.sc_logger, queue.sc_broker) + if queue.sc_event:is_valid_category() then + if queue.sc_event:is_valid_element() then + -- format event if it is validated + if queue.sc_event:is_valid_event() then + queue:format_accepted_event() + end + --- log why the event has been dropped + else + queue.sc_logger:debug("dropping event because element is not valid. Event element is: " + .. tostring(queue.sc_params.params.reverse_element_mapping[queue.sc_event.event.category][queue.sc_event.event.element])) + end + else + queue.sc_logger:debug("dropping event because category is not valid. Event category is: " + .. tostring(queue.sc_params.params.reverse_category_mapping[queue.sc_event.event.category])) + end + + return flush() +end + +-- flush method is called by broker every now and then (more often when broker has nothing else to do) +function flush() + local queues_size = queue.sc_flush:get_queues_size() + + -- nothing to flush + if queues_size == 0 then + return true + end + + -- flush all queues because last global flush is too old + if queue.sc_flush.last_global_flush < os.time() - queue.sc_params.params.max_all_queues_age then + if not queue.sc_flush:flush_all_queues(queue.build_payload_method[1], queue.send_data_method[1]) then + return false + end + + return true + end + + -- flush queues because too many events are stored in them + if queues_size > queue.sc_params.params.max_buffer_size then + if not queue.sc_flush:flush_all_queues(queue.build_payload_method[1], queue.send_data_method[1]) then + return false + end + + return true + end + + -- there are events in the queue but they were not ready to be send + return false +end From 1bd9e4086abafe943073e91b04983af340d6ebd8 Mon Sep 17 00:00:00 2001 From: omercier <32134301+omercier@users.noreply.github.com> Date: Tue, 13 May 2025 08:57:47 +0200 Subject: [PATCH 06/27] force max_buffer_size to 1 because we each service is sent to its own url Co-authored-by: tcharles --- .../prometheus/prometheus-pushgateway-events-apiv2.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua index 9df5352c..220bc17e 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua @@ -77,6 +77,8 @@ function EventQueue.new(params) self.sc_params.params.prometheus_url = params.prometheus_url or "http://127.0.0.1:9091" self.sc_params.params.http_timeout = params.http_timeout or 30 self.sc_params.params.prometheus_gateway_job = params.prometheus_gateway_job or "monitoring" + -- force max_buffer_size to 1 because we each service is sent to its own url + self.sc_params.params.max_buffer_size = 1 -- apply users params and check syntax of standard ones self.sc_params:param_override(params) From f8f647820ee00369d18f72dd16f28badf9fe7f1a Mon Sep 17 00:00:00 2001 From: omercier <32134301+omercier@users.noreply.github.com> Date: Tue, 13 May 2025 08:58:33 +0200 Subject: [PATCH 07/27] convert httpResponseBody to string before logging it Co-authored-by: tcharles --- .../prometheus/prometheus-pushgateway-events-apiv2.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua index 220bc17e..3c4d9339 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua @@ -292,7 +292,7 @@ function EventQueue:send_data(payload, queue_metadata) self.events = {} retval = true else - self.sc_logger:error("EventQueue:send_data: HTTP POST request FAILED, return code is " .. httpResponseCode .. " message is:\n\"" .. httpResponseBody .. "\n\"\n") + self.sc_logger:error("EventQueue:send_data: HTTP POST request FAILED, return code is " .. httpResponseCode .. " message is:\n\"" .. tostring(httpResponseBody) .. "\n\"\n") self.sc_logger:error("the body request " .. data) end From 60a38cfbe46df027a9e8ea9a8db2a8974b6e96e0 Mon Sep 17 00:00:00 2001 From: omercier <32134301+omercier@users.noreply.github.com> Date: Tue, 13 May 2025 08:59:28 +0200 Subject: [PATCH 08/27] remove useless __internal_ts_last_flush update Co-authored-by: tcharles --- .../prometheus/prometheus-pushgateway-events-apiv2.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua index 3c4d9339..e13b4003 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua @@ -296,8 +296,6 @@ function EventQueue:send_data(payload, queue_metadata) self.sc_logger:error("the body request " .. data) end - -- and update the timestamp - self.__internal_ts_last_flush = os.time() self.sc_logger:debug("[EventQueue:send_data]: End") From 1c5fe2dd490840d9fbaa60d78ce3731ca78077c4 Mon Sep 17 00:00:00 2001 From: omercier Date: Thu, 15 May 2025 16:19:12 +0200 Subject: [PATCH 09/27] enh(lib): use mime.b64 --- .../prometheus/prometheus-pushgateway-events-apiv2.lua | 6 +++--- .../prometheus/prometheus-pushgateway-metrics-apiv2.lua | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua index e13b4003..43041de4 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua @@ -6,7 +6,7 @@ -- Libraries local curl = require "cURL" -local base64 = require("base64") +local mime = require("mime") local sc_common = require("centreon-stream-connectors-lib.sc_common") local sc_logger = require("centreon-stream-connectors-lib.sc_logger") local sc_broker = require("centreon-stream-connectors-lib.sc_broker") @@ -159,7 +159,7 @@ function EventQueue:format_event_host() event_type = "host", prom_hname = event.cache.host.name, prom_sdesc = sdesc, - prom_sdesc_url = base64.encode(sdesc), + prom_sdesc_url = mime.b64(sdesc), state = event.state, state_type = event.state_type, hostname = event.cache.host.name, @@ -177,7 +177,7 @@ function EventQueue:format_event_service() event_type = "service", prom_hname = event.cache.host.name, prom_sdesc = sdesc, - prom_sdesc_url = base64.encode(sdesc), + prom_sdesc_url = mime.b64(sdesc), state = event.state, state_type = event.state_type, hostname = event.cache.host.name, diff --git a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua index 35170d25..49cc41c2 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua @@ -6,7 +6,7 @@ -- Libraries local curl = require "cURL" -local base64 = require("base64") +local mime = require("mime") local sc_common = require("centreon-stream-connectors-lib.sc_common") local sc_logger = require("centreon-stream-connectors-lib.sc_logger") local sc_broker = require("centreon-stream-connectors-lib.sc_broker") @@ -290,7 +290,7 @@ function EventQueue:format_metric_host(metric) event.formated_event = { prom_hname = event.cache.host.name, prom_sdesc = sdesc, - prom_sdesc_url = base64.encode(sdesc) + prom_sdesc_url = mime.b64(sdesc) } self.sc_logger:debug("[EventQueue:format_metric_host]: call format_metric ") self:format_metric_event(metric) @@ -309,7 +309,7 @@ function EventQueue:format_metric_service(metric) event.formated_event = { prom_hname = event.cache.host.name, prom_sdesc = sdesc, - prom_sdesc_url = base64.encode(sdesc) + prom_sdesc_url = mime.b64(sdesc) } self.sc_logger:debug("[EventQueue:format_metric_service]: call format_metric ") self:format_metric_event(metric) From e888de00958014d224bd117de92dd40d5e23cd77 Mon Sep 17 00:00:00 2001 From: omercier Date: Fri, 16 May 2025 11:55:31 +0200 Subject: [PATCH 10/27] enh: move formatting of payload where it belongs --- .../prometheus-pushgateway-events-apiv2.lua | 62 ++++++++++++------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua index 43041de4..fa971602 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua @@ -153,26 +153,51 @@ function EventQueue:format_event_host() self.sc_logger:debug("[EventQueue:format_event_host]: starting format event host.") local event = self.sc_event.event + local hname = event.cache.host.name local sdesc = "host" - self.sc_event.event.formated_event = { - event_type = "host", - prom_hname = event.cache.host.name, - prom_sdesc = sdesc, - prom_sdesc_url = mime.b64(sdesc), - state = event.state, - state_type = event.state_type, - hostname = event.cache.host.name, - output = event.output, + local name = convert_to_openmetric(hname .. '_' .. sdesc .. ':status:monitoring_status') + + local data = '# TYPE ' .. name .. ' counter\n' + data = data .. '# HELP ' .. name .. ' 0 is OK, 1 or higher is DOWN\n' + if not event.hostgroupsLabel then + data = data .. name .. '{label="monitoring_status", host="' .. hname .. '", service="' .. sdesc .. '"} ' .. event.state .. '\n' + else + data = data .. name .. '{label="monitoring_status", host="' .. hname .. '", service="' .. sdesc .. '", ' .. event.hostgroupsLabel .. '} ' .. event.state .. '\n' + end + + event.formated_event = { + event_type = "host", + prom_hname = event.cache.host.name, + prom_sdesc = sdesc, + prom_sdesc_url = mime.b64(sdesc), + state = event.state, + state_type = event.state_type, + hostname = hname, + service_description = sdesc, + output = event.output, + formatted_payload = data } + end function EventQueue:format_event_service() self.sc_logger:debug("[EventQueue:format_event_service]: starting format event service.") local event = self.sc_event.event + local hname = event.cache.host.name local sdesc = event.cache.service.description + local name = convert_to_openmetric(hname .. '_' .. sdesc .. ':status:monitoring_status') + + local data = '# TYPE ' .. name .. ' counter\n' + data = data .. '# HELP ' .. name .. ' 0 is OK, 1 is WARNING, 2 is CRITICAL, 3 is UNKNOWN\n' + if not event.hostgroupsLabel then + data = data .. name .. '{label="monitoring_status", host="' .. hname .. '", service="' .. sdesc .. '"} ' .. event.state .. '\n' + else + data = data .. name .. '{label="monitoring_status", host="' .. hname .. '", service="' .. sdesc .. '", ' .. event.hostgroupsLabel .. '} ' .. event.state .. '\n' + end + event.formated_event = { event_type = "service", prom_hname = event.cache.host.name, @@ -180,9 +205,10 @@ function EventQueue:format_event_service() prom_sdesc_url = mime.b64(sdesc), state = event.state, state_type = event.state_type, - hostname = event.cache.host.name, + hostname = hname, service_description = sdesc, output = event.output, + formatted_payload = data } end @@ -225,20 +251,10 @@ end function EventQueue:send_data(payload, queue_metadata) self.sc_logger:debug("[EventQueue:send_data]: Starting to send data") --self.sc_logger:warning("[EventQueue:send_data]: payload: " .. self.sc_common:dumper(payload)) - local data = "" + local httpResponseBody = "" local label = "status" - local name = convert_to_openmetric(payload.prom_hname .. '_' .. payload.prom_sdesc .. ':' .. label .. ':monitoring_status') - data = data .. '# TYPE ' .. name .. ' counter\n' - data = data .. '# HELP ' .. name .. ' 0 is OK, 1 is WARNING, 2 is CRITICAL, 3 is UNKNOWN\n' - if not payload.hostgroupsLabel then - data = data .. name .. '{label="monitoring_status", host="' .. payload.prom_hname .. '", service="' .. payload.prom_sdesc .. '"} ' .. payload.state .. '\n' - else - data = data .. name .. '{label="monitoring_status", host="' .. payload.prom_hname .. '", service="' .. payload.prom_sdesc .. '", ' .. payload.hostgroupsLabel .. '} ' .. payload.state .. '\n' - end - - local httpRequest = curl.easy() :setopt_url(self.sc_params.params.prometheus_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance/' .. payload.prom_hname .. '/service@base64/' .. payload.prom_sdesc_url) :setopt_writefunction( @@ -273,8 +289,8 @@ function EventQueue:send_data(payload, queue_metadata) end -- adding the HTTP POST data - self.sc_logger:debug("EventQueue:send_data: POST data: '" .. data .. "'") - httpRequest:setopt_postfields(data) + self.sc_logger:debug("EventQueue:send_data: POST data: '" .. payload.formatted_payload .. "'") + httpRequest:setopt_postfields(payload.formatted_payload) -- performing the HTTP request httpRequest:perform() From 19fb8ae8fedbccdddf46b89c00f813eceb87aaf2 Mon Sep 17 00:00:00 2001 From: omercier <32134301+omercier@users.noreply.github.com> Date: Fri, 16 May 2025 14:16:57 +0200 Subject: [PATCH 11/27] Apply suggestions from code review by Tanguy Co-authored-by: tcharles --- .../prometheus-pushgateway-metrics-apiv2.lua | 41 +++++++------------ 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua index 49cc41c2..1a9f0832 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua @@ -49,8 +49,8 @@ local function unit_mapping (unit) V = 'volts', A = 'amperes', K = 'kelvins', - ratio = 'ratios', - degres = 'celsius' + ["%"] = 'ratios', + ["°"] = 'celsius' } local unhandledUnit = nil @@ -59,15 +59,9 @@ local function unit_mapping (unit) unit = '' end - if unit == '%' then - unit = unitMapping['ratio'] - elseif unit == '°' then - unit = unitMapping['degres'] - else - if (unitMapping[unit] ~= nil) then - unit = unitMapping[unit] - end - end +if unitMapping[unit] then + unit = unitMapping[unit] +end return unit, true end @@ -398,18 +392,20 @@ function EventQueue:format_metric_event(metric) end -------------------------------------------------------------------------------- --- ifnumber_not_nan: [for Prometheus] check if a number is a number (and not a NaN) +-- ifnumber_not_nan: check if a number is a number (and not a NaN) -- @param {number} number, the number to check -- @return {boolean} -------------------------------------------------------------------------------- -local function ifnumber_not_nan (number) +local function is_number_and_not_a_NaN (number) if (number ~= number) then return false - elseif (type(number) ~= 'number') then + end + + if (type(number) ~= "number") then return false - else - return true end + + return true end -------------------------------------------------------------------------------- @@ -418,14 +414,11 @@ end -- @return {string} metricType, the type of the metric -------------------------------------------------------------------------------- function EventQueue:get_metric_type (perfdata) - local metricType = nil; if (ifnumber_not_nan(perfdata.max)) then - metricType = 'gauge' - else - metricType = 'counter' + return "gauge" end - return metricType + return "counter" end -------------------------------------------------------------------------------- @@ -521,13 +514,9 @@ function EventQueue:send_data(payload, queue_metadata) self.events = {} retval = true else - self.sc_logger:error("EventQueue:send_data: HTTP POST request FAILED, return code is " .. httpResponseCode .. " message is:\n\"" .. httpResponseBody .. "\n\"\n") + self.sc_logger:error("EventQueue:send_data: HTTP POST request FAILED, return code is " .. httpResponseCode .. " message is:\n\"" .. tostring(httpResponseBody) .. "\n\"\n") self.sc_logger:error("the body request " .. httpPostData) end - - -- and update the timestamp - self.__internal_ts_last_flush = os.time() - self.sc_logger:debug("[EventQueue:send_data]: End") return retval end From 05ecaf90a607c963a2232802d47d4e073f0a0cde Mon Sep 17 00:00:00 2001 From: omercier Date: Fri, 16 May 2025 16:08:26 +0200 Subject: [PATCH 12/27] fix: default value for log_level was empty string instead of number --- modules/centreon-stream-connectors-lib/sc_params.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/centreon-stream-connectors-lib/sc_params.lua b/modules/centreon-stream-connectors-lib/sc_params.lua index 0d5cf141..fb9a0514 100644 --- a/modules/centreon-stream-connectors-lib/sc_params.lua +++ b/modules/centreon-stream-connectors-lib/sc_params.lua @@ -124,7 +124,7 @@ function sc_params.new(common, logger) -- logging parameters logfile = "", - log_level = "", + log_level = 1, log_curl_commands = 0, -- metric From 5b1594440e85827fdec61d9e808c86ac172b1dc7 Mon Sep 17 00:00:00 2001 From: omercier Date: Fri, 16 May 2025 16:11:57 +0200 Subject: [PATCH 13/27] end: use existing generic parameters to format metrics instead of hard coded --- .../prometheus-pushgateway-events-apiv2.lua | 25 +++------- .../prometheus-pushgateway-metrics-apiv2.lua | 46 +++++++------------ 2 files changed, 24 insertions(+), 47 deletions(-) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua index fa971602..65367008 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua @@ -5,7 +5,7 @@ -- Libraries -local curl = require "cURL" +local curl = require("cURL") local mime = require("mime") local sc_common = require("centreon-stream-connectors-lib.sc_common") local sc_logger = require("centreon-stream-connectors-lib.sc_logger") @@ -20,19 +20,6 @@ local sc_flush = require("centreon-stream-connectors-lib.sc_flush") -- Local functions -------------------------------------------------------------------------------- --------------------------------------------------------------------------------- --- convert_to_openmetric: [for Prometheus] replace unwanted characters in order to comply with the open metrics format --- @param {string} string, the string to convert --- @return {string} string, a string that matches [a-zA-Z0-9_\.]+ --------------------------------------------------------------------------------- -local function convert_to_openmetric (string) - if string == nil or string == '' or type(string) ~= 'string' then - return false - end - - return string.gsub(string, '[^a-zA-Z0-9_:]', '_') -end - -------------------------------------------------------------------------------- -- Classe event_queue -------------------------------------------------------------------------------- @@ -70,8 +57,10 @@ function EventQueue.new(params) end -- overriding default parameters for this stream connector if the default values doesn't suit the basic needs - self.sc_params.params.accepted_categories = params.accepted_categories or "neb" - self.sc_params.params.accepted_elements = params.accepted_elements or "host_status,service_status" + self.sc_params.params.accepted_categories = params.accepted_categories or "neb" + self.sc_params.params.accepted_elements = params.accepted_elements or "host_status,service_status" + self.sc_params.params.enable_host_status_dedup = params.enable_host_status_dedup or 1 + self.sc_params.params.enable_service_status_dedup = params.enable_service_status_dedup or 1 -- prometheus specific parameters self.sc_params.params.prometheus_url = params.prometheus_url or "http://127.0.0.1:9091" @@ -156,7 +145,7 @@ function EventQueue:format_event_host() local hname = event.cache.host.name local sdesc = "host" - local name = convert_to_openmetric(hname .. '_' .. sdesc .. ':status:monitoring_status') + local name = string.gsub(hname .. '_' .. sdesc .. ':status:monitoring_status', self.sc_params.metric_name_regex, self.sc_params.metric_replacement_character) local data = '# TYPE ' .. name .. ' counter\n' data = data .. '# HELP ' .. name .. ' 0 is OK, 1 or higher is DOWN\n' @@ -188,7 +177,7 @@ function EventQueue:format_event_service() local hname = event.cache.host.name local sdesc = event.cache.service.description - local name = convert_to_openmetric(hname .. '_' .. sdesc .. ':status:monitoring_status') + local name = string.gsub(hname .. '_' .. sdesc .. ':status:monitoring_status', self.sc_params.metric_name_regex, self.sc_params.metric_replacement_character) local data = '# TYPE ' .. name .. ' counter\n' data = data .. '# HELP ' .. name .. ' 0 is OK, 1 is WARNING, 2 is CRITICAL, 3 is UNKNOWN\n' diff --git a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua index 1a9f0832..b5d0bd32 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua @@ -5,7 +5,7 @@ -- Libraries -local curl = require "cURL" +local curl = require("cURL") local mime = require("mime") local sc_common = require("centreon-stream-connectors-lib.sc_common") local sc_logger = require("centreon-stream-connectors-lib.sc_logger") @@ -21,19 +21,6 @@ local sc_metrics = require("centreon-stream-connectors-lib.sc_metrics") -- Local functions -------------------------------------------------------------------------------- --------------------------------------------------------------------------------- --- convert_to_openmetric: [for Prometheus] replace unwanted characters in order to comply with the open metrics format --- @param {string} string, the string to convert --- @return {string} string, a string that matches [a-zA-Z0-9_\.]+ --------------------------------------------------------------------------------- -local function convert_to_openmetric (string) - if string == nil or string == '' or type(string) ~= 'string' then - return false - end - - return string.gsub(string, '[^a-zA-Z0-9_:]', '_') -end - -------------------------------------------------------------------------------- -- unit_mapping: convert perfdata units to openmetrics standard -- @param {string} unit, the unit value @@ -105,8 +92,10 @@ function EventQueue.new(params) params.max_buffer_size = 1 -- overriding default parameters for this stream connector if the default values doesn't suit the basic needs - self.sc_params.params.accepted_categories = params.accepted_categories or "neb" - self.sc_params.params.accepted_elements = params.accepted_elements or "host_status,service_status" + self.sc_params.params.accepted_categories = params.accepted_categories or "neb" + self.sc_params.params.accepted_elements = params.accepted_elements or "host_status,service_status" + self.sc_params.metric_name_regex = params.metric_name_regex or '[^a-zA-Z0-9_:]' + self.sc_params.metric_replacement_character = params.metric_replacement_character or '_' -- prometheus specific parameters self.sc_params.params.prometheus_url = params.prometheus_url or "http://127.0.0.1:9091" @@ -328,10 +317,10 @@ function EventQueue:add_unit_info (label, unit, name) end -------------------------------------------------------------------------------- --- create_metric_name: concatenates data to create the metric name --- @param {string} label, the name of the perfdata --- @param {string} unit, the unit name --- @return {string} name, the prometheus metric name (open metric format) +--- create_metric_name: concatenates data to create the metric name +--- @param {string} label, the name of the perfdata +--- @param {string} unit, the unit name +--- @return {string} name, the prometheus metric name (open metric format) -------------------------------------------------------------------------------- function EventQueue:create_metric_name (label, unit) local name = '' @@ -355,12 +344,12 @@ function EventQueue:create_metric_name (label, unit) end end - return convert_to_openmetric(name) + return string.gsub(name, self.sc_params.metric_name_regex, self.sc_params.metric_replacement_character) end -------------------------------------------------------------------------------- ----- EventQueue:format_metric_service method --- @param metric {table} a single metric data +--- EventQueue:format_metric_service method +--- @param metric {table} a single metric data ------------------------------------------------------------------------------- function EventQueue:format_metric_event(metric) self.sc_logger:debug("[EventQueue:format_metric]: start real format metric ") @@ -369,10 +358,9 @@ function EventQueue:format_metric_event(metric) local unit = unit_mapping(metric.uom) local label = metric.metric_name local name = self:create_metric_name(label, unit) - local data = '' local sdesc = event.formated_event.prom_sdesc - data = '# TYPE ' .. name .. ' ' .. type .. '\n' + local data = '# TYPE ' .. name .. ' ' .. type .. '\n' data = data .. self:add_unit_info(label, unit, name) if not event.hostgroupsLabel then @@ -392,9 +380,9 @@ function EventQueue:format_metric_event(metric) end -------------------------------------------------------------------------------- --- ifnumber_not_nan: check if a number is a number (and not a NaN) --- @param {number} number, the number to check --- @return {boolean} +--- is_number_and_not_a_NaN: check if a number is a number (and not a NaN) +--- @param {number} number, the number to check +--- @return {boolean} -------------------------------------------------------------------------------- local function is_number_and_not_a_NaN (number) if (number ~= number) then @@ -414,7 +402,7 @@ end -- @return {string} metricType, the type of the metric -------------------------------------------------------------------------------- function EventQueue:get_metric_type (perfdata) - if (ifnumber_not_nan(perfdata.max)) then + if (is_number_and_not_a_NaN(perfdata.max)) then return "gauge" end From 05129e0c885973c5e38a3c4ed0cac4ad745194ed Mon Sep 17 00:00:00 2001 From: omercier Date: Fri, 16 May 2025 16:39:55 +0200 Subject: [PATCH 14/27] enh: handle enable_extended_metric_name param --- .../prometheus-pushgateway-metrics-apiv2.lua | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua index b5d0bd32..9149e703 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua @@ -98,9 +98,10 @@ function EventQueue.new(params) self.sc_params.metric_replacement_character = params.metric_replacement_character or '_' -- prometheus specific parameters - self.sc_params.params.prometheus_url = params.prometheus_url or "http://127.0.0.1:9091" - self.sc_params.params.http_timeout = params.http_timeout or 30 - self.sc_params.params.prometheus_gateway_job = params.prometheus_gateway_job or "monitoring" + self.sc_params.params.prometheus_url = params.prometheus_url or "http://127.0.0.1:9091" + self.sc_params.params.http_timeout = params.http_timeout or 30 + self.sc_params.params.prometheus_gateway_job = params.prometheus_gateway_job or "monitoring" + self.sc_params.params.enable_extended_metric_name = params.enable_extended_metric_name or 1 -- apply users params and check syntax of standard ones self.sc_params:param_override(params) @@ -324,25 +325,17 @@ end -------------------------------------------------------------------------------- function EventQueue:create_metric_name (label, unit) local name = '' - local sdesc = 'host' - if (self.sc_event.event.service_description) then - sdesc = self.sc_event.event.service_description - end + local sdesc = self.sc_event.event.service_description or 'host' local hname = self.sc_event.event.cache.host.name - if (unit ~= '') then - if (self.enable_extended_metric_name == 0) then - name = label .. '_' .. unit - else - name = hname .. '_' .. sdesc .. ':' .. label .. '_' .. unit - end - else - if (self.enable_extended_metric_name == 0) then + if (self.sc_params.params.enable_extended_metric_name == 0) then name = label else name = hname .. '_' .. sdesc .. ':' .. label end - end + if (unit ~= '') then + name = name .. '_' .. unit + end return string.gsub(name, self.sc_params.metric_name_regex, self.sc_params.metric_replacement_character) end From dcef3bdd59ef88c8d33240fbc008eb8c06a2b9be Mon Sep 17 00:00:00 2001 From: omercier Date: Fri, 16 May 2025 18:08:56 +0200 Subject: [PATCH 15/27] enh: add log_curl_command and send_data debug --- .../prometheus-pushgateway-events-apiv2.lua | 9 ++++++++- .../prometheus-pushgateway-metrics-apiv2.lua | 13 +++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua index 65367008..32d822e6 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua @@ -277,10 +277,17 @@ function EventQueue:send_data(payload, queue_metadata) end end + -- write payload in the logfile for test purpose + if self.sc_params.params.send_data_test == 1 then + self.sc_logger:notice("[send_data]: " .. tostring(payload.formatted_payload)) + return true + end -- adding the HTTP POST data - self.sc_logger:debug("EventQueue:send_data: POST data: '" .. payload.formatted_payload .. "'") httpRequest:setopt_postfields(payload.formatted_payload) + -- log the curl command for troubleshooting + self.sc_logger:log_curl_command(url, queue_metadata, self.sc_params.params, payload.formatted_payload) + -- performing the HTTP request httpRequest:perform() diff --git a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua index 9149e703..2781867d 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua @@ -441,9 +441,10 @@ function EventQueue:send_data(payload, queue_metadata) self.sc_logger:debug("[EventQueue:send_data]: Starting to send data") local httpPostData = payload.payload local httpResponseBody = "" + local url = self.sc_params.params.prometheus_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance/' .. payload.prom_hname .. '/service@base64/' .. payload.prom_sdesc_url local httpRequest = curl.easy() - :setopt_url(self.sc_params.params.prometheus_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance/' .. payload.prom_hname .. '/service@base64/' .. payload.prom_sdesc_url) + :setopt_url(url) :setopt_writefunction( function (response) httpResponseBody = httpResponseBody .. tostring(response) @@ -475,10 +476,18 @@ function EventQueue:send_data(payload, queue_metadata) end end + -- write payload in the logfile for test purpose + if self.sc_params.params.send_data_test == 1 then + self.sc_logger:notice("[send_data]: " .. tostring(httpPostData)) + return true + end + -- adding the HTTP POST data - self.sc_logger:debug("EventQueue:send_data: POST data: '" .. httpPostData .. "'") httpRequest:setopt_postfields(httpPostData) + -- log the curl command for troubleshooting + self.sc_logger:log_curl_command(url, queue_metadata, self.sc_params.params, httpPostData) + -- performing the HTTP request httpRequest:perform() From ae1c304c41c0581fb4978b8a5578f4dd0e8141b5 Mon Sep 17 00:00:00 2001 From: omercier Date: Tue, 20 May 2025 15:37:09 +0200 Subject: [PATCH 16/27] enh: various enhancements - metrics naming looks ok: no unit redundancy in names - instances and subinstances handled - removed hosts events since theyre not relevant - the headers were not displayed by log_curl_command --- .../prometheus-pushgateway-metrics-apiv2.lua | 61 ++++++++++++++----- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua index 2781867d..17557b07 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua @@ -37,7 +37,8 @@ local function unit_mapping (unit) A = 'amperes', K = 'kelvins', ["%"] = 'ratios', - ["°"] = 'celsius' + ["°"] = 'celsius', + ["€"] = 'euros' } local unhandledUnit = nil @@ -93,7 +94,7 @@ function EventQueue.new(params) -- overriding default parameters for this stream connector if the default values doesn't suit the basic needs self.sc_params.params.accepted_categories = params.accepted_categories or "neb" - self.sc_params.params.accepted_elements = params.accepted_elements or "host_status,service_status" + self.sc_params.params.accepted_elements = params.accepted_elements or "service_status" self.sc_params.metric_name_regex = params.metric_name_regex or '[^a-zA-Z0-9_:]' self.sc_params.metric_replacement_character = params.metric_replacement_character or '_' @@ -325,7 +326,7 @@ end -------------------------------------------------------------------------------- function EventQueue:create_metric_name (label, unit) local name = '' - local sdesc = self.sc_event.event.service_description or 'host' + local sdesc = self.sc_event.event.cache.service.description or 'host' local hname = self.sc_event.event.cache.host.name if (self.sc_params.params.enable_extended_metric_name == 0) then @@ -334,9 +335,12 @@ function EventQueue:create_metric_name (label, unit) name = hname .. '_' .. sdesc .. ':' .. label end if (unit ~= '') then - name = name .. '_' .. unit + local pos_unit = string.find(name, unit) + -- we append the unit only if the name is not already ending with it + if not pos_unit or not (pos_unit > 0 and pos_unit == string.len(name) - string.len(unit) + 1) then + name = name .. '_' .. unit + end end - return string.gsub(name, self.sc_params.metric_name_regex, self.sc_params.metric_replacement_character) end @@ -349,20 +353,47 @@ function EventQueue:format_metric_event(metric) local event = self.sc_event.event local type = self:get_metric_type(metric) local unit = unit_mapping(metric.uom) - local label = metric.metric_name + local label = '' + + -- case when the metric belongs to an instance + if metric.instance and metric.instance ~= '' then + label = metric.instance .. '_' + end + + -- case when there are sub-levels of an instance + local i, sub_instance + for i, sub_instance in ipairs(metric.subinstance) do + label = label .. sub_instance .. '_' + end + + label = label .. metric.metric_name + local name = self:create_metric_name(label, unit) local sdesc = event.formated_event.prom_sdesc + -- Example of data to send + --[[ +# TYPE CENTREON_proc_crond:nbproc counter +CENTREON_proc_crond:nbproc{label="nbproc", host="CENTREON", service="proc-crond"} 1.0 + ]] + -- Other example + --[[ +# TYPE CENTREON_Ah_Que_Coucou:bnp_bank_business_gold_reserve_euros counter +# UNIT CENTREON_Ah_Que_Coucou:bnp_bank_business_gold_reserve_euros +CENTREON_Financial:acme_bank_business_gold_reserve_euros{label="acme_bank_business_gold.reserve.euros", host="CENTREON", service="Financial"} 3.0 + ]] + local data = '# TYPE ' .. name .. ' ' .. type .. '\n' data = data .. self:add_unit_info(label, unit, name) - - if not event.hostgroupsLabel then - data = data .. name .. '{label="' .. label .. '", host="' .. event.cache.host.name .. '", service="' .. sdesc .. '"} ' .. metric.value .. '\n' - else - data = data .. name .. '{label="' .. label .. '", host="' .. event.cache.host.name .. '", service="' .. sdesc .. '", ' .. event.hostgroupsLabel .. '} ' .. metric.value .. '\n' + data = data .. name .. '{label="' .. label .. '", host="' .. event.cache.host.name .. '", service="' .. sdesc .. '"' + + if event.hostgroupsLabel then + data = data .. ', ' .. event.hostgroupsLabel end - if (self.enable_threshold_metrics == 1) then + data = data .. '} ' .. metric.value .. '\n' + + if (self.enable_threshold_metrics == 1) then data = data .. self:threshold_metrics(metric, label, unit, type) end @@ -443,6 +474,8 @@ function EventQueue:send_data(payload, queue_metadata) local httpResponseBody = "" local url = self.sc_params.params.prometheus_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance/' .. payload.prom_hname .. '/service@base64/' .. payload.prom_sdesc_url + queue_metadata.headers = { "content-type: application/openmetrics-text" } + local httpRequest = curl.easy() :setopt_url(url) :setopt_writefunction( @@ -453,9 +486,7 @@ function EventQueue:send_data(payload, queue_metadata) :setopt(curl.OPT_TIMEOUT, self.sc_params.params.http_timeout) :setopt( curl.OPT_HTTPHEADER, - { - "content-type: application/openmetrics-text" - } + queue_metadata.headers ) -- set proxy address configuration From ae7fc3d911815863a49bb72c06827050522e6964 Mon Sep 17 00:00:00 2001 From: omercier Date: Tue, 20 May 2025 17:02:30 +0200 Subject: [PATCH 17/27] fix: fixed some silly mistakes --- .../prometheus-pushgateway-events-apiv2.lua | 34 +++++++++++-------- .../prometheus-pushgateway-metrics-apiv2.lua | 10 +++--- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua index 32d822e6..8011c14e 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua @@ -55,19 +55,22 @@ function EventQueue.new(params) if not self.sc_params:is_mandatory_config_set(mandatory_parameters, params) then self.fail = true end + + params.max_buffer_size = 1 -- overriding default parameters for this stream connector if the default values doesn't suit the basic needs - self.sc_params.params.accepted_categories = params.accepted_categories or "neb" - self.sc_params.params.accepted_elements = params.accepted_elements or "host_status,service_status" - self.sc_params.params.enable_host_status_dedup = params.enable_host_status_dedup or 1 - self.sc_params.params.enable_service_status_dedup = params.enable_service_status_dedup or 1 + self.sc_params.params.accepted_categories = params.accepted_categories or "neb" + self.sc_params.params.accepted_elements = params.accepted_elements or "host_status,service_status" + self.sc_params.params.metric_name_regex = params.metric_name_regex or '[^a-zA-Z0-9_:]' + self.sc_params.params.metric_replacement_character = params.metric_replacement_character or '_' + self.sc_params.params.enable_host_status_dedup = params.enable_host_status_dedup or 1 + self.sc_params.params.enable_service_status_dedup = params.enable_service_status_dedup or 1 -- prometheus specific parameters self.sc_params.params.prometheus_url = params.prometheus_url or "http://127.0.0.1:9091" self.sc_params.params.http_timeout = params.http_timeout or 30 self.sc_params.params.prometheus_gateway_job = params.prometheus_gateway_job or "monitoring" -- force max_buffer_size to 1 because we each service is sent to its own url - self.sc_params.params.max_buffer_size = 1 -- apply users params and check syntax of standard ones self.sc_params:param_override(params) @@ -145,7 +148,7 @@ function EventQueue:format_event_host() local hname = event.cache.host.name local sdesc = "host" - local name = string.gsub(hname .. '_' .. sdesc .. ':status:monitoring_status', self.sc_params.metric_name_regex, self.sc_params.metric_replacement_character) + local name = string.gsub(hname .. '_' .. sdesc .. ':monitoring_status', self.sc_params.params.metric_name_regex, self.sc_params.params.metric_replacement_character) local data = '# TYPE ' .. name .. ' counter\n' data = data .. '# HELP ' .. name .. ' 0 is OK, 1 or higher is DOWN\n' @@ -177,10 +180,10 @@ function EventQueue:format_event_service() local hname = event.cache.host.name local sdesc = event.cache.service.description - local name = string.gsub(hname .. '_' .. sdesc .. ':status:monitoring_status', self.sc_params.metric_name_regex, self.sc_params.metric_replacement_character) + local name = string.gsub(hname .. '_' .. sdesc .. ':monitoring_status', self.sc_params.params.metric_name_regex, self.sc_params.params.metric_replacement_character) local data = '# TYPE ' .. name .. ' counter\n' - data = data .. '# HELP ' .. name .. ' 0 is OK, 1 is WARNING, 2 is CRITICAL, 3 is UNKNOWN\n' + data = data .. '# HELP ' .. name .. ' 0 is OK, 1 is WARNING, 2 is CRITICAL, 3 or higher is UNKNOWN\n' if not event.hostgroupsLabel then data = data .. name .. '{label="monitoring_status", host="' .. hname .. '", service="' .. sdesc .. '"} ' .. event.state .. '\n' else @@ -227,10 +230,10 @@ end -------------------------------------------------------------------------------- function EventQueue:build_payload(payload, event) if not payload then - payload = event --TBD + payload = event else self.sc_logger:error("[EventQueue:build_payload]: payload should be nil at this point.") - table.insert(payload, event) --TBD + table.insert(payload, event) end return payload @@ -243,9 +246,12 @@ function EventQueue:send_data(payload, queue_metadata) local httpResponseBody = "" local label = "status" + local url = self.sc_params.params.prometheus_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance/' .. payload.prom_hname .. '/service@base64/' .. payload.prom_sdesc_url + + queue_metadata.headers = { "content-type: application/openmetrics-text" } local httpRequest = curl.easy() - :setopt_url(self.sc_params.params.prometheus_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance/' .. payload.prom_hname .. '/service@base64/' .. payload.prom_sdesc_url) + :setopt_url(url) :setopt_writefunction( function (response) httpResponseBody = httpResponseBody .. tostring(response) @@ -254,9 +260,7 @@ function EventQueue:send_data(payload, queue_metadata) :setopt(curl.OPT_TIMEOUT, self.sc_params.params.http_timeout) :setopt( curl.OPT_HTTPHEADER, - { - "content-type: application/openmetrics-text" - } + queue_metadata.headers ) -- set proxy address configuration @@ -305,7 +309,7 @@ function EventQueue:send_data(payload, queue_metadata) retval = true else self.sc_logger:error("EventQueue:send_data: HTTP POST request FAILED, return code is " .. httpResponseCode .. " message is:\n\"" .. tostring(httpResponseBody) .. "\n\"\n") - self.sc_logger:error("the body request " .. data) + self.sc_logger:error("the body request " .. payload.formatted_payload) end diff --git a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua index 17557b07..85add8eb 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua @@ -93,10 +93,10 @@ function EventQueue.new(params) params.max_buffer_size = 1 -- overriding default parameters for this stream connector if the default values doesn't suit the basic needs - self.sc_params.params.accepted_categories = params.accepted_categories or "neb" - self.sc_params.params.accepted_elements = params.accepted_elements or "service_status" - self.sc_params.metric_name_regex = params.metric_name_regex or '[^a-zA-Z0-9_:]' - self.sc_params.metric_replacement_character = params.metric_replacement_character or '_' + self.sc_params.params.accepted_categories = params.accepted_categories or "neb" + self.sc_params.params.accepted_elements = params.accepted_elements or "service_status" + self.sc_params.params.metric_name_regex = params.metric_name_regex or '[^a-zA-Z0-9_:]' + self.sc_params.params.metric_replacement_character = params.metric_replacement_character or '_' -- prometheus specific parameters self.sc_params.params.prometheus_url = params.prometheus_url or "http://127.0.0.1:9091" @@ -341,7 +341,7 @@ function EventQueue:create_metric_name (label, unit) name = name .. '_' .. unit end end - return string.gsub(name, self.sc_params.metric_name_regex, self.sc_params.metric_replacement_character) + return string.gsub(name, self.sc_params.params.metric_name_regex, self.sc_params.params.metric_replacement_character) end -------------------------------------------------------------------------------- From 0d1bc309543d093b8a26fb87533d5df6c67753d4 Mon Sep 17 00:00:00 2001 From: omercier Date: Tue, 20 May 2025 17:12:09 +0200 Subject: [PATCH 18/27] enh: partially convert camelCase to snake_case --- .../prometheus-pushgateway-events-apiv2.lua | 92 ++++++++-------- .../prometheus-pushgateway-metrics-apiv2.lua | 102 +++++++++--------- 2 files changed, 97 insertions(+), 97 deletions(-) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua index 8011c14e..619d394d 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua @@ -24,16 +24,16 @@ local sc_flush = require("centreon-stream-connectors-lib.sc_flush") -- Classe event_queue -------------------------------------------------------------------------------- -local EventQueue = {} -EventQueue.__index = EventQueue +local event_queue = {} +event_queue.__index = event_queue -------------------------------------------------------------------------------- ---- Constructor ---- @param conf The table given by the init() function and returned from the GUI ----- @return the new EventQueue +---- @return the new event_queue ---------------------------------------------------------------------------------- -function EventQueue.new(params) +function event_queue.new(params) local self = {} local mandatory_parameters = { @@ -81,7 +81,7 @@ function EventQueue.new(params) -- only load the custom code file, not executed yet if self.sc_params.load_custom_code_file and not self.sc_params:load_custom_code_file(self.sc_params.params.custom_code_file) then - self.sc_logger:error("[EventQueue:new]: couldn't successfully load the custom code file: " .. tostring(self.sc_params.params.custom_code_file)) + self.sc_logger:error("[event_queue:new]: couldn't successfully load the custom code file: " .. tostring(self.sc_params.params.custom_code_file)) end self.sc_params:build_accepted_elements_info() @@ -106,19 +106,19 @@ function EventQueue.new(params) [1] = function (payload, event) return self:build_payload(payload, event) end } - -- return EventQueue object - setmetatable(self, { __index = EventQueue }) + -- return event_queue object + setmetatable(self, { __index = event_queue }) return self end -------------------------------------------------------------------------------- ----- EventQueue:format_event method +---- event_queue:format_event method ---------------------------------------------------------------------------------- -function EventQueue:format_accepted_event() +function event_queue:format_accepted_event() local category = self.sc_event.event.category local element = self.sc_event.event.element local template = self.sc_params.params.format_template[category][element] - self.sc_logger:debug("[EventQueue:format_event]: starting format event") + self.sc_logger:debug("[event_queue:format_event]: starting format event") self.sc_event.event.formated_event = {} if self.format_template and template ~= nil and template ~= "" then @@ -138,11 +138,11 @@ function EventQueue:format_accepted_event() end self:add() - self.sc_logger:debug("[EventQueue:format_event]: event formatting is finished") + self.sc_logger:debug("[event_queue:format_event]: event formatting is finished") end -function EventQueue:format_event_host() - self.sc_logger:debug("[EventQueue:format_event_host]: starting format event host.") +function event_queue:format_event_host() + self.sc_logger:debug("[event_queue:format_event_host]: starting format event host.") local event = self.sc_event.event local hname = event.cache.host.name @@ -152,10 +152,10 @@ function EventQueue:format_event_host() local data = '# TYPE ' .. name .. ' counter\n' data = data .. '# HELP ' .. name .. ' 0 is OK, 1 or higher is DOWN\n' - if not event.hostgroupsLabel then + if not event.hostgroups_label then data = data .. name .. '{label="monitoring_status", host="' .. hname .. '", service="' .. sdesc .. '"} ' .. event.state .. '\n' else - data = data .. name .. '{label="monitoring_status", host="' .. hname .. '", service="' .. sdesc .. '", ' .. event.hostgroupsLabel .. '} ' .. event.state .. '\n' + data = data .. name .. '{label="monitoring_status", host="' .. hname .. '", service="' .. sdesc .. '", ' .. event.hostgroups_label .. '} ' .. event.state .. '\n' end event.formated_event = { @@ -173,8 +173,8 @@ function EventQueue:format_event_host() end -function EventQueue:format_event_service() - self.sc_logger:debug("[EventQueue:format_event_service]: starting format event service.") +function event_queue:format_event_service() + self.sc_logger:debug("[event_queue:format_event_service]: starting format event service.") local event = self.sc_event.event local hname = event.cache.host.name @@ -184,10 +184,10 @@ function EventQueue:format_event_service() local data = '# TYPE ' .. name .. ' counter\n' data = data .. '# HELP ' .. name .. ' 0 is OK, 1 is WARNING, 2 is CRITICAL, 3 or higher is UNKNOWN\n' - if not event.hostgroupsLabel then + if not event.hostgroups_label then data = data .. name .. '{label="monitoring_status", host="' .. hname .. '", service="' .. sdesc .. '"} ' .. event.state .. '\n' else - data = data .. name .. '{label="monitoring_status", host="' .. hname .. '", service="' .. sdesc .. '", ' .. event.hostgroupsLabel .. '} ' .. event.state .. '\n' + data = data .. name .. '{label="monitoring_status", host="' .. hname .. '", service="' .. sdesc .. '", ' .. event.hostgroups_label .. '} ' .. event.state .. '\n' end event.formated_event = { @@ -205,34 +205,34 @@ function EventQueue:format_event_service() end -------------------------------------------------------------------------------- --- EventQueue:add, add an event to the sending queue +-- event_queue:add, add an event to the sending queue -------------------------------------------------------------------------------- -function EventQueue:add() +function event_queue:add() -- store event in self.events lists local category = self.sc_event.event.category local element = self.sc_event.event.element - self.sc_logger:debug("[EventQueue:add]: add event in queue category: " .. tostring(self.sc_params.params.reverse_category_mapping[category]) + self.sc_logger:debug("[event_queue:add]: add event in queue category: " .. tostring(self.sc_params.params.reverse_category_mapping[category]) .. " element: " .. tostring(self.sc_params.params.reverse_element_mapping[category][element])) - self.sc_logger:debug("[EventQueue:add]: queue size before adding event: " .. tostring(#self.sc_flush.queues[category][element].events)) + self.sc_logger:debug("[event_queue:add]: queue size before adding event: " .. tostring(#self.sc_flush.queues[category][element].events)) self.sc_flush.queues[category][element].events[#self.sc_flush.queues[category][element].events + 1] = self.sc_event.event.formated_event - self.sc_logger:info("[EventQueue:add]: queue size is now: " .. tostring(#self.sc_flush.queues[category][element].events) + self.sc_logger:info("[event_queue:add]: queue size is now: " .. tostring(#self.sc_flush.queues[category][element].events) .. ", max is: " .. tostring(self.sc_params.params.max_buffer_size)) end -------------------------------------------------------------------------------- --- EventQueue:build_payload, concatenate data so it is ready to be sent +-- event_queue:build_payload, concatenate data so it is ready to be sent -- @param payload {string} json encoded string -- @param event {table} the event that is going to be added to the payload -- @return payload {string} json encoded string -------------------------------------------------------------------------------- -function EventQueue:build_payload(payload, event) +function event_queue:build_payload(payload, event) if not payload then payload = event else - self.sc_logger:error("[EventQueue:build_payload]: payload should be nil at this point.") + self.sc_logger:error("[event_queue:build_payload]: payload should be nil at this point.") table.insert(payload, event) end @@ -240,21 +240,21 @@ function EventQueue:build_payload(payload, event) end -function EventQueue:send_data(payload, queue_metadata) - self.sc_logger:debug("[EventQueue:send_data]: Starting to send data") - --self.sc_logger:warning("[EventQueue:send_data]: payload: " .. self.sc_common:dumper(payload)) +function event_queue:send_data(payload, queue_metadata) + self.sc_logger:debug("[event_queue:send_data]: Starting to send data") + --self.sc_logger:warning("[event_queue:send_data]: payload: " .. self.sc_common:dumper(payload)) - local httpResponseBody = "" + local http_response_body = "" local label = "status" local url = self.sc_params.params.prometheus_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance/' .. payload.prom_hname .. '/service@base64/' .. payload.prom_sdesc_url queue_metadata.headers = { "content-type: application/openmetrics-text" } - local httpRequest = curl.easy() + local http_request = curl.easy() :setopt_url(url) :setopt_writefunction( function (response) - httpResponseBody = httpResponseBody .. tostring(response) + http_response_body = http_response_body .. tostring(response) end ) :setopt(curl.OPT_TIMEOUT, self.sc_params.params.http_timeout) @@ -266,18 +266,18 @@ function EventQueue:send_data(payload, queue_metadata) -- set proxy address configuration if (self.sc_params.params.proxy_address and self.sc_params.params.proxy_address ~= '') then if (self.sc_params.params.proxy_port and self.sc_params.params.proxy_port ~= '') then - httpRequest:setopt(curl.OPT_PROXY, self.sc_params.params.proxy_address .. ':' .. self.sc_params.params.proxy_port) + http_request:setopt(curl.OPT_PROXY, self.sc_params.params.proxy_address .. ':' .. self.sc_params.params.proxy_port) else - self.sc_logger:error("EventQueue:send_data: proxy_port parameter is not set but proxy_address is used") + self.sc_logger:error("event_queue:send_data: proxy_port parameter is not set but proxy_address is used") end end -- set proxy user configuration if (self.sc_params.params.proxy_username ~= '') then if (self.sc_params.params.proxy_password ~= '') then - httpRequest:setopt(curl.OPT_PROXYUSERPWD, self.sc_params.params.proxy_username .. ':' .. self.sc_params.params.proxy_password) + http_request:setopt(curl.OPT_PROXYUSERPWD, self.sc_params.params.proxy_username .. ':' .. self.sc_params.params.proxy_password) else - self.sc_logger:error("EventQueue:send_data: proxy_password parameter is not set but proxy_username is used") + self.sc_logger:error("event_queue:send_data: proxy_password parameter is not set but proxy_username is used") end end @@ -287,33 +287,33 @@ function EventQueue:send_data(payload, queue_metadata) return true end -- adding the HTTP POST data - httpRequest:setopt_postfields(payload.formatted_payload) + http_request:setopt_postfields(payload.formatted_payload) -- log the curl command for troubleshooting self.sc_logger:log_curl_command(url, queue_metadata, self.sc_params.params, payload.formatted_payload) -- performing the HTTP request - httpRequest:perform() + http_request:perform() -- collecting results - local httpResponseCode = httpRequest:getinfo(curl.INFO_RESPONSE_CODE) + local http_response_code = http_request:getinfo(curl.INFO_RESPONSE_CODE) - httpRequest:close() + http_request:close() -- Handling the return code local retval = false - if httpResponseCode == 200 then - self.sc_logger:info("EventQueue:send_data: HTTP POST request successful: return code is " .. httpResponseCode) + if http_response_code == 200 then + self.sc_logger:info("event_queue:send_data: HTTP POST request successful: return code is " .. http_response_code) -- now that the data has been sent, we empty the events array self.events = {} retval = true else - self.sc_logger:error("EventQueue:send_data: HTTP POST request FAILED, return code is " .. httpResponseCode .. " message is:\n\"" .. tostring(httpResponseBody) .. "\n\"\n") + self.sc_logger:error("event_queue:send_data: HTTP POST request FAILED, return code is " .. http_response_code .. " message is:\n\"" .. tostring(http_response_body) .. "\n\"\n") self.sc_logger:error("the body request " .. payload.formatted_payload) end - self.sc_logger:debug("[EventQueue:send_data]: End") + self.sc_logger:debug("[event_queue:send_data]: End") return retval end @@ -326,7 +326,7 @@ local queue -- Fonction init() function init(conf) - queue = EventQueue.new(conf) + queue = event_queue.new(conf) end -------------------------------------------------------------------------------- diff --git a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua index 85add8eb..d4c7830d 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua @@ -58,16 +58,16 @@ end -- Classe event_queue -------------------------------------------------------------------------------- -local EventQueue = {} -EventQueue.__index = EventQueue +local event_queue = {} +event_queue.__index = event_queue -------------------------------------------------------------------------------- ---- Constructor ---- @param conf The table given by the init() function and returned from the GUI ----- @return the new EventQueue +---- @return the new event_queue ---------------------------------------------------------------------------------- -function EventQueue.new(params) +function event_queue.new(params) local self = {} local mandatory_parameters = { @@ -115,7 +115,7 @@ function EventQueue.new(params) -- only load the custom code file, not executed yet if self.sc_params.load_custom_code_file and not self.sc_params:load_custom_code_file(self.sc_params.params.custom_code_file) then - self.sc_logger:error("[EventQueue:new]: couldn't successfully load the custom code file: " .. tostring(self.sc_params.params.custom_code_file)) + self.sc_logger:error("[event_queue:new]: couldn't successfully load the custom code file: " .. tostring(self.sc_params.params.custom_code_file)) end self.sc_params:build_accepted_elements_info() @@ -167,19 +167,19 @@ function EventQueue.new(params) self.send_data_sleep_counter = self.sc_common:create_sleep_counter_table({}, 0, 300, 10) self.init_fail_sleep_counter = self.sc_common:create_sleep_counter_table({}, 0, 300, 10) - -- return EventQueue object - setmetatable(self, { __index = EventQueue }) + -- return event_queue object + setmetatable(self, { __index = event_queue }) return self end -------------------------------------------------------------------------------- ----- EventQueue:format_accepted_event method +---- event_queue:format_accepted_event method -------------------------------------------------------------------------------- -function EventQueue:format_accepted_event() +function event_queue:format_accepted_event() local category = self.sc_event.event.category local element = self.sc_event.event.element - self.sc_logger:debug("[EventQueue:format_accepted_event]: starting format event") + self.sc_logger:debug("[event_queue:format_accepted_event]: starting format event") -- can't format event if stream connector is not handling this kind of event and that it is not handled with a template file if not self.format_event[category][element] then @@ -191,13 +191,13 @@ function EventQueue:format_accepted_event() self.format_event[category][element]() end - self.sc_logger:debug("[EventQueue:format_accepted_event]: event formatting is finished") + self.sc_logger:debug("[event_queue:format_accepted_event]: event formatting is finished") end -------------------------------------------------------------------------------- ----- EventQueue:format_event_host method +---- event_queue:format_event_host method -------------------------------------------------------------------------------- -function EventQueue:format_event_host() +function event_queue:format_event_host() local event = self.sc_event.event self.previous_info[event.category][event.element].flush_success = false @@ -220,15 +220,15 @@ function EventQueue:format_event_host() self.previous_info[event.category][event.element].host_id = event.host_id end end - self.sc_logger:debug("[EventQueue:format_event_host]: call build_metric ") + self.sc_logger:debug("[event_queue:format_event_host]: call build_metric ") self.sc_metrics:build_metric(self.format_metric[event.category][event.element]) end -------------------------------------------------------------------------------- ----- EventQueue:format_event_service method +---- event_queue:format_event_service method -------------------------------------------------------------------------------- -function EventQueue:format_event_service() - self.sc_logger:debug("[EventQueue:format_event_service]: starting format event service.") +function event_queue:format_event_service() + self.sc_logger:debug("[event_queue:format_event_service]: starting format event service.") local event = self.sc_event.event self.previous_info[event.category][event.element].flush_success = false @@ -258,17 +258,17 @@ function EventQueue:format_event_service() self.previous_info[event.category][event.element].service_id = event.service_id end end - self.sc_logger:debug("[EventQueue:format_event_service]: call build_metric ") + self.sc_logger:debug("[event_queue:format_event_service]: call build_metric ") self.sc_metrics:build_metric(self.format_metric[event.category][event.element]) - self.sc_logger:debug("[EventQueue:format_event_service]: format metric service is finished ") + self.sc_logger:debug("[event_queue:format_event_service]: format metric service is finished ") end -------------------------------------------------------------------------------- ----- EventQueue:format_metric_host method +---- event_queue:format_metric_host method -- @param metric {table} a single metric data -------------------------------------------------------------------------------- -function EventQueue:format_metric_host(metric) - self.sc_logger:debug("[EventQueue:format_metric_host]: starting format event host.") +function event_queue:format_metric_host(metric) + self.sc_logger:debug("[event_queue:format_metric_host]: starting format event host.") local event = self.sc_event.event local sdesc = "host" @@ -277,17 +277,17 @@ function EventQueue:format_metric_host(metric) prom_sdesc = sdesc, prom_sdesc_url = mime.b64(sdesc) } - self.sc_logger:debug("[EventQueue:format_metric_host]: call format_metric ") + self.sc_logger:debug("[event_queue:format_metric_host]: call format_metric ") self:format_metric_event(metric) - self.sc_logger:debug("[EventQueue:format_metric_host]: format metric host is finished ") + self.sc_logger:debug("[event_queue:format_metric_host]: format metric host is finished ") end -------------------------------------------------------------------------------- ----- EventQueue:format_metric_service method +---- event_queue:format_metric_service method -- @param metric {table} a single metric data -------------------------------------------------------------------------------- -function EventQueue:format_metric_service(metric) - self.sc_logger:debug("[EventQueue:format_metric_service]: starting format event service.") +function event_queue:format_metric_service(metric) + self.sc_logger:debug("[event_queue:format_metric_service]: starting format event service.") local event = self.sc_event.event local sdesc = event.cache.service.description @@ -296,9 +296,9 @@ function EventQueue:format_metric_service(metric) prom_sdesc = sdesc, prom_sdesc_url = mime.b64(sdesc) } - self.sc_logger:debug("[EventQueue:format_metric_service]: call format_metric ") + self.sc_logger:debug("[event_queue:format_metric_service]: call format_metric ") self:format_metric_event(metric) - self.sc_logger:debug("[EventQueue:format_metric_service]: format metric service is finished ") + self.sc_logger:debug("[event_queue:format_metric_service]: format metric service is finished ") end -------------------------------------------------------------------------------- @@ -308,7 +308,7 @@ end -- @param {string} name, the name of the metric -- @return {string} data, the unit metadata information -------------------------------------------------------------------------------- -function EventQueue:add_unit_info (label, unit, name) +function event_queue:add_unit_info (label, unit, name) local data = '' if (unit ~= '' and unit ~= nil) then @@ -324,7 +324,7 @@ end --- @param {string} unit, the unit name --- @return {string} name, the prometheus metric name (open metric format) -------------------------------------------------------------------------------- -function EventQueue:create_metric_name (label, unit) +function event_queue:create_metric_name (label, unit) local name = '' local sdesc = self.sc_event.event.cache.service.description or 'host' local hname = self.sc_event.event.cache.host.name @@ -345,11 +345,11 @@ function EventQueue:create_metric_name (label, unit) end -------------------------------------------------------------------------------- ---- EventQueue:format_metric_service method +--- event_queue:format_metric_service method --- @param metric {table} a single metric data ------------------------------------------------------------------------------- -function EventQueue:format_metric_event(metric) - self.sc_logger:debug("[EventQueue:format_metric]: start real format metric ") +function event_queue:format_metric_event(metric) + self.sc_logger:debug("[event_queue:format_metric]: start real format metric ") local event = self.sc_event.event local type = self:get_metric_type(metric) local unit = unit_mapping(metric.uom) @@ -400,7 +400,7 @@ CENTREON_Financial:acme_bank_business_gold_reserve_euros{label="acme_bank_busine event.formated_event.payload = data self:add() - self.sc_logger:debug("[EventQueue:format_metric]: end real format metric ") + self.sc_logger:debug("[event_queue:format_metric]: end real format metric ") end -------------------------------------------------------------------------------- @@ -425,7 +425,7 @@ end -- @param {table} perfdata, the perfdata informations -- @return {string} metricType, the type of the metric -------------------------------------------------------------------------------- -function EventQueue:get_metric_type (perfdata) +function event_queue:get_metric_type (perfdata) if (is_number_and_not_a_NaN(perfdata.max)) then return "gauge" end @@ -434,30 +434,30 @@ function EventQueue:get_metric_type (perfdata) end -------------------------------------------------------------------------------- --- EventQueue:add, add an event to the sending queue +-- event_queue:add, add an event to the sending queue -------------------------------------------------------------------------------- -function EventQueue:add() +function event_queue:add() -- store event in self.events lists local category = self.sc_event.event.category local element = self.sc_event.event.element - self.sc_logger:debug("[EventQueue:add]: add event in queue category: " .. tostring(self.sc_params.params.reverse_category_mapping[category]) + self.sc_logger:debug("[event_queue:add]: add event in queue category: " .. tostring(self.sc_params.params.reverse_category_mapping[category]) .. " element: " .. tostring(self.sc_params.params.reverse_element_mapping[category][element])) - self.sc_logger:debug("[EventQueue:add]: queue size before adding event: " .. tostring(#self.sc_flush.queues[category][element].events)) + self.sc_logger:debug("[event_queue:add]: queue size before adding event: " .. tostring(#self.sc_flush.queues[category][element].events)) self.sc_flush.queues[category][element].events[#self.sc_flush.queues[category][element].events + 1] = self.sc_event.event.formated_event - self.sc_logger:info("[EventQueue:add]: queue size is now: " .. tostring(#self.sc_flush.queues[category][element].events) + self.sc_logger:info("[event_queue:add]: queue size is now: " .. tostring(#self.sc_flush.queues[category][element].events) .. ", max is: " .. tostring(self.sc_params.params.max_buffer_size)) end -------------------------------------------------------------------------------- --- EventQueue:build_payload, concatenate data so it is ready to be sent +-- event_queue:build_payload, concatenate data so it is ready to be sent -- @param payload {string} json encoded string -- @param event {table} the event that is going to be added to the payload -- @return payload {string} json encoded string -------------------------------------------------------------------------------- -function EventQueue:build_payload(payload, event) +function event_queue:build_payload(payload, event) if not payload then -- FIXME: voir obsidian payload = event @@ -468,8 +468,8 @@ function EventQueue:build_payload(payload, event) return payload end -function EventQueue:send_data(payload, queue_metadata) - self.sc_logger:debug("[EventQueue:send_data]: Starting to send data") +function event_queue:send_data(payload, queue_metadata) + self.sc_logger:debug("[event_queue:send_data]: Starting to send data") local httpPostData = payload.payload local httpResponseBody = "" local url = self.sc_params.params.prometheus_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance/' .. payload.prom_hname .. '/service@base64/' .. payload.prom_sdesc_url @@ -494,7 +494,7 @@ function EventQueue:send_data(payload, queue_metadata) if (self.sc_params.params.proxy_port and self.sc_params.params.proxy_port ~= '') then httpRequest:setopt(curl.OPT_PROXY, self.sc_params.params.proxy_address .. ':' .. self.sc_params.params.proxy_port) else - self.sc_logger:error("EventQueue:send_data: proxy_port parameter is not set but proxy_address is used") + self.sc_logger:error("event_queue:send_data: proxy_port parameter is not set but proxy_address is used") end end @@ -503,7 +503,7 @@ function EventQueue:send_data(payload, queue_metadata) if (self.sc_params.params.proxy_password ~= '') then httpRequest:setopt(curl.OPT_PROXYUSERPWD, self.sc_params.params.proxy_username .. ':' .. self.sc_params.params.proxy_password) else - self.sc_logger:error("EventQueue:send_data: proxy_password parameter is not set but proxy_username is used") + self.sc_logger:error("event_queue:send_data: proxy_password parameter is not set but proxy_username is used") end end @@ -530,15 +530,15 @@ function EventQueue:send_data(payload, queue_metadata) -- Handling the return code local retval = false if httpResponseCode == 200 then - self.sc_logger:info("EventQueue:send_data: HTTP POST request successful: return code is " .. httpResponseCode) + self.sc_logger:info("event_queue:send_data: HTTP POST request successful: return code is " .. httpResponseCode) -- now that the data has been sent, we empty the events array self.events = {} retval = true else - self.sc_logger:error("EventQueue:send_data: HTTP POST request FAILED, return code is " .. httpResponseCode .. " message is:\n\"" .. tostring(httpResponseBody) .. "\n\"\n") + self.sc_logger:error("event_queue:send_data: HTTP POST request FAILED, return code is " .. httpResponseCode .. " message is:\n\"" .. tostring(httpResponseBody) .. "\n\"\n") self.sc_logger:error("the body request " .. httpPostData) end - self.sc_logger:debug("[EventQueue:send_data]: End") + self.sc_logger:debug("[event_queue:send_data]: End") return retval end @@ -550,7 +550,7 @@ local queue -- Fonction init() function init(conf) - queue = EventQueue.new(conf) + queue = event_queue.new(conf) end -- -------------------------------------------------------------------------------- From 73ffa4b3b2154b6164fe2bdd7b3e3d7e4100792c Mon Sep 17 00:00:00 2001 From: omercier Date: Thu, 22 May 2025 11:40:29 +0200 Subject: [PATCH 19/27] finish removing camelCase --- .../prometheus-pushgateway-metrics-apiv2.lua | 77 ++++++++++--------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua index d4c7830d..411c124a 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua @@ -22,13 +22,13 @@ local sc_metrics = require("centreon-stream-connectors-lib.sc_metrics") -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- --- unit_mapping: convert perfdata units to openmetrics standard +-- get_unit_full_name: convert perfdata units to openmetrics standard -- @param {string} unit, the unit value -- @return {string} unit, the openmetrics unit name -- @return {boolean}, true if the unit is found in the mapping or empty -------------------------------------------------------------------------------- -local function unit_mapping (unit) - local unitMapping = { +local function get_unit_full_name (unit) + local unit_mapping = { s = 'seconds', m = 'meters', B = 'bytes', @@ -41,15 +41,13 @@ local function unit_mapping (unit) ["€"] = 'euros' } - local unhandledUnit = nil - if unit == nil or unit == '' or type(unit) ~= 'string' then unit = '' end -if unitMapping[unit] then - unit = unitMapping[unit] -end + if unit_mapping[unit] then + unit = unit_mapping[unit] + end return unit, true end @@ -202,8 +200,15 @@ function event_queue:format_event_host() self.previous_info[event.category][event.element].flush_success = false -- this is the first time we receive a metric from a host, we store host id in the table - if self.previous_info[event.category][event.element].host_id == "" then + if self.previous_info[event.category][event.element].host_id == "" then self.previous_info[event.category][event.element].host_id = event.host_id + -- handle hostgroups + if self.add_hostgroups == 1 then + self.current_event.hostgroupsLabel = self:display_hostgroups() + else + self.current_event.hostgroupsLabel = false + end + else -- the event is linked to a new host, we can't send payload with data from different hosts so we force a data flush -- we store the new host id and then we continue working on metrics from said host @@ -352,7 +357,7 @@ function event_queue:format_metric_event(metric) self.sc_logger:debug("[event_queue:format_metric]: start real format metric ") local event = self.sc_event.event local type = self:get_metric_type(metric) - local unit = unit_mapping(metric.uom) + local unit = get_unit_full_name(metric.uom) local label = '' -- case when the metric belongs to an instance @@ -378,17 +383,17 @@ CENTREON_proc_crond:nbproc{label="nbproc", host="CENTREON", service="proc-crond" ]] -- Other example --[[ -# TYPE CENTREON_Ah_Que_Coucou:bnp_bank_business_gold_reserve_euros counter -# UNIT CENTREON_Ah_Que_Coucou:bnp_bank_business_gold_reserve_euros -CENTREON_Financial:acme_bank_business_gold_reserve_euros{label="acme_bank_business_gold.reserve.euros", host="CENTREON", service="Financial"} 3.0 +# TYPE CENTREON_Financial_Check:bnp_bank_business_gold_reserve_euros counter +# UNIT CENTREON_Financial_Check:bnp_bank_business_gold_reserve_euros +CENTREON_Financial_Check:acme_bank_business_gold_reserve_euros{label="acme_bank_business_gold.reserve.euros", host="CENTREON", service="Financial-Check"} 3.0 ]] local data = '# TYPE ' .. name .. ' ' .. type .. '\n' data = data .. self:add_unit_info(label, unit, name) data = data .. name .. '{label="' .. label .. '", host="' .. event.cache.host.name .. '", service="' .. sdesc .. '"' - if event.hostgroupsLabel then - data = data .. ', ' .. event.hostgroupsLabel + if event.hostgroups_label then + data = data .. ', ' .. event.hostgroups_label end data = data .. '} ' .. metric.value .. '\n' @@ -404,11 +409,11 @@ CENTREON_Financial:acme_bank_business_gold_reserve_euros{label="acme_bank_busine end -------------------------------------------------------------------------------- ---- is_number_and_not_a_NaN: check if a number is a number (and not a NaN) +--- is_number_and_not_a_nan: check if a number is a number (and not a NaN) --- @param {number} number, the number to check --- @return {boolean} -------------------------------------------------------------------------------- -local function is_number_and_not_a_NaN (number) +local function is_number_and_not_a_nan (number) if (number ~= number) then return false end @@ -423,10 +428,10 @@ end -------------------------------------------------------------------------------- -- get_metric_type: [for Prometheus] find out the metric type to match openmetrics standard -- @param {table} perfdata, the perfdata informations --- @return {string} metricType, the type of the metric +-- @return {string} metric_type, the type of the metric -------------------------------------------------------------------------------- function event_queue:get_metric_type (perfdata) - if (is_number_and_not_a_NaN(perfdata.max)) then + if (is_number_and_not_a_nan(perfdata.max)) then return "gauge" end @@ -470,17 +475,17 @@ end function event_queue:send_data(payload, queue_metadata) self.sc_logger:debug("[event_queue:send_data]: Starting to send data") - local httpPostData = payload.payload - local httpResponseBody = "" + local http_post_data = payload.payload + local http_response_body = "" local url = self.sc_params.params.prometheus_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance/' .. payload.prom_hname .. '/service@base64/' .. payload.prom_sdesc_url queue_metadata.headers = { "content-type: application/openmetrics-text" } - local httpRequest = curl.easy() + local http_request = curl.easy() :setopt_url(url) :setopt_writefunction( function (response) - httpResponseBody = httpResponseBody .. tostring(response) + http_response_body = http_response_body .. tostring(response) end ) :setopt(curl.OPT_TIMEOUT, self.sc_params.params.http_timeout) @@ -492,7 +497,7 @@ function event_queue:send_data(payload, queue_metadata) -- set proxy address configuration if (self.sc_params.params.proxy_address and self.sc_params.params.proxy_address ~= '') then if (self.sc_params.params.proxy_port and self.sc_params.params.proxy_port ~= '') then - httpRequest:setopt(curl.OPT_PROXY, self.sc_params.params.proxy_address .. ':' .. self.sc_params.params.proxy_port) + http_request:setopt(curl.OPT_PROXY, self.sc_params.params.proxy_address .. ':' .. self.sc_params.params.proxy_port) else self.sc_logger:error("event_queue:send_data: proxy_port parameter is not set but proxy_address is used") end @@ -501,7 +506,7 @@ function event_queue:send_data(payload, queue_metadata) -- set proxy user configuration if (self.sc_params.params.proxy_username ~= '') then if (self.sc_params.params.proxy_password ~= '') then - httpRequest:setopt(curl.OPT_PROXYUSERPWD, self.sc_params.params.proxy_username .. ':' .. self.sc_params.params.proxy_password) + http_request:setopt(curl.OPT_PROXYUSERPWD, self.sc_params.params.proxy_username .. ':' .. self.sc_params.params.proxy_password) else self.sc_logger:error("event_queue:send_data: proxy_password parameter is not set but proxy_username is used") end @@ -509,41 +514,41 @@ function event_queue:send_data(payload, queue_metadata) -- write payload in the logfile for test purpose if self.sc_params.params.send_data_test == 1 then - self.sc_logger:notice("[send_data]: " .. tostring(httpPostData)) + self.sc_logger:notice("[send_data]: " .. tostring(http_post_data)) return true end -- adding the HTTP POST data - httpRequest:setopt_postfields(httpPostData) + http_request:setopt_postfields(http_post_data) -- log the curl command for troubleshooting - self.sc_logger:log_curl_command(url, queue_metadata, self.sc_params.params, httpPostData) + self.sc_logger:log_curl_command(url, queue_metadata, self.sc_params.params, http_post_data) -- performing the HTTP request - httpRequest:perform() + http_request:perform() -- collecting results - local httpResponseCode = httpRequest:getinfo(curl.INFO_RESPONSE_CODE) + local http_response_code = http_request:getinfo(curl.INFO_RESPONSE_CODE) - httpRequest:close() + http_request:close() -- Handling the return code local retval = false - if httpResponseCode == 200 then - self.sc_logger:info("event_queue:send_data: HTTP POST request successful: return code is " .. httpResponseCode) + if http_response_code == 200 then + self.sc_logger:info("event_queue:send_data: HTTP POST request successful: return code is " .. http_response_code) -- now that the data has been sent, we empty the events array self.events = {} retval = true else - self.sc_logger:error("event_queue:send_data: HTTP POST request FAILED, return code is " .. httpResponseCode .. " message is:\n\"" .. tostring(httpResponseBody) .. "\n\"\n") - self.sc_logger:error("the body request " .. httpPostData) + self.sc_logger:error("event_queue:send_data: HTTP POST request FAILED, return code is " .. http_response_code .. " message is:\n\"" .. tostring(http_response_body) .. "\n\"\n") + self.sc_logger:error("the body request " .. http_post_data) end self.sc_logger:debug("[event_queue:send_data]: End") return retval end -------------------------------------------------------------------------------- --- Required functions for Broker StreamConnector +-- Required functions for Broker Stream Connector -------------------------------------------------------------------------------- local queue From d1c1e2712d8965d8da80e948fa8d54b4229af4e8 Mon Sep 17 00:00:00 2001 From: omercier Date: Thu, 22 May 2025 18:14:40 +0200 Subject: [PATCH 20/27] enh: improve self-documentation --- .../prometheus-pushgateway-events-apiv2.lua | 124 ++++++---- .../prometheus-pushgateway-metrics-apiv2.lua | 211 +++++++++--------- 2 files changed, 189 insertions(+), 146 deletions(-) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua index 619d394d..bb62e705 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua @@ -1,8 +1,5 @@ #!/usr/bin/lua --------------------------------------------------------------------------------- -- Centreon Broker Splunk Connector Events --------------------------------------------------------------------------------- - -- Libraries local curl = require("cURL") @@ -15,24 +12,15 @@ local sc_params = require("centreon-stream-connectors-lib.sc_params") local sc_macros = require("centreon-stream-connectors-lib.sc_macros") local sc_flush = require("centreon-stream-connectors-lib.sc_flush") +-- event_queue class --------------------------------------------------------------------------------- --- Local functions --------------------------------------------------------------------------------- - --------------------------------------------------------------------------------- --- Classe event_queue --------------------------------------------------------------------------------- - +--- @class event_queue Class that handles all the actions of the stream connector local event_queue = {} event_queue.__index = event_queue --------------------------------------------------------------------------------- ----- Constructor ----- @param conf The table given by the init() function and returned from the GUI ----- @return the new event_queue ----------------------------------------------------------------------------------- - +--- Constructor of the event_queue class +--- @param params table The table given by the init() function and returned from the GUI +--- @return table the new event_queue function event_queue.new(params) local self = {} @@ -70,6 +58,7 @@ function event_queue.new(params) self.sc_params.params.prometheus_url = params.prometheus_url or "http://127.0.0.1:9091" self.sc_params.params.http_timeout = params.http_timeout or 30 self.sc_params.params.prometheus_gateway_job = params.prometheus_gateway_job or "monitoring" + self.sc_params.params.add_hostgroups = params.add_hostgroups or 0 -- force max_buffer_size to 1 because we each service is sent to its own url -- apply users params and check syntax of standard ones @@ -111,9 +100,9 @@ function event_queue.new(params) return self end --------------------------------------------------------------------------------- ----- event_queue:format_event method ----------------------------------------------------------------------------------- +--- Calls the adequate format_event_* function and then +--- calls add() +--- @return void function event_queue:format_accepted_event() local category = self.sc_event.event.category local element = self.sc_event.event.element @@ -141,6 +130,8 @@ function event_queue:format_accepted_event() self.sc_logger:debug("[event_queue:format_event]: event formatting is finished") end +--- Prepares the self.sc_event.event.formated_event object +--- @return void function event_queue:format_event_host() self.sc_logger:debug("[event_queue:format_event_host]: starting format event host.") @@ -170,9 +161,16 @@ function event_queue:format_event_host() output = event.output, formatted_payload = data } - + -- handle hostgroups + if self.sc_params.params.add_hostgroups == 1 then + event.formated_event.hostgroups_label = self:display_hostgroups() + else + event.formated_event.hostgroups_label = false + end end +--- Prepares the self.sc_event.event.formated_event object +--- @return void function event_queue:format_event_service() self.sc_logger:debug("[event_queue:format_event_service]: starting format event service.") @@ -202,11 +200,45 @@ function event_queue:format_event_service() output = event.output, formatted_payload = data } + + -- handle hostgroups + if self.sc_params.params.add_hostgroups == 1 then + event.formated_event.hostgroups_label = self:display_hostgroups() + else + event.formated_event.hostgroups_label = false + end end --------------------------------------------------------------------------------- --- event_queue:add, add an event to the sending queue --------------------------------------------------------------------------------- +--- Creates the hostgroup label for the event +--- @return string hostgroups_label: the full label for the metric +function event_queue:display_hostgroups () + self.sc_logger:debug("[display_hostgroups]: function starting") + + if not self.sc_event.event.cache.hostgroups then + self.sc_logger:debug("[display_hostgroups]: no hostgroups, exiting") + return false + end + + local hostgroups_label = 'hostgroup="' + local counter = 0 + + for i, v in pairs(self.sc_event.event.cache.hostgroups) do + if counter == 0 then + hostgroups_label = hostgroups_label .. v.group_name + counter = 1 + else + hostgroups_label = hostgroups_label .. ',' .. v.group_name + end + end + hostgroups_label = hostgroups_label .. '"' + + self.sc_logger:debug("[display_hostgroups]: hostgroup string composed: '" .. hostgroups_label .. "'") + return hostgroups_label +end + + +--- event_queue:add, add an event to the sending queue +--- @return void function event_queue:add() -- store event in self.events lists local category = self.sc_event.event.category @@ -222,12 +254,10 @@ function event_queue:add() .. ", max is: " .. tostring(self.sc_params.params.max_buffer_size)) end --------------------------------------------------------------------------------- --- event_queue:build_payload, concatenate data so it is ready to be sent --- @param payload {string} json encoded string --- @param event {table} the event that is going to be added to the payload --- @return payload {string} json encoded string --------------------------------------------------------------------------------- +--- Concatenate data so it is ready to be sent +--- @param payload string json encoded string +--- @param event table the event that is going to be added to the payload +--- @return string payload json encoded string function event_queue:build_payload(payload, event) if not payload then payload = event @@ -239,11 +269,13 @@ function event_queue:build_payload(payload, event) return payload end - +--- Tries to send the data to the third-party tool +--- @param payload table table containing payload and host/service metadata +--- @param queue_metadata table global metadata +--- @return boolean true if the data has been sent, false otherwise function event_queue:send_data(payload, queue_metadata) self.sc_logger:debug("[event_queue:send_data]: Starting to send data") - --self.sc_logger:warning("[event_queue:send_data]: payload: " .. self.sc_common:dumper(payload)) - + local http_response_body = "" local label = "status" local url = self.sc_params.params.prometheus_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance/' .. payload.prom_hname .. '/service@base64/' .. payload.prom_sdesc_url @@ -318,23 +350,22 @@ function event_queue:send_data(payload, queue_metadata) return retval end --------------------------------------------------------------------------------- --- Required functions for Broker StreamConnector --------------------------------------------------------------------------------- - +-- global stream connector object local queue --- Fonction init() +-- Required functions for Broker Stream Connector + +--- Mandatory function for centreon-broker +--- @param conf table parameters as a table +--- @return void function init(conf) queue = event_queue.new(conf) end --------------------------------------------------------------------------------- --- write, --- @param {table} event, the event from broker --- @return {boolean} --------------------------------------------------------------------------------- -function write (event) +--- Mandatory function for centreon-broker +--- @param event table event sent by broker +--- @return boolean +function write(event) -- skip event if a mandatory parameter is missing if queue.fail then queue.sc_logger:error("Skipping event because a mandatory parameter is not set") @@ -362,7 +393,10 @@ function write (event) return flush() end --- flush method is called by broker every now and then (more often when broker has nothing else to do) +--- Optional function for centreon-broker. +--- flush() method is called by broker every now and then (more often when broker has nothing else to do) +--- @param event table event sent by broker +--- @return boolean true if the queue is flushed, false otherwise function flush() local queues_size = queue.sc_flush:get_queues_size() diff --git a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua index 411c124a..600ccc9d 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua @@ -1,8 +1,5 @@ #!/usr/bin/lua --------------------------------------------------------------------------------- -- Centreon Broker Datadog Connector Events --------------------------------------------------------------------------------- - -- Libraries local curl = require("cURL") @@ -10,24 +7,17 @@ local mime = require("mime") local sc_common = require("centreon-stream-connectors-lib.sc_common") local sc_logger = require("centreon-stream-connectors-lib.sc_logger") local sc_broker = require("centreon-stream-connectors-lib.sc_broker") -local sc_event = require("centreon-stream-connectors-lib.sc_event") local sc_params = require("centreon-stream-connectors-lib.sc_params") local sc_macros = require("centreon-stream-connectors-lib.sc_macros") local sc_flush = require("centreon-stream-connectors-lib.sc_flush") local sc_metrics = require("centreon-stream-connectors-lib.sc_metrics") - --------------------------------------------------------------------------------- -- Local functions --------------------------------------------------------------------------------- - --------------------------------------------------------------------------------- --- get_unit_full_name: convert perfdata units to openmetrics standard --- @param {string} unit, the unit value --- @return {string} unit, the openmetrics unit name --- @return {boolean}, true if the unit is found in the mapping or empty --------------------------------------------------------------------------------- -local function get_unit_full_name (unit) + +--- Converts perfdata units to openmetrics standard +--- @param unit string The unit symbol found in perfdata +--- @return string The openmetrics unit name +local function get_unit_full_name(unit) local unit_mapping = { s = 'seconds', m = 'meters', @@ -49,22 +39,16 @@ local function get_unit_full_name (unit) unit = unit_mapping[unit] end - return unit, true + return unit end --------------------------------------------------------------------------------- --- Classe event_queue --------------------------------------------------------------------------------- - +--- @class event_queue Class that handles all the actions of the stream connector local event_queue = {} event_queue.__index = event_queue --------------------------------------------------------------------------------- ----- Constructor ----- @param conf The table given by the init() function and returned from the GUI ----- @return the new event_queue ----------------------------------------------------------------------------------- - +--- Constructor of the event_queue class +--- @param params table The table given by the init() function and returned from the GUI +--- @return event_queue The new event_queue function event_queue.new(params) local self = {} @@ -101,6 +85,7 @@ function event_queue.new(params) self.sc_params.params.http_timeout = params.http_timeout or 30 self.sc_params.params.prometheus_gateway_job = params.prometheus_gateway_job or "monitoring" self.sc_params.params.enable_extended_metric_name = params.enable_extended_metric_name or 1 + self.sc_params.params.add_hostgroups = params.add_hostgroups or 0 -- apply users params and check syntax of standard ones self.sc_params:param_override(params) @@ -170,9 +155,9 @@ function event_queue.new(params) return self end --------------------------------------------------------------------------------- ----- event_queue:format_accepted_event method --------------------------------------------------------------------------------- +--- Calls the adequate format_event_* function and then +--- calls add() functions +--- @return void function event_queue:format_accepted_event() local category = self.sc_event.event.category local element = self.sc_event.event.element @@ -192,9 +177,8 @@ function event_queue:format_accepted_event() self.sc_logger:debug("[event_queue:format_accepted_event]: event formatting is finished") end --------------------------------------------------------------------------------- ----- event_queue:format_event_host method --------------------------------------------------------------------------------- +--- Formats host events by calling the format_metric() function defined for host status events +--- @return void function event_queue:format_event_host() local event = self.sc_event.event self.previous_info[event.category][event.element].flush_success = false @@ -202,13 +186,6 @@ function event_queue:format_event_host() -- this is the first time we receive a metric from a host, we store host id in the table if self.previous_info[event.category][event.element].host_id == "" then self.previous_info[event.category][event.element].host_id = event.host_id - -- handle hostgroups - if self.add_hostgroups == 1 then - self.current_event.hostgroupsLabel = self:display_hostgroups() - else - self.current_event.hostgroupsLabel = false - end - else -- the event is linked to a new host, we can't send payload with data from different hosts so we force a data flush -- we store the new host id and then we continue working on metrics from said host @@ -229,9 +206,8 @@ function event_queue:format_event_host() self.sc_metrics:build_metric(self.format_metric[event.category][event.element]) end --------------------------------------------------------------------------------- ----- event_queue:format_event_service method --------------------------------------------------------------------------------- +--- Formats service events by calling the format_metric() function defined for service status events +--- @return void function event_queue:format_event_service() self.sc_logger:debug("[event_queue:format_event_service]: starting format event service.") local event = self.sc_event.event @@ -239,9 +215,9 @@ function event_queue:format_event_service() self.previous_info[event.category][event.element].flush_success = false -- this is the first time we receive a metric from a servuce, we store host id and service id in the table - if self.previous_info[event.category][event.element].host_id == "" - or self.previous_info[event.category][event.element].service_id == "" - then + if self.previous_info[event.category][event.element].host_id == "" + or self.previous_info[event.category][event.element].service_id == "" + then self.previous_info[event.category][event.element].host_id = event.host_id self.previous_info[event.category][event.element].service_id = event.service_id else @@ -268,10 +244,9 @@ function event_queue:format_event_service() self.sc_logger:debug("[event_queue:format_event_service]: format metric service is finished ") end --------------------------------------------------------------------------------- ----- event_queue:format_metric_host method --- @param metric {table} a single metric data --------------------------------------------------------------------------------- +--- Formats metrics for host status events +--- @param metric table A single metric's data +--- @return void function event_queue:format_metric_host(metric) self.sc_logger:debug("[event_queue:format_metric_host]: starting format event host.") local event = self.sc_event.event @@ -282,15 +257,22 @@ function event_queue:format_metric_host(metric) prom_sdesc = sdesc, prom_sdesc_url = mime.b64(sdesc) } + + -- handle hostgroups + if self.sc_params.params.add_hostgroups == 1 then + event.formated_event.hostgroups_label = self:display_hostgroups() + else + event.formated_event.hostgroups_label = false + end + self.sc_logger:debug("[event_queue:format_metric_host]: call format_metric ") self:format_metric_event(metric) self.sc_logger:debug("[event_queue:format_metric_host]: format metric host is finished ") end --------------------------------------------------------------------------------- ----- event_queue:format_metric_service method --- @param metric {table} a single metric data --------------------------------------------------------------------------------- +--- Formats metrics for service status events +--- @param metric table a single metric's data +--- @return void function event_queue:format_metric_service(metric) self.sc_logger:debug("[event_queue:format_metric_service]: starting format event service.") local event = self.sc_event.event @@ -301,18 +283,24 @@ function event_queue:format_metric_service(metric) prom_sdesc = sdesc, prom_sdesc_url = mime.b64(sdesc) } + + -- handle hostgroups + if self.sc_params.params.add_hostgroups == 1 then + event.formated_event.hostgroups_label = self:display_hostgroups() + else + event.formated_event.hostgroups_label = false + end + self.sc_logger:debug("[event_queue:format_metric_service]: call format_metric ") self:format_metric_event(metric) self.sc_logger:debug("[event_queue:format_metric_service]: format metric service is finished ") end --------------------------------------------------------------------------------- --- add_unit_info: add unit metadata to match openmetrics standard --- @param {string} label, the name of the metric --- @param {string} unit, the unit name --- @param {string} name, the name of the metric --- @return {string} data, the unit metadata information --------------------------------------------------------------------------------- +--- event_queue:add_unit_info metadata to match openmetrics standard +--- @param label string The name of the metric +--- @param unit string The unit name +--- @param name string The name of the metric +--- @return string The unit metadata information function event_queue:add_unit_info (label, unit, name) local data = '' @@ -323,12 +311,10 @@ function event_queue:add_unit_info (label, unit, name) return data end --------------------------------------------------------------------------------- --- create_metric_name: concatenates data to create the metric name ---- @param {string} label, the name of the perfdata ---- @param {string} unit, the unit name ---- @return {string} name, the prometheus metric name (open metric format) --------------------------------------------------------------------------------- +--- @param label string The name of the perfdata +--- @param unit string The unit name +--- @return string The prometheus metric name (open metric format) function event_queue:create_metric_name (label, unit) local name = '' local sdesc = self.sc_event.event.cache.service.description or 'host' @@ -349,10 +335,8 @@ function event_queue:create_metric_name (label, unit) return string.gsub(name, self.sc_params.params.metric_name_regex, self.sc_params.params.metric_replacement_character) end --------------------------------------------------------------------------------- --- event_queue:format_metric_service method ---- @param metric {table} a single metric data -------------------------------------------------------------------------------- +--- @param metric table A single metric data function event_queue:format_metric_event(metric) self.sc_logger:debug("[event_queue:format_metric]: start real format metric ") local event = self.sc_event.event @@ -366,7 +350,6 @@ function event_queue:format_metric_event(metric) end -- case when there are sub-levels of an instance - local i, sub_instance for i, sub_instance in ipairs(metric.subinstance) do label = label .. sub_instance .. '_' end @@ -392,8 +375,8 @@ CENTREON_Financial_Check:acme_bank_business_gold_reserve_euros{label="acme_bank_ data = data .. self:add_unit_info(label, unit, name) data = data .. name .. '{label="' .. label .. '", host="' .. event.cache.host.name .. '", service="' .. sdesc .. '"' - if event.hostgroups_label then - data = data .. ', ' .. event.hostgroups_label + if event.formated_event.hostgroups_label then + data = data .. ', ' .. event.formated_event.hostgroups_label end data = data .. '} ' .. metric.value .. '\n' @@ -408,11 +391,9 @@ CENTREON_Financial_Check:acme_bank_business_gold_reserve_euros{label="acme_bank_ self.sc_logger:debug("[event_queue:format_metric]: end real format metric ") end --------------------------------------------------------------------------------- --- is_number_and_not_a_nan: check if a number is a number (and not a NaN) ---- @param {number} number, the number to check ---- @return {boolean} --------------------------------------------------------------------------------- +--- @param number number The number to check +--- @return boolean true if it is actually a number, false otherwise local function is_number_and_not_a_nan (number) if (number ~= number) then return false @@ -425,11 +406,10 @@ local function is_number_and_not_a_nan (number) return true end --------------------------------------------------------------------------------- --- get_metric_type: [for Prometheus] find out the metric type to match openmetrics standard --- @param {table} perfdata, the perfdata informations --- @return {string} metric_type, the type of the metric --------------------------------------------------------------------------------- +--- For Prometheus steam connector, find out the metric type +--- to match openmetrics standard. +--- @param perfdata table The perfdata information +--- @return string metric_type, the type of the metric function event_queue:get_metric_type (perfdata) if (is_number_and_not_a_nan(perfdata.max)) then return "gauge" @@ -438,9 +418,35 @@ function event_queue:get_metric_type (perfdata) return "counter" end --------------------------------------------------------------------------------- --- event_queue:add, add an event to the sending queue --------------------------------------------------------------------------------- +--- Create the hostgroup label for the metric +--- @return string hostgroups_label: the full label for the metric +function event_queue:display_hostgroups () + self.sc_logger:debug("[display_hostgroups]: function starting") + + if not self.sc_event.event.cache.hostgroups then + self.sc_logger:debug("[display_hostgroups]: no hostgroups, exiting") + return false + end + + local hostgroups_label = 'hostgroup="' + local counter = 0 + + for i, v in pairs(self.sc_event.event.cache.hostgroups) do + if counter == 0 then + hostgroups_label = hostgroups_label .. v.group_name + counter = 1 + else + hostgroups_label = hostgroups_label .. ',' .. v.group_name + end + end + hostgroups_label = hostgroups_label .. '"' + + self.sc_logger:debug("[display_hostgroups]: hostgroup string composed: '" .. hostgroups_label .. "'") + return hostgroups_label +end + +--- Adds an event to the sending queue @type void +--- @return void function event_queue:add() -- store event in self.events lists local category = self.sc_event.event.category @@ -456,15 +462,13 @@ function event_queue:add() .. ", max is: " .. tostring(self.sc_params.params.max_buffer_size)) end --------------------------------------------------------------------------------- --- event_queue:build_payload, concatenate data so it is ready to be sent --- @param payload {string} json encoded string --- @param event {table} the event that is going to be added to the payload --- @return payload {string} json encoded string --------------------------------------------------------------------------------- +--- Concatenates data so it is ready to be sent +--- @param payload string json encoded string +--- @param event table the event that is going to be added to the payload +--- @return string payload json encoded string function event_queue:build_payload(payload, event) - if not payload then -- FIXME: voir obsidian + if not payload then payload = event else table.insert(payload, event) @@ -473,8 +477,13 @@ function event_queue:build_payload(payload, event) return payload end +--- Tries to send the data to the third-party tool +--- @param payload table table containing payload and host/service metadata +--- @param queue_metadata table global metadata +--- @return boolean true if the data has been sent, false otherwise function event_queue:send_data(payload, queue_metadata) self.sc_logger:debug("[event_queue:send_data]: Starting to send data") + local http_post_data = payload.payload local http_response_body = "" local url = self.sc_params.params.prometheus_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance/' .. payload.prom_hname .. '/service@base64/' .. payload.prom_sdesc_url @@ -547,22 +556,19 @@ function event_queue:send_data(payload, queue_metadata) return retval end --------------------------------------------------------------------------------- --- Required functions for Broker Stream Connector --------------------------------------------------------------------------------- - +-- Global stream connector object local queue --- Fonction init() +--- Mandatory function for centreon-broker - must be exposed (not local) +--- @param conf table Configuration parameters as a table +--- @return void function init(conf) queue = event_queue.new(conf) end --- -------------------------------------------------------------------------------- --- write, --- @param {table} event, the event from broker --- @return {boolean} --------------------------------------------------------------------------------- +--- Mandatory function for centreon-broker - must be exposed (not local) +--- @param event table Event sent by broker +--- @return boolean function write(event) -- skip event if a mandatory parameter is missing if queue.fail then @@ -596,7 +602,10 @@ function write(event) return flush() end --- flush method is called by broker every now and then (more often when broker has nothing else to do) +--- Optional function for centreon-broker. +--- flush() method is called by broker every now and then (more often when broker has nothing else to do) +--- @param event table Event sent by broker +--- @return boolean true if the queue is flushed, false otherwise function flush() local queues_size = queue.sc_flush:get_queues_size() From 7ad413e192da2fc2493ab8ec19438d149827ab55 Mon Sep 17 00:00:00 2001 From: omercier Date: Mon, 26 May 2025 11:58:16 +0200 Subject: [PATCH 21/27] fix: handle host names with special characters + other - rename url parameter to match specs - enable smart sleep for events too - rewrite end of write() function --- .../prometheus-pushgateway-events-apiv2.lua | 52 +++++++++++------- .../prometheus-pushgateway-metrics-apiv2.lua | 54 +++++++++++-------- 2 files changed, 63 insertions(+), 43 deletions(-) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua index bb62e705..27c3af32 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua @@ -55,7 +55,7 @@ function event_queue.new(params) self.sc_params.params.enable_service_status_dedup = params.enable_service_status_dedup or 1 -- prometheus specific parameters - self.sc_params.params.prometheus_url = params.prometheus_url or "http://127.0.0.1:9091" + self.sc_params.params.prometheus_gateway_url = params.prometheus_gateway_url or "http://127.0.0.1:9091" self.sc_params.params.http_timeout = params.http_timeout or 30 self.sc_params.params.prometheus_gateway_job = params.prometheus_gateway_job or "monitoring" self.sc_params.params.add_hostgroups = params.add_hostgroups or 0 @@ -95,6 +95,10 @@ function event_queue.new(params) [1] = function (payload, event) return self:build_payload(payload, event) end } + -- those sleep counters will avoid log spam and connection spam + self.send_data_sleep_counter = self.sc_common:create_sleep_counter_table({}, 0, 300, 10) + self.init_fail_sleep_counter = self.sc_common:create_sleep_counter_table({}, 0, 300, 10) + -- return event_queue object setmetatable(self, { __index = event_queue }) return self @@ -139,7 +143,7 @@ function event_queue:format_event_host() local hname = event.cache.host.name local sdesc = "host" - local name = string.gsub(hname .. '_' .. sdesc .. ':monitoring_status', self.sc_params.params.metric_name_regex, self.sc_params.params.metric_replacement_character) + local name = 'monitoring_status' local data = '# TYPE ' .. name .. ' counter\n' data = data .. '# HELP ' .. name .. ' 0 is OK, 1 or higher is DOWN\n' @@ -152,6 +156,7 @@ function event_queue:format_event_host() event.formated_event = { event_type = "host", prom_hname = event.cache.host.name, + prom_hname_url = mime.b64(event.cache.host.name), prom_sdesc = sdesc, prom_sdesc_url = mime.b64(sdesc), state = event.state, @@ -178,7 +183,7 @@ function event_queue:format_event_service() local hname = event.cache.host.name local sdesc = event.cache.service.description - local name = string.gsub(hname .. '_' .. sdesc .. ':monitoring_status', self.sc_params.params.metric_name_regex, self.sc_params.params.metric_replacement_character) + local name = 'monitoring_status' local data = '# TYPE ' .. name .. ' counter\n' data = data .. '# HELP ' .. name .. ' 0 is OK, 1 is WARNING, 2 is CRITICAL, 3 or higher is UNKNOWN\n' @@ -191,6 +196,7 @@ function event_queue:format_event_service() event.formated_event = { event_type = "service", prom_hname = event.cache.host.name, + prom_hname_url = mime.b64(event.cache.host.name), prom_sdesc = sdesc, prom_sdesc_url = mime.b64(sdesc), state = event.state, @@ -209,12 +215,22 @@ function event_queue:format_event_service() end end +--- Replace unwanted characters in order to comply with the open metrics format +--- @param string string the string to convert +--- @return string A string that matches openmetrics +function event_queue:convert_to_openmetric(string) + if string == nil or string == '' or type(string) ~= 'string' then + return false + end + return string.gsub(string, self.sc_params.params.metric_name_regex, self.sc_params.params.metric_replacement_character) +end + --- Creates the hostgroup label for the event --- @return string hostgroups_label: the full label for the metric function event_queue:display_hostgroups () self.sc_logger:debug("[display_hostgroups]: function starting") - if not self.sc_event.event.cache.hostgroups then + if not self.sc_event.event.cache.hostgroups or #self.sc_event.event.cache.hostgroups == 0 then self.sc_logger:debug("[display_hostgroups]: no hostgroups, exiting") return false end @@ -278,7 +294,7 @@ function event_queue:send_data(payload, queue_metadata) local http_response_body = "" local label = "status" - local url = self.sc_params.params.prometheus_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance/' .. payload.prom_hname .. '/service@base64/' .. payload.prom_sdesc_url + local url = self.sc_params.params.prometheus_gateway_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance@base64/' .. payload.prom_hname_url .. '/service@base64/' .. payload.prom_sdesc_url queue_metadata.headers = { "content-type: application/openmetrics-text" } @@ -369,9 +385,12 @@ function write(event) -- skip event if a mandatory parameter is missing if queue.fail then queue.sc_logger:error("Skipping event because a mandatory parameter is not set") + queue.init_fail_sleep_counter:sleep() return false end + queue.init_fail_sleep_counter:reset() + -- initiate event object queue.sc_event = sc_event.new(event, queue.sc_params.params, queue.sc_common, queue.sc_logger, queue.sc_broker) if queue.sc_event:is_valid_category() then @@ -399,28 +418,21 @@ end --- @return boolean true if the queue is flushed, false otherwise function flush() local queues_size = queue.sc_flush:get_queues_size() - + -- nothing to flush if queues_size == 0 then return true end -- flush all queues because last global flush is too old - if queue.sc_flush.last_global_flush < os.time() - queue.sc_params.params.max_all_queues_age then - if not queue.sc_flush:flush_all_queues(queue.build_payload_method[1], queue.send_data_method[1]) then - return false + -- or because too many events are stored in them + if queue.sc_flush.last_global_flush < os.time() - queue.sc_params.params.max_all_queues_age + or queues_size > queue.sc_params.params.max_buffer_size then + if queue.sc_flush:flush_all_queues(queue.build_payload_method[1], queue.send_data_method[1]) then + queue.send_data_sleep_counter:reset() + return true end - - return true - end - - -- flush queues because too many events are stored in them - if queues_size > queue.sc_params.params.max_buffer_size then - if not queue.sc_flush:flush_all_queues(queue.build_payload_method[1], queue.send_data_method[1]) then - return false - end - - return true + queue.send_data_sleep_counter:sleep() end -- there are events in the queue but they were not ready to be send diff --git a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua index 600ccc9d..879d69ec 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua @@ -81,10 +81,10 @@ function event_queue.new(params) self.sc_params.params.metric_replacement_character = params.metric_replacement_character or '_' -- prometheus specific parameters - self.sc_params.params.prometheus_url = params.prometheus_url or "http://127.0.0.1:9091" + self.sc_params.params.prometheus_gateway_url = params.prometheus_gateway_url or "http://127.0.0.1:9091" self.sc_params.params.http_timeout = params.http_timeout or 30 self.sc_params.params.prometheus_gateway_job = params.prometheus_gateway_job or "monitoring" - self.sc_params.params.enable_extended_metric_name = params.enable_extended_metric_name or 1 + self.sc_params.params.enable_extended_metric_name = params.enable_extended_metric_name or 0 self.sc_params.params.add_hostgroups = params.add_hostgroups or 0 -- apply users params and check syntax of standard ones @@ -254,6 +254,7 @@ function event_queue:format_metric_host(metric) event.formated_event = { prom_hname = event.cache.host.name, + prom_hname_url = mime.b64(event.cache.host.name), prom_sdesc = sdesc, prom_sdesc_url = mime.b64(sdesc) } @@ -280,6 +281,7 @@ function event_queue:format_metric_service(metric) event.formated_event = { prom_hname = event.cache.host.name, + prom_hname_url = mime.b64(event.cache.host.name), prom_sdesc = sdesc, prom_sdesc_url = mime.b64(sdesc) } @@ -311,7 +313,7 @@ function event_queue:add_unit_info (label, unit, name) return data end ---- create_metric_name: concatenates data to create the metric name +--- Concatenates data to create the metric name --- @param label string The name of the perfdata --- @param unit string The unit name --- @return string The prometheus metric name (open metric format) @@ -332,7 +334,17 @@ function event_queue:create_metric_name (label, unit) name = name .. '_' .. unit end end - return string.gsub(name, self.sc_params.params.metric_name_regex, self.sc_params.params.metric_replacement_character) + return self:convert_to_openmetric(name) +end + +--- Replace unwanted characters in order to comply with the open metrics format +--- @param string string the string to convert +--- @return string A string that matches openmetrics +function event_queue:convert_to_openmetric (string) + if string == nil or string == '' or type(string) ~= 'string' then + return false + end + return string.gsub(string, self.sc_params.params.metric_name_regex, self.sc_params.params.metric_replacement_character) end --- event_queue:format_metric_service method @@ -372,7 +384,9 @@ CENTREON_Financial_Check:acme_bank_business_gold_reserve_euros{label="acme_bank_ ]] local data = '# TYPE ' .. name .. ' ' .. type .. '\n' - data = data .. self:add_unit_info(label, unit, name) + if (type == 'counter') then + data = data .. self:add_unit_info(label, unit, name) + end data = data .. name .. '{label="' .. label .. '", host="' .. event.cache.host.name .. '", service="' .. sdesc .. '"' if event.formated_event.hostgroups_label then @@ -411,7 +425,7 @@ end --- @param perfdata table The perfdata information --- @return string metric_type, the type of the metric function event_queue:get_metric_type (perfdata) - if (is_number_and_not_a_nan(perfdata.max)) then + if (is_number_and_not_a_nan(perfdata.max) or (perfdata.uom and perfdata.uom == '%')) then return "gauge" end @@ -423,7 +437,7 @@ end function event_queue:display_hostgroups () self.sc_logger:debug("[display_hostgroups]: function starting") - if not self.sc_event.event.cache.hostgroups then + if not self.sc_event.event.cache.hostgroups or #self.sc_event.event.cache.hostgroups == 0 then self.sc_logger:debug("[display_hostgroups]: no hostgroups, exiting") return false end @@ -486,7 +500,7 @@ function event_queue:send_data(payload, queue_metadata) local http_post_data = payload.payload local http_response_body = "" - local url = self.sc_params.params.prometheus_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance/' .. payload.prom_hname .. '/service@base64/' .. payload.prom_sdesc_url + local url = self.sc_params.params.prometheus_gateway_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance@base64/' .. payload.prom_hname_url .. '/service@base64/' .. payload.prom_sdesc_url queue_metadata.headers = { "content-type: application/openmetrics-text" } @@ -589,7 +603,7 @@ function write(event) if queue.sc_metrics:is_valid_metric_event() then queue:format_accepted_event() end - --- log why the event has been dropped + -- log why the event has been dropped else queue.sc_logger:debug("dropping event because element is not valid. Event element is: " .. tostring(queue.sc_params.params.reverse_element_mapping[queue.sc_event.event.category][queue.sc_event.event.element])) @@ -615,23 +629,17 @@ function flush() end -- flush all queues because last global flush is too old - if queue.sc_flush.last_global_flush < os.time() - queue.sc_params.params.max_all_queues_age then - if not queue.sc_flush:flush_all_queues(queue.build_payload_method[1], queue.send_data_method[1]) then - return false + -- or because too many events are stored in them + if queue.sc_flush.last_global_flush < os.time() - queue.sc_params.params.max_all_queues_age + or queues_size > queue.sc_params.params.max_buffer_size then + if queue.sc_flush:flush_all_queues(queue.build_payload_method[1], queue.send_data_method[1]) then + queue.send_data_sleep_counter:reset() + return true end - - return true - end - - -- flush queues because too many events are stored in them - if queues_size > queue.sc_params.params.max_buffer_size then - if not queue.sc_flush:flush_all_queues(queue.build_payload_method[1], queue.send_data_method[1]) then - return false - end - - return true + queue.send_data_sleep_counter:sleep() end -- there are events in the queue but they were not ready to be send return false end + From 88489ffb289b55fa27728f0e2af257bc050c1001 Mon Sep 17 00:00:00 2001 From: omercier Date: Fri, 27 Jun 2025 15:55:35 +0200 Subject: [PATCH 22/27] enh: add get_hostgroup_alias --- .../sc_broker.lua | 110 ++++++++++-------- 1 file changed, 60 insertions(+), 50 deletions(-) diff --git a/modules/centreon-stream-connectors-lib/sc_broker.lua b/modules/centreon-stream-connectors-lib/sc_broker.lua index 2d659f60..eb13811f 100644 --- a/modules/centreon-stream-connectors-lib/sc_broker.lua +++ b/modules/centreon-stream-connectors-lib/sc_broker.lua @@ -1,9 +1,7 @@ #!/usr/bin/lua ---- --- Module with Centreon broker related methods for easier usage --- @module sc_broker --- @alias sc_broker +--- Module with Centreon broker related methods for easier usage +--- @module sc_broker local sc_broker = {} @@ -25,10 +23,10 @@ function sc_broker.new(logger) end ---- get_host_all_infos: retrieve all informations from a host --- @param host_id (number) --- @return false (boolean) if host_id isn't valid or no information were found in broker cache --- @return host_info (table) all the informations from the host +--- Retrieve all information from a host +--- @param host_id number ID of the host +--- @return boolean false if host_id is not valid or no information was found in the broker cache +--- @return table all information from the host function ScBroker:get_host_all_infos(host_id) -- return because host_id isn't valid if host_id == nil or host_id == "" then @@ -48,11 +46,10 @@ function ScBroker:get_host_all_infos(host_id) return host_info end ---- get_service_all_infos: retrieve informations from a service --- @param host_id (number) --- @params service_id (number) --- @return false (boolean) if host id or service id aren't valid --- @return service (table) all the informations from the service +--- Retrieve information from a service +--- @param host_id (number) ID of the host +--- @param service_id (number) +--- @return (boolean|table) Table of all the information from the service. Returns false if host id or service id aren't valid. function ScBroker:get_service_all_infos(host_id, service_id) -- return because host_id or service_id isn't valid if host_id == nil or host_id == "" or service_id == nil or service_id == "" then @@ -73,11 +70,10 @@ function ScBroker:get_service_all_infos(host_id, service_id) return service_info end ---- get_host_infos: retrieve the the desired host informations --- @param host_id (number) --- @params info (string|table) the name of the wanted host parameter or a table of all wanted host parameters --- @return false (boolean) if host_id is nil or empty --- @return host (any) a table of all wanted host params if input param is a table. The single parameter if input param is a string +--- Retrieve the the desired host informations +--- @param host_id (number) ID of the host +--- @param info (string|table) Name of the wanted host parameter or a table of all wanted host parameters +--- @return (boolean|table) Table of all wanted host params if input param is a table. The single parameter if input param is a string. Returns false if host_id is nil or empty. function ScBroker:get_host_infos(host_id, info) -- return because host_id isn't valid if host_id == nil or host_id == "" then @@ -123,12 +119,11 @@ function ScBroker:get_host_infos(host_id, info) end end ---- get_service_infos: retrieve the the desired service informations --- @param host_id (number) --- @param service_id (number) --- @params info (string|table) the name of the wanted host parameter or a table of all wanted service parameters --- @return false (boolean) if host_id and/or service_id are nil or empty --- @return service (any) a table of all wanted service params if input param is a table. A single parameter if input param is a string +--- Retrieve the the desired service informations +--- @param host_id (number) ID of the host +--- @param service_id (number) ID of the service +--- @param info (string|table) the name of the wanted host parameter or a table of all wanted service parameters +--- @return (boolean|table) Table of all wanted service params if input param is a table. A single parameter if input param is a string. Returns false if host_id and/or service_id are nil or empty function ScBroker:get_service_infos(host_id, service_id, info) -- return because host_id or service_id isn't valid if host_id == nil or host_id == "" or service_id == nil or service_id == "" then @@ -176,10 +171,9 @@ function ScBroker:get_service_infos(host_id, service_id, info) end end ---- get_hostgroups: retrieve hostgroups from host_id --- @param host_id (number) --- @return false (boolean) if host id is invalid or no hostgroup found --- @return hostgroups (table) a table of all hostgroups for the host +--- Retrieve hostgroups from host_id +--- @param host_id (number) ID of the host +--- @return (boolean|table) Table of all hostgroups of the host or false if host id is invalid or no hostgroup found function ScBroker:get_hostgroups(host_id) -- return false if host id is invalid if host_id == nil or host_id == "" then @@ -198,11 +192,31 @@ function ScBroker:get_hostgroups(host_id) return hostgroups end ---- get_servicegroups: retrieve servicegroups from service_id --- @param host_id (number) --- @param service_id (number) --- @return false (boolean) if host_id or service_id are invalid or no service group found --- @return servicegroups (table) a table of all servicegroups for the service +--- Retrieve hostgroup alias from hostgroup_id +--- @param hostgroup_id number ID of the host group +--- @return (boolean|string) Hostgroup alias or false if hostgroup ID is invalid +function ScBroker:get_hostgroup_alias(hostgroup_id) + -- return false if host id is invalid + if hostgroup_id == nil or hostgroup_id == "" then + self.logger:warning("[sc_broker:get_hostgroup_alias]: hostgroup_id is nil or empty") + return false + end + + -- get hostgroup alias + local alias = broker_cache:get_hostgroup_alias(hostgroup_id) + + -- return false if no hostgroups were found + if not alias then + return false + end + + return alias +end + +--- Retrieve servicegroups from service_id +--- @param host_id (number) ID of the host +--- @param service_id (number) ID of the service +--- @return (boolean|table) Table of all servicegroups of the service or false if host_id or service_id is invalid or no information are found in the broker_cache function ScBroker:get_servicegroups(host_id, service_id) -- return false if service id is invalid if host_id == nil or host_id == "" or service_id == nil or service_id == "" then @@ -221,11 +235,10 @@ function ScBroker:get_servicegroups(host_id, service_id) return servicegroups end ---- get_severity: retrieve severity from host or service --- @param host_id (number) --- @param [opt] service_id (number) --- @return false (boolean) if host id is invalid or no severity were found --- @return severity (table) all the severity from the host or the service +--- Retrieve severity from host or service +--- @param host_id (number) ID of the host +--- @param service_id (number) OPTIONAL: ID of the service (do not use for a host) +--- @return (boolean|table) Severity of a host/service or false if host_id is invalid or no information are found in the broker_cache function ScBroker:get_severity(host_id, service_id) -- return false if host id is invalid if host_id == nil or host_id == "" then @@ -261,10 +274,9 @@ function ScBroker:get_severity(host_id, service_id) return severity end ---- get_instance: retrieve poller from instance_id --- @param host_id (number) --- @return false (boolean) if host_id is invalid or no instance found in cache --- @return name (string) the name of the instance +--- Retrieve poller from instance_id +--- @param host_id (number) ID of the host +--- @return (boolean|table) Name of the poller/instance or false if host_id is invalid or no information are found in the broker_cache function ScBroker:get_instance(instance_id) -- return false if instance_id is invalid if instance_id == nil or instance_id == "" then @@ -284,10 +296,9 @@ function ScBroker:get_instance(instance_id) return name end ---- get_ba_info: retrieve ba name and description from ba id --- @param ba_id (number) --- @return false (boolean) if the ba_id is invalid or no information were found in the broker cache --- @return ba_info (table) a table with the name and description of the ba +--- Retrieve BA name and description from ba id +--- @param ba_id (number) ID of the BA +--- @return (boolean|table) Name and description of all the BA or false if ba_id is invalid or no information are found in the broker_cache function ScBroker:get_ba_infos(ba_id) -- return false if ba_id is invalid if ba_id == nil or ba_id == "" then @@ -307,10 +318,9 @@ function ScBroker:get_ba_infos(ba_id) return ba_info end ---- get_bvs_infos: retrieve bv name and description from ba_id --- @param ba_id (number) --- @param false (boolean) if ba_id is invalid or no information are found in the broker_cache --- @return bvs (table) name and description of all the bvs +--- Retrieve bv name and description from ba_id +--- @param ba_id (number) +--- @return (boolean|table) Name and description of all the bvs or false if ba_id is invalid or no information are found in the broker_cache function ScBroker:get_bvs_infos(ba_id) -- return false if ba_id is invalid if ba_id == nil or ba_id == "" then From ac4f0916aeb918d5704f9cd4276eaf5a797b5376 Mon Sep 17 00:00:00 2001 From: omercier Date: Fri, 27 Jun 2025 16:20:38 +0200 Subject: [PATCH 23/27] enh sc_broker documentation --- .../sc_broker.lua | 151 +++++++++++------- 1 file changed, 92 insertions(+), 59 deletions(-) diff --git a/modules/centreon-stream-connectors-lib/sc_broker.lua b/modules/centreon-stream-connectors-lib/sc_broker.lua index eb13811f..e6585424 100644 --- a/modules/centreon-stream-connectors-lib/sc_broker.lua +++ b/modules/centreon-stream-connectors-lib/sc_broker.lua @@ -1,6 +1,7 @@ #!/usr/bin/lua ---- Module with Centreon broker related methods for easier usage +--- Module providing utility methods to interact with the Centreon broker. +--- Facilitates retrieval of information about hosts, services, groups, severities, instances, BAs, and BVs. --- @module sc_broker local sc_broker = {} @@ -23,33 +24,35 @@ function sc_broker.new(logger) end ---- Retrieve all information from a host ---- @param host_id number ID of the host ---- @return boolean false if host_id is not valid or no information was found in the broker cache ---- @return table all information from the host +--- This function interacts with the broker cache to fetch all available information for a given host. +--- If the host ID is invalid or no information is found, it logs a warning and returns `false`. +--- @param host_id number ID of the host to retrieve information for +--- @return boolean|table Returns `false` if the host ID is invalid or no information is found in the broker cache. +--- Returns a table containing all information about the host if successful. function ScBroker:get_host_all_infos(host_id) - -- return because host_id isn't valid + -- Check if the host_id is valid (not nil or empty) if host_id == nil or host_id == "" then self.logger:warning("[sc_broker:get_host_all_infos]: host id is nil") return false end - - -- get host information from broker cache + + -- Retrieve host information from the broker cache local host_info = broker_cache:get_host(host_id) - -- return false only if no host information were found in broker cache + -- Check if host information was found in the broker cache if not host_info then - self.logger:warning("[sc_broker:get_host_all_infos]: No host information found for host_id: " .. tostring(host_id) .. ". Restarting centengine should fix this.") + self.logger:warning("[sc_broker:get_host_all_infos]: No host information found for host_id: " .. tostring(host_id) .. ". Restarting centengine should fix this.") return false end return host_info end ---- Retrieve information from a service ---- @param host_id (number) ID of the host ---- @param service_id (number) ---- @return (boolean|table) Table of all the information from the service. Returns false if host id or service id aren't valid. +--- Retrieves all available information for a specific service from the broker cache. +--- Logs a warning and returns `false` if the host or service ID is invalid or if no information is found. +--- @param host_id number The ID of the host associated with the service. +--- @param service_id number The ID of the service to retrieve information for. +--- @return table|boolean Returns a table with all service information if successful, or `false` if the IDs are invalid or no data is found. function ScBroker:get_service_all_infos(host_id, service_id) -- return because host_id or service_id isn't valid if host_id == nil or host_id == "" or service_id == nil or service_id == "" then @@ -70,44 +73,49 @@ function ScBroker:get_service_all_infos(host_id, service_id) return service_info end ---- Retrieve the the desired host informations ---- @param host_id (number) ID of the host ---- @param info (string|table) Name of the wanted host parameter or a table of all wanted host parameters ---- @return (boolean|table) Table of all wanted host params if input param is a table. The single parameter if input param is a string. Returns false if host_id is nil or empty. +--- Retrieves the desired information about a host from the broker cache. +--- If the host ID is invalid or empty, logs a warning and returns `false`. +--- If no specific parameter is requested, returns a table with only the host ID. +--- If the host is not found in the cache, returns a table with only the host ID. +--- If a string is provided as `info`, returns the corresponding parameter value if it exists. +--- If a table is provided as `info`, returns a table with the requested parameters. +--- @param host_id number ID of the host. +--- @param info string|table Name of the desired host parameter (string) or a table of all desired host parameters. +--- @return boolean|table Table of all requested host parameters if `info` is a table, the single parameter if `info` is a string, or `false` if `host_id` is nil or empty. function ScBroker:get_host_infos(host_id, info) - -- return because host_id isn't valid + -- Return false if host_id is not valid if host_id == nil or host_id == "" then self.logger:warning("[sc_broker:get_host_infos]: host id is nil") return false end - - -- prepare return table with host information + + -- Prepare return table with host_id local host = { host_id = host_id } - -- return host_id only if no specific param is asked + -- Return host_id only if no specific parameter is requested if info == nil then return host end - -- get host information from broker cache + -- Get host information from broker cache local host_info = broker_cache:get_host(host_id) - -- return host_id only if no host information were found in broker cache + -- Return host_id only if no host information was found in broker cache if not host_info then - self.logger:warning("[sc_broker:get_host_infos]: No host information found for host_id: " .. tostring(host_id) .. ". Restarting centengine should fix this.") + self.logger:warning("[sc_broker:get_host_infos]: No host information found for host_id: " .. tostring(host_id) .. ". Restarting centengine should fix this.") return host end - -- get the desired param and return the information + -- Get the desired parameter and return the information if type(info) == "string" then if host_info[info] then return host_info[info] end end - -- get all the desired params and return the information + -- Get all the desired parameters and return the information if type(info) == "table" then for _, param in ipairs(info) do if host_info[param] then @@ -120,46 +128,49 @@ function ScBroker:get_host_infos(host_id, info) end --- Retrieve the the desired service informations ---- @param host_id (number) ID of the host ---- @param service_id (number) ID of the service ---- @param info (string|table) the name of the wanted host parameter or a table of all wanted service parameters ---- @return (boolean|table) Table of all wanted service params if input param is a table. A single parameter if input param is a string. Returns false if host_id and/or service_id are nil or empty +--- This function fetches specific service information from the broker cache based on the provided host ID, service ID, and requested parameters. +--- Logs warnings if the host ID or service ID is invalid or if no service information is found in the broker cache. +--- @param host_id number ID of the host +--- @param service_id number ID of the service +--- @param info string|table The name of the desired service parameter (string) or a table of all desired service parameters +--- @return boolean|table Returns a table containing the requested service parameters if successful. +--- Returns a single parameter if `info` is a string. Returns `false` if `host_id` or `service_id` are invalid or empty. function ScBroker:get_service_infos(host_id, service_id, info) - -- return because host_id or service_id isn't valid + -- Return false if host_id or service_id is invalid if host_id == nil or host_id == "" or service_id == nil or service_id == "" then self.logger:warning("[sc_broker:get_service_infos]: host id or service id is invalid") return false end - - -- prepare return table with service information + + -- Prepare a return table with basic service information local service = { host_id = host_id, service_id = service_id } - -- return host_id and service_id only if no specific param is asked + -- Return basic service information if no specific parameter is requested if info == nil then return service end - -- get service information from broker cache + -- Retrieve service information from the broker cache local service_info = broker_cache:get_service(host_id, service_id) - -- return host_id and service_id only if no host information were found in broker cache + -- Return basic service information if no data is found in the broker cache if not service_info then - self.logger:warning("[sc_broker:get_service_infos]: No service information found for host_id: " .. tostring(host_id) .. " and service_id: " .. tostring(service_id) - .. ". Restarting centengine should fix this.") + self.logger:warning("[sc_broker:get_service_infos]: No service information found for host_id: " .. tostring(host_id) .. " and service_id: " .. tostring(service_id) + .. ". Restarting centengine should fix this.") return service end - -- get the desired param and return the information + -- Retrieve and return the requested parameter if `info` is a string if type(info) == "string" then if service_info[info] then return service_info[info] end end - -- get all the desired params and return the information + -- Retrieve and return all requested parameters if `info` is a table if type(info) == "table" then for _, param in ipairs(info) do if service_info[param] then @@ -172,8 +183,11 @@ function ScBroker:get_service_infos(host_id, service_id, info) end --- Retrieve hostgroups from host_id ---- @param host_id (number) ID of the host ---- @return (boolean|table) Table of all hostgroups of the host or false if host id is invalid or no hostgroup found +--- This function fetches all hostgroups associated with a given host ID from the broker cache. +--- Logs a warning if the host ID is invalid or empty, and returns `false` if no hostgroups are found. +--- @param host_id number ID of the host +--- @return boolean|table Returns a table containing all hostgroups of the host if successful. +--- Returns `false` if the host ID is invalid or no hostgroups are found. function ScBroker:get_hostgroups(host_id) -- return false if host id is invalid if host_id == nil or host_id == "" then @@ -193,8 +207,11 @@ function ScBroker:get_hostgroups(host_id) end --- Retrieve hostgroup alias from hostgroup_id +--- This function fetches the alias of a specific hostgroup based on its ID from the broker cache. +--- Logs a warning if the hostgroup ID is invalid or empty, and returns `false` if no alias is found. --- @param hostgroup_id number ID of the host group ---- @return (boolean|string) Hostgroup alias or false if hostgroup ID is invalid +--- @return boolean|string Returns the alias of the hostgroup if successful. +--- Returns `false` if the hostgroup ID is invalid or no alias is found. function ScBroker:get_hostgroup_alias(hostgroup_id) -- return false if host id is invalid if hostgroup_id == nil or hostgroup_id == "" then @@ -214,9 +231,12 @@ function ScBroker:get_hostgroup_alias(hostgroup_id) end --- Retrieve servicegroups from service_id ---- @param host_id (number) ID of the host ---- @param service_id (number) ID of the service ---- @return (boolean|table) Table of all servicegroups of the service or false if host_id or service_id is invalid or no information are found in the broker_cache +--- This function fetches all servicegroups associated with a given service ID and host ID from the broker cache. +--- Logs a warning if the host ID or service ID is invalid or empty, and returns `false` if no servicegroups are found. +--- @param host_id number ID of the host +--- @param service_id number ID of the service +--- @return boolean|table Returns a table containing all servicegroups of the service if successful. +--- Returns `false` if the host ID or service ID is invalid or no servicegroups are found. function ScBroker:get_servicegroups(host_id, service_id) -- return false if service id is invalid if host_id == nil or host_id == "" or service_id == nil or service_id == "" then @@ -236,9 +256,13 @@ function ScBroker:get_servicegroups(host_id, service_id) end --- Retrieve severity from host or service ---- @param host_id (number) ID of the host ---- @param service_id (number) OPTIONAL: ID of the service (do not use for a host) ---- @return (boolean|table) Severity of a host/service or false if host_id is invalid or no information are found in the broker_cache +--- This function fetches the severity level of a host or service from the broker cache. +--- Logs a warning if the host ID is invalid or empty, and returns `false` if no severity is found. +--- If a service ID is provided, it retrieves the severity for the service; otherwise, it retrieves the severity for the host. +--- @param host_id number ID of the host +--- @param service_id number OPTIONAL: ID of the service (do not use for a host) +--- @return boolean|table Returns the severity of the host or service if successful. +--- Returns `false` if the host ID is invalid or no severity is found. function ScBroker:get_severity(host_id, service_id) -- return false if host id is invalid if host_id == nil or host_id == "" then @@ -274,9 +298,12 @@ function ScBroker:get_severity(host_id, service_id) return severity end ---- Retrieve poller from instance_id ---- @param host_id (number) ID of the host ---- @return (boolean|table) Name of the poller/instance or false if host_id is invalid or no information are found in the broker_cache +--- Retrieve poller information from instance ID +--- This function fetches the name of the poller/instance associated with a given instance ID from the broker cache. +--- Logs a warning if the instance ID is invalid or empty, and returns `false` if no information is found. +--- @param instance_id number ID of the instance +--- @return boolean|string Returns the name of the poller/instance if successful. +--- Returns `false` if the instance ID is invalid or no information is found in the broker cache. function ScBroker:get_instance(instance_id) -- return false if instance_id is invalid if instance_id == nil or instance_id == "" then @@ -296,9 +323,12 @@ function ScBroker:get_instance(instance_id) return name end ---- Retrieve BA name and description from ba id ---- @param ba_id (number) ID of the BA ---- @return (boolean|table) Name and description of all the BA or false if ba_id is invalid or no information are found in the broker_cache +--- Retrieve BA information from BA ID +--- This function fetches the name and description of a specific Business Activity (BA) based on its ID from the broker cache. +--- Logs a warning if the BA ID is invalid or empty, and returns `false` if no information is found. +--- @param ba_id number ID of the Business Activity (BA) +--- @return boolean|table Returns a table containing the name and description of the BA if successful. +--- Returns `false` if the BA ID is invalid or no information is found in the broker cache. function ScBroker:get_ba_infos(ba_id) -- return false if ba_id is invalid if ba_id == nil or ba_id == "" then @@ -318,9 +348,12 @@ function ScBroker:get_ba_infos(ba_id) return ba_info end ---- Retrieve bv name and description from ba_id ---- @param ba_id (number) ---- @return (boolean|table) Name and description of all the bvs or false if ba_id is invalid or no information are found in the broker_cache +--- Retrieve Business View (BV) names and descriptions associated with a given Business Activity (BA) ID. +--- This function interacts with the broker cache to fetch all BV information linked to the specified BA ID. +--- Logs warnings if the BA ID is invalid or empty, or if no BV information is found. +--- @param ba_id number ID of the Business Activity (BA). +--- @return boolean|table Returns a table containing names and descriptions of all BVs if successful. +--- Returns `false` if the BA ID is invalid or no BV information is found in the broker cache. function ScBroker:get_bvs_infos(ba_id) -- return false if ba_id is invalid if ba_id == nil or ba_id == "" then From 63ca5de2e178ce1c646e17c17e4c45f93c83c1f3 Mon Sep 17 00:00:00 2001 From: omercier Date: Fri, 27 Jun 2025 16:31:08 +0200 Subject: [PATCH 24/27] enh sc_flush doc --- .../sc_flush.lua | 152 +++++++++++------- 1 file changed, 95 insertions(+), 57 deletions(-) diff --git a/modules/centreon-stream-connectors-lib/sc_flush.lua b/modules/centreon-stream-connectors-lib/sc_flush.lua index 71ab72d3..d79bdff9 100644 --- a/modules/centreon-stream-connectors-lib/sc_flush.lua +++ b/modules/centreon-stream-connectors-lib/sc_flush.lua @@ -1,9 +1,9 @@ #!/usr/bin/lua --- --- Module that handles data queue for stream connectors --- @module sc_flush --- @alias sc_flush +--- Module that handles data queue for stream connectors +--- @module sc_flush +--- @alias sc_flush sc_flush local sc_flush = {} local sc_logger = require("centreon-stream-connectors-lib.sc_logger") @@ -11,15 +11,18 @@ local sc_common = require("centreon-stream-connectors-lib.sc_common") local ScFlush = {} ---- sc_flush.new: sc_flush constructor --- @param params (table) the params table of the stream connector --- @param [opt] sc_logger (object) a sc_logger object +--- Creates a new instance of the `sc_flush` module. +--- This constructor initializes the logger, common utilities, and data queues for the stream connector. +--- It also links event queues to their respective categories and elements based on the provided parameters. +--- @param params table The parameters table of the stream connector, containing configuration details. +--- @param logger sc_logger Optional. A `sc_logger` object for logging. If not provided, a default logger is created. +--- @return table Returns a new instance of the `sc_flush` module. function sc_flush.new(params, logger) local self = {} - - -- create a default logger if it is not provided + + -- Create a default logger if it is not provided self.sc_logger = logger - if not self.sc_logger then + if not self.sc_logger then self.sc_logger = sc_logger.new() end @@ -37,8 +40,8 @@ function sc_flush.new(params, logger) [categories.bam.id] = {}, global_queues_metadata = {} } - - -- link events queues to their respective categories and elements + + -- Link event queues to their respective categories and elements for element_name, element_info in pairs(self.params.accepted_elements_info) do self.queues[element_info.category_id][element_info.element_id] = { events = {}, @@ -53,19 +56,23 @@ function sc_flush.new(params, logger) return self end ---- add_queue_metadata: add specific metadata to a queue --- @param category_id (number) the id of the bbdo category --- @param element_id (number) the id of the bbdo element --- @param metadata (table) a table with keys that are the name of the metadata and values the metadata values +--- Adds specific metadata to a queue. +--- This function updates the metadata of a queue associated with a given category and element. +--- If the category or element is not accepted, it logs a warning and does not modify the queue. +--- @param category_id number The ID of the BBDO category. +--- @param element_id number The ID of the BBDO element. +--- @param metadata table A table containing metadata as key-value pairs to be added to the queue. function ScFlush:add_queue_metadata(category_id, element_id, metadata) + -- Check if the category exists in the queues if not self.queues[category_id] then self.sc_logger:warning("[ScFlush:add_queue_metadata]: can't add queue metadata for category: " .. self.params.reverse_category_mapping[category_id] .. " (id: " .. category_id .. ") and element: " .. self.params.reverse_element_mapping[category_id][element_id] .. " (id: " .. element_id .. ")." .. ". metadata name: " .. tostring(metadata_name) .. ", metadata value: " .. tostring(metadata_value) - .. ". You need to accept this category with the parameter 'accepted_categories'.") + .. ". You need to accept this category with the parameter 'accepted_categories'.") return end + -- Check if the element exists in the category if not self.queues[category_id][element_id] then self.sc_logger:warning("[ScFlush:add_queue_metadata]: can't add queue metadata for category: " .. self.params.reverse_category_mapping[category_id] .. " (id: " .. category_id .. ") and element: " .. self.params.reverse_element_mapping[category_id][element_id] .. " (id: " .. element_id .. ")." @@ -74,31 +81,40 @@ function ScFlush:add_queue_metadata(category_id, element_id, metadata) return end + -- Add metadata to the queue for metadata_name, metadata_value in pairs(metadata) do self.queues[category_id][element_id].queue_metadata[metadata_name] = metadata_value end end ---- flush_all_queues: tries to flush all queues according to accepted elements --- @param build_payload_method (function) the function from the stream connector that will concatenate events in the payload --- @param send_method (function) the function from the stream connector that will send the data to the wanted tool --- @return boolean (boolean) if flush failed or not +--- Flushes all queues according to the accepted elements. +--- This function determines whether to flush mixed or homogeneous payloads based on the `send_mixed_events` parameter. +--- After flushing, it resets all queues to their initial state. +--- @param build_payload_method function The function used to concatenate events into the payload. +--- @param send_method function The function used to send the payload to the desired tool. +--- @return boolean Returns `true` if all queues are successfully flushed, or `false` if an error occurs during the process. function ScFlush:flush_all_queues(build_payload_method, send_method) + -- Check if mixed events should be sent if self.params.send_mixed_events == 1 then + -- Flush mixed payloads if not self:flush_mixed_payload(build_payload_method, send_method) then return false end else + -- Flush homogeneous payloads if not self:flush_homogeneous_payload(build_payload_method, send_method) then return false end end + -- Reset all queues after flushing self:reset_all_queues() return true end ---- reset_all_queues: put all queues back to their initial state after flushing their events +--- Resets all queues to their initial state after flushing their events. +--- This function iterates through all accepted elements and clears the events stored in their respective queues. +--- Additionally, it updates the timestamp of the last global flush to the current time. function ScFlush:reset_all_queues() for _, element_info in pairs(self.params.accepted_elements_info) do self.queues[element_info.category_id][element_info.element_id].events = {} @@ -107,13 +123,18 @@ function ScFlush:reset_all_queues() self.last_global_flush = os.time() end ---- get_queues_size: get the number of events stored in all the queues --- @return queues_size (number) the number of events stored in all queues +--- Calculates the total number of events stored across all queues. +--- This function iterates through all accepted elements and sums up the number of events in their respective queues. +--- Additionally, it logs the size of each queue for debugging purposes. +--- @return number The total number of events stored in all queues. function ScFlush:get_queues_size() local queues_size = 0 + -- Iterate through all accepted elements and sum up the number of events in their queues for _, element_info in pairs(self.params.accepted_elements_info) do queues_size = queues_size + #self.queues[element_info.category_id][element_info.element_id].events + + -- Log the size of each queue for debugging purposes self.sc_logger:debug("[sc_flush:get_queues_size]: size of queue for category " .. tostring(element_info.category_name) .. " and element: " .. tostring(element_info.element_name) .. " is: " .. tostring(#self.queues[element_info.category_id][element_info.element_id].events)) @@ -122,109 +143,126 @@ function ScFlush:get_queues_size() return queues_size end ---- flush_mixed_payload: flush a payload that contains various type of events (services mixed hosts for example) --- @return boolean (boolean) true or false depending on the success of the operation +--- Flushes a payload containing various types of events (e.g., services mixed with hosts). +--- This function iterates through all queues, builds a payload for each event, and sends it using the provided methods. +--- If the maximum buffer size is reached, the payload is sent and reset before continuing. +--- Ensures that all queues are emptied to avoid broker retention issues. +--- @param build_payload_method function The function used to build the payload from events. +--- @param send_method function The function used to send the payload to the desired tool. +--- @return boolean Returns `true` if all events are successfully flushed, or `false` if an error occurs during the process. function ScFlush:flush_mixed_payload(build_payload_method, send_method) local payload = nil local counter = 0 - -- get all queues + -- Iterate through all queues for _, element_info in pairs(self.params.accepted_elements_info) do - -- get events from queues + -- Retrieve events from queues for _, event in ipairs(self.queues[element_info.category_id][element_info.element_id].events) do - -- add event to the payload + -- Add event to the payload payload = build_payload_method(payload, event) counter = counter + 1 - -- send events if max buffer size is reached + -- Send events if the maximum buffer size is reached if counter >= self.params.max_buffer_size then if not self:flush_payload(send_method, payload, self.queues.global_queues_metadata) then return false end - -- reset payload and counter because events have been sent + -- Reset payload and counter after sending events payload = nil counter = 0 end end end - -- we need to empty all queues to not mess with broker retention + -- Ensure all queues are emptied to avoid broker retention issues if not self:flush_payload(send_method, payload, self.queues.global_queues_metadata) then return false end - -- all events have been sent + -- All events have been sent successfully return true -end +end ---- flush_homogeneous_payload: flush a payload that contains a single type of events (services with services only and hosts with hosts only for example) --- @return boolean (boolean) true or false depending on the success of the operation +--- Flushes a payload containing a single type of events (e.g., services only or hosts only). +--- This function iterates through all queues, builds a payload for each event, and sends it using the provided methods. +--- If the maximum buffer size is reached, the payload is sent and reset before continuing. +--- Ensures that no events are left in the queues after processing. +--- @param build_payload_method function The function used to build the payload from events. +--- @param send_method function The function used to send the payload to the desired tool. +--- @return boolean Returns `true` if all events are successfully flushed, or `false` if an error occurs during the process. function ScFlush:flush_homogeneous_payload(build_payload_method, send_method) local counter = 0 local payload = nil - - -- get all queues + + -- Iterate through all queues for _, element_info in pairs(self.params.accepted_elements_info) do - -- get events from queues + -- Retrieve events from queues for _, event in ipairs(self.queues[element_info.category_id][element_info.element_id].events) do - -- add event to the payload + -- Add event to the payload payload = build_payload_method(payload, event) counter = counter + 1 - - -- send events if max buffer size is reached + + -- Send events if the maximum buffer size is reached if counter >= self.params.max_buffer_size then if not self:flush_payload( - send_method, - payload, + send_method, + payload, self.queues[element_info.category_id][element_info.element_id].queue_metadata ) then return false end - - -- reset payload and counter because events have been sent + + -- Reset payload and counter after sending events counter = 0 payload = nil end end - -- make sure there are no events left inside a specific queue + -- Ensure no events are left in the current queue if not self:flush_payload( - send_method, - payload, + send_method, + payload, self.queues[element_info.category_id][element_info.element_id].queue_metadata ) then return false end - -- reset payload to not mix events from different queues + -- Reset payload to avoid mixing events from different queues payload = nil end return true end ---- flush_payload: flush a given payload by sending it using the given send function --- @param send_method (function) the function that will be used to send the payload --- @param payload (any) the data that needs to be sent --- @param metadata (table) all metadata for the payload --- @return boolean (boolean) true or false depending on the success of the operation +--- Sends a given payload using the provided send function. +--- This function attempts to send the payload and its associated metadata using the `send_method`. +--- If the payload is empty or `nil`, it returns `true` to indicate no issues on the stream connector side. +--- Logs debug information about the sending attempt and errors if the operation fails. +--- @param send_method function The function used to send the payload. +--- @param payload any The data to be sent. Can be of any type. +--- @param metadata table Metadata associated with the payload. +--- @return boolean Returns `true` if the payload is successfully sent or if the payload is empty. +--- Returns `false` if an error occurs during the sending process. function ScFlush:flush_payload(send_method, payload, metadata) - -- when the payload doesn't exist or is empty, we just tell broker that everything is fine on the stream connector side + -- When the payload doesn't exist or is empty, we just tell broker that everything is fine on the stream connector side if not payload or payload == "" then return true end + -- Attempt to send the payload using the provided send method, protected by pcall local pcall_status, result = pcall(send_method, payload, metadata) + -- Log debug information about the sending attempt self.sc_logger:debug("[sc_flush:flush_payload]: tried to send payload protected by pcall. Status: " .. tostring(pcall_status) .. ", Message: " .. tostring(result)) + -- Log an error and return false if the sending operation fails if not pcall_status then self.sc_logger:error("[sc_flush:flush_payload]: could not send payload because of an internal error. pcall status: " .. tostring(pcall_status) .. ", error message: " .. tostring(result)) return false end + -- Return the result of the sending operation return result end - -return sc_flush \ No newline at end of file +return sc_flush From 1651cc4ac54817324759417b064d55492cc3f7b1 Mon Sep 17 00:00:00 2001 From: omercier Date: Fri, 27 Jun 2025 16:33:25 +0200 Subject: [PATCH 25/27] enh: quick doc enhancement --- .../sc_event.lua | 201 +++++++++--------- 1 file changed, 101 insertions(+), 100 deletions(-) diff --git a/modules/centreon-stream-connectors-lib/sc_event.lua b/modules/centreon-stream-connectors-lib/sc_event.lua index 807f39b3..6bfc6c3d 100644 --- a/modules/centreon-stream-connectors-lib/sc_event.lua +++ b/modules/centreon-stream-connectors-lib/sc_event.lua @@ -40,23 +40,23 @@ function sc_event.new(broker_event, params, common, logger, broker) return self end ---- is_valid_category: check if the event is in an accepted category --- @retun true|false (boolean) +--- Check if the event is in an accepted category +--- @return (boolean) true if the event's category makes it eligible for being handled, false if not function ScEvent:is_valid_category() return self:find_in_mapping(self.params.category_mapping, self.params.accepted_categories, self.event.category) end ---- is_valid_element: check if the event is an accepted element --- @return true|false (boolean) +--- Check if the event is an accepted element +--- @return (boolean) true if the event's element makes it eligible for being handled, false if not function ScEvent:is_valid_element() return self:find_in_mapping(self.params.element_mapping[self.event.category], self.params.accepted_elements, self.event.element) end ---- find_in_mapping: check if item type is in the mapping and is accepted --- @param mapping (table) the mapping table --- @param reference (string) the accepted values for the item --- @param item (string) the item we want to find in the mapping table and in the reference --- @return (boolean) +--- Check if item type is in the mapping and is accepted +--- @param mapping (table) the mapping table +--- @param reference (string) the accepted values for the item +--- @param item (string) the item we want to find in the mapping table and in the reference +--- @return (boolean) function ScEvent:find_in_mapping(mapping, reference, item) for mapping_index, mapping_value in pairs(mapping) do for reference_index, reference_value in pairs(self.sc_common:split(reference, ",")) do @@ -69,8 +69,8 @@ function ScEvent:find_in_mapping(mapping, reference, item) return false end ---- is_valid_event: check if the event is accepted depending on configured conditions --- @return true|false (boolean) +--- Check if the event is accepted depending on configured conditions +--- @return (boolean) true if the event has to be handled, false if not function ScEvent:is_valid_event() local is_valid_event = false @@ -96,8 +96,8 @@ function ScEvent:is_valid_event() return is_valid_event end ---- is_valid_neb_event: check if the event is an accepted neb type event --- @return true|false (boolean) +--- Check if the event is an accepted neb type event +--- @return (boolean) true if the event's category makes it eligible for being handled, false if not function ScEvent:is_valid_neb_event() local is_valid_event = false @@ -115,8 +115,8 @@ function ScEvent:is_valid_neb_event() return is_valid_event end ---- is_valid_host_status_event: check if the host status event is an accepted one --- @return true|false (boolean) +--- Check if the host status event is an accepted one +--- @return (boolean) function ScEvent:is_valid_host_status_event() -- return false if we can't get hostname or host id is nil if not self:is_valid_host() then @@ -175,8 +175,8 @@ function ScEvent:is_valid_host_status_event() return true end ---- is_valid_service_status_event: check if the service status event is an accepted one --- @return true|false (boolean) +--- Check if the service status event is an accepted one +--- @return (boolean) function ScEvent:is_valid_service_status_event() -- return false if we can't get hostname or host id is nil if not self:is_valid_host() then @@ -258,8 +258,8 @@ function ScEvent:is_valid_service_status_event() return true end ---- is_valid_host: check if host name and/or id are valid --- @return true|false (boolean) +--- Check if host name and/or id are valid +--- @return (boolean) function ScEvent:is_valid_host() -- return false if host id is nil @@ -317,8 +317,8 @@ function ScEvent:is_valid_host() return true end ---- is_valid_service: check if service description and/or id are valid --- @return true|false (boolean) +--- Check if service description and/or id are valid +--- @return (boolean) function ScEvent:is_valid_service() -- return false if service id is nil @@ -382,8 +382,8 @@ function ScEvent:is_valid_service() return true end ---- is_valid_event_states: wrapper method that checks common aspect of an event such as ack and state_type --- @return true|false (boolean) +--- Wrapper method that checks common aspect of an event such as ack and state_type +--- @return (boolean) function ScEvent:is_valid_event_states() -- return false if state_type (HARD/SOFT) is not valid if not self:is_valid_event_state_type() then @@ -408,9 +408,9 @@ function ScEvent:is_valid_event_states() return true end ---- is_valid_event_status: check if the event has an accepted status --- @param accepted_status_list (string) a coma separated list of accepted status ("ok,warning,critical") --- @return true|false (boolean) +--- Check if the event has an accepted status +--- @param accepted_status_list (string) a coma separated list of accepted status ("ok,warning,critical") +--- @return (boolean) function ScEvent:is_valid_event_status(accepted_status_list) local status_list = self.sc_common:split(accepted_status_list, ",") @@ -448,8 +448,8 @@ function ScEvent:is_valid_event_status(accepted_status_list) return false end ---- is_valid_event_state_type: check if the state type (HARD/SOFT) is accepted --- @return true|false (boolean) +--- Check if the state type (HARD/SOFT) is accepted +--- @return (boolean) function ScEvent:is_valid_event_state_type() if not self.sc_common:compare_numbers(self.event.state_type, self.params.hard_only, ">=") then self.sc_logger:warning("[sc_event:is_valid_event_state_type]: event is not in an valid state type. Event state type must be above or equal to " .. tostring(self.params.hard_only) @@ -460,8 +460,8 @@ function ScEvent:is_valid_event_state_type() return true end ---- is_valid_event_acknowledge_state: check if the acknowledge state of the event is valid --- @return true|false (boolean) +--- Check if the acknowledge state of the event is valid +--- @return (boolean) function ScEvent:is_valid_event_acknowledge_state() -- compat patch bbdo 3 => bbdo 2 if (not self.event.acknowledged and self.event.acknowledgement_type) then @@ -481,8 +481,8 @@ function ScEvent:is_valid_event_acknowledge_state() return true end ---- is_valid_event_downtime_state: check if the event is in an accepted downtime state --- @return true|false (boolean) +--- Check if the event is in an accepted downtime state +--- @return (boolean) function ScEvent:is_valid_event_downtime_state() -- patch compat bbdo 3 => bbdo 2 if (not self.event.scheduled_downtime_depth and self.event.downtime_depth) then @@ -498,8 +498,8 @@ function ScEvent:is_valid_event_downtime_state() return true end ---- is_valid_event_flapping_state: check if the event is in an accepted flapping state --- @return true|false (boolean) +--- Check if the event is in an accepted flapping state +--- @return (boolean) function ScEvent:is_valid_event_flapping_state() if not self.sc_common:compare_numbers(self.params.flapping, self.sc_common:boolean_to_number(self.event.flapping), ">=") then self.sc_logger:warning("[sc_event:is_valid_event_flapping_state]: event is not in an valid flapping state. Event flapping state must be below or equal to " .. tostring(self.params.flapping) @@ -510,8 +510,8 @@ function ScEvent:is_valid_event_flapping_state() return true end ---- is_valid_hostgroup: check if the event is in an accepted hostgroup --- @return true|false (boolean) +--- Check if the event is in an accepted hostgroup +--- @return (boolean) function ScEvent:is_valid_hostgroup() self.event.cache.hostgroups = self.sc_broker:get_hostgroups(self.event.host_id) @@ -560,10 +560,9 @@ function ScEvent:is_valid_hostgroup() return true end ---- find_hostgroup_in_list: compare accepted hostgroups from parameters with the event hostgroups --- @param hostgroups_list (string) a coma separated list of hostgroup name --- @return hostgroup_name (string) the name of the first matching hostgroup --- @return false (boolean) if no matching hostgroup has been found +--- Compare accepted hostgroups from parameters with the event hostgroups +--- @param hostgroups_list (string) a coma separated list of hostgroup name +--- @return (string|boolean) Name of the first matching hostgroup or false if no matching hostgroup has been found function ScEvent:find_hostgroup_in_list(hostgroups_list) if hostgroups_list == nil or hostgroups_list == "" then return false @@ -579,8 +578,8 @@ function ScEvent:find_hostgroup_in_list(hostgroups_list) return false end ---- is_valid_servicegroup: check if the event is in an accepted servicegroup --- @return true|false (boolean) +--- Check if the event is in an accepted servicegroup +--- @return (boolean) function ScEvent:is_valid_servicegroup() self.event.cache.servicegroups = self.sc_broker:get_servicegroups(self.event.host_id, self.event.service_id) @@ -629,9 +628,9 @@ function ScEvent:is_valid_servicegroup() return true end ---- find_servicegroup_in_list: compare accepted servicegroups from parameters with the event servicegroups --- @param servicegroups_list (string) a coma separated list of servicegroup name --- @return servicegroup_name or false (string|boolean) the name of the first matching servicegroup if found or false if not found +--- Compare accepted servicegroups from parameters with the event servicegroups +--- @param servicegroups_list (string) a coma separated list of servicegroup name +--- @return (string|boolean) Name of the first matching servicegroup if found or false if not found function ScEvent:find_servicegroup_in_list(servicegroups_list) if servicegroups_list == nil or servicegroups_list == "" then return false @@ -647,8 +646,8 @@ function ScEvent:find_servicegroup_in_list(servicegroups_list) return false end ---- is_valid_bam_event: check if the event is an accepted bam type event --- @return true|false (boolean) +--- Check if the event is an accepted bam type event +--- @return (boolean) function ScEvent:is_valid_bam_event() -- return false if ba name is invalid or ba_id is nil if not self:is_valid_ba() then @@ -683,8 +682,8 @@ function ScEvent:is_valid_bam_event() return true end ---- is_valid_ba: check if ba name and/or id are valid --- @return true|false (boolean) +--- Check if ba name and/or id are valid +--- @return (boolean) function ScEvent:is_valid_ba() -- return false if ba_id is nil @@ -709,8 +708,8 @@ function ScEvent:is_valid_ba() return true end ---- is_valid_ba_status_event: check if the ba status event is an accepted one --- @return true|false (boolean) +--- Check if the ba status event is an accepted one +--- @return (boolean) function ScEvent:is_valid_ba_status_event() if not self:is_valid_event_status(self.params.ba_status) then self.sc_logger:warning("[sc_event:is_valid_ba]: Invalid BA status for BA id: " .. tostring(self.event.ba_id) .. ". State is: " @@ -721,8 +720,8 @@ function ScEvent:is_valid_ba_status_event() return true end ---- is_valid_ba_downtime_state: check if the ba downtime state is an accepted one --- @return true|false (boolean) +--- Check if the ba downtime state is an accepted one +--- @return (boolean) function ScEvent:is_valid_ba_downtime_state() if not self.sc_common:compare_numbers(self.params.in_downtime, self.sc_common:boolean_to_number(self.event.in_downtime), ">=") then self.sc_logger:warning("[sc_event:is_valid_ba]: Invalid BA downtime state for BA id: " .. tostring(self.event.ba_id) .. " downtime state is : " .. tostring(self.event.in_downtime) @@ -733,8 +732,8 @@ function ScEvent:is_valid_ba_downtime_state() return true end ---- is_valid_ba_acknowledge_state: check if the ba acknowledge state is an accepted one --- @return true|false (boolean) +--- Check if the ba acknowledge state is an accepted one +--- @return (boolean) function ScEvent:is_valid_ba_acknowledge_state() -- if not self.sc_common:compare_numbers(self.params.in_downtime, self.event.in_downtime, '>=') then -- return false @@ -743,8 +742,8 @@ function ScEvent:is_valid_ba_acknowledge_state() return true end ---- is_valid_bv: check if the event is in an accepted BV --- @return true|false (boolean) +--- Check if the event is in an accepted BV +--- @return (boolean) function ScEvent:is_valid_bv() self.event.cache.bvs = self.sc_broker:get_bvs_infos(self.event.host_id) @@ -788,10 +787,10 @@ function ScEvent:is_valid_bv() return true end ---- find_bv_in_list: compare accepted BVs from parameters with the event BVs --- @param bvs_list (string) a coma separated list of BV name --- @return bv_name (string) the name of the first matching BV --- @return false (boolean) if no matching BV has been found +--- Compare accepted BVs from parameters with the event BVs +--- @param bvs_list (string) a coma separated list of BV name +--- @return (string) Name of the first matching BV +--- @return (boolean) false if no matching BV has been found function ScEvent:find_bv_in_list(bvs_list) if bvs_list == nil or bvs_list == "" then return false @@ -807,8 +806,8 @@ function ScEvent:find_bv_in_list(bvs_list) return false end ---- is_valid_poller: check if the event is monitored from an accepted poller --- @return true|false (boolean) +--- Check if the event is monitored from an accepted poller +--- @return (boolean) function ScEvent:is_valid_poller() -- return false if instance id is not found in cache if not self.event.cache.host.instance_id then @@ -864,9 +863,9 @@ function ScEvent:is_valid_poller() return true end ---- find_poller_in_list: compare accepted pollers from parameters with the event poller --- @param pollers_list (string) a coma separated list of poller name --- @return poller_name or false (string|boolean) the name of the first matching poller if found or false if not found +--- Compare accepted pollers from parameters with the event poller +--- @param pollers_list (string) a coma separated list of poller name +--- @return (string|boolean) Name of the first matching poller if found or false if not found function ScEvent:find_poller_in_list(pollers_list) if pollers_list == nil or pollers_list == "" then return false @@ -880,8 +879,8 @@ function ScEvent:find_poller_in_list(pollers_list) return false end ---- is_valid_host_severity: checks if the host severity is accepted --- @return true|false (boolean) +--- Checks if the host severity is accepted +--- @return (boolean) function ScEvent:is_valid_host_severity() -- initiate the severity table in the cache if it doesn't exist if not self.event.cache.severity then @@ -908,8 +907,8 @@ function ScEvent:is_valid_host_severity() return true end ---- is_valid_service_severity: checks if the service severity is accepted --- @return true|false (boolean) +--- Checks if the service severity is accepted +--- @return (boolean) function ScEvent:is_valid_service_severity() -- initiate the severity table in the cache if it doesn't exist if not self.event.cache.severity then @@ -937,8 +936,8 @@ function ScEvent:is_valid_service_severity() return true end ----is_valid_acknowledgement_event: checks if the event is a valid acknowledge event --- @return true|false (boolean) +--- Checks if the event is a valid acknowledge event +--- @return (boolean) function ScEvent:is_valid_acknowledgement_event() -- return false if we can't get hostname or host id is nil if not self:is_valid_host() then @@ -987,7 +986,7 @@ function ScEvent:is_valid_acknowledgement_event() return false end - -- use dedicated ack host status configuration or host_status configuration + -- use dedicated ack service status configuration or service_status configuration event_status = self.sc_common:ifnil_or_empty(self.params.ack_service_status, self.params.service_status) -- return false if event status is not accepted @@ -1021,8 +1020,8 @@ function ScEvent:is_valid_acknowledgement_event() return true end ---- is_vaid_downtime_event: check if the event is a valid downtime event --- return true|false (boolean) +--- Check if the event is a valid downtime event +--- @return (boolean) function ScEvent:is_valid_downtime_event() -- return false if the event is one of all the "fake" start or end downtime event received from broker if not self:is_downtime_event_useless() then @@ -1054,7 +1053,7 @@ function ScEvent:is_valid_downtime_event() -- store the result in the self.event.state because doing that allow us to use the is_valid_event_status method self.event.state = self:get_downtime_host_status() - -- checks if the current host downtime state is an accpeted status + -- checks if the current host downtime state is an accepted status if not self:is_valid_event_status(self.params.dt_host_status) then self.sc_logger:warning("[sc_event:is_valid_downtime_event]: host_id: " .. tostring(self.event.host_id) .. " do not have a validated status. Status: " .. tostring(self.params.status_mapping[self.event.category][self.event.element][self.event.type][self.event.state]) @@ -1103,8 +1102,8 @@ function ScEvent:is_valid_downtime_event() return true end ---- is_valid_author: check if the author of a comment is valid based on contact alias in Centreon --- return true|false (boolean) +--- Check if the author of a comment is valid based on contact alias in Centreon +--- @return (boolean) function ScEvent:is_valid_author() -- return true if options are not set or if both options are set local accepted_authors_isnotempty = self.params.accepted_authors ~= "" @@ -1130,8 +1129,8 @@ function ScEvent:is_valid_author() end --- find_author_in_list: compare accepted authors from parameters with the event author --- @param authors_list (string) a coma separeted list of author name --- @return accepted_alias or false (string|boolean) the alias of the first matching author if found or false if not found +--- @param authors_list (string) a coma separated list of author name +--- @return (string|boolean) accepted_alias or false - the alias of the first matching author if found or false if not found function ScEvent:find_author_in_list(authors_list) if authors_list == nil or authors_list == "" then return false @@ -1145,8 +1144,8 @@ function ScEvent:find_author_in_list(authors_list) return false end ---- get_downtime_host_status: retrieve the status of a host based on last_time_up/down dates found in cache (self.event.cache.host must be set) --- return status (number) the status code of the host +--- Retrieve the status of a host based on last_time_up/down dates found in cache (self.event.cache.host must be set) +--- @return (number) the status code of the host function ScEvent:get_downtime_host_status() -- if cache is not filled we can't get the state of the host if not self.event.cache.host.last_time_up or not self.event.cache.host.last_time_down then @@ -1162,8 +1161,8 @@ function ScEvent:get_downtime_host_status() return self:get_most_recent_status_code(timestamp) end ---- get_downtime_service_status: retrieve the status of a service based on last_time_ok/warning/critical/unknown dates found in cache (self.event.cache.host must be set) --- return status (number) the status code of the service +--- Retrieve the status of a service based on last_time_ok/warning/critical/unknown dates found in cache (self.event.cache.host must be set) +--- @return (number) the status code of the service function ScEvent:get_downtime_service_status() -- if cache is not filled we can't get the state of the service if @@ -1186,9 +1185,9 @@ function ScEvent:get_downtime_service_status() return self:get_most_recent_status_code(timestamp) end ---- get_most_recent_status_code: retrieve the last status code from a list of status and timestamp --- @param timestamp (table) a table with the association of the last known timestamp of a status and its corresponding status code --- @return status (number) the most recent status code of the object +--- Retrieve the last status code from a list of status and timestamp +--- @param timestamp (table) a table with the association of the last known timestamp of a status and its corresponding status code +--- @return (number) the most recent status code of the object function ScEvent:get_most_recent_status_code(timestamp) -- prepare the table in wich the latest known status timestamp and status code will be stored @@ -1209,7 +1208,7 @@ function ScEvent:get_most_recent_status_code(timestamp) end --- is_service_status_event_duplicated: check if the service event is the same than the last one (will not work for OK(H) -> CRITICAL(S) -> OK(H)) --- @return true|false (boolean) +--- @return (boolean) function ScEvent:is_service_status_event_duplicated() -- return false if option is not activated if self.params.enable_service_status_dedup ~= 1 then @@ -1248,7 +1247,7 @@ function ScEvent:is_service_status_event_duplicated() end --- is_host_status_event_duplicated: check if the host event is the same than the last one (will not work for UP(H) -> DOWN(S) -> UP(H)) --- @return true|false (boolean) +--- @return (boolean) function ScEvent:is_host_status_event_duplicated() -- return false if option is not activated if self.params.enable_host_status_dedup ~= 1 then @@ -1257,7 +1256,8 @@ function ScEvent:is_host_status_event_duplicated() end -- if last check is the same than last_hard_state_change (allowing a delta timestamp), it means the event just change its status so it cannot be a duplicated event - if math.abs(self.event.last_hard_state_change - self.event.last_check) <= self.params.delta_host_status_change_allow or (self.event.last_update ~= nil and math.abs(self.event.last_hard_state_change - self.event.last_update) <= self.params.delta_host_status_change_allow) then + if math.abs(self.event.last_hard_state_change - self.event.last_check) <= self.params.delta_host_status_change_allow + or (self.event.last_update ~= nil and math.abs(self.event.last_hard_state_change - self.event.last_update) <= self.params.delta_host_status_change_allow) then return false end @@ -1285,9 +1285,9 @@ function ScEvent:is_host_status_event_duplicated() end ---- is_downtime_event_useless: the purpose of this method is to filter out unnecessary downtime event. It appears that broker --- is sending many downtime events before sending the one we want --- @return true|false (boolean) +--- The purpose of this method is to filter out unnecessary downtime event. It appears that broker +--- is sending many downtime events before sending the one we want +--- @return (boolean) function ScEvent:is_downtime_event_useless() -- return false if downtime event is not a valid start of downtime event if self:is_valid_downtime_event_start() then @@ -1302,8 +1302,8 @@ function ScEvent:is_downtime_event_useless() return false end ---- is_valid_downtime_event_start: make sure that the event is the one notifying us that a downtime has just started --- @return true|false (boolean) +--- Make sure that the event is the one notifying us that a downtime has just started +--- @return (boolean) function ScEvent:is_valid_downtime_event_start() -- event is about the end of the downtime (actual_end_time key is not present in a start downtime bbdo2 event) -- with bbdo3 value is set to -1 @@ -1332,8 +1332,8 @@ function ScEvent:is_valid_downtime_event_start() return true end ---- is_valid_downtime_event_end: make sure that the event is the one notifying us that a downtime has just ended --- @return true|false (boolean) +--- Make sure that the event is the one notifying us that a downtime has just ended +--- @return (boolean) function ScEvent:is_valid_downtime_event_end() -- event is about the end of the downtime (deletion_time key is only present in a end downtime event) if (self.bbdo_version == 2 and self.event.deletion_time) or (self.bbdo_version > 2 and self.event.deletion_time ~= -1) then @@ -1355,7 +1355,8 @@ function ScEvent:is_valid_downtime_event_end() return false end ---- build_outputs: adds short_output and long_output entries in the event table. output entry will be equal to one or another depending on the use_longoutput param +--- Adds short_output and long_output entries in the event table. output entry will be equal to one or another depending on the use_longoutput param +--- @return void function ScEvent:build_outputs() -- build long output if self.event.long_output and self.event.long_output ~= "" then @@ -1388,8 +1389,8 @@ function ScEvent:build_outputs() end ---- is_valid_storage: DEPRECATED method, use NEB category to get metric data instead --- @return true (boolean) +--- **DEPRECATED METHOD** use NEB category to get metric data instead +--- @return (boolean) Returns always true function ScEvent:is_valid_storage_event() return true end From 956da43c7f844e2802ff11c940d807abf572854d Mon Sep 17 00:00:00 2001 From: omercier Date: Fri, 27 Jun 2025 17:20:09 +0200 Subject: [PATCH 26/27] =?UTF-8?q?enh:=20improve=20documentaiton=20of=20sc?= =?UTF-8?q?=5Fevent=C2=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sc_event.lua | 757 ++++++++++-------- 1 file changed, 433 insertions(+), 324 deletions(-) diff --git a/modules/centreon-stream-connectors-lib/sc_event.lua b/modules/centreon-stream-connectors-lib/sc_event.lua index 6bfc6c3d..e8a07c66 100644 --- a/modules/centreon-stream-connectors-lib/sc_event.lua +++ b/modules/centreon-stream-connectors-lib/sc_event.lua @@ -14,49 +14,64 @@ local sc_broker = require("centreon-stream-connectors-lib.sc_broker") local ScEvent = {} +--- Create a new ScEvent instance. +--- This function initializes a new ScEvent object with the provided broker event, parameters, common utilities, logger, and broker. +--- It sets up the event table and meta table for accessing broker event properties. +--- @param broker_event (table) The event data received from the Centreon broker. +--- @param params (sc_params) Configuration parameters for event processing. +--- @param common (sc_common) Common utility functions. +--- @param logger (sc_logger) Logger instance for logging messages. +--- @param broker (sc_broker) Broker instance for interacting with Centreon broker data. +--- @return (sc_event) A new ScEvent instance. function sc_event.new(broker_event, params, common, logger, broker) local self = {} + -- Initialize logger, or create a new one if not provided. self.sc_logger = logger - if not self.sc_logger then + if not self.sc_logger then self.sc_logger = sc_logger.new() end + + -- Assign common utilities, parameters, broker event, and broker instance. self.sc_common = common self.params = params self.broker_event = broker_event self.sc_broker = broker self.bbdo_version = self.sc_common:get_bbdo_version() - -- we create our event table + -- Create the event table with a cache for storing intermediate data. self.event = { cache = {} } - -- create the meta table for the self.event table - local event_meta = { __index = function (tbl, key) return self.broker_event[key] end} + -- Create a meta table for accessing broker event properties dynamically. + local event_meta = { __index = function (tbl, key) return self.broker_event[key] end } setmetatable(self.event, event_meta) + -- Set the meta table for the ScEvent instance. setmetatable(self, { __index = ScEvent }) return self end ---- Check if the event is in an accepted category ---- @return (boolean) true if the event's category makes it eligible for being handled, false if not +--- Check if the event is in an accepted category. +--- This method validates whether the event's category matches the accepted categories defined in the configuration. +--- @return (boolean) true if the event's category is accepted, `false` otherwise. function ScEvent:is_valid_category() return self:find_in_mapping(self.params.category_mapping, self.params.accepted_categories, self.event.category) end ---- Check if the event is an accepted element ---- @return (boolean) true if the event's element makes it eligible for being handled, false if not +--- Check if the event is an accepted element. +--- This method validates whether the event's element matches the accepted elements defined in the configuration. +--- @return (boolean) true if the event's element is accepted, `false` otherwise. function ScEvent:is_valid_element() return self:find_in_mapping(self.params.element_mapping[self.event.category], self.params.accepted_elements, self.event.element) end ---- Check if item type is in the mapping and is accepted ---- @param mapping (table) the mapping table ---- @param reference (string) the accepted values for the item ---- @param item (string) the item we want to find in the mapping table and in the reference ---- @return (boolean) +--- Check if an item type is in the mapping and is accepted. +--- This method checks whether a given item exists in the mapping table and matches the accepted reference values. +--- @param mapping (table) The mapping table containing item mappings. +--- @param reference (string) A comma-separated list of accepted values for the item. +--- @param item (string) The item to validate function ScEvent:find_in_mapping(mapping, reference, item) for mapping_index, mapping_value in pairs(mapping) do for reference_index, reference_value in pairs(self.sc_common:split(reference, ",")) do @@ -69,12 +84,14 @@ function ScEvent:find_in_mapping(mapping, reference, item) return false end ---- Check if the event is accepted depending on configured conditions ---- @return (boolean) true if the event has to be handled, false if not +--- Check if the event is accepted depending on configured conditions. +--- This method validates the event based on its category and custom code. +--- It ensures the event meets the criteria defined in the configuration parameters. +--- @return (boolean) true if the event has to be handled, false otherwise. function ScEvent:is_valid_event() local is_valid_event = false - - -- run validation tests depending on the category of the event + + -- Run validation tests depending on the category of the event. if self.event.category == self.params.bbdo.categories.neb.id then is_valid_event = self:is_valid_neb_event() elseif self.event.category == self.params.bbdo.categories.storage.id then @@ -83,30 +100,31 @@ function ScEvent:is_valid_event() is_valid_event = self:is_valid_bam_event() end - -- drop the event if it was not valid. Custom code do not have to work on already invalid events + -- Drop the event if it was not valid. Custom code does not work on already invalid events. if not is_valid_event then return is_valid_event end - -- run custom code + -- Run custom code if provided in the configuration. if self.params.custom_code and type(self.params.custom_code) == "function" then self, is_valid_event = self.params.custom_code(self) - end + end return is_valid_event end ---- Check if the event is an accepted neb type event ---- @return (boolean) true if the event's category makes it eligible for being handled, false if not +--- Check if the event is an accepted NEB type event. +--- This method validates NEB events based on their element type. +--- @return (boolean) true if the event's category makes it eligible for being handled, `false` otherwise. function ScEvent:is_valid_neb_event() local is_valid_event = false - - -- run validation tests depending on the element type of the neb event + + -- Run validation tests depending on the element type of the NEB event. if self.event.element == self.params.bbdo.elements.host_status.id then is_valid_event = self:is_valid_host_status_event() elseif self.event.element == self.params.bbdo.elements.service_status.id then is_valid_event = self:is_valid_service_status_event() - elseif self.event.element == self.params.bbdo.elements.acknowledgement.id then + elseif self.event.element == self.params.bbdo.elements.acknowledgement.id then is_valid_event = self:is_valid_acknowledgement_event() elseif self.event.element == self.params.bbdo.elements.downtime.id then is_valid_event = self:is_valid_downtime_event() @@ -115,55 +133,55 @@ function ScEvent:is_valid_neb_event() return is_valid_event end ---- Check if the host status event is an accepted one ---- @return (boolean) +--- Check if the host status event is an accepted one. +--- This method validates host status events based on various criteria, including host validation, event status, and severity. +--- @return (boolean) true if the host status event is valid, `false` otherwise. function ScEvent:is_valid_host_status_event() - -- return false if we can't get hostname or host id is nil + -- Return `false` if we can't get hostname or host ID is nil. if not self:is_valid_host() then self.sc_logger:warning("[sc_event:is_valid_host_status_event]: host_id: " .. tostring(self.event.host_id) .. " hasn't been validated") return false end - - -- return false if event status is not accepted + + -- Return `false` if event status is not accepted. if not self:is_valid_event_status(self.params.host_status) then - self.sc_logger:warning("[sc_event:is_valid_host_status_event]: host_id: " .. tostring(self.event.host_id) - .. " do not have a validated status. Status: " .. tostring(self.params.status_mapping[self.event.category][self.event.element][self.event.state])) + self.sc_logger:warning("[sc_event:is_valid_host_status_event]: host_id: " .. tostring(self.event.host_id) + .. " does not have a validated status. Status: " .. tostring(self.params.status_mapping[self.event.category][self.event.element][self.event.state])) return false end - -- return false if event status is a duplicate and dedup is enabled + -- Return `false` if event status is a duplicate and deduplication is enabled. if self:is_host_status_event_duplicated() then self.sc_logger:warning("[sc_event:is_host_status_event_duplicated]: host_id: " .. tostring(self.event.host_id) - .. " is sending a duplicated event. Dedup option (enable_host_status_dedup) is set to: " .. tostring(self.params.enable_host_status_dedup)) + .. " is sending a duplicated event. Deduplication option (enable_host_status_dedup) is set to: " .. tostring(self.params.enable_host_status_dedup)) return false end - -- return false if one of event ack, downtime, state type (hard soft) or flapping aren't valid + -- Return `false` if one of event acknowledgment, downtime, state type (hard/soft), or flapping states is not valid. if not self:is_valid_event_states() then - self.sc_logger:warning("[sc_event:is_valid_host_status_event]: host_id: " .. tostring(self.event.host_id) .. " is not in a validated downtime, ack or hard/soft state") + self.sc_logger:warning("[sc_event:is_valid_host_status_event]: host_id: " .. tostring(self.event.host_id) .. " is not in a validated downtime, acknowledgment, or hard/soft state") return false end - -- return false if host is not monitored from an accepted poller + -- Return `false` if host is not monitored from an accepted poller. if not self:is_valid_poller() then self.sc_logger:warning("[sc_event:is_valid_host_status_event]: host_id: " .. tostring(self.event.host_id) .. " is not monitored from an accepted poller") return false end - -- return false if host has not an accepted severity + -- Return `false` if host does not have an accepted severity. if not self:is_valid_host_severity() then - self.sc_logger:warning("[sc_event:is_valid_host_status_event]: host_id: " .. tostring(self.event.host_id) .. " has not an accepted severity") + self.sc_logger:warning("[sc_event:is_valid_host_status_event]: host_id: " .. tostring(self.event.host_id) .. " does not have an accepted severity") return false end - -- return false if host is not in an accepted hostgroup + -- Return `false` if host is not in an accepted hostgroup. if not self:is_valid_hostgroup() then self.sc_logger:warning("[sc_event:is_valid_host_status_event]: host_id: " .. tostring(self.event.host_id) .. " is not in an accepted hostgroup") return false end - -- in bbdo 2 last_update do exist but not in bbdo3. - -- last_check also exist in bbdo2 but it is preferable to stay compatible with all stream connectors + -- Compatibility patch for BBDO versions 2 and 3. if not self.event.last_update and self.event.last_check then self.event.last_update = self.event.last_check elseif not self.event.last_check and self.event.last_update then @@ -175,78 +193,78 @@ function ScEvent:is_valid_host_status_event() return true end ---- Check if the service status event is an accepted one ---- @return (boolean) +--- Check if the service status event is an accepted one. +--- This method validates service status events based on various criteria, including host and service validation, event status, and severity. +--- @return (boolean) true if the service status event is valid, `false` otherwise. function ScEvent:is_valid_service_status_event() - -- return false if we can't get hostname or host id is nil + -- Return `false` if we can't get hostname or host ID is nil. if not self:is_valid_host() then - self.sc_logger:warning("[sc_event:is_valid_service_status_event]: host_id: " .. tostring(self.event.host_id) + self.sc_logger:warning("[sc_event:is_valid_service_status_event]: host_id: " .. tostring(self.event.host_id) .. " hasn't been validated for service with id: " .. tostring(self.event.service_id)) return false end - -- return false if we can't get service description of service id is nil + -- Return `false` if we can't get service description or service ID is nil. if not self:is_valid_service() then self.sc_logger:warning("[sc_event:is_valid_service_status_event]: service with id: " .. tostring(self.event.service_id) .. " hasn't been validated") return false end - -- return false if event status is not accepted + -- Return `false` if event status is not accepted. if not self:is_valid_event_status(self.params.service_status) then - self.sc_logger:warning("[sc_event:is_valid_service_status_event]: service with id: " .. tostring(self.event.service_id) - .. " hasn't a validated status. Status: " .. tostring(self.params.status_mapping[self.event.category][self.event.element][self.event.state])) + self.sc_logger:warning("[sc_event:is_valid_service_status_event]: service with id: " .. tostring(self.event.service_id) + .. " does not have a validated status. Status: " .. tostring(self.params.status_mapping[self.event.category][self.event.element][self.event.state])) return false end - -- return false if event status is a duplicate and dedup is enabled + -- Return `false` if event status is a duplicate and deduplication is enabled. if self:is_service_status_event_duplicated() then self.sc_logger:warning("[sc_event:is_service_status_event_duplicated]: host_id: " .. tostring(self.event.host_id) - .. " service_id: " .. tostring(self.event.service_id) .. " is sending a duplicated event. Dedup option (enable_service_status_dedup) is set to: " .. tostring(self.params.enable_service_status_dedup)) + .. " service_id: " .. tostring(self.event.service_id) .. " is sending a duplicated event. Deduplication option (enable_service_status_dedup) is set to: " .. tostring(self.params.enable_service_status_dedup)) return false end - -- return false if one of event ack, downtime, state type (hard soft) or flapping aren't valid + -- Return `false` if one of event acknowledgment, downtime, state type (hard/soft), or flapping states is not valid. if not self:is_valid_event_states() then - self.sc_logger:warning("[sc_event:is_valid_service_status_event]: service_id: " .. tostring(self.event.service_id) .. " is not in a validated downtime, ack or hard/soft state") + self.sc_logger:warning("[sc_event:is_valid_service_status_event]: service_id: " .. tostring(self.event.service_id) .. " is not in a validated downtime, acknowledgment, or hard/soft state") return false end - -- return false if host is not monitored from an accepted poller + -- Return `false` if host is not monitored from an accepted poller. if not self:is_valid_poller() then - self.sc_logger:warning("[sc_event:is_valid_service_status_event]: service id: " .. tostring(self.event.service_id) + self.sc_logger:warning("[sc_event:is_valid_service_status_event]: service id: " .. tostring(self.event.service_id) .. ". host_id: " .. tostring(self.event.host_id) .. " is not monitored from an accepted poller") return false end - -- return false if host has not an accepted severity + -- Return `false` if host does not have an accepted severity. if not self:is_valid_host_severity() then - self.sc_logger:warning("[sc_event:is_valid_service_status_event]: service id: " .. tostring(self.event.service_id) - .. ". host_id: " .. tostring(self.event.host_id) .. ". Host has not an accepted severity") + self.sc_logger:warning("[sc_event:is_valid_service_status_event]: service id: " .. tostring(self.event.service_id) + .. ". host_id: " .. tostring(self.event.host_id) .. ". Host does not have an accepted severity") return false end - -- return false if service has not an accepted severity + -- Return `false` if service does not have an accepted severity. if not self:is_valid_service_severity() then - self.sc_logger:warning("[sc_event:is_valid_service_status_event]: service id: " .. tostring(self.event.service_id) - .. ". host_id: " .. tostring(self.event.host_id) .. ". Service has not an accepted severity") + self.sc_logger:warning("[sc_event:is_valid_service_status_event]: service id: " .. tostring(self.event.service_id) + .. ". host_id: " .. tostring(self.event.host_id) .. ". Service does not have an accepted severity") return false end - -- return false if host is not in an accepted hostgroup + -- Return `false` if host is not in an accepted hostgroup. if not self:is_valid_hostgroup() then - self.sc_logger:warning("[sc_event:is_valid_service_status_event]: service_id: " .. tostring(self.event.service_id) + self.sc_logger:warning("[sc_event:is_valid_service_status_event]: service_id: " .. tostring(self.event.service_id) .. " is not in an accepted hostgroup. Host ID is: " .. tostring(self.event.host_id)) return false end - - -- return false if service is not in an accepted servicegroup + + -- Return `false` if service is not in an accepted servicegroup. if not self:is_valid_servicegroup() then self.sc_logger:warning("[sc_event:is_valid_service_status_event]: service_id: " .. tostring(self.event.service_id) .. " is not in an accepted servicegroup") return false end - -- in bbdo 2 last_update do exist but not in bbdo3. - -- last_check also exist in bbdo2 but it is preferable to stay compatible with all stream connectors + -- Compatibility patch for BBDO versions 2 and 3. if not self.event.last_update and self.event.last_check then self.event.last_update = self.event.last_check elseif not self.event.last_check and self.event.last_update then @@ -258,41 +276,45 @@ function ScEvent:is_valid_service_status_event() return true end ---- Check if host name and/or id are valid ---- @return (boolean) +--- Validate the host name and/or ID. +--- This method checks if the host associated with the event is valid based on its ID and name. +--- It retrieves host information from the broker cache and applies validation rules based on configuration parameters. +--- @return (boolean) Returns `true` if the host is valid, `false` otherwise. function ScEvent:is_valid_host() - -- return false if host id is nil + -- Return `false` if the host ID is nil and the `skip_nil_id` parameter is enabled. if (not self.event.host_id and self.params.skip_nil_id == 1) then self.sc_logger:warning("[sc_event:is_valid_host]: Invalid host with id: " .. tostring(self.event.host_id) .. " skip nil id is: " .. tostring(self.params.skip_nil_id)) return false end + -- Retrieve host information from the broker cache. self.event.cache.host = self.sc_broker:get_host_all_infos(self.event.host_id) - -- return false if we can't get hostname + -- Return `false` if the host name is not found and the `skip_anon_events` parameter is enabled. if (not self.event.cache.host and self.params.skip_anon_events == 1) then - self.sc_logger:warning("[sc_event:is_valid_host]: No name for host with id: " .. tostring(self.event.host_id) + self.sc_logger:warning("[sc_event:is_valid_host]: No name for host with id: " .. tostring(self.event.host_id) .. " and skip anon events is: " .. tostring(self.params.skip_anon_events)) return false elseif (not self.event.cache.host and self.params.skip_anon_events == 0) then + -- Assign the host ID as the name if the host name is not found and anonymous events are allowed. self.event.cache.host = { name = self.event.host_id } end - -- force host name to be its id if no name has been found + -- Force the host name to be its ID if no name has been found. if not self.event.cache.host.name then self.event.cache.host.name = self.event.cache.host.host_id or self.event.host_id end - -- return false if event is coming from fake bam host + -- Return `false` if the event is coming from a fake BAM host and BAM hosts are disabled. if string.find(self.event.cache.host.name, "^_Module_BAM_*") and self.params.enable_bam_host == 0 then self.sc_logger:debug("[sc_event:is_valid_host]: Host is a BAM fake host: " .. tostring(self.event.cache.host.name)) return false end - -- loop through each Lua pattern to check if host name match the filter + -- Loop through each Lua pattern to check if the host name matches the filter. local is_valid_pattern = false if self.params.accepted_hosts ~= "" then for index, pattern in ipairs(self.params.accepted_hosts_pattern_list) do @@ -307,8 +329,9 @@ function ScEvent:is_valid_host() is_valid_pattern = true end + -- Return `false` if the host name does not match any accepted patterns. if not is_valid_pattern then - self.sc_logger:info("[sc_event:is_valid_host]: Host: " .. tostring(self.event.cache.host.name) + self.sc_logger:info("[sc_event:is_valid_host]: Host: " .. tostring(self.event.cache.host.name) .. " doesn't match accepted_hosts pattern: " .. tostring(self.params.accepted_hosts) .. " or any of the sub-patterns if accepted_hosts_enable_split_pattern is enabled") return false @@ -317,35 +340,39 @@ function ScEvent:is_valid_host() return true end ---- Check if service description and/or id are valid ---- @return (boolean) +--- Validate the service description and/or ID. +--- This method checks if the service associated with the event is valid based on its ID and description. +--- It retrieves service information from the broker cache and applies validation rules based on configuration parameters. +--- @return (boolean) Returns `true` if the service is valid, `false` otherwise. function ScEvent:is_valid_service() - -- return false if service id is nil + -- Return `false` if the service ID is nil and the `skip_nil_id` parameter is enabled. if (not self.event.service_id and self.params.skip_nil_id == 1) then self.sc_logger:warning("[sc_event:is_valid_service]: Invalid service with id: " .. tostring(self.event.service_id) .. " skip nil id is: " .. tostring(self.params.skip_nil_id)) return false end + -- Retrieve service information from the broker cache. self.event.cache.service = self.sc_broker:get_service_all_infos(self.event.host_id, self.event.service_id) - -- return false if we can't get service description + -- Return `false` if the service description is not found and the `skip_anon_events` parameter is enabled. if (not self.event.cache.service and self.params.skip_anon_events == 1) then - self.sc_logger:warning("[sc_event:is_valid_service]: Invalid description for service with id: " .. tostring(self.event.service_id) + self.sc_logger:warning("[sc_event:is_valid_service]: Invalid description for service with id: " .. tostring(self.event.service_id) .. " and skip anon events is: " .. tostring(self.params.skip_anon_events)) return false elseif (not self.event.cache.service and self.params.skip_anon_events == 0) then + -- Assign the service ID as the description if the service description is not found and anonymous events are allowed. self.event.cache.service = { description = self.event.service_id } end - -- force service description to its id if no description has been found + -- Force the service description to be its ID if no description has been found. if not self.event.cache.service.description then self.event.cache.service.description = self.event.service_id end - -- loop through each Lua pattern to check if service description match the filter + -- Loop through each Lua pattern to check if the service description matches the filter. local is_valid_pattern = false if self.params.accepted_services ~= "" then for index, pattern in ipairs(self.params.accepted_services_pattern_list) do @@ -360,16 +387,17 @@ function ScEvent:is_valid_service() is_valid_pattern = true end + -- Return `false` if the service description does not match any accepted patterns. if not is_valid_pattern then - self.sc_logger:info("[sc_event:is_valid_service]: Service: " .. tostring(self.event.cache.service.description) .. " from host: " .. tostring(self.event.cache.host.name) + self.sc_logger:info("[sc_event:is_valid_service]: Service: " .. tostring(self.event.cache.service.description) .. " from host: " .. tostring(self.event.cache.host.name) .. " doesn't match accepted_services pattern: " .. tostring(self.params.accepted_services) .. " or any of the sub-patterns if accepted_services_enable_split_pattern is enabled") return false end - -- if we want to send BA status using the service status mecanism, we need to use the ba_description instead of host name + -- If BAM hosts are enabled, replace the host name with the BA name for BA status events. if string.find(self.event.cache.host.name, "^_Module_BAM_*") and self.params.enable_bam_host == 1 then - self.sc_logger:debug("[sc_event:is_valid_service]: Host is a fake BAM host. Therefore, host name: " + self.sc_logger:debug("[sc_event:is_valid_service]: Host is a fake BAM host. Therefore, host name: " .. tostring(self.event.cache.host.name) .. " must be replaced by the name of the BA.") self.event.ba_id = string.gsub(self.event.cache.service.description, "ba_", "") self.event.ba_id = tonumber(self.event.ba_id) @@ -382,25 +410,26 @@ function ScEvent:is_valid_service() return true end ---- Wrapper method that checks common aspect of an event such as ack and state_type ---- @return (boolean) +--- Validate common aspects of an event such as acknowledgment and state type. +--- This method checks whether the event's state type, acknowledgment state, downtime state, and flapping state are valid. +--- @return (boolean) Returns `true` if all aspects of the event are valid, `false` otherwise. function ScEvent:is_valid_event_states() - -- return false if state_type (HARD/SOFT) is not valid + -- Return `false` if the state type (HARD/SOFT) is not valid. if not self:is_valid_event_state_type() then return false end - -- return false if acknowledge state is not valid + -- Return `false` if the acknowledgment state is not valid. if not self:is_valid_event_acknowledge_state() then return false end - -- return false if downtime state is not valid + -- Return `false` if the downtime state is not valid. if not self:is_valid_event_downtime_state() then return false end - -- return false if flapping state is not valid + -- Return `false` if the flapping state is not valid. if not self:is_valid_event_flapping_state() then return false end @@ -408,18 +437,21 @@ function ScEvent:is_valid_event_states() return true end ---- Check if the event has an accepted status ---- @param accepted_status_list (string) a coma separated list of accepted status ("ok,warning,critical") ---- @return (boolean) +--- Validate the event's status against a list of accepted statuses. +--- This method checks whether the event's status matches any of the statuses in the provided list. +--- Compatibility patches are applied for BBDO versions 2 and 3 to ensure proper handling of state fields. +--- @param accepted_status_list (string) A comma-separated list of accepted statuses (e.g., "ok,warning,critical"). +--- @return (boolean) Returns `true` if the event's status is valid, `false` otherwise. function ScEvent:is_valid_event_status(accepted_status_list) local status_list = self.sc_common:split(accepted_status_list, ",") - + + -- Return `false` if the accepted status list is nil or empty. if not status_list then self.sc_logger:error("[sc_event:is_valid_event_status]: accepted_status list is nil or empty") return false end - -- start compat patch bbdo2 => bbdo 3 + -- Compatibility patch for BBDO version 2 to version 3. if (not self.event.state and self.event.current_state) then self.event.state = self.event.current_state end @@ -427,32 +459,33 @@ function ScEvent:is_valid_event_status(accepted_status_list) if (not self.event.current_state and self.event.state) then self.event.current_state = self.event.state end - -- end compat patch + -- Check if the event's state matches any of the accepted statuses. for _, status_id in ipairs(status_list) do - if tostring(self.event.state) == status_id then + if tostring(self.event.state) == status_id then return true end end - -- handle downtime event specific case for logging + -- Log a warning for invalid downtime events. if (self.event.category == self.params.bbdo.categories.neb.id and self.event.element == self.params.bbdo.elements.downtime.id) then - self.sc_logger:warning("[sc_event:is_valid_event_status] event has an invalid state. Current state: " + self.sc_logger:warning("[sc_event:is_valid_event_status] event has an invalid state. Current state: " .. tostring(self.params.status_mapping[self.event.category][self.event.element][self.event.type][self.event.state]) .. ". Accepted states are: " .. tostring(accepted_status_list)) return false end - -- log for everything else - self.sc_logger:warning("[sc_event:is_valid_event_status] event has an invalid state. Current state: " + -- Log a warning for all other invalid events. + self.sc_logger:warning("[sc_event:is_valid_event_status] event has an invalid state. Current state: " .. tostring(self.params.status_mapping[self.event.category][self.event.element][self.event.state]) .. ". Accepted states are: " .. tostring(accepted_status_list)) return false end ---- Check if the state type (HARD/SOFT) is accepted ---- @return (boolean) +--- Validate the event's state type (HARD/SOFT). +--- This method checks whether the event's state type meets the configured criteria. +--- @return (boolean) Returns `true` if the state type is valid, `false` otherwise. function ScEvent:is_valid_event_state_type() if not self.sc_common:compare_numbers(self.event.state_type, self.params.hard_only, ">=") then - self.sc_logger:warning("[sc_event:is_valid_event_state_type]: event is not in an valid state type. Event state type must be above or equal to " .. tostring(self.params.hard_only) + self.sc_logger:warning("[sc_event:is_valid_event_state_type]: event is not in an valid state type. Event state type must be above or equal to " .. tostring(self.params.hard_only) .. ". Current state type: " .. tostring(self.event.state_type)) return false end @@ -460,10 +493,12 @@ function ScEvent:is_valid_event_state_type() return true end ---- Check if the acknowledge state of the event is valid ---- @return (boolean) +--- Validate the acknowledgment state of the event. +--- This method checks whether the event's acknowledgment state meets the configured criteria. +--- Compatibility patches are applied for BBDO versions 2 and 3 to ensure proper handling of acknowledgment fields. +--- @return (boolean) Returns `true` if the acknowledgment state is valid, `false` otherwise. function ScEvent:is_valid_event_acknowledge_state() - -- compat patch bbdo 3 => bbdo 2 + -- Compatibility patch for BBDO version 3 to version 2. if (not self.event.acknowledged and self.event.acknowledgement_type) then if self.event.acknowledgement_type >= 1 then self.event.acknowledged = true @@ -472,25 +507,28 @@ function ScEvent:is_valid_event_acknowledge_state() end end + -- Validate the acknowledgment state against the configured threshold. if not self.sc_common:compare_numbers(self.params.acknowledged, self.sc_common:boolean_to_number(self.event.acknowledged), ">=") then - self.sc_logger:warning("[sc_event:is_valid_event_acknowledge_state]: event is not in an valid ack state. Event ack state must be below or equal to " .. tostring(self.params.acknowledged) + self.sc_logger:warning("[sc_event:is_valid_event_acknowledge_state]: event is not in an valid ack state. Event ack state must be below or equal to " .. tostring(self.params.acknowledged) .. ". Current ack state: " .. tostring(self.sc_common:boolean_to_number(self.event.acknowledged))) return false end return true end - ---- Check if the event is in an accepted downtime state ---- @return (boolean) +--- Check if the event is in an accepted downtime state. +--- This method validates whether the event's downtime state meets the configured criteria. +--- It applies compatibility patches for BBDO versions 2 and 3 to ensure proper handling of downtime depth. +--- @return (boolean) Returns `true` if the event's downtime state is valid, `false` otherwise. function ScEvent:is_valid_event_downtime_state() - -- patch compat bbdo 3 => bbdo 2 - if (not self.event.scheduled_downtime_depth and self.event.downtime_depth) then + -- Compatibility patch for BBDO version 3 to version 2. + if (not self.event.scheduled_downtime_depth and self.event.downtime_depth) then self.event.scheduled_downtime_depth = self.event.downtime_depth end + -- Validate the downtime state against the configured threshold. if not self.sc_common:compare_numbers(self.params.in_downtime, self.event.scheduled_downtime_depth, ">=") then - self.sc_logger:warning("[sc_event:is_valid_event_downtime_state]: event is not in an valid downtime state. Event downtime state must be below or equal to " .. tostring(self.params.in_downtime) + self.sc_logger:warning("[sc_event:is_valid_event_downtime_state]: event is not in a valid downtime state. Event downtime state must be below or equal to " .. tostring(self.params.in_downtime) .. ". Current downtime state: " .. tostring(self.sc_common:boolean_to_number(self.event.scheduled_downtime_depth))) return false end @@ -498,11 +536,13 @@ function ScEvent:is_valid_event_downtime_state() return true end ---- Check if the event is in an accepted flapping state ---- @return (boolean) +--- Check if the event is in an accepted flapping state. +--- This method validates whether the event's flapping state meets the configured criteria. +--- @return (boolean) Returns `true` if the event's flapping state is valid, `false` otherwise. function ScEvent:is_valid_event_flapping_state() + -- Validate the flapping state against the configured threshold. if not self.sc_common:compare_numbers(self.params.flapping, self.sc_common:boolean_to_number(self.event.flapping), ">=") then - self.sc_logger:warning("[sc_event:is_valid_event_flapping_state]: event is not in an valid flapping state. Event flapping state must be below or equal to " .. tostring(self.params.flapping) + self.sc_logger:warning("[sc_event:is_valid_event_flapping_state]: event is not in a valid flapping state. Event flapping state must be below or equal to " .. tostring(self.params.flapping) .. ". Current flapping state: " .. tostring(self.sc_common:boolean_to_number(self.event.flapping))) return false end @@ -510,19 +550,22 @@ function ScEvent:is_valid_event_flapping_state() return true end ---- Check if the event is in an accepted hostgroup ---- @return (boolean) +--- Check if the event is in an accepted hostgroup. +--- This method validates whether the host associated with the event belongs to an accepted hostgroup. +--- It retrieves hostgroup information from the broker cache and compares it against the accepted/rejected hostgroup lists. +--- @return (boolean) Returns `true` if the host is in an accepted hostgroup, `false` otherwise. function ScEvent:is_valid_hostgroup() + -- Retrieve hostgroup information from the broker cache. self.event.cache.hostgroups = self.sc_broker:get_hostgroups(self.event.host_id) - -- return true if options are not set or if both options are set + -- Return `true` if neither accepted nor rejected hostgroup lists are configured, or if both are configured. local accepted_hostgroups_isnotempty = self.params.accepted_hostgroups ~= "" local rejected_hostgroups_isnotempty = self.params.rejected_hostgroups ~= "" if (not accepted_hostgroups_isnotempty and not rejected_hostgroups_isnotempty) or (accepted_hostgroups_isnotempty and rejected_hostgroups_isnotempty) then return true end - -- return false if no hostgroups were found + -- Return `false` if no hostgroups were found. if not self.event.cache.hostgroups then if accepted_hostgroups_isnotempty then self.sc_logger:warning("[sc_event:is_valid_hostgroup]: dropping event because host with id: " .. tostring(self.event.host_id) @@ -535,16 +578,17 @@ function ScEvent:is_valid_hostgroup() end end + -- Compare the hostgroup name against the accepted and rejected hostgroup lists. local accepted_hostgroup_name = self:find_hostgroup_in_list(self.params.accepted_hostgroups) local rejected_hostgroup_name = self:find_hostgroup_in_list(self.params.rejected_hostgroups) - -- return false if the host is not in a valid hostgroup + -- Return `false` if the host is not in a valid hostgroup. if accepted_hostgroups_isnotempty and not accepted_hostgroup_name then - self.sc_logger:warning("[sc_event:is_valid_hostgroup]: dropping event because host with id: " .. tostring(self.event.host_id) + self.sc_logger:warning("[sc_event:is_valid_hostgroup]: dropping event because host with id: " .. tostring(self.event.host_id) .. " is not in an accepted hostgroup. Accepted hostgroups are: " .. self.params.accepted_hostgroups) return false elseif rejected_hostgroups_isnotempty and rejected_hostgroup_name then - self.sc_logger:warning("[sc_event:is_valid_hostgroup]: dropping event because host with id: " .. tostring(self.event.host_id) + self.sc_logger:warning("[sc_event:is_valid_hostgroup]: dropping event because host with id: " .. tostring(self.event.host_id) .. " is in a rejected hostgroup. Rejected hostgroups are: " .. self.params.rejected_hostgroups) return false else @@ -560,13 +604,16 @@ function ScEvent:is_valid_hostgroup() return true end ---- Compare accepted hostgroups from parameters with the event hostgroups ---- @param hostgroups_list (string) a coma separated list of hostgroup name ---- @return (string|boolean) Name of the first matching hostgroup or false if no matching hostgroup has been found +--- Compare accepted hostgroups from parameters with the event hostgroups. +--- This method checks if the hostgroup associated with the event matches any of the hostgroups in the provided list. +--- @param hostgroups_list (string) A comma-separated list of hostgroup names. +--- @return (string|boolean) Returns the name of the first matching hostgroup if found, or `false` if no match is found. function ScEvent:find_hostgroup_in_list(hostgroups_list) + -- Return `false` if the hostgroup list is nil or empty. if hostgroups_list == nil or hostgroups_list == "" then return false else + -- Iterate through the hostgroup list and check for a match with the event hostgroup. for _, hostgroup_name in ipairs(self.sc_common:split(hostgroups_list, ",")) do for _, event_hostgroup in pairs(self.event.cache.hostgroups) do if hostgroup_name == event_hostgroup.group_name then @@ -578,19 +625,22 @@ function ScEvent:find_hostgroup_in_list(hostgroups_list) return false end ---- Check if the event is in an accepted servicegroup ---- @return (boolean) +--- Check if the event is in an accepted servicegroup. +--- This method validates whether the service associated with the event belongs to an accepted servicegroup. +--- It retrieves servicegroup information from the broker cache and compares it against the accepted/rejected servicegroup lists. +--- @return (boolean) Returns `true` if the service is in an accepted servicegroup, `false` otherwise. function ScEvent:is_valid_servicegroup() + -- Retrieve servicegroup information from the broker cache. self.event.cache.servicegroups = self.sc_broker:get_servicegroups(self.event.host_id, self.event.service_id) - -- return true if options are not set or if both options are set + -- Return `true` if neither accepted nor rejected servicegroup lists are configured, or if both are configured. local accepted_servicegroups_isnotempty = self.params.accepted_servicegroups ~= "" local rejected_servicegroups_isnotempty = self.params.rejected_servicegroups ~= "" if (not accepted_servicegroups_isnotempty and not rejected_servicegroups_isnotempty) or (accepted_servicegroups_isnotempty and rejected_servicegroups_isnotempty) then return true end - -- return false if no servicegroups were found + -- Return `false` if no servicegroups were found. if not self.event.cache.servicegroups then if accepted_servicegroups_isnotempty then self.sc_logger:debug("[sc_event:is_valid_servicegroup]: dropping event because service with id: " .. tostring(self.event.service_id) @@ -603,20 +653,21 @@ function ScEvent:is_valid_servicegroup() end end + -- Compare the servicegroup name against the accepted and rejected servicegroup lists. local accepted_servicegroup_name = self:find_servicegroup_in_list(self.params.accepted_servicegroups) local rejected_servicegroup_name = self:find_servicegroup_in_list(self.params.rejected_servicegroups) - -- return false if the service is not in a valid servicegroup + -- Return `false` if the service is not in a valid servicegroup. if accepted_servicegroups_isnotempty and not accepted_servicegroup_name then - self.sc_logger:debug("[sc_event:is_valid_servicegroup]: dropping event because service with id: " .. tostring(self.event.service_id) + self.sc_logger:debug("[sc_event:is_valid_servicegroup]: dropping event because service with id: " .. tostring(self.event.service_id) .. " is not in an accepted servicegroup. Accepted servicegroups are: " .. self.params.accepted_servicegroups) return false elseif rejected_servicegroups_isnotempty and rejected_servicegroup_name then - self.sc_logger:debug("[sc_event:is_valid_servicegroup]: dropping event because service with id: " .. tostring(self.event.service_id) - .. " is in an rejected servicegroup. Rejected servicegroups are: " .. self.params.rejected_servicegroups) + self.sc_logger:debug("[sc_event:is_valid_servicegroup]: dropping event because service with id: " .. tostring(self.event.service_id) + .. " is in a rejected servicegroup. Rejected servicegroups are: " .. self.params.rejected_servicegroups) return false end - + local debug_msg = "[sc_event:is_valid_servicegroup]: event for service with id: " .. tostring(self.event.service_id) if accepted_servicegroups_isnotempty then debug_msg = debug_msg .. " matched servicegroup: " .. tostring(accepted_servicegroup_name) @@ -628,78 +679,86 @@ function ScEvent:is_valid_servicegroup() return true end ---- Compare accepted servicegroups from parameters with the event servicegroups ---- @param servicegroups_list (string) a coma separated list of servicegroup name ---- @return (string|boolean) Name of the first matching servicegroup if found or false if not found +--- Compare accepted servicegroups from parameters with the event servicegroups. +--- This method checks if the servicegroup associated with the event matches any of the servicegroups in the provided list. +--- @param servicegroups_list (string) A comma-separated list of servicegroup names. +--- @return (string|boolean) Returns the name of the first matching servicegroup if found, or `false` if no match is found. function ScEvent:find_servicegroup_in_list(servicegroups_list) + -- Return `false` if the servicegroup list is nil or empty. if servicegroups_list == nil or servicegroups_list == "" then return false else + -- Iterate through the servicegroup list and check for a match with the event servicegroup. for _, servicegroup_name in ipairs(self.sc_common:split(servicegroups_list, ",")) do for _, event_servicegroup in pairs(self.event.cache.servicegroups) do if servicegroup_name == event_servicegroup.group_name then return servicegroup_name end end - end + end end return false end ---- Check if the event is an accepted bam type event ---- @return (boolean) +--- Check if the event is an accepted BAM type event. +--- This method validates whether the event is associated with a valid Business Activity Monitoring (BAM) entity. +--- It performs checks on the BA name, status, downtime state, acknowledge state, and associated Business View (BV). +--- @return (boolean) Returns `true` if the BAM event is valid, `false` otherwise. function ScEvent:is_valid_bam_event() - -- return false if ba name is invalid or ba_id is nil + -- Return false if the BA name is invalid or the BA ID is nil. if not self:is_valid_ba() then self.sc_logger:warning("[sc_event:is_valid_bam_event]: ba_id: " .. tostring(self.event.ba_id) .. " hasn't been validated") return false end - -- return false if BA status is not accepted + -- Return false if the BA status is not accepted. if not self:is_valid_ba_status_event() then self.sc_logger:warning("[sc_event:is_valid_bam_event]: ba_id: " .. tostring(self.event.ba_id) .. " has an invalid state") return false end - -- return false if BA downtime state is not accepted + -- Return false if the BA downtime state is not accepted. if not self:is_valid_ba_downtime_state() then self.sc_logger:warning("[sc_event:is_valid_bam_event]: ba_id: " .. tostring(self.event.ba_id) .. " is not in a validated downtime state") return false end - -- DO NOTHING FOR THE MOMENT + -- Return false if the BA acknowledge state is not accepted (currently does nothing). if not self:is_valid_ba_acknowledge_state() then self.sc_logger:warning("[sc_event:is_valid_bam_event]: ba_id: " .. tostring(self.event.ba_id) .. " is not in a validated acknowledge state") return false end - -- return false if BA is not in an accepted BV + -- Return false if the BA is not in an accepted BV. if not self:is_valid_bv() then self.sc_logger:warning("[sc_event:is_valid_bam_event]: ba_id: " .. tostring(self.event.ba_id) .. " is not in an accepted BV") return false end - + return true end ---- Check if ba name and/or id are valid ---- @return (boolean) +--- Check if the BA name and/or ID are valid. +--- This method validates the Business Activity (BA) entity by checking its ID and name. +--- It retrieves BA information from the broker cache and applies validation rules based on configuration parameters. +--- @return (boolean) Returns `true` if the BA is valid, `false` otherwise. function ScEvent:is_valid_ba() - - -- return false if ba_id is nil + -- Return false if the BA ID is nil and the `skip_nil_id` parameter is enabled. if (not self.event.ba_id and self.params.skip_nil_id == 1) then self.sc_logger:warning("[sc_event:is_valid_ba]: Invalid BA with id: " .. tostring(self.event.ba_id) .. ". And skip nil id is set to: " .. tostring(self.params.skip_nil_id)) return false end + -- Retrieve BA information from the broker cache. self.event.cache.ba = self.sc_broker:get_ba_infos(self.event.ba_id) - - -- return false if we can't get ba name + + -- Return false if the BA name is not found and the `skip_anon_events` parameter is enabled. if (not self.event.cache.ba.ba_name and self.params.skip_anon_events == 1) then self.sc_logger:warning("[sc_event:is_valid_ba]: Invalid BA with id: " .. tostring(self.event.ba_id) .. ". Found BA name is: " .. tostring(self.event.cache.ba.ba_name) .. ". And skip anon event param is set to: " .. tostring(self.params.skip_anon_events)) return false - elseif (not self.event.cache.ba.ba_name and self.params.skip_anon_events == 0) then + elseif (not self.event.cache.ba.ba_name and self.params.skip_anon_events == 0) then + -- Assign the BA ID as the name if the BA name is not found and anonymous events are allowed. self.event.cache.ba = { ba_name = self.event.ba_id } @@ -708,23 +767,25 @@ function ScEvent:is_valid_ba() return true end ---- Check if the ba status event is an accepted one ---- @return (boolean) +--- Check if the BA status event is an accepted one. +--- This method validates the status of a Business Activity (BA) entity against the configured accepted states. +--- @return (boolean) Returns `true` if the BA status is valid, `false` otherwise. function ScEvent:is_valid_ba_status_event() if not self:is_valid_event_status(self.params.ba_status) then - self.sc_logger:warning("[sc_event:is_valid_ba]: Invalid BA status for BA id: " .. tostring(self.event.ba_id) .. ". State is: " - .. tostring(self.params.status_mapping[self.event.category][self.event.element][self.event.state]) .. ". Acceptes states are: " .. tostring(self.params.ba_status)) + self.sc_logger:warning("[sc_event:is_valid_ba]: Invalid BA status for BA id: " .. tostring(self.event.ba_id) .. ". State is: " + .. tostring(self.params.status_mapping[self.event.category][self.event.element][self.event.state]) .. ". Accepted states are: " .. tostring(self.params.ba_status)) return false end return true end ---- Check if the ba downtime state is an accepted one ---- @return (boolean) +--- Check if the BA downtime state is an accepted one. +--- This method validates whether the Business Activity (BA) entity is in an acceptable downtime state. +--- @return (boolean) Returns `true` if the BA downtime state is valid, `false` otherwise. function ScEvent:is_valid_ba_downtime_state() if not self.sc_common:compare_numbers(self.params.in_downtime, self.sc_common:boolean_to_number(self.event.in_downtime), ">=") then - self.sc_logger:warning("[sc_event:is_valid_ba]: Invalid BA downtime state for BA id: " .. tostring(self.event.ba_id) .. " downtime state is : " .. tostring(self.event.in_downtime) + self.sc_logger:warning("[sc_event:is_valid_ba]: Invalid BA downtime state for BA id: " .. tostring(self.event.ba_id) .. " downtime state is : " .. tostring(self.event.in_downtime) .. " and accepted downtime state must be below or equal to: " .. tostring(self.params.in_downtime)) return false end @@ -732,29 +793,31 @@ function ScEvent:is_valid_ba_downtime_state() return true end ---- Check if the ba acknowledge state is an accepted one ---- @return (boolean) +--- Check if the BA acknowledge state is an accepted one. +--- This method validates whether the Business Activity (BA) entity is in an acceptable acknowledge state. +--- Currently, this method does nothing and always returns `true`. +--- @return (boolean) Returns `true`. function ScEvent:is_valid_ba_acknowledge_state() - -- if not self.sc_common:compare_numbers(self.params.in_downtime, self.event.in_downtime, '>=') then - -- return false - -- end - + -- Placeholder for future implementation. return true end ---- Check if the event is in an accepted BV ---- @return (boolean) +--- Check if the event is in an accepted Business View (BV). +--- This method validates whether the Business Activity (BA) entity is associated with an accepted BV. +--- It retrieves BV information from the broker cache and applies validation rules based on configuration parameters. +--- @return (boolean) Returns `true` if the BA is in an accepted BV, `false` otherwise. function ScEvent:is_valid_bv() + -- Retrieve BV information from the broker cache. self.event.cache.bvs = self.sc_broker:get_bvs_infos(self.event.host_id) - -- return true if options are not set or if both options are set + -- Return true if neither accepted nor rejected BV lists are configured, or if both are configured. local accepted_bvs_isnotempty = self.params.accepted_bvs ~= "" local rejected_bvs_isnotempty = self.params.rejected_bvs ~= "" if (not accepted_bvs_isnotempty and not rejected_bvs_isnotempty) or (accepted_bvs_isnotempty and rejected_bvs_isnotempty) then return true end - - -- return false if no bvs were found + + -- Return false if no BVs were found. if not self.event.cache.bvs then if accepted_bvs_isnotempty then self.sc_logger:debug("[sc_event:is_valid_bv]: dropping event because host with id: " .. tostring(self.event.host_id) @@ -767,10 +830,11 @@ function ScEvent:is_valid_bv() end end + -- Compare the BV name against the accepted and rejected BV lists. local accepted_bv_name = self:find_bv_in_list(self.params.accepted_bvs) local rejected_bv_name = self:find_bv_in_list(self.params.rejected_bvs) - -- return false if the BA is not in a valid BV + -- Return false if the BA is not in a valid BV. if accepted_bvs_isnotempty and not accepted_bv_name then self.sc_logger:debug("[sc_event:is_valid_bv]: dropping event because BA with id: " .. tostring(self.event.ba_id) .. " is not in an accepted BV. Accepted BVs are: " .. self.params.accepted_bvs) @@ -787,14 +851,16 @@ function ScEvent:is_valid_bv() return true end ---- Compare accepted BVs from parameters with the event BVs ---- @param bvs_list (string) a coma separated list of BV name ---- @return (string) Name of the first matching BV ---- @return (boolean) false if no matching BV has been found +--- Compare accepted BVs from parameters with the event BVs. +--- This method checks if the BV associated with the event matches any of the BVs in the provided list. +--- @param bvs_list (string) A comma-separated list of BV names. +--- @return (string|boolean) Returns the name of the first matching BV if found, or `false` if no match is found. function ScEvent:find_bv_in_list(bvs_list) + -- Return false if the BV list is nil or empty. if bvs_list == nil or bvs_list == "" then return false else + -- Iterate through the BV list and check for a match with the event BV. for _, bv_name in ipairs(self.sc_common:split(bvs_list,",")) do for _, event_bv in pairs(self.event.cache.bvs) do if bv_name == event_bv.bv_name then @@ -806,31 +872,34 @@ function ScEvent:find_bv_in_list(bvs_list) return false end ---- Check if the event is monitored from an accepted poller ---- @return (boolean) +--- Check if the event is monitored from an accepted poller. +--- This method validates whether the host associated with the event is monitored by an accepted poller. +--- It checks the instance ID, retrieves the poller information, and compares it against the accepted/rejected poller lists. +--- @return (boolean) Returns `true` if the host is monitored by an accepted poller, `false` otherwise. function ScEvent:is_valid_poller() - -- return false if instance id is not found in cache + -- Return false if instance ID is not found in the cache. if not self.event.cache.host.instance_id then self.sc_logger:warning("[sc_event:is_valid_poller]: no instance ID found for host ID: " .. tostring(self.event.host_id)) return false end + -- Retrieve poller information from the broker cache. self.event.cache.poller = self.sc_broker:get_instance(self.event.cache.host.instance_id) - -- required if we want to easily have access to poller name with macros {cache.instance.name} + -- Store poller information in the event cache for easy access. self.event.cache.instance = { id = self.event.cache.host.instance_id, name = self.event.cache.poller } - -- return true if options are not set or if both options are set + -- Return true if neither accepted nor rejected poller lists are configured, or if both are configured. local accepted_pollers_isnotempty = self.params.accepted_pollers ~= "" local rejected_pollers_isnotempty = self.params.rejected_pollers ~= "" if (not accepted_pollers_isnotempty and not rejected_pollers_isnotempty) or (accepted_pollers_isnotempty and rejected_pollers_isnotempty) then return true end - -- return false if no poller found in cache + -- Return false if no poller is found in the cache. if not self.event.cache.poller then if accepted_pollers_isnotempty then self.sc_logger:debug("[sc_event:is_valid_poller]: dropping event because host with id: " .. tostring(self.event.host_id) @@ -843,12 +912,13 @@ function ScEvent:is_valid_poller() end end + -- Compare the poller name against the accepted and rejected poller lists. local accepted_poller_name = self:find_poller_in_list(self.params.accepted_pollers) local rejected_poller_name = self:find_poller_in_list(self.params.rejected_pollers) - -- return false if the host is not monitored from a valid poller + -- Return false if the host is not monitored by a valid poller. if accepted_pollers_isnotempty and not accepted_poller_name then - self.sc_logger:debug("[sc_event:is_valid_poller]: dropping event because host with id: " .. tostring(self.event.host_id) + self.sc_logger:debug("[sc_event:is_valid_poller]: dropping event because host with id: " .. tostring(self.event.host_id) .. " is not linked to an accepted poller. Host is monitored from: " .. tostring(self.event.cache.poller) .. ". Accepted pollers are: " .. self.params.accepted_pollers) return false elseif rejected_pollers_isnotempty and rejected_poller_name then @@ -863,13 +933,16 @@ function ScEvent:is_valid_poller() return true end ---- Compare accepted pollers from parameters with the event poller ---- @param pollers_list (string) a coma separated list of poller name ---- @return (string|boolean) Name of the first matching poller if found or false if not found +--- Compare accepted pollers from parameters with the event poller. +--- This method checks if the poller associated with the event matches any of the pollers in the provided list. +--- @param pollers_list (string) A comma-separated list of poller names. +--- @return (string|boolean) Returns the name of the first matching poller if found, or `false` if no match is found. function ScEvent:find_poller_in_list(pollers_list) + -- Return false if the poller list is nil or empty. if pollers_list == nil or pollers_list == "" then return false else + -- Iterate through the poller list and check for a match with the event poller. for _, poller_name in ipairs(self.sc_common:split(pollers_list, ",")) do if poller_name == self.event.cache.poller then return poller_name @@ -879,27 +952,28 @@ function ScEvent:find_poller_in_list(pollers_list) return false end ---- Checks if the host severity is accepted ---- @return (boolean) +--- Checks if the host severity is accepted. +--- This method validates the severity of a host against a configured threshold. +--- It retrieves the severity from the broker cache and compares it using the specified operator. +--- @return (boolean) Returns `true` if the host severity is accepted, `false` otherwise. function ScEvent:is_valid_host_severity() - -- initiate the severity table in the cache if it doesn't exist + -- Initialize the severity table in the cache if it doesn't exist. if not self.event.cache.severity then self.event.cache.severity = {} end - -- get severity of the host from broker cache + -- Retrieve the severity of the host from the broker cache. self.event.cache.severity.host = self.sc_broker:get_severity(self.event.host_id) - -- return true if there is no severity filter + -- Return `true` if there is no severity filter configured. if self.params.host_severity_threshold == nil then return true end - - -- return false if host severity doesn't match + -- Return `false` if the host severity does not match the configured threshold. if not self.sc_common:compare_numbers(self.params.host_severity_threshold, self.event.cache.severity.host, self.params.host_severity_operator) then self.sc_logger:debug("[sc_event:is_valid_host_severity]: dropping event because host with id: " .. tostring(self.event.host_id) .. " has an invalid severity. Severity is: " - .. tostring(self.event.cache.severity.host) .. ". host_severity_threshold (" .. tostring(self.params.host_severity_threshold) .. ") is " .. self.params.host_severity_operator + .. tostring(self.event.cache.severity.host) .. ". host_severity_threshold (" .. tostring(self.params.host_severity_threshold) .. ") is " .. self.params.host_severity_operator .. " to the severity of the host (" .. tostring(self.event.cache.severity.host) .. ")") return false end @@ -907,28 +981,28 @@ function ScEvent:is_valid_host_severity() return true end ---- Checks if the service severity is accepted ---- @return (boolean) +--- Checks if the service severity is accepted. +--- This method validates the severity of a service against a configured threshold. +--- It retrieves the severity from the broker cache and compares it using the specified operator. +--- @return (boolean) Returns `true` if the service severity is accepted, `false` otherwise. function ScEvent:is_valid_service_severity() - -- initiate the severity table in the cache if it doesn't exist + -- Initialize the severity table in the cache if it doesn't exist. if not self.event.cache.severity then self.event.cache.severity = {} end - -- get severity of the host from broker cache + -- Retrieve the severity of the service from the broker cache. self.event.cache.severity.service = self.sc_broker:get_severity(self.event.host_id, self.event.service_id) - -- return true if there is no severity filter + -- Return `true` if there is no severity filter configured. if self.params.service_severity_threshold == nil then return true end - - - -- return false if service severity doesn't match + -- Return `false` if the service severity does not match the configured threshold. if not self.sc_common:compare_numbers(self.params.service_severity_threshold, self.event.cache.severity.service, self.params.service_severity_operator) then self.sc_logger:debug("[sc_event:is_valid_service_severity]: dropping event because service with id: " .. tostring(self.event.service_id) .. " has an invalid severity. Severity is: " - .. tostring(self.event.cache.severity.service) .. ". service_severity_threshold (" .. tostring(self.params.service_severity_threshold) .. ") is " .. self.params.service_severity_operator + .. tostring(self.event.cache.severity.service) .. ". service_severity_threshold (" .. tostring(self.params.service_severity_threshold) .. ") is " .. self.params.service_severity_operator .. " to the severity of the host (" .. tostring(self.event.cache.severity.service) .. ")") return false end @@ -936,165 +1010,170 @@ function ScEvent:is_valid_service_severity() return true end ---- Checks if the event is a valid acknowledge event ---- @return (boolean) +--- Checks if the event is a valid acknowledgement event. +--- This method validates whether an acknowledgement event meets the configured criteria. +--- It performs checks on the host, service, author, poller, severity, and other attributes. +--- @return (boolean) Returns `true` if the acknowledgement event is valid, `false` otherwise. function ScEvent:is_valid_acknowledgement_event() - -- return false if we can't get hostname or host id is nil + -- Return `false` if the host is invalid or the host ID is nil. if not self:is_valid_host() then self.sc_logger:warning("[sc_event:is_valid_acknowledge_event]: host_id: " .. tostring(self.event.host_id) .. " hasn't been validated") return false end - -- check if ack author is valid + -- Check if the acknowledgement author is valid. if not self:is_valid_author() then self.sc_logger:warning("[sc_event:is_valid_acknowledgement_event]: acknowledgement on host: " .. tostring(self.event.host_id) - .. "and service: " .. tostring(self.event.service_id) .. "(0 means ack is on host) is not made by a valid author. Author is: " + .. "and service: " .. tostring(self.event.service_id) .. "(0 means ack is on host) is not made by a valid author. Author is: " .. tostring(self.event.author) .. " Accepted authors are: " .. self.params.accepted_authors) return false end - - -- return false if host is not monitored from an accepted poller + + -- Return `false` if the host is not monitored by an accepted poller. if not self:is_valid_poller() then self.sc_logger:warning("[sc_event:is_valid_acknowledgement_event]: host_id: " .. tostring(self.event.host_id) .. " is not monitored from an accepted poller") return false end - -- return false if host has not an accepted severity + -- Return `false` if the host does not have an accepted severity. if not self:is_valid_host_severity() then - self.sc_logger:warning("[sc_event:is_valid_acknowledgement_event]: service id: " .. tostring(self.event.service_id) + self.sc_logger:warning("[sc_event:is_valid_acknowledgement_event]: service id: " .. tostring(self.event.service_id) .. ". host_id: " .. tostring(self.event.host_id) .. ". Host has not an accepted severity") return false end local event_status = "" - -- service_id = 0 means ack is on a host + -- If `service_id` is 0, the acknowledgement is for a host. if self.event.type == 0 then - -- use dedicated ack host status configuration or host_status configuration + -- Use the dedicated acknowledgement host status configuration or the general host status configuration. event_status = self.sc_common:ifnil_or_empty(self.params.ack_host_status, self.params.host_status) - -- return false if event status is not accepted + -- Return `false` if the event status is not accepted. if not self:is_valid_event_status(event_status) then - self.sc_logger:warning("[sc_event:is_valid_acknowledgement_event]: host_id: " .. tostring(self.event.host_id) + self.sc_logger:warning("[sc_event:is_valid_acknowledgement_event]: host_id: " .. tostring(self.event.host_id) .. " do not have a validated status. Status: " .. tostring(self.params.status_mapping[self.event.category][self.params.bbdo.elements.host_status.id][self.event.state])) return false end - -- service_id != 0 means ack is on a service - else - -- return false if we can't get service description of service id is nil + else + -- If `service_id` is not 0, the acknowledgement is for a service. + + -- Return `false` if the service description is invalid or the service ID is nil. if not self:is_valid_service() then self.sc_logger:warning("[sc_event:is_valid_acknowledgement_event]: service with id: " .. tostring(self.event.service_id) .. " hasn't been validated") return false end - -- use dedicated ack service status configuration or service_status configuration + -- Use the dedicated acknowledgement service status configuration or the general service status configuration. event_status = self.sc_common:ifnil_or_empty(self.params.ack_service_status, self.params.service_status) - -- return false if event status is not accepted + -- Return `false` if the event status is not accepted. if not self:is_valid_event_status(event_status) then - self.sc_logger:warning("[sc_event:is_valid_acknowledgement_event]: service with id: " .. tostring(self.event.service_id) + self.sc_logger:warning("[sc_event:is_valid_acknowledgement_event]: service with id: " .. tostring(self.event.service_id) .. " hasn't a validated status. Status: " .. tostring(self.params.status_mapping[self.event.category][self.params.bbdo.elements.service_status.id][self.event.state])) return false end - -- return false if service has not an accepted severity + -- Return `false` if the service does not have an accepted severity. if not self:is_valid_service_severity() then - self.sc_logger:warning("[sc_event:is_valid_acknowledgement_event]: service id: " .. tostring(self.event.service_id) + self.sc_logger:warning("[sc_event:is_valid_acknowledgement_event]: service id: " .. tostring(self.event.service_id) .. ". host_id: " .. tostring(self.event.host_id) .. ". Service has not an accepted severity") return false end - -- return false if service is not in an accepted servicegroup + -- Return `false` if the service is not in an accepted service group. if not self:is_valid_servicegroup() then self.sc_logger:warning("[sc_event:is_valid_acknowledgement_event]: service_id: " .. tostring(self.event.service_id) .. " is not in an accepted servicegroup") return false end end - -- return false if host is not in an accepted hostgroup + -- Return `false` if the host is not in an accepted host group. if not self:is_valid_hostgroup() then - self.sc_logger:warning("[sc_event:is_valid_acknowledgement_event]: service_id: " .. tostring(self.event.service_id) + self.sc_logger:warning("[sc_event:is_valid_acknowledgement_event]: service_id: " .. tostring(self.event.service_id) .. " is not in an accepted hostgroup. Host ID is: " .. tostring(self.event.host_id)) return false end - + return true end - ---- Check if the event is a valid downtime event ---- @return (boolean) +--- Check if the event is a valid downtime event. +--- This method validates whether the event represents a legitimate downtime event. +--- It performs checks on the event type, host, author, poller, and other attributes to ensure the event meets the configured criteria. +--- Host and service-specific validations are applied based on the event type. +--- @return (boolean) Returns `true` if the event is a valid downtime event, `false` otherwise. function ScEvent:is_valid_downtime_event() - -- return false if the event is one of all the "fake" start or end downtime event received from broker + -- Return false if the event is not a start or end downtime event. if not self:is_downtime_event_useless() then self.sc_logger:debug("[sc_event:is_valid_downtime_event]: dropping downtime event because it is not a start nor end of downtime event.") return false end - -- return false if we can't get hostname or host id is nil + -- Return false if the host is invalid or host ID is nil. if not self:is_valid_host() then self.sc_logger:warning("[sc_event:is_valid_downtime_event]: host_id: " .. tostring(self.event.host_id) .. " hasn't been validated") return false end - -- check if downtime author is valid + -- Return false if the downtime author is invalid. if not self:is_valid_author() then self.sc_logger:warning("[sc_event:is_valid_downtime_event]: downtime with internal ID: " .. tostring(self.event.internal_id) .. " is not made by a valid author. Author is: " .. tostring(self.event.author) .. " Accepted authors are: " .. self.params.accepted_authors) return false end - -- return false if host is not monitored from an accepted poller + -- Return false if the host is not monitored by an accepted poller. if not self:is_valid_poller() then self.sc_logger:warning("[sc_event:is_valid_downtime_event]: host_id: " .. tostring(self.event.host_id) .. " is not monitored from an accepted poller") return false end - -- this is a host event + -- Check if the event is a host event. if self.event.type == 2 then - -- store the result in the self.event.state because doing that allow us to use the is_valid_event_status method + -- Store the host downtime status in the event state for validation. self.event.state = self:get_downtime_host_status() - - -- checks if the current host downtime state is an accepted status + + -- Return false if the host downtime status is not accepted. if not self:is_valid_event_status(self.params.dt_host_status) then - self.sc_logger:warning("[sc_event:is_valid_downtime_event]: host_id: " .. tostring(self.event.host_id) + self.sc_logger:warning("[sc_event:is_valid_downtime_event]: host_id: " .. tostring(self.event.host_id) .. " do not have a validated status. Status: " .. tostring(self.params.status_mapping[self.event.category][self.event.element][self.event.type][self.event.state]) .. " Accepted states are: " .. tostring(self.params.dt_host_status)) return false end else - -- return false if we can't get service description or service id is nil + -- Return false if the service description or service ID is invalid. if not self:is_valid_service() then self.sc_logger:warning("[sc_event:is_valid_service_status_event]: service with id: " .. tostring(self.event.service_id) .. " hasn't been validated") return false end - -- store the result in the self.event.state because doing that allow us to use the is_valid_event_status method + -- Store the service downtime status in the event state for validation. self.event.state = self:get_downtime_service_status() - - -- return false if event status is not accepted + + -- Return false if the service downtime status is not accepted. if not self:is_valid_event_status(self.params.dt_service_status) then - self.sc_logger:warning("[sc_event:is_valid_downtime_event]: service with id: " .. tostring(self.event.service_id) + self.sc_logger:warning("[sc_event:is_valid_downtime_event]: service with id: " .. tostring(self.event.service_id) .. " hasn't a validated status. Status: " .. tostring(self.params.status_mapping[self.event.category][self.event.element][self.event.type][self.event.state]) .. " Accepted states are: " .. tostring(self.params.dt_service_status)) return false end - -- return false if service has not an accepted severity + -- Return false if the service severity is not accepted. if not self:is_valid_service_severity() then - self.sc_logger:warning("[sc_event:is_valid_downtime_event]: service id: " .. tostring(self.event.service_id) + self.sc_logger:warning("[sc_event:is_valid_downtime_event]: service id: " .. tostring(self.event.service_id) .. ". host_id: " .. tostring(self.event.host_id) .. ". Service has not an accepted severity") return false end - -- return false if service is not in an accepted servicegroup + -- Return false if the service is not in an accepted service group. if not self:is_valid_servicegroup() then self.sc_logger:warning("[sc_event:is_valid_downtime_event]: service_id: " .. tostring(self.event.service_id) .. " is not in an accepted servicegroup") return false end end - -- return false if host is not in an accepted hostgroup + -- Return false if the host is not in an accepted host group. if not self:is_valid_hostgroup() then - self.sc_logger:warning("[sc_event:is_valid_downtime_event]: service_id: " .. tostring(self.event.service_id) + self.sc_logger:warning("[sc_event:is_valid_downtime_event]: service_id: " .. tostring(self.event.service_id) .. " is not in an accepted hostgroup. Host ID is: " .. tostring(self.event.host_id)) return false end @@ -1102,21 +1181,23 @@ function ScEvent:is_valid_downtime_event() return true end ---- Check if the author of a comment is valid based on contact alias in Centreon ---- @return (boolean) +--- Check if the author of a comment is valid based on contact alias in Centreon. +--- This method validates the event author against accepted and rejected author lists. +--- If both lists are empty or both are populated, the author is considered valid. +--- @return (boolean) Returns `true` if the author is valid, `false` otherwise. function ScEvent:is_valid_author() - -- return true if options are not set or if both options are set + -- Return true if both accepted and rejected author lists are empty or both are populated. local accepted_authors_isnotempty = self.params.accepted_authors ~= "" local rejected_authors_isnotempty = self.params.rejected_authors ~= "" if (not accepted_authors_isnotempty and not rejected_authors_isnotempty) or (accepted_authors_isnotempty and rejected_authors_isnotempty) then return true end - -- check if author is accepted + -- Check if the author is in the accepted list. local accepted_author_name = self:find_author_in_list(self.params.accepted_authors) local rejected_author_name = self:find_author_in_list(self.params.rejected_authors) if accepted_authors_isnotempty and not accepted_author_name then - self.sc_logger:debug("[sc_event:is_valid_author]: dropping event because author: " .. tostring(self.event.author) + self.sc_logger:debug("[sc_event:is_valid_author]: dropping event because author: " .. tostring(self.event.author) .. " is not in an accepted authors list. Accepted authors are: " .. self.params.accepted_authors) return false elseif rejected_authors_isnotempty and rejected_author_name then @@ -1127,54 +1208,63 @@ function ScEvent:is_valid_author() return true end - ---- find_author_in_list: compare accepted authors from parameters with the event author ---- @param authors_list (string) a coma separated list of author name ---- @return (string|boolean) accepted_alias or false - the alias of the first matching author if found or false if not found +--- Compare accepted authors from parameters with the event author. +--- This method checks if the event's author matches any of the accepted authors provided in the list. +--- It splits the `authors_list` into individual author aliases and compares them with the event's author. +--- @param authors_list (string) A comma-separated list of author names. +--- @return (string|boolean) Returns the alias of the first matching author if found, or `false` if no match is found. function ScEvent:find_author_in_list(authors_list) + -- Return false if the authors list is nil or empty. if authors_list == nil or authors_list == "" then return false else + -- Iterate through the list of author aliases and check for a match with the event's author. for _, author_alias in ipairs(self.sc_common:split(authors_list, ",")) do if author_alias == self.event.author then return author_alias end end end + -- Return false if no matching author is found. return false end ---- Retrieve the status of a host based on last_time_up/down dates found in cache (self.event.cache.host must be set) ---- @return (number) the status code of the host +--- Retrieve the status of a host based on last_time_up/down dates found in cache. +--- This method determines the host's status by comparing the timestamps of its last known "up" and "down" states. +--- It uses the `get_most_recent_status_code` method to identify the most recent status. +--- @return (number|string) Returns the status code of the host, or "N/A" if the cache is not filled. function ScEvent:get_downtime_host_status() - -- if cache is not filled we can't get the state of the host + -- Return "N/A" if the cache does not contain the required timestamps. if not self.event.cache.host.last_time_up or not self.event.cache.host.last_time_down then return "N/A" end - -- affect the status known dates to their respective status code + -- Map the timestamps to their respective status codes. local timestamp = { [0] = tonumber(self.event.cache.host.last_time_up), [1] = tonumber(self.event.cache.host.last_time_down) } + -- Retrieve the most recent status code based on the timestamps. return self:get_most_recent_status_code(timestamp) end ---- Retrieve the status of a service based on last_time_ok/warning/critical/unknown dates found in cache (self.event.cache.host must be set) ---- @return (number) the status code of the service +--- Retrieve the status of a service based on last_time_ok/warning/critical/unknown dates found in cache. +--- This method determines the service's status by comparing the timestamps of its last known states. +--- It uses the `get_most_recent_status_code` method to identify the most recent status. +--- @return (number|string) Returns the status code of the service, or "N/A" if the cache is not filled. function ScEvent:get_downtime_service_status() - -- if cache is not filled we can't get the state of the service - if - not self.event.cache.service.last_time_ok - or not self.event.cache.service.last_time_warning - or not self.event.cache.service.last_time_critical - or not self.event.cache.service.last_time_unknown + -- Return "N/A" if the cache does not contain the required timestamps. + if + not self.event.cache.service.last_time_ok + or not self.event.cache.service.last_time_warning + or not self.event.cache.service.last_time_critical + or not self.event.cache.service.last_time_unknown then return "N/A" end - -- affect the status known dates to their respective status code + -- Map the timestamps to their respective status codes. local timestamp = { [0] = tonumber(self.event.cache.service.last_time_ok), [1] = tonumber(self.event.cache.service.last_time_warning), @@ -1182,21 +1272,24 @@ function ScEvent:get_downtime_service_status() [3] = tonumber(self.event.cache.service.last_time_unknown) } + -- Retrieve the most recent status code based on the timestamps. return self:get_most_recent_status_code(timestamp) end ---- Retrieve the last status code from a list of status and timestamp ---- @param timestamp (table) a table with the association of the last known timestamp of a status and its corresponding status code ---- @return (number) the most recent status code of the object +--- Retrieve the last status code from a list of status and timestamp. +--- This method iterates through a table of timestamps associated with status codes +--- and determines the most recent status code based on the highest timestamp value. +--- @param timestamp (table) A table where keys are status codes and values are their corresponding timestamps. +--- @return (number) The most recent status code based on the highest timestamp. function ScEvent:get_most_recent_status_code(timestamp) - -- prepare the table in wich the latest known status timestamp and status code will be stored + -- Prepare the table to store the latest known status timestamp and status code. local status_info = { highest_timestamp = 0, status = nil } - - -- compare all status timestamp and keep the most recent one and the corresponding status code + + -- Iterate through the timestamps and find the most recent status code. for status_code, status_timestamp in ipairs(timestamp) do if status_timestamp > status_info.highest_timestamp then status_info.highest_timestamp = status_timestamp @@ -1207,20 +1300,23 @@ function ScEvent:get_most_recent_status_code(timestamp) return status_info.status end ---- is_service_status_event_duplicated: check if the service event is the same than the last one (will not work for OK(H) -> CRITICAL(S) -> OK(H)) ---- @return (boolean) +--- Check if the service status event is a duplicate. +--- This method determines whether the current service status event is identical to the previous one. +--- It does not work for transitions like OK(H) -> CRITICAL(S) -> OK(H). +--- @return (boolean) Returns `true` if the event is a duplicate, `false` otherwise. function ScEvent:is_service_status_event_duplicated() - -- return false if option is not activated + -- Return false if the deduplication option is not activated. if self.params.enable_service_status_dedup ~= 1 then - self.sc_logger:debug("[sc_event:is_service_status_event_duplicated]: service status is not enabled option enable_service_status_dedup is set to: " .. tostring(self.params.enable_service_status_dedup)) + self.sc_logger:debug("[sc_event:is_service_status_event_duplicated]: Service status deduplication is not enabled. Option enable_service_status_dedup is set to: " .. tostring(self.params.enable_service_status_dedup)) return false end - -- if last check is the same than last_hard_state_change, it means the event just change its status so it cannot be a duplicated event + -- Check if the last check timestamp is the same as the last hard state change timestamp. + -- If true, the event is not a duplicate. if self.event.last_hard_state_change == self.event.last_check or self.event.last_hard_state_change == self.event.last_update then return false end - + return true --[[ IT LOOKS LIKE THIS PIECE OF CODE IS USELESS @@ -1240,27 +1336,30 @@ function ScEvent:is_service_status_event_duplicated() return false end end - -- at the end, it only remains two cases, the first one is a duplicated event. The second one is when we have: -- OK(H) --> NOT-OK(S) --> OK(H) ]]-- end ---- is_host_status_event_duplicated: check if the host event is the same than the last one (will not work for UP(H) -> DOWN(S) -> UP(H)) ---- @return (boolean) +--- Check if the host status event is a duplicate. +--- This method determines whether the current host status event is identical to the previous one. +--- It does not work for transitions like UP(H) -> DOWN(S) -> UP(H). +--- @return boolean Returns `true` if the event is a duplicate, `false` otherwise. function ScEvent:is_host_status_event_duplicated() - -- return false if option is not activated + -- Return false if the deduplication option is not activated. if self.params.enable_host_status_dedup ~= 1 then - self.sc_logger:debug("[sc_event:is_host_status_event_duplicated]: host status is not enabled option enable_host_status_dedup is set to: " .. tostring(self.params.enable_host_status_dedup)) + self.sc_logger:debug("[sc_event:is_host_status_event_duplicated]: host status deduplication is not enabled. Option enable_host_status_dedup is set to: " .. tostring(self.params.enable_host_status_dedup)) return false end - -- if last check is the same than last_hard_state_change (allowing a delta timestamp), it means the event just change its status so it cannot be a duplicated event + -- Check if the last check timestamp is the same as the last hard state change timestamp, allowing for a delta. + -- If true, the event is not a duplicate. if math.abs(self.event.last_hard_state_change - self.event.last_check) <= self.params.delta_host_status_change_allow or (self.event.last_update ~= nil and math.abs(self.event.last_hard_state_change - self.event.last_update) <= self.params.delta_host_status_change_allow) then return false end + -- If none of the above conditions are met, the event is considered a duplicate. return true --[[ IT LOOKS LIKE THIS PIECE OF CODE IS USELESS @@ -1278,48 +1377,51 @@ function ScEvent:is_host_status_event_duplicated() return false end end - -- at the end, it only remains two cases, the first one is a duplicated event. The second one is when we have: -- UP(H) --> NOT-UP(S) --> UP(H) ]]-- end - ---- The purpose of this method is to filter out unnecessary downtime event. It appears that broker ---- is sending many downtime events before sending the one we want ---- @return (boolean) +--- Filter out unnecessary downtime events. +--- This method checks whether a downtime event is valid and necessary. +--- It ensures that only start or end downtime events are processed. +--- @return boolean Returns `true` if the downtime event is valid, `false` otherwise. function ScEvent:is_downtime_event_useless() - -- return false if downtime event is not a valid start of downtime event + -- Return true if the downtime event is a valid start of downtime event. if self:is_valid_downtime_event_start() then return true end - - -- return false if downtime event is not a valid end of downtime event + + -- Return true if the downtime event is a valid end of downtime event. if self:is_valid_downtime_event_end() then return true end + -- If neither condition is met, the downtime event is considered unnecessary. return false end ---- Make sure that the event is the one notifying us that a downtime has just started ---- @return (boolean) +--- Make sure that the event is the one notifying us that a downtime has just started. +--- This method checks the `actual_end_time` and `actual_start_time` fields of the event to determine if it represents the start of a downtime. +--- It also applies compatibility patches for BBDO versions 2 and 3 to ensure proper handling of event IDs. +--- @return boolean Returns `true` if the event is a valid downtime start event, `false` otherwise. function ScEvent:is_valid_downtime_event_start() - -- event is about the end of the downtime (actual_end_time key is not present in a start downtime bbdo2 event) - -- with bbdo3 value is set to -1 + -- Check if the event is about the end of the downtime. + -- For BBDO version 3, `actual_end_time` should be -1. For BBDO version 2, it should not exist. if (self.bbdo_version > 2 and self.event.actual_end_time ~= -1) or (self.bbdo_version == 2 and self.event.actual_end_time) then self.sc_logger:debug("[sc_event:is_valid_downtime_event_start]: actual_end_time found in the downtime event and value equal to -1 or bbdo v2 in use. It can't be a downtime start event") return false end - -- event hasn't actually started until the actual_start_time key is present in the start downtime bbdo 2 event - -- with bbdo3 donwtime is not started until value is a valid timestamp + -- Check if the event has actually started. + -- For BBDO version 2, `actual_start_time` must exist. For BBDO version 3, it must be a valid timestamp. if (not self.event.actual_start_time and self.bbdo_version == 2) or (self.event.actual_start_time == -1 and self.bbdo_version > 2) then self.sc_logger:debug("[sc_event:is_valid_downtime_event_start]: actual_start_time not found in the downtime event (or value set to -1). The downtime hasn't yet started") return false end - -- start compat patch bbdo2 => bbdo 3 + -- Compatibility patch for BBDO versions 2 and 3. + -- Ensure `internal_id` and `id` fields are properly set. if (not self.event.internal_id and self.event.id) then self.event.internal_id = self.event.id end @@ -1327,17 +1429,20 @@ function ScEvent:is_valid_downtime_event_start() if (not self.event.id and self.event.internal_id) then self.event.id = self.event.internal_id end - -- end compat patch return true end ---- Make sure that the event is the one notifying us that a downtime has just ended ---- @return (boolean) +--- Make sure that the event is the one notifying us that a downtime has just ended. +--- This method checks the `deletion_time` field of the event to determine if it represents the end of a downtime. +--- It also applies compatibility patches for BBDO versions 2 and 3 to ensure proper handling of event IDs. +--- @return boolean Returns `true` if the event is a valid downtime end event, `false` otherwise. function ScEvent:is_valid_downtime_event_end() - -- event is about the end of the downtime (deletion_time key is only present in a end downtime event) + -- Check if the event is about the end of the downtime. + -- For BBDO version 2, `deletion_time` must exist. For BBDO version 3, it must not be -1. if (self.bbdo_version == 2 and self.event.deletion_time) or (self.bbdo_version > 2 and self.event.deletion_time ~= -1) then - -- start compat patch bbdo2 => bbdo 3 + -- Compatibility patch for BBDO versions 2 and 3. + -- Ensure `internal_id` and `id` fields are properly set. if (not self.event.internal_id and self.event.id) then self.event.internal_id = self.event.id end @@ -1345,17 +1450,18 @@ function ScEvent:is_valid_downtime_event_end() if (not self.event.id and self.event.internal_id) then self.event.id = self.event.internal_id end - -- end compat patch return true end - - -- any other downtime event is not about the actual end of a downtime so we return false + + -- Any other downtime event is not about the actual end of a downtime. self.sc_logger:debug("[sc_event:is_valid_downtime_event_end]: deletion_time not found in the downtime event or equal to -1. The downtime event is not about the end of a downtime") return false end - ---- Adds short_output and long_output entries in the event table. output entry will be equal to one or another depending on the use_longoutput param +--- Adds short_output and long_output entries in the event table. +--- This method processes the `output` field of the event table to generate `short_output` and `long_output` entries. +--- Depending on the configuration parameters, it modifies the `output` field to use either the short or long output, +--- replaces line breaks, or truncates the output to a specified size limit. --- @return void function ScEvent:build_outputs() -- build long output @@ -1389,10 +1495,13 @@ function ScEvent:build_outputs() end ---- **DEPRECATED METHOD** use NEB category to get metric data instead ---- @return (boolean) Returns always true +--- **DEPRECATED METHOD** +--- This method is deprecated and should not be used. It always returns `true`. +--- Use the NEB category to retrieve metric data instead. +--- @return boolean Always returns `true`. function ScEvent:is_valid_storage_event() return true end -return sc_event \ No newline at end of file +return sc_event + From dad573eec61d8093560c6a9c9c0d65764892f52d Mon Sep 17 00:00:00 2001 From: omercier Date: Fri, 27 Jun 2025 17:25:12 +0200 Subject: [PATCH 27/27] enh prometheus events sc --- .../prometheus-pushgateway-events-apiv2.lua | 121 +++++++++++++----- 1 file changed, 86 insertions(+), 35 deletions(-) diff --git a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua index 27c3af32..eb3ec0b2 100644 --- a/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua +++ b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua @@ -12,9 +12,7 @@ local sc_params = require("centreon-stream-connectors-lib.sc_params") local sc_macros = require("centreon-stream-connectors-lib.sc_macros") local sc_flush = require("centreon-stream-connectors-lib.sc_flush") --- event_queue class - ---- @class event_queue Class that handles all the actions of the stream connector +--- @class event_queue Handles all the actions of the stream connector local event_queue = {} event_queue.__index = event_queue @@ -24,8 +22,7 @@ event_queue.__index = event_queue function event_queue.new(params) local self = {} - local mandatory_parameters = { - } + local mandatory_parameters = {} self.fail = false @@ -44,6 +41,7 @@ function event_queue.new(params) self.fail = true end + -- force max_buffer_size to 1 because we each service is sent to its own url params.max_buffer_size = 1 -- overriding default parameters for this stream connector if the default values doesn't suit the basic needs @@ -55,11 +53,14 @@ function event_queue.new(params) self.sc_params.params.enable_service_status_dedup = params.enable_service_status_dedup or 1 -- prometheus specific parameters - self.sc_params.params.prometheus_gateway_url = params.prometheus_gateway_url or "http://127.0.0.1:9091" - self.sc_params.params.http_timeout = params.http_timeout or 30 - self.sc_params.params.prometheus_gateway_job = params.prometheus_gateway_job or "monitoring" - self.sc_params.params.add_hostgroups = params.add_hostgroups or 0 - -- force max_buffer_size to 1 because we each service is sent to its own url + self.sc_params.params.prometheus_gateway_url = params.prometheus_gateway_url or "http://127.0.0.1:9091" + self.sc_params.params.http_timeout = params.http_timeout or 30 + self.sc_params.params.prometheus_gateway_job = params.prometheus_gateway_job or "monitoring" + self.sc_params.params.add_hostgroups = params.add_hostgroups or 0 + self.sc_params.params.prometheus_metrics_prefix = params.prometheus_metrics_prefix or "centreon_" + self.sc_params.params.prometheus_username = params.prometheus_username or "" + self.sc_params.params.prometheus_password = params.prometheus_password or "" + self.sc_params.params.send_mixed_events = 1 -- apply users params and check syntax of standard ones self.sc_params:param_override(params) @@ -78,11 +79,22 @@ function event_queue.new(params) local categories = self.sc_params.params.bbdo.categories local elements = self.sc_params.params.bbdo.elements + local headers = { ["content-type"] = "application/openmetrics-text" } + if self.sc_params.params.prometheus_username ~= "" and self.sc_params.params.prometheus_password ~= "" then + headers["Authorization"] = "Basic " .. mime.b64(self.sc_params.params.prometheus_username .. ":" .. self.sc_params.params.prometheus_password) + end + + -- case when send_mixed_events == 0 + self.sc_flush:add_queue_metadata(categories.neb.id, elements.host_status.id, {headers = headers}) + self.sc_flush:add_queue_metadata(categories.neb.id, elements.service_status.id, {headers = headers}) + -- case when send_mixed_events == 1 + self.sc_flush.queues.global_queues_metadata.headers = headers self.format_event = { [categories.neb.id] = { [elements.host_status.id] = function () return self:format_event_host() end, - [elements.service_status.id] = function () return self:format_event_service() end + [elements.service_status.id] = function () return self:format_event_service() end, + [elements.acknowledgement.id] = function () return self:format_event_acknowledgement() end }, [categories.bam.id] = {} } @@ -143,14 +155,19 @@ function event_queue:format_event_host() local hname = event.cache.host.name local sdesc = "host" - local name = 'monitoring_status' + local name = self.sc_params.params.prometheus_metrics_prefix .. 'status' + local hostgroups_label = false + -- handle hostgroups + if self.sc_params.params.add_hostgroups == 1 then + hostgroups_label = self:display_hostgroups() + end local data = '# TYPE ' .. name .. ' counter\n' - data = data .. '# HELP ' .. name .. ' 0 is OK, 1 or higher is DOWN\n' - if not event.hostgroups_label then - data = data .. name .. '{label="monitoring_status", host="' .. hname .. '", service="' .. sdesc .. '"} ' .. event.state .. '\n' + data = data .. '# HELP ' .. name .. ' 0 is UP, 1 or higher is DOWN\n' + if not hostgroups_label then + data = data .. name .. '{label="' .. self.sc_params.params.prometheus_metrics_prefix .. '"status", host="' .. hname .. '", service="' .. sdesc .. '"} ' .. event.state .. '\n' else - data = data .. name .. '{label="monitoring_status", host="' .. hname .. '", service="' .. sdesc .. '", ' .. event.hostgroups_label .. '} ' .. event.state .. '\n' + data = data .. name .. '{label="' .. self.sc_params.params.prometheus_metrics_prefix .. '"status", host="' .. hname .. '", service="' .. sdesc .. '", ' .. hostgroups_label .. '} ' .. event.state .. '\n' end event.formated_event = { @@ -166,12 +183,6 @@ function event_queue:format_event_host() output = event.output, formatted_payload = data } - -- handle hostgroups - if self.sc_params.params.add_hostgroups == 1 then - event.formated_event.hostgroups_label = self:display_hostgroups() - else - event.formated_event.hostgroups_label = false - end end --- Prepares the self.sc_event.event.formated_event object @@ -183,14 +194,19 @@ function event_queue:format_event_service() local hname = event.cache.host.name local sdesc = event.cache.service.description - local name = 'monitoring_status' + local name = self.sc_params.params.prometheus_metrics_prefix .. 'status' + local hostgroups_label = false + -- handle hostgroups + if self.sc_params.params.add_hostgroups == 1 then + hostgroups_label = self:display_hostgroups() + end local data = '# TYPE ' .. name .. ' counter\n' data = data .. '# HELP ' .. name .. ' 0 is OK, 1 is WARNING, 2 is CRITICAL, 3 or higher is UNKNOWN\n' - if not event.hostgroups_label then - data = data .. name .. '{label="monitoring_status", host="' .. hname .. '", service="' .. sdesc .. '"} ' .. event.state .. '\n' + if not hostgroups_label then + data = data .. name .. '{label="' .. self.sc_params.params.prometheus_metrics_prefix .. '"status", host="' .. hname .. '", service="' .. sdesc .. '"} ' .. event.state .. '\n' else - data = data .. name .. '{label="monitoring_status", host="' .. hname .. '", service="' .. sdesc .. '", ' .. event.hostgroups_label .. '} ' .. event.state .. '\n' + data = data .. name .. '{label="' .. self.sc_params.params.prometheus_metrics_prefix .. '"status", host="' .. hname .. '", service="' .. sdesc .. '", ' .. hostgroups_label .. '} ' .. event.state .. '\n' end event.formated_event = { @@ -206,13 +222,49 @@ function event_queue:format_event_service() output = event.output, formatted_payload = data } +end +--- Prepares the self.sc_event.event.formated_event object +--- @return void +function event_queue:format_event_acknowledgement() + self.sc_logger:debug("[event_queue:format_event_acknowledgement]: starting to format acknowledgement event.") + + local event = self.sc_event.event + local hname = event.cache.host.name + local type = "host" + local sdesc = "host" + if event.cache.service and event.cache.service.description and event.cache.service.description ~= "" then + type = "service" + sdesc = event.cache.service.description + end + local name = self.sc_params.params.prometheus_metrics_prefix .. type .. '_ack' + local hostgroups_label = false -- handle hostgroups if self.sc_params.params.add_hostgroups == 1 then - event.formated_event.hostgroups_label = self:display_hostgroups() + hostgroups_label = self:display_hostgroups() + end + + local data = '# TYPE ' .. name .. ' gauge\n' + data = data .. '# HELP ' .. name .. ' 0 is unacknowledged, 1 is acknowledged\n' + if not hostgroups_label then + data = data .. name .. '{label="' .. self.sc_params.params.prometheus_metrics_prefix .. '"status", host="' .. hname .. '", service="' .. sdesc .. '"} ' .. 1 .. '\n' else - event.formated_event.hostgroups_label = false + data = data .. name .. '{label="' .. self.sc_params.params.prometheus_metrics_prefix .. '"status", host="' .. hname .. '", service="' .. sdesc .. '", ' .. hostgroups_label .. '} ' .. 1 .. '\n' end + + event.formated_event = { + event_type = "service", + prom_hname = event.cache.host.name, + prom_hname_url = mime.b64(event.cache.host.name), + prom_sdesc = sdesc, + prom_sdesc_url = mime.b64(sdesc), + hostname = hname, + service_description = sdesc, + timestamp = event.entry_time, + output = event.comment_data, + long_output = event.comment_data, + formatted_payload = data + } end --- Replace unwanted characters in order to comply with the open metrics format @@ -235,21 +287,21 @@ function event_queue:display_hostgroups () return false end - local hostgroups_label = 'hostgroup="' + local hostgroups_names = 'hostgroup="' local counter = 0 for i, v in pairs(self.sc_event.event.cache.hostgroups) do if counter == 0 then - hostgroups_label = hostgroups_label .. v.group_name + hostgroups_names = hostgroups_names .. v.group_name counter = 1 else - hostgroups_label = hostgroups_label .. ',' .. v.group_name + hostgroups_names = hostgroups_names .. ',' .. v.group_name end end - hostgroups_label = hostgroups_label .. '"' + hostgroups_names = hostgroups_names .. '"' - self.sc_logger:debug("[display_hostgroups]: hostgroup string composed: '" .. hostgroups_label .. "'") - return hostgroups_label + self.sc_logger:debug("[display_hostgroups]: hostgroup string composed: '" .. hostgroups_names .. "'") + return hostgroups_names end @@ -296,7 +348,6 @@ function event_queue:send_data(payload, queue_metadata) local label = "status" local url = self.sc_params.params.prometheus_gateway_url .. '/metrics/job/' .. self.sc_params.params.prometheus_gateway_job .. '/instance@base64/' .. payload.prom_hname_url .. '/service@base64/' .. payload.prom_sdesc_url - queue_metadata.headers = { "content-type: application/openmetrics-text" } local http_request = curl.easy() :setopt_url(url)