From 8219bbf0e7835bf97ffc2e705c84cf8954ed1f35 Mon Sep 17 00:00:00 2001 From: tanguyvda Date: Wed, 5 Mar 2025 11:49:06 +0100 Subject: [PATCH 1/9] add virtual queue mecanism and doc --- .../sc_flush.lua | 46 +++++++++++++++ modules/docs/README.md | 55 ++++++++--------- modules/docs/sc_flush.md | 59 +++++++++++++++++++ 3 files changed, 133 insertions(+), 27 deletions(-) diff --git a/modules/centreon-stream-connectors-lib/sc_flush.lua b/modules/centreon-stream-connectors-lib/sc_flush.lua index 71ab72d3..07d5c865 100644 --- a/modules/centreon-stream-connectors-lib/sc_flush.lua +++ b/modules/centreon-stream-connectors-lib/sc_flush.lua @@ -53,6 +53,52 @@ function sc_flush.new(params, logger) return self end +-- create_new_virtual_queue: this will create a virtual queue. Normally queues are created for accepted elements from the known BBDO elements. You can extend this mecanism with virtual queues +-- @param category_id (number) the ID of the bbdo category to which you want your virtual queue to be associated +-- @param virtual_element_id (number) a virtual element ID. It must not be an already existing BBDO element ID in your BBDO category +-- @param virtual_element_name (string) a name for your virtual element +-- @return true|false (boolean) true if the virtual queue has been created, false otherwise +function ScFlush:create_new_virtual_queue(category_id, virtual_element_id, virtual_element_name) + if self.params.reverse_element_mapping[category_id][virtual_element_id] then + self.sc_logger:error("[sc_flush:create_new_queue]: You are trying to create a new virtual queue using an already existing element_id." + .. " element_id used is: " .. tostring(virtual_element_id) .. ", with category_id: " .. tostring(category_id) + .. ". This is already used by element: " .. tostring(self.params.reverse_element_mapping[category_id][virtual_element_id]) + .. ". You should change the element_id used for your virtual queue") + return false + end + + if not virtual_element_name then + self.sc_logger:error("[sc_flush:create_new_queue]: No element name provided for your virtual queue") + return false + end + + -- add a prefix for the element name to have it is easily identified as being a virtual queue + virtual_element_name = "virtual_" .. virtual_element_name + + -- create queue + self.queues[category_id][virtual_element_id] = { + events = {}, + queue_metadata = { + category_id = category_id, + element_id = virtual_element_id + } + } + + -- need to add this virtual element to the list of accepted elements. This is not for filter purpose. + -- This is because we only flush queues from accepted elements + self.params.accepted_elements_info[virtual_element_name] = { + category_id = category_id, + category_name = self.params.reverse_category_mapping[category_id], + element_id = virtual_element_id, + element_name = virtual_element_name + } + + self.sc_logger:debug("[sc_flush:create_new_virtual_queue]: created new virtual queue: " .. tostring(virtual_element_name) + .. " for category: " .. self.params.reverse_category_mapping[category_id] .. " with virtual element id: " .. tostring(virtual_element_id)) + + return true +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 diff --git a/modules/docs/README.md b/modules/docs/README.md index 65eae402..6ff21d70 100644 --- a/modules/docs/README.md +++ b/modules/docs/README.md @@ -31,25 +31,25 @@ ## sc_common methods -| Method name | Method description | Link | -| ---------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | -| ifnil_or_empty | check if a variable is empty or nil and replace it with a default value if it is the case | [Documentation](sc_common.md#ifnil_or_empty-method) | -| if_wrong_type | check the type of a variable, if it is wrong, replace the variable with a default value | [Documentation](sc_common.md#if_wrong_type-method) | -| boolean_to_number | change a true/false boolean to a 1/0 value | [Documentation](sc_common.md#boolean_to_number-method) | -| number_to_boolean | change a 0/1 number to a false/true value | [Documentation](sc_common.md#number_to_boolean-method) | -| check_boolean_number_option_syntax | make sure that a boolean is 0 or 1, if that's not the case, replace it with a default value | [Documentation](sc_common.md#check_boolean_number_option_syntax-method) | -| split | split a string using a separator (default is ",") and store each part in a table | [Documentation](sc_common.md#split-method) | -| compare_numbers | compare two numbers using the given mathematical operator and return true or false | [Documentation](sc_common.md#compare_numbers-method) | -| generate_postfield_param_string | convert a table of parameters into a URL encoded parameter string | [Documentation](sc_common.md#generate_postfield_param_string-method) | -| load_json_file | the method loads a json file and parses it | [Documentation](sc_common.md#load_json_file-method) | -| json_escape | escape json characters in a string | [Documentation](sc_common.md#json_escape-method) | -| xml_escape | escape xml characters in a string | [Documentation](sc_common.md#xml_escape-method) | -| lua_regex_escape | escape lua regex special characters in a string | [Documentation](sc_common.md#lua_regex_escape-method) | +| Method name | Method description | Link | +| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | +| ifnil_or_empty | check if a variable is empty or nil and replace it with a default value if it is the case | [Documentation](sc_common.md#ifnil_or_empty-method) | +| if_wrong_type | check the type of a variable, if it is wrong, replace the variable with a default value | [Documentation](sc_common.md#if_wrong_type-method) | +| boolean_to_number | change a true/false boolean to a 1/0 value | [Documentation](sc_common.md#boolean_to_number-method) | +| number_to_boolean | change a 0/1 number to a false/true value | [Documentation](sc_common.md#number_to_boolean-method) | +| check_boolean_number_option_syntax | make sure that a boolean is 0 or 1, if that's not the case, replace it with a default value | [Documentation](sc_common.md#check_boolean_number_option_syntax-method) | +| split | split a string using a separator (default is ",") and store each part in a table | [Documentation](sc_common.md#split-method) | +| compare_numbers | compare two numbers using the given mathematical operator and return true or false | [Documentation](sc_common.md#compare_numbers-method) | +| generate_postfield_param_string | convert a table of parameters into a URL encoded parameter string | [Documentation](sc_common.md#generate_postfield_param_string-method) | +| load_json_file | the method loads a json file and parses it | [Documentation](sc_common.md#load_json_file-method) | +| json_escape | escape json characters in a string | [Documentation](sc_common.md#json_escape-method) | +| xml_escape | escape xml characters in a string | [Documentation](sc_common.md#xml_escape-method) | +| lua_regex_escape | escape lua regex special characters in a string | [Documentation](sc_common.md#lua_regex_escape-method) | | dumper | dump any variable for debug purposes | [Documentation](sc_common.md#dumper-method) | -| trim | trim spaces (or provided character) at the beginning and the end of a string | [Documentation](sc_common.md#trim-method) | -| get_bbdo_version | returns the first digit of the bbdo protocol version | [Documentation](sc_common.md#get_bbdo_version-method) | -| is_valid_pattern | check if a Lua pattern is valid | [Documentation](sc_common.md#is_valid_pattern-method) | -| sleep | wait a given number of seconds | [Documentation](sc_common.md#sleep-method) | +| trim | trim spaces (or provided character) at the beginning and the end of a string | [Documentation](sc_common.md#trim-method) | +| get_bbdo_version | returns the first digit of the bbdo protocol version | [Documentation](sc_common.md#get_bbdo_version-method) | +| is_valid_pattern | check if a Lua pattern is valid | [Documentation](sc_common.md#is_valid_pattern-method) | +| sleep | wait a given number of seconds | [Documentation](sc_common.md#sleep-method) | | create_sleep_counter_table | create a table to handle sleep counters. Useful when you want to log something less often after some repetitions | [Documentation](sc_common.md#create_sleep_counter_table-method) | ## sc_logger methods @@ -80,16 +80,16 @@ ## sc_param methods -| Method name | Method description | Link | -| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | -| param_override | replace default values of params with the ones provided by users in the web configuration of the stream connector | [Documentation](sc_param.md#param_override-method) | +| Method name | Method description | Link | +| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | +| param_override | replace default values of params with the ones provided by users in the web configuration of the stream connector | [Documentation](sc_param.md#param_override-method) | | check_params | make sure that the default stream connector params provided by the user from the web configuration are valid. If not, uses the default value | [Documentation](sc_param.md#check_params-method) | -| is_mandatory_config_set | check that all mandatory parameters for a stream connector are set | [Documentation](sc_param.md#is_mandatory_config_set-method) | -| get_kafka_params | retreive Kafka dedicated parameters from the parameter list and put them in the provided kafka_config object | [Documentation](sc_param.md#get_kafka_params-method) | -| load_event_format_file | load a file that serves as a template for formatting events | [Documentation](sc_param.md#load_event_format_file-method) | -| build_accepted_elements_info | build a table that stores information about accepted elements | [Documentation](sc_param.md#build_accepted_elements_info-method) | -| validate_pattern_param | check if a parameter has a valid Lua pattern as a value | [Documentation](sc_param.md#validate_pattern_param-method) | -| build_and_validate_filters_pattern | build a table that stores information about patterns for compatible parameters | [Documentation](sc_param.md#build_and_validate_filters_pattern-method) | +| is_mandatory_config_set | check that all mandatory parameters for a stream connector are set | [Documentation](sc_param.md#is_mandatory_config_set-method) | +| get_kafka_params | retreive Kafka dedicated parameters from the parameter list and put them in the provided kafka_config object | [Documentation](sc_param.md#get_kafka_params-method) | +| load_event_format_file | load a file that serves as a template for formatting events | [Documentation](sc_param.md#load_event_format_file-method) | +| build_accepted_elements_info | build a table that stores information about accepted elements | [Documentation](sc_param.md#build_accepted_elements_info-method) | +| validate_pattern_param | check if a parameter has a valid Lua pattern as a value | [Documentation](sc_param.md#validate_pattern_param-method) | +| build_and_validate_filters_pattern | build a table that stores information about patterns for compatible parameters | [Documentation](sc_param.md#build_and_validate_filters_pattern-method) | ## sc_event methods @@ -163,6 +163,7 @@ | Method name | Method description | Link | | ------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------- | +| create_new_virtual_queue | create a virtual queue | [Documentation](sc_flush.md#create_new_virtual_queue-method) | | add_queue_metadata | add specific metadata to a queue | [Documentation](sc_flush.md#add_queue_metadata-method) | | flush_all_queues | try to flush all queues according to accepted elements | [Documentation](sc_flush.md#flush_all_queues-method) | | reset_all_queues | put all queues back to their initial state after flushing their events | [Documentation](sc_flush.md#reset_all_queues-method) | diff --git a/modules/docs/sc_flush.md b/modules/docs/sc_flush.md index e18a3a30..bbc405fb 100644 --- a/modules/docs/sc_flush.md +++ b/modules/docs/sc_flush.md @@ -5,6 +5,10 @@ - [Module initialization](#module-initialization) - [Module constructor](#module-constructor) - [constructor: Example](#constructor-example) + - [create\_new\_virtual\_queue method](#create_new_virtual_queue-method) + - [create\_new\_virtual\_queue: parameters](#create_new_virtual_queue-parameters) + - [create\_new\_virtual\_queue: returns](#create_new_virtual_queue-returns) + - [create\_new\_virtual\_queue: example](#create_new_virtual_queue-example) - [add\_queue\_metadata method](#add_queue_metadata-method) - [add\_queue\_metadata: parameters](#add_queue_metadata-parameters) - [add\_queue\_metadata: example](#add_queue_metadata-example) @@ -70,6 +74,61 @@ local params = { local test_flush = sc_flush.new(params, test_logger) ``` +## create_new_virtual_queue method + +The **create_new_virtual_queue** method will create a virtual queue. Normally queues are created for accepted elements from the known BBDO elements. You can extend this mecanism with virtual queues. + +Use case: +downtime end events are not sent the same way than downtime start events (not sent to the same API endpoint or not the same HTTP method for example). +with the standard queue mecanism, those events are stored in the same queue. A queue only accepts a single method to send events (same endpoint, same HTTP method and so on). Therefore you can't discriminate downtime start and downtime end events. +That is where virtual queues come in handy. Instead of storing both event in the standard downtime queue, you create a virtual queue for one of them. +Usually when creating a virtual queue, you should then add metadata to your virtual queue using the [**add_queue_metadata method**](#add_queue_metadata-method) + +### create_new_virtual_queue: parameters + +| parameter | type | optional | default value | +| ------------------------------------------- | ------ | -------- | ------------- | +| the category id of the virtual queue | number | no | | +| the virtual element id of the virtual queue | number | no | | +| the name of the virtual element | string | no | | + +### create_new_virtual_queue: returns + +| return | type | always | condition | +| ------------- | ------- | ------ | --------- | +| true or false | boolean | yes | | + +### create_new_virtual_queue: example + +```lua +-- add a special queue for downtime deletion +local virtual_queues_info = { + downtime_end_element = { + id = 1000, -- a virtual element id that doesn't exist + name = "downtime_end" + } +} + +local category = 1 -- 1 = neb category, since downtime are from the neb category, it makes sense to put them here + +test_flush:create_new_virtual_queue(category, virtual_queues_info.downtime_end_element.id, virtual_queues_info.downtime_end_element.name) +--> the downtime_end category is now created (category: 1, element: 1000) +--[[ + test_flush.queues = { + [1] = { + [1000] = { + events = {}, + queue_metadata = { + category_id = 1, + element_id = 1000 + } + } + } + } +]]-- +``` + + ## add_queue_metadata method The **add_queue_metadata** method adds a list of metadata to a given queue. From d333f120fc9f48481ebcd3da242905ce8886daaa Mon Sep 17 00:00:00 2001 From: tanguyvda Date: Wed, 5 Mar 2025 11:49:42 +0100 Subject: [PATCH 2/9] start refacto downtime part of canopsis --- .../canopsis/canopsis2x-events-apiv2.lua | 94 +++++++++++-------- 1 file changed, 54 insertions(+), 40 deletions(-) diff --git a/centreon-certified/canopsis/canopsis2x-events-apiv2.lua b/centreon-certified/canopsis/canopsis2x-events-apiv2.lua index 7be345a5..11ff2c49 100644 --- a/centreon-certified/canopsis/canopsis2x-events-apiv2.lua +++ b/centreon-certified/canopsis/canopsis2x-events-apiv2.lua @@ -64,7 +64,7 @@ function EventQueue.new(params) end -- force buffer size to 1 because this SC does not allows the event bulk at this moment. (can't send more than one event at once) - params.max_buffer_size = 1 + -- params.max_buffer_size = 1 self.sc_logger:notice("[EventQueue:new]: max_buffer_size = 1 (force buffer size to 1 because this SC does not allows the event bulk at this moment)") -- mandatory parameter @@ -76,7 +76,7 @@ function EventQueue.new(params) self.sc_params.params.canopsis_downtime_comment_route = params.canopsis_downtime_comment_route or "/api/v4/pbehavior-comments" self.sc_params.params.canopsis_downtime_reason_name = params.canopsis_downtime_reason_name or "Centreon_downtime" self.sc_params.params.canopsis_downtime_reason_route = params.canopsis_downtime_reason_route or "/api/v4/pbehavior-reasons" - self.sc_params.params.canopsis_downtime_route = params.canopsis_downtime_route or "/api/v4/pbehaviors" + self.sc_params.params.canopsis_downtime_route = params.canopsis_downtime_route or "/api/v4/bulk/pbehaviors" self.sc_params.params.canopsis_downtime_send_pbh = params.canopsis_downtime_send_pbh or 1 self.sc_params.params.canopsis_downtime_type_name = params.canopsis_downtime_type_name or "Default maintenance" self.sc_params.params.canopsis_downtime_type_route = params.canopsis_downtime_type_route or "/api/v4/pbehavior-types" @@ -122,6 +122,18 @@ function EventQueue.new(params) self.sc_flush:add_queue_metadata(categories.neb.id, elements.service_status.id, {event_route = self.sc_params.params.canopsis_event_route}) self.sc_flush:add_queue_metadata(categories.neb.id, elements.acknowledgement.id, {event_route = self.sc_params.params.canopsis_event_route}) self.sc_flush:add_queue_metadata(categories.neb.id, elements.downtime.id, {event_route = self.sc_params.params.canopsis_downtime_route}) + + -- add a special queue for downtime deletion + self.virtual_queues_info = { + downtime_end_element = { + id = 1000, + name = "downtime_end" + } + } + + if self.sc_flush:create_new_virtual_queue(categories.neb.id, self.virtual_queues_info.downtime_end_element.id, self.virtual_queues_info.downtime_end_element.name) then + self.sc_flush:add_queue_metadata(categories.neb.id, self.virtual_queues_info.downtime_end_element.id, {event_route = self.sc_params.params.canopsis_downtime_route, method = "DELETE"}) + end self.format_event = { [categories.neb.id] = { @@ -214,7 +226,12 @@ function EventQueue.new(params) method = "GET", event_route = "/api/v4/app-info" } - canopsis_version = self:getCanopsisAPI(metadata_type, "/api/v4/app-info", "", "") + + if self.sc_params.params.send_data_test == 1 then + self.canopsis_version = "fake_canopsis_version" + else + self.canopsis_version = self:getCanopsisAPI(metadata_type, "/api/v4/app-info", "", "") + end end return self @@ -392,14 +409,14 @@ function EventQueue:format_event_downtime() event.internal_id = event.id end - local downtime_name = "centreon-downtime-" .. tostring(event.internal_id) .. "-" .. tostring(event.entry_time) + local downtime_name = "centreon-downtime-" .. tostring(event.host_id) .. "-" .. tostring(event.service_id) .. "-" .. tostring(event.internal_id) .. "-" .. tostring(event.entry_time) if event.cancelled == true or (self.bbdo_version == 2 and event.deletion_time == 1) or (self.bbdo_version > 2 and event.deletion_time ~= -1) then - local metadata = { - method = "DELETE", - event_route = self.sc_params.params.canopsis_downtime_route + -- transform the element_id into the virtual one. The add() function will properly store the event in the virtual queue. Since filters have already been appied, we don't mind tweaking the element_id + self.sc_event.event.element = self.virtual_queues_info.downtime_end_element.id + self.sc_event.event.formated_event = { + name = downtime_name } - self:send_data({name = downtime_name}, metadata) else self.sc_event.event.formated_event = { _id = downtime_name, @@ -441,7 +458,7 @@ function EventQueue:format_event_downtime() end -- In case of Canopsis version 22.10.X a color value is add in downtime: - if string.find(canopsis_version, "22.10.") ~= nil then + if self.canopsis_version and string.find(self.canopsis_version, "22.10.") ~= nil then self.sc_event.event.formated_event["color"] = "#73D8FF" end end @@ -454,18 +471,16 @@ function EventQueue:add() -- store event in self.events lists local category = self.sc_event.event.category local element = self.sc_event.event.element - local next = next - if next(self.sc_event.event.formated_event) ~= nil then - self.sc_logger:debug("[EventQueue:add]: add event in queue category: " .. tostring(self.sc_params.params.reverse_category_mapping[category]) - .. " element: " .. tostring(self.sc_params.params.reverse_element_mapping[category][element])) - self.sc_logger:debug("[EventQueue:add]: queue size before adding event: " .. tostring(#self.sc_flush.queues[category][element].events)) + self.sc_logger:debug("[EventQueue:add]: add event in queue category: " .. tostring(self.sc_params.params.reverse_category_mapping[category]) + .. " element: " .. tostring(self.sc_params.params.reverse_element_mapping[category][element])) + self.sc_logger:debug("[EventQueue:add]: queue size before adding event: " .. tostring(#self.sc_flush.queues[category][element].events)) - self.sc_flush.queues[category][element].events[#self.sc_flush.queues[category][element].events + 1] = self.sc_event.event.formated_event + -- self.sc_logger:notice(self.sc_common:dumper(self.sc_flush.queues[category])) + self.sc_flush.queues[category][element].events[#self.sc_flush.queues[category][element].events + 1] = self.sc_event.event.formated_event - self.sc_logger:info("[EventQueue:add]: queue size is now: " .. tostring(#self.sc_flush.queues[category][element].events) - .. ", max is: " .. tostring(self.sc_params.params.max_buffer_size)) - end + self.sc_logger:info("[EventQueue:add]: queue size is now: " .. tostring(#self.sc_flush.queues[category][element].events) + .. ", max is: " .. tostring(self.sc_params.params.max_buffer_size)) end -------------------------------------------------------------------------------- @@ -486,7 +501,7 @@ end function EventQueue:send_data(payload, queue_metadata) self.sc_logger:debug("[EventQueue:send_data]: Starting to send data") - + -- self.sc_logger:notice(self.sc_common:dumper(payload)) local params = self.sc_params.params local downtime_comment = "" local send_downtime_comment = false @@ -494,25 +509,24 @@ function EventQueue:send_data(payload, queue_metadata) local url = params.sending_protocol .. "://" .. params.canopsis_host .. ':' .. params.canopsis_port .. queue_metadata.event_route -- Deletion (for downtimes) - if queue_metadata.method ~= nil and queue_metadata.method == "DELETE" then - http_method = queue_metadata.method - url = url .. "?name=" .. payload.name - queue_metadata.headers = { - "accept: */*", - "x-canopsis-authkey: " .. tostring(self.sc_params.params.canopsis_authkey) - } - payload = broker.json_encode(payload) - self.sc_logger:log_curl_command(url, queue_metadata, self.sc_params.params, "") + -- if queue_metadata.method ~= nil and queue_metadata.method == "DELETE" then + -- http_method = queue_metadata.method + -- url = url .. "?name=" .. payload.name + -- queue_metadata.headers = { + -- "accept: */*", + -- "x-canopsis-authkey: " .. tostring(self.sc_params.params.canopsis_authkey) + -- } + -- payload = broker.json_encode(payload) + -- self.sc_logger:log_curl_command(url, queue_metadata, self.sc_params.params, "") -- Downtime events creation - elseif queue_metadata.event_route == self.sc_params.params.canopsis_downtime_route then - payload = payload[1] + if queue_metadata.event_route == self.sc_params.params.canopsis_downtime_route and queue_metadata.method ~= "DELETE" then queue_metadata.headers = { "content-type: application/json", "x-canopsis-authkey: " .. tostring(self.sc_params.params.canopsis_authkey) } - downtime_comment = table_extract_and_remove_key(payload,"comment") - send_downtime_comment = true + -- downtime_comment = table_extract_and_remove_key(payload,"comment") + -- send_downtime_comment = true payload = broker.json_encode(payload) self.sc_logger:log_curl_command(url, queue_metadata, self.sc_params.params, payload) @@ -588,14 +602,14 @@ function EventQueue:send_data(payload, queue_metadata) .. tostring(http_response_code)) -- in case of Downtime event post, an other post is required - if send_downtime_comment == true then - self.sc_logger:info("[EventQueue:send_data]: Comment to send is " .. tostring(downtime_comment)) - local metadata_comment = { - method = "POST", - event_route = self.sc_params.params.canopsis_downtime_comment_route - } - self:postCanopsisAPI(metadata_comment, self.sc_params.params.canopsis_downtime_comment_route, downtime_comment) - end + -- if send_downtime_comment == true then + -- self.sc_logger:info("[EventQueue:send_data]: Comment to send is " .. tostring(downtime_comment)) + -- local metadata_comment = { + -- method = "POST", + -- event_route = self.sc_params.params.canopsis_downtime_comment_route + -- } + -- self:postCanopsisAPI(metadata_comment, self.sc_params.params.canopsis_downtime_comment_route, downtime_comment) + -- end retval = true elseif http_response_code == 400 and (string.match(tostring(http_response_body), "Trying to insert PBehavior with already existing _id") or string.find(tostring(http_response_body), "ID already exists")) then From 0c3a43c738186ee649bae5cbc63c63e1a06c3fa6 Mon Sep 17 00:00:00 2001 From: tanguyvda Date: Wed, 5 Mar 2025 21:07:53 +0100 Subject: [PATCH 3/9] add logs --- modules/centreon-stream-connectors-lib/sc_flush.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/centreon-stream-connectors-lib/sc_flush.lua b/modules/centreon-stream-connectors-lib/sc_flush.lua index 07d5c865..f9c6a68d 100644 --- a/modules/centreon-stream-connectors-lib/sc_flush.lua +++ b/modules/centreon-stream-connectors-lib/sc_flush.lua @@ -73,7 +73,13 @@ function ScFlush:create_new_virtual_queue(category_id, virtual_element_id, virtu end -- add a prefix for the element name to have it is easily identified as being a virtual queue - virtual_element_name = "virtual_" .. virtual_element_name + virtual_element_name = "_virtual_" .. virtual_element_name + + if self.params.accepted_elements_info[virtual_element_name] then + self.sc_logger:error("[sc_flush:create_new_virtual_queue]: virtual element name already exists: " .. tostring(virtual_element_name) + .. ". You must change it. (the _virtual_ prefix is automatically added by stream connectors libraries)") + return false + end -- create queue self.queues[category_id][virtual_element_id] = { From 8090db791b46dbc6526c2233265766c9f7076bd8 Mon Sep 17 00:00:00 2001 From: tanguyvda Date: Mon, 14 Apr 2025 15:44:39 +0200 Subject: [PATCH 4/9] reenable comment for downtimes --- .../canopsis/canopsis2x-events-apiv2.lua | 53 ++++++++----------- .../sc_event.lua | 2 +- modules/docs/sc_flush.md | 3 +- 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/centreon-certified/canopsis/canopsis2x-events-apiv2.lua b/centreon-certified/canopsis/canopsis2x-events-apiv2.lua index 11ff2c49..bb779766 100644 --- a/centreon-certified/canopsis/canopsis2x-events-apiv2.lua +++ b/centreon-certified/canopsis/canopsis2x-events-apiv2.lua @@ -14,16 +14,6 @@ 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") --------------------------------------------------------------------------------- --- Misc function --------------------------------------------------------------------------------- - -local function table_extract_and_remove_key(table, key) - local element = table[key] - table[key] = nil - return element -end - -------------------------------------------------------------------------------- -- Classe event_queue -------------------------------------------------------------------------------- @@ -508,25 +498,21 @@ function EventQueue:send_data(payload, queue_metadata) local http_method = "POST" local url = params.sending_protocol .. "://" .. params.canopsis_host .. ':' .. params.canopsis_port .. queue_metadata.event_route - -- Deletion (for downtimes) - -- if queue_metadata.method ~= nil and queue_metadata.method == "DELETE" then - -- http_method = queue_metadata.method - -- url = url .. "?name=" .. payload.name - -- queue_metadata.headers = { - -- "accept: */*", - -- "x-canopsis-authkey: " .. tostring(self.sc_params.params.canopsis_authkey) - -- } - -- payload = broker.json_encode(payload) - -- self.sc_logger:log_curl_command(url, queue_metadata, self.sc_params.params, "") - - -- Downtime events creation + -- Downtime events creation (downtime deletion goes though the standard mecanism like any other event) if queue_metadata.event_route == self.sc_params.params.canopsis_downtime_route and queue_metadata.method ~= "DELETE" then queue_metadata.headers = { "content-type: application/json", "x-canopsis-authkey: " .. tostring(self.sc_params.params.canopsis_authkey) } - -- downtime_comment = table_extract_and_remove_key(payload,"comment") - -- send_downtime_comment = true + + local downtimes_comment_data = {} + for _, downtime_info in ipairs(payload) do + table.insert(downtimes_comment_data, downtime_info.comment) + -- remove comment from downtime creation payload. While it the paylaod is accepted with this data, it is not computed so it adds weight to the payload for nothing + downtime_info.comment = nil + end + + send_downtime_comment = true payload = broker.json_encode(payload) self.sc_logger:log_curl_command(url, queue_metadata, self.sc_params.params, payload) @@ -602,14 +588,17 @@ function EventQueue:send_data(payload, queue_metadata) .. tostring(http_response_code)) -- in case of Downtime event post, an other post is required - -- if send_downtime_comment == true then - -- self.sc_logger:info("[EventQueue:send_data]: Comment to send is " .. tostring(downtime_comment)) - -- local metadata_comment = { - -- method = "POST", - -- event_route = self.sc_params.params.canopsis_downtime_comment_route - -- } - -- self:postCanopsisAPI(metadata_comment, self.sc_params.params.canopsis_downtime_comment_route, downtime_comment) - -- end + if send_downtime_comment == true then + self.sc_logger:info("[EventQueue:send_data]: Comment to send is " .. tostring(downtime_comment)) + local metadata_comment = { + method = "POST", + event_route = self.sc_params.params.canopsis_downtime_comment_route + } + + for _, downtime_comment in ipairs(downtimes_comment_data) do + self:postCanopsisAPI(metadata_comment, self.sc_params.params.canopsis_downtime_comment_route, downtime_comment) + end + end retval = true elseif http_response_code == 400 and (string.match(tostring(http_response_body), "Trying to insert PBehavior with already existing _id") or string.find(tostring(http_response_body), "ID already exists")) then diff --git a/modules/centreon-stream-connectors-lib/sc_event.lua b/modules/centreon-stream-connectors-lib/sc_event.lua index 3df94c61..cb420de0 100644 --- a/modules/centreon-stream-connectors-lib/sc_event.lua +++ b/modules/centreon-stream-connectors-lib/sc_event.lua @@ -1191,7 +1191,7 @@ function ScEvent:get_most_recent_status_code(timestamp) } -- compare all status timestamp and keep the most recent one and the corresponding status code - for status_code, status_timestamp in ipairs(timestamp) do + for status_code, status_timestamp in pairs(timestamp) do if status_timestamp > status_info.highest_timestamp then status_info.highest_timestamp = status_timestamp status_info.status = status_code diff --git a/modules/docs/sc_flush.md b/modules/docs/sc_flush.md index bbc405fb..d9c7ad76 100644 --- a/modules/docs/sc_flush.md +++ b/modules/docs/sc_flush.md @@ -81,7 +81,7 @@ The **create_new_virtual_queue** method will create a virtual queue. Normally qu Use case: downtime end events are not sent the same way than downtime start events (not sent to the same API endpoint or not the same HTTP method for example). with the standard queue mecanism, those events are stored in the same queue. A queue only accepts a single method to send events (same endpoint, same HTTP method and so on). Therefore you can't discriminate downtime start and downtime end events. -That is where virtual queues come in handy. Instead of storing both event in the standard downtime queue, you create a virtual queue for one of them. +That is where virtual queues come in handy. Instead of storing both events in the standard downtime queue, you create a virtual queue for one of them. Usually when creating a virtual queue, you should then add metadata to your virtual queue using the [**add_queue_metadata method**](#add_queue_metadata-method) ### create_new_virtual_queue: parameters @@ -128,7 +128,6 @@ test_flush:create_new_virtual_queue(category, virtual_queues_info.downtime_end_e ]]-- ``` - ## add_queue_metadata method The **add_queue_metadata** method adds a list of metadata to a given queue. From 3c1c7e25ec12fd08e99e8556261e672b0ef8131d Mon Sep 17 00:00:00 2001 From: tanguyvda Date: Wed, 23 Apr 2025 17:55:37 +0200 Subject: [PATCH 5/9] change how severity to state works to avoid errors when no severity --- .../canopsis/canopsis2x-events-apiv2.lua | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/centreon-certified/canopsis/canopsis2x-events-apiv2.lua b/centreon-certified/canopsis/canopsis2x-events-apiv2.lua index bb779766..5625b47d 100644 --- a/centreon-certified/canopsis/canopsis2x-events-apiv2.lua +++ b/centreon-certified/canopsis/canopsis2x-events-apiv2.lua @@ -284,13 +284,27 @@ function EventQueue:list_hostgroups() return hostgroups end -function EventQueue:get_state(event, severity) - -- return standard centreon state - if severity and self.sc_params.params.use_severity_as_state == 1 then - return severity +function EventQueue:get_state() + local event = self.sc_event.event + local params = self.sc_params.params + + if params.use_severity_as_state ~= 1 then + return self.centreon_to_canopsis_state[event.category][event.element][event.state] end - return self.centreon_to_canopsis_state[event.category][event.element][event.state] + local severity_cache_type = { + [params.bbdo.categories["neb"].id] = { + [params.bbdo.elements["host_status"].id] = "host", + [params.bbdo.elements["service_status"].id] = "service" + } + } + + if event.cache.severity + and event.cache.severity[severity_cache_type[event.category][event.element]] + and params.use_severity_as_state == 1 + then + return event.cache.severity[severity_cache_type[event.category][event.element]] + end end function EventQueue:get_connector_name() @@ -313,7 +327,7 @@ function EventQueue:format_event_host() component = tostring(event.cache.host.name), output = event.short_output, long_output = event.long_output, - state = self:get_state(event, event.cache.severity.host), + state = self:get_state(), timestamp = event.last_check, hostgroups = self:list_hostgroups(), notes_url = tostring(event.cache.host.notes_url), @@ -334,7 +348,7 @@ function EventQueue:format_event_service() resource = tostring(event.cache.service.description), output = event.short_output, long_output = event.long_output, - state = self:get_state(event, event.cache.severity.service), + state = self:get_state(), timestamp = event.last_check, servicegroups = self:list_servicegroups(), notes_url = event.cache.service.notes_url, From c57fc5aaab5b9a705060a2e1979f223e21159ed2 Mon Sep 17 00:00:00 2001 From: tanguyvda Date: Wed, 23 Apr 2025 18:09:45 +0200 Subject: [PATCH 6/9] avoid for loop for hg/sg when there is none in the cache --- centreon-certified/canopsis/canopsis2x-events-apiv2.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/centreon-certified/canopsis/canopsis2x-events-apiv2.lua b/centreon-certified/canopsis/canopsis2x-events-apiv2.lua index 5625b47d..29012611 100644 --- a/centreon-certified/canopsis/canopsis2x-events-apiv2.lua +++ b/centreon-certified/canopsis/canopsis2x-events-apiv2.lua @@ -273,10 +273,12 @@ end function EventQueue:list_hostgroups() local hostgroups = {} - for _, hg in pairs(self.sc_event.event.cache.hostgroups) do - table.insert(hostgroups, hg.group_name) + if type(self.sc_event.event.cache.hostgroups) == "table" then + for _, hg in pairs(self.sc_event.event.cache.hostgroups) do + table.insert(hostgroups, hg.group_name) + end end - + if self.sc_params.params.canopsis_sort_list_hostgroups == 1 then table.sort(hostgroups) end From d08072388be3fcccd3ac5d196d2c550420cd327e Mon Sep 17 00:00:00 2001 From: tanguyvda Date: Wed, 23 Apr 2025 18:13:16 +0200 Subject: [PATCH 7/9] forgot the sg part --- centreon-certified/canopsis/canopsis2x-events-apiv2.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/centreon-certified/canopsis/canopsis2x-events-apiv2.lua b/centreon-certified/canopsis/canopsis2x-events-apiv2.lua index 29012611..23ad4e62 100644 --- a/centreon-certified/canopsis/canopsis2x-events-apiv2.lua +++ b/centreon-certified/canopsis/canopsis2x-events-apiv2.lua @@ -259,8 +259,10 @@ end function EventQueue:list_servicegroups() local servicegroups = {} - for _, sg in pairs(self.sc_event.event.cache.servicegroups) do - table.insert(servicegroups, sg.group_name) + if type(self.sc_event.event.cache.servicegroups) == "table" then + for _, sg in pairs(self.sc_event.event.cache.servicegroups) do + table.insert(servicegroups, sg.group_name) + end end if self.sc_params.params.canopsis_sort_list_servicegroups == 1 then From 9746b654ec265d8bed4276f111da52a7ae5b575e Mon Sep 17 00:00:00 2001 From: tanguyvda Date: Thu, 24 Apr 2025 11:01:18 +0200 Subject: [PATCH 8/9] fix local variable --- centreon-certified/canopsis/canopsis2x-events-apiv2.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/centreon-certified/canopsis/canopsis2x-events-apiv2.lua b/centreon-certified/canopsis/canopsis2x-events-apiv2.lua index 23ad4e62..ab9cca1b 100644 --- a/centreon-certified/canopsis/canopsis2x-events-apiv2.lua +++ b/centreon-certified/canopsis/canopsis2x-events-apiv2.lua @@ -523,9 +523,9 @@ function EventQueue:send_data(payload, queue_metadata) "x-canopsis-authkey: " .. tostring(self.sc_params.params.canopsis_authkey) } - local downtimes_comment_data = {} + self.downtimes_comment_data = {} for _, downtime_info in ipairs(payload) do - table.insert(downtimes_comment_data, downtime_info.comment) + table.insert(self.downtimes_comment_data, downtime_info.comment) -- remove comment from downtime creation payload. While it the paylaod is accepted with this data, it is not computed so it adds weight to the payload for nothing downtime_info.comment = nil end @@ -613,7 +613,7 @@ function EventQueue:send_data(payload, queue_metadata) event_route = self.sc_params.params.canopsis_downtime_comment_route } - for _, downtime_comment in ipairs(downtimes_comment_data) do + for _, downtime_comment in ipairs(self.downtimes_comment_data) do self:postCanopsisAPI(metadata_comment, self.sc_params.params.canopsis_downtime_comment_route, downtime_comment) end end From bd0f756df18d498b2c5eaa6a456c2ba0b1c1225c Mon Sep 17 00:00:00 2001 From: tanguyvda Date: Wed, 18 Jun 2025 15:43:58 +0200 Subject: [PATCH 9/9] use new pbehavior endpoint and include PR work from Capensis #278 --- .../canopsis/canopsis2x-events-apiv2.lua | 344 +++++++++++------- 1 file changed, 207 insertions(+), 137 deletions(-) diff --git a/centreon-certified/canopsis/canopsis2x-events-apiv2.lua b/centreon-certified/canopsis/canopsis2x-events-apiv2.lua index ab9cca1b..de938a29 100644 --- a/centreon-certified/canopsis/canopsis2x-events-apiv2.lua +++ b/centreon-certified/canopsis/canopsis2x-events-apiv2.lua @@ -66,7 +66,7 @@ function EventQueue.new(params) self.sc_params.params.canopsis_downtime_comment_route = params.canopsis_downtime_comment_route or "/api/v4/pbehavior-comments" self.sc_params.params.canopsis_downtime_reason_name = params.canopsis_downtime_reason_name or "Centreon_downtime" self.sc_params.params.canopsis_downtime_reason_route = params.canopsis_downtime_reason_route or "/api/v4/pbehavior-reasons" - self.sc_params.params.canopsis_downtime_route = params.canopsis_downtime_route or "/api/v4/bulk/pbehaviors" + self.sc_params.params.canopsis_downtime_route = params.canopsis_downtime_route or "/api/v4/bulk/connector-pbehaviors" self.sc_params.params.canopsis_downtime_send_pbh = params.canopsis_downtime_send_pbh or 1 self.sc_params.params.canopsis_downtime_type_name = params.canopsis_downtime_type_name or "Default maintenance" self.sc_params.params.canopsis_downtime_type_route = params.canopsis_downtime_type_route or "/api/v4/pbehavior-types" @@ -111,19 +111,20 @@ function EventQueue.new(params) self.sc_flush:add_queue_metadata(categories.neb.id, elements.host_status.id, {event_route = self.sc_params.params.canopsis_event_route}) self.sc_flush:add_queue_metadata(categories.neb.id, elements.service_status.id, {event_route = self.sc_params.params.canopsis_event_route}) self.sc_flush:add_queue_metadata(categories.neb.id, elements.acknowledgement.id, {event_route = self.sc_params.params.canopsis_event_route}) - self.sc_flush:add_queue_metadata(categories.neb.id, elements.downtime.id, {event_route = self.sc_params.params.canopsis_downtime_route}) + self.sc_flush:add_queue_metadata(categories.neb.id, elements.downtime.id, {method = "PUT", event_route = self.sc_params.params.canopsis_downtime_route}) + -- NOT NEEDED ANYMORE WITH THE NEW DOWNTIME ENDPOINT -- add a special queue for downtime deletion - self.virtual_queues_info = { - downtime_end_element = { - id = 1000, - name = "downtime_end" - } - } + -- self.virtual_queues_info = { + -- downtime_end_element = { + -- id = 1000, + -- name = "downtime_end" + -- } + -- } - if self.sc_flush:create_new_virtual_queue(categories.neb.id, self.virtual_queues_info.downtime_end_element.id, self.virtual_queues_info.downtime_end_element.name) then - self.sc_flush:add_queue_metadata(categories.neb.id, self.virtual_queues_info.downtime_end_element.id, {event_route = self.sc_params.params.canopsis_downtime_route, method = "DELETE"}) - end + -- if self.sc_flush:create_new_virtual_queue(categories.neb.id, self.virtual_queues_info.downtime_end_element.id, self.virtual_queues_info.downtime_end_element.name) then + -- self.sc_flush:add_queue_metadata(categories.neb.id, self.virtual_queues_info.downtime_end_element.id, {event_route = self.sc_params.params.canopsis_downtime_route, method = "DELETE"}) + -- end self.format_event = { [categories.neb.id] = { @@ -162,16 +163,37 @@ function EventQueue.new(params) -- return EventQueue object setmetatable(self, { __index = EventQueue }) - --Check if downtime are wanted + --Check if downtime are wanted if self.sc_params.params.canopsis_downtime_send_pbh ~= 0 and self.sc_params.params.accepted_elements_info["downtime"] then + -- 1. Check Canopsis version : if the API endpoint exists + local metadata_route = { + method = "PUT", + event_route = self.sc_params.params.canopsis_downtime_route + } + local route_exists = self:checkCanopsisAPI(metadata_route, {}) + if route_exists ~= true then + self.sc_logger:info("[EventQueue.new]: route " .. self.sc_params.params.canopsis_downtime_route .. " is not available - check Canopsis API release") + -- if the route doesn't exist disable pbehavior management + self.sc_params.params.canopsis_downtime_send_pbh = 0 + self.sc_params.params.canopsis_downtime_not_send_pbh_reason = " route " .. self.sc_params.params.canopsis_downtime_route .. " is not available - check Canopsis API release" + + return self + end + + -- 2. Reason : Ensure reason "centreon_reason" exists, if not create it and post it local metadata_reason = { method = "GET", event_route = self.sc_params.params.canopsis_downtime_reason_route } - pbh_maintenance_reason_id = self:getCanopsisAPI(metadata_reason, self.sc_params.params.canopsis_downtime_reason_route, "", self.sc_params.params.canopsis_downtime_reason_name) + local pbh_maintenance_reason_id + + if self.sc_params.params.send_data_test == 1 then + pbh_maintenance_reason_id = "fake_reason_id" + else + pbh_maintenance_reason_id = self:getCanopsisAPI(metadata_reason, self.sc_params.params.canopsis_downtime_reason_route, "", self.sc_params.params.canopsis_downtime_reason_name) + end - -- 1. Reason : Ensure reason "centreon_reason" exists, if not create it and post it if pbh_maintenance_reason_id == false then self.sc_logger:notice("Reason for Centreon downtimes doesn't exist in Canopsis API: Creating pbehavior-reason 'centreon_reason") new_reason = { @@ -182,25 +204,37 @@ function EventQueue.new(params) method = "POST", event_route = self.sc_params.params.canopsis_downtime_reason_route } - self:postCanopsisAPI(metadata_post, self.sc_params.params.canopsis_downtime_reason_route, new_reason) - else + pbh_maintenance_reason_id = self:postCanopsisAPI(metadata_post, self.sc_params.params.canopsis_downtime_reason_route, new_reason) + end + + if pbh_maintenance_reason_id ~= false then -- If the reason id is reachable with downtime_reason_route - if pbh_maintenance_reason_id ~= false and self.sc_params.params.send_data_test ~= 1 then + if self.sc_params.params.send_data_test ~= 1 then self.sc_params.params.canopsis_downtime_reason_id = pbh_maintenance_reason_id - elseif pbh_maintenance_reason_id ~= false and self.sc_params.params.send_data_test == 1 then - self.sc_params.params.canopsis_downtime_reason_id = "RRRR" else - -- if unable to get reason id, disable pbehavior management - self.sc_params.params.canopsis_downtime_send_pbh = 0 + self.sc_params.params.canopsis_downtime_reason_id = "RRRR" end + else + -- if unable to get reason id, disable pbehavior management + self.sc_logger:info("[EventQueue.new]: Canopsis reason is not available") + self.sc_params.params.canopsis_downtime_send_pbh = 0 + self.sc_params.params.canopsis_downtime_not_send_pbh_reason = " Canopsis reason is not available" end - -- 2. Type : Dynamically get pbehavior type id for canopsis_downtime_type_name + -- 3. Type : Dynamically get pbehavior type id for canopsis_downtime_type_name local metadata_type = { method = "GET", event_route = self.sc_params.params.canopsis_downtime_type_route } - pbh_maintenance_type_id = self:getCanopsisAPI(metadata_type, self.sc_params.params.canopsis_downtime_type_route, self.sc_params.params.canopsis_downtime_type_name, "") + + local pbh_maintenance_type_id + + if self.sc_params.params.send_data_test == 1 then + pbh_maintenance_type_id = "fake_maintenance_type_id" + else + pbh_maintenance_type_id = self:getCanopsisAPI(metadata_type, self.sc_params.params.canopsis_downtime_type_route, self.sc_params.params.canopsis_downtime_type_name, "") + end + -- If the type id is reachable with downtime_type_route if pbh_maintenance_type_id ~= false and self.sc_params.params.send_data_test ~= 1 then self.sc_params.params.canopsis_downtime_type_id = pbh_maintenance_type_id @@ -208,19 +242,15 @@ function EventQueue.new(params) self.sc_params.params.canopsis_downtime_type_id = "TTTT" else -- if unable to get type id, disable pbehavior management + self.sc_logger:info("[EventQueue.new]: Canopsis type is not available") self.sc_params.params.canopsis_downtime_send_pbh = 0 + self.sc_params.params.canopsis_downtime_not_send_pbh_reason = " Canopsis type is not available" end - - -- 3. Type : Check Canopsis version to add or not the color value - local metadata_type = { - method = "GET", - event_route = "/api/v4/app-info" - } - - if self.sc_params.params.send_data_test == 1 then - self.canopsis_version = "fake_canopsis_version" + else + if self.sc_params.params.canopsis_downtime_send_pbh ~= 1 then + self.sc_params.params.canopsis_downtime_not_send_pbh_reason = " parameter canopsis_downtime_send_pbh is set to 0" else - self.canopsis_version = self:getCanopsisAPI(metadata_type, "/api/v4/app-info", "", "") + self.sc_params.params.canopsis_downtime_not_send_pbh_reason = " parameter accepted_elements doesn't contain downtime" end end @@ -417,58 +447,41 @@ function EventQueue:format_event_downtime() event.internal_id = event.id end - local downtime_name = "centreon-downtime-" .. tostring(event.host_id) .. "-" .. tostring(event.service_id) .. "-" .. tostring(event.internal_id) .. "-" .. tostring(event.entry_time) + local origin = self.sc_params.params.connector .. "/" .. self:get_connector_name() if event.cancelled == true or (self.bbdo_version == 2 and event.deletion_time == 1) or (self.bbdo_version > 2 and event.deletion_time ~= -1) then - -- transform the element_id into the virtual one. The add() function will properly store the event in the virtual queue. Since filters have already been appied, we don't mind tweaking the element_id - self.sc_event.event.element = self.virtual_queues_info.downtime_end_element.id self.sc_event.event.formated_event = { - name = downtime_name + action = "delete", + origin = origin, + tstart = event.start_time, + tstop = event.end_time, + comment = event.comment_data } else + local downtime_name = origin .. " downtime " .. tostring(event.start_time) .. "-" .. tostring(event.end_time) .. " " .. event.comment_data + if string.len(downtime_name) > 255 then + downtime_name = string.sub(downtime_name, 1, 252) .. "..." + end + self.sc_event.event.formated_event = { - _id = downtime_name, - author = event.author, - enabled = true, + action = "create", name = downtime_name, + origin = origin, reason = self.sc_params.params.canopsis_downtime_reason_id, - rrule = "", tstart = event.start_time, tstop = event.end_time, type = self.sc_params.params.canopsis_downtime_type_id, - -- timezone = self.sc_params.params.timezone, - comment = { - { - --['author'] = event.author, - ['pbehavior'] = downtime_name, - ['message'] = event.comment_data - } - }, - entity_pattern = { - { - { - field = "_id", - cond = { - type = "eq" - } - } - } - } - -- exdates = {} + comment = event.comment_data, + color = "#73D8FF" } + end - -- in downtime events, service id is equal to 0 when the downtime is about a host (same for BBDO 2 and 3) - if event.service_id ~= 0 then - self.sc_event.event.formated_event["entity_pattern"][1][1]["cond"]["value"] = tostring(event.cache.service.description) - .. "/" .. tostring(event.cache.host.name) - else - self.sc_event.event.formated_event["entity_pattern"][1][1]["cond"]["value"] = tostring(event.cache.host.name) - end - - -- In case of Canopsis version 22.10.X a color value is add in downtime: - if self.canopsis_version and string.find(self.canopsis_version, "22.10.") ~= nil then - self.sc_event.event.formated_event["color"] = "#73D8FF" - end + -- in downtime events, service id is equal to 0 when the downtime is about a host (same for BBDO 2 and 3) + if event.service_id ~= 0 then + self.sc_event.event.formated_event["entities"] = {tostring(event.cache.service.description) + .. "/" .. tostring(event.cache.host.name)} + else + self.sc_event.event.formated_event["entities"] = {tostring(event.cache.host.name)} end end @@ -511,41 +524,32 @@ function EventQueue:send_data(payload, queue_metadata) self.sc_logger:debug("[EventQueue:send_data]: Starting to send data") -- self.sc_logger:notice(self.sc_common:dumper(payload)) local params = self.sc_params.params - local downtime_comment = "" - local send_downtime_comment = false local http_method = "POST" local url = params.sending_protocol .. "://" .. params.canopsis_host .. ':' .. params.canopsis_port .. queue_metadata.event_route - -- Downtime events creation (downtime deletion goes though the standard mecanism like any other event) - if queue_metadata.event_route == self.sc_params.params.canopsis_downtime_route and queue_metadata.method ~= "DELETE" then - queue_metadata.headers = { - "content-type: application/json", - "x-canopsis-authkey: " .. tostring(self.sc_params.params.canopsis_authkey) - } - - self.downtimes_comment_data = {} - for _, downtime_info in ipairs(payload) do - table.insert(self.downtimes_comment_data, downtime_info.comment) - -- remove comment from downtime creation payload. While it the paylaod is accepted with this data, it is not computed so it adds weight to the payload for nothing - downtime_info.comment = nil + if params.canopsis_downtime_send_pbh ~= 1 and queue_metadata.event_route == params.canopsis_downtime_route then + if self.sc_params.params.canopsis_downtime_not_send_pbh_reason ~= nil then + self.sc_logger:info("[EventQueue:send_data]: Downtime data is not sent, " .. params.canopsis_downtime_not_send_pbh_reason) + else + self.sc_logger:info("[EventQueue:send_data]: Downtime data is not sent") end - send_downtime_comment = true - payload = broker.json_encode(payload) - - self.sc_logger:log_curl_command(url, queue_metadata, self.sc_params.params, payload) + return true + end - -- Other Event than downtimes - else - payload = broker.json_encode(payload) - queue_metadata.headers = { - "content-length: " .. string.len(payload), - "content-type: application/json", - "x-canopsis-authkey: " .. tostring(self.sc_params.params.canopsis_authkey) - } - self.sc_logger:log_curl_command(url, queue_metadata, self.sc_params.params, payload) + + if queue_metadata.method ~= nil then + http_method = queue_metadata.method end + payload = broker.json_encode(payload) + queue_metadata.headers = { + "content-length: " .. string.len(payload), + "content-type: application/json", + "x-canopsis-authkey: " .. tostring(self.sc_params.params.canopsis_authkey) + } + self.sc_logger:log_curl_command(url, queue_metadata, self.sc_params.params, payload) + -- 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)) @@ -604,20 +608,6 @@ function EventQueue:send_data(payload, queue_metadata) if http_response_code >= 200 and http_response_code <= 299 then self.sc_logger:info("[EventQueue:send_data]: HTTP " .. http_method .. " request successful: return code is " .. tostring(http_response_code)) - - -- in case of Downtime event post, an other post is required - if send_downtime_comment == true then - self.sc_logger:info("[EventQueue:send_data]: Comment to send is " .. tostring(downtime_comment)) - local metadata_comment = { - method = "POST", - event_route = self.sc_params.params.canopsis_downtime_comment_route - } - - for _, downtime_comment in ipairs(self.downtimes_comment_data) do - self:postCanopsisAPI(metadata_comment, self.sc_params.params.canopsis_downtime_comment_route, downtime_comment) - end - end - retval = true elseif http_response_code == 400 and (string.match(tostring(http_response_body), "Trying to insert PBehavior with already existing _id") or string.find(tostring(http_response_body), "ID already exists")) then self.sc_logger:notice("[EventQueue:send_data]: Ignoring downtime with id: " .. tostring(payload._id) @@ -731,22 +721,12 @@ function EventQueue:postCanopsisAPI(self_metadata, route, data_to_send) local params = self.sc_params.params local url = params.sending_protocol .. "://" .. params.canopsis_host .. ':' .. params.canopsis_port .. route - if route == self.sc_params.params.canopsis_downtime_comment_route then - data_to_send = data_to_send[1] - self_metadata.headers = { - "accept: application/json", - "content-type: application/json", - "x-canopsis-authkey: " .. tostring(self.sc_params.params.canopsis_authkey) - } - data_to_send = broker.json_encode(data_to_send) - else - data_to_send = broker.json_encode(data_to_send) - self_metadata.headers = { - "content-length: " .. string.len(data_to_send), - "content-type: application/json", - "x-canopsis-authkey: " .. tostring(self.sc_params.params.canopsis_authkey) - } - end + data_to_send = broker.json_encode(data_to_send) + self_metadata.headers = { + "content-length: " .. string.len(data_to_send), + "content-type: application/json", + "x-canopsis-authkey: " .. tostring(self.sc_params.params.canopsis_authkey) + } self.sc_logger:log_curl_command(url, self_metadata, self.sc_params.params, data_to_send) @@ -807,7 +787,15 @@ function EventQueue:postCanopsisAPI(self_metadata, route, data_to_send) .. tostring(http_response_code)) self.sc_logger:notice("[postCanopsisAPI]: HTTP POST request successful: return code is " .. tostring(http_response_code)) - retval = true + + local json_response_decoded, error = broker.json_decode(http_response_body) + if error then + self.sc_logger:error("[postCanopsisAPI]: couldn't decode json string: " .. tostring(http_response_body) + .. ". Error is: " .. tostring(error)) + return retval + end + + retval = json_response_decoded["_id"] else self.sc_logger:error("[postCanopsisAPI]: HTTP POST request FAILED, return code is " .. tostring(http_response_code) .. ". Message is: " .. tostring(http_response_body)) @@ -914,13 +902,6 @@ function EventQueue:getCanopsisAPI(self_metadata, route, type_name, reason_name) retval = reason_object["_id"] end end - -- No type_name and no reason_name had been given => getCanopsisAPI is used to check Canopsis version - elseif type_name == "" and reason_name == "" then - for json_element, json_object in pairs(json_response_decoded) do - if json_element == "version" then - retval = json_object - end - end end else self.sc_logger:error("[getCanopsisAPI]: HTTP request FAILED, return code is " @@ -929,3 +910,92 @@ function EventQueue:getCanopsisAPI(self_metadata, route, type_name, reason_name) return retval end + +-------------------------------------------------------------------------------- +-- Function to send a request to Canopsis API and check if a route exists +-------------------------------------------------------------------------------- +function EventQueue:checkCanopsisAPI(self_metadata, data_to_send) + self.sc_logger:debug("[checkCanopsisAPI]:Sending data to Canopsis route: ".. self_metadata.event_route) + + -- Handling the return code + local retval = false + local data_to_send = data_to_send + local params = self.sc_params.params + local url = params.sending_protocol .. "://" .. params.canopsis_host .. ':' .. params.canopsis_port .. self_metadata.event_route + + data_to_send = broker.json_encode(data_to_send) + self_metadata.headers = { + "content-length: " .. string.len(data_to_send), + "content-type: application/json", + "x-canopsis-authkey: " .. tostring(self.sc_params.params.canopsis_authkey) + } + + self.sc_logger:log_curl_command(url, self_metadata, self.sc_params.params, data_to_send) + + -- write payload in the logfile for test purpose + if self.sc_params.params.send_data_test == 1 then + self.sc_logger:notice("[checkCanopsisAPI]: " .. tostring(data_to_send)) + return true + end + + self.sc_logger:info("[checkCanopsisAPI]: Going to send the following json: " .. data_to_send) + self.sc_logger:info("[checkCanopsisAPI]: Canopsis address is: " .. tostring(url)) + + local http_response_body = "" + 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.connection_timeout) + :setopt(curl.OPT_SSL_VERIFYPEER, self.sc_params.params.verify_certificate) + :setopt(curl.OPT_SSL_VERIFYHOST, self.sc_params.params.verify_certificate) + :setopt(curl.OPT_HTTPHEADER, self_metadata.headers) + :setopt(curl.OPT_CUSTOMREQUEST, self_metadata.method) + + -- set proxy address configuration + if (self.sc_params.params.proxy_address ~= '') then + if (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("[checkCanopsisAPI]: 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("[checkCanopsisAPI]: proxy_password parameter is not set but proxy_username is used") + end + end + + if self_metadata.method == 'POST' or self_metadata.method == 'PUT' then + http_request:setopt_postfields(data_to_send) + end + + -- performing the HTTP request + http_request:perform() + + -- collecting results + http_response_code = http_request:getinfo(curl.INFO_RESPONSE_CODE) + + http_request:close() + + if http_response_code >= 200 and http_response_code <= 299 or http_response_code == 400 then + self.sc_logger:info("[checkCanopsisAPI]: HTTP request successful: return code is " + .. tostring(http_response_code)) + self.sc_logger:notice("[checkCanopsisAPI]: HTTP request successful: return code is " + .. tostring(http_response_code)) + retval = true + else + self.sc_logger:error("[checkCanopsisAPI]: HTTP request FAILED, return code is " + .. tostring(http_response_code) .. ". Message is: " .. tostring(http_response_body)) + end + + return retval +end \ No newline at end of file