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..eb3ec0b2 --- /dev/null +++ b/centreon-certified/prometheus/prometheus-pushgateway-events-apiv2.lua @@ -0,0 +1,491 @@ +#!/usr/bin/lua +-- Centreon Broker Splunk Connector Events + +-- Libraries +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") +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") + +--- @class event_queue Handles all the actions of the stream connector +local event_queue = {} +event_queue.__index = 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 = {} + + 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 + + -- 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 + 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_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) + 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("[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() + 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 + 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.acknowledgement.id] = function () return self:format_event_acknowledgement() 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 + } + + -- 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 +end + +--- 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 + local template = self.sc_params.params.format_template[category][element] + 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 + 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("[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.") + + local event = self.sc_event.event + local hname = event.cache.host.name + local sdesc = "host" + + 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 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="' .. self.sc_params.params.prometheus_metrics_prefix .. '"status", host="' .. hname .. '", service="' .. sdesc .. '", ' .. hostgroups_label .. '} ' .. event.state .. '\n' + end + + 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, + state_type = event.state_type, + hostname = hname, + service_description = sdesc, + output = event.output, + formatted_payload = data + } +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.") + + local event = self.sc_event.event + local hname = event.cache.host.name + local sdesc = event.cache.service.description + + 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 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="' .. self.sc_params.params.prometheus_metrics_prefix .. '"status", host="' .. hname .. '", service="' .. sdesc .. '", ' .. hostgroups_label .. '} ' .. event.state .. '\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), + state = event.state, + state_type = event.state_type, + hostname = hname, + service_description = sdesc, + 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 + 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 + 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 +--- @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 or #self.sc_event.event.cache.hostgroups == 0 then + self.sc_logger:debug("[display_hostgroups]: no hostgroups, exiting") + return false + end + + local hostgroups_names = 'hostgroup="' + local counter = 0 + + for i, v in pairs(self.sc_event.event.cache.hostgroups) do + if counter == 0 then + hostgroups_names = hostgroups_names .. v.group_name + counter = 1 + else + hostgroups_names = hostgroups_names .. ',' .. v.group_name + end + end + hostgroups_names = hostgroups_names .. '"' + + self.sc_logger:debug("[display_hostgroups]: hostgroup string composed: '" .. hostgroups_names .. "'") + return hostgroups_names +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 + local element = self.sc_event.event.element + + 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("[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("[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 + +--- 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 + else + self.sc_logger:error("[event_queue:build_payload]: payload should be nil at this point.") + table.insert(payload, event) + end + + 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_response_body = "" + 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 + + + local http_request = curl.easy() + :setopt_url(url) + :setopt_writefunction( + function (response) + http_response_body = http_response_body .. tostring(response) + end + ) + :setopt(curl.OPT_TIMEOUT, self.sc_params.params.http_timeout) + :setopt( + curl.OPT_HTTPHEADER, + queue_metadata.headers + ) + + -- 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 + 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 + end + + -- set proxy user configuration + if (self.sc_params.params.proxy_username ~= '') then + if (self.sc_params.params.proxy_password ~= '') then + 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 + 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 + 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 + http_request:perform() + + -- collecting results + local http_response_code = http_request:getinfo(curl.INFO_RESPONSE_CODE) + + http_request:close() + + -- Handling the return code + local retval = false + 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 " .. 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("[event_queue:send_data]: End") + + return retval +end + +-- global stream connector object +local queue + +-- 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 + +--- 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") + 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 + 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 + +--- 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() + + -- nothing to flush + if queues_size == 0 then + return true + end + + -- flush all queues because last global flush is too old + -- 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 + queue.send_data_sleep_counter:sleep() + end + + -- there are events in the queue but they were not ready to be send + return false +end 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..879d69ec --- /dev/null +++ b/centreon-certified/prometheus/prometheus-pushgateway-metrics-apiv2.lua @@ -0,0 +1,645 @@ +#!/usr/bin/lua +-- Centreon Broker Datadog Connector Events + +-- Libraries +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") +local sc_broker = require("centreon-stream-connectors-lib.sc_broker") +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 + +--- 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', + B = 'bytes', + g = 'grams', + V = 'volts', + A = 'amperes', + K = 'kelvins', + ["%"] = 'ratios', + ["°"] = 'celsius', + ["€"] = 'euros' + } + + if unit == nil or unit == '' or type(unit) ~= 'string' then + unit = '' + end + + if unit_mapping[unit] then + unit = unit_mapping[unit] + end + + return unit +end + +--- @class event_queue Class that handles all the actions of the stream connector +local event_queue = {} +event_queue.__index = 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 = {} + + 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 "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_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 0 + 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) + 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("[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() + 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 event_queue object + setmetatable(self, { __index = event_queue }) + return self +end + +--- 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 + + 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 + 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("[event_queue:format_accepted_event]: event formatting is finished") +end + +--- 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 + + -- 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("[event_queue:format_event_host]: call build_metric ") + self.sc_metrics:build_metric(self.format_metric[event.category][event.element]) +end + +--- 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 + + 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("[event_queue:format_event_service]: call build_metric ") + self.sc_metrics:build_metric(self.format_metric[event.category][event.element]) + self.sc_logger:debug("[event_queue:format_event_service]: format metric service is finished ") +end + +--- 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 + local sdesc = "host" + + 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) + } + + -- 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 + +--- 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 + local sdesc = event.cache.service.description + + 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) + } + + -- 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 + +--- 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 = '' + + if (unit ~= '' and unit ~= nil) then + data = '# UNIT ' .. name .. '\n' + end + + return data +end + +--- 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) +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 + + if (self.sc_params.params.enable_extended_metric_name == 0) then + name = label + else + name = hname .. '_' .. sdesc .. ':' .. label + end + if (unit ~= '') then + 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 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 +--- @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 + local type = self:get_metric_type(metric) + local unit = get_unit_full_name(metric.uom) + 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 + 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_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' + 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 + data = data .. ', ' .. event.formated_event.hostgroups_label + end + + data = data .. '} ' .. metric.value .. '\n' + + 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("[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 true if it is actually a number, false otherwise +local function is_number_and_not_a_nan (number) + if (number ~= number) then + return false + end + + if (type(number) ~= "number") then + return false + end + + return true +end + +--- 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) or (perfdata.uom and perfdata.uom == '%')) then + return "gauge" + end + + return "counter" +end + +--- 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 or #self.sc_event.event.cache.hostgroups == 0 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 + local element = self.sc_event.event.element + + 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("[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("[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 + +--- 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 + payload = event + else + table.insert(payload, event) + end + + 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_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) + :setopt_writefunction( + function (response) + http_response_body = http_response_body .. tostring(response) + end + ) + :setopt(curl.OPT_TIMEOUT, self.sc_params.params.http_timeout) + :setopt( + curl.OPT_HTTPHEADER, + queue_metadata.headers + ) + + -- 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 + 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 + end + + -- set proxy user configuration + if (self.sc_params.params.proxy_username ~= '') then + if (self.sc_params.params.proxy_password ~= '') then + 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 + 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(http_post_data)) + return true + end + + -- adding the HTTP POST data + 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, http_post_data) + + -- performing the HTTP request + http_request:perform() + + -- collecting results + local http_response_code = http_request:getinfo(curl.INFO_RESPONSE_CODE) + + http_request:close() + + -- Handling the return code + local retval = false + 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 " .. 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 + +-- Global stream connector object +local queue + +--- 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 + +--- 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 + 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 + +--- 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() + + -- nothing to flush + if queues_size == 0 then + return true + end + + -- flush all queues because last global flush is too old + -- 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 + queue.send_data_sleep_counter:sleep() + end + + -- there are events in the queue but they were not ready to be send + return false +end + diff --git a/modules/centreon-stream-connectors-lib/sc_broker.lua b/modules/centreon-stream-connectors-lib/sc_broker.lua index 2d659f60..e6585424 100644 --- a/modules/centreon-stream-connectors-lib/sc_broker.lua +++ b/modules/centreon-stream-connectors-lib/sc_broker.lua @@ -1,9 +1,8 @@ #!/usr/bin/lua ---- --- Module with Centreon broker related methods for easier usage --- @module sc_broker --- @alias sc_broker +--- 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 = {} @@ -25,34 +24,35 @@ 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 +--- 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 ---- 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 +--- 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 @@ -73,45 +73,49 @@ 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 +--- 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 @@ -123,48 +127,50 @@ 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 +--- 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 @@ -176,10 +182,12 @@ 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 +--- 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 @@ -198,11 +206,37 @@ 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 +--- 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 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 + 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 +--- 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 @@ -221,11 +255,14 @@ 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 +--- 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 @@ -261,10 +298,12 @@ 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 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 @@ -284,10 +323,12 @@ 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 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 @@ -307,10 +348,12 @@ 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 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 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 diff --git a/modules/centreon-stream-connectors-lib/sc_event.lua b/modules/centreon-stream-connectors-lib/sc_event.lua index 807f39b3..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 ---- 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. +--- 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 ---- is_valid_element: check if the event is an accepted element --- @return true|false (boolean) +--- 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 ---- 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 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 ---- 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. +--- 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 ---- 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. +--- 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 ---- 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. +--- 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 ---- 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. +--- 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 ---- is_valid_host: check if host name and/or id are valid --- @return true|false (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 ---- is_valid_service: check if service description and/or id are valid --- @return true|false (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 ---- is_valid_event_states: wrapper method that checks common aspect of an event such as ack and state_type --- @return true|false (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 ---- 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) +--- 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 ---- is_valid_event_state_type: check if the state type (HARD/SOFT) is accepted --- @return true|false (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 ---- is_valid_event_acknowledge_state: check if the acknowledge state of the event is valid --- @return true|false (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 - ---- 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. +--- 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 ---- 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. +--- 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 ---- 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. +--- 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,14 +604,16 @@ 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. +--- 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 @@ -579,19 +625,22 @@ 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. +--- 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) @@ -604,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) @@ -629,78 +679,86 @@ 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. +--- 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 ---- 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. +--- 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 ---- is_valid_ba: check if ba name and/or id are valid --- @return true|false (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 } @@ -709,23 +767,25 @@ 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. +--- 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 ---- 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. +--- 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 @@ -733,29 +793,31 @@ 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. +--- 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 ---- is_valid_bv: check if the event is in an accepted BV --- @return true|false (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) @@ -768,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) @@ -788,14 +851,16 @@ 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. +--- 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 @@ -807,31 +872,34 @@ 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. +--- 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) @@ -844,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 @@ -864,13 +933,16 @@ 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. +--- 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 @@ -880,27 +952,28 @@ 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. +--- 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 @@ -908,28 +981,28 @@ 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. +--- 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 @@ -937,165 +1010,170 @@ 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 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 host status configuration or host_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 - ---- 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. +--- 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 accpeted 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 @@ -1103,21 +1181,23 @@ 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. +--- 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 @@ -1128,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 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 +--- 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 ---- 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. +--- 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 ---- 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. +--- 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), @@ -1183,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 ---- 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. +--- 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 @@ -1208,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 true|false (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 @@ -1241,26 +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 true|false (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 - 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 + -- 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 - ---- 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) +--- 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 ---- 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. +--- 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 ---- 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. +--- 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,19 @@ 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 - ---- 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. +--- 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 if self.event.long_output and self.event.long_output ~= "" then @@ -1388,10 +1495,13 @@ function ScEvent:build_outputs() end ---- is_valid_storage: DEPRECATED method, use NEB category to get metric data instead --- @return true (boolean) +--- **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 + 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 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..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 @@ -1011,6 +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") + + 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) @@ -1242,4 +1247,4 @@ function ScParams:build_and_validate_filters_pattern(param_list) end end -return sc_params \ No newline at end of file +return sc_params