diff options
Diffstat (limited to '')
31 files changed, 2267 insertions, 0 deletions
diff --git a/lib/methods/CMakeLists.txt b/lib/methods/CMakeLists.txt new file mode 100644 index 0000000..a7c3090 --- /dev/null +++ b/lib/methods/CMakeLists.txt @@ -0,0 +1,34 @@ +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ + +mkembedconfig_target(methods-itl.conf methods-itl.cpp) + +set(methods_SOURCES + i2-methods.hpp methods-itl.cpp + clusterchecktask.cpp clusterchecktask.hpp + clusterzonechecktask.cpp clusterzonechecktask.hpp + dummychecktask.cpp dummychecktask.hpp + exceptionchecktask.cpp exceptionchecktask.hpp + icingachecktask.cpp icingachecktask.hpp + ifwapichecktask.cpp ifwapichecktask.hpp + nullchecktask.cpp nullchecktask.hpp + nulleventtask.cpp nulleventtask.hpp + pluginchecktask.cpp pluginchecktask.hpp + plugineventtask.cpp plugineventtask.hpp + pluginnotificationtask.cpp pluginnotificationtask.hpp + randomchecktask.cpp randomchecktask.hpp + timeperiodtask.cpp timeperiodtask.hpp + sleepchecktask.cpp sleepchecktask.hpp +) + +if(ICINGA2_UNITY_BUILD) + mkunity_target(methods methods methods_SOURCES) +endif() + +add_library(methods OBJECT ${methods_SOURCES}) + +add_dependencies(methods base config icinga) + +set_target_properties ( + methods PROPERTIES + FOLDER Lib +) diff --git a/lib/methods/clusterchecktask.cpp b/lib/methods/clusterchecktask.cpp new file mode 100644 index 0000000..6ce28ca --- /dev/null +++ b/lib/methods/clusterchecktask.cpp @@ -0,0 +1,117 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "methods/clusterchecktask.hpp" +#include "remote/apilistener.hpp" +#include "remote/endpoint.hpp" +#include "icinga/cib.hpp" +#include "icinga/service.hpp" +#include "icinga/icingaapplication.hpp" +#include "icinga/checkcommand.hpp" +#include "base/application.hpp" +#include "base/objectlock.hpp" +#include "base/convert.hpp" +#include "base/utility.hpp" +#include "base/function.hpp" +#include "base/configtype.hpp" +#include <boost/algorithm/string/join.hpp> + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, ClusterCheck, &ClusterCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); + +void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) +{ + REQUIRE_NOT_NULL(checkable); + REQUIRE_NOT_NULL(cr); + + if (resolvedMacros && !useResolvedMacros) + return; + + CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); + String commandName = command->GetName(); + + ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) { + String output = "No API listener is configured for this instance."; + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + double now = Utility::GetTime(); + ProcessResult pr; + pr.PID = -1; + pr.ExecutionStart = now; + pr.ExecutionEnd = now; + pr.ExitStatus = 126; + pr.Output = output; + Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr); + } else { + cr->SetOutput(output); + cr->SetState(ServiceUnknown); + checkable->ProcessCheckResult(cr); + } + + return; + } + + std::pair<Dictionary::Ptr, Dictionary::Ptr> stats = listener->GetStatus(); + Dictionary::Ptr status = stats.first; + int numConnEndpoints = status->Get("num_conn_endpoints"); + int numNotConnEndpoints = status->Get("num_not_conn_endpoints"); + + ServiceState state; + String output = "Icinga 2 Cluster"; + + if (numNotConnEndpoints > 0) { + output += " Problem: " + Convert::ToString(numNotConnEndpoints) + " endpoints are not connected."; + output += "\n(" + FormatArray(status->Get("not_conn_endpoints")) + ")"; + + state = ServiceCritical; + } else { + output += " OK: " + Convert::ToString(numConnEndpoints) + " endpoints are connected."; + output += "\n(" + FormatArray(status->Get("conn_endpoints")) + ")"; + + state = ServiceOK; + } + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + double now = Utility::GetTime(); + ProcessResult pr; + pr.PID = -1; + pr.Output = output; + pr.ExecutionStart = now; + pr.ExecutionEnd = now; + pr.ExitStatus = state; + + Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr); + } else { + /* use feature stats perfdata */ + std::pair<Dictionary::Ptr, Array::Ptr> feature_stats = CIB::GetFeatureStats(); + cr->SetPerformanceData(feature_stats.second); + + cr->SetCommand(commandName); + cr->SetState(state); + cr->SetOutput(output); + + checkable->ProcessCheckResult(cr); + } +} + +String ClusterCheckTask::FormatArray(const Array::Ptr& arr) +{ + bool first = true; + String str; + + if (arr) { + ObjectLock olock(arr); + for (const Value& value : arr) { + if (first) + first = false; + else + str += ", "; + + str += Convert::ToString(value); + } + } + + return str; +} diff --git a/lib/methods/clusterchecktask.hpp b/lib/methods/clusterchecktask.hpp new file mode 100644 index 0000000..16ee8a5 --- /dev/null +++ b/lib/methods/clusterchecktask.hpp @@ -0,0 +1,29 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef CLUSTERCHECKTASK_H +#define CLUSTERCHECKTASK_H + +#include "icinga/service.hpp" + +namespace icinga +{ + +/** + * Cluster check type. + * + * @ingroup methods + */ +class ClusterCheckTask +{ +public: + static void ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); + +private: + ClusterCheckTask(); + static String FormatArray(const Array::Ptr& arr); +}; + +} + +#endif /* CLUSTERCHECKTASK_H */ diff --git a/lib/methods/clusterzonechecktask.cpp b/lib/methods/clusterzonechecktask.cpp new file mode 100644 index 0000000..fd52534 --- /dev/null +++ b/lib/methods/clusterzonechecktask.cpp @@ -0,0 +1,218 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "methods/clusterzonechecktask.hpp" +#include "icinga/checkcommand.hpp" +#include "icinga/macroprocessor.hpp" +#include "remote/apilistener.hpp" +#include "remote/endpoint.hpp" +#include "remote/zone.hpp" +#include "base/function.hpp" +#include "base/utility.hpp" +#include "base/perfdatavalue.hpp" + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, ClusterZoneCheck, &ClusterZoneCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); + +void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) +{ + REQUIRE_NOT_NULL(checkable); + REQUIRE_NOT_NULL(cr); + + ApiListener::Ptr listener = ApiListener::GetInstance(); + CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); + String commandName = command->GetName(); + + if (!listener) { + String output = "No API listener is configured for this instance."; + ServiceState state = ServiceUnknown; + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + double now = Utility::GetTime(); + ProcessResult pr; + pr.PID = -1; + pr.Output = output; + pr.ExecutionStart = now; + pr.ExecutionEnd = now; + pr.ExitStatus = state; + + Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr); + } else { + cr->SetCommand(commandName); + cr->SetOutput(output); + cr->SetState(state); + checkable->ProcessCheckResult(cr); + } + + return; + } + + Value raw_command = command->GetCommandLine(); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + MacroProcessor::ResolverList resolvers; + + if (MacroResolver::OverrideMacros) + resolvers.emplace_back("override", MacroResolver::OverrideMacros); + + if (service) + resolvers.emplace_back("service", service); + resolvers.emplace_back("host", host); + resolvers.emplace_back("command", command); + + String zoneName = MacroProcessor::ResolveMacros("$cluster_zone$", resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + + String missingLagWarning; + String missingLagCritical; + + double lagWarning = MacroProcessor::ResolveMacros("$cluster_lag_warning$", resolvers, checkable->GetLastCheckResult(), + &missingLagWarning, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + + double lagCritical = MacroProcessor::ResolveMacros("$cluster_lag_critical$", resolvers, checkable->GetLastCheckResult(), + &missingLagCritical, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + + if (resolvedMacros && !useResolvedMacros) + return; + + if (zoneName.IsEmpty()) { + String output = "Macro 'cluster_zone' must be set."; + ServiceState state = ServiceUnknown; + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + double now = Utility::GetTime(); + ProcessResult pr; + pr.PID = -1; + pr.Output = output; + pr.ExecutionStart = now; + pr.ExecutionEnd = now; + pr.ExitStatus = state; + + Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr); + } else { + cr->SetCommand(commandName); + cr->SetOutput(output); + cr->SetState(state); + checkable->ProcessCheckResult(cr); + } + + return; + } + + Zone::Ptr zone = Zone::GetByName(zoneName); + + if (!zone) { + String output = "Zone '" + zoneName + "' does not exist."; + ServiceState state = ServiceUnknown; + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + double now = Utility::GetTime(); + ProcessResult pr; + pr.PID = -1; + pr.Output = output; + pr.ExecutionStart = now; + pr.ExecutionEnd = now; + pr.ExitStatus = state; + + Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr); + } else { + cr->SetCommand(commandName); + cr->SetOutput(output); + cr->SetState(state); + checkable->ProcessCheckResult(cr); + } + return; + } + + bool connected = false; + double zoneLag = 0; + + double lastMessageSent = 0; + double lastMessageReceived = 0; + double messagesSentPerSecond = 0; + double messagesReceivedPerSecond = 0; + double bytesSentPerSecond = 0; + double bytesReceivedPerSecond = 0; + + { + auto endpoints (zone->GetEndpoints()); + + for (const Endpoint::Ptr& endpoint : endpoints) { + if (endpoint->GetConnected()) + connected = true; + + double eplag = ApiListener::CalculateZoneLag(endpoint); + + if (eplag > 0 && eplag > zoneLag) + zoneLag = eplag; + + if (endpoint->GetLastMessageSent() > lastMessageSent) + lastMessageSent = endpoint->GetLastMessageSent(); + + if (endpoint->GetLastMessageReceived() > lastMessageReceived) + lastMessageReceived = endpoint->GetLastMessageReceived(); + + messagesSentPerSecond += endpoint->GetMessagesSentPerSecond(); + messagesReceivedPerSecond += endpoint->GetMessagesReceivedPerSecond(); + bytesSentPerSecond += endpoint->GetBytesSentPerSecond(); + bytesReceivedPerSecond += endpoint->GetBytesReceivedPerSecond(); + } + + if (!connected && endpoints.size() == 1u && *endpoints.begin() == Endpoint::GetLocalEndpoint()) { + connected = true; + } + } + + ServiceState state; + String output; + + if (connected) { + state = ServiceOK; + output = "Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag); + + /* Check whether the thresholds have been resolved and compare them */ + if (missingLagCritical.IsEmpty() && zoneLag > lagCritical) { + state = ServiceCritical; + output = "Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag) + + " greater than critical threshold: " + Utility::FormatDuration(lagCritical); + } else if (missingLagWarning.IsEmpty() && zoneLag > lagWarning) { + state = ServiceWarning; + output = "Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag) + + " greater than warning threshold: " + Utility::FormatDuration(lagWarning); + } + } else { + state = ServiceCritical; + output = "Zone '" + zoneName + "' is not connected. Log lag: " + Utility::FormatDuration(zoneLag); + } + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + double now = Utility::GetTime(); + ProcessResult pr; + pr.PID = -1; + pr.Output = output; + pr.ExecutionStart = now; + pr.ExecutionEnd = now; + pr.ExitStatus = state; + + Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr); + } else { + cr->SetCommand(commandName); + cr->SetState(state); + cr->SetOutput(output); + cr->SetPerformanceData(new Array({ + new PerfdataValue("slave_lag", zoneLag, false, "s", lagWarning, lagCritical), + new PerfdataValue("last_messages_sent", lastMessageSent), + new PerfdataValue("last_messages_received", lastMessageReceived), + new PerfdataValue("sum_messages_sent_per_second", messagesSentPerSecond), + new PerfdataValue("sum_messages_received_per_second", messagesReceivedPerSecond), + new PerfdataValue("sum_bytes_sent_per_second", bytesSentPerSecond), + new PerfdataValue("sum_bytes_received_per_second", bytesReceivedPerSecond) + })); + + checkable->ProcessCheckResult(cr); + } +} diff --git a/lib/methods/clusterzonechecktask.hpp b/lib/methods/clusterzonechecktask.hpp new file mode 100644 index 0000000..2af442c --- /dev/null +++ b/lib/methods/clusterzonechecktask.hpp @@ -0,0 +1,28 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef CLUSTERZONECHECKTASK_H +#define CLUSTERZONECHECKTASK_H + +#include "icinga/service.hpp" + +namespace icinga +{ + +/** + * Cluster zone check type. + * + * @ingroup methods + */ +class ClusterZoneCheckTask +{ +public: + static void ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); + +private: + ClusterZoneCheckTask(); +}; + +} + +#endif /* CLUSTERZONECHECKTASK_H */ diff --git a/lib/methods/dummychecktask.cpp b/lib/methods/dummychecktask.cpp new file mode 100644 index 0000000..905a022 --- /dev/null +++ b/lib/methods/dummychecktask.cpp @@ -0,0 +1,75 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef _WIN32 +# include <stdlib.h> +#endif /* _WIN32 */ +#include "methods/dummychecktask.hpp" +#include "icinga/pluginutility.hpp" +#include "base/utility.hpp" +#include "base/perfdatavalue.hpp" +#include "base/convert.hpp" +#include "base/function.hpp" +#include "base/logger.hpp" + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, DummyCheck, &DummyCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); + +void DummyCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) +{ + REQUIRE_NOT_NULL(checkable); + REQUIRE_NOT_NULL(cr); + + CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + MacroProcessor::ResolverList resolvers; + + if (MacroResolver::OverrideMacros) + resolvers.emplace_back("override", MacroResolver::OverrideMacros); + + if (service) + resolvers.emplace_back("service", service); + resolvers.emplace_back("host", host); + resolvers.emplace_back("command", command); + + int dummyState = MacroProcessor::ResolveMacros("$dummy_state$", resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + + String dummyText = MacroProcessor::ResolveMacros("$dummy_text$", resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + + if (resolvedMacros && !useResolvedMacros) + return; + + /* Parse output and performance data. */ + std::pair<String, String> co = PluginUtility::ParseCheckOutput(dummyText); + + double now = Utility::GetTime(); + String commandName = command->GetName(); + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + ProcessResult pr; + pr.PID = -1; + pr.Output = dummyText; + pr.ExecutionStart = now; + pr.ExecutionEnd = now; + pr.ExitStatus = dummyState; + + Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr); + } else { + cr->SetOutput(co.first); + cr->SetPerformanceData(PluginUtility::SplitPerfdata(co.second)); + cr->SetState(PluginUtility::ExitStatusToState(dummyState)); + cr->SetExitStatus(dummyState); + cr->SetExecutionStart(now); + cr->SetExecutionEnd(now); + cr->SetCommand(commandName); + + checkable->ProcessCheckResult(cr); + } +} diff --git a/lib/methods/dummychecktask.hpp b/lib/methods/dummychecktask.hpp new file mode 100644 index 0000000..621bbfb --- /dev/null +++ b/lib/methods/dummychecktask.hpp @@ -0,0 +1,30 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef DUMMYCHECKTASK_H +#define DUMMYCHECKTASK_H + +#include "methods/i2-methods.hpp" +#include "icinga/service.hpp" +#include "base/dictionary.hpp" + +namespace icinga +{ + +/** + * Test class for additional check types. Implements the "dummy" check type. + * + * @ingroup methods + */ +class DummyCheckTask +{ +public: + static void ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); + +private: + DummyCheckTask(); +}; + +} + +#endif /* DUMMYCHECKTASK_H */ diff --git a/lib/methods/exceptionchecktask.cpp b/lib/methods/exceptionchecktask.cpp new file mode 100644 index 0000000..47707f2 --- /dev/null +++ b/lib/methods/exceptionchecktask.cpp @@ -0,0 +1,41 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef _WIN32 +# include <stdlib.h> +#endif /* _WIN32 */ +#include "methods/exceptionchecktask.hpp" +#include "base/utility.hpp" +#include "base/convert.hpp" +#include "base/function.hpp" +#include "base/logger.hpp" +#include "base/exception.hpp" + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, ExceptionCheck, &ExceptionCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); + +void ExceptionCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) +{ + REQUIRE_NOT_NULL(checkable); + REQUIRE_NOT_NULL(cr); + + if (resolvedMacros && !useResolvedMacros) + return; + + ScriptError scriptError = ScriptError("Test") << boost::errinfo_api_function("Test"); + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + double now = Utility::GetTime(); + ProcessResult pr; + pr.PID = -1; + pr.Output = scriptError.what(); + pr.ExecutionStart = now; + pr.ExecutionEnd = now; + pr.ExitStatus = 3; + + Checkable::ExecuteCommandProcessFinishedHandler("", pr); + } else { + BOOST_THROW_EXCEPTION(ScriptError("Test") << boost::errinfo_api_function("Test")); + } +} diff --git a/lib/methods/exceptionchecktask.hpp b/lib/methods/exceptionchecktask.hpp new file mode 100644 index 0000000..09db104 --- /dev/null +++ b/lib/methods/exceptionchecktask.hpp @@ -0,0 +1,29 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef EXCEPTIONCHECKTASK_H +#define EXCEPTIONCHECKTASK_H + +#include "icinga/service.hpp" +#include "base/dictionary.hpp" + +namespace icinga +{ + +/** + * Test class for additional check types. Implements the "exception" check type. + * + * @ingroup methods + */ +class ExceptionCheckTask +{ +public: + static void ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); + +private: + ExceptionCheckTask(); +}; + +} + +#endif /* EXCEPTIONCHECKTASK_H */ diff --git a/lib/methods/i2-methods.hpp b/lib/methods/i2-methods.hpp new file mode 100644 index 0000000..ffd8002 --- /dev/null +++ b/lib/methods/i2-methods.hpp @@ -0,0 +1,15 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef I2METHODS_H +#define I2METHODS_H + +/** + * @defgroup methods Icinga methods + * + * The methods library implements methods for various task (e.g. checks, event + * handlers, etc.). + */ + +#include "base/i2-base.hpp" + +#endif /* I2METHODS_H */ diff --git a/lib/methods/icingachecktask.cpp b/lib/methods/icingachecktask.cpp new file mode 100644 index 0000000..d3eae1f --- /dev/null +++ b/lib/methods/icingachecktask.cpp @@ -0,0 +1,209 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "methods/icingachecktask.hpp" +#include "icinga/cib.hpp" +#include "icinga/service.hpp" +#include "icinga/checkcommand.hpp" +#include "icinga/macroprocessor.hpp" +#include "icinga/clusterevents.hpp" +#include "icinga/checkable.hpp" +#include "remote/apilistener.hpp" +#include "base/application.hpp" +#include "base/objectlock.hpp" +#include "base/utility.hpp" +#include "base/perfdatavalue.hpp" +#include "base/function.hpp" +#include "base/configtype.hpp" + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, IcingaCheck, &IcingaCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); + +void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) +{ + REQUIRE_NOT_NULL(checkable); + REQUIRE_NOT_NULL(cr); + + CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + MacroProcessor::ResolverList resolvers; + + if (MacroResolver::OverrideMacros) + resolvers.emplace_back("override", MacroResolver::OverrideMacros); + + if (service) + resolvers.emplace_back("service", service); + resolvers.emplace_back("host", host); + resolvers.emplace_back("command", command); + + String missingIcingaMinVersion; + + String icingaMinVersion = MacroProcessor::ResolveMacros("$icinga_min_version$", resolvers, checkable->GetLastCheckResult(), + &missingIcingaMinVersion, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + + if (resolvedMacros && !useResolvedMacros) + return; + + double interval = Utility::GetTime() - Application::GetStartTime(); + + if (interval > 60) + interval = 60; + + /* use feature stats perfdata */ + std::pair<Dictionary::Ptr, Array::Ptr> feature_stats = CIB::GetFeatureStats(); + + Array::Ptr perfdata = feature_stats.second; + + perfdata->Add(new PerfdataValue("active_host_checks", CIB::GetActiveHostChecksStatistics(interval) / interval)); + perfdata->Add(new PerfdataValue("passive_host_checks", CIB::GetPassiveHostChecksStatistics(interval) / interval)); + perfdata->Add(new PerfdataValue("active_host_checks_1min", CIB::GetActiveHostChecksStatistics(60))); + perfdata->Add(new PerfdataValue("passive_host_checks_1min", CIB::GetPassiveHostChecksStatistics(60))); + perfdata->Add(new PerfdataValue("active_host_checks_5min", CIB::GetActiveHostChecksStatistics(60 * 5))); + perfdata->Add(new PerfdataValue("passive_host_checks_5min", CIB::GetPassiveHostChecksStatistics(60 * 5))); + perfdata->Add(new PerfdataValue("active_host_checks_15min", CIB::GetActiveHostChecksStatistics(60 * 15))); + perfdata->Add(new PerfdataValue("passive_host_checks_15min", CIB::GetPassiveHostChecksStatistics(60 * 15))); + + perfdata->Add(new PerfdataValue("active_service_checks", CIB::GetActiveServiceChecksStatistics(interval) / interval)); + perfdata->Add(new PerfdataValue("passive_service_checks", CIB::GetPassiveServiceChecksStatistics(interval) / interval)); + perfdata->Add(new PerfdataValue("active_service_checks_1min", CIB::GetActiveServiceChecksStatistics(60))); + perfdata->Add(new PerfdataValue("passive_service_checks_1min", CIB::GetPassiveServiceChecksStatistics(60))); + perfdata->Add(new PerfdataValue("active_service_checks_5min", CIB::GetActiveServiceChecksStatistics(60 * 5))); + perfdata->Add(new PerfdataValue("passive_service_checks_5min", CIB::GetPassiveServiceChecksStatistics(60 * 5))); + perfdata->Add(new PerfdataValue("active_service_checks_15min", CIB::GetActiveServiceChecksStatistics(60 * 15))); + perfdata->Add(new PerfdataValue("passive_service_checks_15min", CIB::GetPassiveServiceChecksStatistics(60 * 15))); + + perfdata->Add(new PerfdataValue("current_pending_callbacks", Application::GetTP().GetPending())); + perfdata->Add(new PerfdataValue("current_concurrent_checks", Checkable::CurrentConcurrentChecks.load())); + perfdata->Add(new PerfdataValue("remote_check_queue", ClusterEvents::GetCheckRequestQueueSize())); + + CheckableCheckStatistics scs = CIB::CalculateServiceCheckStats(); + + perfdata->Add(new PerfdataValue("min_latency", scs.min_latency)); + perfdata->Add(new PerfdataValue("max_latency", scs.max_latency)); + perfdata->Add(new PerfdataValue("avg_latency", scs.avg_latency)); + perfdata->Add(new PerfdataValue("min_execution_time", scs.min_execution_time)); + perfdata->Add(new PerfdataValue("max_execution_time", scs.max_execution_time)); + perfdata->Add(new PerfdataValue("avg_execution_time", scs.avg_execution_time)); + + ServiceStatistics ss = CIB::CalculateServiceStats(); + + perfdata->Add(new PerfdataValue("num_services_ok", ss.services_ok)); + perfdata->Add(new PerfdataValue("num_services_warning", ss.services_warning)); + perfdata->Add(new PerfdataValue("num_services_critical", ss.services_critical)); + perfdata->Add(new PerfdataValue("num_services_unknown", ss.services_unknown)); + perfdata->Add(new PerfdataValue("num_services_pending", ss.services_pending)); + perfdata->Add(new PerfdataValue("num_services_unreachable", ss.services_unreachable)); + perfdata->Add(new PerfdataValue("num_services_flapping", ss.services_flapping)); + perfdata->Add(new PerfdataValue("num_services_in_downtime", ss.services_in_downtime)); + perfdata->Add(new PerfdataValue("num_services_acknowledged", ss.services_acknowledged)); + perfdata->Add(new PerfdataValue("num_services_handled", ss.services_handled)); + perfdata->Add(new PerfdataValue("num_services_problem", ss.services_problem)); + + double uptime = Application::GetUptime(); + perfdata->Add(new PerfdataValue("uptime", uptime)); + + HostStatistics hs = CIB::CalculateHostStats(); + + perfdata->Add(new PerfdataValue("num_hosts_up", hs.hosts_up)); + perfdata->Add(new PerfdataValue("num_hosts_down", hs.hosts_down)); + perfdata->Add(new PerfdataValue("num_hosts_pending", hs.hosts_pending)); + perfdata->Add(new PerfdataValue("num_hosts_unreachable", hs.hosts_unreachable)); + perfdata->Add(new PerfdataValue("num_hosts_flapping", hs.hosts_flapping)); + perfdata->Add(new PerfdataValue("num_hosts_in_downtime", hs.hosts_in_downtime)); + perfdata->Add(new PerfdataValue("num_hosts_acknowledged", hs.hosts_acknowledged)); + perfdata->Add(new PerfdataValue("num_hosts_handled", hs.hosts_handled)); + perfdata->Add(new PerfdataValue("num_hosts_problem", hs.hosts_problem)); + + std::vector<Endpoint::Ptr> endpoints = ConfigType::GetObjectsByType<Endpoint>(); + + double lastMessageSent = 0; + double lastMessageReceived = 0; + double messagesSentPerSecond = 0; + double messagesReceivedPerSecond = 0; + double bytesSentPerSecond = 0; + double bytesReceivedPerSecond = 0; + + for (const Endpoint::Ptr& endpoint : endpoints) + { + if (endpoint->GetLastMessageSent() > lastMessageSent) + lastMessageSent = endpoint->GetLastMessageSent(); + + if (endpoint->GetLastMessageReceived() > lastMessageReceived) + lastMessageReceived = endpoint->GetLastMessageReceived(); + + messagesSentPerSecond += endpoint->GetMessagesSentPerSecond(); + messagesReceivedPerSecond += endpoint->GetMessagesReceivedPerSecond(); + bytesSentPerSecond += endpoint->GetBytesSentPerSecond(); + bytesReceivedPerSecond += endpoint->GetBytesReceivedPerSecond(); + } + + perfdata->Add(new PerfdataValue("last_messages_sent", lastMessageSent)); + perfdata->Add(new PerfdataValue("last_messages_received", lastMessageReceived)); + perfdata->Add(new PerfdataValue("sum_messages_sent_per_second", messagesSentPerSecond)); + perfdata->Add(new PerfdataValue("sum_messages_received_per_second", messagesReceivedPerSecond)); + perfdata->Add(new PerfdataValue("sum_bytes_sent_per_second", bytesSentPerSecond)); + perfdata->Add(new PerfdataValue("sum_bytes_received_per_second", bytesReceivedPerSecond)); + + cr->SetPerformanceData(perfdata); + ServiceState state = ServiceOK; + + String appVersion = Application::GetAppVersion(); + + String output = "Icinga 2 has been running for " + Utility::FormatDuration(uptime) + + ". Version: " + appVersion; + + /* Indicate a warning if the last reload failed. */ + double lastReloadFailed = Application::GetLastReloadFailed(); + + if (lastReloadFailed > 0) { + output += "; Last reload attempt failed at " + Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", lastReloadFailed); + state =ServiceWarning; + } + + /* Indicate a warning when the last synced config caused a stage validation error. */ + ApiListener::Ptr listener = ApiListener::GetInstance(); + + if (listener) { + Dictionary::Ptr validationResult = listener->GetLastFailedZonesStageValidation(); + + if (validationResult) { + output += "; Last zone sync stage validation failed at " + + Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", validationResult->Get("ts")); + + state = ServiceWarning; + } + } + + String parsedAppVersion = Utility::ParseVersion(appVersion); + + /* Return an error if the version is less than specified (optional). */ + if (missingIcingaMinVersion.IsEmpty() && !icingaMinVersion.IsEmpty() && Utility::CompareVersion(icingaMinVersion, parsedAppVersion) < 0) { + output += "; Minimum version " + icingaMinVersion + " is not installed."; + state = ServiceCritical; + } + + String commandName = command->GetName(); + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + double now = Utility::GetTime(); + ProcessResult pr; + pr.PID = -1; + pr.Output = output; + pr.ExecutionStart = now; + pr.ExecutionEnd = now; + pr.ExitStatus = state; + + Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr); + } else { + cr->SetState(state); + cr->SetOutput(output); + cr->SetCommand(commandName); + + checkable->ProcessCheckResult(cr); + } +} diff --git a/lib/methods/icingachecktask.hpp b/lib/methods/icingachecktask.hpp new file mode 100644 index 0000000..93def62 --- /dev/null +++ b/lib/methods/icingachecktask.hpp @@ -0,0 +1,29 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef ICINGACHECKTASK_H +#define ICINGACHECKTASK_H + +#include "methods/i2-methods.hpp" +#include "icinga/service.hpp" + +namespace icinga +{ + +/** + * Icinga check type. + * + * @ingroup methods + */ +class IcingaCheckTask +{ +public: + static void ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); + +private: + IcingaCheckTask(); +}; + +} + +#endif /* ICINGACHECKTASK_H */ diff --git a/lib/methods/ifwapichecktask.cpp b/lib/methods/ifwapichecktask.cpp new file mode 100644 index 0000000..8516d70 --- /dev/null +++ b/lib/methods/ifwapichecktask.cpp @@ -0,0 +1,531 @@ +/* Icinga 2 | (c) 2023 Icinga GmbH | GPLv2+ */ + +#ifndef _WIN32 +# include <stdlib.h> +#endif /* _WIN32 */ +#include "methods/ifwapichecktask.hpp" +#include "methods/pluginchecktask.hpp" +#include "icinga/checkresult-ti.hpp" +#include "icinga/icingaapplication.hpp" +#include "icinga/pluginutility.hpp" +#include "base/base64.hpp" +#include "base/defer.hpp" +#include "base/utility.hpp" +#include "base/perfdatavalue.hpp" +#include "base/convert.hpp" +#include "base/function.hpp" +#include "base/io-engine.hpp" +#include "base/json.hpp" +#include "base/logger.hpp" +#include "base/shared.hpp" +#include "base/tcpsocket.hpp" +#include "base/tlsstream.hpp" +#include "remote/apilistener.hpp" +#include "remote/url.hpp" +#include <boost/asio.hpp> +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/system/system_error.hpp> +#include <exception> +#include <set> + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, IfwApiCheck, &IfwApiCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); + +static void ReportIfwCheckResult( + const Checkable::Ptr& checkable, const Value& cmdLine, const CheckResult::Ptr& cr, + const String& output, double start, double end, int exitcode = 3, const Array::Ptr& perfdata = nullptr +) +{ + if (Checkable::ExecuteCommandProcessFinishedHandler) { + ProcessResult pr; + pr.PID = -1; + pr.Output = perfdata ? output + " |" + String(perfdata->Join(" ")) : output; + pr.ExecutionStart = start; + pr.ExecutionEnd = end; + pr.ExitStatus = exitcode; + + Checkable::ExecuteCommandProcessFinishedHandler(cmdLine, pr); + } else { + auto splittedPerfdata (perfdata); + + if (perfdata) { + splittedPerfdata = new Array(); + ObjectLock oLock (perfdata); + + for (String pv : perfdata) { + PluginUtility::SplitPerfdata(pv)->CopyTo(splittedPerfdata); + } + } + + cr->SetOutput(output); + cr->SetPerformanceData(splittedPerfdata); + cr->SetState((ServiceState)exitcode); + cr->SetExitStatus(exitcode); + cr->SetExecutionStart(start); + cr->SetExecutionEnd(end); + cr->SetCommand(cmdLine); + + checkable->ProcessCheckResult(cr); + } +} + +static void ReportIfwCheckResult( + boost::asio::yield_context yc, const Checkable::Ptr& checkable, const Value& cmdLine, + const CheckResult::Ptr& cr, const String& output, double start +) +{ + double end = Utility::GetTime(); + CpuBoundWork cbw (yc); + + ReportIfwCheckResult(checkable, cmdLine, cr, output, start, end); +} + +static const char* GetUnderstandableError(const std::exception& ex) +{ + auto se (dynamic_cast<const boost::system::system_error*>(&ex)); + + if (se && se->code() == boost::asio::error::operation_aborted) { + return "Timeout exceeded"; + } + + return ex.what(); +} + +static void DoIfwNetIo( + boost::asio::yield_context yc, const Checkable::Ptr& checkable, const Array::Ptr& cmdLine, + const CheckResult::Ptr& cr, const String& psCommand, const String& psHost, const String& san, const String& psPort, + AsioTlsStream& conn, boost::beast::http::request<boost::beast::http::string_body>& req, double start +) +{ + namespace http = boost::beast::http; + + boost::beast::flat_buffer buf; + http::response<http::string_body> resp; + + try { + Connect(conn.lowest_layer(), psHost, psPort, yc); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + yc, checkable, cmdLine, cr, + "Can't connect to IfW API on host '" + psHost + "' port '" + psPort + "': " + GetUnderstandableError(ex), + start + ); + return; + } + + auto& sslConn (conn.next_layer()); + + try { + sslConn.async_handshake(conn.next_layer().client, yc); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + yc, checkable, cmdLine, cr, + "TLS handshake with IfW API on host '" + psHost + "' (SNI: '" + san + + "') port '" + psPort + "' failed: " + GetUnderstandableError(ex), + start + ); + return; + } + + if (!sslConn.IsVerifyOK()) { + auto cert (sslConn.GetPeerCertificate()); + Value cn; + + try { + cn = GetCertificateCN(cert); + } catch (const std::exception&) { + } + + ReportIfwCheckResult( + yc, checkable, cmdLine, cr, + "Certificate validation failed for IfW API on host '" + psHost + "' (SNI: '" + san + "'; CN: " + + (cn.IsString() ? "'" + cn + "'" : "N/A") + ") port '" + psPort + "': " + sslConn.GetVerifyError(), + start + ); + return; + } + + try { + http::async_write(conn, req, yc); + conn.async_flush(yc); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + yc, checkable, cmdLine, cr, + "Can't send HTTP request to IfW API on host '" + psHost + "' port '" + psPort + "': " + GetUnderstandableError(ex), + start + ); + return; + } + + try { + http::async_read(conn, buf, resp, yc); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + yc, checkable, cmdLine, cr, + "Can't read HTTP response from IfW API on host '" + psHost + "' port '" + psPort + "': " + GetUnderstandableError(ex), + start + ); + return; + } + + double end = Utility::GetTime(); + + { + boost::system::error_code ec; + sslConn.async_shutdown(yc[ec]); + } + + CpuBoundWork cbw (yc); + Value jsonRoot; + + try { + jsonRoot = JsonDecode(resp.body()); + } catch (const std::exception& ex) { + ReportIfwCheckResult( + checkable, cmdLine, cr, + "Got bad JSON from IfW API on host '" + psHost + "' port '" + psPort + "': " + ex.what(), start, end + ); + return; + } + + if (!jsonRoot.IsObjectType<Dictionary>()) { + ReportIfwCheckResult( + checkable, cmdLine, cr, + "Got JSON, but not an object, from IfW API on host '" + + psHost + "' port '" + psPort + "': " + JsonEncode(jsonRoot), + start, end + ); + return; + } + + Value jsonBranch; + + if (!Dictionary::Ptr(jsonRoot)->Get(psCommand, &jsonBranch)) { + ReportIfwCheckResult( + checkable, cmdLine, cr, + "Missing ." + psCommand + " in JSON object from IfW API on host '" + + psHost + "' port '" + psPort + "': " + JsonEncode(jsonRoot), + start, end + ); + return; + } + + if (!jsonBranch.IsObjectType<Dictionary>()) { + ReportIfwCheckResult( + checkable, cmdLine, cr, + "." + psCommand + " in JSON from IfW API on host '" + + psHost + "' port '" + psPort + "' is not an object: " + JsonEncode(jsonBranch), + start, end + ); + return; + } + + Dictionary::Ptr result = jsonBranch; + + Value exitcode; + + if (!result->Get("exitcode", &exitcode)) { + ReportIfwCheckResult( + checkable, cmdLine, cr, + "Missing ." + psCommand + ".exitcode in JSON object from IfW API on host '" + + psHost + "' port '" + psPort + "': " + JsonEncode(result), + start, end + ); + return; + } + + static const std::set<double> exitcodes {ServiceOK, ServiceWarning, ServiceCritical, ServiceUnknown}; + static const auto exitcodeList (Array::FromSet(exitcodes)->Join(", ")); + + if (!exitcode.IsNumber() || exitcodes.find(exitcode) == exitcodes.end()) { + ReportIfwCheckResult( + checkable, cmdLine, cr, + "Got bad exitcode " + JsonEncode(exitcode) + " from IfW API on host '" + psHost + "' port '" + psPort + + "', expected one of: " + exitcodeList, + start, end + ); + return; + } + + auto perfdataVal (result->Get("perfdata")); + Array::Ptr perfdata; + + try { + perfdata = perfdataVal; + } catch (const std::exception&) { + ReportIfwCheckResult( + checkable, cmdLine, cr, + "Got bad perfdata " + JsonEncode(perfdataVal) + " from IfW API on host '" + + psHost + "' port '" + psPort + "', expected an array", + start, end + ); + return; + } + + if (perfdata) { + ObjectLock oLock (perfdata); + + for (auto& pv : perfdata) { + if (!pv.IsString()) { + ReportIfwCheckResult( + checkable, cmdLine, cr, + "Got bad perfdata value " + JsonEncode(perfdata) + " from IfW API on host '" + + psHost + "' port '" + psPort + "', expected an array of strings", + start, end + ); + return; + } + } + } + + ReportIfwCheckResult(checkable, cmdLine, cr, result->Get("checkresult"), start, end, exitcode, perfdata); +} + +void IfwApiCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) +{ + namespace asio = boost::asio; + namespace http = boost::beast::http; + using http::field; + + REQUIRE_NOT_NULL(checkable); + REQUIRE_NOT_NULL(cr); + + // We're going to just resolve macros for the actual check execution happening elsewhere + if (resolvedMacros && !useResolvedMacros) { + auto commandEndpoint (checkable->GetCommandEndpoint()); + + // There's indeed a command endpoint, obviously for the actual check execution + if (commandEndpoint) { + // But it doesn't have this function, yet ("ifw-api-check-command") + if (!(commandEndpoint->GetCapabilities() & (uint_fast64_t)ApiCapabilities::IfwApiCheckCommand)) { + // Assume "ifw-api-check-command" has been imported into a check command which can also work + // based on "plugin-check-command", delegate respectively and hope for the best + PluginCheckTask::ScriptFunc(checkable, cr, resolvedMacros, useResolvedMacros); + return; + } + } + } + + CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); + auto lcr (checkable->GetLastCheckResult()); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + MacroProcessor::ResolverList resolvers; + + if (MacroResolver::OverrideMacros) + resolvers.emplace_back("override", MacroResolver::OverrideMacros); + + if (service) + resolvers.emplace_back("service", service); + resolvers.emplace_back("host", host); + resolvers.emplace_back("command", command); + + auto resolveMacros ([&resolvers, &lcr, &resolvedMacros, useResolvedMacros](const char* macros) -> Value { + return MacroProcessor::ResolveMacros( + macros, resolvers, lcr, nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros + ); + }); + + String psCommand = resolveMacros("$ifw_api_command$"); + Dictionary::Ptr arguments = resolveMacros("$ifw_api_arguments$"); + String psHost = resolveMacros("$ifw_api_host$"); + String psPort = resolveMacros("$ifw_api_port$"); + String expectedSan = resolveMacros("$ifw_api_expected_san$"); + String cert = resolveMacros("$ifw_api_cert$"); + String key = resolveMacros("$ifw_api_key$"); + String ca = resolveMacros("$ifw_api_ca$"); + String crl = resolveMacros("$ifw_api_crl$"); + String username = resolveMacros("$ifw_api_username$"); + String password = resolveMacros("$ifw_api_password$"); + + Dictionary::Ptr params = new Dictionary(); + + if (arguments) { + ObjectLock oLock (arguments); + Array::Ptr emptyCmd = new Array(); + + for (auto& kv : arguments) { + Dictionary::Ptr argSpec; + + if (kv.second.IsObjectType<Dictionary>()) { + argSpec = Dictionary::Ptr(kv.second)->ShallowClone(); + } else { + argSpec = new Dictionary({{ "value", kv.second }}); + } + + // See default branch of below switch + argSpec->Set("repeat_key", false); + + { + ObjectLock oLock (argSpec); + + for (auto& kv : argSpec) { + if (kv.second.GetType() == ValueObject) { + auto now (Utility::GetTime()); + + ReportIfwCheckResult( + checkable, command->GetName(), cr, + "$ifw_api_arguments$ may not directly contain objects (especially functions).", now, now + ); + + return; + } + } + } + + /* MacroProcessor::ResolveArguments() converts + * + * [ "check_example" ] + * and + * { + * "-f" = { set_if = "$example_flag$" } + * "-a" = "$example_arg$" + * } + * + * to + * + * [ "check_example", "-f", "-a", "X" ] + * + * but we need the args one-by-one like [ "-f" ] or [ "-a", "X" ]. + */ + Array::Ptr arg = MacroProcessor::ResolveArguments( + emptyCmd, new Dictionary({{kv.first, argSpec}}), resolvers, lcr, resolvedMacros, useResolvedMacros + ); + + switch (arg ? arg->GetLength() : 0) { + case 0: + break; + case 1: // [ "-f" ] + params->Set(arg->Get(0), true); + break; + case 2: // [ "-a", "X" ] + params->Set(arg->Get(0), arg->Get(1)); + break; + default: { // [ "-a", "X", "Y" ] + auto k (arg->Get(0)); + + arg->Remove(0); + params->Set(k, arg); + } + } + } + } + + auto checkTimeout (command->GetTimeout()); + auto checkableTimeout (checkable->GetCheckTimeout()); + + if (!checkableTimeout.IsEmpty()) + checkTimeout = checkableTimeout; + + if (resolvedMacros && !useResolvedMacros) + return; + + if (psHost.IsEmpty()) { + psHost = "localhost"; + } + + if (expectedSan.IsEmpty()) { + expectedSan = IcingaApplication::GetInstance()->GetNodeName(); + } + + if (cert.IsEmpty()) { + cert = ApiListener::GetDefaultCertPath(); + } + + if (key.IsEmpty()) { + key = ApiListener::GetDefaultKeyPath(); + } + + if (ca.IsEmpty()) { + ca = ApiListener::GetDefaultCaPath(); + } + + Url::Ptr uri = new Url(); + + uri->SetPath({ "v1", "checker" }); + uri->SetQuery({{ "command", psCommand }}); + + static const auto userAgent ("Icinga/" + Application::GetAppVersion()); + auto relative (uri->Format()); + auto body (JsonEncode(params)); + auto req (Shared<http::request<http::string_body>>::Make()); + + req->method(http::verb::post); + req->target(relative); + req->set(field::accept, "application/json"); + req->set(field::content_type, "application/json"); + req->set(field::host, expectedSan + ":" + psPort); + req->set(field::user_agent, userAgent); + req->body() = body; + req->content_length(req->body().size()); + + static const auto curlTlsMinVersion ((String("--") + DEFAULT_TLS_PROTOCOLMIN).ToLower()); + + Array::Ptr cmdLine = new Array({ + "curl", "--verbose", curlTlsMinVersion, "--fail-with-body", + "--connect-to", expectedSan + ":" + psPort + ":" + psHost + ":" + psPort, + "--ciphers", DEFAULT_TLS_CIPHERS, + "--cert", cert, + "--key", key, + "--cacert", ca, + "--request", "POST", + "--url", "https://" + expectedSan + ":" + psPort + relative, + "--user-agent", userAgent, + "--header", "Accept: application/json", + "--header", "Content-Type: application/json", + "--data-raw", body + }); + + if (!crl.IsEmpty()) { + cmdLine->Add("--crlfile"); + cmdLine->Add(crl); + } + + if (!username.IsEmpty() && !password.IsEmpty()) { + auto authn (username + ":" + password); + + req->set(field::authorization, "Basic " + Base64::Encode(authn)); + cmdLine->Add("--user"); + cmdLine->Add(authn); + } + + auto& io (IoEngine::Get().GetIoContext()); + auto strand (Shared<asio::io_context::strand>::Make(io)); + Shared<asio::ssl::context>::Ptr ctx; + double start = Utility::GetTime(); + + try { + ctx = SetupSslContext(cert, key, ca, crl, DEFAULT_TLS_CIPHERS, DEFAULT_TLS_PROTOCOLMIN, DebugInfo()); + } catch (const std::exception& ex) { + ReportIfwCheckResult(checkable, cmdLine, cr, ex.what(), start, Utility::GetTime()); + return; + } + + auto conn (Shared<AsioTlsStream>::Make(io, *ctx, expectedSan)); + + IoEngine::SpawnCoroutine( + *strand, + [strand, checkable, cmdLine, cr, psCommand, psHost, expectedSan, psPort, conn, req, start, checkTimeout](asio::yield_context yc) { + Timeout::Ptr timeout = new Timeout(strand->context(), *strand, boost::posix_time::microseconds(int64_t(checkTimeout * 1e6)), + [&conn, &checkable](boost::asio::yield_context yc) { + Log(LogNotice, "IfwApiCheckTask") + << "Timeout while checking " << checkable->GetReflectionType()->GetName() + << " '" << checkable->GetName() << "', cancelling attempt"; + + boost::system::error_code ec; + conn->lowest_layer().cancel(ec); + } + ); + + Defer cancelTimeout ([&timeout]() { timeout->Cancel(); }); + + DoIfwNetIo(yc, checkable, cmdLine, cr, psCommand, psHost, expectedSan, psPort, *conn, *req, start); + } + ); +} diff --git a/lib/methods/ifwapichecktask.hpp b/lib/methods/ifwapichecktask.hpp new file mode 100644 index 0000000..3932733 --- /dev/null +++ b/lib/methods/ifwapichecktask.hpp @@ -0,0 +1,27 @@ +/* Icinga 2 | (c) 2023 Icinga GmbH | GPLv2+ */ + +#pragma once + +#include "methods/i2-methods.hpp" +#include "icinga/service.hpp" +#include "base/dictionary.hpp" + +namespace icinga +{ + +/** + * Executes checks via Icinga for Windows API. + * + * @ingroup methods + */ +class IfwApiCheckTask +{ +public: + static void ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); + +private: + IfwApiCheckTask(); +}; + +} diff --git a/lib/methods/methods-itl.conf b/lib/methods/methods-itl.conf new file mode 100644 index 0000000..6249692 --- /dev/null +++ b/lib/methods/methods-itl.conf @@ -0,0 +1,90 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +System.assert(Internal.run_with_activation_context(function() { + template CheckCommand "icinga-check-command" use (IcingaCheck = Internal.IcingaCheck) { + execute = IcingaCheck + + vars.icinga_min_version = "" + } + + template CheckCommand "cluster-check-command" use (ClusterCheck = Internal.ClusterCheck) { + execute = ClusterCheck + } + + template CheckCommand "cluster-zone-check-command" use (ClusterZoneCheck = Internal.ClusterZoneCheck) { + execute = ClusterZoneCheck + } + + template CheckCommand "plugin-check-command" use (PluginCheck = Internal.PluginCheck) default { + execute = PluginCheck + } + + template NotificationCommand "plugin-notification-command" use (PluginNotification = Internal.PluginNotification) default { + execute = PluginNotification + } + + template EventCommand "plugin-event-command" use (PluginEvent = Internal.PluginEvent) default { + execute = PluginEvent + } + + template CheckCommand "dummy-check-command" use (DummyCheck = Internal.DummyCheck) { + execute = DummyCheck + } + + template CheckCommand "random-check-command" use (RandomCheck = Internal.RandomCheck) { + execute = RandomCheck + } + + template CheckCommand "exception-check-command" use (ExceptionCheck = Internal.ExceptionCheck) { + execute = ExceptionCheck + } + + template CheckCommand "null-check-command" use (NullCheck = Internal.NullCheck) { + execute = NullCheck + } + + template CheckCommand "ifw-api-check-command" use (IfwApiCheck = Internal.IfwApiCheck) { + execute = IfwApiCheck + } + + template EventCommand "null-event-command" use (NullEvent = Internal.NullEvent) { + execute = NullEvent + } + + template TimePeriod "empty-timeperiod" use (EmptyTimePeriod = Internal.EmptyTimePeriod) { + update = EmptyTimePeriod + } + + template TimePeriod "even-minutes-timeperiod" use (EvenMinutesTimePeriod = Internal.EvenMinutesTimePeriod) { + update = EvenMinutesTimePeriod + } + + template CheckCommand "sleep-check-command" use (SleepCheck = Internal.SleepCheck) { + execute = SleepCheck + + vars.sleep_time = 1s + } +})) + +var methods = [ + "IcingaCheck", + "IfwApiCheck", + "ClusterCheck", + "ClusterZoneCheck", + "PluginCheck", + "ClrCheck", + "PluginNotification", + "PluginEvent", + "DummyCheck", + "RandomCheck", + "ExceptionCheck", + "NullCheck", + "NullEvent", + "EmptyTimePeriod", + "EvenMinutesTimePeriod", + "SleepCheck" +] + +for (method in methods) { + Internal.remove(method) +} diff --git a/lib/methods/nullchecktask.cpp b/lib/methods/nullchecktask.cpp new file mode 100644 index 0000000..ee66029 --- /dev/null +++ b/lib/methods/nullchecktask.cpp @@ -0,0 +1,50 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef _WIN32 +# include <stdlib.h> +#endif /* _WIN32 */ +#include "methods/nullchecktask.hpp" +#include "icinga/icingaapplication.hpp" +#include "base/utility.hpp" +#include "base/perfdatavalue.hpp" +#include "base/convert.hpp" +#include "base/function.hpp" +#include "base/logger.hpp" + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, NullCheck, &NullCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); + +void NullCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) +{ + REQUIRE_NOT_NULL(checkable); + REQUIRE_NOT_NULL(cr); + + if (resolvedMacros && !useResolvedMacros) + return; + + String output = "Hello from "; + output += IcingaApplication::GetInstance()->GetNodeName(); + ServiceState state = ServiceOK; + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + double now = Utility::GetTime(); + ProcessResult pr; + pr.PID = -1; + pr.Output = output; + pr.ExecutionStart = now; + pr.ExecutionEnd = now; + pr.ExitStatus = state; + + Checkable::ExecuteCommandProcessFinishedHandler("", pr); + } else { + cr->SetOutput(output); + cr->SetPerformanceData(new Array({ + new PerfdataValue("time", Convert::ToDouble(Utility::GetTime())) + })); + cr->SetState(state); + + checkable->ProcessCheckResult(cr); + } +} diff --git a/lib/methods/nullchecktask.hpp b/lib/methods/nullchecktask.hpp new file mode 100644 index 0000000..954cf8d --- /dev/null +++ b/lib/methods/nullchecktask.hpp @@ -0,0 +1,30 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef NULLCHECKTASK_H +#define NULLCHECKTASK_H + +#include "methods/i2-methods.hpp" +#include "icinga/service.hpp" +#include "base/dictionary.hpp" + +namespace icinga +{ + +/** + * Test class for additional check types. Implements the "null" check type. + * + * @ingroup methods + */ +class NullCheckTask +{ +public: + static void ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); + +private: + NullCheckTask(); +}; + +} + +#endif /* NULLCHECKTASK_H */ diff --git a/lib/methods/nulleventtask.cpp b/lib/methods/nulleventtask.cpp new file mode 100644 index 0000000..3c02f23 --- /dev/null +++ b/lib/methods/nulleventtask.cpp @@ -0,0 +1,26 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "methods/nulleventtask.hpp" +#include "base/function.hpp" +#include "base/logger.hpp" + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, NullEvent, &NullEventTask::ScriptFunc, "checkable:resolvedMacros:useResolvedMacros"); + +void NullEventTask::ScriptFunc(const Checkable::Ptr& checkable, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) +{ + REQUIRE_NOT_NULL(checkable); + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + double now = Utility::GetTime(); + ProcessResult pr; + pr.PID = -1; + pr.Output = ""; + pr.ExecutionStart = now; + pr.ExecutionEnd = now; + pr.ExitStatus = 0; + + Checkable::ExecuteCommandProcessFinishedHandler("", pr); + } +} diff --git a/lib/methods/nulleventtask.hpp b/lib/methods/nulleventtask.hpp new file mode 100644 index 0000000..153470f --- /dev/null +++ b/lib/methods/nulleventtask.hpp @@ -0,0 +1,30 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef NULLEVENTTASK_H +#define NULLEVENTTASK_H + +#include "methods/i2-methods.hpp" +#include "icinga/service.hpp" +#include "base/dictionary.hpp" + +namespace icinga +{ + +/** + * Test class for additional event handler types. Implements the "null" event handler type. + * + * @ingroup methods + */ +class NullEventTask +{ +public: + static void ScriptFunc(const Checkable::Ptr& service, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); + +private: + NullEventTask(); +}; + +} + +#endif /* NULLEVENTTASK_H */ diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp new file mode 100644 index 0000000..b4749fb --- /dev/null +++ b/lib/methods/pluginchecktask.cpp @@ -0,0 +1,89 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "methods/pluginchecktask.hpp" +#include "icinga/pluginutility.hpp" +#include "icinga/checkcommand.hpp" +#include "icinga/macroprocessor.hpp" +#include "base/configtype.hpp" +#include "base/logger.hpp" +#include "base/function.hpp" +#include "base/utility.hpp" +#include "base/process.hpp" +#include "base/convert.hpp" + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, PluginCheck, &PluginCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); + +void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) +{ + REQUIRE_NOT_NULL(checkable); + REQUIRE_NOT_NULL(cr); + + CheckCommand::Ptr commandObj = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + MacroProcessor::ResolverList resolvers; + + if (MacroResolver::OverrideMacros) + resolvers.emplace_back("override", MacroResolver::OverrideMacros); + + if (service) + resolvers.emplace_back("service", service); + resolvers.emplace_back("host", host); + resolvers.emplace_back("command", commandObj); + + int timeout = commandObj->GetTimeout(); + + if (!checkable->GetCheckTimeout().IsEmpty()) + timeout = checkable->GetCheckTimeout(); + + std::function<void(const Value& commandLine, const ProcessResult&)> callback; + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + callback = Checkable::ExecuteCommandProcessFinishedHandler; + } else { + callback = [checkable, cr](const Value& commandLine, const ProcessResult& pr) { + ProcessFinishedHandler(checkable, cr, commandLine, pr); + }; + } + + PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(), + resolvers, resolvedMacros, useResolvedMacros, timeout, callback); + + if (!resolvedMacros || useResolvedMacros) { + Checkable::CurrentConcurrentChecks.fetch_add(1); + Checkable::IncreasePendingChecks(); + } +} + +void PluginCheckTask::ProcessFinishedHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Value& commandLine, const ProcessResult& pr) +{ + Checkable::CurrentConcurrentChecks.fetch_sub(1); + Checkable::DecreasePendingChecks(); + + if (pr.ExitStatus > 3) { + Process::Arguments parguments = Process::PrepareCommand(commandLine); + Log(LogWarning, "PluginCheckTask") + << "Check command for object '" << checkable->GetName() << "' (PID: " << pr.PID + << ", arguments: " << Process::PrettyPrintArguments(parguments) << ") terminated with exit code " + << pr.ExitStatus << ", output: " << pr.Output; + } + + String output = pr.Output.Trim(); + + std::pair<String, String> co = PluginUtility::ParseCheckOutput(output); + cr->SetCommand(commandLine); + cr->SetOutput(co.first); + cr->SetPerformanceData(PluginUtility::SplitPerfdata(co.second)); + cr->SetState(PluginUtility::ExitStatusToState(pr.ExitStatus)); + cr->SetExitStatus(pr.ExitStatus); + cr->SetExecutionStart(pr.ExecutionStart); + cr->SetExecutionEnd(pr.ExecutionEnd); + + checkable->ProcessCheckResult(cr); +} diff --git a/lib/methods/pluginchecktask.hpp b/lib/methods/pluginchecktask.hpp new file mode 100644 index 0000000..a4fc3a3 --- /dev/null +++ b/lib/methods/pluginchecktask.hpp @@ -0,0 +1,33 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef PLUGINCHECKTASK_H +#define PLUGINCHECKTASK_H + +#include "methods/i2-methods.hpp" +#include "base/process.hpp" +#include "icinga/service.hpp" + +namespace icinga +{ + +/** + * Implements service checks based on external plugins. + * + * @ingroup methods + */ +class PluginCheckTask +{ +public: + static void ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); + +private: + PluginCheckTask(); + + static void ProcessFinishedHandler(const Checkable::Ptr& service, + const CheckResult::Ptr& cr, const Value& commandLine, const ProcessResult& pr); +}; + +} + +#endif /* PLUGINCHECKTASK_H */ diff --git a/lib/methods/plugineventtask.cpp b/lib/methods/plugineventtask.cpp new file mode 100644 index 0000000..00efb6c --- /dev/null +++ b/lib/methods/plugineventtask.cpp @@ -0,0 +1,61 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "methods/plugineventtask.hpp" +#include "icinga/eventcommand.hpp" +#include "icinga/macroprocessor.hpp" +#include "icinga/pluginutility.hpp" +#include "base/configtype.hpp" +#include "base/logger.hpp" +#include "base/function.hpp" +#include "base/utility.hpp" +#include "base/process.hpp" +#include "base/convert.hpp" + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, PluginEvent, &PluginEventTask::ScriptFunc, "checkable:resolvedMacros:useResolvedMacros"); + +void PluginEventTask::ScriptFunc(const Checkable::Ptr& checkable, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) +{ + REQUIRE_NOT_NULL(checkable); + + EventCommand::Ptr commandObj = EventCommand::ExecuteOverride ? EventCommand::ExecuteOverride : checkable->GetEventCommand(); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + MacroProcessor::ResolverList resolvers; + + if (MacroResolver::OverrideMacros) + resolvers.emplace_back("override", MacroResolver::OverrideMacros); + + if (service) + resolvers.emplace_back("service", service); + resolvers.emplace_back("host", host); + resolvers.emplace_back("command", commandObj); + + int timeout = commandObj->GetTimeout(); + std::function<void(const Value& commandLine, const ProcessResult&)> callback; + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + callback = Checkable::ExecuteCommandProcessFinishedHandler; + } else { + callback = [checkable](const Value& commandLine, const ProcessResult& pr) { ProcessFinishedHandler(checkable, commandLine, pr); }; + } + + PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(), + resolvers, resolvedMacros, useResolvedMacros, timeout, callback); +} + +void PluginEventTask::ProcessFinishedHandler(const Checkable::Ptr& checkable, const Value& commandLine, const ProcessResult& pr) +{ + if (pr.ExitStatus != 0) { + Process::Arguments parguments = Process::PrepareCommand(commandLine); + Log(LogWarning, "PluginEventTask") + << "Event command for object '" << checkable->GetName() << "' (PID: " << pr.PID + << ", arguments: " << Process::PrettyPrintArguments(parguments) << ") terminated with exit code " + << pr.ExitStatus << ", output: " << pr.Output; + } +} diff --git a/lib/methods/plugineventtask.hpp b/lib/methods/plugineventtask.hpp new file mode 100644 index 0000000..8908a82 --- /dev/null +++ b/lib/methods/plugineventtask.hpp @@ -0,0 +1,33 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef PLUGINEVENTTASK_H +#define PLUGINEVENTTASK_H + +#include "methods/i2-methods.hpp" +#include "icinga/service.hpp" +#include "base/process.hpp" + +namespace icinga +{ + +/** + * Implements event handlers based on external plugins. + * + * @ingroup methods + */ +class PluginEventTask +{ +public: + static void ScriptFunc(const Checkable::Ptr& service, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); + +private: + PluginEventTask(); + + static void ProcessFinishedHandler(const Checkable::Ptr& checkable, + const Value& commandLine, const ProcessResult& pr); +}; + +} + +#endif /* PLUGINEVENTTASK_H */ diff --git a/lib/methods/pluginnotificationtask.cpp b/lib/methods/pluginnotificationtask.cpp new file mode 100644 index 0000000..95911fa --- /dev/null +++ b/lib/methods/pluginnotificationtask.cpp @@ -0,0 +1,123 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "methods/pluginnotificationtask.hpp" +#include "icinga/notification.hpp" +#include "icinga/notificationcommand.hpp" +#include "icinga/pluginutility.hpp" +#include "icinga/service.hpp" +#include "icinga/macroprocessor.hpp" +#include "base/function.hpp" +#include "base/logger.hpp" +#include "base/utility.hpp" +#include "base/process.hpp" +#include "base/convert.hpp" + +#ifdef __linux__ +# include <linux/binfmts.h> +# include <unistd.h> + +# ifndef PAGE_SIZE +// MAX_ARG_STRLEN is a multiple of PAGE_SIZE which is missing +# define PAGE_SIZE getpagesize() +# endif /* PAGE_SIZE */ + +// Make e.g. the $host.output$ itself even 10% shorter to leave enough room +// for e.g. --host-output= as in --host-output=$host.output$, but without int overflow +const static auto l_MaxOutLen = MAX_ARG_STRLEN - MAX_ARG_STRLEN / 10u; +#endif /* __linux__ */ + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, PluginNotification, &PluginNotificationTask::ScriptFunc, "notification:user:cr:itype:author:comment:resolvedMacros:useResolvedMacros"); + +void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification, + const User::Ptr& user, const CheckResult::Ptr& cr, int itype, + const String& author, const String& comment, const Dictionary::Ptr& resolvedMacros, + bool useResolvedMacros) +{ + REQUIRE_NOT_NULL(notification); + REQUIRE_NOT_NULL(user); + + NotificationCommand::Ptr commandObj = NotificationCommand::ExecuteOverride ? NotificationCommand::ExecuteOverride : notification->GetCommand(); + + auto type = static_cast<NotificationType>(itype); + + Checkable::Ptr checkable = notification->GetCheckable(); + + Dictionary::Ptr notificationExtra = new Dictionary({ + { "type", Notification::NotificationTypeToStringCompat(type) }, //TODO: Change that to our types. + { "author", author }, +#ifdef __linux__ + { "comment", comment.SubStr(0, l_MaxOutLen) } +#else /* __linux__ */ + { "comment", comment } +#endif /* __linux__ */ + }); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + MacroProcessor::ResolverList resolvers; + + if (MacroResolver::OverrideMacros) + resolvers.emplace_back("override", MacroResolver::OverrideMacros); + + resolvers.emplace_back("user", user); + resolvers.emplace_back("notification", notificationExtra); + resolvers.emplace_back("notification", notification); + + if (service) { +#ifdef __linux__ + auto cr (service->GetLastCheckResult()); + + if (cr) { + auto output (cr->GetOutput()); + + if (output.GetLength() > l_MaxOutLen) { + resolvers.emplace_back("service", new Dictionary({{"output", output.SubStr(0, l_MaxOutLen)}})); + } + } +#endif /* __linux__ */ + + resolvers.emplace_back("service", service); + } + +#ifdef __linux__ + auto hcr (host->GetLastCheckResult()); + + if (hcr) { + auto output (hcr->GetOutput()); + + if (output.GetLength() > l_MaxOutLen) { + resolvers.emplace_back("host", new Dictionary({{"output", output.SubStr(0, l_MaxOutLen)}})); + } + } +#endif /* __linux__ */ + + resolvers.emplace_back("host", host); + resolvers.emplace_back("command", commandObj); + + int timeout = commandObj->GetTimeout(); + std::function<void(const Value& commandLine, const ProcessResult&)> callback; + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + callback = Checkable::ExecuteCommandProcessFinishedHandler; + } else { + callback = [checkable](const Value& commandline, const ProcessResult& pr) { ProcessFinishedHandler(checkable, commandline, pr); }; + } + + PluginUtility::ExecuteCommand(commandObj, checkable, cr, resolvers, + resolvedMacros, useResolvedMacros, timeout, callback); +} + +void PluginNotificationTask::ProcessFinishedHandler(const Checkable::Ptr& checkable, const Value& commandLine, const ProcessResult& pr) +{ + if (pr.ExitStatus != 0) { + Process::Arguments parguments = Process::PrepareCommand(commandLine); + Log(LogWarning, "PluginNotificationTask") + << "Notification command for object '" << checkable->GetName() << "' (PID: " << pr.PID + << ", arguments: " << Process::PrettyPrintArguments(parguments) << ") terminated with exit code " + << pr.ExitStatus << ", output: " << pr.Output; + } +} diff --git a/lib/methods/pluginnotificationtask.hpp b/lib/methods/pluginnotificationtask.hpp new file mode 100644 index 0000000..66d6539 --- /dev/null +++ b/lib/methods/pluginnotificationtask.hpp @@ -0,0 +1,36 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef PLUGINNOTIFICATIONTASK_H +#define PLUGINNOTIFICATIONTASK_H + +#include "methods/i2-methods.hpp" +#include "icinga/notification.hpp" +#include "icinga/service.hpp" +#include "base/process.hpp" + +namespace icinga +{ + +/** + * Implements sending notifications based on external plugins. + * + * @ingroup methods + */ +class PluginNotificationTask +{ +public: + static void ScriptFunc(const Notification::Ptr& notification, + const User::Ptr& user, const CheckResult::Ptr& cr, int itype, + const String& author, const String& comment, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); + +private: + PluginNotificationTask(); + + static void ProcessFinishedHandler(const Checkable::Ptr& checkable, + const Value& commandLine, const ProcessResult& pr); +}; + +} + +#endif /* PLUGINNOTIFICATIONTASK_H */ diff --git a/lib/methods/randomchecktask.cpp b/lib/methods/randomchecktask.cpp new file mode 100644 index 0000000..9b133ef --- /dev/null +++ b/lib/methods/randomchecktask.cpp @@ -0,0 +1,65 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef _WIN32 +# include <stdlib.h> +#endif /* _WIN32 */ +#include "methods/randomchecktask.hpp" +#include "icinga/icingaapplication.hpp" +#include "icinga/checkcommand.hpp" +#include "base/utility.hpp" +#include "base/perfdatavalue.hpp" +#include "base/function.hpp" +#include "base/logger.hpp" + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, RandomCheck, &RandomCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); + +void RandomCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) +{ + REQUIRE_NOT_NULL(checkable); + REQUIRE_NOT_NULL(cr); + + if (resolvedMacros && !useResolvedMacros) + return; + + double now = Utility::GetTime(); + double uptime = Application::GetUptime(); + + String output = "Hello from " + IcingaApplication::GetInstance()->GetNodeName() + + ". Icinga 2 has been running for " + Utility::FormatDuration(uptime) + + ". Version: " + Application::GetAppVersion(); + + CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); + String commandName = command->GetName(); + ServiceState state = static_cast<ServiceState>(Utility::Random() % 4); + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + double now = Utility::GetTime(); + ProcessResult pr; + pr.PID = -1; + pr.Output = output; + pr.ExecutionStart = now; + pr.ExecutionEnd = now; + pr.ExitStatus = state; + + Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr); + } else { + cr->SetOutput(output); + + double random = Utility::Random() % 1000; + cr->SetPerformanceData(new Array({ + new PerfdataValue("time", now), + new PerfdataValue("value", random), + new PerfdataValue("value_1m", random * 0.9), + new PerfdataValue("value_5m", random * 0.8), + new PerfdataValue("uptime", uptime), + })); + + cr->SetState(state); + cr->SetCommand(commandName); + + checkable->ProcessCheckResult(cr); + } +} diff --git a/lib/methods/randomchecktask.hpp b/lib/methods/randomchecktask.hpp new file mode 100644 index 0000000..00ce663 --- /dev/null +++ b/lib/methods/randomchecktask.hpp @@ -0,0 +1,29 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef RANDOMCHECKTASK_H +#define RANDOMCHECKTASK_H + +#include "icinga/service.hpp" +#include "base/dictionary.hpp" + +namespace icinga +{ + +/** + * Test class for additional check types. Implements the "null" check type. + * + * @ingroup methods + */ +class RandomCheckTask +{ +public: + static void ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); + +private: + RandomCheckTask(); +}; + +} + +#endif /* RANDOMCHECKTASK_H */ diff --git a/lib/methods/sleepchecktask.cpp b/lib/methods/sleepchecktask.cpp new file mode 100644 index 0000000..af6b063 --- /dev/null +++ b/lib/methods/sleepchecktask.cpp @@ -0,0 +1,67 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "methods/sleepchecktask.hpp" +#include "icinga/pluginutility.hpp" +#include "base/utility.hpp" +#include "base/convert.hpp" +#include "base/function.hpp" +#include "base/logger.hpp" + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, SleepCheck, &SleepCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); + +void SleepCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) +{ + REQUIRE_NOT_NULL(checkable); + REQUIRE_NOT_NULL(cr); + + CheckCommand::Ptr commandObj = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + MacroProcessor::ResolverList resolvers; + + if (MacroResolver::OverrideMacros) + resolvers.emplace_back("override", MacroResolver::OverrideMacros); + + if (service) + resolvers.emplace_back("service", service); + resolvers.emplace_back("host", host); + resolvers.emplace_back("command", commandObj); + + double sleepTime = MacroProcessor::ResolveMacros("$sleep_time$", resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros); + + if (resolvedMacros && !useResolvedMacros) + return; + + Utility::Sleep(sleepTime); + + String output = "Slept for " + Convert::ToString(sleepTime) + " seconds."; + + double now = Utility::GetTime(); + CheckCommand::Ptr command = checkable->GetCheckCommand(); + String commandName = command->GetName(); + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + ProcessResult pr; + pr.PID = -1; + pr.Output = output; + pr.ExecutionStart = now - sleepTime; + pr.ExecutionEnd = now; + pr.ExitStatus = 0; + + Checkable::ExecuteCommandProcessFinishedHandler("", pr); + } else { + cr->SetOutput(output); + cr->SetExecutionStart(now); + cr->SetExecutionEnd(now); + cr->SetCommand(commandName); + + checkable->ProcessCheckResult(cr); + } +}
\ No newline at end of file diff --git a/lib/methods/sleepchecktask.hpp b/lib/methods/sleepchecktask.hpp new file mode 100644 index 0000000..b104f60 --- /dev/null +++ b/lib/methods/sleepchecktask.hpp @@ -0,0 +1,30 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef SLEEPCHECKTASK_H +#define SLEEPCHECKTASK_H + +#include "methods/i2-methods.hpp" +#include "icinga/service.hpp" +#include "base/dictionary.hpp" + +namespace icinga +{ + +/** + * Test class for additional check types. Implements the "sleep" check type. + * + * @ingroup methods + */ +class SleepCheckTask +{ +public: + static void ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, + const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros); + +private: + SleepCheckTask(); +}; + +} + +#endif /* SLEEPCHECKTASK_H */
\ No newline at end of file diff --git a/lib/methods/timeperiodtask.cpp b/lib/methods/timeperiodtask.cpp new file mode 100644 index 0000000..bb3f1bb --- /dev/null +++ b/lib/methods/timeperiodtask.cpp @@ -0,0 +1,35 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "methods/timeperiodtask.hpp" +#include "base/function.hpp" + +using namespace icinga; + +REGISTER_FUNCTION_NONCONST(Internal, EmptyTimePeriod, &TimePeriodTask::EmptyTimePeriodUpdate, "tp:begin:end"); +REGISTER_FUNCTION_NONCONST(Internal, EvenMinutesTimePeriod, &TimePeriodTask::EvenMinutesTimePeriodUpdate, "tp:begin:end"); + +Array::Ptr TimePeriodTask::EmptyTimePeriodUpdate(const TimePeriod::Ptr& tp, double, double) +{ + REQUIRE_NOT_NULL(tp); + + Array::Ptr segments = new Array(); + return segments; +} + +Array::Ptr TimePeriodTask::EvenMinutesTimePeriodUpdate(const TimePeriod::Ptr& tp, double begin, double end) +{ + REQUIRE_NOT_NULL(tp); + + ArrayData segments; + + for (long t = begin / 60 - 1; t * 60 < end; t++) { + if ((t % 2) == 0) { + segments.push_back(new Dictionary({ + { "begin", t * 60 }, + { "end", (t + 1) * 60 } + })); + } + } + + return new Array(std::move(segments)); +} diff --git a/lib/methods/timeperiodtask.hpp b/lib/methods/timeperiodtask.hpp new file mode 100644 index 0000000..0dff1c6 --- /dev/null +++ b/lib/methods/timeperiodtask.hpp @@ -0,0 +1,28 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef TIMEPERIODTASK_H +#define TIMEPERIODTASK_H + +#include "icinga/timeperiod.hpp" + +namespace icinga +{ + +/** +* Test timeperiod functions. +* +* @ingroup methods +*/ +class TimePeriodTask +{ +public: + static Array::Ptr EmptyTimePeriodUpdate(const TimePeriod::Ptr& tp, double begin, double end); + static Array::Ptr EvenMinutesTimePeriodUpdate(const TimePeriod::Ptr& tp, double begin, double end); + +private: + TimePeriodTask(); +}; + +} + +#endif /* TIMEPERIODTASK_H */ |