diff --git a/.github/scripts/agent_installer_test.ps1 b/.github/scripts/agent_installer_test.ps1 index c2d91f3b04..a52b126550 100644 --- a/.github/scripts/agent_installer_test.ps1 +++ b/.github/scripts/agent_installer_test.ps1 @@ -51,9 +51,13 @@ function test_args_to_registry { for (($i = 0); $i -lt 10; $i++) { Start-Sleep -Seconds 1 - if (Get-ItemProperty -Path HKLM:\Software\Centreon\CentreonMonitoringAgent) { + try { + Get-ItemProperty -Path HKLM:\Software\Centreon\CentreonMonitoringAgent break } + catch { + continue + } } foreach ($value_name in $expected_registry_values.Keys) { diff --git a/.github/scripts/agent_robot_test.ps1 b/.github/scripts/agent_robot_test.ps1 index 0190b0dff2..3f0fe0cd6b 100644 --- a/.github/scripts/agent_robot_test.ps1 +++ b/.github/scripts/agent_robot_test.ps1 @@ -104,6 +104,14 @@ $uptime = (Get-WmiObject -Class Win32_OperatingSystem).LastBootUpTime #dtmf form $d_uptime = [Management.ManagementDateTimeConverter]::ToDateTime($uptime) #datetime format $ts_uptime = ([DateTimeOffset]$d_uptime).ToUnixTimeSeconds() #timestamp format +$systeminfo_data = systeminfo /FO CSV | ConvertFrom-Csv +$memory_info = @{ + 'total' = $systeminfo_data.'Total Physical Memory' + 'free' = $systeminfo_data.'Available Physical Memory' + 'virtual_max' = $systeminfo_data.'Virtual Memory: Max Size' + 'virtual_free' = $systeminfo_data.'Virtual Memory: Available' +} + $test_param = @{ 'host'= $my_host_name 'ip'= $my_ip @@ -111,8 +119,8 @@ $test_param = @{ 'pwsh_path'= $pwsh_path 'drive' = @() 'current_dir' = $current_dir.replace('\','/') - 'uptime' = $ts_uptime -} + 'uptime' = $ts_uptime + 'mem_info' = $memory_info} Get-PSDrive -PSProvider FileSystem | Select Name, Used, Free | ForEach-Object -Process {$test_param.drive += $_} diff --git a/agent/CMakeLists.txt b/agent/CMakeLists.txt index c690932e2a..6938b90f50 100644 --- a/agent/CMakeLists.txt +++ b/agent/CMakeLists.txt @@ -128,6 +128,7 @@ set( SRC_WINDOWS ${SRC_DIR}/config_win.cc ${NATIVE_SRC}/check_uptime.cc ${NATIVE_SRC}/check_drive_size.cc + ${NATIVE_SRC}/check_memory.cc ) set( SRC_LINUX diff --git a/agent/inc/com/centreon/agent/check.hh b/agent/inc/com/centreon/agent/check.hh index 132009e161..4c970fb15d 100644 --- a/agent/inc/com/centreon/agent/check.hh +++ b/agent/inc/com/centreon/agent/check.hh @@ -149,6 +149,8 @@ class check : public std::enable_shared_from_this { public: using pointer = std::shared_ptr; + static const std::array status_label; + check(const std::shared_ptr& io_context, const std::shared_ptr& logger, time_point first_start_expected, diff --git a/agent/inc/com/centreon/agent/native_check_memory_base.hh b/agent/inc/com/centreon/agent/native_check_memory_base.hh new file mode 100644 index 0000000000..c2610b0476 --- /dev/null +++ b/agent/inc/com/centreon/agent/native_check_memory_base.hh @@ -0,0 +1,160 @@ +/** + * Copyright 2024 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 CENTREON_AGENT_NATIVE_CHECK_MEMORY_BASE_HH +#define CENTREON_AGENT_NATIVE_CHECK_MEMORY_BASE_HH + +#include "check.hh" + +namespace com::centreon::agent { + +namespace check_memory_detail { + +/** + * @brief we store the result of a measure in this struct + * + * @tparam nb_metric + */ +template +class memory_info { + protected: + std::array _metrics; + + public: + virtual ~memory_info() = default; + + uint64_t get_metric(unsigned data_index) const { + return _metrics[data_index]; + } + + double get_proportional_value(unsigned data_index, + unsigned total_data_index) const { + const uint64_t& total = _metrics[total_data_index]; + if (!total) { + return 0.0; + } + return (static_cast(_metrics[data_index]) / total); + } + + virtual void dump_to_output(std::string* output, unsigned flags) const = 0; +}; + +/** + * @brief this class compare a measure with threshold and returns a plugins + * status + * + * @tparam nb_metric + */ +template +class mem_to_status { + e_status _status; + unsigned _data_index; + double _threshold; + unsigned _total_data_index; + bool _percent; + bool _free_threshold; + + public: + mem_to_status(e_status status, + unsigned data_index, + double threshold, + unsigned total_data_index, + bool _percent, + bool free_threshold); + + unsigned get_data_index() const { return _data_index; } + unsigned get_total_data_index() const { return _total_data_index; } + e_status get_status() const { return _status; } + double get_threshold() const { return _threshold; } + + void compute_status(const memory_info& to_test, + e_status* status) const; +}; + +/** + * @brief this struct will be used to create metrics + * + */ +struct metric_definition { + std::string_view name; + unsigned data_index; + unsigned total_data_index; + bool percent; +}; + +/** + * @brief this must be defined and filled in final OS implementation + * + */ +extern const std::vector metric_definitions; + +} // namespace check_memory_detail + +/** + * @brief native check base (to inherit) + * + * @tparam nb_metric + */ +template +class check_memory_base : public check { + protected: + /** + * @brief key used to store mem_to_status + * @tparam 1 index (phys, virtual..) + * @tparam 2 total index (phys, virtual..) + * @tparam 3 e_status warning or critical + * + */ + using mem_to_status_key = std::tuple; + + boost::container::flat_map> + _mem_to_status; + + unsigned _output_flags = 0; + + public: + check_memory_base(const std::shared_ptr& io_context, + const std::shared_ptr& logger, + time_point first_start_expected, + duration check_interval, + const std::string& serv, + const std::string& cmd_name, + const std::string& cmd_line, + const rapidjson::Value& args, + const engine_to_agent_request_ptr& cnf, + check::completion_handler&& handler); + + std::shared_ptr> shared_from_this() { + return std::static_pointer_cast>( + check::shared_from_this()); + } + + void start_check(const duration& timeout) override; + + virtual std::shared_ptr> measure() + const = 0; + + e_status compute(const check_memory_detail::memory_info& data, + std::string* output, + std::list* perfs) const; +}; + +} // namespace com::centreon::agent + +#endif diff --git a/agent/native_windows/inc/com/centreon/agent/check_memory.hh b/agent/native_windows/inc/com/centreon/agent/check_memory.hh new file mode 100644 index 0000000000..eedc504bfa --- /dev/null +++ b/agent/native_windows/inc/com/centreon/agent/check_memory.hh @@ -0,0 +1,88 @@ +/** + * Copyright 2024 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 CENTREON_AGENT_NATIVE_CHECK_MEMORY_HH +#define CENTREON_AGENT_NATIVE_CHECK_MEMORY_HH + +#include "native_check_memory_base.hh" + +struct _PERFORMANCE_INFORMATION; + +namespace com::centreon::agent { +namespace check_memory_detail { + +enum e_metric : unsigned { + phys_total, + phys_free, + phys_used, + swap_total, + swap_free, + swap_used, + virtual_total, + virtual_free, + virtual_used, + nb_metric +}; + +/** + * @brief this class compute a measure of memory metrics and store in _metrics + * member + * + */ +class w_memory_info + : public memory_info { + public: + enum output_flags : unsigned { dump_swap = 1, dump_virtual }; + + w_memory_info(); + w_memory_info(const MEMORYSTATUSEX& mem_status, + const struct _PERFORMANCE_INFORMATION& perf_mem_status); + void init(const MEMORYSTATUSEX& mem_status, + const struct _PERFORMANCE_INFORMATION& perf_mem_status); + + void dump_to_output(std::string* output, unsigned flags) const override; +}; + +} // namespace check_memory_detail + +/** + * @brief native final check object + * + */ +class check_memory + : public check_memory_base { + public: + check_memory(const std::shared_ptr& io_context, + const std::shared_ptr& logger, + time_point first_start_expected, + duration check_interval, + const std::string& serv, + const std::string& cmd_name, + const std::string& cmd_line, + const rapidjson::Value& args, + const engine_to_agent_request_ptr& cnf, + check::completion_handler&& handler); + + std::shared_ptr> + measure() const override; +}; + +} // namespace com::centreon::agent + +#endif \ No newline at end of file diff --git a/agent/native_windows/src/check_memory.cc b/agent/native_windows/src/check_memory.cc new file mode 100644 index 0000000000..36e7862a01 --- /dev/null +++ b/agent/native_windows/src/check_memory.cc @@ -0,0 +1,489 @@ +/** + * Copyright 2024 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 +#include + +#include "agent/native_windows/inc/com/centreon/agent/check_memory.hh" +#include "check_memory.hh" +#include "native_check_memory_base.cc" + +using namespace com::centreon::agent; +using namespace com::centreon::agent::check_memory_detail; + +namespace com::centreon::agent::check_memory_detail { +/** + * @brief little struct used to format memory output (B, KB, MB or GB) + * + */ +struct byte_metric { + uint64_t byte_value; +}; +} // namespace com::centreon::agent::check_memory_detail + +namespace fmt { + +/** + * @brief formatter of byte_metric + * + * @tparam + */ +template <> +struct formatter { + constexpr auto parse(format_parse_context& ctx) + -> format_parse_context::iterator { + return ctx.begin(); + } + auto format(const com::centreon::agent::check_memory_detail::byte_metric& v, + format_context& ctx) const -> format_context::iterator { + if (v.byte_value < 1024) { + return fmt::format_to(ctx.out(), "{} B", v.byte_value); + } + if (v.byte_value < 1024 * 1024) { + return fmt::format_to( + ctx.out(), "{} KB", + static_cast(v.byte_value * 100 / 1024) / 100); + } + + if (v.byte_value < 1024 * 1024 * 1024) { + return fmt::format_to( + ctx.out(), "{} MB", + static_cast(v.byte_value * 100 / 1024 / 1024) / 100); + } + if (v.byte_value < 1024ull * 1024 * 1024 * 1024) { + return fmt::format_to( + ctx.out(), "{} GB", + static_cast(v.byte_value * 100 / 1024ull / 1024 / 1024) / + 100); + } + return fmt::format_to( + ctx.out(), "{} TB", + static_cast(v.byte_value * 100 / 1024ull / 1024 / 1024 / 1024) / + 100); + } +}; +} // namespace fmt + +namespace com::centreon::agent::check_memory_detail { + +/** + * @brief Construct a new w_memory info + * it measures memory usage and fill _metrics + * + */ +w_memory_info::w_memory_info() { + MEMORYSTATUSEX mem_status; + mem_status.dwLength = sizeof(mem_status); + if (!GlobalMemoryStatusEx(&mem_status)) { + throw std::runtime_error("fail to get memory status"); + } + + PERFORMANCE_INFORMATION perf_mem_status; + perf_mem_status.cb = sizeof(perf_mem_status); + if (!GetPerformanceInfo(&perf_mem_status, sizeof(perf_mem_status))) { + throw std::runtime_error("fail to get memory status"); + } + + init(mem_status, perf_mem_status); +} + +/** + * @brief mock for tests + * + * @param mem_status + */ +w_memory_info::w_memory_info(const MEMORYSTATUSEX& mem_status, + const PERFORMANCE_INFORMATION& perf_mem_status) { + init(mem_status, perf_mem_status); +} + +/** + * @brief fills _metrics + * + * @param mem_status + */ +void w_memory_info::init(const MEMORYSTATUSEX& mem_status, + const PERFORMANCE_INFORMATION& perf_mem_status) { + _metrics[e_metric::phys_total] = mem_status.ullTotalPhys; + _metrics[e_metric::phys_free] = mem_status.ullAvailPhys; + _metrics[e_metric::phys_used] = + mem_status.ullTotalPhys - mem_status.ullAvailPhys; + _metrics[e_metric::swap_total] = + perf_mem_status.PageSize * + (perf_mem_status.CommitLimit - perf_mem_status.PhysicalTotal); + _metrics[e_metric::swap_used] = + perf_mem_status.PageSize * + (perf_mem_status.CommitTotal + perf_mem_status.PhysicalAvailable - + perf_mem_status.PhysicalTotal); + _metrics[e_metric::swap_free] = + _metrics[e_metric::swap_total] - _metrics[e_metric::swap_used]; + _metrics[e_metric::virtual_total] = mem_status.ullTotalPageFile; + _metrics[e_metric::virtual_free] = mem_status.ullAvailPageFile; + _metrics[e_metric::virtual_used] = + _metrics[e_metric::virtual_total] - _metrics[e_metric::virtual_free]; +} + +/** + * @brief plugins output + * + * @param output + */ +void w_memory_info::dump_to_output(std::string* output, unsigned flags) const { + fmt::format_to( + std::back_inserter(*output), + "Ram total: {}, used (-buffers/cache): {} ({:.2f}%), " + "free: {} ({:.2f}%)", + byte_metric{_metrics[e_metric::phys_total]}, + byte_metric{_metrics[e_metric::phys_used]}, + get_proportional_value(e_metric::phys_used, e_metric::phys_total) * 100, + byte_metric{_metrics[e_metric::phys_free]}, + get_proportional_value(e_metric::phys_free, e_metric::phys_total) * 100); + + if (flags & output_flags::dump_swap) { + fmt::format_to( + std::back_inserter(*output), + " Swap total: {}, used: {} ({:.2f}%), free: {} ({:.2f}%)", + byte_metric{_metrics[e_metric::swap_total]}, + byte_metric{_metrics[e_metric::swap_used]}, + get_proportional_value(e_metric::swap_used, e_metric::swap_total) * 100, + byte_metric{_metrics[e_metric::swap_free]}, + get_proportional_value(e_metric::swap_free, e_metric::swap_total) * + 100); + } + + if (flags & output_flags::dump_virtual) { + fmt::format_to(std::back_inserter(*output), + " Virtual total: {}, used: {} ({:.2f}%), free: {} ({:.2f}%)", + byte_metric{_metrics[e_metric::virtual_total]}, + byte_metric{_metrics[e_metric::virtual_used]}, + get_proportional_value(e_metric::virtual_used, + e_metric::virtual_total) * + 100, + byte_metric{_metrics[e_metric::virtual_free]}, + get_proportional_value(e_metric::virtual_free, + e_metric::virtual_total) * + 100); + } +} + +/** + * @brief metric defines + * + */ +const std::vector metric_definitions = { + {"memory.usage.bytes", e_metric::phys_used, e_metric::phys_total, false}, + {"memory.free.bytes", e_metric::phys_free, e_metric::phys_total, false}, + {"memory.usage.percentage", e_metric::phys_used, e_metric::phys_total, + true}, + + {"swap.usage.bytes", e_metric::swap_used, e_metric::swap_total, false}, + {"swap.free.bytes", e_metric::swap_free, e_metric::swap_total, false}, + {"swap.usage.percentage", e_metric::swap_used, e_metric::swap_total, true}, + + {"virtual-memory.usage.bytes", e_metric::virtual_used, + e_metric::virtual_total, false}, + {"virtual-memory.free.bytes", e_metric::virtual_free, + e_metric::virtual_total, false}, + {"virtual-memory.usage.percentage", e_metric::virtual_used, + e_metric::virtual_total, true}, +}; + +} // namespace com::centreon::agent::check_memory_detail + +using windows_mem_to_status = mem_to_status; + +using mem_to_status_constructor = + std::function; + +/** + * @brief status threshold defines + * + */ +static const absl::flat_hash_map + _label_to_mem_to_status = { + // phys + {"critical-usage", + [](double threshold) { + return windows_mem_to_status(e_status::critical, e_metric::phys_used, + threshold, e_metric::phys_total, false, + false); + }}, + {"warning-usage", + [](double threshold) { + return windows_mem_to_status(e_status::warning, e_metric::phys_used, + threshold, e_metric::phys_total, false, + false); + }}, + {"critical-usage-free", + [](double threshold) { + return windows_mem_to_status(e_status::critical, e_metric::phys_free, + threshold, e_metric::phys_total, false, + true); + }}, + {"warning-usage-free", + [](double threshold) { + return windows_mem_to_status(e_status::warning, e_metric::phys_free, + threshold, e_metric::phys_total, false, + true); + }}, + {"critical-usage-prct", + [](double threshold) { + return windows_mem_to_status(e_status::critical, e_metric::phys_used, + threshold / 100, e_metric::phys_total, + true, false); + }}, + {"warning-usage-prct", + [](double threshold) { + return windows_mem_to_status(e_status::warning, e_metric::phys_used, + threshold / 100, e_metric::phys_total, + true, false); + }}, + {"critical-usage-free-prct", + [](double threshold) { + return windows_mem_to_status(e_status::critical, e_metric::phys_free, + threshold / 100, e_metric::phys_total, + true, true); + }}, + {"warning-usage-free-prct", + [](double threshold) { + return windows_mem_to_status(e_status::warning, e_metric::phys_free, + threshold / 100, e_metric::phys_total, + true, true); + }}, + // swap + {"critical-swap", + [](double threshold) { + return windows_mem_to_status(e_status::critical, e_metric::swap_used, + threshold, e_metric::swap_total, false, + false); + }}, + {"warning-swap", + [](double threshold) { + return windows_mem_to_status(e_status::warning, e_metric::swap_used, + threshold, e_metric::swap_total, false, + false); + }}, + {"critical-swap-free", + [](double threshold) { + return windows_mem_to_status(e_status::critical, e_metric::swap_free, + threshold, e_metric::swap_total, false, + true); + }}, + {"warning-swap-free", + [](double threshold) { + return windows_mem_to_status(e_status::warning, e_metric::swap_free, + threshold, e_metric::swap_total, false, + true); + }}, + {"critical-swap-prct", + [](double threshold) { + return windows_mem_to_status(e_status::critical, e_metric::swap_used, + threshold / 100, e_metric::swap_total, + true, false); + }}, + {"warning-swap-prct", + [](double threshold) { + return windows_mem_to_status(e_status::warning, e_metric::swap_used, + threshold / 100, e_metric::swap_total, + true, false); + }}, + {"critical-swap-free-prct", + [](double threshold) { + return windows_mem_to_status(e_status::critical, e_metric::swap_free, + threshold / 100, e_metric::swap_total, + true, true); + }}, + {"warning-swap-free-prct", + [](double threshold) { + return windows_mem_to_status(e_status::warning, e_metric::swap_free, + threshold / 100, e_metric::swap_total, + true, true); + }}, + // virtual memory + {"critical-virtual", + [](double threshold) { + return windows_mem_to_status(e_status::critical, + e_metric::virtual_used, threshold, + e_metric::virtual_total, false, false); + }}, + {"warning-virtual", + [](double threshold) { + return windows_mem_to_status(e_status::warning, + e_metric::virtual_used, threshold, + e_metric::virtual_total, false, false); + }}, + {"critical-virtual-free", + [](double threshold) { + return windows_mem_to_status(e_status::critical, + e_metric::virtual_free, threshold, + e_metric::virtual_total, false, true); + }}, + {"warning-virtual-free", + [](double threshold) { + return windows_mem_to_status(e_status::warning, + e_metric::virtual_free, threshold, + e_metric::virtual_total, false, true); + }}, + {"critical-virtual-prct", + [](double threshold) { + return windows_mem_to_status(e_status::critical, + e_metric::virtual_used, threshold / 100, + e_metric::virtual_total, true, false); + }}, + {"warning-virtual-prct", + [](double threshold) { + return windows_mem_to_status(e_status::warning, + e_metric::virtual_used, threshold / 100, + e_metric::virtual_total, true, false); + }}, + {"critical-virtual-free-prct", + [](double threshold) { + return windows_mem_to_status(e_status::critical, + e_metric::virtual_free, threshold / 100, + e_metric::virtual_total, true, true); + }}, + {"warning-virtual-free-prct", + [](double threshold) { + return windows_mem_to_status(e_status::warning, + e_metric::virtual_free, threshold / 100, + e_metric::virtual_total, true, true); + }} + +}; + +/** + * @brief Construct a new check memory::check memory object + * + * @param io_context + * @param logger + * @param first_start_expected + * @param check_interval + * @param serv + * @param cmd_name + * @param cmd_line + * @param args + * @param cnf + * @param handler + */ +check_memory::check_memory(const std::shared_ptr& io_context, + const std::shared_ptr& logger, + time_point first_start_expected, + duration check_interval, + const std::string& serv, + const std::string& cmd_name, + const std::string& cmd_line, + const rapidjson::Value& args, + const engine_to_agent_request_ptr& cnf, + check::completion_handler&& handler) + : check_memory_base(io_context, + logger, + first_start_expected, + check_interval, + serv, + cmd_name, + cmd_line, + args, + cnf, + std::move(handler)) { + if (args.IsObject()) { + for (auto member_iter = args.MemberBegin(); member_iter != args.MemberEnd(); + ++member_iter) { + std::string key = absl::AsciiStrToLower(member_iter->name.GetString()); + if (key == "swap") { + const rapidjson::Value& val = member_iter->value; + if (val.IsBool()) { + if (val.GetBool()) { + _output_flags |= w_memory_info::output_flags::dump_swap; + } + } else { + SPDLOG_LOGGER_ERROR(logger, "command: {}, bad value for parameter {}", + cmd_name, member_iter->name); + } + continue; + } + if (key == "virtual") { + const rapidjson::Value& val = member_iter->value; + if (val.IsBool()) { + if (val.GetBool()) { + _output_flags |= w_memory_info::output_flags::dump_virtual; + } + } else { + SPDLOG_LOGGER_ERROR(logger, "command: {}, bad value for parameter {}", + cmd_name, member_iter->name); + } + continue; + } + + auto mem_to_status_search = _label_to_mem_to_status.find(key); + if (mem_to_status_search != _label_to_mem_to_status.end()) { + const rapidjson::Value& val = member_iter->value; + if (val.IsFloat() || val.IsInt() || val.IsUint() || val.IsInt64() || + val.IsUint64()) { + windows_mem_to_status mem_checker = + mem_to_status_search->second(member_iter->value.GetDouble()); + _mem_to_status.emplace( + std::make_tuple(mem_checker.get_data_index(), + mem_checker.get_total_data_index(), + mem_checker.get_status()), + mem_checker); + } else if (val.IsString()) { + auto to_conv = val.GetString(); + double dval; + if (absl::SimpleAtod(to_conv, &dval)) { + windows_mem_to_status mem_checker = + mem_to_status_search->second(dval); + _mem_to_status.emplace( + std::make_tuple(mem_checker.get_data_index(), + mem_checker.get_total_data_index(), + mem_checker.get_status()), + mem_checker); + } else { + SPDLOG_LOGGER_ERROR( + logger, + "command: {}, value is not a number for parameter {}: {}", + cmd_name, member_iter->name, val); + } + + } else { + SPDLOG_LOGGER_ERROR(logger, + "command: {}, bad value for parameter {}: {}", + cmd_name, member_iter->name, val); + } + } else { + SPDLOG_LOGGER_ERROR(logger, "command: {}, unknown parameter {}", + cmd_name, member_iter->name); + } + } + } +} + +/** + * @brief create a w_memory_info + * + * @return std::shared_ptr< + * check_memory_detail::memory_info> + */ +std::shared_ptr< + check_memory_detail::memory_info> +check_memory::measure() const { + return std::make_shared(); +} + +namespace com::centreon::agent { +template class check_memory_base; +} diff --git a/agent/src/check.cc b/agent/src/check.cc index a150217d57..e79cf6a533 100644 --- a/agent/src/check.cc +++ b/agent/src/check.cc @@ -20,6 +20,9 @@ using namespace com::centreon::agent; +const std::array check::status_label = { + "OK: ", "WARNING: ", "CRITICAL: ", "UNKNOWN: "}; + /** * @brief Construct a new check::check object * diff --git a/agent/src/native_check_cpu_base.cc b/agent/src/native_check_cpu_base.cc index 984e13d47a..f86b0a8ae6 100644 --- a/agent/src/native_check_cpu_base.cc +++ b/agent/src/native_check_cpu_base.cc @@ -294,9 +294,6 @@ void native_check_cpu::start_check(const duration& timeout) { } } -constexpr std::array _sz_status = { - "OK: ", "WARNING: ", "CRITICAL: ", "UNKNOWN: "}; - /** * @brief called at measure timer expiration * Then we take a new snapshot of /proc/stat, compute difference with @@ -379,7 +376,7 @@ e_status native_check_cpu::_compute( } else { output->push_back(' '); } - *output += _sz_status[cpu_status.second]; + *output += status_label[cpu_status.second]; delta[cpu_status.first].dump(cpu_status.first, summary_labels, output); } } diff --git a/agent/src/native_check_memory_base.cc b/agent/src/native_check_memory_base.cc new file mode 100644 index 0000000000..3d8a8716b8 --- /dev/null +++ b/agent/src/native_check_memory_base.cc @@ -0,0 +1,203 @@ +/** + * Copyright 2024 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 "native_check_memory_base.hh" +#include "com/centreon/common/rapidjson_helper.hh" + +using namespace com::centreon::agent; +using namespace com::centreon::agent::check_memory_detail; + +/** + * @brief construct a memory_info to status converter + * + * @tparam nb_metric + * @param status e_warning or e_critical + * @param data_index index of the data to compare + * @param threshold + * @param total_data_index index of the total data in order to do a percent + * compare + * @param free_threshold if true, status is set if value < threshold + */ +template +mem_to_status::mem_to_status(e_status status, + unsigned data_index, + double threshold, + unsigned total_data_index, + bool percent, + bool free_threshold) + : _status(status), + _data_index(data_index), + _threshold(threshold), + _total_data_index(total_data_index), + _percent(percent), + _free_threshold(free_threshold) {} + +template +void mem_to_status::compute_status( + const memory_info& to_test, + e_status* status) const { + if (_status <= *status) { + return; + } + double value = + _percent ? to_test.get_proportional_value(_data_index, _total_data_index) + : to_test.get_metric(_data_index); + if (_free_threshold) { + if (value < _threshold) { + *status = _status; + } + } else { + if (value > _threshold) { + *status = _status; + } + } +} + +/** + * @brief Construct a new check check_memory_base + * + * @param io_context + * @param logger + * @param first_start_expected start expected + * @param check_interval check interval between two checks (not only this but + * also others) + * @param serv service + * @param cmd_name + * @param cmd_line + * @param args native plugin arguments + * @param cnf engine configuration received object + * @param handler called at measure completion + */ +template +check_memory_base::check_memory_base( + const std::shared_ptr& io_context, + const std::shared_ptr& logger, + time_point first_start_expected, + duration check_interval, + const std::string& serv, + const std::string& cmd_name, + const std::string& cmd_line, + const rapidjson::Value& args, + const engine_to_agent_request_ptr& cnf, + check::completion_handler&& handler) + : check(io_context, + logger, + first_start_expected, + check_interval, + serv, + cmd_name, + cmd_line, + cnf, + std::move(handler)) {} + +/** + * @brief start a measure + * + * @param timeout + */ +template +void check_memory_base::start_check(const duration& timeout) { + if (!check::_start_check(timeout)) { + return; + } + + try { + std::shared_ptr> mem_metrics = + measure(); + + _io_context->post([me = shared_from_this(), + start_check_index = _get_running_check_index(), + metrics = mem_metrics]() mutable { + std::string output; + output.reserve(1024); + std::list perfs; + e_status status = me->compute(*metrics, &output, &perfs); + me->on_completion(start_check_index, status, perfs, {output}); + }); + + } catch (const std::exception& e) { + SPDLOG_LOGGER_ERROR(_logger, "fail to get memory info: {}", e.what()); + _io_context->post([me = shared_from_this(), + start_check_index = _get_running_check_index(), + err = e.what()] { + me->on_completion(start_check_index, e_status::unknown, {}, {err}); + }); + } +} + +/** + * @brief compute status, output and metrics from a measure + * + * @tparam nb_metric + * @param data memory measure + * @param output plugins output + * @param perfs perfdatas + * @return e_status plugins status output + */ +template +e_status check_memory_base::compute( + const check_memory_detail::memory_info& data, + std::string* output, + std::list* perfs) const { + e_status status = e_status::ok; + + for (const auto& mem_status : _mem_to_status) { + mem_status.second.compute_status(data, &status); + } + + *output = status_label[status]; + data.dump_to_output(output, _output_flags); + + for (const auto& metric : metric_definitions) { + common::perfdata& to_add = perfs->emplace_back(); + to_add.name(metric.name); + if (metric.percent) { + to_add.unit("%"); + to_add.min(0); + to_add.max(100); + to_add.value(data.get_proportional_value(metric.data_index, + metric.total_data_index) * + 100); + } else { + to_add.unit("B"); + to_add.min(0); + to_add.max(data.get_metric(metric.total_data_index)); + to_add.value(data.get_metric(metric.data_index)); + } + // we search mem_to_status to get warning and critical thresholds + // warning + auto mem_to_status_search = _mem_to_status.find(std::make_tuple( + metric.data_index, metric.total_data_index, e_status::warning)); + if (mem_to_status_search != _mem_to_status.end()) { + to_add.warning_low(0); + to_add.warning(metric.percent + ? 100 * mem_to_status_search->second.get_threshold() + : mem_to_status_search->second.get_threshold()); + } + // critical + mem_to_status_search = _mem_to_status.find(std::make_tuple( + metric.data_index, metric.total_data_index, e_status::critical)); + if (mem_to_status_search != _mem_to_status.end()) { + to_add.critical_low(0); + to_add.critical(metric.percent + ? 100 * mem_to_status_search->second.get_threshold() + : mem_to_status_search->second.get_threshold()); + } + } + return status; +} \ No newline at end of file diff --git a/agent/src/scheduler.cc b/agent/src/scheduler.cc index 6111cdd359..d35d24ab70 100644 --- a/agent/src/scheduler.cc +++ b/agent/src/scheduler.cc @@ -19,6 +19,7 @@ #include "scheduler.hh" #include "check_cpu.hh" #ifdef _WINDOWS +#include "check_memory.hh" #include "check_uptime.hh" #endif #include "check_exec.hh" @@ -570,6 +571,10 @@ std::shared_ptr scheduler::default_check_builder( return std::make_shared( io_context, logger, first_start_expected, check_interval, service, cmd_name, cmd_line, *args, conf, std::move(handler)); + } else if (check_type == "memory"sv) { + return std::make_shared( + io_context, logger, first_start_expected, check_interval, service, + cmd_name, cmd_line, *args, conf, std::move(handler)); #endif } else { throw exceptions::msg_fmt("command {}, unknown native check:{}", cmd_name, diff --git a/agent/test/CMakeLists.txt b/agent/test/CMakeLists.txt index 0bdeed186f..8afe26f26f 100644 --- a/agent/test/CMakeLists.txt +++ b/agent/test/CMakeLists.txt @@ -27,7 +27,7 @@ set( SRC_COMMON if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(SRC ${SRC_COMMON} config_test.cc check_linux_cpu_test.cc) else() - set(SRC ${SRC_COMMON} check_windows_cpu_test.cc check_uptime_test.cc) + set(SRC ${SRC_COMMON} check_windows_cpu_test.cc check_windows_memory_test.cc check_uptime_test.cc) endif() diff --git a/agent/test/check_windows_memory_test.cc b/agent/test/check_windows_memory_test.cc new file mode 100644 index 0000000000..3403432d71 --- /dev/null +++ b/agent/test/check_windows_memory_test.cc @@ -0,0 +1,262 @@ +/** + * Copyright 2024 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 + +#include +#include + +#include "check.hh" +#include "com/centreon/common/perfdata.hh" +#include "com/centreon/common/rapidjson_helper.hh" + +#include "check_memory.hh" + +extern std::shared_ptr g_io_context; + +using namespace com::centreon::agent; +using namespace com::centreon::agent::check_memory_detail; + +using namespace std::string_literals; + +class test_check : public check_memory { + public: + static MEMORYSTATUSEX mock; + static PERFORMANCE_INFORMATION perf_mock; + + test_check(const rapidjson::Value& args) + : check_memory( + g_io_context, + spdlog::default_logger(), + {}, + {}, + "serv"s, + "cmd_name"s, + "cmd_line"s, + args, + nullptr, + [](const std::shared_ptr& caller, + int status, + const std::list& perfdata, + const std::list& outputs) {}) {} + + std::shared_ptr> + measure() const override { + return std::make_shared(mock, + perf_mock); + } +}; + +MEMORYSTATUSEX test_check::mock = { + 0, + 0, + 16ull * 1024 * 1024 * 1024, // ullTotalPhys + 7ull * 1024 * 1024, // ullAvailPhys + 24ull * 1024 * 1024 * 1024, // ullTotalPageFile + 6ull * 1024 * 1024 * 1024, // ullAvailPageFile + 100ull * 1024 * 1024 * 1024, // ullTotalVirtual + 40ull * 1024 * 1024 * 1024}; // ullAvailVirtual + +PERFORMANCE_INFORMATION test_check::perf_mock = { + 0, // cb + 5 * 1024 * 1024, // CommitTotal + 15 * 1024 * 1024, // CommitLimit + 0, // CommitPeak + 4194304, // PhysicalTotal + 1792, // PhysicalAvailable + 0, // SystemCache + 0, // KernelTotal + 0, // KernelPaged + 0, // KernelNonpaged + 4096, // PageSize + 0, // HandleCount + 0, // ProcessCount + 0, // ThreadCount +}; + +const uint64_t _total_phys = test_check::mock.ullTotalPhys; +const uint64_t _available_phys = test_check::mock.ullAvailPhys; +const uint64_t _total_swap = + (test_check::perf_mock.CommitLimit - test_check::perf_mock.PhysicalTotal) * + test_check::perf_mock.PageSize; +const uint64_t _used_swap = (test_check::perf_mock.CommitTotal + + test_check::perf_mock.PhysicalAvailable - + test_check::perf_mock.PhysicalTotal) * + test_check::perf_mock.PageSize; + +const uint64_t _total_virtual = test_check::mock.ullTotalPageFile; +const uint64_t _available_virtual = test_check::mock.ullAvailPageFile; + +static void test_perfs(std::list perfs) { + ASSERT_EQ(perfs.size(), 9); + for (const auto& perf : perfs) { + ASSERT_EQ(perf.min(), 0); + if (perf.name() == "memory.usage.bytes") { + ASSERT_EQ(perf.value(), _total_phys - _available_phys); + ASSERT_EQ(perf.max(), _total_phys); + ASSERT_EQ(perf.unit(), "B"); + } else if (perf.name() == "memory.free.bytes") { + ASSERT_EQ(perf.value(), _available_phys); + ASSERT_EQ(perf.max(), _total_phys); + ASSERT_EQ(perf.unit(), "B"); + } else if (perf.name() == "memory.usage.percentage") { + ASSERT_NEAR(perf.value(), + (_total_phys - _available_phys) * 100.0 / _total_phys, 0.01); + ASSERT_EQ(perf.max(), 100); + ASSERT_EQ(perf.unit(), "%"); + } else if (perf.name() == "swap.free.bytes") { + ASSERT_EQ(perf.max(), _total_swap); + ASSERT_EQ(perf.value(), _total_swap - _used_swap); + ASSERT_EQ(perf.unit(), "B"); + } else if (perf.name() == "swap.usage.bytes") { + ASSERT_EQ(perf.max(), _total_swap); + ASSERT_EQ(perf.value(), _used_swap); + ASSERT_EQ(perf.unit(), "B"); + } else if (perf.name() == "swap.usage.percentage") { + ASSERT_NEAR(perf.value(), _used_swap * 100.0 / _total_swap, 0.01); + ASSERT_EQ(perf.max(), 100); + ASSERT_EQ(perf.unit(), "%"); + } else if (perf.name() == "virtual-memory.usage.bytes") { + ASSERT_EQ(perf.max(), _total_virtual); + ASSERT_EQ(perf.value(), (_total_virtual - _available_virtual)); + ASSERT_EQ(perf.unit(), "B"); + } else if (perf.name() == "virtual-memory.free.bytes") { + ASSERT_EQ(perf.max(), _total_virtual); + ASSERT_EQ(perf.value(), _available_virtual); + ASSERT_EQ(perf.unit(), "B"); + } else if (perf.name() == "virtual-memory.usage.percentage") { + ASSERT_EQ(perf.value(), + (_total_virtual - _available_virtual) * 100.0 / _total_virtual); + ASSERT_EQ(perf.max(), 100); + ASSERT_EQ(perf.unit(), "%"); + } else { + FAIL() << "unexpected perfdata name:" << perf.name(); + } + } +} + +TEST(native_check_memory_windows, output_no_threshold) { + using namespace com::centreon::common::literals; + rapidjson::Document check_args = R"({})"_json; + test_check to_check(check_args); + std::string output; + std::list perfs; + + com::centreon::agent::e_status status = + to_check.compute(*to_check.measure(), &output, &perfs); + + ASSERT_EQ(output, + "OK: Ram total: 16 GB, used (-buffers/cache): 15.99 GB (99.96%), " + "free: 7 MB (0.04%)"); + test_perfs(perfs); +} + +TEST(native_check_memory_windows, output_no_threshold2) { + using namespace com::centreon::common::literals; + rapidjson::Document check_args = R"({ "swap": true})"_json; + test_check to_check(check_args); + std::string output; + std::list perfs; + + com::centreon::agent::e_status status = + to_check.compute(*to_check.measure(), &output, &perfs); + + ASSERT_EQ(output, + "OK: Ram total: 16 GB, used (-buffers/cache): 15.99 GB (99.96%), " + "free: 7 MB (0.04%) " + "Swap total: 44 GB, used: 4 GB (9.11%), free: 39.99 GB (90.89%)"); + test_perfs(perfs); +} + +TEST(native_check_memory_windows, output_no_threshold3) { + using namespace com::centreon::common::literals; + rapidjson::Document check_args = R"({ "swap": true, "virtual": true})"_json; + test_check to_check(check_args); + std::string output; + std::list perfs; + + com::centreon::agent::e_status status = + to_check.compute(*to_check.measure(), &output, &perfs); + + ASSERT_EQ(output, + "OK: Ram total: 16 GB, used (-buffers/cache): 15.99 GB (99.96%), " + "free: 7 MB (0.04%) " + "Swap total: 44 GB, used: 4 GB (9.11%), free: 39.99 GB (90.89%) " + "Virtual total: 24 GB, used: 18 GB (75.00%), free: 6 GB (25.00%)"); + test_perfs(perfs); +} + +TEST(native_check_memory_windows, output_threshold) { + using namespace com::centreon::common::literals; + rapidjson::Document check_args = + R"({ "warning-usage-free": "8388609", "critical-usage-prct": "99.99", "warning-virtual": "20000000000", "critical-virtual": 50000000000 })"_json; + test_check to_check(check_args); + std::string output; + std::list perfs; + + com::centreon::agent::e_status status = + to_check.compute(*to_check.measure(), &output, &perfs); + + ASSERT_EQ( + output, + "WARNING: Ram total: 16 GB, used (-buffers/cache): 15.99 GB (99.96%), " + "free: 7 MB (0.04%)"); + test_perfs(perfs); + for (const auto& perf : perfs) { + if (perf.name() == "memory.free.bytes") { + ASSERT_EQ(perf.warning_low(), 0); + ASSERT_EQ(perf.warning(), 8388609); + } else if (perf.name() == "memory.usage.percentage") { + ASSERT_EQ(perf.critical_low(), 0); + ASSERT_NEAR(perf.critical(), 99.99, 0.01); + } else if (perf.name() == "virtual-memory.usage.bytes") { + ASSERT_EQ(perf.warning_low(), 0); + ASSERT_EQ(perf.warning(), 20000000000); + ASSERT_EQ(perf.critical_low(), 0); + ASSERT_EQ(perf.critical(), 50000000000); + } + } +} + +TEST(native_check_memory_windows, output_threshold_2) { + using namespace com::centreon::common::literals; + rapidjson::Document check_args = + R"({ "warning-usage-prct": "1", "critical-usage-prct": "99.5" })"_json; + test_check to_check(check_args); + std::string output; + std::list perfs; + + com::centreon::agent::e_status status = + to_check.compute(*to_check.measure(), &output, &perfs); + + ASSERT_EQ( + output, + "CRITICAL: Ram total: 16 GB, used (-buffers/cache): 15.99 GB (99.96%), " + "free: 7 MB (0.04%)"); + test_perfs(perfs); + for (const auto& perf : perfs) { + if (perf.name() == "memory.usage.percentage") { + ASSERT_EQ(perf.warning_low(), 0); + ASSERT_NEAR(perf.warning(), 1, 0.01); + } else if (perf.name() == "memory.usage.percentage") { + ASSERT_EQ(perf.critical_low(), 0); + ASSERT_NEAR(perf.critical(), 99.5, 0.01); + } + } +} diff --git a/tests/broker-engine/cma.robot b/tests/broker-engine/cma.robot index 879ab08736..880e64ece1 100644 --- a/tests/broker-engine/cma.robot +++ b/tests/broker-engine/cma.robot @@ -56,9 +56,7 @@ BEOTEL_CENTREON_AGENT_CHECK_HOST Ctn Start Agent # Let's wait for the otel server start - ${content} Create List unencrypted server listening on 0.0.0.0:4317 - ${result} Ctn Find In Log With Timeout ${engineLog0} ${start} ${content} 10 - Should Be True ${result} "unencrypted server listening on 0.0.0.0:4317" should be available. + Ctn Wait For Otel Server To Be Ready ${start} Sleep 1s ${result} Ctn Check Host Output Resource Status With Timeout host_1 60 ${start_int} 0 HARD OK - 127.0.0.1 @@ -122,9 +120,7 @@ BEOTEL_CENTREON_AGENT_CHECK_SERVICE Ctn Start Agent # Let's wait for the otel server start - ${content} Create List unencrypted server listening on 0.0.0.0:4317 - ${result} Ctn Find In Log With Timeout ${engineLog0} ${start} ${content} 10 - Should Be True ${result} "unencrypted server listening on 0.0.0.0:4317" should be available. + Ctn Wait For Otel Server To Be Ready ${start} ${result} Ctn Check Service Output Resource Status With Timeout host_1 service_1 60 ${start_int} 2 HARD Test check 456 Should Be True ${result} resources table not updated @@ -381,9 +377,7 @@ BEOTEL_CENTREON_AGENT_CHECK_NATIVE_CPU Ctn Start Agent # Let's wait for the otel server start - ${content} Create List unencrypted server listening on 0.0.0.0:4317 - ${result} Ctn Find In Log With Timeout ${engineLog0} ${start} ${content} 10 - Should Be True ${result} "unencrypted server listening on 0.0.0.0:4317" should be available. + Ctn Wait For Otel Server To Be Ready ${start} ${result} Ctn Check Service Resource Status With Timeout host_1 service_1 0 120 HARD Should Be True ${result} resources table not updated @@ -450,9 +444,7 @@ BEOTEL_CENTREON_AGENT_CHECK_NATIVE_STORAGE Ctn Start Agent # Let's wait for the otel server start - ${content} Create List unencrypted server listening on 0.0.0.0:4317 - ${result} Ctn Find In Log With Timeout ${engineLog0} ${start} ${content} 10 - Should Be True ${result} "unencrypted server listening on 0.0.0.0:4317" should be available. + Ctn Wait For Otel Server To Be Ready ${start} ${result} Ctn Check Service Resource Status With Timeout host_1 service_1 0 120 HARD Should Be True ${result} resources table not updated @@ -520,9 +512,7 @@ BEOTEL_CENTREON_AGENT_CHECK_NATIVE_UPTIME Ctn Start Agent # Let's wait for the otel server start - ${content} Create List unencrypted server listening on 0.0.0.0:4317 - ${result} Ctn Find In Log With Timeout ${engineLog0} ${start} ${content} 10 - Should Be True ${result} "unencrypted server listening on 0.0.0.0:4317" should be available. + Ctn Wait For Otel Server To Be Ready ${start} ${result} Ctn Check Service Resource Status With Timeout host_1 service_1 0 120 HARD Should Be True ${result} resources table not updated @@ -550,6 +540,73 @@ BEOTEL_CENTREON_AGENT_CHECK_NATIVE_UPTIME ${result} Ctn Check Service Resource Status With Timeout host_1 service_1 2 60 ANY Should Be True ${result} resources table not updated +BEOTEL_CENTREON_AGENT_CHECK_NATIVE_MEMORY + [Documentation] agent check service with native check memory and we expect to get it in check result + [Tags] broker engine opentelemetry MON-147916 + + ${run_env} Ctn Run Env + Pass Execution If "${run_env}" != "WSL" "This test is only for WSL" + + Ctn Config Engine ${1} ${2} ${2} + Ctn Add Otl ServerModule + ... 0 + ... {"otel_server":{"host": "0.0.0.0","port": 4317},"max_length_grpc_log":0,"centreon_agent":{"check_interval":10, "export_period":15}} + Ctn Config Add Otl Connector + ... 0 + ... OTEL connector + ... opentelemetry --processor=centreon_agent --extractor=attributes --host_path=resource_metrics.resource.attributes.host.name --service_path=resource_metrics.resource.attributes.service.name + Ctn Engine Config Replace Value In Services ${0} service_1 check_command otel_check + Ctn Set Services Passive 0 service_1 + + Ctn Engine Config Add Command ${0} otel_check {"check": "memory"} OTEL connector + + Ctn Engine Config Set Value 0 log_level_checks trace + + Ctn Clear Db metrics + + Ctn Config Broker central + Ctn Config Broker module + Ctn Config Broker rrd + Ctn Config Centreon Agent + + Ctn Config BBDO3 1 + Ctn Clear Retention + + ${start} Ctn Get Round Current Date + Ctn Start Broker + Ctn Start Engine + Ctn Start Agent + + # Let's wait for the otel server start + Ctn Wait For Otel Server To Be Ready ${start} + + ${result} Ctn Check Service Resource Status With Timeout host_1 service_1 0 120 HARD + Should Be True ${result} resources table not updated + + ${expected_perfdata} Ctn Get Memory + #many process (cbd centengine under wsl) had consumed a lot of memory since tests began so we have to use a huge interval (800 Mo) + ${result} Ctn Check Service Perfdata host_1 service_1 60 800000000 ${expected_perfdata} + Should be True ${result} data_bin not updated + + + #a small threshold to make service_1 warning + Ctn Engine Config Replace Value In Services ${0} service_1 check_command otel_check2 + + Ctn Engine Config Add Command ${0} otel_check2 {"check": "memory", "args": {"warning-usage-prct" : "1"}} OTEL connector + + Ctn Reload Engine + ${result} Ctn Check Service Resource Status With Timeout host_1 service_1 1 60 ANY + Should Be True ${result} resources table not updated + + #a small threshold to make service_1 critical + Ctn Engine Config Replace Value In Services ${0} service_1 check_command otel_check3 + + Ctn Engine Config Add Command ${0} otel_check3 {"check": "memory", "args": {"critical-usage-prct" : "1"}} OTEL connector + + Ctn Reload Engine + ${result} Ctn Check Service Resource Status With Timeout host_1 service_1 2 60 ANY + Should Be True ${result} resources table not updated + *** Keywords *** diff --git a/tests/broker-engine/opentelemetry.robot b/tests/broker-engine/opentelemetry.robot index 83235f2900..6137d464c2 100644 --- a/tests/broker-engine/opentelemetry.robot +++ b/tests/broker-engine/opentelemetry.robot @@ -34,9 +34,7 @@ Test Teardown Ctn Stop Engine Broker And Save Logs # Ctn Start Engine # # Let's wait for the otel server start -# ${content} Create List unencrypted server listening on 0.0.0.0:4317 -# ${result} Find In Log With Timeout ${engineLog0} ${start} ${content} 10 -# Should Be True ${result} "unencrypted server listening on 0.0.0.0:4317" should be available. +# Ctn Wait For Otel Server To Be Ready ${start} # Sleep 1s @@ -111,9 +109,7 @@ BEOTEL_TELEGRAF_CHECK_HOST Ctn Start Engine # Let's wait for the otel server start - ${content} Create List unencrypted server listening on 0.0.0.0:4317 - ${result} Ctn Find In Log With Timeout ${engineLog0} ${start} ${content} 10 - Should Be True ${result} "unencrypted server listening on 0.0.0.0:4317" should be available. + Ctn Wait For Otel Server To Be Ready ${start} Sleep 1 @@ -183,9 +179,7 @@ BEOTEL_TELEGRAF_CHECK_SERVICE Ctn Start Engine # Let's wait for the otel server start - ${content} Create List unencrypted server listening on 0.0.0.0:4317 - ${result} Ctn Find In Log With Timeout ${engineLog0} ${start} ${content} 10 - Should Be True ${result} "unencrypted server listening on 0.0.0.0:4317" should be available. + Ctn Wait For Otel Server To Be Ready ${start} Sleep 1 diff --git a/tests/resources/Agent.py b/tests/resources/Agent.py index db31d38ea0..ea59e13f57 100644 --- a/tests/resources/Agent.py +++ b/tests/resources/Agent.py @@ -186,7 +186,7 @@ def ctn_get_drive_statistics(drive_name_format:str): return drive_dict else: return None - + def ctn_get_uptime(): """ ctn_get_uptime @@ -200,4 +200,39 @@ def ctn_get_uptime(): uptime_dict['uptime'] = time.time() - test_args["uptime"] return uptime_dict return None - \ No newline at end of file + +def ctn_get_memory(): + """ + ctn_get_memory statistics + return a dict with these elements (expected perfdata): + - memory.free.bytes + - memory.usage.bytes + - memory.usage.percentage + - swap.free.bytes + - swap.usage.bytes + - swap.usage.percentage + - virtual-memory.free.bytes + - virtual-memory.usage.bytes + - virtual-memory.usage.percentage + """ + + if environ.get("RUN_ENV","") == "WSL": + memory_dict = {'swap.free.bytes': None, 'swap.usage.bytes': None, 'swap.usage.percentage': None } + json_test_args = environ.get("JSON_TEST_PARAMS") + test_args = json.loads(json_test_args) + if test_args["mem_info"] is not None: + #values of systeminfo are given in Mb + virtual_free = int(test_args["mem_info"]["virtual_free"].replace(",", "").split()[0]) *1024 *1024 + virtual_max = int(test_args["mem_info"]["virtual_max"].replace(",", "").split()[0])*1024 *1024 + free= int(test_args["mem_info"]["free"].replace(",", "").split()[0])*1024 *1024 + total = int(test_args["mem_info"]["total"].replace(",", "").split()[0])*1024 *1024 + memory_dict['virtual-memory.free.bytes'] = virtual_free + memory_dict['virtual-memory.usage.bytes'] = virtual_max - virtual_free + memory_dict['virtual-memory.usage.percentage'] = 100 - (100.0 * virtual_free) / virtual_max + + memory_dict['memory.free.bytes'] = free + memory_dict['memory.usage.bytes'] = total - free + memory_dict['memory.usage.percentage'] = 100 - (100.0 * free) / total + return memory_dict + return None + diff --git a/tests/resources/Common.py b/tests/resources/Common.py index 0875f6ada4..87e358bba2 100644 --- a/tests/resources/Common.py +++ b/tests/resources/Common.py @@ -1956,9 +1956,10 @@ def ctn_check_service_perfdata(host: str, serv: str, timeout: int, precision: fl if metric not in expected: logger.console(f"ERROR unexpected metric: {metric}") return False - if abs(value - expected[metric]) > precision: - logger.console(f"ERROR unexpected value for {metric}: {value}") + if expected[metric] is not None and abs(value - expected[metric]) > precision: + logger.console(f"ERROR unexpected value for {metric}, expected: {expected[metric]}, found: {value}") return False return True time.sleep(1) + logger.console(f"unexpected result: {result}") return False \ No newline at end of file diff --git a/tests/resources/resources.resource b/tests/resources/resources.resource index bcf0b5de4b..009c43d99e 100644 --- a/tests/resources/resources.resource +++ b/tests/resources/resources.resource @@ -429,3 +429,12 @@ Ctn Kindly Stop Agent Fail centreon_agent not correctly stopped, result status: ${result.rc} END END + + +Ctn Wait For Otel Server To Be Ready + [Documentation] Wait for engine otel server to be ready + [Arguments] ${start} + ${content} Create List unencrypted server listening on 0.0.0.0:4317 + ${result} Ctn Find In Log With Timeout ${engineLog0} ${start} ${content} 10 + Should Be True ${result} "unencrypted server listening on 0.0.0.0:4317" should be available. +