diff --git a/CMakeLists.txt b/CMakeLists.txt index f6e48640113..6cf4081d0ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2009-2021 Centreon +# Copyright 2009-2023 Centreon # # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy of @@ -26,8 +26,13 @@ set(CMAKE_COLOR_DIAGNOSTICS ON) cmake_minimum_required(VERSION 3.16) project("Centreon Collect" C CXX) +option(WITH_ASAN + "Add the libasan to check memory leaks and other memory issues." OFF) + +option(WITH_TSAN + "Add the libtsan to check threads and other multithreading issues." OFF) if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_ID - STREQUAL "Clang") + STREQUAL "Clang") message( FATAL_ERROR "You can build broker with g++ or clang++. CMake will exit.") endif() @@ -35,6 +40,7 @@ endif() # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -stdlib=libc++") # set(CMAKE_CXX_COMPILER "clang++") add_definitions("-D_GLIBCXX_USE_CXX11_ABI=1") + option(NG "C++17 build." OFF) if(NG) @@ -46,6 +52,28 @@ endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) +if(WITH_TSAN) + set(CMAKE_CXX_FLAGS_DEBUG + "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=thread") + set(CMAKE_LINKER_FLAGS_DEBUG + "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=thread") +endif() + +if(WITH_ASAN) + set(CMAKE_BUILD_TYPE Debug) + if(WITH_CLANG) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address") + set(CMAKE_LINKER_FLAGS_DEBUG + "${CMAKE_LINKER_FLAGS_DEBUG} -fsanitize=address") + else() + set(CMAKE_CXX_FLAGS_DEBUG + "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set(CMAKE_LINKER_FLAGS_DEBUG + "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address" + ) + endif() +endif() + set(ALLOW_DUPLICATE_EXECUTABLE TRUE) set(BUILD_ARGS "-w" "dupbuild=warn") @@ -70,11 +98,11 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") string(TOLOWER "${id}" id) if(("${id}" MATCHES "debian") - OR("${like}" MATCHES "debian") - OR("${id}" MATCHES "ubuntu") - OR("${like}" MATCHES "ubuntu")) + OR ("${like}" MATCHES "debian") + OR ("${id}" MATCHES "ubuntu") + OR ("${like}" MATCHES "ubuntu")) set(OS_DISTRIBUTOR "Debian") - elseif(("${id}" MATCHES "centos") OR("${like}" MATCHES "centos")) + elseif(("${id}" MATCHES "centos") OR ("${like}" MATCHES "centos")) set(OS_DISTRIBUTOR "CentOS") else() message(WARNING "lsb_release in not installed") @@ -116,15 +144,15 @@ include(GNUInstallDirs) # var directories. set(BROKER_VAR_LOG_DIR - "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/log/centreon-broker") + "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/log/centreon-broker") set(BROKER_VAR_LIB_DIR - "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/centreon-broker") + "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/centreon-broker") set(ENGINE_VAR_LOG_DIR - "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/log/centreon-engine") + "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/log/centreon-engine") set(ENGINE_VAR_LOG_ARCHIVE_DIR - "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/log/centreon-engine/archives") + "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/log/centreon-engine/archives") set(ENGINE_VAR_LIB_DIR - "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/centreon-engine") + "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/centreon-engine") set(CMAKE_INSTALL_PREFIX "/usr") option(WITH_TESTING "Build unit tests." OFF) @@ -145,7 +173,7 @@ set(protobuf_MODULE_COMPATIBLE True) add_definitions(${CONAN_DEFINES_SPDLOG}) include_directories(${CMAKE_SOURCE_DIR} ${CONAN_INCLUDE_DIRS} - ${CMAKE_SOURCE_DIR}/clib/inc) + ${CMAKE_SOURCE_DIR}/clib/inc) add_subdirectory(bbdo) add_subdirectory(broker) @@ -162,4 +190,4 @@ add_custom_target(test-connector COMMAND tests/ut_connector) add_custom_target(test DEPENDS test-broker test-engine test-clib test-connector) add_custom_target(test-coverage DEPENDS broker-test-coverage - engine-test-coverage clib-test-coverage) + engine-test-coverage clib-test-coverage) diff --git a/README.md b/README.md index b96bbb21aac..79fbe803664 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,33 @@ test/ut You're done! +### inside a docker, ubuntu for example +In my case I have the home directory of the centreon project in /data/dev/centreon-collect + +First create the ubuntu container and jump into +```shell +docker container run --name ubuntu22.04 -ti -v /data/dev/centreon-collect:/root/centreon-collect ubuntu:22.04 /bin/bash +``` +Now you are in your ubuntu container +```shell +cd /root/centreon-collect/ +apt update +apt install python3 +apt install python3-pip +pip3 install conan +apt install libgnutls28-dev +apt install liblua5.4-dev +apt install librrd-dev +apt install cmake + +./cmake.sh +cd build +make +``` +Then you have all binaries compiled in the ubuntu distribution. +You can access it outside the container in the /data/dev/centreon-collect/build directory + + ## Bug reports / Feature requests The best way to report a bug or to request a feature is to open an issue diff --git a/broker/CMakeLists.txt b/broker/CMakeLists.txt index 3d9747de362..ef05ea37630 100644 --- a/broker/CMakeLists.txt +++ b/broker/CMakeLists.txt @@ -343,7 +343,6 @@ set(LIBROKER_SOURCES ${SRC_DIR}/io/protocols.cc ${SRC_DIR}/io/raw.cc ${SRC_DIR}/io/stream.cc - ${SRC_DIR}/log_v2.cc ${SRC_DIR}/mapping/entry.cc ${SRC_DIR}/misc/diagnostic.cc ${SRC_DIR}/misc/filesystem.cc @@ -363,6 +362,7 @@ set(LIBROKER_SOURCES ${SRC_DIR}/pool.cc ${SRC_DIR}/database/mysql_bind_base.cc ${SRC_DIR}/database/mysql_bind.cc + ${SRC_DIR}/database/mysql_bind_result.cc ${SRC_DIR}/database/mysql_bulk_bind.cc ${SRC_DIR}/database/mysql_column.cc ${SRC_DIR}/mysql_manager.cc @@ -447,8 +447,6 @@ set(LIBROKER_SOURCES ${INC_DIR}/multiplexing/publisher.hh ${INC_DIR}/mysql.hh ${INC_DIR}/pool.hh - ${INC_DIR}/database/mysql_bind_base.hh - ${INC_DIR}/database/mysql_bind.hh ${INC_DIR}/database/mysql_bulk_bind.hh ${INC_DIR}/database/mysql_column.hh ${INC_DIR}/database/mysql_error.hh @@ -480,6 +478,11 @@ set(LIBROKER_SOURCES ${INC_DIR}/version.hh) # Static libraries. +add_library(rokerlog STATIC ${SRC_DIR}/log_v2.cc) +set_target_properties(rokerlog PROPERTIES COMPILE_FLAGS "-fPIC") +target_precompile_headers(rokerlog PRIVATE core/precomp_inc/precomp.hpp) +target_link_libraries(rokerlog CONAN_PKG::spdlog) + add_library(rokerbase STATIC ${LIBROKER_SOURCES}) set_target_properties(rokerbase PROPERTIES COMPILE_FLAGS "-fPIC") target_precompile_headers(rokerbase PRIVATE core/precomp_inc/precomp.hpp) @@ -506,7 +509,8 @@ add_executable(cbd ${SRC_DIR}/main.cc) # Flags needed to include all symbols in binary. target_link_libraries( cbd - "-export-dynamic" + "-rdynamic" + rokerlog "-Wl,--whole-archive" rokerbase roker diff --git a/broker/bam/inc/com/centreon/broker/bam/availability_thread.hh b/broker/bam/inc/com/centreon/broker/bam/availability_thread.hh index c86b49c0875..519e56b952c 100644 --- a/broker/bam/inc/com/centreon/broker/bam/availability_thread.hh +++ b/broker/bam/inc/com/centreon/broker/bam/availability_thread.hh @@ -39,7 +39,7 @@ namespace bam { * @brief Availability thread * */ -class availability_thread { +class availability_thread final { public: availability_thread(database_config const& db_cfg, timeperiod_map& shared_map); diff --git a/broker/bam/inc/com/centreon/broker/bam/monitoring_stream.hh b/broker/bam/inc/com/centreon/broker/bam/monitoring_stream.hh index f242dbbb009..2a897e719e3 100644 --- a/broker/bam/inc/com/centreon/broker/bam/monitoring_stream.hh +++ b/broker/bam/inc/com/centreon/broker/bam/monitoring_stream.hh @@ -73,6 +73,7 @@ class monitoring_stream : public io::stream { database::mysql_stmt _ba_update; database::mysql_stmt _kpi_update; int32_t _pending_events; + unsigned _pending_request; database_config _storage_db_cfg; std::shared_ptr _cache; @@ -85,7 +86,6 @@ class monitoring_stream : public io::stream { absl::Hash>> _timer_forced_svc_checks; - void _check_replication(); void _prepare(); void _rebuild(); void _write_external_command(const std::string& cmd); diff --git a/broker/bam/src/monitoring_stream.cc b/broker/bam/src/monitoring_stream.cc index 12df55a4190..396539ee3d1 100644 --- a/broker/bam/src/monitoring_stream.cc +++ b/broker/bam/src/monitoring_stream.cc @@ -64,6 +64,7 @@ monitoring_stream::monitoring_stream(const std::string& ext_cmd_file, _ext_cmd_file(ext_cmd_file), _mysql(db_cfg), _pending_events(0), + _pending_request(0), _storage_db_cfg(storage_db_cfg), _cache(std::move(cache)), _forced_svc_checks_timer{pool::io_context()} { @@ -99,6 +100,7 @@ monitoring_stream::~monitoring_stream() { */ int32_t monitoring_stream::flush() { _mysql.commit(); + _pending_request = 0; int retval = _pending_events; SPDLOG_LOGGER_TRACE(log_v2::bam(), "BAM: monitoring_stream flush: {} events", retval); @@ -195,8 +197,21 @@ int monitoring_stream::write(std::shared_ptr const& data) { SPDLOG_LOGGER_TRACE(log_v2::bam(), "BAM: monitoring_stream write {}", *data); // Take this event into account. ++_pending_events; - if (!validate(data, get_name())) - return 0; + + // this lambda ask mysql to do a commit if we have get_queries_per_transaction + // waiting requests + auto commit_if_needed = [this]() { + if (_mysql.get_config().get_queries_per_transaction() > 0) { + if (_pending_request >= + _mysql.get_config().get_queries_per_transaction()) { + _mysql.commit(); + _pending_request = 0; + } + } else { // auto commit => nothing to do + _pending_request = 0; + } + }; + SPDLOG_LOGGER_TRACE(log_v2::bam(), "BAM: {} pending events", _pending_events); // Process service status events. @@ -313,6 +328,8 @@ int monitoring_stream::write(std::shared_ptr const& data) { _ba_update.bind_value_as_i32(5, status->state); _mysql.run_statement(_ba_update, database::mysql_error::update_ba, true); + ++_pending_request; + commit_if_needed(); if (status->state_changed) { std::pair ba_svc_name( @@ -354,6 +371,8 @@ int monitoring_stream::write(std::shared_ptr const& data) { _ba_update.bind_value_as_i32(5, status.state()); _mysql.run_statement(_ba_update, database::mysql_error::update_ba, true); + ++_pending_request; + commit_if_needed(); if (status.state_changed()) { std::pair ba_svc_name( @@ -398,6 +417,9 @@ int monitoring_stream::write(std::shared_ptr const& data) { _mysql.run_statement(_kpi_update, database::mysql_error::update_kpi, true); + ++_pending_request; + commit_if_needed(); + } break; case bam::pb_kpi_status::static_type(): { const KpiStatus& status( @@ -425,6 +447,9 @@ int monitoring_stream::write(std::shared_ptr const& data) { _mysql.run_statement(_kpi_update, database::mysql_error::update_kpi, true); + ++_pending_request; + commit_if_needed(); + } break; case inherited_downtime::static_type(): { std::string cmd; @@ -480,8 +505,26 @@ int monitoring_stream::write(std::shared_ptr const& data) { break; } - // Event acknowledgement. - return 0; + // if uncommited request, we can't yet acknowledge + if (_pending_request) { + if (_pending_events >= + 10 * _mysql.get_config().get_queries_per_transaction()) { + log_v2::bam()->trace( + "BAM: monitoring_stream write: too many pending events =>flush and " + "acknowledge {} events", + _pending_events); + return flush(); + } + log_v2::bam()->trace( + "BAM: monitoring_stream write: 0 events (request pending) {} to " + "acknowledge", + _pending_events); + return 0; + } + int retval = _pending_events; + _pending_events = 0; + log_v2::bam()->trace("BAM: monitoring_stream write: {} events", retval); + return retval; } /** diff --git a/broker/bam/test/ba/kpi_ba.cc b/broker/bam/test/ba/kpi_ba.cc index ebcca256e35..e3cae43916b 100644 --- a/broker/bam/test/ba/kpi_ba.cc +++ b/broker/bam/test/ba/kpi_ba.cc @@ -31,6 +31,8 @@ using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + class KpiBA : public ::testing::Test { protected: std::unique_ptr _aply_state; @@ -40,6 +42,7 @@ class KpiBA : public ::testing::Test { public: void SetUp() override { // Initialization. + g_io_context->restart(); config::applier::init(0, "test_broker", 0); _aply_state = std::make_unique(); diff --git a/broker/bam/test/ba/kpi_service.cc b/broker/bam/test/ba/kpi_service.cc index 88d310f6565..93ab6d7b288 100644 --- a/broker/bam/test/ba/kpi_service.cc +++ b/broker/bam/test/ba/kpi_service.cc @@ -36,6 +36,8 @@ using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + class BamBA : public ::testing::Test { protected: std::unique_ptr _aply_state; @@ -45,6 +47,7 @@ class BamBA : public ::testing::Test { public: void SetUp() override { // Initialization. + g_io_context->restart(); config::applier::init(0, "test_broker", 0); _aply_state = std::make_unique(); diff --git a/broker/bam/test/configuration/applier-boolexp.cc b/broker/bam/test/configuration/applier-boolexp.cc index c5900f2331b..60659b4763c 100644 --- a/broker/bam/test/configuration/applier-boolexp.cc +++ b/broker/bam/test/configuration/applier-boolexp.cc @@ -24,9 +24,12 @@ using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + class ApplierBoolexp : public ::testing::Test { public: void SetUp() override { + g_io_context->restart(); // Initialization. config::applier::init(0, "test_broker", 0); diff --git a/broker/bam/test/monitoring_stream.cc b/broker/bam/test/monitoring_stream.cc index e7e37b83466..d59e8745357 100644 --- a/broker/bam/test/monitoring_stream.cc +++ b/broker/bam/test/monitoring_stream.cc @@ -23,20 +23,26 @@ #include "bbdo/bam/ba_status.hh" #include "com/centreon/broker/config/applier/init.hh" #include "com/centreon/broker/multiplexing/engine.hh" +#include "com/centreon/broker/neb/acknowledgement.hh" using namespace com::centreon::broker; using namespace com::centreon::broker::bam; +extern std::shared_ptr g_io_context; + class BamMonitoringStream : public testing::Test { - void SetUp() override { config::applier::init(0, "test_broker", 0); } + void SetUp() override { + g_io_context->restart(); + config::applier::init(0, "test_broker", 0); + } void TearDown() override { config::applier::deinit(); } }; TEST_F(BamMonitoringStream, WriteKpi) { - database_config cfg("MySQL", "127.0.0.1", "", 3306, "centreon", "centreon", + database_config cfg("MySQL", "127.0.0.1", "", 3306, "root", "centreon", "centreon"); - database_config storage("MySQL", "127.0.0.1", "", 3306, "centreon", - "centreon", "centreon_storage"); + database_config storage("MySQL", "127.0.0.1", "", 3306, "root", "centreon", + "centreon_storage"); std::shared_ptr cache; std::unique_ptr ms; @@ -50,10 +56,10 @@ TEST_F(BamMonitoringStream, WriteKpi) { } TEST_F(BamMonitoringStream, WriteBA) { - database_config cfg("MySQL", "127.0.0.1", "", 3306, "centreon", "centreon", + database_config cfg("MySQL", "127.0.0.1", "", 3306, "root", "centreon", "centreon"); - database_config storage("MySQL", "127.0.0.1", "", 3306, "centreon", - "centreon", "centreon_storage"); + database_config storage("MySQL", "127.0.0.1", "", 3306, "root", "centreon", + "centreon_storage"); ; std::shared_ptr cache; std::unique_ptr ms; @@ -64,3 +70,53 @@ TEST_F(BamMonitoringStream, WriteBA) { ms->write(std::static_pointer_cast(st)); } + +TEST_F(BamMonitoringStream, WorkWithNoPendigMysqlRequest) { + database_config cfg("MySQL", "127.0.0.1", "", 3306, "root", "centreon", + "centreon", 0); + database_config storage("MySQL", "127.0.0.1", "", 3306, "root", "centreon", + "centreon_storage", 0); + ; + std::shared_ptr cache; + std::unique_ptr ms; + + ASSERT_NO_THROW(ms.reset(new monitoring_stream("", cfg, storage, cache))); + + std::shared_ptr st{std::make_shared(ba_status())}; + + ASSERT_EQ(ms->write(std::static_pointer_cast(st)), 1); + + std::shared_ptr dt{ + std::make_shared()}; + + ASSERT_EQ(ms->write(std::static_pointer_cast(dt)), 1); +} + +TEST_F(BamMonitoringStream, WorkWithPendigMysqlRequest) { + database_config cfg("MySQL", "127.0.0.1", "", 3306, "root", "centreon", + "centreon", 5); + database_config storage("MySQL", "127.0.0.1", "", 3306, "root", "centreon", + "centreon_storage", 5); + ; + std::shared_ptr cache; + std::unique_ptr ms; + + ASSERT_NO_THROW(ms.reset(new monitoring_stream("", cfg, storage, cache))); + + std::shared_ptr st{std::make_shared(ba_status())}; + + for (unsigned ii = 0; ii < 4; ++ii) { + ASSERT_EQ(ms->write(std::static_pointer_cast(st)), 0); + } + ASSERT_EQ(ms->write(std::static_pointer_cast(st)), 5); + + std::shared_ptr dt{ + std::make_shared()}; + + ASSERT_EQ(ms->write(std::static_pointer_cast(st)), 0); + for (unsigned ii = 0; ii < 48; ++ii) { + std::cout << ii << std::endl; + ASSERT_EQ(ms->write(std::static_pointer_cast(dt)), 0); + } + ASSERT_EQ(ms->write(std::static_pointer_cast(dt)), 50); +} diff --git a/broker/bam/test/time/check_timeperiod.cc b/broker/bam/test/time/check_timeperiod.cc index 010be0ff3a5..876d49c921d 100644 --- a/broker/bam/test/time/check_timeperiod.cc +++ b/broker/bam/test/time/check_timeperiod.cc @@ -40,6 +40,8 @@ using namespace com::centreon::exceptions; using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + struct options { options() : preferred_time(0), ref_time(0) {} std::vector > period; @@ -141,7 +143,10 @@ static void parse_file(char const* filename, options& opt) { class BamTime : public ::testing::Test { public: - void SetUp() override { config::applier::init(0, "test_broker", 0); } + void SetUp() override { + g_io_context->restart(); + config::applier::init(0, "test_broker", 0); + } void TearDown() override { config::applier::deinit(); } }; diff --git a/broker/core/inc/com/centreon/broker/broker_impl.hh b/broker/core/inc/com/centreon/broker/broker_impl.hh index 459da486a2b..ba9501e0589 100644 --- a/broker/core/inc/com/centreon/broker/broker_impl.hh +++ b/broker/core/inc/com/centreon/broker/broker_impl.hh @@ -97,10 +97,15 @@ class broker_impl final : public Broker::Service { grpc::Status GetLogInfo(grpc::ServerContext* context [[maybe_unused]], const GenericString* request, LogInfo* response) override; + grpc::Status SetLogLevel(grpc::ServerContext* context [[maybe_unused]], const LogLevel* request, ::google::protobuf::Empty*) override; + grpc::Status SetLogFlushPeriod(grpc::ServerContext* context [[maybe_unused]], + const LogFlushPeriod* request, + ::google::protobuf::Empty*) override; + public: void set_broker_name(const std::string& s); }; diff --git a/broker/core/inc/com/centreon/broker/database/mysql_bind.hh b/broker/core/inc/com/centreon/broker/database/mysql_bind.hh index b61b316f4ef..1adac48bca5 100644 --- a/broker/core/inc/com/centreon/broker/database/mysql_bind.hh +++ b/broker/core/inc/com/centreon/broker/database/mysql_bind.hh @@ -54,9 +54,8 @@ class mysql_bind : public mysql_bind_base { * the column contains strings. By default, this value is 0 and * no reservation are made. */ - mysql_bind(int size, int length = 0); + mysql_bind(int size); //, int length = 0); ~mysql_bind() noexcept = default; - void set_size(int size); /** * @brief getter to the int32 value at index range. The type of the column @@ -283,7 +282,6 @@ class mysql_bind : public mysql_bind_base { */ void set_null_tiny(size_t range); - int get_size() const; bool value_is_null(size_t range) const; size_t rows_count() const; diff --git a/broker/core/inc/com/centreon/broker/database/mysql_bind_base.hh b/broker/core/inc/com/centreon/broker/database/mysql_bind_base.hh index a18cf8c09cf..a9699fbb8db 100644 --- a/broker/core/inc/com/centreon/broker/database/mysql_bind_base.hh +++ b/broker/core/inc/com/centreon/broker/database/mysql_bind_base.hh @@ -38,7 +38,6 @@ class mysql_bind_base { bool _prepared(size_t range) const; void _set_typed(uint32_t range); - void _set_empty(bool empty); public: /** @@ -50,9 +49,6 @@ class mysql_bind_base { * @brief Destructor */ virtual ~mysql_bind_base() noexcept = default; - void set_size(int size); - int get_size() const; - const MYSQL_BIND* get_bind() const; MYSQL_BIND* get_bind(); bool empty() const; void set_empty(); diff --git a/broker/core/inc/com/centreon/broker/database/mysql_bind_result.hh b/broker/core/inc/com/centreon/broker/database/mysql_bind_result.hh new file mode 100644 index 00000000000..8e045160bca --- /dev/null +++ b/broker/core/inc/com/centreon/broker/database/mysql_bind_result.hh @@ -0,0 +1,292 @@ +/* +** Copyright 2023 Centreon +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +** +** For more information : contact@centreon.com +*/ + +#ifndef CCB_MYSQL_BIND_RESULT_HH +#define CCB_MYSQL_BIND_RESULT_HH + +#include +#include "com/centreon/broker/database/mysql_bind_base.hh" +#include "com/centreon/broker/database/mysql_column.hh" + +CCB_BEGIN() + +// Forward declarations +class mysql; + +namespace database { +class mysql_bind_result : public mysql_bind_base { + // Each data in the bind. + int _length; + std::vector _is_null; + std::vector _buffer; + + public: + /** + * @brief Default constructor + */ + mysql_bind_result() = delete; + /** + * @brief Constructor + * + * @param size Number of columns in this bind + * @param length Size to reserve for each column's buffer. This is useful when + * the column contains strings. By default, this value is 0 and + * no reservation is made. + */ + mysql_bind_result(int size, int length); + ~mysql_bind_result() noexcept = default; + + /** + * @brief getter to the int32 value at index range. The type of the column + * must be MYSQL_TYPE_LONG. + * + * @param range A non negative integer. + * + * @return An int32 integer. + */ + int value_as_i32(size_t range) const; + /** + * @brief Setter of the value at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_LONG. + * + * @param range A non negative integer. + * @param value The integer value to set. + */ + // void set_value_as_i32(size_t range, int32_t value); + /** + * @brief Setter of NULL at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_LONG. + * + * @param range A non negative integer. + */ + // void set_null_i32(size_t range); + + /** + * @brief getter to the uint32 value at index range. The type of the column + * must be MYSQL_TYPE_LONG. + * + * @param range A non negative integer. + * + * @return An uint32 integer. + */ + uint32_t value_as_u32(size_t range) const; + /** + * @brief Setter of the value at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_LONG. + * + * @param range A non negative integer. + * @param value The unsigned integer value to set. + */ + // void set_value_as_u32(size_t range, uint32_t value); + /** + * @brief Setter of NULL at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_LONG. + * + * @param range A non negative integer. + */ + // void set_null_u32(size_t range); + + /** + * @brief getter to the int64 value at index range. The type of the column + * must be MYSQL_TYPE_LONGLONG. + * + * @param range A non negative integer. + * + * @return An int64 integer. + */ + int64_t value_as_i64(size_t range) const; + /** + * @brief Setter of the value at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_LONGLONG. + * + * @param range A non negative integer. + * @param value The long integer value to set. + */ + // void set_value_as_i64(size_t range, int64_t value); + /** + * @brief Setter of NULL at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_LONGLONG. + * + * @param range A non negative integer. + */ + // void set_null_i64(size_t range); + + /** + * @brief getter to the uint64 value at index range. The type of the column + * must be MYSQL_TYPE_LONGLONG. + * + * @param range A non negative integer. + * + * @return An uint64 integer. + */ + uint64_t value_as_u64(size_t range) const; + /** + * @brief Setter of the value at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_LONGLONG. + * + * @param range A non negative integer. + * @param value The unsigned long integer value to set. + */ + // void set_value_as_u64(size_t range, uint64_t value); + /** + * @brief Setter of NULL at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_LONGLONG. + * + * @param range A non negative integer. + */ + // void set_null_u64(size_t range); + + /** + * @brief getter to the bool value at index range. The type of the column + * must be MYSQL_TYPE_TINY. + * + * @param range A non negative integer. + * + * @return A boolean. + */ + bool value_as_bool(size_t range) const; + // /** + // * @brief Setter of the value at the column at index range and at the + // current + // * row. The type of the column must be MYSQL_TYPE_TINY. + // * + // * @param range A non negative integer. + // * @param value The boolean value to set. + // */ + // void set_value_as_bool(size_t range, bool value); + /** + * @brief Setter of NULL at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_TINY. + * + * @param range A non negative integer. + */ + // void set_null_bool(size_t range); + + /** + * @brief getter to the float value at index range. The type of the column + * must be MYSQL_TYPE_FLOAT. + * + * @param range A non negative integer. + * + * @return A float. + */ + float value_as_f32(size_t range) const; + /** + * @brief Setter of the value at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_FLOAT. + * + * @param range A non negative integer. + * @param value The float value to set. + */ + // void set_value_as_f32(size_t range, float value); + /** + * @brief Setter of NULL at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_FLOAT. + * + * @param range A non negative integer. + */ + // void set_null_f32(size_t range); + + /** + * @brief getter to the double value at index range. The type of the column + * must be MYSQL_TYPE_DOUBLE. + * + * @param range A non negative integer. + * + * @return A double. + */ + double value_as_f64(size_t range) const; + /** + * @brief Setter of the value at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_DOUBLE. + * + * @param range A non negative integer. + * @param value The double value to set. + */ + // void set_value_as_f64(size_t range, double value); + /** + * @brief Setter of NULL at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_DOUBLE. + * + * @param range A non negative integer. + */ + // void set_null_f64(size_t range); + + /** + * @brief getter to the string value at index range. The type of the column + * must be MYSQL_TYPE_STRING. + * + * @param range A non negative integer. + * + * @return A const char* pointer. + */ + const char* value_as_str(size_t range) const; + /** + * @brief Setter of the value at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_STRING. + * + * @param range A non negative integer. + * @param value The string to set. + */ + // void set_value_as_str(size_t range, const fmt::string_view& value); + /** + * @brief Setter of NULL at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_STRING. + * + * @param range A non negative integer. + */ + // void set_null_str(size_t range); + + /** + * @brief getter to the char value at index range. The type of the column + * must be MYSQL_TYPE_TINY. + * + * @param range A non negative integer. + * + * @return A char. + */ + char value_as_tiny(size_t range) const; + /** + * @brief Setter of the value at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_TINY. + * + * @param range A non negative integer. + * @param value The char to set. + */ + // void set_value_as_tiny(size_t range, char value); + /** + * @brief Setter of NULL at the column at index range and at the current + * row. The type of the column must be MYSQL_TYPE_TINY. + * + * @param range A non negative integer. + */ + // void set_null_tiny(size_t range); + + bool value_is_null(size_t range) const; + size_t rows_count() const; + + size_t current_row() const; + void next_row(); + void reserve(size_t size); +}; + +} // namespace database + +CCB_END() + +#endif // CCB_MYSQL_BIND_RESULT_HH diff --git a/broker/core/inc/com/centreon/broker/database/mysql_bulk_bind.hh b/broker/core/inc/com/centreon/broker/database/mysql_bulk_bind.hh index 4565e4f482a..bce3086d978 100644 --- a/broker/core/inc/com/centreon/broker/database/mysql_bulk_bind.hh +++ b/broker/core/inc/com/centreon/broker/database/mysql_bulk_bind.hh @@ -44,15 +44,11 @@ class mysql_bulk_bind : public mysql_bind_base { * @brief Constructor * * @param size Number of columns in this bind - * @param length Size to reserve for each column's buffer. This is useful when - * the column contains strings. By default, this value is 0 and - * no reservation are made. - * @param row_count Number of row to reserve. Columns are not allocated with a - * such size, they are just reserved. + * @param row_count Number of rows to reserve. Columns are not allocated with + * a such size, they are just reserved. */ - mysql_bulk_bind(int size, int length = 0, size_t row_count = 1); + mysql_bulk_bind(int size, size_t reserved_rows_count); ~mysql_bulk_bind() noexcept = default; - void set_size(int size); /** * @brief getter to the int32 value at index range. The type of the column @@ -282,7 +278,6 @@ class mysql_bulk_bind : public mysql_bind_base { int get_size() const; bool value_is_null(size_t range) const; bool empty() const; - void set_empty(); size_t rows_count() const; size_t current_row() const; diff --git a/broker/core/inc/com/centreon/broker/database/mysql_bulk_stmt.hh b/broker/core/inc/com/centreon/broker/database/mysql_bulk_stmt.hh index 7c23f0799ac..153437ad4be 100644 --- a/broker/core/inc/com/centreon/broker/database/mysql_bulk_stmt.hh +++ b/broker/core/inc/com/centreon/broker/database/mysql_bulk_stmt.hh @@ -38,14 +38,12 @@ class mysql_bulk_stmt : public mysql_stmt_base { boost::circular_buffer _hist_size; public: - mysql_bulk_stmt(); - mysql_bulk_stmt(const std::string& query, bool named); mysql_bulk_stmt( const std::string& query, mysql_bind_mapping const& bind_mapping = mysql_bind_mapping()); - mysql_bulk_stmt(mysql_bulk_stmt&& other); + mysql_bulk_stmt(mysql_bulk_stmt&& other) = delete; mysql_bulk_stmt& operator=(const mysql_bulk_stmt&) = delete; - mysql_bulk_stmt& operator=(mysql_bulk_stmt&& other); + mysql_bulk_stmt& operator=(mysql_bulk_stmt&& other)= delete; std::unique_ptr get_bind(); // void operator<<(io::data const& d); @@ -94,15 +92,6 @@ class mysql_bulk_stmt : public mysql_stmt_base { * @param value The value to set. */ void bind_value_as_i64(size_t range, int64_t value); - template - void bind_value_as_i64(size_t range, - int64_t value, - const not_null_predicate& pred) { - if (pred(value)) - bind_value_as_i64(range, value); - else - bind_null_i64(range); - } /** * @brief Set the NULL value at the column in the prepared statement at index * range in the current row of the column. The type of the column must be @@ -121,15 +110,6 @@ class mysql_bulk_stmt : public mysql_stmt_base { * @param value The value to set. */ void bind_value_as_u64(size_t range, uint64_t value); - template - void bind_value_as_u64(size_t range, - uint64_t value, - const not_null_predicate& pred) { - if (pred(value)) - bind_value_as_u64(range, value); - else - bind_null_u64(range); - } /** * @brief Set the NULL value at the column in the prepared statement at index * range in the current row of the column. The type of the column must be diff --git a/broker/core/inc/com/centreon/broker/database/mysql_column.hh b/broker/core/inc/com/centreon/broker/database/mysql_column.hh index e07dbf65738..f02dbac02fb 100644 --- a/broker/core/inc/com/centreon/broker/database/mysql_column.hh +++ b/broker/core/inc/com/centreon/broker/database/mysql_column.hh @@ -28,13 +28,14 @@ CCB_BEGIN() namespace database { class mysql_column { - int _type; + int _type = MYSQL_TYPE_NULL; + size_t _rows_to_reserve = 0; size_t _row_count = 0; int32_t _current_row = 0; /** A vector with data of type _type * Its content corresponds to a database table column. */ - void* _vector; + void* _vector = nullptr; /** A vector of indicators. * Indicators are used in prepared statements. See the MariadDB C connector @@ -72,9 +73,9 @@ class mysql_column { void _push_null_str(); public: - mysql_column(int type = MYSQL_TYPE_LONG, size_t row_count = 0); - mysql_column(mysql_column&& other); - mysql_column& operator=(mysql_column&& other); + mysql_column() = default; + mysql_column(mysql_column&& other) = delete; + mysql_column& operator=(mysql_column&& other) = delete; ~mysql_column() noexcept; int get_type() const; void* get_buffer(); diff --git a/broker/core/inc/com/centreon/broker/database/mysql_error.hh b/broker/core/inc/com/centreon/broker/database/mysql_error.hh index a4672889958..da7eb403db2 100644 --- a/broker/core/inc/com/centreon/broker/database/mysql_error.hh +++ b/broker/core/inc/com/centreon/broker/database/mysql_error.hh @@ -143,8 +143,8 @@ class mysql_error { "could not delete outdated entries from the hosts table: ", "could not delete outdated entries from the modules table: ", "cannot update state of index: ", - "availability thread could not delete the BA availabilities from the " - "reporting database: ", + ("availability thread could not delete the BA availabilities from the " + "reporting database: "), "availability thread could not insert an availability: ", "could not update the list of BAs to rebuild: ", "could not close inconsistent event: ", diff --git a/broker/core/inc/com/centreon/broker/database/mysql_result.hh b/broker/core/inc/com/centreon/broker/database/mysql_result.hh index 311222f061c..4cbde32e11a 100644 --- a/broker/core/inc/com/centreon/broker/database/mysql_result.hh +++ b/broker/core/inc/com/centreon/broker/database/mysql_result.hh @@ -1,5 +1,5 @@ /* -** Copyright 2018 Centreon +** Copyright 2018-2023 Centreon ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ #ifndef CCB_MYSQL_RESULT_HH #define CCB_MYSQL_RESULT_HH -#include "com/centreon/broker/database/mysql_bind.hh" +#include "com/centreon/broker/database/mysql_bind_result.hh" CCB_BEGIN() @@ -33,16 +33,28 @@ namespace database { * delete functionality that must call the mysql_free_result() function. */ class mysql_result { + mysql_connection* _parent; + std::shared_ptr _result; + + // The row contains the result for a simple query (no statement). + MYSQL_ROW _row; + + // the bind and the statement_id are filled in both or empty both. + std::unique_ptr _bind; + int _statement_id; + public: mysql_result(mysql_connection* parent, int statement = 0); mysql_result(mysql_connection* parent, MYSQL_RES* res); mysql_result(mysql_result&& other); - ~mysql_result(); - mysql_result& operator=(mysql_result const& other); + ~mysql_result() noexcept = default; + mysql_result& operator=(mysql_result&& other); + mysql_result& operator=(const mysql_result&) = delete; bool value_as_bool(int idx); float value_as_f32(int idx); double value_as_f64(int idx); - int value_as_i32(int idx); + int32_t value_as_i32(int idx); + char value_as_tiny(int idx); std::string value_as_str(int idx); uint32_t value_as_u32(int idx); int64_t value_as_i64(int idx); @@ -52,24 +64,13 @@ class mysql_result { int get_rows_count() const; void set(MYSQL_RES* res); MYSQL_RES* get(); - void set_bind(std::unique_ptr&& bind); - std::unique_ptr& get_bind(); + void set_bind(std::unique_ptr&& bind); + std::unique_ptr& get_bind(); void set_row(MYSQL_ROW row); int get_statement_id() const; mysql_connection* get_connection(); int get_num_fields() const; char const* get_field_name(int idx) const; - - private: - mysql_connection* _parent; - std::shared_ptr _result; - - // The row contains the result for a simple query (no statement). - MYSQL_ROW _row; - - // the bind and the statement_id are filled in both or empty both. - std::unique_ptr _bind; - int _statement_id; }; } // namespace database diff --git a/broker/core/inc/com/centreon/broker/database/mysql_task.hh b/broker/core/inc/com/centreon/broker/database/mysql_task.hh index 4f40a6fb30a..e1afa75f831 100644 --- a/broker/core/inc/com/centreon/broker/database/mysql_task.hh +++ b/broker/core/inc/com/centreon/broker/database/mysql_task.hh @@ -173,7 +173,7 @@ class mysql_task_statement : public mysql_task { bulk = false; } } - int statement_id; + uint32_t statement_id; int param_count; std::unique_ptr bind; mysql_error::code error_code; @@ -184,8 +184,10 @@ class mysql_task_statement : public mysql_task { class mysql_task_statement_res : public mysql_task { public: mysql_task_statement_res(database::mysql_stmt_base& stmt, + size_t length, std::promise&& promise) : mysql_task(mysql_task::STATEMENT_RES), + length(length), promise(std::move(promise)), statement_id(stmt.get_id()), param_count(stmt.get_param_count()) { @@ -201,8 +203,9 @@ class mysql_task_statement_res : public mysql_task { } } + size_t length; std::promise promise; - int statement_id; + uint32_t statement_id; int param_count; std::unique_ptr bind; bool bulk; @@ -237,7 +240,7 @@ class mysql_task_statement_int : public mysql_task { } std::promise promise; int_type return_type; - int statement_id; + uint32_t statement_id; int param_count; std::unique_ptr bind; bool bulk; diff --git a/broker/core/inc/com/centreon/broker/exceptions/timeout.hh b/broker/core/inc/com/centreon/broker/exceptions/timeout.hh index 6ced3ebde65..3b944dba2fa 100644 --- a/broker/core/inc/com/centreon/broker/exceptions/timeout.hh +++ b/broker/core/inc/com/centreon/broker/exceptions/timeout.hh @@ -33,6 +33,7 @@ namespace exceptions { class timeout : public std::exception { public: timeout() noexcept : std::exception() {} + timeout(const timeout& e) : std::exception(e) {} timeout& operator=(const timeout&) = delete; }; } // namespace exceptions diff --git a/broker/core/inc/com/centreon/broker/file/splitter.hh b/broker/core/inc/com/centreon/broker/file/splitter.hh index 8bf38ead976..d3460efa2b9 100644 --- a/broker/core/inc/com/centreon/broker/file/splitter.hh +++ b/broker/core/inc/com/centreon/broker/file/splitter.hh @@ -38,47 +38,46 @@ namespace file { * each time a new file is created. * * We don't want to lock accesses to those files each time a reading or a - * writing is done. But we must lock access when we read and write on the same - * file. To aquieve this, we introduce two mutexes, _mutex1 and _mutex2. At - * the beginning, when the splitter is open for an action (read or write), - * there are two cases: - * * The file we want is not already open, so we open it and we associate one - * mutex to it (for reading, mutex1 is chosen and we set its pointer to - * _rmutex, whereas for writing, mutex2 is chosen and set to _wmutex). - * * The file to access is already open. We get the same file and we get the - * same mutex pointer. + * writing is done. But we must lock accesses when: + * * we read and write on the same file. + * * we write and write on the same file. + * + * To aquieve this, all write operations are protected by a single mutex + * _write_m. When a new sub-file is created, when we write to a file, this + * mutex is locked. + * + * We can have several producers, that's why all write operations are + * protected. But we only have one consumer. Two possibilities, the reader + * reads a file already written and there is no need to have a protection. + * The current read file is also written, and it is mandatory to protect the + * access. So in the read method the _write_m mutex is locked only when we + * read in the same file as we write into. * * _rid and _wid are indexes to the current files, _rid for reading and _wid * for writing., _rfile and _wfile are shared pointers to FILE structs. + * _wid is also atomic so that read can read it without any protection. * * _base_path is the base name of files, it may be followed by a number that * is _rid or _wid. * * _woffset and _roffset are offsets from the files begin to write or read. - * - * A third mutex id_m is set essentially when it is time to open a file. It - * is the _wid and _rid lock. _rmutex and _wmutex are set while it is locked. - * - * FIXME: Maybe a better algorithm would allow us to avoid it. */ class splitter : public fs_file { bool _auto_delete; std::string _base_path; const uint32_t _max_file_size; + std::shared_ptr _rfile; - std::mutex* _rmutex; int32_t _rid; long _roffset; + + std::mutex _write_m; std::shared_ptr _wfile; - std::mutex* _wmutex; - int32_t _wid; + std::atomic_int _wid; long _woffset; - std::mutex _mutex1; - std::mutex _mutex2; - std::mutex _id_m; void _open_read_file(); - void _open_write_file(); + bool _open_write_file(); public: splitter(const std::string& path, diff --git a/broker/core/inc/com/centreon/broker/log_v2.hh b/broker/core/inc/com/centreon/broker/log_v2.hh index 6f975d4709a..df21161d926 100644 --- a/broker/core/inc/com/centreon/broker/log_v2.hh +++ b/broker/core/inc/com/centreon/broker/log_v2.hh @@ -1,5 +1,5 @@ /* -** Copyright 2020-2022 Centreon +** Copyright 2020-2023 Centreon ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -20,11 +20,20 @@ #include "com/centreon/broker/config/state.hh" #include "com/centreon/broker/namespace.hh" +#include "com/centreon/engine/log_v2_base.hh" CCB_BEGIN() -class log_v2 { - std::string _log_name; +class log_v2 : public com::centreon::engine::log_v2_base { + std::array, 17> _log; + std::atomic_bool _running; + asio::system_timer _flush_timer; + std::mutex _flush_timer_m; + bool _flush_timer_active; + std::shared_ptr _io_context; + + static std::shared_ptr _instance; + enum logger { log_bam, log_bbdo, @@ -45,35 +54,94 @@ class log_v2 { log_tls, }; - std::array, 17> _log; - std::atomic_bool _running; std::mutex _load_m; - log_v2(); - ~log_v2(); + log_v2(const std::shared_ptr& io_context); + + std::shared_ptr get_logger(logger log_type, + const char* log_str); + + void start_flush_timer(spdlog::sink_ptr sink); public: - static log_v2& instance(); + ~log_v2() noexcept; + + void stop_flush_timer(); void apply(const config::state& conf); - const std::string& log_name() const; - - static std::shared_ptr bam(); - static std::shared_ptr bbdo(); - static std::shared_ptr config(); - static std::shared_ptr core(); - static std::shared_ptr influxdb(); - static std::shared_ptr graphite(); - static std::shared_ptr notification(); - static std::shared_ptr rrd(); - static std::shared_ptr stats(); - static std::shared_ptr lua(); - static std::shared_ptr neb(); - static std::shared_ptr perfdata(); - static std::shared_ptr processing(); - static std::shared_ptr sql(); - static std::shared_ptr tcp(); - static std::shared_ptr tls(); - static std::shared_ptr grpc(); + + static void load(const std::shared_ptr& io_context); + + static std::shared_ptr instance(); + void set_flush_interval(unsigned second_flush_interval); + + static inline std::shared_ptr bam() { + return _instance->get_logger(log_bam, "bam"); + } + + static inline std::shared_ptr bbdo() { + return _instance->get_logger(log_bbdo, "bbdo"); + } + + static inline std::shared_ptr config() { + return _instance->get_logger(log_config, "config"); + } + + static inline std::shared_ptr core() { + return _instance->get_logger(log_core, "core"); + } + + static inline std::shared_ptr influxdb() { + return _instance->get_logger(log_influxdb, "influxdb"); + } + + static inline std::shared_ptr graphite() { + return _instance->get_logger(log_graphite, "graphite"); + } + + static inline std::shared_ptr notification() { + return _instance->get_logger(log_notification, "notification"); + } + + static inline std::shared_ptr rrd() { + return _instance->get_logger(log_rrd, "rrd"); + } + + static inline std::shared_ptr stats() { + return _instance->get_logger(log_stats, "stats"); + } + + static inline std::shared_ptr lua() { + return _instance->get_logger(log_lua, "lua"); + } + + static inline std::shared_ptr neb() { + return _instance->get_logger(log_neb, "neb"); + } + + static inline std::shared_ptr perfdata() { + return _instance->get_logger(log_perfdata, "perfdata"); + } + + static inline std::shared_ptr processing() { + return _instance->get_logger(log_processing, "processing"); + } + + static inline std::shared_ptr sql() { + return _instance->get_logger(log_sql, "sql"); + } + + static inline std::shared_ptr tcp() { + return _instance->get_logger(log_tcp, "tcp"); + } + + static inline std::shared_ptr tls() { + return _instance->get_logger(log_tls, "tls"); + } + + static inline std::shared_ptr grpc() { + return _instance->get_logger(log_grpc, "grpc"); + } + static bool contains_logger(const std::string& logger); static bool contains_level(const std::string& level); std::vector> levels() const; diff --git a/broker/core/inc/com/centreon/broker/multiplexing/engine.hh b/broker/core/inc/com/centreon/broker/multiplexing/engine.hh index 50e4bade047..13b5f135c1e 100644 --- a/broker/core/inc/com/centreon/broker/multiplexing/engine.hh +++ b/broker/core/inc/com/centreon/broker/multiplexing/engine.hh @@ -28,6 +28,9 @@ CCB_BEGIN() namespace multiplexing { // Forward declaration. class muxer; +namespace detail { +class callback_caller; +} // namespace detail /** * @class engine engine.hh "com/centreon/broker/multiplexing/engine.hh" @@ -60,13 +63,15 @@ class muxer; * * @see muxer */ -class engine { +class engine : public std::enable_shared_from_this { static std::mutex _load_m; static std::shared_ptr _instance; enum state { not_started, running, stopped }; + + using send_to_mux_callback_type = std::function; + state _state; - asio::io_context::strand _strand; std::unique_ptr _cache_file; @@ -77,7 +82,8 @@ class engine { std::deque> _kiew; // Subscriber. - std::vector _muxers; + std::vector> _muxers; + std::mutex _muxers_m; // Statistics. EngineStats* _stats; @@ -87,10 +93,12 @@ class engine { engine(); std::string _cache_file_path() const; - void _send_to_subscribers(); + bool _send_to_subscribers(send_to_mux_callback_type&& callback); void (engine::*_write_func)(std::shared_ptr const&); + friend class detail::callback_caller; + public: static void load(); static void unload(); @@ -105,8 +113,8 @@ class engine { void publish(const std::list>& to_publish); void start(); void stop(); - void subscribe(muxer* subscriber); - void unsubscribe(muxer* subscriber); + void subscribe(const std::shared_ptr& subscriber); + void unsubscribe(const muxer* subscriber); }; } // namespace multiplexing diff --git a/broker/core/inc/com/centreon/broker/multiplexing/muxer.hh b/broker/core/inc/com/centreon/broker/multiplexing/muxer.hh index 5d2139c19bd..05dc633e41c 100644 --- a/broker/core/inc/com/centreon/broker/multiplexing/muxer.hh +++ b/broker/core/inc/com/centreon/broker/multiplexing/muxer.hh @@ -19,6 +19,7 @@ #ifndef CCB_MULTIPLEXING_MUXER_HH #define CCB_MULTIPLEXING_MUXER_HH +#include #include "com/centreon/broker/multiplexing/engine.hh" #include "com/centreon/broker/persistent_file.hh" @@ -47,7 +48,7 @@ namespace multiplexing { * * @see engine */ -class muxer : public io::stream { +class muxer : public io::stream, public std::enable_shared_from_this { public: using filters = absl::flat_hash_set; @@ -56,10 +57,10 @@ class muxer : public io::stream { const std::string _name; const std::string _queue_file_name; - const filters _read_filters; - const filters _write_filters; - const std::string _read_filters_str; - const std::string _write_filters_str; + filters _read_filters; + filters _write_filters; + std::string _read_filters_str; + std::string _write_filters_str; const bool _persistent; std::unique_ptr _file; @@ -70,27 +71,35 @@ class muxer : public io::stream { std::list>::iterator _pos; std::time_t _last_stats; + static std::mutex _running_muxers_m; + static absl::flat_hash_map> _running_muxers; + void _clean(); void _get_event_from_file(std::shared_ptr& event); void _push_to_queue(std::shared_ptr const& event); void _update_stats(void) noexcept; + muxer(std::string name, + muxer::filters r_filters, + muxer::filters w_filters, + bool persistent = false); + public: static std::string queue_file(const std::string& name); static std::string memory_file(const std::string& name); static void event_queue_max_size(uint32_t max) noexcept; static uint32_t event_queue_max_size() noexcept; - muxer(std::string name, - muxer::filters r_filters, - muxer::filters w_filters, - bool persistent = false); + static std::shared_ptr create(std::string name, + muxer::filters r_filters, + muxer::filters w_filters, + bool persistent = false); muxer(const muxer&) = delete; muxer& operator=(const muxer&) = delete; ~muxer() noexcept; void ack_events(int count); - void publish(std::shared_ptr const event); + void publish(const std::deque>& event); bool read(std::shared_ptr& event, time_t deadline) override; const filters& read_filters() const; const filters& write_filters() const; @@ -104,6 +113,7 @@ class muxer : public io::stream { int32_t write(std::shared_ptr const& d) override; int32_t stop() override; const std::string& name() const; + void set_filters(muxer::filters r_filters, muxer::filters w_filters); }; } // namespace multiplexing diff --git a/broker/core/inc/com/centreon/broker/mysql.hh b/broker/core/inc/com/centreon/broker/mysql.hh index 4e187bca27e..9c8766cfdbc 100644 --- a/broker/core/inc/com/centreon/broker/mysql.hh +++ b/broker/core/inc/com/centreon/broker/mysql.hh @@ -58,7 +58,8 @@ class mysql { int run_statement_and_get_result( database::mysql_stmt& stmt, std::promise&& promise, - int thread_id = -1); + int thread_id, /* = -1, */ + size_t length /*= 200*/); template int run_statement_and_get_int(database::mysql_stmt& stmt, @@ -78,7 +79,6 @@ class mysql { bool fetch_row(database::mysql_result& res); int get_last_insert_id(int thread_id); int connections_count() const; - bool commit_if_needed(); int choose_connection_by_name(std::string const& name); int choose_connection_by_instance(int instance_id) const; int choose_best_connection(int32_t type); diff --git a/broker/core/inc/com/centreon/broker/mysql_connection.hh b/broker/core/inc/com/centreon/broker/mysql_connection.hh index b7163ddd1b2..3390a3c2f11 100644 --- a/broker/core/inc/com/centreon/broker/mysql_connection.hh +++ b/broker/core/inc/com/centreon/broker/mysql_connection.hh @@ -137,11 +137,7 @@ class mysql_connection { void _clear_connection(); void _update_stats() noexcept; - inline void set_need_to_commit() { - if (_qps > 1) { - _need_commit = true; - } - } + inline void set_need_to_commit() { _need_commit = true; } public: /**************************************************************************/ @@ -168,7 +164,8 @@ class mysql_connection { bool fatal); void run_statement_and_get_result( database::mysql_stmt& stmt, - std::promise&& promise); + std::promise&& promise, + size_t length); template void run_statement_and_get_int(database::mysql_stmt& stmt, diff --git a/broker/core/inc/com/centreon/broker/persistent_file.hh b/broker/core/inc/com/centreon/broker/persistent_file.hh index 3989b867e90..138b1d60caf 100644 --- a/broker/core/inc/com/centreon/broker/persistent_file.hh +++ b/broker/core/inc/com/centreon/broker/persistent_file.hh @@ -34,7 +34,6 @@ CCB_BEGIN() * It uses BBDO, compression and file streams. */ class persistent_file : public io::stream { - QueueFileStats* _stats; std::shared_ptr _splitter; public: diff --git a/broker/core/inc/com/centreon/broker/pool.hh b/broker/core/inc/com/centreon/broker/pool.hh index 9bec373be4f..791a765f811 100644 --- a/broker/core/inc/com/centreon/broker/pool.hh +++ b/broker/core/inc/com/centreon/broker/pool.hh @@ -45,8 +45,9 @@ CCB_BEGIN() * initialized with the number of cpus on the host computer. * * To post tasks to the pool, we use the ASIO api, for that we need an - * asio::io_context and an asio::io_service::work which are defined when then - * pool is constructed. + * asio::io_context witch is g_io_context instanciated in both main of engine + * and cbd and an asio::io_service::work which are defined when then pool is + * constructed. * * There is a _closed boolean variable used internally to know if the pool is * running (and not closed) or stopped (and closed). To work with it, we also @@ -67,7 +68,7 @@ class pool { ThreadPool* _stats; const size_t _pool_size; - asio::io_context _io_context; + std::shared_ptr _io_context; asio::executor_work_guard _worker; std::vector _pool; bool _closed; @@ -76,7 +77,7 @@ class pool { asio::steady_timer _timer; std::atomic_bool _stats_running; - pool(size_t size); + pool(const std::shared_ptr& io_context, size_t size); ~pool() noexcept; void _stop(); void _check_latency(asio::error_code ec); @@ -85,7 +86,8 @@ class pool { pool(const pool&) = delete; pool& operator=(const pool&) = delete; - static void load(size_t size); + static void load(const std::shared_ptr& io_context, + size_t size); static void unload(); static pool& instance(); static asio::io_context& io_context(); diff --git a/broker/core/inc/com/centreon/broker/processing/feeder.hh b/broker/core/inc/com/centreon/broker/processing/feeder.hh index e1c6cc7691f..29f5d5c2e67 100644 --- a/broker/core/inc/com/centreon/broker/processing/feeder.hh +++ b/broker/core/inc/com/centreon/broker/processing/feeder.hh @@ -51,7 +51,9 @@ class feeder : public stat_visitable { std::atomic_bool _should_exit; std::unique_ptr _client; - multiplexing::muxer _muxer; + // as the muxer may be embeded in a lambda run by asio thread, we use a + // shared_ptr + std::shared_ptr _muxer; // This mutex is used for the stat thread. mutable misc::shared_mutex _client_m; diff --git a/broker/core/src/broker.proto b/broker/core/src/broker.proto index 0933768c2f5..07af9d13578 100644 --- a/broker/core/src/broker.proto +++ b/broker/core/src/broker.proto @@ -58,14 +58,25 @@ service Broker { */ rpc GetLogInfo(GenericString) returns (LogInfo) {} + /** - * @brief Set a new level to the given logger. - * - * @param A message with a logger and a level as strings. - * - * @return nothing. - */ + * @brief Set level of a logger. + * + * @param A message with a logger and a level as strings. + * + * @return nothing. + */ rpc SetLogLevel(LogLevel) returns (google.protobuf.Empty) {} + + /** + * @brief Set log flush period of all loggers. + * + * @param period in second, 0 means flush all the times + * + * @return nothing. + */ + rpc SetLogFlushPeriod(LogFlushPeriod) returns (google.protobuf.Empty) {} + } message Version { @@ -75,13 +86,28 @@ message Version { } message LogInfo { - string log_file = 1; - map level = 2; + string log_name = 1; + string log_file = 2; + uint32 log_flush_period = 3; + map level = 4; } message LogLevel { - string logger = 1; - string level = 2; + string name = 1; + enum LogLevelEnum { + TRACE = 0; + DEBUG = 1; + INFO = 2; + WARNING = 3; + ERROR = 4; + CRITICAL = 5; + OFF = 6; + } + LogLevelEnum level = 2; +} + +message LogFlushPeriod { + uint32 period = 1; } message GenericString { diff --git a/broker/core/src/broker_impl.cc b/broker/core/src/broker_impl.cc index 5b6ef740ead..1a65720ac5c 100644 --- a/broker/core/src/broker_impl.cc +++ b/broker/core/src/broker_impl.cc @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 Centreon (https://www.centreon.com/) + * Copyright 2020-2023 Centreon (https://www.centreon.com/) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -279,8 +279,11 @@ grpc::Status broker_impl::GetLogInfo(grpc::ServerContext* context LogInfo* response) { auto& name{request->str_arg()}; auto& map = *response->mutable_level(); - auto lvs = log_v2::instance().levels(); - response->set_log_file(log_v2::instance().log_name()); + auto lvs = log_v2::instance()->levels(); + response->set_log_name(log_v2::instance()->log_name()); + response->set_log_file(log_v2::instance()->file_path()); + response->set_log_flush_period( + log_v2::instance()->get_flush_interval().count()); if (!name.empty()) { auto found = std::find_if(lvs.begin(), lvs.end(), [&name](std::pair& p) { @@ -304,12 +307,33 @@ grpc::Status broker_impl::SetLogLevel(grpc::ServerContext* context [[maybe_unused]], const LogLevel* request, ::google::protobuf::Empty*) { - const std::string& logger_name{request->logger()}; - const std::string& level{request->level()}; - try { - log_v2::instance().set_level(logger_name, level); - } catch (const std::exception& e) { - return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, e.what()); + const std::string& logger_name{request->name()}; + std::shared_ptr logger = spdlog::get(logger_name); + if (!logger) { + std::string err_detail = fmt::format("unknow logger:{}", logger_name); + SPDLOG_LOGGER_ERROR(log_v2::core(), err_detail); + return grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, err_detail); + } else { + logger->set_level(spdlog::level::level_enum(request->level())); + return grpc::Status::OK; } +} + +grpc::Status broker_impl::SetLogFlushPeriod(grpc::ServerContext* context + [[maybe_unused]], + const LogFlushPeriod* request, + ::google::protobuf::Empty*) { + bool done = false; + spdlog::apply_all([&](const std::shared_ptr logger) { + if (!done) { + std::shared_ptr logger_base = + std::dynamic_pointer_cast( + logger); + if (logger_base) { + logger_base->get_parent()->set_flush_interval(request->period()); + done = true; + } + } + }); return grpc::Status::OK; } diff --git a/broker/core/src/compression/stream.cc b/broker/core/src/compression/stream.cc index 9017ddc83ea..4b00057800f 100644 --- a/broker/core/src/compression/stream.cc +++ b/broker/core/src/compression/stream.cc @@ -87,14 +87,16 @@ bool stream::read(std::shared_ptr& data, time_t deadline) { unsigned char const* buff((unsigned char const*)_rbuffer.data()); size = static_cast((buff[0] << 24) | (buff[1] << 16) | (buff[2] << 8) | (buff[3])); + log_v2::core()->trace( + "extract size: {} from {:02X} {:02X} {:02X} {:02X}", size, + buff[0], buff[1], buff[2], buff[3]); } // Check if size is within bounds. if (size <= 0 || size > max_data_size) { // Skip corrupted data, one byte at a time. log_v2::core()->error( - "compression: stream got corrupted packet size of {} bytes, not " - "in " + "compression: stream got corrupted packet size of {} bytes, not in " "the 0-{} range, skipping next byte", size, max_data_size); if (!skipped) @@ -160,9 +162,9 @@ bool stream::read(std::shared_ptr& data, time_t deadline) { } catch (exceptions::shutdown const& e) { _shutdown = true; if (!_wbuffer.empty()) { - std::shared_ptr r(new io::raw); - r.get()->get_buffer() = _wbuffer; - data = r; + auto r = std::make_shared(); + r.get()->get_buffer() = std::move(_wbuffer); + data = std::move(r); _wbuffer.clear(); } else throw; diff --git a/broker/core/src/config/applier/endpoint.cc b/broker/core/src/config/applier/endpoint.cc index e8abcccc73d..0c4b9c066d1 100644 --- a/broker/core/src/config/applier/endpoint.cc +++ b/broker/core/src/config/applier/endpoint.cc @@ -99,16 +99,14 @@ endpoint::~endpoint() { void endpoint::apply(std::list const& endpoints) { // Log messages. log_v2::config()->info("endpoint applier: loading configuration"); - log_v2::config()->debug("endpoint applier: {} endpoints to apply", - endpoints.size()); { std::vector eps; for (auto& ep : endpoints) eps.push_back(ep.name); - log_v2::core()->debug("endpoint applier: {} endpoints to apply: {}", - endpoints.size(), - fmt::format("{}", fmt::join(eps, ", "))); + log_v2::config()->debug("endpoint applier: {} endpoints to apply: {}", + endpoints.size(), + fmt::format("{}", fmt::join(eps, ", "))); } // Copy endpoint configurations and apply eventual modifications. @@ -128,7 +126,9 @@ void endpoint::apply(std::list const& endpoints) { // resources that might be used by other endpoints. auto it = _endpoints.find(ep); if (it != _endpoints.end()) { - log_v2::core()->debug("removing old endpoint {}", it->first.name); + log_v2::config()->debug("endpoint applier: removing old endpoint {}", + it->first.name); + /* failover::exit() is called. */ it->second->exit(); delete it->second; _endpoints.erase(it); @@ -138,13 +138,14 @@ void endpoint::apply(std::list const& endpoints) { // Update existing endpoints. for (auto it = _endpoints.begin(), end = _endpoints.end(); it != end; ++it) { - log_v2::core()->debug("updating endpoint {}", it->first.name); + log_v2::config()->debug("endpoint applier: updating endpoint {}", + it->first.name); it->second->update(); } // Debug message. - log_v2::core()->debug("endpoint applier: {} endpoints to create", - endp_to_create.size()); + log_v2::config()->debug("endpoint applier: {} endpoints to create", + endp_to_create.size()); // Create new endpoints. for (config::endpoint& ep : endp_to_create) { @@ -153,11 +154,8 @@ void endpoint::apply(std::list const& endpoints) { if (ep.name.empty() || std::find_if(endp_to_create.begin(), endp_to_create.end(), name_match_failover(ep.name)) == endp_to_create.end()) { - log_v2::core()->debug("creating endpoint {}", ep.name); - // Create muxer and endpoint. - auto mux{std::make_shared( - ep.name, _filters(ep.read_filters), _filters(ep.write_filters), - true)}; + log_v2::config()->debug("endpoint applier: creating endpoint {}", + ep.name); bool is_acceptor; std::shared_ptr e{_create_endpoint(ep, is_acceptor)}; std::unique_ptr endp; @@ -180,8 +178,12 @@ void endpoint::apply(std::list const& endpoints) { acceptr->set_read_filters(_filters(ep.read_filters)); acceptr->set_write_filters(_filters(ep.write_filters)); endp.reset(acceptr.release()); - } else + } else { + // Create muxer and endpoint. + auto mux{multiplexing::muxer::create(ep.name, _filters(ep.read_filters), + _filters(ep.write_filters), true)}; endp.reset(_create_failover(ep, mux, e, endp_to_create)); + } { std::lock_guard lock(_endpointsm); _endpoints[ep] = endp.get(); @@ -541,7 +543,7 @@ absl::flat_hash_set endpoint::_filters( it = tmp_elements.cbegin(), end = tmp_elements.cend(); it != end; ++it) { - log_v2::config()->debug("endpoint applier: new filtering element: {}", + log_v2::config()->trace("endpoint applier: new filtering element: {}", it->first); elements.insert(it->first); retval = true; diff --git a/broker/core/src/config/applier/init.cc b/broker/core/src/config/applier/init.cc index 81519a099d0..f08bc99060f 100644 --- a/broker/core/src/config/applier/init.cc +++ b/broker/core/src/config/applier/init.cc @@ -49,6 +49,9 @@ using namespace com::centreon::broker; std::atomic config::applier::mode{not_started}; +extern std::shared_ptr g_io_context; +extern bool g_io_context_started; + /** * @brief Load necessary structures. It initializes exactly the same structures * as init(const config::state& conf) just with detailed parameters. @@ -60,7 +63,8 @@ void config::applier::init(size_t n_thread, const std::string&, size_t event_queues_total_size) { // Load singletons. - pool::load(n_thread); + pool::load(g_io_context, n_thread); + g_io_context_started = true; stats::center::load(); mysql_manager::load(); config::applier::state::load(); @@ -78,10 +82,11 @@ void config::applier::init(size_t n_thread, void config::applier::deinit() { mode = finished; config::applier::endpoint::unload(); - std::shared_ptr engine_instance = - multiplexing::engine::instance_ptr(); - if (engine_instance) { - engine_instance->stop(); + { + auto eng = multiplexing::engine::instance_ptr(); + if (eng) { + multiplexing::engine::unload(); + } } config::applier::state::unload(); io::events::unload(); @@ -89,9 +94,7 @@ void config::applier::deinit() { mysql_manager::unload(); stats::center::unload(); file::disk_accessor::unload(); - if (engine_instance) { - multiplexing::engine::unload(); - } + pool::unload(); } diff --git a/broker/core/src/database/mysql_bind.cc b/broker/core/src/database/mysql_bind.cc index 70dd4062862..b8174f8a286 100644 --- a/broker/core/src/database/mysql_bind.cc +++ b/broker/core/src/database/mysql_bind.cc @@ -27,30 +27,18 @@ using namespace com::centreon::broker; using namespace com::centreon::broker::database; -mysql_bind::mysql_bind(int size, int length) +mysql_bind::mysql_bind(int size) //, int length) : mysql_bind_base(size), _buffer(size) { - if (length) { - for (int i = 0; i < size; ++i) { - _bind[i].buffer_type = MYSQL_TYPE_STRING; - _buffer[i] = ""; - absl::get(_buffer[i]).reserve(length); - std::string* s = absl::get_if(&_buffer[i]); - _bind[i].buffer = const_cast(s->data()); - _bind[i].buffer_length = length; - } - } -} - -/** - * @brief Set the size of the bind, that is to say the number of columns. - * - * @param size An integer. - */ -void mysql_bind::set_size(int size) { - _bind.resize(size); - _buffer.resize(size); - for (int i = 0; i < size; ++i) - _bind[i].buffer = get_value_pointer(i); + // if (length) { + // for (int i = 0; i < size; ++i) { + // _bind[i].buffer_type = MYSQL_TYPE_STRING; + // _buffer[i] = ""; + // absl::get(_buffer[i]).reserve(length); + // std::string* s = absl::get_if(&_buffer[i]); + // _bind[i].buffer = const_cast(s->data()); + // _bind[i].buffer_length = length; + // } + // } } void* mysql_bind::get_value_pointer(size_t range) { @@ -142,7 +130,6 @@ VALUE(f64, double, MYSQL_TYPE_DOUBLE) VALUE(str, const char*, MYSQL_TYPE_STRING) SET_VALUE(tiny, char, MYSQL_TYPE_TINY, false) VALUE(tiny, char, MYSQL_TYPE_TINY) -// SET_VALUE(bool, bool, MYSQL_TYPE_TINY, false) VALUE(bool, bool, MYSQL_TYPE_TINY) #undef SET_VALUE @@ -173,94 +160,3 @@ void mysql_bind::set_null_str(size_t range) { bool mysql_bind::value_is_null(size_t range) const { return _bind[range].buffer_type == MYSQL_TYPE_NULL; } - -/** - * @brief A debug function to display the content of this bind. - */ -// void mysql_bind::debug() { -// std::cout << "DEBUG BIND " << this << std::endl; -// int size = _bind.size(); -// for (int i = 0; i < size; ++i) { -// switch (_bind[i].buffer_type) { -// case MYSQL_TYPE_LONGLONG: { -// std::cout << "LONGLONG : " -// << "BL: " << _bind[i].buffer_length << " NULL: " -// << (_bind[i].buffer_type == MYSQL_TYPE_NULL ? "1" : "0") -// << " : " -// << get_value_pointer(i) -// << std::endl; -// } break; -// case MYSQL_TYPE_LONG: { -// std::cout << "LONG : " -// << "BL: " << _bind[i].buffer_length << " NULL: " -// << (_bind[i].buffer_type == MYSQL_TYPE_NULL ? "1" : "0") -// << " : " -// << get_value_pointer(i) -// << std::endl; -// } break; -// case MYSQL_TYPE_TINY: { -// std::cout << "TINY : " -// << "BL: " << _bind[i].buffer_length << " NULL: " -// << (_bind[i].buffer_type == MYSQL_TYPE_NULL ? "1" : "0") -// << " : " -// << get_value_pointer(i) -// << std::endl; -// } break; -// case MYSQL_TYPE_NULL: -// std::cout << "NULL : " -// << "BL: " << _bind[i].buffer_length; -// break; -// case MYSQL_TYPE_ENUM: -// std::cout << "ENUM : " -// << "BL: " << _bind[i].buffer_length << " : " -// << static_cast(_column[i].get_buffer())[j] -// << std::endl; -// break; -// case MYSQL_TYPE_STRING: -// std::cout << "STRING : " -// << "BL: " << _bind[i].buffer_length << " NULL: " -// << (_bind[i].u.indicator[j] == STMT_INDICATOR_NULL ? "1" -// : "0") -// << " : " << -// static_cast(_column[i].get_buffer())[j] -// << std::endl; -// break; -// case MYSQL_TYPE_DOUBLE: { -// std::cout << "DOUBLE : " -// "BL: " -// << _bind[i].buffer_length << " NULL: " -// << (_bind[i].u.indicator[j] == STMT_INDICATOR_NULL ? "1" -// : "0") -// << " : " << -// static_cast(_column[i].get_buffer())[j] -// << std::endl; -// } break; -// case MYSQL_TYPE_FLOAT: { -// std::cout << "FLOAT : " -// "BL: " -// << _bind[i].buffer_length << " NULL: " -// << (_bind[i].u.indicator[j] == STMT_INDICATOR_NULL ? "1" -// : "0") -// << " : " << -// static_cast(_column[i].get_buffer())[j] -// << std::endl; -// } break; -// default: -// std::cout << _bind[i].buffer_type -// << " : BL: " << _bind[i].buffer_length << " : " -// << "TYPE NOT MANAGED...\n"; -// assert(1 == 0); // Should not arrive... -// break; -// } -// std::cout << std::endl; -// } -// } - -/** - * @brief Accessor to the number of columns in this bind. - * - * @return An integer. - */ -int mysql_bind::get_size() const { - return _bind.size(); -} diff --git a/broker/core/src/database/mysql_bind_base.cc b/broker/core/src/database/mysql_bind_base.cc index ed4c514bf75..66be0ee4f03 100644 --- a/broker/core/src/database/mysql_bind_base.cc +++ b/broker/core/src/database/mysql_bind_base.cc @@ -35,15 +35,6 @@ using namespace com::centreon::broker::database; */ mysql_bind_base::mysql_bind_base(int size) : _typed(size), _bind(size) {} -/** - * @brief Set the size of the bind, that is to say the number of columns. - * - * @param size An integer. - */ -void mysql_bind_base::set_size(int size) { - _bind.resize(size); -} - /** * @brief Return a boolean telling if the column at index range has been * prepared or not. A column is prepared when its column has a type defined. @@ -56,16 +47,6 @@ bool mysql_bind_base::_prepared(size_t range) const { return _typed[range]; } -/** - * @brief Accessor to the MYSQL_BIND* contained in this mysql_bind_base. This is - * useful to call the MariaDB C connector functions. - * - * @return A MYSQL_BIND* pointer. - */ -const MYSQL_BIND* mysql_bind_base::get_bind() const { - return &_bind[0]; -} - /** * @brief Accessor to the MYSQL_BIND* contained in this mysql_bind_base. This is * useful to call the MariaDB C connector functions. @@ -81,19 +62,12 @@ MYSQL_BIND* mysql_bind_base::get_bind() { * * @return An integer. */ -int mysql_bind_base::get_size() const { - return _bind.size(); -} void mysql_bind_base::_set_typed(uint32_t range) { _empty = false; _typed[range] = true; } -void mysql_bind_base::_set_empty(bool empty) { - _empty = empty; -} - void mysql_bind_base::set_empty() { _empty = true; } diff --git a/broker/core/src/database/mysql_bind_result.cc b/broker/core/src/database/mysql_bind_result.cc new file mode 100644 index 00000000000..23589dd8da3 --- /dev/null +++ b/broker/core/src/database/mysql_bind_result.cc @@ -0,0 +1,156 @@ +/* +** Copyright 2023 Centreon +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +** +** For more information : contact@centreon.com +*/ + +#include "com/centreon/broker/database/mysql_bind_result.hh" + +#include + +#include "com/centreon/broker/io/events.hh" +#include "com/centreon/broker/mapping/entry.hh" +#include "com/centreon/broker/mysql.hh" + +using namespace com::centreon::broker; +using namespace com::centreon::broker::database; + +mysql_bind_result::mysql_bind_result(int size, int length) + : mysql_bind_base(size), _length(length), _is_null(size) { + if (length) { + int alloc = length + 1; + _buffer.resize(size * alloc); + char* tmp = _buffer.data(); + for (int i = 0; i < size; ++i) { + _bind[i].buffer_type = MYSQL_TYPE_STRING; + _bind[i].buffer = const_cast(tmp); + _bind[i].buffer_length = length; + _bind[i].is_null = &_is_null[i]; + tmp += alloc; + } + } +} + +int32_t mysql_bind_result::value_as_i32(size_t range) const { + assert(_bind[range].buffer_type == MYSQL_TYPE_STRING); + int32_t retval; + if (absl::SimpleAtoi(static_cast(_bind[range].buffer), &retval)) + return retval; + else { + log_v2::sql()->debug( + "mysql_bind_result: unable to parse an i32 at range {}", range); + return 0; + } +} + +uint32_t mysql_bind_result::value_as_u32(size_t range) const { + assert(_bind[range].buffer_type == MYSQL_TYPE_STRING); + uint32_t retval; + if (absl::SimpleAtoi(static_cast(_bind[range].buffer), &retval)) + return retval; + else { + log_v2::sql()->debug( + "mysql_bind_result: unable to parse an u32 at range {}", range); + return 0; + } +} + +int64_t mysql_bind_result::value_as_i64(size_t range) const { + assert(_bind[range].buffer_type == MYSQL_TYPE_STRING); + int64_t retval; + if (absl::SimpleAtoi(static_cast(_bind[range].buffer), &retval)) + return retval; + else { + log_v2::sql()->debug( + "mysql_bind_result: unable to parse an i64 at range {}", range); + return 0; + } +} + +uint64_t mysql_bind_result::value_as_u64(size_t range) const { + assert(_bind[range].buffer_type == MYSQL_TYPE_STRING); + uint64_t retval; + if (absl::SimpleAtoi(static_cast(_bind[range].buffer), &retval)) + return retval; + else { + log_v2::sql()->debug( + "mysql_bind_result: unable to parse an u64 at range {}", range); + return 0; + } +} + +float mysql_bind_result::value_as_f32(size_t range) const { + assert(_bind[range].buffer_type == MYSQL_TYPE_STRING); + float retval; + if (absl::SimpleAtof(static_cast(_bind[range].buffer), &retval)) + return retval; + else { + log_v2::sql()->debug( + "mysql_bind_result: unable to parse an f32 at range {}", range); + return 0; + } +} + +double mysql_bind_result::value_as_f64(size_t range) const { + assert(_bind[range].buffer_type == MYSQL_TYPE_STRING); + double retval; + if (absl::SimpleAtod(static_cast(_bind[range].buffer), &retval)) + return retval; + else { + log_v2::sql()->debug( + "mysql_bind_result: unable to parse an f64 at range {}", range); + return 0; + } +} + +const char* mysql_bind_result::value_as_str(size_t range) const { + assert(_bind[range].buffer_type == MYSQL_TYPE_STRING); + return static_cast(_bind[range].buffer); +} + +char mysql_bind_result::value_as_tiny(size_t range) const { + assert(_bind[range].buffer_type == MYSQL_TYPE_STRING); + int32_t retval; + if (absl::SimpleAtoi(static_cast(_bind[range].buffer), &retval)) + return static_cast(retval); + else { + log_v2::sql()->debug( + "mysql_bind_result: unable to parse a tiny at range {}", range); + return 0; + } +} + +bool mysql_bind_result::value_as_bool(size_t range) const { + assert(_bind[range].buffer_type == MYSQL_TYPE_STRING); + bool retval; + if (absl::SimpleAtob(static_cast(_bind[range].buffer), &retval)) + return retval; + else { + log_v2::sql()->debug( + "mysql_bind_result: unable to parse a bool at range {}", range); + return false; + } +} + +/** + * @brief Return if the value at index range is NULL or not. + * + * @param range A non negative integer. + * + * @return A boolean True when the value is NULL. + */ +bool mysql_bind_result::value_is_null(size_t range) const { + return _is_null[range]; +} diff --git a/broker/core/src/database/mysql_bulk_bind.cc b/broker/core/src/database/mysql_bulk_bind.cc index acb15cf59d0..49ff137ab9d 100644 --- a/broker/core/src/database/mysql_bulk_bind.cc +++ b/broker/core/src/database/mysql_bulk_bind.cc @@ -27,31 +27,10 @@ using namespace com::centreon::broker; using namespace com::centreon::broker::database; -mysql_bulk_bind::mysql_bulk_bind(int size, int length, size_t row_count) +mysql_bulk_bind::mysql_bulk_bind(int size, size_t reserved_rows_count) : mysql_bind_base(size), _column(size) { - if (length) { - for (int i = 0; i < size; ++i) { - _bind[i].buffer_type = MYSQL_TYPE_STRING; - _column[i] = mysql_column(MYSQL_TYPE_STRING, row_count); - _bind[i].buffer = _column[i].get_buffer(); - _bind[i].u.indicator = _column[i].indicator_buffer(); - _bind[i].length = _column[i].length_buffer(); - _bind[i].buffer_length = length; - _bind[i].error = _column[i].error_buffer(); - } - } -} - -/** - * @brief Set the size of the bind, that is to say the number of columns. - * - * @param size An integer. - */ -void mysql_bulk_bind::set_size(int size) { - _bind.resize(size); - _column.resize(size); - for (int i = 0; i < size; ++i) - _bind[i].buffer = _column[i].get_buffer(); + for (auto& c : _column) + c.reserve(reserved_rows_count); } /** @@ -151,15 +130,6 @@ size_t mysql_bulk_bind::rows_count() const { return _column[0].array_size(); } -/** - * @brief Empty the bind. Actually, the current_row is set to -1. - */ -void mysql_bulk_bind::set_empty() { - for (auto& c : _column) - c.clear(); - _current_row = 0; -} - /** * @brief Accessor to the current row index. * diff --git a/broker/core/src/database/mysql_bulk_stmt.cc b/broker/core/src/database/mysql_bulk_stmt.cc index c3d28acdff3..6c18d39b6e8 100644 --- a/broker/core/src/database/mysql_bulk_stmt.cc +++ b/broker/core/src/database/mysql_bulk_stmt.cc @@ -30,22 +30,6 @@ using namespace com::centreon::exceptions; using namespace com::centreon::broker; using namespace com::centreon::broker::database; -/** - * @brief Default constructor. - */ -mysql_bulk_stmt::mysql_bulk_stmt() : mysql_stmt_base(true), _hist_size(10) {} - -/** - * @brief Constructor of a mysql_bulk_stmt from a SQL query template. This - * template can be named or not, i.e. respectively like UPDATE foo SET - * a=:a_value or UPDATE foo SET a=? - * - * @param query The query template - * @param named a boolean telling if the query is named or not (with ?). - */ -mysql_bulk_stmt::mysql_bulk_stmt(std::string const& query, bool named) - : mysql_stmt_base(query, named), _hist_size(10) {} - /** * @brief Constructor of a mysql_bulk_stmt from a not named query template and a * correspondance table making the relation between column names and their @@ -76,7 +60,7 @@ std::unique_ptr mysql_bulk_stmt::create_bind() { log_v2::sql()->trace("new mysql bind of stmt {} reserved with {} rows", get_id(), _reserved_size); auto retval = - std::make_unique(get_param_count(), 0, _reserved_size); + std::make_unique(get_param_count(), _reserved_size); return retval; } @@ -89,30 +73,6 @@ void mysql_bulk_stmt::set_bind(std::unique_ptr&& bind) { _bind = std::move(bind); } -/** - * Move constructor - */ -mysql_bulk_stmt::mysql_bulk_stmt(mysql_bulk_stmt&& other) - : mysql_stmt_base(std::move(other)), - _reserved_size(std::move(other._reserved_size)), - _bind(std::move(other._bind)) {} - -/** - * @brief Move copy - * - * @param other the statement to move. - * - * @return a reference to the self statement. - */ -mysql_bulk_stmt& mysql_bulk_stmt::operator=(mysql_bulk_stmt&& other) { - if (this != &other) { - mysql_stmt_base::operator=(std::move(other)); - _reserved_size = std::move(other._reserved_size); - _bind = std::move(other._bind); - } - return *this; -} - /** * @brief Return an unique pointer to the bind contained inside the statement. * Since the bind is in an unique pointer, it is removed from the statement when @@ -129,59 +89,27 @@ std::unique_ptr mysql_bulk_stmt::get_bind() { return std::move(_bind); } -void mysql_bulk_stmt::bind_value_as_f64(size_t range, double value) { - if (!_bind) - _bind = std::make_unique(get_param_count(), 0, - _reserved_size); - if (std::isinf(value) || std::isnan(value)) - _bind->set_null_f64(range); - else - _bind->set_value_as_f64(range, value); -} - -void mysql_bulk_stmt::bind_null_f64(size_t range) { - if (!_bind) - _bind = std::make_unique(get_param_count(), 0, - _reserved_size); - _bind->set_null_f64(range); -} - -void mysql_bulk_stmt::bind_value_as_f32(size_t range, float value) { - if (!_bind) - _bind = std::make_unique(get_param_count(), 0, - _reserved_size); - if (std::isinf(value) || std::isnan(value)) - _bind->set_null_f32(range); - else - _bind->set_value_as_f32(range, value); -} - -void mysql_bulk_stmt::bind_null_f32(size_t range) { - if (!_bind) - _bind = std::make_unique(get_param_count(), 0, - _reserved_size); - _bind->set_null_f32(range); -} - -#define BIND_VALUE(ftype, vtype) \ - void mysql_bulk_stmt::bind_value_as_##ftype(size_t range, vtype value) { \ - if (!_bind) \ - _bind = std::make_unique(get_param_count(), \ - 0, _reserved_size); \ - _bind->set_value_as_##ftype(range, value); \ - } \ - \ - void mysql_bulk_stmt::bind_null_##ftype(size_t range) { \ - if (!_bind) \ - _bind = std::make_unique(get_param_count(), \ - 0, _reserved_size); \ - _bind->set_null_##ftype(range); \ +#define BIND_VALUE(ftype, vtype) \ + void mysql_bulk_stmt::bind_value_as_##ftype(size_t range, vtype value) { \ + if (!_bind) \ + _bind = std::make_unique(get_param_count(), \ + _reserved_size); \ + _bind->set_value_as_##ftype(range, value); \ + } \ + \ + void mysql_bulk_stmt::bind_null_##ftype(size_t range) { \ + if (!_bind) \ + _bind = std::make_unique(get_param_count(), \ + _reserved_size); \ + _bind->set_null_##ftype(range); \ } BIND_VALUE(i32, int32_t) BIND_VALUE(u32, uint32_t) BIND_VALUE(i64, int64_t) BIND_VALUE(u64, uint64_t) +BIND_VALUE(f32, float) +BIND_VALUE(f64, double) BIND_VALUE(tiny, char) BIND_VALUE(bool, bool) BIND_VALUE(str, const fmt::string_view&) diff --git a/broker/core/src/database/mysql_column.cc b/broker/core/src/database/mysql_column.cc index ff5c11cd385..17ab26c585d 100644 --- a/broker/core/src/database/mysql_column.cc +++ b/broker/core/src/database/mysql_column.cc @@ -25,43 +25,15 @@ using namespace com::centreon::broker; using namespace com::centreon::broker::database; -/** - * @brief Constructor of a mysql_column. The row_count parameter is a - * reservation, not a predefined size. - * - * @param type The type of the column to allocate. - * @param row_count The number of rows to reserve whereas the initial size is 0. - */ -mysql_column::mysql_column(int type, size_t row_count) - : _type(type), _row_count{row_count}, _vector{nullptr} { - if (row_count > 0) { - set_type(type); - reserve(row_count); - } -} - /** * @brief Destructor */ mysql_column::~mysql_column() noexcept { + /* In case of vector of strings, we have to remove them properly */ if (_vector) _free_vector(); } -/** - * @brief Move constructor. - * - * @param other The column to move. - */ -mysql_column::mysql_column(mysql_column&& other) - : _type(other._type), - _row_count(other._row_count), - _vector(other._vector), - _indicator(std::move(other._indicator)), - _error(std::move(other._error)), - _length(std::move(other._length)) { - other._vector = nullptr; -} /** * @brief Move operator @@ -70,21 +42,6 @@ mysql_column::mysql_column(mysql_column&& other) * * @return a reference of this column. */ -mysql_column& mysql_column::operator=(mysql_column&& other) { - if (this == &other) - return *this; - - _type = other._type; - _row_count = other._row_count; - _current_row = other._current_row; - _length = std::move(other._length); - _error = std::move(other._error); - _indicator = std::move(other._indicator); - _free_vector(); - _vector = other._vector; - other._vector = nullptr; - return *this; -} /** * @brief Destroy the main vector of the column. @@ -127,6 +84,8 @@ void mysql_column::_free_vector() { delete vector; } break; default: + log_v2::sql()->critical( + "mysql_column: unexpected type while vector is freed"); assert(1 == 0); } _vector = nullptr; @@ -173,6 +132,7 @@ void* mysql_column::get_buffer() { return vector->data(); } break; default: + log_v2::sql()->critical("Unexpected type while getting the buffer value"); assert(1 == 0); } return nullptr; @@ -228,17 +188,22 @@ void mysql_column::clear() { vector->clear(); } break; default: + log_v2::sql()->critical( + "mysql_column: unexpected type while clearing the vector"); assert(1 == 0); } } /** - * @brief Reserve the size of the column, that is to say its number of rows. - * It is not an allocation, the size itself does not change. + * @brief Reserve the size of the column, that is to say the number of rows. + * It is not an allocation, the size itself does not change. The vector may + * not already exist, this is the case when the type is still unknown. The + * reservation can thus be incomplete. * * @param s The size to reserve. */ void mysql_column::reserve(size_t s) { + _rows_to_reserve = s; _indicator.reserve(s); _error.reserve(s); _length.reserve(s); @@ -275,6 +240,8 @@ void mysql_column::reserve(size_t s) { vector->reserve(s); } break; default: + log_v2::sql()->critical( + "mysql_column: Unexpected type while vector reservation"); assert(1 == 0); } } @@ -412,60 +379,62 @@ unsigned long* mysql_column::length_buffer() { /** * @brief Set the type of the column with a value like MYSQL_TYPE_STRING, - * MYSQL_TYPE_LONG, etc... + * MYSQL_TYPE_LONG, etc... The vector must not already exist. And if a + * reservation has already been declared, it is applied to the vector. * * @param type The type to specify. */ void mysql_column::set_type(int type) { assert(_vector == nullptr); assert(_row_count <= 1); - assert(_current_row <= 1); + assert(_current_row <= 0); _type = type; - size_t reserved_size = _indicator.capacity(); switch (type) { case MYSQL_TYPE_STRING: { auto* vector = new std::vector(); - if (reserved_size > 0) - vector->reserve(reserved_size); + if (_rows_to_reserve > 0) + vector->reserve(_rows_to_reserve); _vector = vector; } break; case MYSQL_TYPE_FLOAT: { auto* vector = new std::vector(); - if (reserved_size > 0) - vector->reserve(reserved_size); + if (_rows_to_reserve > 0) + vector->reserve(_rows_to_reserve); _vector = vector; } break; case MYSQL_TYPE_LONG: { auto* vector = new std::vector(); - if (reserved_size > 0) - vector->reserve(reserved_size); + if (_rows_to_reserve > 0) + vector->reserve(_rows_to_reserve); _vector = vector; } break; case MYSQL_TYPE_TINY: { auto* vector = new std::vector(); - if (reserved_size > 0) - vector->reserve(reserved_size); + if (_rows_to_reserve > 0) + vector->reserve(_rows_to_reserve); _vector = vector; } break; case MYSQL_TYPE_DOUBLE: { auto* vector = new std::vector(); - if (reserved_size > 0) - vector->reserve(reserved_size); + if (_rows_to_reserve > 0) + vector->reserve(_rows_to_reserve); _vector = vector; } break; case MYSQL_TYPE_LONGLONG: { auto* vector = new std::vector(); - if (reserved_size > 0) - vector->reserve(reserved_size); + if (_rows_to_reserve > 0) + vector->reserve(_rows_to_reserve); _vector = vector; } break; case MYSQL_TYPE_NULL: { auto* vector = new std::vector(); - if (reserved_size > 0) - vector->reserve(reserved_size); + if (_rows_to_reserve > 0) + vector->reserve(_rows_to_reserve); _vector = vector; } break; default: + log_v2::sql()->critical("mysql_column: unexpected type {} for column", + type); assert(1 == 0); } } diff --git a/broker/core/src/database/mysql_result.cc b/broker/core/src/database/mysql_result.cc index 0e3d3ad6111..5f58291f44a 100644 --- a/broker/core/src/database/mysql_result.cc +++ b/broker/core/src/database/mysql_result.cc @@ -1,5 +1,5 @@ /* -** Copyright 2018 Centreon +** Copyright 2018-2023 Centreon ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -44,7 +44,10 @@ mysql_result::mysql_result(mysql_connection* parent, int statement_id) * @param result The result returned by the Mariadb Connector. */ mysql_result::mysql_result(mysql_connection* parent, MYSQL_RES* result) - : _parent(parent), _result(result, mysql_free_result), _statement_id(0) {} + : _parent(parent), + _result(result, mysql_free_result), + _row(nullptr), + _statement_id(0) {} /** * Move Constructor @@ -52,34 +55,34 @@ mysql_result::mysql_result(mysql_connection* parent, MYSQL_RES* result) * @param other Another result to move into this one. */ mysql_result::mysql_result(mysql_result&& other) - : _parent(other._parent), - _result(other._result), - _row(other._row), - _statement_id(other._statement_id) { - other._row = nullptr; - other._result = nullptr; + : _parent(std::move(other._parent)), + _result(std::move(other._result)), + _row(std::move(other._row)), + _bind(std::move(other._bind)), + _statement_id(std::move(other._statement_id)) { other._parent = nullptr; - _bind = move(other._bind); + other._result = nullptr; + other._row = nullptr; + other._statement_id = 0; } /** - * Result copy operator. + * Move operator. * * @param other * * @return this object. */ -mysql_result& mysql_result::operator=(mysql_result const& other) { - _result = other._result; - _statement_id = other._statement_id; +mysql_result& mysql_result::operator=(mysql_result&& other) { + _parent = std::move(other._parent); + _result = std::move(other._result); + _row = std::move(other._row); + _bind = std::move(other._bind); + _statement_id = std::move(other._statement_id); + other._statement_id = 0; return *this; } -/** - * Destructor - */ -mysql_result::~mysql_result() {} - /** * Affects the result comming from the mariadb connector. * @@ -198,8 +201,8 @@ double mysql_result::value_as_f64(int idx) { * * @return an int */ -int mysql_result::value_as_i32(int idx) { - int retval; +int32_t mysql_result::value_as_i32(int idx) { + int32_t retval; if (_bind) retval = _bind->value_as_i32(idx); else if (_row) { @@ -216,6 +219,32 @@ int mysql_result::value_as_i32(int idx) { return retval; } +/** + * Accessor to a column tiny value + * + * @param idx The index of the column + * + * @return a char + */ +char mysql_result::value_as_tiny(int idx) { + char retval; + if (_bind) + retval = _bind->value_as_tiny(idx); + else if (_row) { + if (_row[idx]) { + int32_t tmp; + if (!absl::SimpleAtoi(_row[idx], &tmp)) + throw msg_fmt( + "mysql: result at index {} should be an integer, the current value " + "is '{}'", + idx, _row[idx]); + retval = static_cast(tmp); + } else + retval = 0; + } else + throw msg_fmt("mysql: No row fetched in result"); + return retval; +} /** * Accessor to a column uint32_t value * @@ -335,7 +364,8 @@ int mysql_result::get_rows_count() const { * * @param bind The bind to set to this result. */ -void mysql_result::set_bind(std::unique_ptr&& bind) { +void mysql_result::set_bind( + std::unique_ptr&& bind) { _bind = std::move(bind); } @@ -353,7 +383,7 @@ void mysql_result::set_row(MYSQL_ROW row) { * * @return the bind */ -std::unique_ptr& mysql_result::get_bind() { +std::unique_ptr& mysql_result::get_bind() { return _bind; } diff --git a/broker/core/src/database/mysql_stmt.cc b/broker/core/src/database/mysql_stmt.cc index 4ded079822d..22585524f6d 100644 --- a/broker/core/src/database/mysql_stmt.cc +++ b/broker/core/src/database/mysql_stmt.cc @@ -64,7 +64,7 @@ mysql_stmt::mysql_stmt(const std::string& query, */ std::unique_ptr mysql_stmt::create_bind() { log_v2::sql()->trace("new mysql bind of stmt {}", get_id()); - auto retval = std::make_unique(get_param_count(), 0); + auto retval = std::make_unique(get_param_count()); return retval; } @@ -393,7 +393,7 @@ void mysql_stmt::operator<<(io::data const& d) { void mysql_stmt::bind_value_as_f32(size_t range, float value) { if (!_bind) - _bind = std::make_unique(get_param_count(), 0); + _bind = std::make_unique(get_param_count()); if (std::isinf(value) || std::isnan(value)) _bind->set_null_f32(range); @@ -403,13 +403,13 @@ void mysql_stmt::bind_value_as_f32(size_t range, float value) { void mysql_stmt::bind_null_f32(size_t range) { if (!_bind) - _bind = std::make_unique(get_param_count(), 0); + _bind = std::make_unique(get_param_count()); _bind->set_null_f32(range); } void mysql_stmt::bind_value_as_f64(size_t range, double value) { if (!_bind) - _bind = std::make_unique(get_param_count(), 0); + _bind = std::make_unique(get_param_count()); if (std::isinf(value) || std::isnan(value)) _bind->set_null_f64(range); @@ -419,21 +419,21 @@ void mysql_stmt::bind_value_as_f64(size_t range, double value) { void mysql_stmt::bind_null_f64(size_t range) { if (!_bind) - _bind = std::make_unique(get_param_count(), 0); + _bind = std::make_unique(get_param_count()); _bind->set_null_f64(range); } -#define BIND_VALUE(ftype, vtype) \ - void mysql_stmt::bind_value_as_##ftype(size_t range, vtype value) { \ - if (!_bind) \ - _bind = std::make_unique(get_param_count(), 0); \ - _bind->set_value_as_##ftype(range, value); \ - } \ - \ - void mysql_stmt::bind_null_##ftype(size_t range) { \ - if (!_bind) \ - _bind = std::make_unique(get_param_count(), 0); \ - _bind->set_null_##ftype(range); \ +#define BIND_VALUE(ftype, vtype) \ + void mysql_stmt::bind_value_as_##ftype(size_t range, vtype value) { \ + if (!_bind) \ + _bind = std::make_unique(get_param_count()); \ + _bind->set_value_as_##ftype(range, value); \ + } \ + \ + void mysql_stmt::bind_null_##ftype(size_t range) { \ + if (!_bind) \ + _bind = std::make_unique(get_param_count()); \ + _bind->set_null_##ftype(range); \ } BIND_VALUE(i32, int32_t) diff --git a/broker/core/src/file/splitter.cc b/broker/core/src/file/splitter.cc index 898eed632e2..0a944823015 100644 --- a/broker/core/src/file/splitter.cc +++ b/broker/core/src/file/splitter.cc @@ -1,20 +1,20 @@ /* -** Copyright 2020-2022 Centreon -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -** -** For more information : contact@centreon.com -*/ + * Copyright 2020-2023 Centreon + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information : contact@centreon.com + */ #include "com/centreon/broker/file/splitter.hh" @@ -52,12 +52,9 @@ splitter::splitter(std::string const& path, _max_file_size{max_file_size == 0u ? std::numeric_limits::max() : std::max(max_file_size, 10000u)}, _rfile{}, - _rmutex{nullptr}, - _rid{0}, _roffset{0}, + _write_m{}, _wfile{}, - _wmutex{nullptr}, - _wid{0}, _woffset{0} { // Get IDs of already existing file parts. File parts are suffixed // with their order number. A file named /var/lib/foo would have @@ -124,16 +121,12 @@ splitter::~splitter() { * If no files are open, nothing is done. */ void splitter::close() { - if (_rfile) { - std::lock_guard lck(*_rmutex); + std::lock_guard lck(_write_m); + if (_rfile) _rfile.reset(); - _rmutex = nullptr; - } - if (_wfile) { - std::lock_guard lck(*_wmutex); + + if (_wfile) _wfile.reset(); - _wmutex = nullptr; - } } /** @@ -145,41 +138,62 @@ void splitter::close() { * @return Number of bytes read. */ long splitter::read(void* buffer, long max_size) { + /* No lock here, there is only one consumer. */ if (!_rfile) { _open_read_file(); if (!_rfile) return 0; } - std::unique_lock lck(*_rmutex); + /* We introduce the locker, but don't lock if not necessary */ + std::unique_lock lck(_write_m, std::defer_lock); + + /* Here, _wid is atomic so we can read it. Maybe when we'll lock _wid will + * be greater but it is not so important. Usually, if _rid == _wid, we read + * and write in the same file, so we have to lock the mutex. */ + if (_rid == _wid) + lck.lock(); + // Seek to current read position. fseek(_rfile.get(), _roffset, SEEK_SET); // Read data. long rb = disk_accessor::instance().fread(buffer, 1, max_size, _rfile.get()); std::string file_path(get_file_path(_rid)); - log_v2::bbdo()->debug("file: read {} bytes from '{}'", rb, file_path); + log_v2::bbdo()->debug("splitter: read {} bytes at offset {} from '{}'", rb, + _roffset, file_path); _roffset += rb; if (rb == 0) { if (feof(_rfile.get())) { if (_auto_delete) { log_v2::bbdo()->info("file: end of file '{}' reached, erasing it", file_path); + /* Here we have to really verify that _wfile and _rfile are the same, + * and then we close files before removing them. */ + if (lck.owns_lock() && _wfile == _rfile) { + _rfile.reset(); + _wfile.reset(); + } else + _rfile.reset(); disk_accessor::instance().remove(file_path); } if (_rid < _wid) { _rid++; - lck.unlock(); - _open_read_file(); - return read(static_cast(buffer), max_size); + /* As we said earlier, maybe we locked lck abusively while _rid < _wid + */ + if (lck.owns_lock()) + lck.unlock(); + return read(buffer, max_size); } else throw exceptions::shutdown("No more data to read"); } else { if (errno == EAGAIN || errno == EINTR) return 0; - else + else { + char msg[1024]; throw msg_fmt("error while reading file '{}': {}", file_path, - strerror(errno)); + strerror_r(errno, msg, sizeof(msg))); + } } } return rb; @@ -215,25 +229,25 @@ long splitter::tell() { * @return Number of bytes written. */ long splitter::write(void const* buffer, long size) { + /* It is impossible for two threads to write at the same time. */ + std::lock_guard lck(_write_m); if (!_wfile) - _open_write_file(); + if (!_open_write_file()) + return 0; - { - std::unique_lock lck(*_wmutex); - // Open next write file is max file size is reached. - if ((_woffset + size) > _max_file_size) { - ++_wid; - lck.unlock(); - // After this call, _wmutex may change. - _open_write_file(); + // Open next write file is max file size is reached. + if ((_woffset + size) > _max_file_size) { + if (fflush(_wfile.get())) { + log_v2::bbdo()->error("splitter: cannot flush file '{}'", + get_file_path(_wid)); + char msg[1024]; + throw msg_fmt("cannot flush file '{}': {}", get_file_path(_wid), + strerror_r(errno, msg, sizeof(msg))); } + ++_wid; + if (!_open_write_file()) + return 0; } - std::unique_lock lck(*_wmutex); - if (!_wfile) { - log_v2::core()->error("file: data of size {} lost", size); - return size; - } - // Otherwise seek to end of file. fseek(_wfile.get(), _woffset, SEEK_SET); @@ -243,15 +257,15 @@ long splitter::write(void const* buffer, long size) { get_file_path(_wid)); // Write data. - size_t wb = disk_accessor::instance().fwrite(buffer, 1, size, _wfile.get()); - if (wb > 0) - _woffset += wb; - else { - log_v2::core()->error( - "file: error while writing event into the file '{}' - event lost: {}", - get_file_path(_wid), strerror(errno)); + long wb = disk_accessor::instance().fwrite(buffer, 1, size, _wfile.get()); + if (wb != size) { + std::string wfile(get_file_path(_wid)); + char msg[1024]; + log_v2::bbdo()->critical("splitter: cannot write to file '{}': {}", wfile, + strerror_r(errno, msg, sizeof(msg))); + return 0; } - + _woffset += size; return size; } @@ -259,9 +273,12 @@ long splitter::write(void const* buffer, long size) { * Flush the write stream. */ void splitter::flush() { - if (fflush(_wfile.get()) == EOF) + std::lock_guard lck(_write_m); + if (fflush(_wfile.get()) == EOF) { + char msg[1024]; throw msg_fmt("error while writing the file '{}' content: {}", - get_file_path(_wid), strerror(errno)); + get_file_path(_wid), strerror_r(errno, msg, sizeof(msg))); + } } /** @@ -325,9 +342,12 @@ long splitter::get_woffset() const { * Remove all the files the splitter is concerned by. */ void splitter::remove_all_files() { - close(); - std::lock_guard lck1(_mutex1); - std::lock_guard lck2(_mutex2); + std::lock_guard lck(_write_m); + if (_rfile) + _rfile.reset(); + + if (_wfile) + _wfile.reset(); std::string base_dir; std::string base_name; { @@ -344,60 +364,71 @@ void splitter::remove_all_files() { misc::filesystem::dir_content_with_filter(base_dir, base_name + '*')}; for (const std::string& f : parts) disk_accessor::instance().remove(f); + + /* No more files, we reset rid and wid. */ + _rid = 0; + _wid = 0; } /** * @brief Open the splitter in read mode. */ void splitter::_open_read_file() { + bool done = false; { - std::lock_guard lck(_id_m); + std::lock_guard lck(_write_m); if (_rid == _wid && _wfile) { _rfile = _wfile; - _rmutex = _wmutex; - } else { - std::string fname(get_file_path(_rid)); - FILE* f = disk_accessor::instance().fopen(fname, "r+"); - _rfile = f ? std::shared_ptr(f, fclose) : std::shared_ptr(); - if (_rfile) - _rmutex = &_mutex1; + done = true; } } + if (!done) { + std::string fname(get_file_path(_rid)); + FILE* f = disk_accessor::instance().fopen(fname, "r+b"); + if (f) + log_v2::bbdo()->debug("splitter: read open '{}'", fname); + else + log_v2::bbdo()->error("splitter: read fail open '{}'", fname); + + _rfile = f ? std::shared_ptr(f, fclose) : std::shared_ptr(); + } + if (!_rfile) { if (errno == ENOENT) return; - else - throw msg_fmt("cannot open '{}' to read/write: {}", get_file_path(_rid), - strerror(errno)); + else { + char msg[1024]; + throw msg_fmt("cannot open '{}' to read: {}", get_file_path(_rid), + strerror_r(errno, msg, sizeof(msg))); + } } - std::lock_guard lck(*_rmutex); _roffset = 2 * sizeof(uint32_t); fseek(_rfile.get(), _roffset, SEEK_SET); } /** - * @brief Open the splitter in write mode. + * @brief Open the splitter in write mode. This call must be protected by the + * _write_m mutex. + * + * @return True on success, False otherwise. */ -void splitter::_open_write_file() { - { - std::lock_guard lck(_id_m); - if (_wid == _rid && _rfile) { - _wfile = _rfile; - _wmutex = _rmutex; - } else { - std::string fname(get_file_path(_wid)); - FILE* f = disk_accessor::instance().fopen(fname, "a+"); - _wfile = f ? std::shared_ptr(f, fclose) : std::shared_ptr(); - _wmutex = &_mutex2; - } - } +bool splitter::_open_write_file() { + std::string fname(get_file_path(_wid)); + FILE* f = disk_accessor::instance().fopen(fname, "a+b"); + if (f) + log_v2::bbdo()->debug("splitter: write open '{}'", fname); + else + log_v2::bbdo()->error("splitter: write fail open '{}'", fname); - if (!_wfile) + _wfile = f ? std::shared_ptr(f, fclose) : std::shared_ptr(); + + if (!_wfile) { + char msg[1024]; throw msg_fmt("cannot open '{}' to read/write: {}", get_file_path(_wid), - strerror(errno)); + strerror_r(errno, msg, sizeof(msg))); + } - std::lock_guard lck(*_wmutex); fseek(_wfile.get(), 0, SEEK_END); _woffset = ftell(_wfile.get()); @@ -412,13 +443,15 @@ void splitter::_open_write_file() { header.integers[1] = htonl(2 * sizeof(uint32_t)); size_t size = disk_accessor::instance().fwrite( header.bytes, 1, sizeof(header), _wfile.get()); - if (size > 0) - _woffset = 2 * sizeof(uint32_t); - else { - log_v2::core()->error("file: could not create file '{}': {}", - get_file_path(_wid), strerror(errno)); + if (size != sizeof(header)) { + std::string wfile(get_file_path(_wid)); + char msg[1024]; + log_v2::bbdo()->critical("splitter: cannot write to file '{}': {}", wfile, + strerror_r(errno, msg, sizeof(msg))); _wfile.reset(); - _woffset = 0; + return false; } + _woffset = 2 * sizeof(uint32_t); } + return true; } diff --git a/broker/core/src/file/stream.cc b/broker/core/src/file/stream.cc index 5551b15f93b..f799c4fb5ba 100644 --- a/broker/core/src/file/stream.cc +++ b/broker/core/src/file/stream.cc @@ -141,7 +141,7 @@ void stream::statistics(nlohmann::json& tree) const { } } - if (max_file_size == std::numeric_limits::max()) { + if (max_file_size == std::numeric_limits::max()) { tree["file_expected_max_size"] = static_cast( fwoffset + (fwoffset - _last_write_offset) * (eta - now) / (now - _last_time)); diff --git a/broker/core/src/log_v2.cc b/broker/core/src/log_v2.cc index b428978f437..9823e84fe2d 100644 --- a/broker/core/src/log_v2.cc +++ b/broker/core/src/log_v2.cc @@ -1,5 +1,5 @@ /* -** Copyright 2020-2022 Centreon +** Copyright 2020-2023 Centreon ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -72,17 +72,30 @@ static void grpc_logger(gpr_log_func_args* args) { } } -log_v2& log_v2::instance() { - static log_v2 instance; - return instance; +std::shared_ptr log_v2::_instance; + +void log_v2::load(const std::shared_ptr& io_context) { + if (!_instance) + _instance.reset(new log_v2(io_context)); +} + +std::shared_ptr log_v2::instance() { + return _instance; } -log_v2::log_v2() : _running{false} { +log_v2::log_v2(const std::shared_ptr& io_context) + : log_v2_base("broker"), + _running{false}, + _flush_timer(*io_context), + _flush_timer_active(true), + _io_context(io_context) { auto stdout_sink = std::make_shared(); - auto create_logger = [&stdout_sink](const std::string& name) { + auto create_logger = [&](const std::string& name) { std::shared_ptr log = - std::make_shared(name, stdout_sink); + std::make_shared(name, this, + stdout_sink); log->flush_on(level::info); + log->set_level(level::info); spdlog::register_logger(log); return log; }; @@ -108,10 +121,9 @@ log_v2::log_v2() : _running{false} { _running = true; } -log_v2::~log_v2() { - core()->info("log finished"); +log_v2::~log_v2() noexcept { + _log[log_v2::log_core]->info("log finished"); _running = false; - spdlog::shutdown(); for (auto& l : _log) l.reset(); } @@ -122,40 +134,38 @@ void log_v2::apply(const config::state& conf) { const auto& log = conf.log_conf(); - _log_name = log.log_path(); // reset loggers to null sink auto null_sink = std::make_shared(); std::shared_ptr> file_sink; + _file_path = log.log_path(); if (log.max_size) file_sink = std::make_shared( - _log_name, log.max_size, 99); + _file_path, log.max_size, 99); else - file_sink = std::make_shared(_log_name); + file_sink = std::make_shared(_file_path); - auto create_log = [&file_sink, log_pid = log.log_pid, - log_source = log.log_source, - flush_period = log.flush_period](const std::string& name, - level::level_enum lvl) { + auto create_log = [&](const std::string& name, level::level_enum lvl) { spdlog::drop(name); - auto log = std::make_shared(name, file_sink); - log->set_level(lvl); + auto logger = std::make_shared( + name, this, file_sink); + logger->set_level(lvl); if (lvl != level::off) { - if (flush_period) - log->flush_on(level::warn); + if (log.flush_period) + logger->flush_on(level::warn); else - log->flush_on(lvl); - if (log_pid) { - if (log_source) - log->set_pattern( + logger->flush_on(level::trace); + if (log.log_pid) { + if (log.log_source) + logger->set_pattern( "[%Y-%m-%dT%H:%M:%S.%e%z] [%n] [%l] [%s:%#] [%P] %v"); else - log->set_pattern("[%Y-%m-%dT%H:%M:%S.%e%z] [%n] [%l] [%P] %v"); + logger->set_pattern("[%Y-%m-%dT%H:%M:%S.%e%z] [%n] [%l] [%P] %v"); } else { - if (log_source) - log->set_pattern("[%Y-%m-%dT%H:%M:%S.%e%z] [%n] [%l] [%s:%#] %v"); + if (log.log_source) + logger->set_pattern("[%Y-%m-%dT%H:%M:%S.%e%z] [%n] [%l] [%s:%#] %v"); else - log->set_pattern("[%Y-%m-%dT%H:%M:%S.%e%z] [%n] [%l] %v"); + logger->set_pattern("[%Y-%m-%dT%H:%M:%S.%e%z] [%n] [%l] %v"); } } @@ -177,12 +187,12 @@ void log_v2::apply(const config::state& conf) { } } - spdlog::register_logger(log); - return log; + spdlog::register_logger(logger); + return logger; }; _log[log_v2::log_core] = create_log("core", level::info); - core()->info("{} : log started", _log_name); + core()->info("{} : log started", _file_path); absl::flat_hash_map lgs{ {"bam", log_v2::log_bam}, @@ -211,16 +221,55 @@ void log_v2::apply(const config::state& conf) { } } - spdlog::flush_every(std::chrono::seconds(log.flush_period)); for (auto it = lgs.begin(); it != lgs.end(); ++it) { - spdlog::drop(it->first); - auto l = std::make_shared(it->first, null_sink); - spdlog::register_logger(l); - _log[it->second] = std::move(l); + _log[lgs[it->first]] = create_log(it->first, spdlog::level::off); } + + _flush_interval = + std::chrono::seconds(log.flush_period > 0 ? log.flush_period : 2); + start_flush_timer(file_sink); _running = true; } +void log_v2::set_flush_interval(unsigned second_flush_interval) { + log_v2_base::set_flush_interval(second_flush_interval); + if (second_flush_interval) { + for (auto logger : _log) { + logger->flush_on(level::warn); + } + } else { + for (auto logger : _log) { + logger->flush_on(level::trace); + } + } +} + +/** + * @brief logs are written periodicaly to disk + * + * @param sink + */ +void log_v2::start_flush_timer(spdlog::sink_ptr sink) { + std::lock_guard l(_flush_timer_m); + _flush_timer.expires_after(_flush_interval); + _flush_timer.async_wait([me = std::static_pointer_cast(_instance), + sink](const asio::error_code& err) { + if (err || !me->_flush_timer_active) { + return; + } + if (me->get_flush_interval().count() > 0) { + sink->flush(); + } + me->start_flush_timer(sink); + }); +} + +void log_v2::stop_flush_timer() { + std::lock_guard l(_flush_timer_m); + _flush_timer_active = false; + _flush_timer.cancel(); +} + /** * @brief Check if the given logger makes part of our loggers * @@ -255,6 +304,23 @@ std::vector> log_v2::levels() const { return retval; } +/** + * @brief this private static method is used to access a specific logger + * + * @param log_type + * @param log_str + * @return std::shared_ptr + */ +std::shared_ptr log_v2::get_logger(logger log_type, + const char* log_str) { + if (_running) + return _log[log_type]; + else { + auto null_sink = std::make_shared(); + return std::make_shared(log_str, null_sink); + } +} + /** * @brief Check if the given level makes part of the available levels. * @@ -271,168 +337,6 @@ bool log_v2::contains_level(const std::string& level) { return l != level::off; } -std::shared_ptr log_v2::bam() { - if (instance()._running) - return instance()._log[log_v2::log_bam]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("bam", null_sink); - } -} - -std::shared_ptr log_v2::bbdo() { - if (instance()._running) - return instance()._log[log_v2::log_bbdo]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("bbdo", null_sink); - } -} - -std::shared_ptr log_v2::config() { - if (instance()._running) - return instance()._log[log_v2::log_config]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("config", null_sink); - } -} - -std::shared_ptr log_v2::core() { - if (instance()._running) - return instance()._log[log_v2::log_core]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("core", null_sink); - } -} - -std::shared_ptr log_v2::influxdb() { - if (instance()._running) - return instance()._log[log_v2::log_influxdb]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("influxdb", null_sink); - } -} - -std::shared_ptr log_v2::graphite() { - if (instance()._running) - return instance()._log[log_v2::log_graphite]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("graphite", null_sink); - } -} - -std::shared_ptr log_v2::notification() { - if (instance()._running) - return instance()._log[log_v2::log_notification]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("notification", null_sink); - } -} - -std::shared_ptr log_v2::rrd() { - if (instance()._running) - return instance()._log[log_v2::log_rrd]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("rrd", null_sink); - } -} - -std::shared_ptr log_v2::stats() { - if (instance()._running) - return instance()._log[log_v2::log_stats]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("stats", null_sink); - } -} - -std::shared_ptr log_v2::lua() { - if (instance()._running) - return instance()._log[log_v2::log_lua]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("lua", null_sink); - } -} - -std::shared_ptr log_v2::neb() { - if (instance()._running) - return instance()._log[log_v2::log_neb]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("neb", null_sink); - } -} - -std::shared_ptr log_v2::perfdata() { - if (instance()._running) - return instance()._log[log_v2::log_perfdata]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("perfdata", null_sink); - } -} - -std::shared_ptr log_v2::processing() { - if (instance()._running) - return instance()._log[log_v2::log_processing]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("processing", null_sink); - } -} - -std::shared_ptr log_v2::sql() { - if (instance()._running) - return instance()._log[log_v2::log_sql]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("sql", null_sink); - } -} - -std::shared_ptr log_v2::tcp() { - if (instance()._running) - return instance()._log[log_v2::log_tcp]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("tcp", null_sink); - } -} - -std::shared_ptr log_v2::tls() { - if (instance()._running) - return instance()._log[log_v2::log_tls]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("tls", null_sink); - } -} - -std::shared_ptr log_v2::grpc() { - if (instance()._running) - return instance()._log[log_v2::log_grpc]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("grpc", null_sink); - } -} - -/** - * @brief Accessor to the log file. - * - * @return - */ -const std::string& log_v2::log_name() const { - return _log_name; -} - /** * @brief Set the level of a logger. * diff --git a/broker/core/src/main.cc b/broker/core/src/main.cc index 363ab951d82..47a9901ad12 100644 --- a/broker/core/src/main.cc +++ b/broker/core/src/main.cc @@ -51,6 +51,10 @@ using namespace com::centreon::broker; using namespace com::centreon::exceptions; +std::shared_ptr g_io_context = + std::make_shared(); +bool g_io_context_started = false; + // Main config file. static std::vector gl_mainconfigfiles; static config::state gl_state; @@ -81,7 +85,7 @@ static void hup_handler(int) { config::parser parsr; config::state conf{parsr.parse(gl_mainconfigfiles.front())}; try { - log_v2::instance().apply(conf); + log_v2::instance()->apply(conf); } catch (const std::exception& e) { log_v2::core()->error("problem while reloading cbd: {}", e.what()); } @@ -145,6 +149,8 @@ int main(int argc, char* argv[]) { uint16_t default_port{51000}; std::string default_listen_address{"localhost"}; + log_v2::load(g_io_context); + // Set configuration update handler. if (signal(SIGHUP, hup_handler) == SIG_ERR) { char const* err{strerror(errno)}; @@ -247,7 +253,7 @@ int main(int argc, char* argv[]) { config::parser parsr; config::state conf{parsr.parse(gl_mainconfigfiles.front())}; try { - log_v2::instance().apply(conf); + log_v2::instance()->apply(conf); } catch (const std::exception& e) { log_v2::core()->error("{}", e.what()); } @@ -288,8 +294,10 @@ int main(int argc, char* argv[]) { log_v2::core()->info("main: termination request received by process {}", getpid()); } + log_v2::instance()->stop_flush_timer(); // Unload endpoints. config::applier::deinit(); + spdlog::shutdown(); } } // Standard exception. diff --git a/broker/core/src/multiplexing/engine.cc b/broker/core/src/multiplexing/engine.cc index 49b9643d505..37d672f8f39 100644 --- a/broker/core/src/multiplexing/engine.cc +++ b/broker/core/src/multiplexing/engine.cc @@ -41,7 +41,6 @@ std::mutex engine::_load_m; * @return Class instance. */ std::shared_ptr engine::instance_ptr() { - std::lock_guard lk(_load_m); return _instance; } @@ -81,102 +80,110 @@ void engine::unload() { */ void engine::publish(const std::shared_ptr& e) { // Lock mutex. - std::lock_guard lock(_engine_m); - switch (_state) { - case stopped: - log_v2::core()->trace("engine::publish one event to file"); - _cache_file->add(e); - _unprocessed_events++; - break; - case not_started: - log_v2::core()->trace("engine::publish one event to queue"); - _kiew.push_back(e); - break; - default: - log_v2::core()->trace("engine::publish one event to queue_"); - _kiew.push_back(e); - if (!_sending_to_subscribers) { - _sending_to_subscribers = true; - _strand.post(std::bind(&engine::_send_to_subscribers, this)); - } - break; - } -} - -void engine::publish(const std::list>& to_publish) { - std::lock_guard lock(_engine_m); - switch (_state) { - case stopped: - log_v2::core()->trace("engine::publish {} event to file", - to_publish.size()); - for (auto& e : to_publish) { + bool have_to_send = false; + { + std::lock_guard lock(_engine_m); + switch (_state) { + case stopped: + log_v2::core()->trace("engine::publish one event to file"); _cache_file->add(e); _unprocessed_events++; - } - break; - case not_started: - log_v2::core()->trace("engine::publish {} event to queue", - to_publish.size()); - for (auto& e : to_publish) + break; + case not_started: + log_v2::core()->trace("engine::publish one event to queue"); _kiew.push_back(e); - break; - default: - log_v2::core()->trace("engine::publish {} event to queue_", - to_publish.size()); - for (auto& e : to_publish) + break; + default: + log_v2::core()->trace("engine::publish one event to queue_"); _kiew.push_back(e); - if (!_sending_to_subscribers) { - _sending_to_subscribers = true; - _strand.post(std::bind(&engine::_send_to_subscribers, this)); - } - break; + have_to_send = true; + break; + } + } + if (have_to_send) { + _send_to_subscribers(nullptr); } } +void engine::publish(const std::list>& to_publish) { + bool have_to_send = false; + { + std::lock_guard lock(_engine_m); + switch (_state) { + case stopped: + log_v2::core()->trace("engine::publish {} event to file", + to_publish.size()); + for (auto& e : to_publish) { + _cache_file->add(e); + _unprocessed_events++; + } + break; + case not_started: + log_v2::core()->trace("engine::publish {} event to queue", + to_publish.size()); + for (auto& e : to_publish) + _kiew.push_back(e); + break; + default: + log_v2::core()->trace("engine::publish {} event to queue_", + to_publish.size()); + for (auto& e : to_publish) + _kiew.push_back(e); + have_to_send = true; + break; + } + } + if (have_to_send) { + _send_to_subscribers(nullptr); + } +} /** * Start multiplexing. This function gets back the retention content and * inserts it in front of the engine's queue. Then all this content is * published. */ void engine::start() { - std::lock_guard lock(_engine_m); - if (_state == not_started) { - // Set writing method. - log_v2::core()->debug("multiplexing: engine starting"); - _state = running; - stats::center::instance().update(&EngineStats::set_mode, _stats, - EngineStats::RUNNING); - - // Local queue. - std::deque> kiew; - // Get events from the cache file to the local queue. - try { - persistent_cache cache(_cache_file_path()); - std::shared_ptr d; - for (;;) { - cache.get(d); - if (!d) - break; - kiew.push_back(d); + bool have_to_send = false; + { + std::lock_guard lock(_engine_m); + if (_state == not_started) { + // Set writing method. + log_v2::core()->debug("multiplexing: engine starting"); + _state = running; + stats::center::instance().update(&EngineStats::set_mode, _stats, + EngineStats::RUNNING); + + // Local queue. + std::deque> kiew; + // Get events from the cache file to the local queue. + try { + persistent_cache cache(_cache_file_path()); + std::shared_ptr d; + for (;;) { + cache.get(d); + if (!d) + break; + kiew.push_back(d); + } + } catch (const std::exception& e) { + log_v2::core()->error( + "multiplexing: engine couldn't read cache file: {}", e.what()); } - } catch (const std::exception& e) { - log_v2::core()->error("multiplexing: engine couldn't read cache file: {}", - e.what()); - } - // Copy global event queue to local queue. - while (!_kiew.empty()) { - kiew.push_back(_kiew.front()); - _kiew.pop_front(); - } + // Copy global event queue to local queue. + while (!_kiew.empty()) { + kiew.push_back(_kiew.front()); + _kiew.pop_front(); + } - // Send events queued while multiplexing was stopped. - _kiew = std::move(kiew); - if (!_sending_to_subscribers) { - _sending_to_subscribers = true; - _strand.post(std::bind(&engine::_send_to_subscribers, this)); + // Send events queued while multiplexing was stopped. + _kiew = std::move(kiew); + have_to_send = true; } } + if (have_to_send) { + _send_to_subscribers(nullptr); + } log_v2::core()->info("multiplexing: engine started"); } @@ -201,11 +208,11 @@ void engine::stop() { _sending_to_subscribers = true; lock.unlock(); std::promise promise; - _strand.post([this, &promise] { - _send_to_subscribers(); - promise.set_value(); - }); - promise.get_future().get(); + if (_send_to_subscribers([&promise]() { promise.set_value(); })) { + promise.get_future().get(); + } else { // nothing to send or no muxer + break; + } lock.lock(); } } while (!_kiew.empty()); @@ -232,18 +239,21 @@ void engine::stop() { } /** - * Subscribe to the multiplexing engine. + * Subscribe a muxer to the multiplexing engine if not already subscribed. * - * @param[in] subscriber Subscriber. + * @param[in] subscriber A muxer. */ -void engine::subscribe(muxer* subscriber) { - std::promise promise; - _strand.post([this, &promise, subscriber] { - log_v2::core()->trace("muxer {} subscribes to engine", subscriber->name()); - _muxers.push_back(subscriber); - promise.set_value(); - }); - promise.get_future().get(); +void engine::subscribe(const std::shared_ptr& subscriber) { + log_v2::config()->debug("engine: muxer {} subscribes to engine", + subscriber->name()); + std::lock_guard l(_engine_m); + for (auto& m : _muxers) + if (m == subscriber) { + log_v2::config()->debug("engine: muxer {} already subscribed", + subscriber->name()); + return; + } + _muxers.push_back(subscriber); } /** @@ -251,19 +261,16 @@ void engine::subscribe(muxer* subscriber) { * * @param[in] subscriber Subscriber. */ -void engine::unsubscribe(muxer* subscriber) { - std::promise promise; - _strand.post([this, &promise, subscriber] { - for (auto it = _muxers.begin(), end = _muxers.end(); it != end; ++it) - if (*it == subscriber) { - log_v2::core()->trace("muxer {} unsubscribes to engine", +void engine::unsubscribe(const muxer* subscriber) { + std::lock_guard l(_engine_m); + for (auto it = _muxers.begin(); it != _muxers.end(); ++it) { + if (it->get() == subscriber) { + log_v2::config()->debug("engine: muxer {} unsubscribes to engine", subscriber->name()); - _muxers.erase(it); - break; - } - promise.set_value(); - }); - promise.get_future().get(); + _muxers.erase(it); + return; + } + } } /** @@ -271,8 +278,6 @@ void engine::unsubscribe(muxer* subscriber) { */ engine::engine() : _state{not_started}, - _strand(pool::instance().io_context()), - _muxers{}, _stats{stats::center::instance().register_engine()}, _unprocessed_events{0u}, _sending_to_subscribers{false} { @@ -295,65 +300,122 @@ std::string engine::_cache_file_path() const { return retval; } +CCB_BEGIN() + +namespace multiplexing { +namespace detail { + +/** + * @brief The goal of this class is to do the completion job once all muxer has + * been fed a shared_ptr of one instance of this class is passed to worker. So + * when all workers have finished, destructor is called and do the job + * + */ +class callback_caller { + engine::send_to_mux_callback_type _callback; + std::shared_ptr _parent; + + public: + callback_caller(engine::send_to_mux_callback_type&& callback, + const std::shared_ptr& parent) + : _callback(callback), _parent(parent) {} + + /** + * @brief Destroy the callback caller object and do the completion job + * + */ + ~callback_caller() { + // job is done + bool expected = true; + _parent->_sending_to_subscribers.compare_exchange_strong(expected, false); + // if another data to publish redo the job + _parent->_send_to_subscribers(nullptr); + if (_callback) { + _callback(); + } + } +}; + +} // namespace detail +} // namespace multiplexing +CCB_END() + /** - * Send queued events to subscribers. Since events are queued, we use a strand - * to keep their order. But there are several muxers, so we parallelize the - * sending of data to each. + * @brief + * Send queued events to subscribers. Since events are queued, we use a + * strand to keep their order. But there are several muxers, so we parallelize + * the sending of data to each. callback is called only if _kiew is not empty + * @param callback + * @return true data sent + * @return false nothing to sent or sent in progress */ -void engine::_send_to_subscribers() { +bool engine::_send_to_subscribers(send_to_mux_callback_type&& callback) { + // is _send_to_subscriber working? (_sending_to_subscribers=false) + bool expected = false; + if (!_sending_to_subscribers.compare_exchange_strong(expected, true)) { + return false; + } + // now we continue and _sending_to_subscribers = true + // Process all queued events. - std::deque> kiew; + std::shared_ptr>> kiew; + std::shared_ptr last_muxer; + std::shared_ptr cb; { std::lock_guard lck(_engine_m); - if (_kiew.empty()) { - _sending_to_subscribers = false; - return; + if (_muxers.empty() || _kiew.empty()) { + // nothing to do true => _sending_to_subscribers + bool expected = true; + _sending_to_subscribers.compare_exchange_strong(expected, false); + return false; } - std::swap(_kiew, kiew); - } - stats::center::instance().update(&EngineStats::set_processed_events, _stats, - static_cast(kiew.size())); - std::atomic count{static_cast(_muxers.size()) - 1}; - if (count >= 0) { - /* We must wait the end of the sending, so we use a promise. */ - std::promise promise; - - /* Since the sending is parallelized, we use the thread pool for this - * purpose except for the last muxer where we use this thread. */ - - /* We get an iterator to the last muxer */ - auto it_last = --_muxers.end(); - - /* We use the thread pool for the muxers from the first one to the second - * to last */ - for (auto it = _muxers.begin(); it != it_last; ++it) { - pool::io_context().post([&kiew, m = *it, &count, &promise] { - for (auto& e : kiew) - m->publish(e); - if (atomic_fetch_sub_explicit(&count, 1, std::memory_order_relaxed) == - 0) - promise.set_value(); - }); + log_v2::core()->trace( + "engine::_send_to_subscribers send {} events to {} muxers", + _kiew.size(), _muxers.size()); + + kiew = std::make_shared>>(); + std::swap(_kiew, *kiew); + // completion object + // it will be destroyed at the end of the scope of this function and at the + // end of lambdas posted + cb = std::make_shared(std::move(callback), + shared_from_this()); + last_muxer = *_muxers.rbegin(); + if (_muxers.size() > 1) { + /* Since the sending is parallelized, we use the thread pool for this + * purpose except for the last muxer where we use this thread. */ + + /* We get an iterator to the last muxer */ + auto it_last = --_muxers.end(); + + /* We use the thread pool for the muxers from the first one to the + * second to last */ + for (auto it = _muxers.begin(); it != it_last; ++it) { + pool::io_context().post([kiew, m = *it, cb]() { + try { + m->publish(*kiew); + } // pool threads protection + catch (const std::exception& ex) { + log_v2::core()->error("publish caught exception: {}", ex.what()); + } catch (...) { + log_v2::core()->error("publish caught unknown exception"); + } + }); + } } - /* The same work but by this thread for the last muxer. */ - auto m = *it_last; - for (auto& e : kiew) - m->publish(e); - - if (atomic_fetch_sub_explicit(&count, 1, std::memory_order_relaxed) == 0) - promise.set_value(); - - promise.get_future().wait(); } - - /* The strand is necessary for the order of data */ - _strand.post(std::bind(&engine::_send_to_subscribers, this)); + stats::center::instance().update(&EngineStats::set_processed_events, _stats, + static_cast(kiew->size())); + /* The same work but by this thread for the last muxer. */ + last_muxer->publish(*kiew); + return true; } /** * Clear events stored in the multiplexing engine. */ void engine::clear() { + std::lock_guard lck(_engine_m); _kiew.clear(); } diff --git a/broker/core/src/multiplexing/muxer.cc b/broker/core/src/multiplexing/muxer.cc index 2e4f0aaee77..d67de5863c8 100644 --- a/broker/core/src/multiplexing/muxer.cc +++ b/broker/core/src/multiplexing/muxer.cc @@ -33,6 +33,9 @@ using namespace com::centreon::broker::multiplexing; uint32_t muxer::_event_queue_max_size = std::numeric_limits::max(); +std::mutex muxer::_running_muxers_m; +absl::flat_hash_map> muxer::_running_muxers; + /** * Constructor. * @@ -104,8 +107,44 @@ muxer::muxer(std::string name, log_v2::core()->info( "multiplexing: '{}' starts with {} in queue and the queue file is {}", _name, _events_size, _file ? "enable" : "disable"); +} - engine::instance_ptr()->subscribe(this); +/** + * @brief muxer must be in a shared_ptr + * so this static method creates it and registers it in engine + * + * @param name + * @param r_filters + * @param w_filters + * @param persistent + * @return std::shared_ptr + */ +std::shared_ptr muxer::create(std::string name, + muxer::filters r_filters, + muxer::filters w_filters, + bool persistent) { + std::shared_ptr retval; + { + std::lock_guard lck(_running_muxers_m); + absl::erase_if(_running_muxers, + [](const std::pair>& p) { + return p.second.expired(); + }); + retval = _running_muxers[name].lock(); + if (retval) { + log_v2::config()->debug("muxer: muxer '{}' already exists, reusing it", + name); + retval->set_filters(r_filters, w_filters); + } else { + log_v2::config()->debug("muxer: muxer '{}' unknown, creating it", name); + retval = std::shared_ptr( + new muxer(name, r_filters, w_filters, persistent)); + _running_muxers[name] = retval; + } + } + + engine::instance_ptr()->subscribe(retval); + return retval; } /** @@ -113,7 +152,9 @@ muxer::muxer(std::string name, */ muxer::~muxer() noexcept { stats::center::instance().unregister_muxer(_name); - engine::instance_ptr()->unsubscribe(this); + auto eng = engine::instance_ptr(); + if (eng) + eng->unsubscribe(this); std::lock_guard lock(_mutex); log_v2::core()->info("Destroying muxer {}: number of events in the queue: {}", _name, _events_size); @@ -200,33 +241,57 @@ uint32_t muxer::event_queue_max_size() noexcept { * * @param[in] event Event to add. */ -void muxer::publish(const std::shared_ptr event) { - if (event) { - SPDLOG_LOGGER_TRACE(log_v2::core(), "{} publish {}", _name, *event); - - std::lock_guard lock(_mutex); - // Check if we should process this event. - if (_write_filters.find(event->type()) == _write_filters.end()) { - SPDLOG_LOGGER_TRACE(log_v2::core(), "{} reject {}", _name, *event); +void muxer::publish(const std::deque>& event_queue) { + auto evt = event_queue.begin(); + while (evt != event_queue.end()) { + bool at_least_one_push_to_queue = false; + { // we stop this first loop when mux queue is full on order to release + // mutex to let read do his job before write to file + std::lock_guard lock(_mutex); + for (; evt != event_queue.end() && _events_size < event_queue_max_size(); + ++evt) { + if (_write_filters.find((*evt)->type()) == _write_filters.end()) { + continue; + } + at_least_one_push_to_queue = true; + log_v2::core()->trace("muxer::publish {} publish one event to queue", + _name); + _push_to_queue(*evt); + } + } + if (evt == event_queue.end()) { return; } - // Check if the event queue limit is reach. - if (_events_size >= event_queue_max_size()) { - // Try to create file if is necessary. + // we have stopped insertion because of full queue => retry + if (at_least_one_push_to_queue) { + continue; + } + // nothing pushed => to file + std::lock_guard lock(_mutex); + for (; evt != event_queue.end(); ++evt) { + if (_write_filters.find((*evt)->type()) == _write_filters.end()) { + continue; + } if (!_file) { QueueFileStats* s = stats::center::instance().muxer_stats(_name)->mutable_queue_file(); _file = std::make_unique(_queue_file_name, s); } - - SPDLOG_LOGGER_TRACE(log_v2::core(), "{} push to file {}", _name, *event); - _file->write(event); - } else { - SPDLOG_LOGGER_TRACE(log_v2::core(), "{} push to queue {}", _name, *event); - _push_to_queue(event); + try { + _file->write(*evt); + SPDLOG_LOGGER_TRACE(log_v2::core(), + "muxer::publish {} publish one event to file {}", + _name, _queue_file_name); + } catch (const std::exception& ex) { + // in case of exception, we lost event. It's mandatory to avoid infinite + // loop in case of permanent disk problem + SPDLOG_LOGGER_ERROR(log_v2::core(), "{} fail to write event to {}: {}", + _name, _queue_file_name, ex.what()); + _file.reset(); + } } - _update_stats(); } + _update_stats(); } /** @@ -507,3 +572,18 @@ void muxer::remove_queue_files() { const std::string& muxer::name() const { return _name; } + +/** + * @brief In case of a muxer reused by a failover, we have to update its + * filters. This is the purpose of this function. + * + * @param r_filters The read filters. + * @param w_filters The write filters. + */ +void muxer::set_filters(muxer::filters r_filters, muxer::filters w_filters) { + std::lock_guard lck(_mutex); + _read_filters = std::move(r_filters); + _write_filters = std::move(w_filters); + _read_filters_str = misc::dump_filters(_read_filters); + _write_filters_str = misc::dump_filters(_write_filters); +} diff --git a/broker/core/src/mysql.cc b/broker/core/src/mysql.cc index 515f1579ea0..af905592f4c 100644 --- a/broker/core/src/mysql.cc +++ b/broker/core/src/mysql.cc @@ -99,24 +99,6 @@ bool mysql::fetch_row(mysql_result& res) { return res.get_connection()->fetch_row(res); } -/** - * This method commits only if the max queries per transaction is reached. - * - * @return true if a commit has been done, false otherwise. - */ -bool mysql::commit_if_needed() { - bool retval(false); - int qpt(_db_cfg.get_queries_per_transaction()); - if (qpt > 1) { - ++_pending_queries; - if (_pending_queries >= qpt) { - commit(); - retval = true; - } - } - return retval; -} - /** * Checks for previous errors. As queries are made asynchronously, errors * may arise after the calls. This function is generally called just before @@ -219,7 +201,7 @@ int mysql::run_query_and_get_int(std::string const& query, * by the mysql connector. * @param fatal A boolean telling if the error is fatal. In that case, an * exception will be thrown if an error occures. - * @param thread A thread id or 0 to keep the library choosing which one. + * @param thread A thread id or -1 to keep the library choosing which one. * * @return The thread id that executed the query. */ @@ -245,7 +227,10 @@ int mysql::run_statement(database::mysql_stmt_base& stmt, * available. * @param error_msg An error message to complete the error message returned * by the mysql connector. - * @param thread_id A thread id or 0 to keep the library choosing which one. + * @param thread_id A thread id or -1 to keep the library choosing which one. + * @param length The max length of a column in characters. If it is too short, + * the query will fail by returning nothing with an error message telling + * this value is too short. * * With this function, the query is done. The promise will provide the result * if available and it will contain an exception if the query failed. @@ -254,14 +239,15 @@ int mysql::run_statement(database::mysql_stmt_base& stmt, */ int mysql::run_statement_and_get_result(database::mysql_stmt& stmt, std::promise&& promise, - int thread_id) { + int thread_id, + size_t length) { _check_errors(); if (thread_id < 0) // Here, we use _current_thread thread_id = choose_best_connection(-1); - _connection[thread_id]->run_statement_and_get_result(stmt, - std::move(promise)); + _connection[thread_id]->run_statement_and_get_result(stmt, std::move(promise), + length); return thread_id; } diff --git a/broker/core/src/mysql_connection.cc b/broker/core/src/mysql_connection.cc index 420907b7208..47d485f9a5a 100644 --- a/broker/core/src/mysql_connection.cc +++ b/broker/core/src/mysql_connection.cc @@ -28,7 +28,6 @@ using namespace com::centreon::broker::database; constexpr const char* mysql_error::msg[]; -const int STR_SIZE = 200; const int MAX_ATTEMPTS = 10; void (mysql_connection::*const mysql_connection::_task_processing_table[])( @@ -323,7 +322,6 @@ void mysql_connection::_prepare(mysql_task* t) { SPDLOG_LOGGER_ERROR(log_v2::sql(), "mysql_connection: Statement already prepared: {} ({})", task->id, task->query); - assert(1 == 0); return; } @@ -458,7 +456,7 @@ void mysql_connection::_statement_res(mysql_task* t) { task->promise.set_exception(std::make_exception_ptr(e)); return; } - MYSQL_BIND* bb(nullptr); + MYSQL_BIND* bb = nullptr; if (task->bind) { bb = const_cast(task->bind->get_bind()); if (task->bulk) { @@ -516,7 +514,7 @@ void mysql_connection::_statement_res(mysql_task* t) { task->promise.set_value(nullptr); } else { int size(mysql_num_fields(prepare_meta_result)); - auto bind = std::make_unique(size, STR_SIZE); + auto bind = std::make_unique(size, task->length); if (mysql_stmt_bind_result(stmt, bind->get_bind())) { std::string err_msg(::mysql_stmt_error(stmt)); @@ -623,8 +621,12 @@ void mysql_connection::_fetch_row_sync(mysql_task* t) { if (stmt_id) { MYSQL_STMT* stmt(_stmt[stmt_id]); int res(mysql_stmt_fetch(stmt)); - if (res != 0) + if (res != 0) { + if (res == MYSQL_DATA_TRUNCATED) + log_v2::sql()->error( + "columns in the current row are too long, data would be truncated"); task->result->get_bind()->set_empty(); + } task->promise.set_value(res == 0); } else { MYSQL_ROW r(mysql_fetch_row(task->result->get())); @@ -754,9 +756,6 @@ void mysql_connection::_run() { _update_stats(); lck.unlock(); - std::chrono::system_clock::time_point last_commit = - std::chrono::system_clock::now(); - bool reconnect_failed_logged = false; std::list> tasks_list; while (_state == running || !_tasks_list.empty()) { @@ -880,7 +879,7 @@ void mysql_connection::_process_while_empty_task( /* Methods executed by the main thread */ /******************************************************************************/ -mysql_connection::mysql_connection(database_config const& db_cfg, +mysql_connection::mysql_connection(const database_config& db_cfg, SqlConnectionStats* stats) : _conn(nullptr), _finish_asked(false), @@ -999,8 +998,10 @@ void mysql_connection::run_statement(database::mysql_stmt_base& stmt, void mysql_connection::run_statement_and_get_result( database::mysql_stmt& stmt, - std::promise&& promise) { - _push(std::make_unique(stmt, std::move(promise))); + std::promise&& promise, + size_t length) { + _push(std::make_unique(stmt, length, + std::move(promise))); } void mysql_connection::finish() { diff --git a/broker/core/src/pool.cc b/broker/core/src/pool.cc index b92ad21cf00..3c75603cf26 100644 --- a/broker/core/src/pool.cc +++ b/broker/core/src/pool.cc @@ -36,10 +36,11 @@ pool& pool::instance() { return *_instance; } -void pool::load(size_t size) { +void pool::load(const std::shared_ptr& io_context, + size_t size) { std::lock_guard lck(_init_m); if (_instance == nullptr) - _instance = new pool(size); + _instance = new pool(io_context, size); else log_v2::core()->error("pool already started."); } @@ -58,7 +59,7 @@ void pool::unload() { * @return the IO context. */ asio::io_context& pool::io_context() { - return instance()._io_context; + return *instance()._io_context; } /** @@ -71,21 +72,29 @@ asio::io_context& pool::io_context() { * The idea here, is that when the pool is started, no stats are done. And when * the stats::center is well started, it asks the pool to start its stats. */ -pool::pool(size_t size) +pool::pool(const std::shared_ptr& io_context, size_t size) : _stats(nullptr), _pool_size{size == 0 ? std::max(std::thread::hardware_concurrency(), 3u) : size}, - _io_context(_pool_size), - _worker{asio::make_work_guard(_io_context)}, + _io_context(io_context), + _worker{asio::make_work_guard(*_io_context)}, _closed(true), - _timer(_io_context), + _timer(*_io_context), _stats_running{false} { std::lock_guard lock(_closed_m); if (_closed) { log_v2::core()->info("Starting the TCP thread pool of {} threads", _pool_size); for (uint32_t i = 0; i < _pool_size; ++i) { - _pool.emplace_back([this] { _io_context.run(); }); + _pool.emplace_back([ctx = _io_context] { + try { + log_v2::core()->info("start of asio thread {:x}", pthread_self()); + ctx->run(); + } catch (const std::exception& e) { + log_v2::core()->critical("catch in io_context run: {} {} thread {:x}", + e.what(), typeid(e).name(), pthread_self()); + } + }); char str[16]; sprintf(str, "pool_thread%u", i); pthread_setname_np(_pool[i].native_handle(), str); @@ -95,16 +104,17 @@ pool::pool(size_t size) } /** - * @brief Start the stats of the pool. This method is called by the stats engine - * when it is ready. + * @brief Start the stats of the pool. This method is called by the stats + * engine when it is ready. * * @param stats The pointer used by the pool to set its data in the stats * engine. */ void pool::start_stats(ThreadPool* stats) { _stats = stats; - /* The only time, we set a data directly to stats, this is because this method - * is called by the stats engine and the _check_latency has not started yet */ + /* The only time, we set a data directly to stats, this is because this + * method is called by the stats engine and the _check_latency has not + * started yet */ _stats->set_size(_pool_size); _stats_running = true; _timer.expires_after(std::chrono::seconds(10)); @@ -159,7 +169,7 @@ void pool::_check_latency(asio::error_code ec) { ec.message()); else { auto start = std::chrono::system_clock::now(); - asio::post(_io_context, [start, this] { + asio::post(*_io_context, [start, this] { auto end = std::chrono::system_clock::now(); auto duration = std::chrono::duration(end - start); stats::center::instance().update( diff --git a/broker/core/src/processing/failover.cc b/broker/core/src/processing/failover.cc index 5b2cbdf686d..80ad5a504b8 100644 --- a/broker/core/src/processing/failover.cc +++ b/broker/core/src/processing/failover.cc @@ -68,7 +68,15 @@ void failover::add_secondary_endpoint(std::shared_ptr endp) { } /** - * Exit failover thread. + * Exit failover thread. This method is called by the endpoint applier when + * the configuration changed. The failover is destroyed and then may be newly + * created. + * + * Inside a failover, we have a muxer. This muxer lives its own life and + * contains a queue file. If the muxer did not finish to write to its queue + * file when exit() is called, we have to be careful with it because if we open + * a new muxer with the same name, we'll have for a moment two splitters + * writing to the same files. */ void failover::exit() { SPDLOG_LOGGER_TRACE(log_v2::core(), "failover '{}' exit.", _name); diff --git a/broker/core/src/processing/feeder.cc b/broker/core/src/processing/feeder.cc index e6ab2c09128..3ac0bc74440 100644 --- a/broker/core/src/processing/feeder.cc +++ b/broker/core/src/processing/feeder.cc @@ -47,7 +47,10 @@ feeder::feeder(const std::string& name, _state{feeder::stopped}, _should_exit{false}, _client(std::move(client)), - _muxer(name, std::move(read_filters), std::move(write_filters), false) { + _muxer(multiplexing::muxer::create(name, + std::move(read_filters), + std::move(write_filters), + false)) { std::unique_lock lck(_state_m); if (!_client) throw msg_fmt("could not process '{}' with no client stream", _name); @@ -78,6 +81,9 @@ feeder::~feeder() { break; } lock.unlock(); + + multiplexing::engine::instance_ptr()->unsubscribe(_muxer.get()); + if (_thread && _thread->joinable()) { _thread->join(); } @@ -94,7 +100,7 @@ bool feeder::is_finished() const noexcept { * @return The read filters used by the feeder. */ const std::string& feeder::_get_read_filters() const { - return _muxer.read_filters_as_str(); + return _muxer->read_filters_as_str(); } /** @@ -103,7 +109,7 @@ const std::string& feeder::_get_read_filters() const { * @return The write filters used by the feeder. */ std::string const& feeder::_get_write_filters() const { - return _muxer.write_filters_as_str(); + return _muxer->write_filters_as_str(); } /** @@ -117,7 +123,7 @@ void feeder::_forward_statistic(nlohmann::json& tree) { _client->statistics(tree); _client_m.unlock(); } - _muxer.statistics(tree); + _muxer->statistics(tree); } void feeder::_callback() noexcept { @@ -141,7 +147,7 @@ void feeder::_callback() noexcept { // Filling stats if (time(nullptr) >= fill_stats_time) { fill_stats_time += 5; - set_queued_events(_muxer.get_event_queue_size()); + set_queued_events(_muxer->get_event_queue_size()); } if (stream_can_read) { @@ -157,7 +163,7 @@ void feeder::_callback() noexcept { *d); { misc::read_lock lock(_client_m); - _muxer.write(d); + _muxer->write(d); } tick(); continue; // Stream read bias. @@ -169,7 +175,7 @@ void feeder::_callback() noexcept { bool timed_out_muxer(true); if (muxer_can_read) try { - timed_out_muxer = !_muxer.read(d, 0); + timed_out_muxer = !_muxer->read(d, 0); } catch (exceptions::shutdown const& e) { muxer_can_read = false; } @@ -180,7 +186,7 @@ void feeder::_callback() noexcept { misc::read_lock lock(_client_m); _client->write(d); } - _muxer.ack_events(1); + _muxer->ack_events(1); tick(); } @@ -205,6 +211,9 @@ void feeder::_callback() noexcept { "feeder: unknown error occured while processing client '{}'", _name); } + // muxer should not receive events + multiplexing::engine::instance_ptr()->unsubscribe(_muxer.get()); + /* If we are here, that is because the loop is finished, and if we want * is_finished() to return true, we have to set _should_exit to true. */ _should_exit = true; @@ -227,13 +236,13 @@ void feeder::_callback() noexcept { _client.reset(); set_state("disconnected"); log_v2::core()->info("feeder: queue files of client '{}' removed", _name); - _muxer.remove_queue_files(); + _muxer->remove_queue_files(); } log_v2::core()->info("feeder: thread of client '{}' will exit", _name); } uint32_t feeder::_get_queued_events() const { - return _muxer.get_event_queue_size(); + return _muxer->get_event_queue_size(); } /** diff --git a/broker/core/test/bbdo/output.cc b/broker/core/test/bbdo/output.cc index 8c4d72b8ae5..208eee088cc 100644 --- a/broker/core/test/bbdo/output.cc +++ b/broker/core/test/bbdo/output.cc @@ -34,6 +34,8 @@ using namespace com::centreon::broker; using namespace com::centreon::broker::misc; +extern std::shared_ptr g_io_context; + class into_memory : public io::stream { public: into_memory() : io::stream("into_memory"), _memory() {} @@ -69,6 +71,7 @@ class OutputTest : public ::testing::Test { public: void SetUp() override { io::data::broker_id = 0; + g_io_context->restart(); try { config::applier::init(0, "broker_test", 0); } catch (std::exception const& e) { @@ -82,7 +85,7 @@ class OutputTest : public ::testing::Test { // The cache must be destroyed before the applier deinit() call. config::applier::deinit(); ::remove("/tmp/broker_test_cache"); - ::remove(log_v2::instance().log_name().c_str()); + ::remove(log_v2::instance()->log_name().c_str()); } }; diff --git a/broker/core/test/compression/stream/read.cc b/broker/core/test/compression/stream/read.cc index 49f1ca4304e..3858b9d31aa 100644 --- a/broker/core/test/compression/stream/read.cc +++ b/broker/core/test/compression/stream/read.cc @@ -26,9 +26,12 @@ using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + class CompressionStreamRead : public ::testing::Test { public: void SetUp() override { + g_io_context->restart(); try { config::applier::init(0, "test_broker", 0); } catch (const std::exception& e) { diff --git a/broker/core/test/compression/stream/write.cc b/broker/core/test/compression/stream/write.cc index 580fc47c85a..16ce298ae3b 100644 --- a/broker/core/test/compression/stream/write.cc +++ b/broker/core/test/compression/stream/write.cc @@ -27,9 +27,12 @@ using namespace com::centreon::broker; using namespace com::centreon::exceptions; +extern std::shared_ptr g_io_context; + class CompressionStreamWrite : public ::testing::Test { public: void SetUp() override { + g_io_context->restart(); try { config::applier::init(0, "test_broker", 0); } catch (const std::exception& e) { diff --git a/broker/core/test/config/init.cc b/broker/core/test/config/init.cc index bcb41d0101c..cd9d33c618f 100644 --- a/broker/core/test/config/init.cc +++ b/broker/core/test/config/init.cc @@ -21,6 +21,8 @@ using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + /** * Check that the logger configuration class can be copied properly. * @@ -28,6 +30,7 @@ using namespace com::centreon::broker; */ TEST(init, init) { // First object. + g_io_context->restart(); config::applier::init(0, "test", 0); ASSERT_NO_THROW(config::applier::deinit()); } diff --git a/broker/core/test/file/splitter/concurrent.cc b/broker/core/test/file/splitter/concurrent.cc index 01c1fb835c3..340c8b6524c 100644 --- a/broker/core/test/file/splitter/concurrent.cc +++ b/broker/core/test/file/splitter/concurrent.cc @@ -1,5 +1,5 @@ /* - * Copyright 2011 - 2019-2022 Centreon (https://www.centreon.com/) + * Copyright 2011 - 2023 Centreon (https://www.centreon.com/) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ * For more information : contact@centreon.com * */ +#include #include #include "com/centreon/broker/exceptions/shutdown.hh" @@ -77,12 +78,13 @@ class write_thread { void callback() { char* buf = new char[_size]; - for (int i(0); i < _size; ++i) + for (int i = 0; i < _size; ++i) buf[i] = i & 255; int wb = 0; - for (int j(0); j < _size; j += wb) { + for (int j = 0; j < _size; j += wb) { wb = _file->write(buf + j, 100); + std::cout << "OK\n"; usleep(rand() % 100); } @@ -165,3 +167,69 @@ TEST_F(FileSplitterConcurrent, MultipleFilesCreated) { RETENTION_DIR, RETENTION_FILE "*"); ASSERT_EQ(entries.size(), 0u); } + +// Given a splitter object +// When twenty writers write in parallel in the splitter +// Then the reader can read all what they wrote and data are not corrupted. +TEST_F(FileSplitterConcurrent, ConcurrentWriteFile10) { + constexpr int COUNT = 20; + constexpr int LENGTH = 1000; + constexpr int BLOCK = 100; + std::vector v; + for (int i = 0; i < COUNT; i++) + v.emplace_back([file = _file.get()]() { + char* buf = new char[LENGTH]; + char v = 1; + int block = BLOCK; + for (int i = 0; i < LENGTH; ++i) { + if (i >= block) { + block += BLOCK; + v++; + } + buf[i] = v; + } + + int wb = 0; + for (int j = 0; j < LENGTH; j += wb) { + wb = file->write(buf + j, BLOCK); + } + + delete[] buf; + }); + + std::vector result(COUNT * LENGTH, '\0'); + + std::thread rt([file = _file.get(), &result]() { + int ret = 0; + int current = 0; + constexpr int size = COUNT * LENGTH; + + do { + try { + ret = file->read(result.data() + current, size - current); + current += ret; + ASSERT_TRUE(current <= size); + } catch (...) { + } + usleep(100); + } while (current < size); + }); + + for (auto& t : v) + t.join(); + + rt.join(); + + ASSERT_EQ(result.size(), COUNT * LENGTH); + ASSERT_EQ(result.size() % BLOCK, 0); + for (uint32_t delta = 0; delta < result.size(); delta += BLOCK) { + absl::Span res(const_cast(result.data()) + delta, BLOCK); + + // Then + char v = res[0]; + for (uint32_t i = 0; i < res.size(); i++) { + ASSERT_EQ(v, res[i]) << " where res[0] = " << v << " delta = " << delta + << " and i = " << i; + } + } +} diff --git a/broker/core/test/file/splitter/split_limited.cc b/broker/core/test/file/splitter/split_limited.cc index a69ecc1c193..13dd70bcc01 100644 --- a/broker/core/test/file/splitter/split_limited.cc +++ b/broker/core/test/file/splitter/split_limited.cc @@ -1,5 +1,5 @@ /* - * Copyright 2011 - 2019 Centreon (https://www.centreon.com/) + * Copyright 2011 - 2023 Centreon (https://www.centreon.com/) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/broker/core/test/main.cc b/broker/core/test/main.cc index bb5259f75d6..8567cdb7d20 100644 --- a/broker/core/test/main.cc +++ b/broker/core/test/main.cc @@ -18,6 +18,11 @@ */ #include #include "com/centreon/broker/config/applier/state.hh" +#include "com/centreon/broker/log_v2.hh" + +std::shared_ptr g_io_context = + std::make_shared(); +bool g_io_context_started = false; class CentreonBrokerEnvironment : public testing::Environment { public: @@ -45,6 +50,9 @@ int main(int argc, char* argv[]) { // Set specific environment. testing::AddGlobalTestEnvironment(new CentreonBrokerEnvironment()); + com::centreon::broker::log_v2::load(g_io_context); // Run all tests. - return (RUN_ALL_TESTS()); + int ret = RUN_ALL_TESTS(); + spdlog::shutdown(); + return ret; } diff --git a/broker/core/test/misc/perfdata.cc b/broker/core/test/misc/perfdata.cc index 9c82cd99656..4f77a2ad6d5 100644 --- a/broker/core/test/misc/perfdata.cc +++ b/broker/core/test/misc/perfdata.cc @@ -27,6 +27,8 @@ using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + /** * Check that the perfdata assignment operator works properly. */ @@ -194,7 +196,10 @@ TEST(MiscPerfdata, DefaultCtor) { class MiscParserParsePerfdata : public testing::Test { public: - void SetUp() override { config::applier::init(0, "test_broker", 0); } + void SetUp() override { + g_io_context->restart(); + config::applier::init(0, "test_broker", 0); + } void TearDown() override { config::applier::deinit(); }; }; diff --git a/broker/core/test/modules/module.cc b/broker/core/test/modules/module.cc index c473650c874..5c8e597e8ec 100644 --- a/broker/core/test/modules/module.cc +++ b/broker/core/test/modules/module.cc @@ -24,9 +24,14 @@ using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + class Modules : public testing::Test { public: - void SetUp() override { config::applier::init(0, "test_broker", 0); } + void SetUp() override { + g_io_context->restart(); + config::applier::init(0, "test_broker", 0); + } void TearDown() override { config::applier::deinit(); } }; diff --git a/broker/core/test/multiplexing/engine/start_stop.cc b/broker/core/test/multiplexing/engine/start_stop.cc index 7c488f77bb0..2ab93bd0c9e 100644 --- a/broker/core/test/multiplexing/engine/start_stop.cc +++ b/broker/core/test/multiplexing/engine/start_stop.cc @@ -33,9 +33,14 @@ const std::string MSG2("foo bar baz"); const std::string MSG3("last message with qux"); const std::string MSG4("no this is the last message"); +extern std::shared_ptr g_io_context; + class StartStop : public testing::Test { public: - void SetUp() override { config::applier::init(0, "test_broker", 0); } + void SetUp() override { + g_io_context->restart(); + config::applier::init(0, "test_broker", 0); + } void TearDown() override { config::applier::deinit(); } }; @@ -52,8 +57,8 @@ TEST_F(StartStop, MultiplexingWorks) { try { // Subscriber. absl::flat_hash_set filters{io::raw::static_type()}; - multiplexing::muxer mux("core_multiplexing_engine_start_stop", filters, - filters, false); + std::shared_ptr mux(multiplexing::muxer::create("core_multiplexing_engine_start_stop", filters, + filters, false)); // Send events through engine. std::array messages{MSG1, MSG2}; @@ -66,7 +71,7 @@ TEST_F(StartStop, MultiplexingWorks) { // Should read no events from muxer. { std::shared_ptr data; - mux.read(data, 0); + mux->read(data, 0); ASSERT_FALSE(data); } @@ -78,7 +83,7 @@ TEST_F(StartStop, MultiplexingWorks) { std::shared_ptr data; bool ret; do { - ret = mux.read(data, 0); + ret = mux->read(data, 0); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } while (!ret); @@ -100,7 +105,7 @@ TEST_F(StartStop, MultiplexingWorks) { std::shared_ptr data; bool ret; do { - ret = mux.read(data, 0); + ret = mux->read(data, 0); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } while (!ret); @@ -124,7 +129,7 @@ TEST_F(StartStop, MultiplexingWorks) { // Read no event. { std::shared_ptr data; - mux.read(data, 0); + mux->read(data, 0); if (data) throw msg_fmt("error at step #6"); } diff --git a/broker/core/test/multiplexing/muxer/read.cc b/broker/core/test/multiplexing/muxer/read.cc index 6e6d5b3dc1d..01161c003b3 100644 --- a/broker/core/test/multiplexing/muxer/read.cc +++ b/broker/core/test/multiplexing/muxer/read.cc @@ -1,5 +1,5 @@ /* - * Copyright 2011 - 2019 Centreon (https://www.centreon.com/) + * Copyright 2011 - 2023 Centreon (https://www.centreon.com/) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,13 +25,16 @@ using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + class MultiplexingMuxerRead : public ::testing::Test { protected: - std::unique_ptr _m; + std::shared_ptr _m; public: void SetUp() override { try { + g_io_context->restart(); config::applier::init(0, "test_broker", 0); stats::center::load(); } catch (std::exception const& e) { @@ -47,16 +50,18 @@ class MultiplexingMuxerRead : public ::testing::Test { void setup(std::string const& name) { multiplexing::muxer::filters f{io::raw::static_type()}; - _m = std::make_unique(name, f, f, false); + _m = multiplexing::muxer::create(name, f, f, false); } void publish_events(int count = 10000) { + std::deque> q; for (int i = 0; i < count; ++i) { std::shared_ptr r{std::make_shared()}; r->resize(sizeof(i)); memcpy(r->data(), &i, sizeof(i)); - _m->publish(r); + q.push_back(std::move(r)); } + _m->publish(q); } void reread_events(int from = 0, int to = 10000) { diff --git a/broker/core/test/multiplexing/publisher/read.cc b/broker/core/test/multiplexing/publisher/read.cc index 99332db448d..3255d98a15f 100644 --- a/broker/core/test/multiplexing/publisher/read.cc +++ b/broker/core/test/multiplexing/publisher/read.cc @@ -23,9 +23,14 @@ using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + class PublisherRead : public testing::Test { public: - void SetUp() override { config::applier::init(0, "test_broker", 0); } + void SetUp() override { + g_io_context->restart(); + config::applier::init(0, "test_broker", 0); + } void TearDown() override { config::applier::deinit(); } }; diff --git a/broker/core/test/multiplexing/publisher/write.cc b/broker/core/test/multiplexing/publisher/write.cc index 1fb8da453bf..377f25865f9 100644 --- a/broker/core/test/multiplexing/publisher/write.cc +++ b/broker/core/test/multiplexing/publisher/write.cc @@ -31,9 +31,14 @@ using namespace com::centreon::broker; const std::string MSG1("0123456789abcdef"); const std::string MSG2("foo bar baz qux"); +extern std::shared_ptr g_io_context; + class PublisherWrite : public testing::Test { public: - void SetUp() override { config::applier::init(0, "test_broker", 0); } + void SetUp() override { + g_io_context->restart(); + config::applier::init(0, "test_broker", 0); + } void TearDown() override { config::applier::deinit(); } }; @@ -49,8 +54,8 @@ TEST_F(PublisherWrite, Write) { // Subscriber. absl::flat_hash_set filters{io::raw::static_type()}; - multiplexing::muxer mux("core_multiplexing_publisher_write", filters, - filters, true); + std::shared_ptr mux(multiplexing::muxer::create( + "core_multiplexing_publisher_write", filters, filters, true)); // Publish event. { @@ -76,7 +81,7 @@ TEST_F(PublisherWrite, Write) { bool ret; int count = 0; do { - ret = mux.read(data, 0); + ret = mux->read(data, 0); std::this_thread::sleep_for(std::chrono::milliseconds(10)); count++; } while (!ret && count < 100); diff --git a/broker/core/test/mysql/mysql.cc b/broker/core/test/mysql/mysql.cc index f65f6d025db..511f5621562 100644 --- a/broker/core/test/mysql/mysql.cc +++ b/broker/core/test/mysql/mysql.cc @@ -1,5 +1,5 @@ /* - * Copyright 2011 - 2022 Centreon (https://www.centreon.com/) + * Copyright 2011 - 2022-2023 Centreon (https://www.centreon.com/) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,9 +49,12 @@ using msg_fmt = com::centreon::exceptions::msg_fmt; using namespace com::centreon::broker; using namespace com::centreon::broker::database; +extern std::shared_ptr g_io_context; + class DatabaseStorageTest : public ::testing::Test { public: void SetUp() override { + g_io_context->restart(); try { config::applier::init(0, "test_broker", 0); } catch (std::exception const& e) { @@ -64,7 +67,7 @@ class DatabaseStorageTest : public ::testing::Test { // When there is no database // Then the mysql creation throws an exception TEST_F(DatabaseStorageTest, NoDatabase) { - database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 9876, "admin", + database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 9876, "root", "centreon", "centreon_storage"); std::unique_ptr ms; ASSERT_THROW(ms.reset(new mysql(db_cfg)), msg_fmt); @@ -74,7 +77,7 @@ TEST_F(DatabaseStorageTest, NoDatabase) { // And when the connection is well done // Then no exception is thrown and the mysql object is well built. TEST_F(DatabaseStorageTest, ConnectionOk) { - database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 3306, "centreon", + database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 3306, "root", "centreon", "centreon_storage"); std::unique_ptr ms; ASSERT_NO_THROW(ms = std::make_unique(db_cfg)); @@ -261,54 +264,6 @@ TEST_F(DatabaseStorageTest, ConnectionOk) { // std::exception); // } // -//// Given a mysql object -//// When a prepare statement is done -//// Then we can bind values to it and execute the statement. -//// Then a commit makes data available in the database. -// TEST_F(DatabaseStorageTest, LastInsertId) { -// database_config db_cfg("MySQL", "127.0.0.1", 3306, "centreon", "centreon", -// "centreon_storage", 5, true, 5); -// std::ostringstream nss; -// nss << "metric_name - " << time(nullptr) << "bis"; -// -// std::ostringstream oss; -// oss << "INSERT INTO metrics" -// << " (index_id, metric_name, unit_name, warn, warn_low," -// " warn_threshold_mode, crit, crit_low," -// " crit_threshold_mode, min, max, current_value," -// " data_source_type)" -// " VALUES (19, '" -// << nss.str() -// << "', 'test/s', 20.0, 40.0, 1, 10.0, 20.0, 1, 0.0, 50.0, 18.0, '2')"; -// -// std::unique_ptr ms(new mysql(db_cfg)); -// // We force the thread 0 -// std::cout << oss.str() << std::endl; -// std::promise promise; -// std::future future = promise.get_future(); -// ms->run_query_and_get_int(oss.str(), std::move(promise), -// mysql_task::int_type::LAST_INSERT_ID); -// int id(future.get()); -// -// // Commit is needed to make the select later. But it is not needed to get -// // the id. Moreover, if we commit before getting the last id, the result -// will -// // be null. -// ms->commit(); -// ASSERT_TRUE(id > 0); -// std::cout << "id = " << id << std::endl; -// oss.str(""); -// oss << "SELECT metric_id FROM metrics WHERE metric_name = '" << nss.str() -// << "'"; -// std::cout << oss.str() << std::endl; -// std::promise promise_r; -// std::future future_r = promise_r.get_future(); -// ms->run_query_and_get_result(oss.str(), std::move(promise_r)); -// mysql_result res(future_r.get()); -// ASSERT_TRUE(ms->fetch_row(res)); -// ASSERT_TRUE(res.value_as_i32(0) == id); -// } -// // TEST_F(DatabaseStorageTest, PrepareQuerySync) { // database_config db_cfg("MySQL", "127.0.0.1", 3306, "centreon", "centreon", // "centreon_storage", 5, true, 5); @@ -553,7 +508,7 @@ TEST_F(DatabaseStorageTest, ConnectionOk) { TEST_F(DatabaseStorageTest, CustomVarStatement) { config::applier::modules modules; modules.load_file("./lib/10-neb.so"); - database_config db_cfg("MySQL", "localhost", MYSQL_SOCKET, 3306, "centreon", + database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 3306, "root", "centreon", "centreon_storage", 5, true, 5); std::unique_ptr ms(new mysql(db_cfg)); query_preparator::event_unique unique; @@ -600,6 +555,18 @@ TEST_F(DatabaseStorageTest, CustomVarStatement) { ms->run_query_and_get_result(query, std::move(promise)); mysql_result res(future.get()); ASSERT_TRUE(ms->fetch_row(res)); + uint64_t r = res.value_as_u64(0); + ASSERT_TRUE(r > 0); + ASSERT_FALSE(ms->fetch_row(res)); + + promise = {}; + future = promise.get_future(); + mysql_stmt stmt(ms->prepare_query(query)); + ms->run_statement_and_get_result(stmt, std::move(promise), -1, 200); + res = future.get(); + ASSERT_TRUE(ms->fetch_row(res)); + r = res.value_as_u64(0); + ASSERT_TRUE(r > 0); ASSERT_FALSE(ms->fetch_row(res)); } @@ -996,26 +963,6 @@ TEST_F(DatabaseStorageTest, CustomVarStatement) { // ASSERT_TRUE(res.value_as_i32(0) == 600); // } // -// TEST_F(DatabaseStorageTest, SelectStatement) { -// modules::loader l; -// l.load_file("./lib/10-neb.so"); -// database_config db_cfg("MySQL", "127.0.0.1", 3306, "centreon", "centreon", -// "centreon_storage", 5, true, 5); -// std::unique_ptr ms(new mysql(db_cfg)); -// std::string query("SELECT value,status FROM data_bin WHERE ctime >= ?"); -// mysql_stmt select_stmt(ms->prepare_query(query)); -// select_stmt.bind_value_as_u64(0, time(nullptr) - 20); -// std::promise promise; -// std::future future = promise.get_future(); -// ms->run_statement_and_get_result(select_stmt, std::move(promise)); -// mysql_result res(future.get()); -// -// while (ms->fetch_row(res)) { -// ASSERT_TRUE(res.value_as_f64(0) == 2.5); -// ASSERT_TRUE(res.value_as_i32(1) == 0); -// } -// } -// // TEST_F(DatabaseStorageTest, DowntimeStatement) { // modules::loader l; // l.load_file("./lib/10-neb.so"); @@ -1313,7 +1260,7 @@ TEST_F(DatabaseStorageTest, CustomVarStatement) { ////} // TEST_F(DatabaseStorageTest, ChooseConnectionByName) { - database_config db_cfg("MySQL", "localhost", MYSQL_SOCKET, 3306, "centreon", + database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 3306, "root", "centreon", "centreon_storage", 5, true, 5); auto ms = std::make_unique(db_cfg); int thread_foo(ms->choose_connection_by_name("foo")); @@ -1335,7 +1282,7 @@ TEST_F(DatabaseStorageTest, ChooseConnectionByName) { // Then we can bind values to it and execute the statement. // Then a commit makes data available in the database. TEST_F(DatabaseStorageTest, RepeatStatements) { - database_config db_cfg("MySQL", "localhost", MYSQL_SOCKET, 3306, "centreon", + database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 3306, "root", "centreon", "centreon_storage", 5, true, 5); auto ms{std::make_unique(db_cfg)}; std::string query1{"DROP TABLE IF EXISTS ut_test"}; @@ -1357,7 +1304,7 @@ TEST_F(DatabaseStorageTest, RepeatStatements) { constexpr int TOTAL = 200000; for (int i = 0; i < TOTAL; i++) { - stmt.bind_value_as_str(0, fmt::format("unit_{}", i)); + stmt.bind_value_as_str(0, fmt::format("unit_{}", i % 100)); stmt.bind_value_as_f64(1, ((double)i) / 500); stmt.bind_value_as_f32(2, ((float)i) / 500 + 12.0f); stmt.bind_value_as_f32(3, ((float)i) / 500 + 25.0f); @@ -1374,16 +1321,53 @@ TEST_F(DatabaseStorageTest, RepeatStatements) { ASSERT_TRUE(ms->fetch_row(res)); std::cout << "***** count = " << res.value_as_i32(0) << std::endl; ASSERT_EQ(res.value_as_i32(0), TOTAL); + + std::string query4( + "SELECT id, unit_name, value, warn, crit, hidden, metric FROM ut_test " + "WHERE id < 20"); + promise = {}; + future = promise.get_future(); + ms->run_query_and_get_result(query4, std::move(promise)); + res = future.get(); + uint32_t count_query = 0; + while (ms->fetch_row(res)) { + count_query++; + ASSERT_TRUE(res.value_as_u64(0) < 20); + ASSERT_TRUE(res.value_as_str(1).substr(0, 5) == "unit_"); + ASSERT_TRUE(res.value_as_f64(3) >= 12); + ASSERT_TRUE(res.value_as_f32(4) >= 25); + ASSERT_TRUE(!res.value_as_bool(5)); + ASSERT_TRUE(res.value_as_str(6).substr(0, 7) == "metric_"); + } + ASSERT_TRUE(count_query); + + /* Same query with a prepared statement */ + promise = {}; + future = promise.get_future(); + mysql_stmt select_stmt(ms->prepare_query(query4)); + ms->run_statement_and_get_result(select_stmt, std::move(promise), -1, 200); + res = future.get(); + uint32_t count_statement = 0; + while (ms->fetch_row(res)) { + count_statement++; + ASSERT_TRUE(res.value_as_i64(0) < 20); + ASSERT_TRUE(res.value_as_str(1).substr(0, 5) == "unit_"); + ASSERT_TRUE(res.value_as_f64(3) >= 12); + ASSERT_TRUE(res.value_as_f32(4) >= 25); + ASSERT_TRUE(!res.value_as_bool(5)); + ASSERT_TRUE(res.value_as_str(6).substr(0, 7) == "metric_"); + } + ASSERT_TRUE(count_query == count_statement); } TEST_F(DatabaseStorageTest, CheckBulkStatement) { - database_config db_cfg("MySQL", "localhost", MYSQL_SOCKET, 3306, "centreon", + database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 3306, "root", "centreon", "centreon_storage", 5, true, 5); auto ms{std::make_unique(db_cfg)}; const char* version = ms->get_server_version(); std::vector arr = absl::StrSplit(version, absl::ByAnyChar(".-")); - ASSERT_EQ(arr.size(), 4u); + ASSERT_TRUE(arr.size() >= 4u); uint32_t major; uint32_t minor; uint32_t patch; @@ -1414,6 +1398,7 @@ TEST_F(DatabaseStorageTest, CheckBulkStatement) { int step = 0; auto bb = stmt.create_bind(); + ASSERT_TRUE(bb->empty()); bb->reserve(20000); for (int j = 0; j < TOTAL; j++) { bb->set_value_as_str(0, fmt::format("unit_{}", step)); @@ -1444,13 +1429,15 @@ TEST_F(DatabaseStorageTest, CheckBulkStatement) { } TEST_F(DatabaseStorageTest, UpdateBulkStatement) { - database_config db_cfg("MySQL", "localhost", MYSQL_SOCKET, 3306, "centreon", + constexpr int TOTAL = 20; + + database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 3306, "root", "centreon", "centreon_storage", 5, true, 5); auto ms{std::make_unique(db_cfg)}; const char* version = ms->get_server_version(); std::vector arr = absl::StrSplit(version, absl::ByAnyChar(".-")); - ASSERT_EQ(arr.size(), 4u); + ASSERT_TRUE(arr.size() >= 4u); uint32_t major; uint32_t minor; uint32_t patch; @@ -1461,20 +1448,19 @@ TEST_F(DatabaseStorageTest, UpdateBulkStatement) { if (server_name == "MariaDB" && (major >= 10 || (major == 10 && minor >= 2))) { std::string query{ - "UPDATE ut_test SET value=?, warn=?, crit=?, metric=? WHERE " + "UPDATE ut_test SET value=?, warn=?, crit=?, metric=?, hidden=? WHERE " "unit_name=?"}; mysql_bulk_stmt s(query); ms->prepare_statement(s); auto b = s.create_bind(); - constexpr int TOTAL = 5; - for (int j = 0; j < TOTAL; j++) { - b->set_value_as_str(4, fmt::format("unit_{}", j)); + b->set_value_as_str(5, fmt::format("unit_{}", j)); b->set_value_as_f64(0, j); b->set_value_as_f32(1, 1000); b->set_value_as_f32(2, 2000); b->set_value_as_str(3, fmt::format("metric_{}", j)); + b->set_value_as_str(4, fmt::format("{}", j % 2)); b->next_row(); } s.set_bind(std::move(b)); @@ -1488,7 +1474,575 @@ TEST_F(DatabaseStorageTest, UpdateBulkStatement) { ASSERT_TRUE(ms->fetch_row(res)); std::cout << "***** count = " << res.value_as_i32(0) << std::endl; - ASSERT_EQ(res.value_as_i32(0), TOTAL * 10); + ASSERT_TRUE(res.value_as_i32(0) == TOTAL * 10); } else std::cout << "Test not executed." << std::endl; + + std::string query( + "SELECT DISTINCT id,value,warn,metric,hidden FROM ut_test WHERE id < ? " + "LIMIT ?"); + mysql_stmt select_stmt(ms->prepare_query(query)); + select_stmt.bind_value_as_i32(0, TOTAL); + select_stmt.bind_value_as_i32(1, TOTAL); + std::promise promise; + std::future future = promise.get_future(); + ms->run_statement_and_get_result(select_stmt, std::move(promise), -1, 200); + mysql_result res(future.get()); + + bool inside = false; + while (ms->fetch_row(res)) { + inside = true; + ASSERT_EQ(res.value_as_f64(1), res.value_as_i32(0) - 1); + ASSERT_EQ(res.value_as_f32(2), 1000); + ASSERT_EQ(res.value_as_str(3), + fmt::format("metric_{}", res.value_as_i32(0) - 1)); + ASSERT_EQ(res.value_as_tiny(4), (res.value_as_i32(0) + 1) % 2); + ASSERT_EQ(res.value_as_bool(4), res.value_as_i32(0) % 2 ? false : true); + } + ASSERT_TRUE(inside); +} + +// Given a mysql object +// When a prepare statement is done +// Then we can bind values to it and execute the statement. +// Then a commit makes data available in the database. +TEST_F(DatabaseStorageTest, LastInsertId) { + database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 3306, "root", + "centreon", "centreon_storage", 5, true, 5); + time_t now = time(nullptr); + std::string query( + fmt::format("INSERT INTO metrics" + " (index_id, metric_name, unit_name, warn, warn_low," + " warn_threshold_mode, crit, crit_low," + " crit_threshold_mode, min, max, current_value," + " data_source_type)" + " VALUES (19, 'metric_name - {}bis', 'test/s', 20.0, 40.0, " + "1, 10.0, 20.0, 1, 0.0, 50.0, 18.0, '2')", + now)); + + auto ms = std::make_unique(db_cfg); + // We force the thread 0 + std::promise promise; + std::future future = promise.get_future(); + ms->run_query_and_get_int(query, std::move(promise), + mysql_task::int_type::LAST_INSERT_ID); + int id = future.get(); + + // Commit is needed to make the select later. But it is not needed to get + // the id. Moreover, if we commit before getting the last id, the result + // will be null. + ms->commit(); + ASSERT_TRUE(id > 0); + std::cout << "id = " << id << std::endl; + query = fmt::format( + "SELECT metric_id FROM metrics WHERE metric_name = 'metric_name - " + "{}bis'", + now); + + std::promise promise_r; + std::future future_r = promise_r.get_future(); + ms->run_query_and_get_result(query, std::move(promise_r)); + mysql_result res(future_r.get()); + ASSERT_TRUE(ms->fetch_row(res)); + ASSERT_TRUE(res.value_as_i32(0) == id); +} + +TEST_F(DatabaseStorageTest, BulkStatementWithNullStr) { + database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 3306, "root", + "centreon", "centreon_storage", 5, true, 5); + auto ms{std::make_unique(db_cfg)}; + const char* version = ms->get_server_version(); + std::vector arr = + absl::StrSplit(version, absl::ByAnyChar(".-")); + ASSERT_TRUE(arr.size() >= 4u); + uint32_t major; + uint32_t minor; + uint32_t patch; + EXPECT_TRUE(absl::SimpleAtoi(arr[0], &major)); + EXPECT_TRUE(absl::SimpleAtoi(arr[1], &minor)); + EXPECT_TRUE(absl::SimpleAtoi(arr[2], &patch)); + absl::string_view server_name(arr[3]); + ASSERT_EQ(server_name, "MariaDB"); + if (major >= 10 || (major == 10 && minor >= 2)) { + std::string query1{"DROP TABLE IF EXISTS ut_test"}; + std::string query2{ + "CREATE TABLE ut_test (id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT " + "PRIMARY KEY, unit_name CHAR(30), value DOUBLE, warn FLOAT, crit " + "FLOAT, hidden enum('0', '1') DEFAULT '0', metric VARCHAR(30) " + "CHARACTER SET utf8mb4 DEFAULT NULL) DEFAULT CHARSET=utf8mb4"}; + ms->run_query(query1); + ms->commit(); + ms->run_query(query2); + ms->commit(); + + std::string query( + "INSERT INTO ut_test (unit_name, value, warn, crit, metric) VALUES " + "(?,?,?,?,?)"); + mysql_bulk_stmt stmt(query); + ms->prepare_statement(stmt); + + constexpr int TOTAL = 200000; + + int step = 0; + auto bb = stmt.create_bind(); + bb->reserve(20000); + for (int j = 0; j < TOTAL; j++) { + if (step % 2) + bb->set_value_as_str(0, fmt::format("unit_{}", step)); + else + bb->set_null_str(0); + if (step % 2 == 0) + bb->set_value_as_f64(1, ((double)step) / 500); + else + bb->set_null_f64(1); + bb->set_value_as_f32(2, ((float)step) / 500 + 12.0f); + bb->set_value_as_f32(3, ((float)step) / 500 + 25.0f); + bb->set_value_as_str(4, fmt::format("metric_{}", step)); + bb->next_row(); + step++; + if (step == 20000) { + step = 0; + stmt.set_bind(std::move(bb)); + ms->run_statement(stmt); + bb = stmt.create_bind(); + } + } + ms->commit(); + std::string query3{ + "SELECT count(*) from ut_test WHERE unit_name is NULL OR value is " + "NULL"}; + std::promise promise; + std::future future = promise.get_future(); + ms->run_query_and_get_result(query3, std::move(promise)); + mysql_result res(future.get()); + + ASSERT_TRUE(ms->fetch_row(res)); + std::cout << "***** count = " << res.value_as_i32(0) << std::endl; + ASSERT_EQ(res.value_as_i32(0), TOTAL); + } +} + +TEST_F(DatabaseStorageTest, RepeatStatementsWithNull) { + database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 3306, "root", + "centreon", "centreon_storage", 5, true, 5); + auto ms{std::make_unique(db_cfg)}; + std::string query1{"DROP TABLE IF EXISTS ut_test"}; + std::string query2{ + "CREATE TABLE ut_test (id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT " + "PRIMARY KEY, unit_name CHAR(30), value DOUBLE, warn FLOAT, crit FLOAT, " + "hidden enum('0', '1') DEFAULT '0', metric VARCHAR(30) CHARACTER SET " + "utf8mb4 DEFAULT NULL) DEFAULT CHARSET=utf8mb4"}; + ms->run_query(query1); + ms->commit(); + ms->run_query(query2); + ms->commit(); + + std::string query( + "INSERT INTO ut_test (unit_name, value, warn, crit, metric) VALUES " + "(?,?,?,?,?)"); + mysql_stmt stmt(ms->prepare_query(query)); + + constexpr int TOTAL = 20000; + + for (int i = 0; i < TOTAL; i++) { + if (i % 2 == 0) + stmt.bind_value_as_str(0, fmt::format("unit_{}", i % 100)); + else + stmt.bind_null_str(0); + if (i % 2 == 1) + stmt.bind_value_as_f64(1, ((double)i) / 500); + else + stmt.bind_null_f64(1); + stmt.bind_value_as_f32(2, ((float)i) / 500 + 12.0f); + stmt.bind_value_as_f32(3, ((float)i) / 500 + 25.0f); + stmt.bind_value_as_str(4, fmt::format("metric_{}", i)); + ms->run_statement(stmt); + } + ms->commit(); + std::string query3{ + "SELECT count(*) from ut_test WHERE unit_name IS NULL OR value IS NULL"}; + std::promise promise; + std::future future = promise.get_future(); + ms->run_query_and_get_result(query3, std::move(promise)); + mysql_result res(future.get()); + + ASSERT_TRUE(ms->fetch_row(res)); + std::cout << "***** count = " << res.value_as_i32(0) << std::endl; + ASSERT_EQ(res.value_as_i32(0), TOTAL); +} + +TEST_F(DatabaseStorageTest, RepeatStatementsWithBigStrings) { + database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 3306, "root", + "centreon", "centreon_storage", 5, true, 5); + auto ms{std::make_unique(db_cfg)}; + std::string query1{"DROP TABLE IF EXISTS ut_test"}; + std::string query2{ + "CREATE TABLE ut_test (id BIGINT NOT NULL AUTO_INCREMENT " + "PRIMARY KEY, name VARCHAR(1000), value DOUBLE, t TINYINT, e enum('a', " + "'b', " + "'c') DEFAULT 'a')"}; + ms->run_query(query1); + ms->commit(); + ms->run_query(query2); + ms->commit(); + + std::string query("INSERT INTO ut_test (name, value, t, e) VALUES (?,?,?,?)"); + mysql_stmt stmt(ms->prepare_query(query)); + + constexpr int TOTAL = 200; + + for (int i = 0; i < TOTAL; i++) { + stmt.bind_value_as_str(0, fmt::format("{:a>{}}", i, 500)); + stmt.bind_value_as_f64(1, static_cast(i)); + stmt.bind_value_as_tiny(2, i % 100); + stmt.bind_value_as_tiny(3, (i % 3) + 1); + ms->run_statement(stmt); + } + ms->commit(); + + std::string query4("SELECT id, name, value, t, e FROM ut_test"); + std::promise promise; + std::future future = promise.get_future(); + ms->run_query_and_get_result(query4, std::move(promise)); + mysql_result res = future.get(); + size_t count = 0; + while (ms->fetch_row(res)) { + count++; + int64_t id = res.value_as_i64(0); + ASSERT_TRUE(id >= 1 && id <= TOTAL); + + double value = res.value_as_f64(2); + int32_t ivalue = static_cast(value); + ASSERT_TRUE(value >= 0 && value <= TOTAL - 1); + + std::string name(res.value_as_str(1)); + std::string exp_name(fmt::format("{:a>{}}", ivalue, 500)); + + ASSERT_EQ(name, exp_name); + + ASSERT_EQ(res.value_as_tiny(3), ivalue % 100); + + char exp_char = 'a' + (ivalue % 3); + char e = res.value_as_str(4)[0]; + ASSERT_EQ(e, exp_char); + } + ASSERT_EQ(count, TOTAL); + + mysql_stmt select_stmt(ms->prepare_query(query4)); + for (size_t length : {200, 1000}) { + std::cout << "Case with length = " << length << std::endl; + promise = {}; + future = promise.get_future(); + ms->run_statement_and_get_result(select_stmt, std::move(promise), -1, + length); + res = future.get(); + count = 0; + + if (length == 200) + testing::internal::CaptureStdout(); + + while (ms->fetch_row(res)) { + count++; + int32_t id = res.value_as_u32(0); + ASSERT_TRUE(id >= 1 && id <= TOTAL); + + double value = res.value_as_f64(2); + int32_t ivalue = static_cast(value); + ASSERT_TRUE(value >= 0 && value <= TOTAL - 1); + + std::string name(res.value_as_str(1)); + /* A string "aaaaa.....aaaaaNNN" where a is repeated 500 times and + * NNN is ivalue */ + std::string exp_name(fmt::format("{:a>{}}", ivalue, 500)); + + ASSERT_EQ(name, exp_name); + + ASSERT_EQ(res.value_as_tiny(3), ivalue % 100); + + char exp_char = 'a' + (ivalue % 3); + char e = res.value_as_str(4)[0]; + ASSERT_EQ(e, exp_char); + } + if (length == 200) { + std::string output(testing::internal::GetCapturedStdout()); + std::cout << output << std::endl; + ASSERT_TRUE(output.find("columns in the current row are too long") != + std::string::npos); + } else + ASSERT_EQ(count, TOTAL); + } +} + +TEST_F(DatabaseStorageTest, RepeatStatementsWithNullValues) { + database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 3306, "root", + "centreon", "centreon_storage", 5, true, 5); + auto ms{std::make_unique(db_cfg)}; + std::string query1{"DROP TABLE IF EXISTS ut_test"}; + std::string query2{ + "CREATE TABLE ut_test (id BIGINT NOT NULL AUTO_INCREMENT " + "PRIMARY KEY, name VARCHAR(1000), value DOUBLE, t TINYINT, e enum('a', " + "'b', 'c') DEFAULT 'a', b TINYINT)"}; + ms->run_query(query1); + ms->commit(); + ms->run_query(query2); + ms->commit(); + + std::string query("INSERT INTO ut_test (name,b) VALUES (?,?)"); + mysql_stmt stmt(ms->prepare_query(query)); + + constexpr int TOTAL = 200; + + for (int i = 0; i < TOTAL; i++) { + stmt.bind_value_as_str(0, fmt::format("foo{}", i)); + stmt.bind_value_as_tiny(1, i % 2); + ms->run_statement(stmt); + } + ms->commit(); + + std::string query4("SELECT id, value, b FROM ut_test LIMIT 5"); + std::promise promise; + std::future future = promise.get_future(); + ms->run_query_and_get_result(query4, std::move(promise)); + mysql_result res = future.get(); + bool inside = false; + while (ms->fetch_row(res)) { + inside = true; + int64_t id = res.value_as_i64(0); + ASSERT_TRUE(id >= 1 && id <= TOTAL); + + bool value = res.value_is_null(0); + ASSERT_FALSE(value); + + value = res.value_is_null(1); + ASSERT_TRUE(value); + + double val = res.value_as_f64(1); + ASSERT_EQ(val, 0); + + char b = res.value_as_tiny(2); + ASSERT_TRUE(b == 0 || b == 1); + + bool bb = res.value_as_bool(2); + ASSERT_TRUE((b && bb) || (!b && !bb)); + } + ASSERT_TRUE(inside); + + mysql_stmt select_stmt(ms->prepare_query(query4)); + promise = {}; + future = promise.get_future(); + ms->run_statement_and_get_result(select_stmt, std::move(promise), -1, 50); + res = future.get(); + inside = false; + + while (ms->fetch_row(res)) { + inside = true; + int32_t id = res.value_as_i64(0); + ASSERT_TRUE(id >= 1 && id <= TOTAL); + + bool value = res.value_is_null(0); + ASSERT_FALSE(value); + + value = res.value_is_null(1); + ASSERT_TRUE(value); + + double val = res.value_as_f64(1); + ASSERT_EQ(val, 0); + } + ASSERT_TRUE(inside); +} + +TEST_F(DatabaseStorageTest, BulkStatementsWithNullValues) { + database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 3306, "root", + "centreon", "centreon_storage", 5, true, 5); + auto ms{std::make_unique(db_cfg)}; + std::string query1{"DROP TABLE IF EXISTS ut_test"}; + std::string query2{ + "CREATE TABLE ut_test (id BIGINT NOT NULL AUTO_INCREMENT " + "PRIMARY KEY, name VARCHAR(1000), value DOUBLE, t TINYINT, e enum('a', " + "'b', 'c') DEFAULT 'a', b TINYINT, i INT, u INT UNSIGNED)"}; + ms->run_query(query1); + ms->commit(); + ms->run_query(query2); + ms->commit(); + + std::string query("INSERT INTO ut_test (name,b, i, u) VALUES (?,?, ?, ?)"); + mysql_bulk_stmt stmt(query); + ms->prepare_statement(stmt); + + auto bb = stmt.create_bind(); + bb->reserve(200); + constexpr int TOTAL = 200; + + for (int i = 0; i < TOTAL; i++) { + ASSERT_EQ(bb->current_row(), i); + if (i % 2) { + bb->set_value_as_str(0, fmt::format("foo{}", i)); + bb->set_value_as_tiny(1, (i / 2) % 2); + bb->set_value_as_i32(2, i + 2); + bb->set_value_as_u32(3, i); + } else { + bb->set_null_str(0); + bb->set_null_tiny(1); + bb->set_null_i32(2); + bb->set_null_u32(3); + } + bb->next_row(); + } + stmt.set_bind(std::move(bb)); + ms->run_statement(stmt); + ms->commit(); + + std::string query4("SELECT id, value, b, i, u FROM ut_test LIMIT 5"); + std::promise promise; + std::future future = promise.get_future(); + ms->run_query_and_get_result(query4, std::move(promise)); + mysql_result res = future.get(); + bool inside1 = false; + bool inside2 = false; + while (ms->fetch_row(res)) { + if (res.value_is_null(2)) { + inside1 = true; + ASSERT_TRUE(!res.value_is_null(0)); + ASSERT_TRUE(res.value_is_null(1)); + ASSERT_TRUE(res.value_is_null(3)); + ASSERT_TRUE(res.value_is_null(4)); + } else { + inside2 = true; + int64_t id = res.value_as_i64(0); + ASSERT_TRUE(id >= 1 && id <= TOTAL); + + bool value = res.value_is_null(0); + ASSERT_FALSE(value); + + value = res.value_is_null(1); + ASSERT_TRUE(value); + + double val = res.value_as_f64(1); + ASSERT_EQ(val, 0); + + char b = res.value_as_tiny(2); + ASSERT_TRUE(b == 0 || b == 1); + + bool bb = res.value_as_bool(2); + ASSERT_TRUE((b && bb) || (!b && !bb)); + + int32_t i = res.value_as_i32(3); + ASSERT_TRUE(i < TOTAL + 2 && i >= 2); + + int32_t u = res.value_as_u32(4); + ASSERT_EQ(u + 2, i); + } + } + ASSERT_TRUE(inside1 && inside2); + + mysql_stmt select_stmt(ms->prepare_query(query4)); + promise = {}; + future = promise.get_future(); + ms->run_statement_and_get_result(select_stmt, std::move(promise), -1, 50); + res = future.get(); + bool inside = false; + + while (ms->fetch_row(res)) { + inside = true; + int32_t id = res.value_as_i64(0); + ASSERT_TRUE(id >= 1 && id <= TOTAL); + + bool value = res.value_is_null(0); + ASSERT_FALSE(value); + + value = res.value_is_null(1); + ASSERT_TRUE(value); + + double val = res.value_as_f64(1); + ASSERT_EQ(val, 0); + } + ASSERT_TRUE(inside); +} + +TEST_F(DatabaseStorageTest, RepeatStatementsWithBooleanValues) { + database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 3306, "root", + "centreon", "centreon_storage", 5, true, 5); + auto ms{std::make_unique(db_cfg)}; + std::string query1{"DROP TABLE IF EXISTS ut_test"}; + std::string query2{ + "CREATE TABLE ut_test (id BIGINT NOT NULL AUTO_INCREMENT " + "PRIMARY KEY, name VARCHAR(1000), value DOUBLE, t TINYINT, e enum('a', " + "'b', 'c') DEFAULT 'a', b TINYINT)"}; + ms->run_query(query1); + ms->commit(); + ms->run_query(query2); + ms->commit(); + + std::string query("INSERT INTO ut_test (name,b, t) VALUES (?,?,?)"); + mysql_stmt stmt(ms->prepare_query(query)); + + constexpr int TOTAL = 200; + + for (int i = 0; i < TOTAL; i++) { + stmt.bind_value_as_str(0, fmt::format("foo{}", i)); + stmt.bind_value_as_bool(1, (i % 2) == 0); + stmt.bind_value_as_bool(2, (i % 2) == 1); + ms->run_statement(stmt); + } + ms->commit(); + + std::string query4("SELECT name, b, t FROM ut_test LIMIT 5"); + std::promise promise; + std::future future = promise.get_future(); + ms->run_query_and_get_result(query4, std::move(promise)); + mysql_result res = future.get(); + bool inside = false; + while (ms->fetch_row(res)) { + inside = true; + ASSERT_TRUE(res.value_as_bool(1) != res.value_as_bool(2)); + } + ASSERT_TRUE(inside); +} + +TEST_F(DatabaseStorageTest, BulkStatementsWithBooleanValues) { + database_config db_cfg("MySQL", "127.0.0.1", MYSQL_SOCKET, 3306, "root", + "centreon", "centreon_storage", 5, true, 5); + auto ms{std::make_unique(db_cfg)}; + std::string query1{"DROP TABLE IF EXISTS ut_test"}; + std::string query2{ + "CREATE TABLE ut_test (id BIGINT NOT NULL AUTO_INCREMENT " + "PRIMARY KEY, name VARCHAR(1000), value DOUBLE, t TINYINT, e enum('a', " + "'b', 'c') DEFAULT 'a', b TINYINT, i INT, u INT UNSIGNED)"}; + ms->run_query(query1); + ms->commit(); + ms->run_query(query2); + ms->commit(); + + std::string query("INSERT INTO ut_test (name,b, t) VALUES (?,?, ?)"); + mysql_bulk_stmt stmt(query); + ms->prepare_statement(stmt); + + auto bb = stmt.create_bind(); + bb->reserve(200); + constexpr int TOTAL = 200; + + for (int i = 0; i < TOTAL; i++) { + ASSERT_EQ(bb->current_row(), i); + bb->set_value_as_str(0, fmt::format("foo{}", i)); + bb->set_value_as_bool(1, (i % 2) == 0); + bb->set_value_as_bool(2, (i % 2) == 1); + bb->next_row(); + } + stmt.set_bind(std::move(bb)); + ms->run_statement(stmt); + ms->commit(); + + std::string query4("SELECT id, value, b, t FROM ut_test LIMIT 5"); + std::promise promise; + std::future future = promise.get_future(); + ms->run_query_and_get_result(query4, std::move(promise)); + mysql_result res = future.get(); + bool inside1 = false; + while (ms->fetch_row(res)) { + inside1 = true; + ASSERT_TRUE(res.value_as_int(2) <= 1); + ASSERT_TRUE(res.value_as_int(3) <= 1); + ASSERT_NE(res.value_as_bool(2), res.value_as_bool(3)); + } + ASSERT_TRUE(inside1); } diff --git a/broker/core/test/processing/acceptor.cc b/broker/core/test/processing/acceptor.cc index d7b5a3dc5f2..9a3e92c918d 100644 --- a/broker/core/test/processing/acceptor.cc +++ b/broker/core/test/processing/acceptor.cc @@ -26,9 +26,12 @@ using namespace com::centreon::broker; using namespace com::centreon::broker::processing; +extern std::shared_ptr g_io_context; + class ProcessingTest : public ::testing::Test { public: void SetUp() override { + g_io_context->restart(); try { config::applier::init(0, "test_broker", 0); } catch (std::exception const& e) { diff --git a/broker/core/test/processing/feeder.cc b/broker/core/test/processing/feeder.cc index 736ba29d15b..03b5259c916 100644 --- a/broker/core/test/processing/feeder.cc +++ b/broker/core/test/processing/feeder.cc @@ -30,6 +30,8 @@ using namespace com::centreon::broker; using namespace com::centreon::broker::processing; +extern std::shared_ptr g_io_context; + class TestStream : public io::stream { public: TestStream() : io::stream("TestStream") {} @@ -45,7 +47,8 @@ class TestFeeder : public ::testing::Test { public: void SetUp() override { - pool::load(0); + g_io_context->restart(); + pool::load(g_io_context, 0); stats::center::load(); config::applier::state::load(); file::disk_accessor::load(10000); diff --git a/broker/core/test/rpc/brokerrpc.cc b/broker/core/test/rpc/brokerrpc.cc index a2b8910be53..25aa8e79e9b 100644 --- a/broker/core/test/rpc/brokerrpc.cc +++ b/broker/core/test/rpc/brokerrpc.cc @@ -33,10 +33,13 @@ using namespace com::centreon; using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + class BrokerRpc : public ::testing::Test { public: void SetUp() override { - pool::pool::load(0); + g_io_context->restart(); + pool::load(g_io_context, 0); stats::center::load(); io::protocols::load(); io::events::load(); diff --git a/broker/grpc/test/acceptor.cc b/broker/grpc/test/acceptor.cc index b56a3ace90e..28f9a66be76 100644 --- a/broker/grpc/test/acceptor.cc +++ b/broker/grpc/test/acceptor.cc @@ -34,9 +34,11 @@ using namespace com::centreon::broker; using namespace com::centreon::broker::grpc; using namespace com::centreon::exceptions; +extern std::shared_ptr g_io_context; + class GrpcTlsTest : public ::testing::Test { public: - void SetUp() override { pool::load(0); } + void SetUp() override { pool::load(g_io_context, 1); } void TearDown() override { pool::unload(); } }; @@ -49,21 +51,21 @@ static auto read_file = [](const std::string& path) { return ss.str(); }; -//TEST_F(GrpcTlsTest, TlsStream) { +// TEST_F(GrpcTlsTest, TlsStream) { // /* Let's prepare certificates */ // std::string hostname = misc::exec("hostname --fqdn"); // hostname = misc::string::trim(hostname); // std::string server_cmd( // fmt::format("openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 " -// "-keyout /tmp/server.key -out /tmp/server.crt -subj '/CN={}'", -// hostname)); +// "-keyout /tmp/server.key -out /tmp/server.crt -subj +// '/CN={}'", hostname)); // std::cout << server_cmd << std::endl; // system(server_cmd.c_str()); // // std::string client_cmd( // fmt::format("openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 " -// "-keyout /tmp/client.key -out /tmp/client.crt -subj '/CN={}'", -// hostname)); +// "-keyout /tmp/client.key -out /tmp/client.crt -subj +// '/CN={}'", hostname)); // std::cout << client_cmd << std::endl; // system(client_cmd.c_str()); // @@ -124,21 +126,21 @@ static auto read_file = [](const std::string& path) { // cbd.join(); //} -//TEST_F(GrpcTlsTest, TlsStreamCaHostname) { +// TEST_F(GrpcTlsTest, TlsStreamCaHostname) { // /* Let's prepare certificates */ // const static std::string s_hostname{"saperlifragilistic"}; // const static std::string c_hostname{"foobar"}; // std::string server_cmd( // fmt::format("openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 " -// "-keyout /tmp/server.key -out /tmp/server.crt -subj '/CN={}'", -// s_hostname)); +// "-keyout /tmp/server.key -out /tmp/server.crt -subj +// '/CN={}'", s_hostname)); // std::cout << server_cmd << std::endl; // system(server_cmd.c_str()); // // std::string client_cmd( // fmt::format("openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 " -// "-keyout /tmp/client.key -out /tmp/client.crt -subj '/CN={}'", -// c_hostname)); +// "-keyout /tmp/client.key -out /tmp/client.crt -subj +// '/CN={}'", c_hostname)); // std::cout << client_cmd << std::endl; // system(client_cmd.c_str()); // diff --git a/broker/grpc/test/channel_test.cc b/broker/grpc/test/channel_test.cc index bb87f94ff4e..5c197c05352 100644 --- a/broker/grpc/test/channel_test.cc +++ b/broker/grpc/test/channel_test.cc @@ -25,10 +25,13 @@ using namespace com::centreon::broker; using namespace com::centreon::broker::grpc; using namespace com::centreon::exceptions; +extern std::shared_ptr g_io_context; + class grpc_channel_tester : public testing::Test { public: static void SetUpTestSuite() { - com::centreon::broker::pool::load(1); + g_io_context->restart(); + com::centreon::broker::pool::load(g_io_context, 1); // log_v2::grpc()->set_level(spdlog::level::trace); } }; diff --git a/broker/grpc/test/stream_test.cc b/broker/grpc/test/stream_test.cc index c7fdbcc802f..5603f5ff7da 100644 --- a/broker/grpc/test/stream_test.cc +++ b/broker/grpc/test/stream_test.cc @@ -23,6 +23,8 @@ using namespace com::centreon::broker; using namespace com::centreon::exceptions; +extern std::shared_ptr g_io_context; + com::centreon::broker::grpc::grpc_config::pointer conf( std::make_shared( "127.0.0.1:4444", @@ -71,7 +73,8 @@ class grpc_test_server : public ::testing::TestWithParam { // log_v2::grpc()->set_level(spdlog::level::trace); s = std::make_unique(conf); std::this_thread::sleep_for(std::chrono::milliseconds(50)); - com::centreon::broker::pool::load(1); + g_io_context->restart(); + com::centreon::broker::pool::load(g_io_context, 1); } static void TearDownTestSuite() { s.reset(); }; @@ -162,7 +165,7 @@ class grpc_comm_failure : public ::testing::TestWithParam { s = std::make_unique(conf_relay_out); relay = std::make_unique( "127.0.0.1", relay_listen_port, "127.0.0.1", server_listen_port); - com::centreon::broker::pool::load(1); + com::centreon::broker::pool::load(std::make_shared(), 1); } static void TearDownTestSuite() { s.reset(); @@ -363,7 +366,7 @@ class grpc_test_server_crypted : public ::testing::TestWithParam { // log_v2::grpc()->set_level(spdlog::level::trace); s = std::make_unique( conf_crypted_server1234); - com::centreon::broker::pool::load(1); + com::centreon::broker::pool::load(std::make_shared(), 1); } static void TearDownTestSuite() { s.reset(); }; diff --git a/broker/lua/test/lua.cc b/broker/lua/test/lua.cc index b6edecb247f..9b5f5c67a77 100644 --- a/broker/lua/test/lua.cc +++ b/broker/lua/test/lua.cc @@ -45,9 +45,12 @@ using namespace com::centreon::broker::lua; #define FILE3 CENTREON_BROKER_LUA_SCRIPT_PATH "/test3.lua" #define FILE4 CENTREON_BROKER_LUA_SCRIPT_PATH "/socket.lua" +extern std::shared_ptr g_io_context; + class LuaTest : public ::testing::Test { public: void SetUp() override { + g_io_context->restart(); try { config::applier::init(0, "test_broker", 0); } catch (std::exception const& e) { diff --git a/broker/neb/CMakeLists.txt b/broker/neb/CMakeLists.txt index 13d3efc3f7a..cb588a6e5a6 100644 --- a/broker/neb/CMakeLists.txt +++ b/broker/neb/CMakeLists.txt @@ -114,7 +114,6 @@ add_library( # Flags needed to include all symbols in binary. target_link_libraries(${NEB} nebbase CONAN_PKG::spdlog) -# "-Wl,--whole-archive" nebbase "-Wl,--no-whole-archive") set_target_properties("${NEB}" PROPERTIES PREFIX "") target_precompile_headers(${NEB} REUSE_FROM nebbase) install(TARGETS "${NEB}" LIBRARY DESTINATION "${PREFIX_MODULES}") @@ -148,6 +147,7 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") # Flags needed to include all symbols in shared library. target_link_libraries( "${CBMOD}" + rokerlog "-Wl,--whole-archive" "rokerbase" "-Wl,--no-whole-archive" diff --git a/broker/neb/src/neb.cc b/broker/neb/src/neb.cc index 60b7167a84a..b536e86184a 100644 --- a/broker/neb/src/neb.cc +++ b/broker/neb/src/neb.cc @@ -33,6 +33,8 @@ using namespace com::centreon::exceptions; // Specify the event broker API version. NEB_API_VERSION(CURRENT_NEB_API_VERSION) +extern std::shared_ptr g_io_context; + extern "C" { /** * @brief Module exit point. @@ -55,6 +57,8 @@ int nebmodule_deinit(int flags, int reason) { neb::unregister_callbacks(); // Unload singletons. + log_v2::instance() + ->stop_flush_timer(); // beware at the order of these two calls com::centreon::broker::config::applier::deinit(); } // Avoid exception propagation in C code. @@ -109,6 +113,7 @@ int nebmodule_init(int flags, char const* args, void* handle) { setlocale(LC_NUMERIC, "C"); try { + log_v2::load(g_io_context); // Set configuration file. if (args) { char const* config_file("config_file="); @@ -127,7 +132,7 @@ int nebmodule_init(int flags, char const* args, void* handle) { // Initialization. com::centreon::broker::config::applier::init(s); try { - log_v2::instance().apply(s); + log_v2::instance()->apply(s); } catch (const std::exception& e) { log_v2::core()->error("main: {}", e.what()); } diff --git a/broker/rrd/test/cached.cc b/broker/rrd/test/cached.cc index 24b586cb59f..6c9d5c01a75 100644 --- a/broker/rrd/test/cached.cc +++ b/broker/rrd/test/cached.cc @@ -18,6 +18,7 @@ */ #include "com/centreon/broker/rrd/cached.hh" +#include #include @@ -99,7 +100,14 @@ TEST(RRDCached, BatchLocal) { t.join(); ASSERT_TRUE(batch_done); - ASSERT_EQ(testing::internal::GetCapturedStdout(), "connected\n"); + auto arr = absl::StrSplit(testing::internal::GetCapturedStdout(), "\n"); + bool connection_found = false; + for (auto& s : arr) + if (s == "connected") { + connection_found = true; + break; + } + ASSERT_TRUE(connection_found); } TEST(RRDCached, BatchRemote) { @@ -158,5 +166,12 @@ TEST(RRDCached, BatchRemote) { t.join(); ASSERT_TRUE(batch_done); - ASSERT_EQ(testing::internal::GetCapturedStdout(), "connected\n"); + auto arr = absl::StrSplit(testing::internal::GetCapturedStdout(), "\n"); + bool connection_found = false; + for (auto& s : arr) + if (s == "connected") { + connection_found = true; + break; + } + ASSERT_TRUE(connection_found); } diff --git a/broker/rrd/test/rrd.cc b/broker/rrd/test/rrd.cc index 9ad0e101e75..471847328d1 100644 --- a/broker/rrd/test/rrd.cc +++ b/broker/rrd/test/rrd.cc @@ -29,9 +29,12 @@ using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + class Rrd : public ::testing::Test { public: void SetUp() override { + g_io_context->restart(); try { config::applier::init(0, "test_broker", 0); } catch (std::exception const& e) { diff --git a/broker/simu/test/simu.cc b/broker/simu/test/simu.cc index b0bb96963f6..34fd90c6e77 100644 --- a/broker/simu/test/simu.cc +++ b/broker/simu/test/simu.cc @@ -30,9 +30,12 @@ using namespace com::centreon::broker; using namespace com::centreon::broker::misc; using namespace com::centreon::broker::simu; +extern std::shared_ptr g_io_context; + class SimuGenericTest : public ::testing::Test { public: void SetUp() override { + g_io_context->restart(); try { config::applier::init(0, "test_broker", 0); } catch (std::exception const& e) { diff --git a/broker/stats/test/stats.cc b/broker/stats/test/stats.cc index 132bfba6668..a3c5bd1190a 100644 --- a/broker/stats/test/stats.cc +++ b/broker/stats/test/stats.cc @@ -42,10 +42,13 @@ using namespace com::centreon::exceptions; using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + class StatsTest : public ::testing::Test { public: void SetUp() override { - pool::load(0); + g_io_context->restart(); + com::centreon::broker::pool::load(g_io_context, 0); stats::center::load(); mysql_manager::load(); config::applier::state::load(); diff --git a/broker/storage/src/conflict_manager_storage.cc b/broker/storage/src/conflict_manager_storage.cc index 36980ffa184..9180745f0c7 100644 --- a/broker/storage/src/conflict_manager_storage.cc +++ b/broker/storage/src/conflict_manager_storage.cc @@ -171,7 +171,7 @@ void conflict_manager::_storage_process_service_status( "Query for index_data for host_id={} and service_id={}", host_id, service_id); _mysql.run_statement_and_get_result(_index_data_query, - std::move(promise), conn); + std::move(promise), conn, 50); database::mysql_result res(future.get()); if (_mysql.fetch_row(res)) @@ -205,7 +205,7 @@ void conflict_manager::_storage_process_service_status( std::promise promise; std::future future = promise.get_future(); _mysql.run_statement_and_get_result(_index_data_update, - std::move(promise), conn); + std::move(promise), conn, 50); future.get(); } diff --git a/broker/storage/test/conflict_manager.cc b/broker/storage/test/conflict_manager.cc index 71f9e8df14a..44040cd3311 100644 --- a/broker/storage/test/conflict_manager.cc +++ b/broker/storage/test/conflict_manager.cc @@ -34,9 +34,12 @@ using namespace com::centreon::broker; using namespace com::centreon::broker::sql; +extern std::shared_ptr g_io_context; + class ConflictManagerTest : public ::testing::Test { public: void SetUp() override { + g_io_context->restart(); try { config::applier::init(0, "test_broker", 0); } catch (std::exception const& e) { diff --git a/broker/storage/test/perfdata.cc b/broker/storage/test/perfdata.cc index 6f49d38ac43..9bd2f953e20 100644 --- a/broker/storage/test/perfdata.cc +++ b/broker/storage/test/perfdata.cc @@ -29,6 +29,8 @@ using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + /** * Check that the perfdata assignment operator works properly. */ @@ -196,7 +198,10 @@ TEST(StoragePerfdata, DefaultCtor) { class StorageParserParsePerfdata : public testing::Test { public: - void SetUp() override { config::applier::init(0, "test_broker", 0); } + void SetUp() override { + g_io_context->restart(); + config::applier::init(0, "test_broker", 0); + } void TearDown() override { config::applier::deinit(); }; }; diff --git a/broker/storage/test/status-entry.cc b/broker/storage/test/status-entry.cc index 441f48c4823..3b24c6abb1a 100644 --- a/broker/storage/test/status-entry.cc +++ b/broker/storage/test/status-entry.cc @@ -37,6 +37,8 @@ using namespace com::centreon::broker; using namespace com::centreon::broker::misc; +extern std::shared_ptr g_io_context; + class into_memory : public io::stream { std::vector _memory; @@ -70,6 +72,7 @@ class StatusEntryTest : public ::testing::Test { public: void SetUp() override { io::data::broker_id = 0; + g_io_context->restart(); try { config::applier::init(0, "test_broker", 0); } catch (std::exception const& e) { @@ -83,7 +86,7 @@ class StatusEntryTest : public ::testing::Test { // The cache must be destroyed before the applier deinit() call. config::applier::deinit(); ::remove("/tmp/broker_test_cache"); - ::remove(log_v2::instance().log_name().c_str()); + ::remove(log_v2::instance()->log_name().c_str()); } }; diff --git a/broker/tcp/inc/com/centreon/broker/tcp/tcp_async.hh b/broker/tcp/inc/com/centreon/broker/tcp/tcp_async.hh index d805493ba30..2ec99e4105a 100644 --- a/broker/tcp/inc/com/centreon/broker/tcp/tcp_async.hh +++ b/broker/tcp/inc/com/centreon/broker/tcp/tcp_async.hh @@ -57,22 +57,22 @@ namespace tcp { * waits for 10s, and then looks if there are not used connections established * for more than 4s. In that case, it removes them. */ -class tcp_async { - static tcp_async* _instance; +class tcp_async : public std::enable_shared_from_this { + static std::shared_ptr _instance; /* The acceptors open by this tcp_async */ std::list> _acceptor; /* Connections opened by acceptors not already got by streams */ - mutable asio::io_context::strand _strand; absl::btree_multimap> _acceptor_available_con; + mutable std::mutex _acceptor_available_con_m; + mutable std::condition_variable _acceptor_available_con_cv; std::unique_ptr _timer; std::atomic_bool _clear_available_con_running; tcp_async(); - ~tcp_async() noexcept; void _clear_available_con(asio::error_code ec); diff --git a/broker/tcp/src/tcp_async.cc b/broker/tcp/src/tcp_async.cc index 274a93fff3b..276cb112bf3 100644 --- a/broker/tcp/src/tcp_async.cc +++ b/broker/tcp/src/tcp_async.cc @@ -55,7 +55,7 @@ using tcp_keep_alive_cnt = using tcp_user_timeout = asio::detail::socket_option::integer; -tcp_async* tcp_async::_instance{nullptr}; +std::shared_ptr tcp_async::_instance; /** * @brief Return the tcp_async singleton. @@ -73,8 +73,8 @@ tcp_async& tcp_async::instance() { * pool initialization. */ void tcp_async::load() { - if (_instance == nullptr) - _instance = new tcp_async(); + if (!_instance) + _instance = std::shared_ptr(new tcp_async); else log_v2::tcp()->error("tcp_async instance already started."); } @@ -85,8 +85,8 @@ void tcp_async::load() { */ void tcp_async::unload() { if (_instance) { - delete _instance; - _instance = nullptr; + _instance->stop_timer(); + _instance.reset(); } } @@ -95,41 +95,22 @@ void tcp_async::unload() { * tcp_async::load() function to initialize it and then, use the instance() * method. */ -tcp_async::tcp_async() - : _strand{pool::instance().io_context()}, - _clear_available_con_running(false) {} +tcp_async::tcp_async() : _clear_available_con_running(false) {} /** * @brief Stop the timer that clears available connections. */ void tcp_async::stop_timer() { log_v2::tcp()->trace("tcp_async::stop_timer"); - if (_clear_available_con_running) { - std::promise p; - std::future f(p.get_future()); - _clear_available_con_running = false; - asio::post(_timer->get_executor(), [this, &p] { + { + std::lock_guard l(_acceptor_available_con_m); + if (_clear_available_con_running) { + _clear_available_con_running = false; _timer->cancel(); - p.set_value(true); - }); - f.get(); + } + if (_timer) + _timer.reset(); } - if (_timer) - _timer.reset(); -} - -/** - * @brief The destructor of tcp_async. You don't have to use it, instead, use - * the unload() function. - */ -tcp_async::~tcp_async() noexcept { - stop_timer(); - /* Before destroying the strand, we have to wait it is really empty. We post - * a last action and wait it is over. */ - std::promise p; - std::future f{p.get_future()}; - _strand.post([&p] { p.set_value(true); }); - f.get(); } /** @@ -144,37 +125,28 @@ tcp_connection::pointer tcp_async::get_connection( const std::shared_ptr& acceptor, uint32_t timeout_s) { auto end = std::chrono::system_clock::now() + std::chrono::seconds{timeout_s}; - do { - std::promise p; - std::future f{p.get_future()}; - _strand.post([&p, a = acceptor.get(), this] { - auto found = _acceptor_available_con.find(a); - if (found != _acceptor_available_con.end()) { - tcp_connection::pointer retval = std::move(found->second.first); - _acceptor_available_con.erase(found); - p.set_value(retval); - } else - p.set_value(nullptr); - }); - auto retval = f.get(); - if (retval) - return retval; - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } while (std::chrono::system_clock::now() < end); - - return nullptr; + tcp_connection::pointer accepted; + { + std::unique_lock l(_acceptor_available_con_m); + bool available = _acceptor_available_con_cv.wait_until( + l, end, [this, a = acceptor.get()]() { + return _acceptor_available_con.find(a) != + _acceptor_available_con.end(); + }); + if (available) { + auto conn_search = _acceptor_available_con.find(acceptor.get()); + accepted = conn_search->second.first; + _acceptor_available_con.erase(conn_search); + } + } + return accepted; } bool tcp_async::contains_available_acceptor_connections( asio::ip::tcp::acceptor* acceptor) const { - std::promise p; - std::future f{p.get_future()}; - _strand.post([&p, &acceptor, this] { - p.set_value(_acceptor_available_con.find(acceptor) != - _acceptor_available_con.end()); - }); - - return f.get(); + std::lock_guard l(_acceptor_available_con_m); + return _acceptor_available_con.find(acceptor) != + _acceptor_available_con.end(); } /** @@ -229,9 +201,10 @@ void tcp_async::_clear_available_con(asio::error_code ec) { if (ec) log_v2::core()->info("Available connections cleaning: {}", ec.message()); else { - log_v2::core()->debug("Available connections cleaning"); - std::time_t now = std::time(nullptr); - _strand.post([now, this] { + std::lock_guard l(_acceptor_available_con_m); + if (_clear_available_con_running) { + log_v2::core()->debug("Available connections cleaning"); + std::time_t now = std::time(nullptr); for (auto it = _acceptor_available_con.begin(); it != _acceptor_available_con.end();) { if (now >= it->second.second + 10) { @@ -247,7 +220,8 @@ void tcp_async::_clear_available_con(asio::error_code ec) { std::placeholders::_1)); } else _clear_available_con_running = false; - }); + } else + log_v2::core()->debug("Available connections cleaner already stopped"); } } @@ -262,6 +236,7 @@ void tcp_async::start_acceptor( const std::shared_ptr& acceptor, const tcp_config::pointer& conf) { log_v2::tcp()->trace("Start acceptor"); + std::lock_guard l(_acceptor_available_con_m); if (!_timer) _timer = std::make_unique(pool::instance().io_context()); @@ -271,8 +246,9 @@ void tcp_async::start_acceptor( log_v2::tcp()->debug("Reschedule available connections cleaning in 10s"); _timer->expires_after(std::chrono::seconds(10)); - _timer->async_wait( - std::bind(&tcp_async::_clear_available_con, this, std::placeholders::_1)); + _timer->async_wait([me = shared_from_this()](const asio::error_code& err) { + me->_clear_available_con(err); + }); tcp_connection::pointer new_connection = std::make_shared(pool::io_context()); @@ -292,6 +268,8 @@ void tcp_async::start_acceptor( */ void tcp_async::stop_acceptor( std::shared_ptr acceptor) { + log_v2::tcp()->debug("stop acceptor"); + std::lock_guard l(_acceptor_available_con_m); std::error_code ec; acceptor->cancel(ec); if (ec) @@ -323,16 +301,13 @@ void tcp_async::handle_accept(std::shared_ptr acceptor, else { std::time_t now = std::time(nullptr); asio::ip::tcp::socket& sock = new_connection->socket(); - try { - _set_sock_opt(sock, conf); - _strand.post([new_connection, now, acceptor, this] { - _acceptor_available_con.insert(std::make_pair( - acceptor.get(), std::make_pair(new_connection, now))); - }); - } catch (const std::exception& e) { - log_v2::tcp()->error( - "fail to activate keepalive on accepted connection"); + _set_sock_opt(sock, conf); + { + std::lock_guard l(_acceptor_available_con_m); + _acceptor_available_con.insert(std::make_pair( + acceptor.get(), std::make_pair(new_connection, now))); } + _acceptor_available_con_cv.notify_all(); start_acceptor(acceptor, conf); } } else diff --git a/broker/tcp/src/tcp_connection.cc b/broker/tcp/src/tcp_connection.cc index 8ee9ab86dcc..1bc63906410 100644 --- a/broker/tcp/src/tcp_connection.cc +++ b/broker/tcp/src/tcp_connection.cc @@ -263,7 +263,11 @@ void tcp_connection::handle_read(const asio::error_code& ec, void tcp_connection::close() { log_v2::tcp()->trace("closing tcp connection"); if (!_closed) { - while (!_closed && (_writing || _write_queue_has_events)) { + std::chrono::system_clock::time_point timeout = + std::chrono::system_clock::now() + std::chrono::seconds(10); + while (!_closed && + (_writing || _write_queue_has_events && + std::chrono::system_clock::now() < timeout)) { log_v2::tcp()->debug( "Finishing to write data before closing the connection"); if (!_writing) { diff --git a/broker/tcp/test/acceptor.cc b/broker/tcp/test/acceptor.cc index 26c8503a837..4dc252a9d67 100644 --- a/broker/tcp/test/acceptor.cc +++ b/broker/tcp/test/acceptor.cc @@ -34,6 +34,8 @@ using namespace com::centreon::broker; using namespace com::centreon::exceptions; +extern std::shared_ptr g_io_context; + const static std::string test_addr("127.0.0.1"); constexpr static uint16_t test_port(4444); static tcp::tcp_config::pointer test_conf( @@ -44,7 +46,9 @@ static tcp::tcp_config::pointer test_conf2( class TcpAcceptor : public ::testing::Test { public: void SetUp() override { - pool::load(0); + log_v2::tcp()->set_level(spdlog::level::trace); + g_io_context->restart(); + pool::load(g_io_context, 0); tcp::tcp_async::load(); } @@ -726,7 +730,7 @@ TEST_F(TcpAcceptor, Wait2Connect) { std::shared_ptr st; std::thread t{[&] { - std::this_thread::sleep_for(std::chrono::seconds{2}); + std::this_thread::sleep_for(std::chrono::milliseconds{2050}); tcp::connector con(test_conf2); std::shared_ptr str{try_connect(con)}; }}; @@ -1046,10 +1050,10 @@ TEST_F(TcpAcceptor, QuestionAnswerMultiple) { TEST_F(TcpAcceptor, MultipleBigSend) { tcp::acceptor acc(test_conf); - const int32_t nb_packet = 10; - const int32_t len = 10024; + constexpr int32_t nb_packet = 10; + constexpr int32_t len = 10024; - std::thread t{[nb_packet] { + std::thread t{[] { tcp::connector con(test_conf); std::shared_ptr str{try_connect(con)}; std::shared_ptr data_read; diff --git a/broker/tcp/test/connector.cc b/broker/tcp/test/connector.cc index da0c3d7e344..146302f5977 100644 --- a/broker/tcp/test/connector.cc +++ b/broker/tcp/test/connector.cc @@ -28,6 +28,8 @@ using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + constexpr static char test_addr[] = "127.0.0.1"; constexpr static uint16_t test_port(4242); @@ -37,7 +39,8 @@ static tcp::tcp_config::pointer test_conf( class TcpConnector : public testing::Test { public: void SetUp() override { - pool::load(0); + g_io_context->restart(); + pool::load(g_io_context, 0); tcp::tcp_async::load(); _server.init(); _thread = std::thread(&test_server::run, &_server); diff --git a/broker/test/CMakeLists.txt b/broker/test/CMakeLists.txt index a5698eaecb5..764a6369a14 100644 --- a/broker/test/CMakeLists.txt +++ b/broker/test/CMakeLists.txt @@ -5,7 +5,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -49,7 +49,8 @@ add_subdirectory(${PROJECT_SOURCE_DIR}/test/test_util) add_definitions(-DCENTREON_BROKER_TEST_MODULE_PATH="${CMAKE_BINARY_DIR}/test/") add_definitions(-DCENTREON_BROKER_BAM_TEST_PATH="${BAM_TESTS_DIR}") add_definitions(-DCENTREON_BROKER_WD_TEST="${WDOG_TESTS_DIR}") -add_definitions(-DCENTREON_BROKER_LUA_SCRIPT_PATH="${PROJECT_SOURCE_DIR}/lua/test") +add_definitions( + -DCENTREON_BROKER_LUA_SCRIPT_PATH="${PROJECT_SOURCE_DIR}/lua/test") add_definitions(-DCENTREON_BROKER_NEB_TEST="${PROJECT_SOURCE_DIR}/neb/test") add_library(null_module SHARED ${TEST_DIR}/modules/null_module.cc) @@ -63,11 +64,25 @@ endif(WITH_SQL_TESTS) add_definitions(-DBROKERRPC_TESTS_PATH="${TESTS_DIR}/rpc") add_executable(rpc_client ${TESTS_DIR}/rpc/client.cc) -target_link_libraries(rpc_client berpc CONAN_PKG::grpc CONAN_PKG::openssl CONAN_PKG::zlib dl pthread) - -# target_link_libraries(rpc_client berpc CONAN_PKG::grpc ${absl_LIBS} CONAN_PKG::openssl ${c-ares_LIBS} CONAN_PKG::zlib dl pthread) -add_executable(ut_broker +target_link_libraries( + rpc_client + berpc + CONAN_PKG::grpc + CONAN_PKG::openssl + CONAN_PKG::zlib + dl + pthread) + +if(WITH_ASAN) + set(CMAKE_BUILD_TYPE Debug) + set(CMAKE_CXX_FLAGS_DEBUG + "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set(CMAKE_LINKER_FLAGS_DEBUG + "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() +add_executable( + ut_broker # Core sources. ${TESTS_DIR}/bbdo/category.cc ${TESTS_DIR}/bbdo/output.cc @@ -106,13 +121,25 @@ add_executable(ut_broker ${TESTS_DIR}/io.cc ${TESTS_DIR}/main.cc ${TESTS_DIR}/test_server.cc - # Module sources. - ${TESTS_SOURCES} -) - -target_link_libraries(ut_broker test_util roker rokerbase ${TESTS_LIBRARIES} conflictmgr - CONAN_PKG::nlohmann_json CONAN_PKG::asio CONAN_PKG::fmt CONAN_PKG::spdlog CONAN_PKG::gtest CONAN_PKG::mariadb-connector-c CONAN_PKG::openssl CONAN_PKG::grpc) + ${TESTS_SOURCES}) + +target_link_libraries( + ut_broker + test_util + roker + rokerbase + rokerlog + ${TESTS_LIBRARIES} + conflictmgr + CONAN_PKG::nlohmann_json + CONAN_PKG::asio + CONAN_PKG::fmt + CONAN_PKG::spdlog + CONAN_PKG::gtest + CONAN_PKG::mariadb-connector-c + CONAN_PKG::openssl + CONAN_PKG::grpc) set_target_properties(ut_broker PROPERTIES COMPILE_FLAGS "-fPIC") @@ -120,29 +147,21 @@ target_precompile_headers(ut_broker REUSE_FROM rokerbase) set_target_properties( ut_broker rpc_client - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests - RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/tests - RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/tests - RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/tests - RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_BINARY_DIR}/tests -) + PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests + RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/tests + RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/tests + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/tests + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_BINARY_DIR}/tests) # keys used by ut_broker grpc -file(COPY ${PROJECT_SOURCE_DIR}/grpc/test/grpc_test_keys DESTINATION ${CMAKE_BINARY_DIR}/tests) +file(COPY ${PROJECT_SOURCE_DIR}/grpc/test/grpc_test_keys + DESTINATION ${CMAKE_BINARY_DIR}/tests) add_test(NAME tests COMMAND ut_broker) if(WITH_COVERAGE) - set(COVERAGE_EXCLUDES - '*/main.cc' - '*/test/*' - '/usr/include/*' - '${CMAKE_BINARY_DIR}/*' - '*/.conan/*') - SETUP_TARGET_FOR_COVERAGE( - NAME broker-test-coverage - EXECUTABLE ut_broker - DEPENDENCIES ut_broker - ) + set(COVERAGE_EXCLUDES '*/main.cc' '*/test/*' '/usr/include/*' + '${CMAKE_BINARY_DIR}/*' '*/.conan/*') + setup_target_for_coverage(NAME broker-test-coverage EXECUTABLE ut_broker + DEPENDENCIES ut_broker) endif() diff --git a/broker/test/test_util/inc/tcp_relais.hh b/broker/test/test_util/inc/tcp_relais.hh index 15282d3e643..f4161a2f0b8 100644 --- a/broker/test/test_util/inc/tcp_relais.hh +++ b/broker/test/test_util/inc/tcp_relais.hh @@ -42,6 +42,7 @@ class tcp_relais { unsigned listen_port, const std::string& dest_host, unsigned dest_port); + ~tcp_relais(); void shutdown_relays(); }; diff --git a/broker/test/test_util/src/tcp_relais.cc b/broker/test/test_util/src/tcp_relais.cc index eca71f388e8..f65518d988c 100644 --- a/broker/test/test_util/src/tcp_relais.cc +++ b/broker/test/test_util/src/tcp_relais.cc @@ -112,7 +112,7 @@ class incomming_outgoing }; /** - * @brief This server class accept incomming connexions and start the connexion + * @brief This server class accept incoming connections and start the connection * to the dest * */ @@ -168,6 +168,10 @@ tcp_relais::tcp_relais(const std::string& listen_interface, start_io_context(); } +tcp_relais::~tcp_relais() { + _io_context.stop(); +} + void tcp_relais::shutdown_relays() { _impl->shutdown_relays(); } diff --git a/broker/tls/test/acceptor.cc b/broker/tls/test/acceptor.cc index 498e4b5bbe3..731204c33d2 100644 --- a/broker/tls/test/acceptor.cc +++ b/broker/tls/test/acceptor.cc @@ -40,6 +40,8 @@ using namespace com::centreon::broker; using namespace com::centreon::exceptions; +extern std::shared_ptr g_io_context; + const static std::string test_addr("127.0.0.1"); constexpr static uint16_t test_port(4444); @@ -51,7 +53,8 @@ static tcp::tcp_config::pointer test_conf2( class TlsTest : public ::testing::Test { public: void SetUp() override { - pool::load(0); + g_io_context->restart(); + pool::load(g_io_context, 0); tcp::tcp_async::load(); tls::initialize(); } diff --git a/broker/tls/test/nominal.cc b/broker/tls/test/nominal.cc index e70182301ea..1df852ba999 100644 --- a/broker/tls/test/nominal.cc +++ b/broker/tls/test/nominal.cc @@ -33,12 +33,17 @@ using namespace com::centreon::broker; using namespace com::centreon::exceptions; +extern std::shared_ptr g_io_context; + const static std::string test_addr("127.0.0.1"); constexpr static uint16_t test_port(4444); class TcpAcceptor : public ::testing::Test { public: - void SetUp() override { pool::load(0); } + void SetUp() override { + g_io_context->restart(); + pool::load(g_io_context, 0); + } void TearDown() override { tcp::tcp_async::instance().stop_timer(); diff --git a/broker/tls/test/read.cc b/broker/tls/test/read.cc index 21be3affc53..62b5de3ed8c 100644 --- a/broker/tls/test/read.cc +++ b/broker/tls/test/read.cc @@ -29,6 +29,8 @@ using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + class TlsStreamRead : public ::testing::Test { protected: std::unique_ptr _connector; @@ -41,6 +43,7 @@ class TlsStreamRead : public ::testing::Test { public: void SetUp() override { + g_io_context->restart(); try { config::applier::init(0, "test_broker", 0); } catch (const std::exception& e) { diff --git a/broker/tls2/test/acceptor.cc b/broker/tls2/test/acceptor.cc index 479b4da0816..7dc22c0c94f 100644 --- a/broker/tls2/test/acceptor.cc +++ b/broker/tls2/test/acceptor.cc @@ -42,13 +42,16 @@ using namespace com::centreon::broker; using namespace com::centreon::exceptions; +extern std::shared_ptr g_io_context; + const static std::string test_addr("127.0.0.1"); constexpr static uint16_t test_port(4444); class Tls2Test : public ::testing::Test { public: void SetUp() override { - pool::load(0); + g_io_context->restart(); + pool::load(g_io_context >, 0); tcp::tcp_async::load(); tls2::initialize(); } diff --git a/broker/tls2/test/nominal.cc b/broker/tls2/test/nominal.cc index e70182301ea..718fba5a87b 100644 --- a/broker/tls2/test/nominal.cc +++ b/broker/tls2/test/nominal.cc @@ -33,12 +33,17 @@ using namespace com::centreon::broker; using namespace com::centreon::exceptions; +extern std::shared_ptr g_io_context; + const static std::string test_addr("127.0.0.1"); constexpr static uint16_t test_port(4444); class TcpAcceptor : public ::testing::Test { public: - void SetUp() override { pool::load(0); } + void SetUp() override { + g_io_context->restart(); + pool::load(g_io_context >, 0); + } void TearDown() override { tcp::tcp_async::instance().stop_timer(); diff --git a/broker/tls2/test/read.cc b/broker/tls2/test/read.cc index 21be3affc53..62b5de3ed8c 100644 --- a/broker/tls2/test/read.cc +++ b/broker/tls2/test/read.cc @@ -29,6 +29,8 @@ using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + class TlsStreamRead : public ::testing::Test { protected: std::unique_ptr _connector; @@ -41,6 +43,7 @@ class TlsStreamRead : public ::testing::Test { public: void SetUp() override { + g_io_context->restart(); try { config::applier::init(0, "test_broker", 0); } catch (const std::exception& e) { diff --git a/broker/unified_sql/inc/com/centreon/broker/unified_sql/bulk_bind.hh b/broker/unified_sql/inc/com/centreon/broker/unified_sql/bulk_bind.hh index 7f7f4c67db5..779a19e62f2 100644 --- a/broker/unified_sql/inc/com/centreon/broker/unified_sql/bulk_bind.hh +++ b/broker/unified_sql/inc/com/centreon/broker/unified_sql/bulk_bind.hh @@ -60,7 +60,6 @@ namespace unified_sql { class bulk_bind { const uint32_t _interval; const uint32_t _max_size; - size_t _connections_count; database::mysql_bulk_stmt& _stmt; mutable std::mutex _queue_m; std::vector> _bind; @@ -79,6 +78,8 @@ class bulk_bind { std::time_t next_time() const; std::size_t connections_count() const; void init_from_stmt(int32_t conn); + void lock(); + void unlock(); }; } // namespace unified_sql CCB_END() diff --git a/broker/unified_sql/src/bulk_bind.cc b/broker/unified_sql/src/bulk_bind.cc index d653bf6b637..2ce9b588779 100644 --- a/broker/unified_sql/src/bulk_bind.cc +++ b/broker/unified_sql/src/bulk_bind.cc @@ -114,8 +114,13 @@ void bulk_bind::apply_to_stmt(int32_t conn) { _next_time[conn] = std::time(nullptr) + _interval; } +/** + * @brief Initialize the bind at the given connection from the associated + * statement. + * + * @param conn + */ void bulk_bind::init_from_stmt(int32_t conn) { - std::lock_guard lck(_queue_m); _bind[conn] = _stmt.create_bind(); } @@ -128,6 +133,22 @@ std::size_t bulk_bind::connections_count() const { return _bind.size(); } +/** + * @brief accessor to the bind corresponding to a connection. This function + * call must be protected. + * + * @param conn + * + * @return An unique_ptr to a mysql_bulk_bind. + */ std::unique_ptr& bulk_bind::bind(int32_t conn) { return _bind[conn]; } + +void bulk_bind::lock() { + _queue_m.lock(); +} + +void bulk_bind::unlock() { + _queue_m.unlock(); +} diff --git a/broker/unified_sql/src/bulk_queries.cc b/broker/unified_sql/src/bulk_queries.cc index ee86a051f88..a7ad2753701 100644 --- a/broker/unified_sql/src/bulk_queries.cc +++ b/broker/unified_sql/src/bulk_queries.cc @@ -32,7 +32,10 @@ using namespace com::centreon::broker::unified_sql; bulk_queries::bulk_queries(const uint32_t max_interval, const uint32_t max_queries, const std::string& query) - : _interval{max_interval}, _max_size{max_queries}, _query(query), _next_time{std::time(nullptr) + max_interval} {} + : _interval{max_interval}, + _max_size{max_queries}, + _query(query), + _next_time{std::time(nullptr) + max_interval} {} /** * @brief Compute the query to execute as a string and return it. The container @@ -50,7 +53,7 @@ std::string bulk_queries::get_query() { std::string query; if (!queue.empty()) { /* Building the query */ - log_v2::sql()->debug("SQL: {} customvariables sent in bulk", queue.size()); + log_v2::sql()->debug("SQL: {} items sent in bulk", queue.size()); query = fmt::format(_query, fmt::join(queue, ",")); log_v2::sql()->trace("sending query << {} >>", query); } diff --git a/broker/unified_sql/src/stream_sql.cc b/broker/unified_sql/src/stream_sql.cc index 565965c7f9f..54088e02a93 100644 --- a/broker/unified_sql/src/stream_sql.cc +++ b/broker/unified_sql/src/stream_sql.cc @@ -2022,6 +2022,7 @@ void stream::_process_pb_host_status(const std::shared_ptr& d) { int32_t conn = _mysql.choose_connection_by_instance( _cache_host_instance[static_cast(hscr.host_id())]); if (_bulk_prepared_statement) { + std::lock_guard lck(*_hscr_bind); if (!_hscr_bind->bind(conn)) _hscr_bind->init_from_stmt(conn); auto* b = _hscr_bind->bind(conn).get(); @@ -2116,6 +2117,7 @@ void stream::_process_pb_host_status(const std::shared_ptr& d) { int32_t conn = _mysql.choose_connection_by_instance( _cache_host_instance[static_cast(hscr.host_id())]); if (_bulk_prepared_statement) { + std::lock_guard lck(*_hscr_resources_bind); if (!_hscr_resources_bind->bind(conn)) _hscr_resources_bind->init_from_stmt(conn); auto* b = _hscr_resources_bind->bind(conn).get(); @@ -2662,8 +2664,7 @@ void stream::_process_service_dependency(const std::shared_ptr& d) { sd.dependent_host_id, sd.dependent_service_id, sd.host_id, sd.service_id); std::string query(fmt::format( - "DELETE FROM serivces_services_dependencies WHERE " - "dependent_host_id={} " + "DELETE FROM services_services_dependencies WHERE dependent_host_id={} " "AND dependent_service_id={} AND host_id={} AND service_id={}", sd.dependent_host_id, sd.dependent_service_id, sd.host_id, sd.service_id)); @@ -3499,8 +3500,9 @@ void stream::_check_and_update_index_cache(const Service& ss) { "Attempt to get the index from the database for service ({}, {})", ss.host_id(), ss.service_id()); - _mysql.run_statement_and_get_result(_index_data_query, std::move(pq), - conn); + _mysql.run_statement_and_get_result( + _index_data_query, std::move(pq), conn, + get_index_data_col_size(index_data_service_description)); try { database::mysql_result res(future_pq.get()); @@ -3702,6 +3704,7 @@ void stream::_process_pb_service_status(const std::shared_ptr& d) { int32_t conn = _mysql.choose_connection_by_instance( _cache_host_instance[static_cast(sscr.host_id())]); if (_bulk_prepared_statement) { + std::lock_guard lck(*_sscr_bind); if (!_sscr_bind->bind(conn)) _sscr_bind->init_from_stmt(conn); auto* b = _sscr_bind->bind(conn).get(); @@ -3803,6 +3806,7 @@ void stream::_process_pb_service_status(const std::shared_ptr& d) { size_t output_size = misc::string::adjust_size_utf8( sscr.output(), get_resources_col_size(resources_output)); if (_bulk_prepared_statement) { + std::lock_guard lck(*_sscr_resources_bind); if (!_sscr_resources_bind->bind(conn)) _sscr_resources_bind->init_from_stmt(conn); auto* b = _sscr_resources_bind->bind(conn).get(); diff --git a/broker/unified_sql/src/stream_storage.cc b/broker/unified_sql/src/stream_storage.cc index 14f432b3db4..89ed0b19976 100644 --- a/broker/unified_sql/src/stream_storage.cc +++ b/broker/unified_sql/src/stream_storage.cc @@ -443,7 +443,7 @@ void stream::_unified_sql_process_service_status( "Query for index_data for host_id={} and service_id={}", host_id, service_id); _mysql.run_statement_and_get_result(_index_data_query, - std::move(promise), conn); + std::move(promise), conn, 50); database::mysql_result res(future.get()); if (_mysql.fetch_row(res)) @@ -478,7 +478,7 @@ void stream::_unified_sql_process_service_status( std::promise promise; std::future future = promise.get_future(); _mysql.run_statement_and_get_result(_index_data_update, - std::move(promise), conn); + std::move(promise), conn, 50); future.get(); } @@ -678,14 +678,14 @@ void stream::_unified_sql_process_service_status( // Append perfdata to queue. std::string row; if (std::isinf(pd.value())) - row = fmt::format("{},{},'{}',{}", metric_id, ss.last_check, + row = fmt::format("({},{},'{}',{})", metric_id, ss.last_check, ss.current_state, pd.value() < 0.0 ? -FLT_MAX : FLT_MAX); else if (std::isnan(pd.value())) - row = fmt::format("{},{},'{}',NULL", metric_id, ss.last_check, + row = fmt::format("({},{},'{}',NULL)", metric_id, ss.last_check, ss.current_state); else - row = fmt::format("{},{},'{}',{}", metric_id, ss.last_check, + row = fmt::format("({},{},'{}',{})", metric_id, ss.last_check, ss.current_state, pd.value()); _perfdata.push_query(row); } diff --git a/broker/unified_sql/test/conflict_manager.cc b/broker/unified_sql/test/conflict_manager.cc index e14b2036c85..9dd3f771f72 100644 --- a/broker/unified_sql/test/conflict_manager.cc +++ b/broker/unified_sql/test/conflict_manager.cc @@ -32,10 +32,13 @@ using namespace com::centreon::broker; using namespace com::centreon::broker::sql; +extern std::shared_ptr g_io_context; + class USConflictManagerTest : public ::testing::Test { public: void SetUp() override { try { + g_io_context->restart(); config::applier::init(0, "test_broker", 0); } catch (std::exception const& e) { (void)e; diff --git a/broker/unified_sql/test/perfdata.cc b/broker/unified_sql/test/perfdata.cc index b54abd81b98..4f117f07450 100644 --- a/broker/unified_sql/test/perfdata.cc +++ b/broker/unified_sql/test/perfdata.cc @@ -29,6 +29,8 @@ using namespace com::centreon::broker; +extern std::shared_ptr g_io_context; + /** * Check that the perfdata assignment operator works properly. */ @@ -196,7 +198,10 @@ TEST(UnifiedSqlPerfdata, DefaultCtor) { class UnifiedSqlParserParsePerfdata : public testing::Test { public: - void SetUp() override { config::applier::init(0, "test_broker", 0); } + void SetUp() override { + g_io_context->restart(); + config::applier::init(0, "test_broker", 0); + } void TearDown() override { config::applier::deinit(); }; }; diff --git a/broker/unified_sql/test/rebuild_message.cc b/broker/unified_sql/test/rebuild_message.cc index 3b75cf078b7..1105b148d6c 100644 --- a/broker/unified_sql/test/rebuild_message.cc +++ b/broker/unified_sql/test/rebuild_message.cc @@ -38,6 +38,8 @@ using namespace com::centreon::broker; using namespace com::centreon::broker::misc; using namespace google::protobuf::util; +extern std::shared_ptr g_io_context; + class into_memory : public io::stream { public: into_memory() : io::stream("into_memory"), _memory() {} @@ -74,6 +76,7 @@ class UnifiedSqlRebuild2Test : public ::testing::Test { void SetUp() override { io::data::broker_id = 0; try { + g_io_context->restart(); config::applier::init(0, "broker_test", 0); } catch (std::exception const& e) { (void)e; @@ -86,7 +89,7 @@ class UnifiedSqlRebuild2Test : public ::testing::Test { // The cache must be destroyed before the applier deinit() call. config::applier::deinit(); ::remove("/tmp/broker_test_cache"); - ::remove(log_v2::instance().log_name().c_str()); + ::remove(log_v2::instance()->log_name().c_str()); } }; diff --git a/broker/unified_sql/test/status-entry.cc b/broker/unified_sql/test/status-entry.cc index e3b76a322b4..327d4fba053 100644 --- a/broker/unified_sql/test/status-entry.cc +++ b/broker/unified_sql/test/status-entry.cc @@ -37,6 +37,8 @@ using namespace com::centreon::broker; using namespace com::centreon::broker::misc; +extern std::shared_ptr g_io_context; + class into_memory : public io::stream { std::vector _memory; @@ -70,6 +72,7 @@ class UnifiedSqlEntryTest : public ::testing::Test { public: void SetUp() override { io::data::broker_id = 0; + g_io_context->restart(); try { config::applier::init(0, "test_broker", 0); } catch (std::exception const& e) { @@ -83,7 +86,7 @@ class UnifiedSqlEntryTest : public ::testing::Test { // The cache must be destroyed before the applier deinit() call. config::applier::deinit(); ::remove("/tmp/broker_test_cache"); - ::remove(log_v2::instance().log_name().c_str()); + ::remove(log_v2::instance()->log_name().c_str()); } }; diff --git a/broker/watchdog/src/instance_configuration.cc b/broker/watchdog/src/instance_configuration.cc index 310958133fe..48be7c67d1d 100644 --- a/broker/watchdog/src/instance_configuration.cc +++ b/broker/watchdog/src/instance_configuration.cc @@ -41,8 +41,8 @@ instance_configuration::instance_configuration(std::string const& name, std::string const& config_file, bool should_run, bool should_reload, - __attribute__((__unused__)) - uint32_t seconds_per_tentative) + uint32_t seconds_per_tentative + [[maybe_unused]]) : _name{name}, _executable{executable}, _config_file{config_file}, diff --git a/ccc/CMakeLists.txt b/ccc/CMakeLists.txt index c1b7fc73212..21679bbd6a2 100644 --- a/ccc/CMakeLists.txt +++ b/ccc/CMakeLists.txt @@ -1,20 +1,20 @@ -## -## Copyright 2022 Centreon -## -## Licensed under the Apache License, Version 2.0 (the "License"); -## you may not use this file except in compliance with the License. -## You may obtain a copy of the License at -## -## http://www.apache.org/licenses/LICENSE-2.0 -## -## Unless required by applicable law or agreed to in writing, software -## distributed under the License is distributed on an "AS IS" BASIS, -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -## See the License for the specific language governing permissions and -## limitations under the License. -## -## For more information : contact@centreon.com -## +# +# Copyright 2022 Centreon +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# For more information : contact@centreon.com +# # # Global settings. @@ -24,32 +24,42 @@ project("Centreon Collect Client" C CXX) # set -latomic if OS is Raspbian. -if (CMAKE_SYSTEM_PROCESSOR MATCHES "arm") +if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm") set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -latomic") -endif () +endif() add_definitions("-D_GLIBCXX_USE_CXX11_ABI=1") option(WITH_LIBCXX "compiles and link cbd with clang++/libc++") -if (WITH_LIBCXX) + +if(WITH_LIBCXX) set(CMAKE_CXX_COMPILER "clang++") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") -# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread -Werror -O1 -fno-omit-frame-pointer") -endif () + + # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread -Werror -O1 -fno-omit-frame-pointer") +endif() include_directories( - ${CMAKE_SOURCE_DIR}/broker/core/src - ${CMAKE_SOURCE_DIR}/engine/enginerpc - ) + ${CMAKE_SOURCE_DIR}/broker/core/src + ${CMAKE_SOURCE_DIR}/engine/enginerpc +) set(ccc_files - main.cc - client.cc - ) + main.cc + client.cc +) + +if(WITH_ASAN) + set(CMAKE_BUILD_TYPE Debug) + set(CMAKE_CXX_FLAGS_DEBUG + "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set(CMAKE_LINKER_FLAGS_DEBUG + "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") +endif() add_executable(ccc ${ccc_files}) target_link_libraries(ccc CONAN_PKG::grpc cerpc berpc CONAN_PKG::abseil CONAN_PKG::fmt) set_target_properties(ccc PROPERTIES COMPILE_FLAGS "-fPIC") install(TARGETS ccc - RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" - ) + RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" +) diff --git a/clib/CMakeLists.txt b/clib/CMakeLists.txt index 93f2dce8f88..a68eced1a0a 100644 --- a/clib/CMakeLists.txt +++ b/clib/CMakeLists.txt @@ -1,20 +1,20 @@ -## -## Copyright 2011-2014,2018-2021 Centreon -## -## Licensed under the Apache License, Version 2.0 (the "License"); -## you may not use this file except in compliance with the License. -## You may obtain a copy of the License at -## -## http://www.apache.org/licenses/LICENSE-2.0 -## -## Unless required by applicable law or agreed to in writing, software -## distributed under the License is distributed on an "AS IS" BASIS, -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -## See the License for the specific language governing permissions and -## limitations under the License. -## -## For more information : contact@centreon.com -## +# +# Copyright 2011-2014,2018-2021 Centreon +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# For more information : contact@centreon.com +# # Global options. cmake_minimum_required(VERSION 2.8) @@ -26,201 +26,151 @@ set(INC_DIR "${INCLUDE_DIR}/com/centreon") set(SRC_DIR "${PROJECT_SOURCE_DIR}/src") set(SCRIPT_DIR "${PROJECT_SOURCE_DIR}/script") - # Version. set(CLIB_VERSION "${COLLECT_MAJOR}.${COLLECT_MINOR}.${COLLECT_PATCH}") # Include module to check existing libraries. include(CheckLibraryExists) # Include module CTest if necessary. -if (WITH_TESTING) +if(WITH_TESTING) include(CTest) -endif () +endif() set(CMAKE_THREAD_PREFER_PTHREAD) include(FindThreads) -if (NOT CMAKE_USE_PTHREADS_INIT) +if(NOT CMAKE_USE_PTHREADS_INIT) message(FATAL_ERROR "Could not find pthreads library.") -endif () +endif() -if (CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" AND CMAKE_COMPILER_IS_GNUCXX) +if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" AND CMAKE_COMPILER_IS_GNUCXX) set(LIB_THREAD "-pthread -lpthread") -else () +else() set(LIB_THREAD "${CMAKE_THREAD_LIBS_INIT}") -endif () +endif() # Find real time library. -if (CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" - OR CMAKE_SYSTEM_NAME STREQUAL "Windows") +if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR CMAKE_SYSTEM_NAME STREQUAL "Windows") set(LIB_RT "") -else () +else() set(LIB_RT "rt") -endif () - -check_library_exists( - "${LIB_RT}" - "clock_gettime" - "${CMAKE_LIBRARY_PATH}" - FIND_LIB_RT - ) -if (NOT FIND_LIB_RT) +endif() + +check_library_exists("${LIB_RT}" "clock_gettime" "${CMAKE_LIBRARY_PATH}" + FIND_LIB_RT) +if(NOT FIND_LIB_RT) message(FATAL_ERROR "Could not find real time library.") -endif () +endif() # Find dynamic linking library. -if (CMAKE_SYSTEM_NAME STREQUAL "Linux") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(LIB_DL "dl") -elseif (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" - OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD" - OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") +elseif( + CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" + OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD" + OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(LIB_DL "c") -else () +else() set(LIB_DL "") -endif () - -check_library_exists( - "${LIB_DL}" - "dlopen" - "${CMAKE_LIBRARY_PATH}" - FIND_LIB_DL - ) -if (NOT FIND_LIB_DL) - message(FATAL_ERROR "Could not find dynamic linking library.") -endif () +endif() +check_library_exists("${LIB_DL}" "dlopen" "${CMAKE_LIBRARY_PATH}" FIND_LIB_DL) +if(NOT FIND_LIB_DL) + message(FATAL_ERROR "Could not find dynamic linking library.") +endif() # Set path. -if (WITH_PREFIX_LIB_CLIB) +if(WITH_PREFIX_LIB_CLIB) set(PREFIX_LIB "${WITH_PREFIX_LIB_CLIB}") -else () +else() set(PREFIX_LIB "${CMAKE_INSTALL_PREFIX}/lib") -endif () - -# Set options. -option(WITH_ASAN "Add the libasan to check memory leaks and other memory issues." OFF) -if (WITH_ASAN) - set(CMAKE_BUILD_TYPE Debug) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") - set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") -endif () - -option(WITH_TSAN "Add the libtsan to check threads and other multithreading issues." OFF) -if (WITH_TSAN) - set(CMAKE_BUILD_TYPE Debug) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=thread") - set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=thread") -endif () +endif() set(UNIT_TEST "No") -if (WITH_TESTING) +if(WITH_TESTING) set(UNIT_TEST "Yes") -endif () +endif() set(DEB_PACKAGE "No") -if (CPACK_BINARY_DEB) +if(CPACK_BINARY_DEB) set(DEB_PACKAGE "Yes") -endif () +endif() set(RPM_PACKAGE "No") -if (CPACK_BINARY_RPM) +if(CPACK_BINARY_RPM) set(RPM_PACKAGE "Yes") -endif () +endif() # Find headers. include(CheckIncludeFileCXX) check_include_file_cxx("spawn.h" HAVE_SPAWN_H) -if (HAVE_SPAWN_H) +if(HAVE_SPAWN_H) add_definitions(-DHAVE_SPAWN_H) -else () +else() message(WARNING "Your plateform does not have spawn.h. Some features might " - "be disabled.") -endif () + "be disabled.") +endif() # Set sources. -set( - SOURCES - "${SRC_DIR}/library.cc" - "${SRC_DIR}/process_manager.cc" - "${SRC_DIR}/process.cc" - "${SRC_DIR}/handle_manager.cc" - "${SRC_DIR}/handle_action.cc" - "${SRC_DIR}/task_manager.cc" - "${SRC_DIR}/timestamp.cc" -) - +set(SOURCES + "${SRC_DIR}/library.cc" + "${SRC_DIR}/process_manager.cc" + "${SRC_DIR}/process.cc" + "${SRC_DIR}/handle_manager.cc" + "${SRC_DIR}/handle_action.cc" + "${SRC_DIR}/task_manager.cc" + "${SRC_DIR}/timestamp.cc") # Set headers. -set( - HEADERS - ${SPECIFIC_HEADERS} - "${INC_DIR}/clib.hh" - "${INC_DIR}/handle.hh" - "${INC_DIR}/handle_action.hh" - "${INC_DIR}/handle_listener.hh" - "${INC_DIR}/handle_manager.hh" - "${INC_DIR}/hash.hh" - "${INC_DIR}/library.hh" - "${INC_DIR}/namespace.hh" - "${INC_DIR}/process_manager.hh" - "${INC_DIR}/process.hh" - "${INC_DIR}/task.hh" - "${INC_DIR}/task_manager.hh" - "${INC_DIR}/timestamp.hh" - "${INC_DIR}/unique_array_ptr.hh" - "${INC_DIR}/unordered_hash.hh" -) - +set(HEADERS + ${SPECIFIC_HEADERS} + "${INC_DIR}/clib.hh" + "${INC_DIR}/handle.hh" + "${INC_DIR}/handle_action.hh" + "${INC_DIR}/handle_listener.hh" + "${INC_DIR}/handle_manager.hh" + "${INC_DIR}/hash.hh" + "${INC_DIR}/library.hh" + "${INC_DIR}/namespace.hh" + "${INC_DIR}/process_manager.hh" + "${INC_DIR}/process.hh" + "${INC_DIR}/task.hh" + "${INC_DIR}/task_manager.hh" + "${INC_DIR}/timestamp.hh" + "${INC_DIR}/unique_array_ptr.hh" + "${INC_DIR}/unordered_hash.hh") # Include directories. include_directories("${INCLUDE_DIR}") - # Add subdirectories. add_subdirectory(src/clib) add_subdirectory(src/exceptions) add_subdirectory(src/logging) add_subdirectory(src/misc) add_subdirectory(src/io) -if (WITH_TESTING) +if(WITH_TESTING) add_subdirectory(test) -endif () +endif() # Create shared library. -add_library( - centreon_clib - SHARED - ${SOURCES} - ${HEADERS} -) +add_library(centreon_clib SHARED ${SOURCES} ${HEADERS}) # Link target with required libraries. -target_link_libraries( - centreon_clib - ${LIB_THREAD} - ${LIB_RT} - ${LIB_DL} -) +target_link_libraries(centreon_clib ${LIB_THREAD} ${LIB_RT} ${LIB_DL}) # Set output name for the shared library. -set_target_properties( - centreon_clib - PROPERTIES - OUTPUT_NAME - centreon_clib -) +set_target_properties(centreon_clib PROPERTIES OUTPUT_NAME centreon_clib) # Install shared library. install( TARGETS centreon_clib DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}" - COMPONENT "runtime" -) + COMPONENT "runtime") # Install header files for devel. install( DIRECTORY "${INCLUDE_DIR}/" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/centreon-clib" COMPONENT "development" - FILES_MATCHING PATTERN "*.hh" -) - + FILES_MATCHING + PATTERN "*.hh") # Include build package. include(cmake/package.cmake) @@ -241,7 +191,7 @@ message(STATUS " - Processor ${CMAKE_SYSTEM_PROCESSOR}") message(STATUS "") message(STATUS " Build") message(STATUS " - Compiler ${CMAKE_CXX_COMPILER} " - "(${CMAKE_CXX_COMPILER_ID})") + "(${CMAKE_CXX_COMPILER_ID})") message(STATUS " - Extra compilation flags ${CMAKE_CXX_FLAGS}") message(STATUS " - Build unit tests ${UNIT_TEST}") message(STATUS "") diff --git a/clib/inc/com/centreon/exceptions/msg_fmt.hh b/clib/inc/com/centreon/exceptions/msg_fmt.hh index 222fd21482f..6a8ee9a75ef 100644 --- a/clib/inc/com/centreon/exceptions/msg_fmt.hh +++ b/clib/inc/com/centreon/exceptions/msg_fmt.hh @@ -40,6 +40,7 @@ class msg_fmt : public std::exception { : _msg(fmt::format(str, args...)) {} msg_fmt() = delete; + msg_fmt(const msg_fmt& e) : std::exception(e), _msg(e._msg) {} msg_fmt& operator=(const msg_fmt&) = delete; const char* what() const noexcept final { return _msg.c_str(); } }; diff --git a/connectors/perl/test/connector.cc b/connectors/perl/test/connector.cc index a9bd3cbf9fd..86da6d744f9 100644 --- a/connectors/perl/test/connector.cc +++ b/connectors/perl/test/connector.cc @@ -424,7 +424,7 @@ TEST_F(TestConnector, ExecuteModuleLoading) { write_cmd(*p, oss.str()); // Read reply. - std::string output{std::move(read_reply(*p))}; + std::string output{read_reply(*p)}; int retval{wait_for_termination(*p)}; @@ -510,7 +510,7 @@ TEST_F(TestConnector, ExecuteSingleScript) { write_cmd(*p, oss.str()); // Read reply. - std::string output{std::move(read_reply(*p))}; + std::string output{read_reply(*p)}; int retval{wait_for_termination(*p)}; @@ -544,7 +544,7 @@ TEST_F(TestConnector, ExecuteSingleWarningScript) { write_cmd(*p, oss.str()); // Read reply. - std::string output{std::move(read_reply(*p))}; + std::string output{read_reply(*p)}; int retval{wait_for_termination(*p)}; @@ -579,7 +579,7 @@ TEST_F(TestConnector, ExecuteSingleCriticalScript) { write_cmd(*p, oss.str()); // Read reply. - std::string output{std::move(read_reply(*p))}; + std::string output{read_reply(*p)}; int retval{wait_for_termination(*p)}; @@ -622,7 +622,7 @@ TEST_F(TestConnector, ExecuteSingleScriptLogFile) { write_cmd(*p, oss.str()); // Read reply. - std::string output{std::move(read_reply(*p))}; + std::string output{read_reply(*p)}; int retval{wait_for_termination(*p)}; @@ -670,7 +670,7 @@ TEST_F(TestConnector, ExecuteWithAdditionalCode) { write_cmd(*p, oss.str()); // Read reply. - std::string output{std::move(read_reply(*p))}; + std::string output{read_reply(*p)}; int retval{wait_for_termination(*p)}; @@ -693,7 +693,7 @@ TEST_F(TestConnector, NonExistantScript) { write_cmd(*p, oss.str()); // Read reply. - std::string output{std::move(read_reply(*p))}; + std::string output{read_reply(*p)}; int retval{wait_for_termination(*p)}; diff --git a/connectors/ssh/test/fake_listener.cc b/connectors/ssh/test/fake_listener.cc index 63723b8adf7..907b58addff 100644 --- a/connectors/ssh/test/fake_listener.cc +++ b/connectors/ssh/test/fake_listener.cc @@ -136,9 +136,9 @@ bool operator==(std::list const& left, if ((it1->callback != it2->callback) || ((it1->callback == fake_listener::cb_execute) && ((it1->cmd_id != it2->cmd_id) || - (fabs(std::chrono::duration_cast>( - it1->timeout - it2->timeout) - .count()) >= 1.0) || + (std::abs(std::chrono::duration_cast>( + it1->timeout - it2->timeout) + .count()) >= 1.0) || (it1->host != it2->host) || (it1->port != it2->port) || (it1->user != it2->user) || (it1->password != it2->password) || (it1->identity != it2->identity) || diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 12d95819643..c346cf30857 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -1,21 +1,20 @@ -# # -# # Copyright 2011-2021 Centreon -# # -# # This file is part of Centreon Engine. -# # -# # Centreon Engine is free software: you can redistribute it and/or -# # modify it under the terms of the GNU General Public License version 2 -# # as published by the Free Software Foundation. -# # -# # Centreon Engine is distributed in the hope that it will be useful, -# # but WITHOUT ANY WARRANTY; without even the implied warranty of -# # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# # General Public License for more details. -# # -# # You should have received a copy of the GNU General Public License -# # along with Centreon Engine. If not, see -# # . -# # +# +# Copyright 2011-2023 Centreon +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# For more information : contact@centreon.com +# # # Global settings. @@ -30,13 +29,15 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm") endif() # With libasan -option(WITH_ASAN "Add the libasan to check memory leaks and other memory issues." OFF) +option(WITH_ASAN + "Add the libasan to check memory leaks and other memory issues." OFF) if(WITH_ASAN) set(CMAKE_BUILD_TYPE Debug) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set(CMAKE_CXX_FLAGS_DEBUG + "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") set(CMAKE_LINKER_FLAGS_DEBUG - "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") endif() set(INC_DIR "${PROJECT_SOURCE_DIR}/inc") @@ -53,14 +54,17 @@ link_directories(${CMAKE_SOURCE_DIR}/build/centreon-clib/) # Version. if(CENTREON_ENGINE_PRERELEASE) - set(CENTREON_ENGINE_VERSION "${COLLECT_MAJOR}.${COLLECT_MINOR}.${COLLECT_PATCH}-${CENTREON_ENGINE_PRERELEASE}") + set(CENTREON_ENGINE_VERSION + "${COLLECT_MAJOR}.${COLLECT_MINOR}.${COLLECT_PATCH}-${CENTREON_ENGINE_PRERELEASE}" + ) else() - set(CENTREON_ENGINE_VERSION "${COLLECT_MAJOR}.${COLLECT_MINOR}.${COLLECT_PATCH}") + set(CENTREON_ENGINE_VERSION + "${COLLECT_MAJOR}.${COLLECT_MINOR}.${COLLECT_PATCH}") endif() message(STATUS "Generating version header (${CENTREON_ENGINE_VERSION}).") configure_file("${INC_DIR}/com/centreon/engine/version.hh.in" - "${INC_DIR}/com/centreon/engine/version.hh") + "${INC_DIR}/com/centreon/engine/version.hh") # # Check and/or find required components. @@ -76,14 +80,16 @@ if(MATH_LIB_FOUND) endif() message(STATUS "Checking for libnsl.") -check_library_exists("nsl" "getservbyname" "${CMAKE_LIBRARY_PATH}" NSL_LIB_FOUND) +check_library_exists("nsl" "getservbyname" "${CMAKE_LIBRARY_PATH}" + NSL_LIB_FOUND) if(NSL_LIB_FOUND) set(NSL_LIBRARIES "nsl") endif() message(STATUS "Checking for libsocket.") -check_library_exists("socket" "connect" "${CMAKE_LIBRARY_PATH}" SOCKET_LIB_FOUND) +check_library_exists("socket" "connect" "${CMAKE_LIBRARY_PATH}" + SOCKET_LIB_FOUND) if(SOCKET_LIB_FOUND) set(SOCKET_LIBRARIES "socket") @@ -99,52 +105,8 @@ endif() set(PTHREAD_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}") -# # Find Centreon Clib's headers. -# if (WITH_CENTREON_CLIB_INCLUDE_DIR) -# find_file( -# CLIB_HEADER_FOUND -# "com/centreon/clib/version.hh" -# PATHS "${WITH_CENTREON_CLIB_INCLUDE_DIR}" -# NO_DEFAULT_PATH) -# if (NOT CLIB_HEADER_FOUND) -# message(FATAL_ERROR "Could not find Centreon Clib's headers in ${WITH_CENTREON_CLIB_INCLUDE_DIR}.") -# endif () -# set(CLIB_INCLUDE_DIR "${WITH_CENTREON_CLIB_INCLUDE_DIR}") -# elseif (CLIB_FOUND) # Was Centreon Clib detected with pkg-config ? -# if (CMAKE_CXX_FLAGS) -# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CLIB_CFLAGS}") -# else () -# set(CMAKE_CXX_FLAGS "${CLIB_CFLAGS}") -# endif () -# else () -# find_path(CLIB_INCLUDE_DIR "com/centreon/clib/version.hh" PATH_SUFFIXES "centreon-clib") -# if (NOT CLIB_INCLUDE_DIR) -# message(FATAL_ERROR "Could not find Centreon Clib's headers (try WITH_CENTREON_CLIB_INCLUDE_DIR).") -# endif () -# endif () include_directories(${CMAKE_SOURCE_DIR}/clib/inc) -# # Find Centreon Clib's library. -# if (WITH_CENTREON_CLIB_LIBRARIES) -# set(CLIB_LIBRARIES "${WITH_CENTREON_CLIB_LIBRARIES}") -# elseif (WITH_CENTREON_CLIB_LIBRARY_DIR) -# find_library( -# CLIB_LIBRARIES -# "centreon_clib" -# PATHS "${WITH_CENTREON_CLIB_LIBRARY_DIR}" -# NO_DEFAULT_PATH) -# if (NOT CLIB_LIBRARIES) -# message(FATAL_ERROR "Could not find Centreon Clib's library in ${WITH_CENTREON_CLIB_LIBRARY_DIR}.") -# endif () -# elseif (CLIB_FOUND) # Was Centreon Clib detected with pkg-config ? -# set(CLIB_LIBRARIES "${CLIB_LDFLAGS}") -# else () -# find_library(CLIB_LIBRARIES "centreon_clib") -# if (NOT CLIB_LIBRARIES) -# message(FATAL_ERROR "Could not find Centreon Clib's library (try WITH_CENTREON_CLIB_LIBRARY_DIR or WITH_CENTREON_CLIB_LIBRARIES).") -# endif () -# endif () - # Check functions. include(CheckIncludeFileCXX) include(CheckFunctionExists) @@ -186,12 +148,9 @@ set(CREATE_FILES "${WITH_CREATE_FILES}") set(PREFIX_ENGINE_CONF "${CMAKE_INSTALL_FULL_SYSCONFDIR}/centreon-engine") -# # Library directory. -# if (WITH_PREFIX_LIB_ENGINE) -# set(PREFIX_LIB "${WITH_PREFIX_LIB_ENGINE}") -# else () -# set(PREFIX_LIB "${CMAKE_INSTALL_PREFIX}/lib/centreon-engine") -# endif () +# # Library directory. if (WITH_PREFIX_LIB_ENGINE) set(PREFIX_LIB +# "${WITH_PREFIX_LIB_ENGINE}") else () set(PREFIX_LIB +# "${CMAKE_INSTALL_PREFIX}/lib/centreon-engine") endif () # # User used to run Centreon Engine. @@ -222,7 +181,10 @@ if(WITH_STARTUP_SCRIPT STREQUAL "auto") set(WITH_STARTUP_SCRIPT "sysv") endif() else() - message(STATUS "Centreon Engine does not provide startup script for ${CMAKE_SYSTEM_NAME}.") + message( + STATUS + "Centreon Engine does not provide startup script for ${CMAKE_SYSTEM_NAME}." + ) endif() endif() @@ -230,8 +192,7 @@ endif() if(WITH_STARTUP_SCRIPT STREQUAL "upstart") # Generate Upstart script. message(STATUS "Generating upstart script.") - configure_file("${SCRIPT_DIR}/upstart.conf.in" - "${SCRIPT_DIR}/upstart.conf") + configure_file("${SCRIPT_DIR}/upstart.conf.in" "${SCRIPT_DIR}/upstart.conf") # Startup dir. if(WITH_STARTUP_DIR) @@ -241,7 +202,8 @@ if(WITH_STARTUP_SCRIPT STREQUAL "upstart") endif() # Script install rule. - install(FILES "${SCRIPT_DIR}/upstart.conf" + install( + FILES "${SCRIPT_DIR}/upstart.conf" DESTINATION "${STARTUP_DIR}" COMPONENT "runtime" RENAME "centengine.conf") @@ -249,15 +211,15 @@ if(WITH_STARTUP_SCRIPT STREQUAL "upstart") # String printed in summary. set(STARTUP_SCRIPT "Upstart configuration file") -# Create SysV start script. + # Create SysV start script. elseif(WITH_STARTUP_SCRIPT STREQUAL "sysv") # Lock file. if(WITH_LOCK_FILE) set(LOCK_FILE "${WITH_LOCK_FILE}") else() if(OS_DISTRIBUTOR STREQUAL "Ubuntu" - OR OS_DISTRIBUTOR STREQUAL "Debian" - OR OS_DISTRIBUTOR STREQUAL "SUSE LINUX") + OR OS_DISTRIBUTOR STREQUAL "Debian" + OR OS_DISTRIBUTOR STREQUAL "SUSE LINUX") set(LOCK_FILE "/var/lock/centengine.lock") else() set(LOCK_FILE "/var/lock/subsys/centengine.lock") @@ -277,8 +239,7 @@ elseif(WITH_STARTUP_SCRIPT STREQUAL "sysv") # Generate SysV script. message(STATUS "Generating generic startup script.") - configure_file("${SCRIPT_DIR}/centengine.sh.in" - "${SCRIPT_DIR}/centengine.sh") + configure_file("${SCRIPT_DIR}/centengine.sh.in" "${SCRIPT_DIR}/centengine.sh") # Startup dir. if(WITH_STARTUP_DIR) @@ -288,7 +249,8 @@ elseif(WITH_STARTUP_SCRIPT STREQUAL "sysv") endif() # Script install rule. - install(PROGRAMS "${SCRIPT_DIR}/centengine.sh" + install( + PROGRAMS "${SCRIPT_DIR}/centengine.sh" DESTINATION "${STARTUP_DIR}" COMPONENT "runtime" RENAME "centengine") @@ -296,12 +258,12 @@ elseif(WITH_STARTUP_SCRIPT STREQUAL "sysv") # String printed in summary. set(STARTUP_SCRIPT "SysV-style script") -# Create Systemd start script. + # Create Systemd start script. elseif(WITH_STARTUP_SCRIPT STREQUAL "systemd") # Generate Systemd script. message(STATUS "Generating systemd startup script.") configure_file("${SCRIPT_DIR}/centengine.service.in" - "${SCRIPT_DIR}/centengine.service") + "${SCRIPT_DIR}/centengine.service") # Startup dir. if(WITH_STARTUP_DIR) @@ -311,7 +273,8 @@ elseif(WITH_STARTUP_SCRIPT STREQUAL "systemd") endif() # Script install rule. - install(PROGRAMS "${SCRIPT_DIR}/centengine.service" + install( + PROGRAMS "${SCRIPT_DIR}/centengine.service" DESTINATION "${STARTUP_DIR}" COMPONENT "runtime") @@ -320,32 +283,30 @@ elseif(WITH_STARTUP_SCRIPT STREQUAL "systemd") else() # Default. - message(STATUS "Invalid value for option WITH_STARTUP_SCRIPT (must be one of 'auto', 'sysv' or 'upstart').") + message( + STATUS + "Invalid value for option WITH_STARTUP_SCRIPT (must be one of 'auto', 'sysv' or 'upstart')." + ) set(STARTUP_SCRIPT "disabled") endif() # logrotate directory. -option(WITH_ENGINE_LOGROTATE_SCRIPT "Generate and install logrotate script." OFF) +option(WITH_ENGINE_LOGROTATE_SCRIPT "Generate and install logrotate script." + OFF) if(WITH_ENGINE_LOGROTATE_SCRIPT) # Generate logrotate file. message(STATUS "Generating logrorate file.") if(WITH_STARTUP_SCRIPT STREQUAL "upstart") - configure_file( - "${SCRIPT_DIR}/logrotate_upstart.conf.in" - "${SCRIPT_DIR}/logrotate.conf" - @ONLY) + configure_file("${SCRIPT_DIR}/logrotate_upstart.conf.in" + "${SCRIPT_DIR}/logrotate.conf" @ONLY) elseif(WITH_STARTUP_SCRIPT STREQUAL "systemd") - configure_file( - "${SCRIPT_DIR}/logrotate_systemd.conf.in" - "${SCRIPT_DIR}/logrotate.conf" - @ONLY) + configure_file("${SCRIPT_DIR}/logrotate_systemd.conf.in" + "${SCRIPT_DIR}/logrotate.conf" @ONLY) else() - configure_file( - "${SCRIPT_DIR}/logrotate_sysv.conf.in" - "${SCRIPT_DIR}/logrotate.conf" - @ONLY) + configure_file("${SCRIPT_DIR}/logrotate_sysv.conf.in" + "${SCRIPT_DIR}/logrotate.conf" @ONLY) endif() # logrotate file install directory. @@ -356,11 +317,13 @@ if(WITH_ENGINE_LOGROTATE_SCRIPT) FILES "${SCRIPT_DIR}/logrotate.conf" DESTINATION "${LOGROTATE_DIR}" COMPONENT "runtime" - RENAME "centengine" - ) + RENAME "centengine") endif() -option(WITH_SHARED_LIB "Define if the core library is to be build as a shared object or a static library." OFF) +option( + WITH_SHARED_LIB + "Define if the core library is to be build as a shared object or a static library." + OFF) if(WITH_SHARED_LIB) set(LIBRARY_TYPE SHARED) @@ -369,7 +332,9 @@ else() endif() # Simumod module to simulate cbmod and catch its output -option(WITH_SIMU "Add a module only used for tests to see data that cbmod should receive" OFF) +option(WITH_SIMU + "Add a module only used for tests to see data that cbmod should receive" + OFF) if(WITH_SIMU) set(CMAKE_BUILD_TYPE "Debug") @@ -377,9 +342,10 @@ if(WITH_SIMU) endif() # DEBUG_CONFIG enables checks on configuration. Those checks are not free and -# may slow down engine reloads. But it provides a way to check bugs in -# the configuration system. -option(WITH_DEBUG_CONFIG "Enables checks on configuration. This is an option for developers." OFF) +# may slow down engine reloads. But it provides a way to check bugs in the +# configuration system. +option(WITH_DEBUG_CONFIG + "Enables checks on configuration. This is an option for developers." OFF) if(WITH_DEBUG_CONFIG) add_definitions(-DDEBUG_CONFIG) @@ -387,7 +353,7 @@ endif() # Configure files. configure_file("${INC_DIR}/compatibility/common.h.in" - "${INC_DIR}/compatibility/common.h") + "${INC_DIR}/compatibility/common.h") # Locations definitions add_definitions(-DDEFAULT_STATUS_FILE="${ENGINE_VAR_LOG_DIR}/status.dat") @@ -395,7 +361,8 @@ add_definitions(-DDEFAULT_LOG_FILE="${ENGINE_VAR_LOG_DIR}/centengine.log") add_definitions(-DDEFAULT_LOG_ARCHIVE_PATH="${ENGINE_VAR_LOG_ARCHIVE_DIR}") add_definitions(-DDEFAULT_DEBUG_FILE="${ENGINE_VAR_LOG_DIR}/centengine.debug") add_definitions(-DDEFAULT_RETENTION_FILE="${ENGINE_VAR_LOG_DIR}/retention.dat") -add_definitions(-DDEFAULT_COMMAND_FILE="${ENGINE_VAR_LIB_DIR}/rw/centengine.cmd") +add_definitions( + -DDEFAULT_COMMAND_FILE="${ENGINE_VAR_LIB_DIR}/rw/centengine.cmd") add_definitions(-DDEFAULT_CONFIG_FILE="${PREFIX_ENGINE_CONF}/centengine.cfg") add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) @@ -407,111 +374,105 @@ endif() # # Targets. # -set( - FILES - - # Sources. - "${SRC_DIR}/anomalydetection.cc" - "${SRC_DIR}/broker.cc" - "${SRC_DIR}/checkable.cc" - "${SRC_DIR}/check_result.cc" - "${SRC_DIR}/command_manager.cc" - "${SRC_DIR}/comment.cc" - "${SRC_DIR}/config.cc" - "${SRC_DIR}/contact.cc" - "${SRC_DIR}/contactgroup.cc" - "${SRC_DIR}/customvariable.cc" - "${SRC_DIR}/daterange.cc" - "${SRC_DIR}/dependency.cc" - "${SRC_DIR}/diagnostic.cc" - "${SRC_DIR}/exceptions/error.cc" - "${SRC_DIR}/flapping.cc" - "${SRC_DIR}/escalation.cc" - "${SRC_DIR}/globals.cc" - "${SRC_DIR}/host.cc" - "${SRC_DIR}/hostdependency.cc" - "${SRC_DIR}/hostescalation.cc" - "${SRC_DIR}/hostgroup.cc" - "${SRC_DIR}/log_v2.cc" - "${SRC_DIR}/macros.cc" - "${SRC_DIR}/nebmods.cc" - "${SRC_DIR}/notification.cc" - "${SRC_DIR}/notifier.cc" - "${SRC_DIR}/sehandlers.cc" - "${SRC_DIR}/service.cc" - "${SRC_DIR}/servicedependency.cc" - "${SRC_DIR}/serviceescalation.cc" - "${SRC_DIR}/servicegroup.cc" - "${SRC_DIR}/severity.cc" - "${SRC_DIR}/shared.cc" - "${SRC_DIR}/statistics.cc" - "${SRC_DIR}/statusdata.cc" - "${SRC_DIR}/string.cc" - "${SRC_DIR}/tag.cc" - "${SRC_DIR}/timeperiod.cc" - "${SRC_DIR}/timerange.cc" - "${SRC_DIR}/timezone_locker.cc" - "${SRC_DIR}/timezone_manager.cc" - "${SRC_DIR}/utils.cc" - "${SRC_DIR}/xpddefault.cc" - "${SRC_DIR}/xsddefault.cc" - - # Headers. - "${INC_DIR}/com/centreon/engine/anomalydetection.hh" - "${INC_DIR}/com/centreon/engine/broker.hh" - "${INC_DIR}/com/centreon/engine/checkable.hh" - "${INC_DIR}/com/centreon/engine/check_result.hh" - "${INC_DIR}/com/centreon/engine/circular_buffer.hh" - "${INC_DIR}/com/centreon/engine/command_manager.hh" - "${INC_DIR}/com/centreon/engine/comment.hh" - "${INC_DIR}/com/centreon/engine/common.hh" - "${INC_DIR}/com/centreon/engine/config.hh" - "${INC_DIR}/com/centreon/engine/contact.hh" - "${INC_DIR}/com/centreon/engine/contactgroup.hh" - "${INC_DIR}/com/centreon/engine/customvariable.hh" - "${INC_DIR}/com/centreon/engine/daterange.hh" - "${INC_DIR}/com/centreon/engine/dependency.hh" - "${INC_DIR}/com/centreon/engine/diagnostic.hh" - "${INC_DIR}/com/centreon/engine/exceptions/error.hh" - "${INC_DIR}/com/centreon/engine/escalation.hh" - "${INC_DIR}/com/centreon/engine/flapping.hh" - "${INC_DIR}/com/centreon/engine/globals.hh" - "${INC_DIR}/com/centreon/engine/host.hh" - "${INC_DIR}/com/centreon/engine/hostdependency.hh" - "${INC_DIR}/com/centreon/engine/hostescalation.hh" - "${INC_DIR}/com/centreon/engine/hostgroup.hh" - "${INC_DIR}/com/centreon/engine/log_v2.hh" - "${INC_DIR}/com/centreon/engine/logging.hh" - "${INC_DIR}/com/centreon/engine/macros.hh" - "${INC_DIR}/com/centreon/engine/nebcallbacks.hh" - "${INC_DIR}/com/centreon/engine/neberrors.hh" - "${INC_DIR}/com/centreon/engine/nebmods.hh" - "${INC_DIR}/com/centreon/engine/nebmodules.hh" - "${INC_DIR}/com/centreon/engine/nebstructs.hh" - "${INC_DIR}/com/centreon/engine/notification.hh" - "${INC_DIR}/com/centreon/engine/notifier.hh" - "${INC_DIR}/com/centreon/engine/objects.hh" - "${INC_DIR}/com/centreon/engine/opt.hh" - "${INC_DIR}/com/centreon/engine/sehandlers.hh" - "${INC_DIR}/com/centreon/engine/service.hh" - "${INC_DIR}/com/centreon/engine/servicedependency.hh" - "${INC_DIR}/com/centreon/engine/serviceescalation.hh" - "${INC_DIR}/com/centreon/engine/servicegroup.hh" - "${INC_DIR}/com/centreon/engine/severity.hh" - "${INC_DIR}/com/centreon/engine/shared.hh" - "${INC_DIR}/com/centreon/engine/statistics.hh" - "${INC_DIR}/com/centreon/engine/statusdata.hh" - "${INC_DIR}/com/centreon/engine/string.hh" - "${INC_DIR}/com/centreon/engine/tag.hh" - "${INC_DIR}/com/centreon/engine/timeperiod.hh" - "${INC_DIR}/com/centreon/engine/timerange.hh" - "${INC_DIR}/com/centreon/engine/timezone_locker.hh" - "${INC_DIR}/com/centreon/engine/timezone_manager.hh" - "${INC_DIR}/com/centreon/engine/utils.hh" - "${INC_DIR}/com/centreon/engine/version.hh" - "${INC_DIR}/com/centreon/engine/xpddefault.hh" - "${INC_DIR}/com/centreon/engine/xsddefault.hh" -) +set(FILES + # Sources. + "${SRC_DIR}/anomalydetection.cc" + "${SRC_DIR}/broker.cc" + "${SRC_DIR}/checkable.cc" + "${SRC_DIR}/check_result.cc" + "${SRC_DIR}/command_manager.cc" + "${SRC_DIR}/comment.cc" + "${SRC_DIR}/config.cc" + "${SRC_DIR}/contact.cc" + "${SRC_DIR}/contactgroup.cc" + "${SRC_DIR}/customvariable.cc" + "${SRC_DIR}/daterange.cc" + "${SRC_DIR}/dependency.cc" + "${SRC_DIR}/diagnostic.cc" + "${SRC_DIR}/exceptions/error.cc" + "${SRC_DIR}/flapping.cc" + "${SRC_DIR}/escalation.cc" + "${SRC_DIR}/globals.cc" + "${SRC_DIR}/host.cc" + "${SRC_DIR}/hostdependency.cc" + "${SRC_DIR}/hostescalation.cc" + "${SRC_DIR}/hostgroup.cc" + "${SRC_DIR}/macros.cc" + "${SRC_DIR}/nebmods.cc" + "${SRC_DIR}/notification.cc" + "${SRC_DIR}/notifier.cc" + "${SRC_DIR}/sehandlers.cc" + "${SRC_DIR}/service.cc" + "${SRC_DIR}/servicedependency.cc" + "${SRC_DIR}/serviceescalation.cc" + "${SRC_DIR}/servicegroup.cc" + "${SRC_DIR}/severity.cc" + "${SRC_DIR}/shared.cc" + "${SRC_DIR}/statistics.cc" + "${SRC_DIR}/statusdata.cc" + "${SRC_DIR}/string.cc" + "${SRC_DIR}/tag.cc" + "${SRC_DIR}/timeperiod.cc" + "${SRC_DIR}/timerange.cc" + "${SRC_DIR}/timezone_locker.cc" + "${SRC_DIR}/timezone_manager.cc" + "${SRC_DIR}/utils.cc" + "${SRC_DIR}/xpddefault.cc" + "${SRC_DIR}/xsddefault.cc" + # Headers. + "${INC_DIR}/com/centreon/engine/anomalydetection.hh" + "${INC_DIR}/com/centreon/engine/broker.hh" + "${INC_DIR}/com/centreon/engine/checkable.hh" + "${INC_DIR}/com/centreon/engine/check_result.hh" + "${INC_DIR}/com/centreon/engine/circular_buffer.hh" + "${INC_DIR}/com/centreon/engine/command_manager.hh" + "${INC_DIR}/com/centreon/engine/comment.hh" + "${INC_DIR}/com/centreon/engine/common.hh" + "${INC_DIR}/com/centreon/engine/config.hh" + "${INC_DIR}/com/centreon/engine/contact.hh" + "${INC_DIR}/com/centreon/engine/contactgroup.hh" + "${INC_DIR}/com/centreon/engine/customvariable.hh" + "${INC_DIR}/com/centreon/engine/daterange.hh" + "${INC_DIR}/com/centreon/engine/dependency.hh" + "${INC_DIR}/com/centreon/engine/diagnostic.hh" + "${INC_DIR}/com/centreon/engine/exceptions/error.hh" + "${INC_DIR}/com/centreon/engine/escalation.hh" + "${INC_DIR}/com/centreon/engine/flapping.hh" + "${INC_DIR}/com/centreon/engine/globals.hh" + "${INC_DIR}/com/centreon/engine/host.hh" + "${INC_DIR}/com/centreon/engine/hostdependency.hh" + "${INC_DIR}/com/centreon/engine/hostescalation.hh" + "${INC_DIR}/com/centreon/engine/hostgroup.hh" + "${INC_DIR}/com/centreon/engine/logging.hh" + "${INC_DIR}/com/centreon/engine/macros.hh" + "${INC_DIR}/com/centreon/engine/nebcallbacks.hh" + "${INC_DIR}/com/centreon/engine/neberrors.hh" + "${INC_DIR}/com/centreon/engine/nebmods.hh" + "${INC_DIR}/com/centreon/engine/nebmodules.hh" + "${INC_DIR}/com/centreon/engine/nebstructs.hh" + "${INC_DIR}/com/centreon/engine/notification.hh" + "${INC_DIR}/com/centreon/engine/notifier.hh" + "${INC_DIR}/com/centreon/engine/objects.hh" + "${INC_DIR}/com/centreon/engine/opt.hh" + "${INC_DIR}/com/centreon/engine/sehandlers.hh" + "${INC_DIR}/com/centreon/engine/service.hh" + "${INC_DIR}/com/centreon/engine/servicedependency.hh" + "${INC_DIR}/com/centreon/engine/serviceescalation.hh" + "${INC_DIR}/com/centreon/engine/servicegroup.hh" + "${INC_DIR}/com/centreon/engine/severity.hh" + "${INC_DIR}/com/centreon/engine/shared.hh" + "${INC_DIR}/com/centreon/engine/statistics.hh" + "${INC_DIR}/com/centreon/engine/statusdata.hh" + "${INC_DIR}/com/centreon/engine/string.hh" + "${INC_DIR}/com/centreon/engine/tag.hh" + "${INC_DIR}/com/centreon/engine/timeperiod.hh" + "${INC_DIR}/com/centreon/engine/timerange.hh" + "${INC_DIR}/com/centreon/engine/timezone_locker.hh" + "${INC_DIR}/com/centreon/engine/timezone_manager.hh" + "${INC_DIR}/com/centreon/engine/utils.hh" + "${INC_DIR}/com/centreon/engine/version.hh" + "${INC_DIR}/com/centreon/engine/xpddefault.hh" + "${INC_DIR}/com/centreon/engine/xsddefault.hh") # Subdirectories with core features. add_subdirectory(src/broker) @@ -535,6 +496,11 @@ add_subdirectory(enginerpc) include_directories(enginerpc) # Library engine target. +add_library(enginelog STATIC src/log_v2.cc) +target_precompile_headers(enginelog PRIVATE ${PRECOMP_HEADER}) +set_target_properties(enginelog PROPERTIES COMPILE_FLAGS "-fPIC") +target_link_libraries(enginelog CONAN_PKG::spdlog) + add_library(cce_core ${LIBRARY_TYPE} ${FILES}) add_dependencies(cce_core engine_rpc) add_dependencies(cce_core centreon_clib) @@ -542,14 +508,15 @@ add_dependencies(cce_core centreon_clib) target_precompile_headers(cce_core PRIVATE ${PRECOMP_HEADER}) # Link target with required libraries. -target_link_libraries(cce_core CONAN_PKG::nlohmann_json +target_link_libraries( + cce_core + CONAN_PKG::nlohmann_json ${MATH_LIBRARIES} ${PTHREAD_LIBRARIES} ${SOCKET_LIBRARIES} centreon_clib ${fmt_LIBRARIES} - CONAN_PKG::spdlog -) + CONAN_PKG::spdlog) # centengine target. add_executable("centengine" "${SRC_DIR}/main.cc") @@ -557,7 +524,25 @@ set_property(TARGET "centengine" PROPERTY ENABLE_EXPORTS "1") add_dependencies(centengine centreon_clib) # Link centengine with required libraries. -target_link_libraries(centengine "-export-dynamic" centreon_clib "-Wl,-whole-archive" enginerpc cce_core "-Wl,-no-whole-archive" -L${CONAN_LIB_DIRS_GRPC} "-Wl,--whole-archive" grpc++ "-Wl,--no-whole-archive" CONAN_PKG::grpc ${absl_LIBS} CONAN_PKG::openssl ${c-ares_LIBS} CONAN_PKG::zlib dl) +target_link_libraries( + centengine + "-rdynamic" + centreon_clib + enginelog + "-Wl,-whole-archive" + enginerpc + cce_core + "-Wl,-no-whole-archive" + -L${CONAN_LIB_DIRS_GRPC} + "-Wl,--whole-archive" + grpc++ + "-Wl,--no-whole-archive" + CONAN_PKG::grpc + ${absl_LIBS} + CONAN_PKG::openssl + ${c-ares_LIBS} + CONAN_PKG::zlib + dl) # centenginestats target. add_executable("centenginestats" "${SRC_DIR}/centenginestats.cc") @@ -573,13 +558,15 @@ add_subdirectory(tests) # # Install rules. -install(TARGETS "centengine" "centenginestats" +install( + TARGETS "centengine" "centenginestats" DESTINATION "${CMAKE_INSTALL_FULL_SBINDIR}" COMPONENT "runtime") -# # Create directories. +# Create directories. if(CREATE_FILES) - install(CODE " + install( + CODE " function(mkdir_chown user group path) if (APPLE OR (UNIX AND NOT CYGWIN)) if (NOT EXISTS \"\$ENV{DESTDIR}\${path}\") @@ -622,12 +609,13 @@ if(CREATE_FILES) endif() # Install header files for development. -install(DIRECTORY "${INC_DIR}/" +install( + DIRECTORY "${INC_DIR}/" DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}/centreon-engine/" COMPONENT "development" - FILES_MATCHING PATTERN "*.hh" - PATTERN "${INC_DIR}/compatibility/" EXCLUDE -) + FILES_MATCHING + PATTERN "*.hh" + PATTERN "${INC_DIR}/compatibility/" EXCLUDE) # # Packaging. @@ -652,7 +640,10 @@ message(STATUS " - Version ${CMAKE_SYSTEM_VERSION}") message(STATUS " - Processor ${CMAKE_SYSTEM_PROCESSOR}") message(STATUS "") message(STATUS " Build") -message(STATUS " - Compiler ${CMAKE_CXX_COMPILER} (${CMAKE_CXX_COMPILER_ID})") +message( + STATUS + " - Compiler ${CMAKE_CXX_COMPILER} (${CMAKE_CXX_COMPILER_ID})" +) message(STATUS " - Extra compilation flags ${CMAKE_CXX_FLAGS}") if(WITH_SHARED_LIB) @@ -686,13 +677,18 @@ message(STATUS "") message(STATUS " Install") # message(STATUS " - Prefix ${CMAKE_INSTALL_PREFIX}") -message(STATUS " - Binary prefix ${CMAKE_INSTALL_FULL_SBINDIR}") +message( + STATUS " - Binary prefix ${CMAKE_INSTALL_FULL_SBINDIR}") message(STATUS " - Configuration prefix ${PREFIX_ENGINE_CONF}") # message(STATUS " - Library prefix ${PREFIX_LIB}") -message(STATUS " - Include prefix ${CMAKE_INSTALL_FULL_INCLUDEDIR}/centreon-engine") +message( + STATUS + " - Include prefix ${CMAKE_INSTALL_FULL_INCLUDEDIR}/centreon-engine" +) message(STATUS " - var directory ${VAR_DIR}") -message(STATUS " - Log archive directory ${ENGINE_VAR_LOG_ARCHIVE_DIR}") +message( + STATUS " - Log archive directory ${ENGINE_VAR_LOG_ARCHIVE_DIR}") message(STATUS " - RW directory ${ENGINE_VAR_LIB_DIR}/rw") if(LOCK_FILE) diff --git a/engine/enginerpc/engine.proto b/engine/enginerpc/engine.proto index ed364883ace..f7a72854a1f 100644 --- a/engine/enginerpc/engine.proto +++ b/engine/enginerpc/engine.proto @@ -114,6 +114,26 @@ service Engine { rpc DisableServiceNotifications(ServiceIdentifier) returns (CommandSuccess) {} rpc EnableServiceNotifications(ServiceIdentifier) returns (CommandSuccess) {} rpc ChangeAnomalyDetectionSensitivity(ChangeServiceNumber) returns (CommandSuccess) {} + + rpc GetLogInfo(google.protobuf.Empty) returns(LogInfo) {} + /** + * @brief Set level of a logger. + * + * @param A message with a logger and a level as strings. + * + * @return nothing. + */ + rpc SetLogLevel(LogLevel) returns (google.protobuf.Empty) {} + + + /** + * @brief Set log flush period of all loggers. + * + * @param period in second, 0 means flush all the times + * + * @return nothing. + */ + rpc SetLogFlushPeriod(LogFlushPeriod) returns (google.protobuf.Empty) {} } message GenericString { @@ -545,3 +565,31 @@ message ChangeObjectCustomVar { string varname = 4; string varvalue = 5; } + +message LogInfo { + message LoggerInfo { + string log_name = 1; + string log_file = 2; + uint32 log_flush_period = 3; + map level = 4; + } + repeated LoggerInfo loggers = 1; +} + +message LogFlushPeriod { + uint32 period = 1; +} + +message LogLevel { + string name = 1; + enum LogLevelEnum { + TRACE = 0; + DEBUG = 1; + INFO = 2; + WARNING = 3; + ERROR = 4; + CRITICAL = 5; + OFF = 6; + } + LogLevelEnum level = 2; +} diff --git a/engine/enginerpc/engine_impl.cc b/engine/enginerpc/engine_impl.cc index ad74c1eb54b..4257d3af353 100644 --- a/engine/enginerpc/engine_impl.cc +++ b/engine/enginerpc/engine_impl.cc @@ -22,8 +22,13 @@ #include #include +#include + +#include + #include #include +#include #include #include "com/centreon/engine/host.hh" @@ -1671,7 +1676,7 @@ grpc::Status engine_impl::DeleteHostDowntimeFull( CommandSuccess* response __attribute__((unused))) { std::string err; auto fn = std::packaged_task([&err, request]() -> int32_t { - std::list > dtlist; + std::list> dtlist; for (auto it = downtimes::downtime_manager::instance() .get_scheduled_downtimes() .begin(), @@ -3254,3 +3259,80 @@ engine_impl::get_serv( } } } + +/** + * @brief get log levels and infos + * + * @param context + * @param request + * @param response + * @return ::grpc::Status + */ +::grpc::Status engine_impl::GetLogInfo( + ::grpc::ServerContext* context, + const ::google::protobuf::Empty* request, + ::com::centreon::engine::LogInfo* response) { + using logger_by_log = + std::map>>; + + logger_by_log summary; + + spdlog::apply_all( + [&summary](const std::shared_ptr& logger_base) { + std::shared_ptr logger = + std::dynamic_pointer_cast(logger_base); + if (logger) { + summary[logger->get_parent()].push_back(logger); + } + }); + + for (const auto& by_parent_loggers : summary) { + LogInfo_LoggerInfo* loggers = response->add_loggers(); + loggers->set_log_name(by_parent_loggers.first->log_name()); + loggers->set_log_file(by_parent_loggers.first->file_path()); + loggers->set_log_flush_period( + by_parent_loggers.first->get_flush_interval().count()); + auto& levels = *loggers->mutable_level(); + for (const std::shared_ptr& logger : + by_parent_loggers.second) { + auto level = spdlog::level::to_string_view(logger->level()); + levels[logger->name()] = std::string(level.data(), level.size()); + } + } + return grpc::Status::OK; +} + +grpc::Status engine_impl::SetLogLevel(grpc::ServerContext* context + [[maybe_unused]], + const LogLevel* request, + ::google::protobuf::Empty*) { + std::shared_ptr logger = spdlog::get(request->name()); + if (!logger) { + std::string err_detail = fmt::format("unknow logger:{}", request->name()); + SPDLOG_LOGGER_ERROR(log_v2::external_command(), err_detail); + return grpc::Status(::grpc::StatusCode::INVALID_ARGUMENT, err_detail); + } else { + logger->set_level(spdlog::level::level_enum(request->level())); + return grpc::Status::OK; + } +} + +grpc::Status engine_impl::SetLogFlushPeriod(grpc::ServerContext* context + [[maybe_unused]], + const LogFlushPeriod* request, + ::google::protobuf::Empty*) { + // first get all log_v2 objects + std::set loggers; + spdlog::apply_all([&](const std::shared_ptr logger) { + std::shared_ptr logger_base = + std::dynamic_pointer_cast(logger); + if (logger_base) { + loggers.insert(logger_base->get_parent()); + } + }); + + for (log_v2_base* to_update : loggers) { + to_update->set_flush_interval(request->period()); + } + return grpc::Status::OK; +} diff --git a/engine/inc/com/centreon/engine/engine_impl.hh b/engine/inc/com/centreon/engine/engine_impl.hh index 20ecba9d53d..54445e36388 100644 --- a/engine/inc/com/centreon/engine/engine_impl.hh +++ b/engine/inc/com/centreon/engine/engine_impl.hh @@ -239,6 +239,19 @@ class engine_impl final : public Engine::Service { static std::pair, std::string /*error*/> get_serv(const ::com::centreon::engine::ServiceIdentifier& serv_info); + + ::grpc::Status GetLogInfo( + ::grpc::ServerContext* context, + const ::google::protobuf::Empty* request, + ::com::centreon::engine::LogInfo* response) override; + + grpc::Status SetLogLevel(grpc::ServerContext* context [[maybe_unused]], + const LogLevel* request, + ::google::protobuf::Empty*) override; + + grpc::Status SetLogFlushPeriod(grpc::ServerContext* context [[maybe_unused]], + const LogFlushPeriod* request, + ::google::protobuf::Empty*) override; }; CCE_END() diff --git a/engine/inc/com/centreon/engine/log_v2.hh b/engine/inc/com/centreon/engine/log_v2.hh index 62cbcea54bb..e6e41f59f8c 100644 --- a/engine/inc/com/centreon/engine/log_v2.hh +++ b/engine/inc/com/centreon/engine/log_v2.hh @@ -1,5 +1,5 @@ /* -** Copyright 2021 Centreon +** Copyright 2021-2023 Centreon ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -19,12 +19,19 @@ #define CCE_LOG_V2_HH #include "com/centreon/engine/configuration/state.hh" +#include "log_v2_base.hh" CCE_BEGIN() -class log_v2 { - std::string _log_name; +class log_v2 : public log_v2_base { std::array, 13> _log; std::atomic_bool _running; + asio::system_timer _flush_timer; + std::mutex _flush_timer_m; + bool _flush_timer_active; + std::shared_ptr _io_context; + + static std::shared_ptr _instance; + enum logger { log_config, log_functions, @@ -41,26 +48,74 @@ class log_v2 { log_runtime, }; - log_v2(); - ~log_v2() noexcept; + log_v2(const std::shared_ptr& io_context); + + std::shared_ptr get_logger(logger log_type, + const char* log_str); + + void start_flush_timer(spdlog::sink_ptr sink); public: + ~log_v2() noexcept; + + void stop_flush_timer(); void apply(const configuration::state& config); + void set_flush_interval(unsigned second_flush_interval); static bool contains_level(const std::string& level_name); - static log_v2& instance(); - static std::shared_ptr functions(); - static std::shared_ptr config(); - static std::shared_ptr events(); - static std::shared_ptr checks(); - static std::shared_ptr notifications(); - static std::shared_ptr eventbroker(); - static std::shared_ptr external_command(); - static std::shared_ptr commands(); - static std::shared_ptr downtimes(); - static std::shared_ptr comments(); - static std::shared_ptr macros(); - static std::shared_ptr process(); - static std::shared_ptr runtime(); + static void load(const std::shared_ptr& io_context); + static std::shared_ptr instance(); + static inline std::shared_ptr functions() { + return _instance->get_logger(log_v2::log_functions, "functions"); + } + + static inline std::shared_ptr config() { + return _instance->get_logger(log_v2::log_config, "configuration"); + } + + static inline std::shared_ptr events() { + return _instance->get_logger(log_v2::log_events, "events"); + } + + static inline std::shared_ptr checks() { + return _instance->get_logger(log_v2::log_checks, "checks"); + } + + static inline std::shared_ptr notifications() { + return _instance->get_logger(log_v2::log_notifications, "notifications"); + } + + static inline std::shared_ptr eventbroker() { + return _instance->get_logger(log_v2::log_eventbroker, "eventbroker"); + } + + static inline std::shared_ptr external_command() { + return _instance->get_logger(log_v2::log_external_command, + "external_command"); + } + + static inline std::shared_ptr commands() { + return _instance->get_logger(log_v2::log_commands, "commands"); + } + + static inline std::shared_ptr downtimes() { + return _instance->get_logger(log_v2::log_downtimes, "downtimes"); + } + + static inline std::shared_ptr comments() { + return _instance->get_logger(log_v2::log_comments, "comments"); + } + + static inline std::shared_ptr macros() { + return _instance->get_logger(log_v2::log_macros, "macros"); + } + + static inline std::shared_ptr process() { + return _instance->get_logger(log_v2::log_process, "process"); + } + + static inline std::shared_ptr runtime() { + return _instance->get_logger(log_v2::log_runtime, "runtime"); + } }; CCE_END() diff --git a/engine/inc/com/centreon/engine/log_v2_base.hh b/engine/inc/com/centreon/engine/log_v2_base.hh new file mode 100644 index 00000000000..cabf9e0f7f6 --- /dev/null +++ b/engine/inc/com/centreon/engine/log_v2_base.hh @@ -0,0 +1,67 @@ +/* +** Copyright 2022 Centreon +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +** +** For more information : contact@centreon.com +*/ +#ifndef CCE_LOG_V2_BASE_HH +#define CCE_LOG_V2_BASE_HH + +#include +#include +#include + +#include "namespace.hh" + +CCE_BEGIN() + +class log_v2_base { + protected: + std::string _log_name; + std::chrono::seconds _flush_interval; + std::string _file_path; + + public: + log_v2_base(const std::string& logger_name) : _log_name(logger_name) {} + virtual ~log_v2_base() noexcept = default; + + const std::string& log_name() const { return _log_name; } + const std::string& file_path() const { return _file_path; } + + std::chrono::seconds get_flush_interval() const { return _flush_interval; } + void set_flush_interval(uint32_t second_flush_interval) { + _flush_interval = std::chrono::seconds(second_flush_interval); + } +}; + +class log_v2_logger : public spdlog::logger { + log_v2_base* _parent; + + public: + template + log_v2_logger(std::string name, + log_v2_base* parent, + sink_iter begin, + sink_iter end) + : spdlog::logger(name, begin, end), _parent(parent) {} + + log_v2_logger(std::string name, log_v2_base* parent, spdlog::sink_ptr sink) + : spdlog::logger(name, sink), _parent(parent) {} + + log_v2_base* get_parent() { return _parent; } +}; + +CCE_END() + +#endif diff --git a/engine/modules/external_commands/precomp_inc/precomp.hh b/engine/modules/external_commands/precomp_inc/precomp.hh index 1405d24a1d3..3ee5870b1f3 100644 --- a/engine/modules/external_commands/precomp_inc/precomp.hh +++ b/engine/modules/external_commands/precomp_inc/precomp.hh @@ -37,6 +37,8 @@ #include #include +#include + #include #include diff --git a/engine/precomp_inc/precomp.hh b/engine/precomp_inc/precomp.hh index 54a17d3b608..844c52ad835 100644 --- a/engine/precomp_inc/precomp.hh +++ b/engine/precomp_inc/precomp.hh @@ -68,6 +68,8 @@ #include #include +#include + #include "com/centreon/engine/namespace.hh" namespace fmt { diff --git a/engine/src/configuration/applier/state.cc b/engine/src/configuration/applier/state.cc index 70bb78f5fff..5b004767b52 100644 --- a/engine/src/configuration/applier/state.cc +++ b/engine/src/configuration/applier/state.cc @@ -1321,7 +1321,7 @@ void applier::state::_processing(configuration::state& new_cfg, applier::logging::instance().apply(new_cfg); - log_v2::instance().apply(new_cfg); + log_v2::instance()->apply(new_cfg); // Apply globals configurations. applier::globals::instance().apply(new_cfg); diff --git a/engine/src/configuration/state.cc b/engine/src/configuration/state.cc index ec63624daab..79e41a540a7 100644 --- a/engine/src/configuration/state.cc +++ b/engine/src/configuration/state.cc @@ -329,7 +329,7 @@ static float const default_low_service_flap_threshold(20.0); static unsigned long const default_max_debug_file_size(1000000); static unsigned int const default_max_host_check_spread(5); static unsigned long const default_max_log_file_size(0); -static constexpr uint32_t default_log_flush_period{5u}; +static constexpr uint32_t default_log_flush_period{2u}; static unsigned int const default_max_parallel_service_checks(0); static unsigned int const default_max_service_check_spread(5); static unsigned int const default_notification_timeout(30); diff --git a/engine/src/log_v2.cc b/engine/src/log_v2.cc index 1660b3dc65e..3251d0cd212 100644 --- a/engine/src/log_v2.cc +++ b/engine/src/log_v2.cc @@ -27,17 +27,26 @@ using namespace com::centreon::engine; using namespace spdlog; -log_v2& log_v2::instance() { - static log_v2 instance; - return instance; +std::shared_ptr log_v2::_instance; + +void log_v2::load(const std::shared_ptr& io_context) { + _instance.reset(new log_v2(io_context)); +} + +std::shared_ptr log_v2::instance() { + return _instance; } -log_v2::log_v2() : _running{false} { +log_v2::log_v2(const std::shared_ptr& io_context) + : log_v2_base("engine"), + _running{false}, + _flush_timer(*io_context), + _flush_timer_active(true), + _io_context(io_context) { auto stdout_sink = std::make_shared(); - auto create_logger = [&stdout_sink](const std::string& name, - level::level_enum lvl) { + auto create_logger = [&](const std::string& name, level::level_enum lvl) { spdlog::drop(name); - auto log = std::make_shared(name, stdout_sink); + auto log = std::make_shared(name, this, stdout_sink); log->set_level(lvl); log->flush_on(lvl); log->set_pattern("[%Y-%m-%dT%H:%M:%S.%e%z] [%n] [%l] %v"); @@ -45,35 +54,29 @@ log_v2::log_v2() : _running{false} { return log; }; - _log[log_v2::log_functions] = - create_logger("functions", level::from_str("error")); - _log[log_v2::log_config] = - create_logger("configuration", level::from_str("info")); - _log[log_v2::log_events] = create_logger("events", level::from_str("info")); - _log[log_v2::log_checks] = create_logger("checks", level::from_str("info")); - _log[log_v2::log_notifications] = - create_logger("notifications", level::from_str("error")); - _log[log_v2::log_eventbroker] = - create_logger("eventbroker", level::from_str("error")); + _log[log_v2::log_functions] = create_logger("functions", level::err); + _log[log_v2::log_config] = create_logger("configuration", level::info); + _log[log_v2::log_events] = create_logger("events", level::info); + _log[log_v2::log_checks] = create_logger("checks", level::info); + _log[log_v2::log_notifications] = create_logger("notifications", level::err); + _log[log_v2::log_eventbroker] = create_logger("eventbroker", level::err); _log[log_v2::log_external_command] = - create_logger("external_command", level::from_str("error")); - _log[log_v2::log_commands] = - create_logger("commands", level::from_str("error")); - _log[log_v2::log_downtimes] = - create_logger("downtimes", level::from_str("error")); - _log[log_v2::log_comments] = - create_logger("comments", level::from_str("error")); - _log[log_v2::log_macros] = create_logger("macros", level::from_str("error")); - _log[log_v2::log_process] = create_logger("process", level::from_str("info")); - _log[log_v2::log_runtime] = - create_logger("runtime", level::from_str("error")); + create_logger("external_command", level::err); + _log[log_v2::log_commands] = create_logger("commands", level::err); + _log[log_v2::log_downtimes] = create_logger("downtimes", level::err); + _log[log_v2::log_comments] = create_logger("comments", level::err); + _log[log_v2::log_macros] = create_logger("macros", level::err); + _log[log_v2::log_process] = create_logger("process", level::info); + _log[log_v2::log_runtime] = create_logger("runtime", level::err); + + _log[log_v2::log_process]->info("{} : log started", _log_name); + _running = true; } log_v2::~log_v2() noexcept { _log[log_v2::log_runtime]->info("log finished"); _running = false; - spdlog::shutdown(); for (auto& l : _log) l.reset(); } @@ -84,44 +87,46 @@ void log_v2::apply(const configuration::state& config) { _running = false; std::vector sinks; + spdlog::sink_ptr sink_to_flush; if (config.log_v2_enabled()) { if (config.log_v2_logger() == "file") { - if (config.log_file() != "") - sinks.push_back( - std::make_shared(config.log_file())); - else { + if (config.log_file() != "") { + _file_path = config.log_file(); + sink_to_flush = std::make_shared(_file_path); + } else { log_v2::config()->error("log_file name is empty"); - sinks.push_back(std::make_shared()); + sink_to_flush = std::make_shared(); } } else if (config.log_v2_logger() == "syslog") - sinks.push_back(std::make_shared("centreon-engine", - 0, 0, true)); + sink_to_flush = std::make_shared("centreon-engine", + 0, 0, true); + if (sink_to_flush) { + sinks.push_back(sink_to_flush); + } auto broker_sink = std::make_shared(); broker_sink->set_level(spdlog::level::info); sinks.push_back(broker_sink); } else sinks.push_back(std::make_shared()); - auto create_logger = [&sinks, log_pid = config.log_pid(), - log_file_line = config.log_file_line(), - log_flush_period = config.log_flush_period()]( - const std::string& name, level::level_enum lvl) { + auto create_logger = [&](const std::string& name, level::level_enum lvl) { spdlog::drop(name); - auto log = std::make_shared(name, begin(sinks), end(sinks)); + auto log = + std::make_shared(name, this, begin(sinks), end(sinks)); log->set_level(lvl); - if (log_flush_period) + if (config.log_flush_period()) log->flush_on(level::warn); else - log->flush_on(lvl); + log->flush_on(level::trace); - if (log_pid) { - if (log_file_line) { + if (config.log_pid()) { + if (config.log_file_line()) { log->set_pattern("[%Y-%m-%dT%H:%M:%S.%e%z] [%s:%#] [%n] [%l] [%P] %v"); } else { log->set_pattern("[%Y-%m-%dT%H:%M:%S.%e%z] [%n] [%l] [%P] %v"); } } else { - if (log_file_line) { + if (config.log_file_line()) { log->set_pattern("[%Y-%m-%dT%H:%M:%S.%e%z] [%s:%#] [%n] [%l] %v"); } else { log->set_pattern("[%Y-%m-%dT%H:%M:%S.%e%z] [%n] [%l] %v"); @@ -158,124 +163,74 @@ void log_v2::apply(const configuration::state& config) { _log[log_runtime] = create_logger("runtime", level::from_str(config.log_level_runtime())); - spdlog::flush_every(std::chrono::seconds(config.log_flush_period())); - _running = true; -} + _flush_interval = std::chrono::seconds( + config.log_flush_period() > 0 ? config.log_flush_period() : 2); -std::shared_ptr log_v2::functions() { - if (instance()._running) - return instance()._log[log_v2::log_functions]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("functions", null_sink); - } -} - -std::shared_ptr log_v2::config() { - if (instance()._running) - return instance()._log[log_v2::log_config]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("configuration", null_sink); - } -} - -std::shared_ptr log_v2::events() { - if (instance()._running) - return instance()._log[log_v2::log_events]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("events", null_sink); - } -} - -std::shared_ptr log_v2::checks() { - if (instance()._running) - return instance()._log[log_v2::log_checks]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("checks", null_sink); - } -} - -std::shared_ptr log_v2::notifications() { - if (instance()._running) - return instance()._log[log_v2::log_notifications]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("notifications", null_sink); - } -} - -std::shared_ptr log_v2::eventbroker() { - if (instance()._running) - return instance()._log[log_v2::log_eventbroker]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("eventbroker", null_sink); - } -} - -std::shared_ptr log_v2::external_command() { - if (instance()._running) - return instance()._log[log_v2::log_external_command]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("external_command", null_sink); - } -} - -std::shared_ptr log_v2::commands() { - if (instance()._running) - return instance()._log[log_v2::log_commands]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("commands", null_sink); - } -} - -std::shared_ptr log_v2::downtimes() { - if (instance()._running) - return instance()._log[log_v2::log_downtimes]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("downtimes", null_sink); + if (sink_to_flush) { + start_flush_timer(sink_to_flush); + } else { + std::lock_guard l(_flush_timer_m); + _flush_timer.cancel(); } + _running = true; } -std::shared_ptr log_v2::comments() { - if (instance()._running) - return instance()._log[log_v2::log_comments]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("comments", null_sink); +void log_v2::set_flush_interval(unsigned second_flush_interval) { + log_v2_base::set_flush_interval(second_flush_interval); + if (second_flush_interval) { + for (auto logger : _log) { + logger->flush_on(level::warn); + } + } else { + for (auto logger : _log) { + logger->flush_on(level::trace); + } } } -std::shared_ptr log_v2::macros() { - if (instance()._running) - return instance()._log[log_v2::log_macros]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("macros", null_sink); - } +/** + * @brief logs are written periodicaly to disk + * + * @param sink + */ +void log_v2::start_flush_timer(spdlog::sink_ptr sink) { + std::lock_guard l(_flush_timer_m); + _flush_timer.expires_after(_flush_interval); + _flush_timer.async_wait([me = _instance, sink](const asio::error_code& err) { + if (err || !me->_flush_timer_active) { + return; + } + if (me->get_flush_interval().count() > 0) { + sink->flush(); + } + me->start_flush_timer(sink); + }); } -std::shared_ptr log_v2::process() { - if (instance()._running) - return instance()._log[log_v2::log_process]; - else { - auto null_sink = std::make_shared(); - return std::make_shared("process", null_sink); - } +/** + * @brief stop flush timer + * + */ +void log_v2::stop_flush_timer() { + std::lock_guard l(_flush_timer_m); + _flush_timer_active = false; + _flush_timer.cancel(); } -std::shared_ptr log_v2::runtime() { - if (instance()._running) - return instance()._log[log_v2::log_runtime]; +/** + * @brief this private static method is used to access a specific logger + * + * @param log_type + * @param log_str + * @return std::shared_ptr + */ +std::shared_ptr log_v2::get_logger(logger log_type, + const char* log_str) { + if (_running) + return _log[log_type]; else { auto null_sink = std::make_shared(); - return std::make_shared("runtime", null_sink); + return std::make_shared(log_str, null_sink); } } diff --git a/engine/src/main.cc b/engine/src/main.cc index c5e4c415f1d..fc40d4db300 100644 --- a/engine/src/main.cc +++ b/engine/src/main.cc @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -64,6 +65,10 @@ using namespace com::centreon::engine; +std::shared_ptr g_io_context( + std::make_shared()); +bool g_io_context_started = false; + // Error message when configuration parsing fail. #define ERROR_CONFIGURATION \ " Check your configuration file(s) to ensure that they contain valid\n" \ @@ -103,6 +108,7 @@ int main(int argc, char* argv[]) { config = new configuration::state; // Hack to instanciate the logger. + log_v2::load(g_io_context); configuration::applier::logging::instance(); logging::broker backend_broker_log; @@ -419,6 +425,22 @@ int main(int argc, char* argv[]) { // Send program data to broker. broker_program_state(NEBTYPE_PROCESS_EVENTLOOPSTART, NEBFLAG_NONE); + // if neb has not started g_io_context we do it here + if (!g_io_context_started) { + SPDLOG_LOGGER_INFO(log_v2::process(), + "io_context not started => create thread"); + std::thread asio_thread([cont = g_io_context]() { + try { + cont->run(); + } catch (const std::exception& e) { + SPDLOG_LOGGER_CRITICAL(log_v2::process(), + "catch in io_context run: {}", e.what()); + } + }); + asio_thread.detach(); + g_io_context_started = true; + } + // Get event start time and save as macro. event_start = time(NULL); mac->x[MACRO_EVENTSTARTTIME] = std::to_string(event_start); @@ -432,10 +454,11 @@ int main(int argc, char* argv[]) { com::centreon::engine::events::loop::instance().run(); if (sigshutdown) { + log_v2::instance()->stop_flush_timer(); engine_logger(logging::log_process_info, logging::basic) << "Caught SIG" << sigs[sig_id] << ", shutting down ..."; - log_v2::process()->info("Caught SIG {}, shutting down ...", - sigs[sig_id]); + SPDLOG_LOGGER_INFO(log_v2::process(), + "Caught SIG {}, shutting down ...", sigs[sig_id]); } // Send program data to broker. broker_program_state(NEBTYPE_PROCESS_EVENTLOOPEND, NEBFLAG_NONE); @@ -453,8 +476,8 @@ int main(int argc, char* argv[]) { if (sigshutdown) { engine_logger(logging::log_process_info, logging::basic) << "Successfully shutdown ... (PID=" << getpid() << ")"; - log_v2::process()->info("Successfully shutdown ... (PID={})", - getpid()); + SPDLOG_LOGGER_INFO(log_v2::process(), + "Successfully shutdown ... (PID={})", getpid()); } retval = EXIT_SUCCESS; @@ -462,7 +485,7 @@ int main(int argc, char* argv[]) { // Log. engine_logger(logging::log_runtime_error, logging::basic) << "Error: " << e.what(); - log_v2::process()->error("Error: {}", e.what()); + SPDLOG_LOGGER_ERROR(log_v2::process(), "Error: {}", e.what()); // Send program data to broker. broker_program_state(NEBTYPE_PROCESS_SHUTDOWN, NEBFLAG_PROCESS_INITIATED); @@ -471,12 +494,13 @@ int main(int argc, char* argv[]) { // Memory cleanup. cleanup(); + spdlog::shutdown(); delete[] config_file; config_file = NULL; } catch (std::exception const& e) { engine_logger(logging::log_runtime_error, logging::basic) << "Error: " << e.what(); - log_v2::process()->error("Error: {}", e.what()); + SPDLOG_LOGGER_ERROR(log_v2::process(), "Error: {}", e.what()); } // Unload singletons and global objects. diff --git a/engine/tests/CMakeLists.txt b/engine/tests/CMakeLists.txt index 18f87c084e2..d071fee54ee 100755 --- a/engine/tests/CMakeLists.txt +++ b/engine/tests/CMakeLists.txt @@ -1,21 +1,19 @@ # # Copyright 2016, 2020-2023 Centreon # - -# This file is part of Centreon Engine. +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at # -# Centreon Engine is free software : you can redistribute it and / or -# modify it under the terms of the GNU General Public License version 2 -# as published by the Free Software Foundation. +# http://www.apache.org/licenses/LICENSE-2.0 # -# Centreon Engine is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. # -# You should have received a copy of the GNU General Public License -# along with Centreon Engine. If not, see -# . +# For more information : contact@centreon.com # # Enable unit tests or not . @@ -42,114 +40,124 @@ if(WITH_TESTING) pthread) add_executable(bin_connector_test_run - "${TESTS_DIR}/commands/bin_connector_test_run.cc") + "${TESTS_DIR}/commands/bin_connector_test_run.cc") target_link_libraries(bin_connector_test_run cce_core pthread) target_precompile_headers(bin_connector_test_run REUSE_FROM cce_core) set(ut_sources - # Sources. - "${TESTS_DIR}/parse-check-output.cc" - "${TESTS_DIR}/checks/service_check.cc" - "${TESTS_DIR}/checks/service_retention.cc" - "${TESTS_DIR}/checks/anomalydetection.cc" - "${TESTS_DIR}/commands/simple-command.cc" - "${TESTS_DIR}/commands/connector.cc" - "${TESTS_DIR}/commands/environment.cc" - "${TESTS_DIR}/configuration/applier/applier-anomalydetection.cc" - "${TESTS_DIR}/configuration/applier/applier-command.cc" - "${TESTS_DIR}/configuration/applier/applier-connector.cc" - "${TESTS_DIR}/configuration/applier/applier-contact.cc" - "${TESTS_DIR}/configuration/applier/applier-contactgroup.cc" - "${TESTS_DIR}/configuration/applier/applier-global.cc" - "${TESTS_DIR}/configuration/applier/applier-log.cc" - "${TESTS_DIR}/configuration/applier/applier-host.cc" - "${TESTS_DIR}/configuration/applier/applier-hostescalation.cc" - "${TESTS_DIR}/configuration/applier/applier-hostdependency.cc" - "${TESTS_DIR}/configuration/applier/applier-hostgroup.cc" - "${TESTS_DIR}/configuration/applier/applier-service.cc" - "${TESTS_DIR}/configuration/applier/applier-serviceescalation.cc" - "${TESTS_DIR}/configuration/applier/applier-servicegroup.cc" - "${TESTS_DIR}/configuration/applier/applier-state.cc" - "${TESTS_DIR}/configuration/contact.cc" - "${TESTS_DIR}/configuration/host.cc" - "${TESTS_DIR}/configuration/object.cc" - "${TESTS_DIR}/configuration/service.cc" - "${TESTS_DIR}/configuration/severity.cc" - "${TESTS_DIR}/configuration/tag.cc" - "${TESTS_DIR}/configuration/timeperiod-test.cc" - "${TESTS_DIR}/contacts/contactgroup-config.cc" - "${TESTS_DIR}/contacts/simple-contactgroup.cc" - "${TESTS_DIR}/custom_vars/extcmd.cc" - "${TESTS_DIR}/downtimes/downtime.cc" - "${TESTS_DIR}/downtimes/downtime_finder.cc" - "${TESTS_DIR}/enginerpc/enginerpc.cc" - "${TESTS_DIR}/helper.cc" - "${TESTS_DIR}/macros/macro.cc" - "${TESTS_DIR}/macros/macro_hostname.cc" - "${TESTS_DIR}/macros/macro_service.cc" - "${TESTS_DIR}/external_commands/anomalydetection.cc" - "${TESTS_DIR}/external_commands/host.cc" - "${TESTS_DIR}/external_commands/service.cc" - "${TESTS_DIR}/main.cc" - "${TESTS_DIR}/loop/loop.cc" - "${TESTS_DIR}/notifications/host_downtime_notification.cc" - "${TESTS_DIR}/notifications/host_flapping_notification.cc" - "${TESTS_DIR}/notifications/host_normal_notification.cc" - "${TESTS_DIR}/notifications/host_recovery_notification.cc" - "${TESTS_DIR}/notifications/service_normal_notification.cc" - "${TESTS_DIR}/notifications/service_timeperiod_notification.cc" - "${TESTS_DIR}/notifications/service_flapping_notification.cc" - "${TESTS_DIR}/notifications/service_downtime_notification_test.cc" - "${TESTS_DIR}/perfdata/perfdata.cc" - "${TESTS_DIR}/retention/host.cc" - "${TESTS_DIR}/retention/service.cc" - "${TESTS_DIR}/string/string.cc" - "${TESTS_DIR}/test_engine.cc" - "${TESTS_DIR}/timeperiod/get_next_valid_time/between_two_years.cc" - "${TESTS_DIR}/timeperiod/get_next_valid_time/calendar_date.cc" - "${TESTS_DIR}/timeperiod/get_next_valid_time/dst_backward.cc" - "${TESTS_DIR}/timeperiod/get_next_valid_time/dst_forward.cc" - "${TESTS_DIR}/timeperiod/get_next_valid_time/earliest_daterange_first.cc" - "${TESTS_DIR}/timeperiod/get_next_valid_time/exclusion.cc" - "${TESTS_DIR}/timeperiod/get_next_valid_time/exceptions_test.cc" - "${TESTS_DIR}/timeperiod/get_next_valid_time/generic_month_date.cc" - "${TESTS_DIR}/timeperiod/get_next_valid_time/normal_weekday.cc" - "${TESTS_DIR}/timeperiod/get_next_valid_time/offset_weekday_of_generic_month.cc" - "${TESTS_DIR}/timeperiod/get_next_valid_time/offset_weekday_of_specific_month.cc" - "${TESTS_DIR}/timeperiod/get_next_valid_time/precedence.cc" - "${TESTS_DIR}/timeperiod/get_next_valid_time/skip_interval.cc" - "${TESTS_DIR}/timeperiod/get_next_valid_time/specific_month_date.cc" - "${TESTS_DIR}/timeperiod/utils.cc" - # # Headers. - "${TESTS_DIR}/test_engine.hh" - "${TESTS_DIR}/timeperiod/utils.hh") + + # Sources. + "${TESTS_DIR}/parse-check-output.cc" + "${TESTS_DIR}/checks/service_check.cc" + "${TESTS_DIR}/checks/service_retention.cc" + "${TESTS_DIR}/checks/anomalydetection.cc" + "${TESTS_DIR}/commands/simple-command.cc" + "${TESTS_DIR}/commands/connector.cc" + "${TESTS_DIR}/commands/environment.cc" + "${TESTS_DIR}/configuration/applier/applier-anomalydetection.cc" + "${TESTS_DIR}/configuration/applier/applier-command.cc" + "${TESTS_DIR}/configuration/applier/applier-connector.cc" + "${TESTS_DIR}/configuration/applier/applier-contact.cc" + "${TESTS_DIR}/configuration/applier/applier-contactgroup.cc" + "${TESTS_DIR}/configuration/applier/applier-global.cc" + "${TESTS_DIR}/configuration/applier/applier-log.cc" + "${TESTS_DIR}/configuration/applier/applier-host.cc" + "${TESTS_DIR}/configuration/applier/applier-hostescalation.cc" + "${TESTS_DIR}/configuration/applier/applier-hostdependency.cc" + "${TESTS_DIR}/configuration/applier/applier-hostgroup.cc" + "${TESTS_DIR}/configuration/applier/applier-service.cc" + "${TESTS_DIR}/configuration/applier/applier-serviceescalation.cc" + "${TESTS_DIR}/configuration/applier/applier-servicegroup.cc" + "${TESTS_DIR}/configuration/applier/applier-state.cc" + "${TESTS_DIR}/configuration/contact.cc" + "${TESTS_DIR}/configuration/host.cc" + "${TESTS_DIR}/configuration/object.cc" + "${TESTS_DIR}/configuration/service.cc" + "${TESTS_DIR}/configuration/severity.cc" + "${TESTS_DIR}/configuration/tag.cc" + "${TESTS_DIR}/configuration/timeperiod-test.cc" + "${TESTS_DIR}/contacts/contactgroup-config.cc" + "${TESTS_DIR}/contacts/simple-contactgroup.cc" + "${TESTS_DIR}/custom_vars/extcmd.cc" + "${TESTS_DIR}/downtimes/downtime.cc" + "${TESTS_DIR}/downtimes/downtime_finder.cc" + "${TESTS_DIR}/enginerpc/enginerpc.cc" + "${TESTS_DIR}/helper.cc" + "${TESTS_DIR}/macros/macro.cc" + "${TESTS_DIR}/macros/macro_hostname.cc" + "${TESTS_DIR}/macros/macro_service.cc" + "${TESTS_DIR}/external_commands/anomalydetection.cc" + "${TESTS_DIR}/external_commands/host.cc" + "${TESTS_DIR}/external_commands/service.cc" + "${TESTS_DIR}/main.cc" + "${TESTS_DIR}/loop/loop.cc" + "${TESTS_DIR}/notifications/host_downtime_notification.cc" + "${TESTS_DIR}/notifications/host_flapping_notification.cc" + "${TESTS_DIR}/notifications/host_normal_notification.cc" + "${TESTS_DIR}/notifications/host_recovery_notification.cc" + "${TESTS_DIR}/notifications/service_normal_notification.cc" + "${TESTS_DIR}/notifications/service_timeperiod_notification.cc" + "${TESTS_DIR}/notifications/service_flapping_notification.cc" + "${TESTS_DIR}/notifications/service_downtime_notification_test.cc" + "${TESTS_DIR}/perfdata/perfdata.cc" + "${TESTS_DIR}/retention/host.cc" + "${TESTS_DIR}/retention/service.cc" + "${TESTS_DIR}/string/string.cc" + "${TESTS_DIR}/test_engine.cc" + "${TESTS_DIR}/timeperiod/get_next_valid_time/between_two_years.cc" + "${TESTS_DIR}/timeperiod/get_next_valid_time/calendar_date.cc" + "${TESTS_DIR}/timeperiod/get_next_valid_time/dst_backward.cc" + "${TESTS_DIR}/timeperiod/get_next_valid_time/dst_forward.cc" + "${TESTS_DIR}/timeperiod/get_next_valid_time/earliest_daterange_first.cc" + "${TESTS_DIR}/timeperiod/get_next_valid_time/exclusion.cc" + "${TESTS_DIR}/timeperiod/get_next_valid_time/exceptions_test.cc" + "${TESTS_DIR}/timeperiod/get_next_valid_time/generic_month_date.cc" + "${TESTS_DIR}/timeperiod/get_next_valid_time/normal_weekday.cc" + "${TESTS_DIR}/timeperiod/get_next_valid_time/offset_weekday_of_generic_month.cc" + "${TESTS_DIR}/timeperiod/get_next_valid_time/offset_weekday_of_specific_month.cc" + "${TESTS_DIR}/timeperiod/get_next_valid_time/precedence.cc" + "${TESTS_DIR}/timeperiod/get_next_valid_time/skip_interval.cc" + "${TESTS_DIR}/timeperiod/get_next_valid_time/specific_month_date.cc" + "${TESTS_DIR}/timeperiod/utils.cc" + + # # Headers. + "${TESTS_DIR}/test_engine.hh" + "${TESTS_DIR}/timeperiod/utils.hh") # Unit test executable. include_directories(${TESTS_DIR}) + if(WITH_ASAN) + set(CMAKE_BUILD_TYPE Debug) + set(CMAKE_CXX_FLAGS_DEBUG + "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + set(CMAKE_LINKER_FLAGS_DEBUG + "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + endif() + add_executable(ut_engine ${ut_sources}) target_precompile_headers(ut_engine REUSE_FROM cce_core) set_target_properties( ut_engine rpc_client_engine bin_connector_test_run PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests - RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/tests - RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/tests - RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/tests - RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_BINARY_DIR}/tests) + RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/tests + RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/tests + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/tests + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_BINARY_DIR}/tests) # file used by timeperiod-test.cc file(COPY ${TESTS_DIR}/configuration/timeperiods.cfg - DESTINATION ${CMAKE_BINARY_DIR}/tests/) + DESTINATION ${CMAKE_BINARY_DIR}/tests/) add_test(NAME tests COMMAND ut_engine) if(WITH_COVERAGE) set(COVERAGE_EXCLUDES - '${PROJECT_BINARY_DIR}/*' '${PROJECT_SOURCE_DIR}/tests/*' - '/usr/include/*' '*/.conan/*') + '${PROJECT_BINARY_DIR}/*' '${PROJECT_SOURCE_DIR}/tests/*' + '/usr/include/*' '*/.conan/*') setup_target_for_coverage(NAME engine-test-coverage EXECUTABLE ut_engine - DEPENDENCIES ut_engine) + DEPENDENCIES ut_engine) set(GCOV gcov) endif() @@ -157,6 +165,7 @@ if(WITH_TESTING) ut_engine ${ENGINERPC} cce_core + enginelog pthread ${GCOV} CONAN_PKG::gtest diff --git a/engine/tests/helper.cc b/engine/tests/helper.cc index fb1133a5e48..a0e12546f84 100644 --- a/engine/tests/helper.cc +++ b/engine/tests/helper.cc @@ -37,7 +37,7 @@ void init_config_state(void) { // Hack to instanciate the logger. configuration::applier::logging::instance().apply(*config); - log_v2::instance().apply(*config); + log_v2::instance()->apply(*config); checks::checker::init(true); } diff --git a/engine/tests/main.cc b/engine/tests/main.cc index 58d5f1d4602..fc6e0c7cac8 100644 --- a/engine/tests/main.cc +++ b/engine/tests/main.cc @@ -19,6 +19,11 @@ #include #include "com/centreon/clib.hh" +#include "com/centreon/engine/log_v2.hh" + +std::shared_ptr g_io_context( + std::make_shared()); +bool g_io_context_started = false; class CentreonEngineEnvironment : public testing::Environment { public: @@ -45,6 +50,9 @@ int main(int argc, char* argv[]) { // Set specific environment. testing::AddGlobalTestEnvironment(new CentreonEngineEnvironment()); + com::centreon::engine::log_v2::load(g_io_context); // Run all tests. - return (RUN_ALL_TESTS()); + int ret = RUN_ALL_TESTS(); + spdlog::shutdown(); + return ret; } diff --git a/resources/init-database-for-sql-ut.sh b/resources/init-database-for-sql-ut.sh new file mode 100644 index 00000000000..c11f4319200 --- /dev/null +++ b/resources/init-database-for-sql-ut.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ -x /usr/bin/mariadb ] ; then + mysql=mariadb +elif [ -x /usr/bin/mysql ] ; then + mysql=mysql +fi + +docker run --name mariadbtest -e MYSQL_ROOT_PASSWORD=centreon -p 3306:3306 -d docker.io/library/mariadb:10.5 +$mysql -h 127.0.0.1 -u root -pcentreon < ../resources/centreon_storage.sql +$mysql -h 127.0.0.1 -u root -pcentreon < ../resources/centreon.sql diff --git a/tests/README.md b/tests/README.md index a6db4c4ed1b..d46620d624a 100644 --- a/tests/README.md +++ b/tests/README.md @@ -12,7 +12,20 @@ These tests are executed from the `centreon-tests/robot` folder and uses the [Ro From a Centreon host, you need to install Robot Framework -On rpm based distributions, you can try the following commands to initialize your robot tests: +On CentOS 7, the following commands should work to initialize your robot tests: + +``` +pip3 install -U robotframework robotframework-databaselibrary pymysql + +yum install "Development Tools" python3-devel -y + +pip3 install grpcio==1.33.2 grpcio_tools==1.33.2 + +./init-proto.sh +./init-sql.sh +``` + +On other rpm based distributions, you can try the following commands to initialize your robot tests: ``` pip3 install -U robotframework robotframework-databaselibrary pymysql @@ -42,6 +55,12 @@ robot broker/sql.robot Here is the list of the currently implemented tests: ### Bam +- [x] **BABEST_SERVICE_CRITICAL**: With bbdo version 3.0.1, a BA of type 'best' with 2 serv, ba is critical only if the 2 services are critical +- [x] **BAPBSTATUS**: With bbdo version 3.0.1, a BA of type 'worst' with one service is configured. The BA is in critical state, because of its service. +- [x] **BA_BOOL_KPI**: With bbdo version 3.0.1, a BA of type 'worst' with 1 boolean kpi +- [x] **BA_IMPACT_2KPI_SERVICES**: With bbdo version 3.0.1, a BA of type 'impact' with 2 serv, ba is critical only if the 2 services are critical +- [x] **BA_RATIO_NUMBER_BA_SERVICE**: With bbdo version 3.0.1, a BA of type 'ratio number' with 2 serv an 1 ba with one service +- [x] **BA_RATIO_PERCENT_BA_SERVICE**: With bbdo version 3.0.1, a BA of type 'ratio percent' with 2 serv an 1 ba with one service - [x] **BEBAMIDT1**: A BA of type 'worst' with one service is configured. The BA is in critical state, because of its service. Then we set a downtime on this last one. An inherited downtime is set to the BA. The downtime is removed from the service, the inherited downtime is then deleted. - [x] **BEBAMIDT2**: A BA of type 'worst' with one service is configured. The BA is in critical state, because of its service. Then we set a downtime on this last one. An inherited downtime is set to the BA. Engine is restarted. Broker is restarted. The two downtimes are still there with no duplicates. The downtime is removed from the service, the inherited downtime is then deleted. - [x] **BEBAMIDTU1**: With bbdo version 3.0.1, a BA of type 'worst' with one service is configured. The BA is in critical state, because of its service. Then we set a downtime on this last one. An inherited downtime is set to the BA. The downtime is removed from the service, the inherited downtime is then deleted. @@ -50,6 +69,15 @@ Here is the list of the currently implemented tests: - [x] **BEBAMIGNDT2**: A BA of type 'worst' with two services is configured. The downtime policy on this ba is "Ignore the indicator in the calculation". The BA is in critical state, because of the second critical service. Then we apply two downtimes on this last one. The BA state is ok because of the policy on indicators. The first downtime reaches its end, the BA is still OK, but when the second downtime reaches its end, the BA should be CRITICAL. - [x] **BEBAMIGNDTU1**: With bbdo version 3.0.1, a BA of type 'worst' with two services is configured. The downtime policy on this ba is "Ignore the indicator in the calculation". The BA is in critical state, because of the second critical service. Then we apply two downtimes on this last one. The BA state is ok because of the policy on indicators. A first downtime is cancelled, the BA is still OK, but when the second downtime is cancelled, the BA should be CRITICAL. - [x] **BEBAMIGNDTU2**: With bbdo version 3.0.1, a BA of type 'worst' with two services is configured. The downtime policy on this ba is "Ignore the indicator in the calculation". The BA is in critical state, because of the second critical service. Then we apply two downtimes on this last one. The BA state is ok because of the policy on indicators. The first downtime reaches its end, the BA is still OK, but when the second downtime reaches its end, the BA should be CRITICAL. +- [x] **BEPB_BA_DURATION_EVENT**: use of pb_ba_duration_event message. +- [x] **BEPB_DIMENSION_BA_BV_RELATION_EVENT**: bbdo_version 3 use pb_dimension_ba_bv_relation_event message. +- [x] **BEPB_DIMENSION_BA_EVENT**: bbdo_version 3 use pb_dimension_ba_event message. +- [x] **BEPB_DIMENSION_BA_TIMEPERIOD_RELATION**: use of pb_dimension_ba_timeperiod_relation message. +- [x] **BEPB_DIMENSION_BV_EVENT**: bbdo_version 3 use pb_dimension_bv_event message. +- [x] **BEPB_DIMENSION_KPI_EVENT**: bbdo_version 3 use pb_dimension_kpi_event message. +- [x] **BEPB_DIMENSION_TIMEPERIOD**: use of pb_dimension_timeperiod message. +- [x] **BEPB_DIMENSION_TRUNCATE_TABLE**: use of pb_dimension_timeperiod message. +- [x] **BEPB_KPI_STATUS**: bbdo_version 3 use kpi_status message. ### Broker - [x] **BCL1**: Starting broker with option '-s foobar' should return an error @@ -177,7 +205,7 @@ Here is the list of the currently implemented tests: - [x] **BECT2**: Broker/Engine communication with TLS between central and poller with key/cert - [x] **BECT3**: Broker/Engine communication with anonymous TLS and ca certificate - [x] **BECT4**: Broker/Engine communication with TLS between central and poller with key/cert and hostname forced -- [x] **BECT_GRPC1**: Broker/Engine communication with anonymous TLS between central and poller +- [x] **BECT_GRPC1**: Broker/Engine communication with GRPC and with anonymous TLS between central and poller - [x] **BECT_GRPC2**: Broker/Engine communication with TLS between central and poller with key/cert - [x] **BECT_GRPC3**: Broker/Engine communication with anonymous TLS and ca certificate - [x] **BECT_GRPC4**: Broker/Engine communication with TLS between central and poller with key/cert and hostname forced @@ -268,6 +296,7 @@ Here is the list of the currently implemented tests: - [x] **BESS_CRYPTED_REVERSED_GRPC1**: Start-Stop grpc version Broker/Engine - well configured - [x] **BESS_CRYPTED_REVERSED_GRPC2**: Start-Stop grpc version Broker/Engine only engine server crypted - [x] **BESS_CRYPTED_REVERSED_GRPC3**: Start-Stop grpc version Broker/Engine only engine crypted +- [x] **BESS_ENGINE_DELETE_HOST**: once engine and cbd started, stop and restart cbd, delete an host and reload engine, cbd mustn't core - [x] **BESS_GRPC1**: Start-Stop grpc version Broker/Engine - Broker started first - Broker stopped first - [x] **BESS_GRPC2**: Start-Stop grpc version Broker/Engine - Broker started first - Engine stopped first - [x] **BESS_GRPC3**: Start-Stop grpc version Broker/Engine - Engine started first - Engine stopped first @@ -288,6 +317,7 @@ Here is the list of the currently implemented tests: - [x] **BEUTAG7**: some services are configured and deleted with tags on two pollers. - [x] **BEUTAG8**: Services have tags provided by templates. - [x] **BEUTAG9**: hosts have tags provided by templates. +- [x] **BE_NOTIF_OVERFLOW**: bbdo 2.0 notification number =40000. make an overflow => notification_number null in db - [x] **BRCS1**: Broker reverse connection stopped - [x] **BRCTS1**: Broker reverse connection too slow - [x] **BRGC1**: Broker good reverse connection @@ -334,10 +364,10 @@ Here is the list of the currently implemented tests: - [x] **EBNSGU1**: New service group with several pollers and connections to DB with broker configured with unified_sql - [x] **EBNSGU2**: New service group with several pollers and connections to DB with broker configured with unified_sql - [x] **EBNSVC1**: New services with several pollers -- [x] **EBSAU2**: New services with action_url with more than 2000 characters -- [x] **EBSN3**: New services with notes with more than 500 characters -- [x] **EBSNU1**: New services with notes_url with more than 2000 characters -- [x] **ENRSCHE1**: check next check of reschedule is last_check+interval_check +- [x] **EBSAU2**: New hosts with action_url with more than 2000 characters +- [x] **EBSN3**: New hosts with notes with more than 500 characters +- [x] **EBSNU1**: New hosts with notes_url with more than 2000 characters +- [x] **ENRSCHE1**: Verify that next check of a rescheduled host is made at last_check + interval_check - [x] **LOGV2BE2**: log-v2 enabled old log enabled check broker sink is equal - [x] **LOGV2BEU2**: Broker sink must have the same behavior with legacy logs enabled. - [x] **LOGV2DB1**: log-v2 disabled old log enabled check broker sink diff --git a/tests/broker-engine/start-stop.robot b/tests/broker-engine/start-stop.robot index 0ca2f020bb2..3e50b9e8c69 100644 --- a/tests/broker-engine/start-stop.robot +++ b/tests/broker-engine/start-stop.robot @@ -397,3 +397,28 @@ BESS_ENGINE_DELETE_HOST Sleep 2s Kindly Stop Broker True Stop Engine + +BESSBQ1 + [Documentation] A very bad queue file is written for broker. Broker and Engine are then started, Broker must read the file raising an error because of that file and then get data sent by Engine. + [Tags] Broker Engine start-stop queue + Config Engine ${1} + Config Broker central + Config Broker rrd + Config Broker module + Broker Config Flush Log central 0 + Broker Config Log central core error + Broker Config Log central bbdo debug + Broker Config Log central sql trace + Broker Config Log central core debug + Config Broker Sql Output central unified_sql + Clear Retention + Create bad queue central-broker-master.queue.central-broker-master-sql + ${start}= Get Current Date + Start Broker + Start Engine + ${content}= Create List mysql_connection: execute statement 306524174: UPDATE services SET command_line + + ${result}= Find In Log With Timeout ${centralLog} ${start} ${content} 120 + Should Be True ${result} msg=Services should be updated after the ingestion of the queue file + Stop Engine + Kindly Stop Broker diff --git a/tests/broker-engine/tls.robot b/tests/broker-engine/tls.robot index aead279f93a..56f8bcab071 100644 --- a/tests/broker-engine/tls.robot +++ b/tests/broker-engine/tls.robot @@ -176,7 +176,7 @@ BECT4 Should Be True ${result} BECT_GRPC1 - [Documentation] Broker/Engine communication with anonymous TLS between central and poller + [Documentation] Broker/Engine communication with GRPC and with anonymous TLS between central and poller [Tags] Broker Engine TLS tcp Config Engine ${1} Config Broker rrd diff --git a/tests/resources/Common.py b/tests/resources/Common.py index f4be91ca4a0..65788088624 100644 --- a/tests/resources/Common.py +++ b/tests/resources/Common.py @@ -1046,6 +1046,25 @@ def find_internal_id(date, exists=True, timeout: int = TIMEOUT): time.sleep(1) return False +def create_bad_queue(filename: str): + f = open(f"{VAR_ROOT}/lib/centreon-broker/{filename}", 'wb') + buffer = bytearray(10000) + buffer[0] = 0 + buffer[1] = 0 + buffer[2] = 0 + buffer[3] = 0 + buffer[4] = 0 + buffer[5] = 0 + buffer[6] = 8 + buffer[7] = 0 + t = 0 + for i in range(8, 10000): + buffer[i] = t + t += 1 + if t > 100: + t = 0 + f.write(buffer) + f.close() def check_types_in_resources(lst: list): connection = pymysql.connect(host=DB_HOST,