From 752df8ea283de41101406b10f74bfd78f6384440 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 2 Jun 2020 11:43:32 +0200 Subject: [PATCH 001/126] Add stub for /v1/actions/execute-command refs #8034 --- lib/icinga/apiactions.cpp | 7 +++++++ lib/icinga/apiactions.hpp | 1 + 2 files changed, 8 insertions(+) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 8a7e8a9a8..dd2def790 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -31,6 +31,7 @@ REGISTER_APIACTION(remove_downtime, "Service;Host;Downtime", &ApiActions::Remove REGISTER_APIACTION(shutdown_process, "", &ApiActions::ShutdownProcess); REGISTER_APIACTION(restart_process, "", &ApiActions::RestartProcess); REGISTER_APIACTION(generate_ticket, "", &ApiActions::GenerateTicket); +REGISTER_APIACTION(execute_command, "Service;Host", &ApiActions::ExecuteCommand); Dictionary::Ptr ApiActions::CreateResult(int code, const String& status, const Dictionary::Ptr& additional) @@ -550,3 +551,9 @@ Dictionary::Ptr ApiActions::GenerateTicket(const ConfigObject::Ptr&, return ApiActions::CreateResult(200, "Generated PKI ticket '" + ticket + "' for common name '" + cn + "'.", additional); } + +Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, + const Dictionary::Ptr& params) +{ + return ApiActions::CreateResult(501, "Not implemented"); +} diff --git a/lib/icinga/apiactions.hpp b/lib/icinga/apiactions.hpp index c2c8b6188..46e03c02f 100644 --- a/lib/icinga/apiactions.hpp +++ b/lib/icinga/apiactions.hpp @@ -29,6 +29,7 @@ public: static Dictionary::Ptr ShutdownProcess(const ConfigObject::Ptr& object, const Dictionary::Ptr& params); static Dictionary::Ptr RestartProcess(const ConfigObject::Ptr& object, const Dictionary::Ptr& params); static Dictionary::Ptr GenerateTicket(const ConfigObject::Ptr& object, const Dictionary::Ptr& params); + static Dictionary::Ptr ExecuteCommand(const ConfigObject::Ptr& object, const Dictionary::Ptr& params); private: static Dictionary::Ptr CreateResult(int code, const String& status, const Dictionary::Ptr& additional = nullptr); From 3f07dd6caeb15662ee7e33479d2480f8eed6062a Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 2 Jun 2020 11:52:58 +0200 Subject: [PATCH 002/126] Add stub for event::ExecuteCommand w/ source refs #8034 --- lib/icinga/clusterevents-check.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 530234c6b..96b3de146 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -92,6 +92,11 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons return; } + if (params->Contains("source")) { + Log(LogCritical, "ApiListener", "Not implemented."); + return; + } + if (!listener->GetAcceptCommands()) { Log(LogWarning, "ApiListener") << "Ignoring command. '" << listener->GetName() << "' does not accept commands."; From fc220bac69370019250af05983627dd94ba217be Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 2 Jun 2020 13:44:15 +0200 Subject: [PATCH 003/126] Add stub for event::ExecutedCommand refs #8034 --- lib/icinga/clusterevents.cpp | 6 ++++++ lib/icinga/clusterevents.hpp | 1 + 2 files changed, 7 insertions(+) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index bc61a58cb..0cea61b9b 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -34,6 +34,7 @@ REGISTER_APIFUNCTION(ExecuteCommand, event, &ClusterEvents::ExecuteCommandAPIHan REGISTER_APIFUNCTION(SendNotifications, event, &ClusterEvents::SendNotificationsAPIHandler); REGISTER_APIFUNCTION(NotificationSentUser, event, &ClusterEvents::NotificationSentUserAPIHandler); REGISTER_APIFUNCTION(NotificationSentToAllUsers, event, &ClusterEvents::NotificationSentToAllUsersAPIHandler); +REGISTER_APIFUNCTION(ExecutedCommand, event, &ClusterEvents::ExecutedCommandAPIHandler); void ClusterEvents::StaticInitialize() { @@ -939,3 +940,8 @@ Value ClusterEvents::NotificationSentToAllUsersAPIHandler(const MessageOrigin::P return Empty; } + +Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) +{ + return "Not implemented"; +} diff --git a/lib/icinga/clusterevents.hpp b/lib/icinga/clusterevents.hpp index a51bc60db..fb816ec0f 100644 --- a/lib/icinga/clusterevents.hpp +++ b/lib/icinga/clusterevents.hpp @@ -60,6 +60,7 @@ public: static void NotificationSentToAllUsersHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set& users, NotificationType notificationType, const CheckResult::Ptr& cr, const String& author, const String& commentText, const MessageOrigin::Ptr& origin); static Value NotificationSentToAllUsersAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); + static Value ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); static int GetCheckRequestQueueSize(); static void LogRemoteCheckQueueInformation(); From d6fad3232b913a0e8c07612998305399927e405d Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 2 Jun 2020 16:59:42 +0200 Subject: [PATCH 004/126] Introduce Checkable#pending_executions refs #8034 --- lib/icinga/checkable.ti | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/icinga/checkable.ti b/lib/icinga/checkable.ti index 98d18cd7f..8a13ad63c 100644 --- a/lib/icinga/checkable.ti +++ b/lib/icinga/checkable.ti @@ -173,6 +173,8 @@ abstract class Checkable : CustomVarObject return Endpoint::GetByName(GetCommandEndpointRaw()); }}} }; + + [state, no_user_view, no_user_modify] Dictionary::Ptr pending_executions; }; } From 120440b84fae1fa60e7ddac11e6cd43af5ca52d6 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 2 Jun 2020 17:09:13 +0200 Subject: [PATCH 005/126] Introduce Checkable#executions refs #8034 --- lib/icinga/checkable.ti | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/icinga/checkable.ti b/lib/icinga/checkable.ti index 8a13ad63c..62b0d7bd1 100644 --- a/lib/icinga/checkable.ti +++ b/lib/icinga/checkable.ti @@ -174,6 +174,7 @@ abstract class Checkable : CustomVarObject }}} }; + [state, no_user_modify] Dictionary::Ptr executions; [state, no_user_view, no_user_modify] Dictionary::Ptr pending_executions; }; From 9cb3779ec2dfc1623cf7b3ed727a5b8197396921 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 2 Jun 2020 17:16:30 +0200 Subject: [PATCH 006/126] Add stub for event::UpdateExecutions refs #8034 --- lib/icinga/clusterevents.cpp | 6 ++++++ lib/icinga/clusterevents.hpp | 1 + 2 files changed, 7 insertions(+) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 0cea61b9b..29fe71702 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -35,6 +35,7 @@ REGISTER_APIFUNCTION(SendNotifications, event, &ClusterEvents::SendNotifications REGISTER_APIFUNCTION(NotificationSentUser, event, &ClusterEvents::NotificationSentUserAPIHandler); REGISTER_APIFUNCTION(NotificationSentToAllUsers, event, &ClusterEvents::NotificationSentToAllUsersAPIHandler); REGISTER_APIFUNCTION(ExecutedCommand, event, &ClusterEvents::ExecutedCommandAPIHandler); +REGISTER_APIFUNCTION(UpdateExecutions, event, &ClusterEvents::UpdateExecutionsAPIHandler); void ClusterEvents::StaticInitialize() { @@ -945,3 +946,8 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, { return "Not implemented"; } + +Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) +{ + return "Not implemented"; +} diff --git a/lib/icinga/clusterevents.hpp b/lib/icinga/clusterevents.hpp index fb816ec0f..bd6174cb2 100644 --- a/lib/icinga/clusterevents.hpp +++ b/lib/icinga/clusterevents.hpp @@ -61,6 +61,7 @@ public: NotificationType notificationType, const CheckResult::Ptr& cr, const String& author, const String& commentText, const MessageOrigin::Ptr& origin); static Value NotificationSentToAllUsersAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); static Value ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); + static Value UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); static int GetCheckRequestQueueSize(); static void LogRemoteCheckQueueInformation(); From 823fe34d24f1a1d09e608145e18f0e521b2dca41 Mon Sep 17 00:00:00 2001 From: Andrea Avancini Date: Tue, 16 Jun 2020 14:18:11 +0200 Subject: [PATCH 007/126] Parameter evaluation, macro resolution, update executions, send ExecuteCommand message for ExecuteCommand Endpoint --- lib/icinga/apiactions.cpp | 183 +++++++++++++++++++++++++++++++++++++- 1 file changed, 182 insertions(+), 1 deletion(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index dd2def790..01f3b7a59 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -555,5 +555,186 @@ Dictionary::Ptr ApiActions::GenerateTicket(const ConfigObject::Ptr&, Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, const Dictionary::Ptr& params) { - return ApiActions::CreateResult(501, "Not implemented"); + Checkable::Ptr checkable = static_pointer_cast(object); + if (! checkable) + return ApiActions::CreateResult(404, "Can't schedule a command execution for non-existent object."); + + ObjectLock oLock (checkable); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + String endpoint = "$command_endpoint$"; + if (params->Contains("endpoint")) + endpoint = params->Get("endpoint"); + + /* Resolve endpoint macro */ + MacroProcessor::ResolverList resolvers; + if (service) + resolvers.emplace_back("service", service); + resolvers.emplace_back("host", host); + resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); + + Dictionary::Ptr resolvedMacros; + bool useResolvedMacros; + + if (params->Contains("macros")) { + resolvedMacros = HttpUtility::GetLastParameter(params, "macros"); + useResolvedMacros = true; + } else { + resolvedMacros = new Dictionary(); + useResolvedMacros = false; + } + + String resolved_endpoint = MacroProcessor::ResolveMacros(endpoint, + resolvers, + checkable->GetLastCheckResult(), + nullptr, + MacroProcessor::EscapeCallback(), + resolvedMacros, + useResolvedMacros); + + /* Check if resolved_endpoint is not empty */ + if (resolved_endpoint.IsEmpty()) + return ApiActions::CreateResult(400, "Endpoint must not be empty."); + + /* Check if endpoint exists */ + if (!Endpoint::GetByName(resolved_endpoint)) + return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); + + /* Get command_type */ + String command_type = "EventCommand"; + if (params->Contains("command_type")) + command_type = HttpUtility::GetLastParameter(params, "command_type"); + + /* Validate command_type */ + if (command_type != "EventCommand" && command_type != "CheckCommand" && command_type != "NotificationCommand") + return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); + + /* Get command */ + String command; + if (!params->Contains("command")) { + if (command_type == "CheckCommand" ) { + command = "$check_command$"; + } else if (command_type == "EventCommand") { + command = "$event_command$"; + } else if (command_type == "NotificationCommand") { + command = "$notification_command$"; + } else { + return ApiActions::CreateResult(404, "Can't infer 'command' from command_type '" + command_type + "'"); + } + } else { + command = HttpUtility::GetLastParameter(params, "command"); + } + + /* Resolve command macro */ + String resolved_command = MacroProcessor::ResolveMacros(command, + resolvers, + checkable->GetLastCheckResult(), + nullptr, + MacroProcessor::EscapeCallback(), + resolvedMacros, + useResolvedMacros); + + /* Check if resolved_command is not empty */ + if (resolved_command.IsEmpty()) + return ApiActions::CreateResult(400, "Command must not be empty."); + + /* Check if resolved_command exists and it is of type command_type */ + if (command_type == "CheckCommand") { + if (!CheckCommand::GetByName(resolved_command)) + return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + } else if (command_type == "EventCommand") { + if (!EventCommand::GetByName(resolved_command)) + return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + } else if (command_type == "NotificationCommand") { + if (!EventCommand::GetByName(resolved_command)) + return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + } else + return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); + + /* Get TTL param */ + double ttl = 0; /* FIXME default value? */ + if (params->Contains("ttl")) + ttl = HttpUtility::GetLastParameter(params, "ttl"); + + if (ttl <= 0) + return ApiActions::CreateResult(400, "ttl must be greater than 0"); + + /* Get wait param */ + bool wait = false; + if (params->Contains("wait")) + wait = HttpUtility::GetLastParameter(params, "wait"); + + /* This generates a UUID */ + String uuid = Utility::NewUniqueID(); + + /* Create the deadline */ + double deadline = Utility::GetTime() + ttl; + + /* Update executions */ + Dictionary::Ptr pending_execution = new Dictionary(); + pending_execution->Set("pending", true); + pending_execution->Set("deadline", deadline); + Dictionary::Ptr executions = checkable->GetExecutions(); + if (!executions) + executions = new Dictionary(); + executions->Set(uuid, pending_execution); + checkable->SetExecutions(executions); + + /* Broadcast the update */ + ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) + return ApiActions::CreateResult(404, "No ApiListener instance available."); + + Dictionary::Ptr updateParams = new Dictionary(); + updateParams->Set("host", host->GetName()); + if (service) + updateParams->Set("service", service->GetShortName()); + updateParams->Set("executions", executions); + + Dictionary::Ptr updateMessage = new Dictionary(); + updateMessage->Set("jsonrpc", "2.0"); + updateMessage->Set("method", "event::UpdateExecutions"); + updateMessage->Set("params", updateParams); + + /* FIXME origin? */ + MessageOrigin::Ptr origin = new MessageOrigin(); + listener->RelayMessage(origin, checkable, updateMessage, true); + + /* Execute command */ + Dictionary::Ptr execMessage = new Dictionary(); + execMessage->Set("jsonrpc", "2.0"); + execMessage->Set("method", "event::ExecuteCommand"); + + /* TODO set the right params */ + Dictionary::Ptr execParams = new Dictionary(); + execMessage->Set("params", execParams); + execParams->Set("command_type", command_type); + execParams->Set("command", resolved_command); + execParams->Set("host", host->GetName()); + if (service) + execParams->Set("service", service->GetShortName()); + + /* + * FIXME? + * If the host/service object specifies the 'check_timeout' attribute, + * forward this to the remote endpoint to limit the command execution time. + */ + if (!checkable->GetCheckTimeout().IsEmpty()) + execParams->Set("check_timeout", checkable->GetCheckTimeout()); + + execParams->Set("macros", resolvedMacros); + execParams->Set("source", uuid); + execParams->Set("deadline", deadline); + + listener->SyncSendMessage(Endpoint::GetByName(resolved_endpoint), execMessage); + + /* TODO handle the wait */ + + Dictionary::Ptr result = new Dictionary(); + result->Set(checkable->GetName(), uuid); + return ApiActions::CreateResult(202, "Accepted", result); } + From 159e3d294578057f22c1ba81c8c94074e706ed04 Mon Sep 17 00:00:00 2001 From: Benjamin Groeber Date: Tue, 23 Jun 2020 11:41:15 +0200 Subject: [PATCH 008/126] Remove superfluous else clause --- lib/icinga/apiactions.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 01f3b7a59..493a8db6d 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -621,8 +621,6 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, command = "$event_command$"; } else if (command_type == "NotificationCommand") { command = "$notification_command$"; - } else { - return ApiActions::CreateResult(404, "Can't infer 'command' from command_type '" + command_type + "'"); } } else { command = HttpUtility::GetLastParameter(params, "command"); From 2bb6f9d38dac35aa8da63e3ec439f85580ccf834 Mon Sep 17 00:00:00 2001 From: Benjamin Groeber Date: Tue, 23 Jun 2020 11:41:49 +0200 Subject: [PATCH 009/126] Fix formatting of long fn --- lib/icinga/apiactions.cpp | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 493a8db6d..82ab05b74 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -587,13 +587,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, useResolvedMacros = false; } - String resolved_endpoint = MacroProcessor::ResolveMacros(endpoint, - resolvers, - checkable->GetLastCheckResult(), - nullptr, - MacroProcessor::EscapeCallback(), - resolvedMacros, - useResolvedMacros); + String resolved_endpoint = MacroProcessor::ResolveMacros( + endpoint, resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, + useResolvedMacros + ); /* Check if resolved_endpoint is not empty */ if (resolved_endpoint.IsEmpty()) @@ -627,13 +625,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, } /* Resolve command macro */ - String resolved_command = MacroProcessor::ResolveMacros(command, - resolvers, - checkable->GetLastCheckResult(), - nullptr, - MacroProcessor::EscapeCallback(), - resolvedMacros, - useResolvedMacros); + String resolved_command = MacroProcessor::ResolveMacros( + command, resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, + useResolvedMacros + ); /* Check if resolved_command is not empty */ if (resolved_command.IsEmpty()) @@ -735,4 +731,3 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, result->Set(checkable->GetName(), uuid); return ApiActions::CreateResult(202, "Accepted", result); } - From e9b6c68fd531d8e53547c5e114adb0e838fc454b Mon Sep 17 00:00:00 2001 From: Benjamin Groeber Date: Tue, 23 Jun 2020 11:44:26 +0200 Subject: [PATCH 010/126] Use tabs instead of spaces --- lib/icinga/apiactions.cpp | 284 +++++++++++++++++++------------------- 1 file changed, 142 insertions(+), 142 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 82ab05b74..153cf7f69 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -555,179 +555,179 @@ Dictionary::Ptr ApiActions::GenerateTicket(const ConfigObject::Ptr&, Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, const Dictionary::Ptr& params) { - Checkable::Ptr checkable = static_pointer_cast(object); - if (! checkable) - return ApiActions::CreateResult(404, "Can't schedule a command execution for non-existent object."); + Checkable::Ptr checkable = static_pointer_cast(object); + if (! checkable) + return ApiActions::CreateResult(404, "Can't schedule a command execution for non-existent object."); - ObjectLock oLock (checkable); + ObjectLock oLock (checkable); - Host::Ptr host; - Service::Ptr service; - tie(host, service) = GetHostService(checkable); + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); - String endpoint = "$command_endpoint$"; - if (params->Contains("endpoint")) - endpoint = params->Get("endpoint"); + String endpoint = "$command_endpoint$"; + if (params->Contains("endpoint")) + endpoint = params->Get("endpoint"); - /* Resolve endpoint macro */ - MacroProcessor::ResolverList resolvers; - if (service) - resolvers.emplace_back("service", service); - resolvers.emplace_back("host", host); - resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); + /* Resolve endpoint macro */ + MacroProcessor::ResolverList resolvers; + if (service) + resolvers.emplace_back("service", service); + resolvers.emplace_back("host", host); + resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); - Dictionary::Ptr resolvedMacros; - bool useResolvedMacros; + Dictionary::Ptr resolvedMacros; + bool useResolvedMacros; - if (params->Contains("macros")) { - resolvedMacros = HttpUtility::GetLastParameter(params, "macros"); - useResolvedMacros = true; - } else { - resolvedMacros = new Dictionary(); - useResolvedMacros = false; - } + if (params->Contains("macros")) { + resolvedMacros = HttpUtility::GetLastParameter(params, "macros"); + useResolvedMacros = true; + } else { + resolvedMacros = new Dictionary(); + useResolvedMacros = false; + } - String resolved_endpoint = MacroProcessor::ResolveMacros( - endpoint, resolvers, checkable->GetLastCheckResult(), - nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, - useResolvedMacros - ); + String resolved_endpoint = MacroProcessor::ResolveMacros( + endpoint, resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, + useResolvedMacros + ); - /* Check if resolved_endpoint is not empty */ - if (resolved_endpoint.IsEmpty()) - return ApiActions::CreateResult(400, "Endpoint must not be empty."); + /* Check if resolved_endpoint is not empty */ + if (resolved_endpoint.IsEmpty()) + return ApiActions::CreateResult(400, "Endpoint must not be empty."); - /* Check if endpoint exists */ - if (!Endpoint::GetByName(resolved_endpoint)) - return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); + /* Check if endpoint exists */ + if (!Endpoint::GetByName(resolved_endpoint)) + return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); - /* Get command_type */ - String command_type = "EventCommand"; - if (params->Contains("command_type")) - command_type = HttpUtility::GetLastParameter(params, "command_type"); + /* Get command_type */ + String command_type = "EventCommand"; + if (params->Contains("command_type")) + command_type = HttpUtility::GetLastParameter(params, "command_type"); - /* Validate command_type */ - if (command_type != "EventCommand" && command_type != "CheckCommand" && command_type != "NotificationCommand") - return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); + /* Validate command_type */ + if (command_type != "EventCommand" && command_type != "CheckCommand" && command_type != "NotificationCommand") + return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); - /* Get command */ - String command; - if (!params->Contains("command")) { - if (command_type == "CheckCommand" ) { - command = "$check_command$"; - } else if (command_type == "EventCommand") { - command = "$event_command$"; - } else if (command_type == "NotificationCommand") { - command = "$notification_command$"; - } - } else { - command = HttpUtility::GetLastParameter(params, "command"); - } + /* Get command */ + String command; + if (!params->Contains("command")) { + if (command_type == "CheckCommand" ) { + command = "$check_command$"; + } else if (command_type == "EventCommand") { + command = "$event_command$"; + } else if (command_type == "NotificationCommand") { + command = "$notification_command$"; + } + } else { + command = HttpUtility::GetLastParameter(params, "command"); + } - /* Resolve command macro */ - String resolved_command = MacroProcessor::ResolveMacros( - command, resolvers, checkable->GetLastCheckResult(), - nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, - useResolvedMacros - ); + /* Resolve command macro */ + String resolved_command = MacroProcessor::ResolveMacros( + command, resolvers, checkable->GetLastCheckResult(), + nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, + useResolvedMacros + ); - /* Check if resolved_command is not empty */ - if (resolved_command.IsEmpty()) - return ApiActions::CreateResult(400, "Command must not be empty."); + /* Check if resolved_command is not empty */ + if (resolved_command.IsEmpty()) + return ApiActions::CreateResult(400, "Command must not be empty."); - /* Check if resolved_command exists and it is of type command_type */ - if (command_type == "CheckCommand") { - if (!CheckCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); - } else if (command_type == "EventCommand") { - if (!EventCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); - } else if (command_type == "NotificationCommand") { - if (!EventCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); - } else - return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); + /* Check if resolved_command exists and it is of type command_type */ + if (command_type == "CheckCommand") { + if (!CheckCommand::GetByName(resolved_command)) + return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + } else if (command_type == "EventCommand") { + if (!EventCommand::GetByName(resolved_command)) + return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + } else if (command_type == "NotificationCommand") { + if (!EventCommand::GetByName(resolved_command)) + return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + } else + return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); - /* Get TTL param */ - double ttl = 0; /* FIXME default value? */ - if (params->Contains("ttl")) - ttl = HttpUtility::GetLastParameter(params, "ttl"); + /* Get TTL param */ + double ttl = 0; /* FIXME default value? */ + if (params->Contains("ttl")) + ttl = HttpUtility::GetLastParameter(params, "ttl"); - if (ttl <= 0) - return ApiActions::CreateResult(400, "ttl must be greater than 0"); + if (ttl <= 0) + return ApiActions::CreateResult(400, "ttl must be greater than 0"); - /* Get wait param */ - bool wait = false; - if (params->Contains("wait")) - wait = HttpUtility::GetLastParameter(params, "wait"); + /* Get wait param */ + bool wait = false; + if (params->Contains("wait")) + wait = HttpUtility::GetLastParameter(params, "wait"); - /* This generates a UUID */ - String uuid = Utility::NewUniqueID(); + /* This generates a UUID */ + String uuid = Utility::NewUniqueID(); - /* Create the deadline */ - double deadline = Utility::GetTime() + ttl; + /* Create the deadline */ + double deadline = Utility::GetTime() + ttl; - /* Update executions */ - Dictionary::Ptr pending_execution = new Dictionary(); - pending_execution->Set("pending", true); - pending_execution->Set("deadline", deadline); - Dictionary::Ptr executions = checkable->GetExecutions(); - if (!executions) - executions = new Dictionary(); - executions->Set(uuid, pending_execution); - checkable->SetExecutions(executions); + /* Update executions */ + Dictionary::Ptr pending_execution = new Dictionary(); + pending_execution->Set("pending", true); + pending_execution->Set("deadline", deadline); + Dictionary::Ptr executions = checkable->GetExecutions(); + if (!executions) + executions = new Dictionary(); + executions->Set(uuid, pending_execution); + checkable->SetExecutions(executions); - /* Broadcast the update */ - ApiListener::Ptr listener = ApiListener::GetInstance(); - if (!listener) - return ApiActions::CreateResult(404, "No ApiListener instance available."); + /* Broadcast the update */ + ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) + return ApiActions::CreateResult(404, "No ApiListener instance available."); - Dictionary::Ptr updateParams = new Dictionary(); - updateParams->Set("host", host->GetName()); - if (service) - updateParams->Set("service", service->GetShortName()); - updateParams->Set("executions", executions); + Dictionary::Ptr updateParams = new Dictionary(); + updateParams->Set("host", host->GetName()); + if (service) + updateParams->Set("service", service->GetShortName()); + updateParams->Set("executions", executions); - Dictionary::Ptr updateMessage = new Dictionary(); - updateMessage->Set("jsonrpc", "2.0"); - updateMessage->Set("method", "event::UpdateExecutions"); - updateMessage->Set("params", updateParams); + Dictionary::Ptr updateMessage = new Dictionary(); + updateMessage->Set("jsonrpc", "2.0"); + updateMessage->Set("method", "event::UpdateExecutions"); + updateMessage->Set("params", updateParams); - /* FIXME origin? */ - MessageOrigin::Ptr origin = new MessageOrigin(); - listener->RelayMessage(origin, checkable, updateMessage, true); + /* FIXME origin? */ + MessageOrigin::Ptr origin = new MessageOrigin(); + listener->RelayMessage(origin, checkable, updateMessage, true); - /* Execute command */ - Dictionary::Ptr execMessage = new Dictionary(); - execMessage->Set("jsonrpc", "2.0"); - execMessage->Set("method", "event::ExecuteCommand"); + /* Execute command */ + Dictionary::Ptr execMessage = new Dictionary(); + execMessage->Set("jsonrpc", "2.0"); + execMessage->Set("method", "event::ExecuteCommand"); - /* TODO set the right params */ - Dictionary::Ptr execParams = new Dictionary(); - execMessage->Set("params", execParams); - execParams->Set("command_type", command_type); - execParams->Set("command", resolved_command); - execParams->Set("host", host->GetName()); - if (service) - execParams->Set("service", service->GetShortName()); + /* TODO set the right params */ + Dictionary::Ptr execParams = new Dictionary(); + execMessage->Set("params", execParams); + execParams->Set("command_type", command_type); + execParams->Set("command", resolved_command); + execParams->Set("host", host->GetName()); + if (service) + execParams->Set("service", service->GetShortName()); - /* - * FIXME? - * If the host/service object specifies the 'check_timeout' attribute, - * forward this to the remote endpoint to limit the command execution time. - */ - if (!checkable->GetCheckTimeout().IsEmpty()) - execParams->Set("check_timeout", checkable->GetCheckTimeout()); + /* + * FIXME? + * If the host/service object specifies the 'check_timeout' attribute, + * forward this to the remote endpoint to limit the command execution time. + */ + if (!checkable->GetCheckTimeout().IsEmpty()) + execParams->Set("check_timeout", checkable->GetCheckTimeout()); - execParams->Set("macros", resolvedMacros); - execParams->Set("source", uuid); - execParams->Set("deadline", deadline); + execParams->Set("macros", resolvedMacros); + execParams->Set("source", uuid); + execParams->Set("deadline", deadline); - listener->SyncSendMessage(Endpoint::GetByName(resolved_endpoint), execMessage); + listener->SyncSendMessage(Endpoint::GetByName(resolved_endpoint), execMessage); - /* TODO handle the wait */ + /* TODO handle the wait */ - Dictionary::Ptr result = new Dictionary(); - result->Set(checkable->GetName(), uuid); + Dictionary::Ptr result = new Dictionary(); + result->Set(checkable->GetName(), uuid); return ApiActions::CreateResult(202, "Accepted", result); } From cc6fa66ba6e20d3605817dc9340b4c12d613615f Mon Sep 17 00:00:00 2001 From: Benjamin Groeber Date: Tue, 23 Jun 2020 11:52:22 +0200 Subject: [PATCH 011/126] Allow apiactions to return sucecss http codes different from 200 --- lib/remote/actionshandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp index 7b61cd5b9..e94debb63 100644 --- a/lib/remote/actionshandler.cpp +++ b/lib/remote/actionshandler.cpp @@ -94,7 +94,7 @@ bool ActionsHandler::HandleRequest( int statusCode = 500; for (const Dictionary::Ptr& res : results) { - if (res->Contains("code") && res->Get("code") == 200) { + if (res->Contains("code") && res->Get("code") >= 200 && res->Get("code") <= 299) { statusCode = 200; break; } From 714e75bbd1102964c05b6a006e0a5e35bb823cd3 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 26 Jun 2020 09:33:23 +0200 Subject: [PATCH 012/126] Remove wait parameter, ttl is required, remove unnecessary checks --- lib/icinga/apiactions.cpp | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 153cf7f69..0a396a656 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -579,6 +579,10 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, Dictionary::Ptr resolvedMacros; bool useResolvedMacros; + if (params->Contains("macros") && !params->Get("macros").IsObjectType()) { + return ApiActions::CreateResult(400, "macros must be a dictionary"); + } + if (params->Contains("macros")) { resolvedMacros = HttpUtility::GetLastParameter(params, "macros"); useResolvedMacros = true; @@ -593,10 +597,6 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, useResolvedMacros ); - /* Check if resolved_endpoint is not empty */ - if (resolved_endpoint.IsEmpty()) - return ApiActions::CreateResult(400, "Endpoint must not be empty."); - /* Check if endpoint exists */ if (!Endpoint::GetByName(resolved_endpoint)) return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); @@ -645,22 +645,16 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, } else if (command_type == "NotificationCommand") { if (!EventCommand::GetByName(resolved_command)) return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); - } else - return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); + } /* Get TTL param */ - double ttl = 0; /* FIXME default value? */ - if (params->Contains("ttl")) - ttl = HttpUtility::GetLastParameter(params, "ttl"); + if (!params->Contains("ttl")) + return ApiActions::CreateResult(400, "ttl is required"); + double ttl = HttpUtility::GetLastParameter(params, "ttl"); if (ttl <= 0) return ApiActions::CreateResult(400, "ttl must be greater than 0"); - /* Get wait param */ - bool wait = false; - if (params->Contains("wait")) - wait = HttpUtility::GetLastParameter(params, "wait"); - /* This generates a UUID */ String uuid = Utility::NewUniqueID(); @@ -725,8 +719,6 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, listener->SyncSendMessage(Endpoint::GetByName(resolved_endpoint), execMessage); - /* TODO handle the wait */ - Dictionary::Ptr result = new Dictionary(); result->Set(checkable->GetName(), uuid); return ApiActions::CreateResult(202, "Accepted", result); From 82444a59e2b97132052ba60fe444e3a5289b144d Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 26 Jun 2020 12:32:12 +0200 Subject: [PATCH 013/126] Fix resolve macros. Check if endpoint is local or remote --- lib/icinga/apiactions.cpp | 115 ++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 61 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 0a396a656..199eb68be 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -569,32 +569,23 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, if (params->Contains("endpoint")) endpoint = params->Get("endpoint"); - /* Resolve endpoint macro */ MacroProcessor::ResolverList resolvers; + if (params->Contains("macros")) { + if (params->Get("macros").IsObjectType()) + resolvers.emplace_back("override",HttpUtility::GetLastParameter(params, "macros")); + else + return ApiActions::CreateResult(400, "macros must be a dictionary"); + } + if (service) resolvers.emplace_back("service", service); resolvers.emplace_back("host", host); resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); - Dictionary::Ptr resolvedMacros; - bool useResolvedMacros; - - if (params->Contains("macros") && !params->Get("macros").IsObjectType()) { - return ApiActions::CreateResult(400, "macros must be a dictionary"); - } - - if (params->Contains("macros")) { - resolvedMacros = HttpUtility::GetLastParameter(params, "macros"); - useResolvedMacros = true; - } else { - resolvedMacros = new Dictionary(); - useResolvedMacros = false; - } - String resolved_endpoint = MacroProcessor::ResolveMacros( endpoint, resolvers, checkable->GetLastCheckResult(), - nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, - useResolvedMacros + nullptr, MacroProcessor::EscapeCallback(), nullptr, + false ); /* Check if endpoint exists */ @@ -626,9 +617,8 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Resolve command macro */ String resolved_command = MacroProcessor::ResolveMacros( - command, resolvers, checkable->GetLastCheckResult(), - nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, - useResolvedMacros + command, resolvers, checkable->GetLastCheckResult(), nullptr, + MacroProcessor::EscapeCallback(), nullptr, false ); /* Check if resolved_command is not empty */ @@ -671,55 +661,58 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, executions->Set(uuid, pending_execution); checkable->SetExecutions(executions); - /* Broadcast the update */ - ApiListener::Ptr listener = ApiListener::GetInstance(); - if (!listener) - return ApiActions::CreateResult(404, "No ApiListener instance available."); + Endpoint::Ptr endpointPtr = Endpoint::GetByName(resolved_endpoint); + bool local = !endpointPtr || endpointPtr == Endpoint::GetLocalEndpoint(); + if (local) { + /* TODO */ + } else { + /* Broadcast the update */ + ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) + return ApiActions::CreateResult(404, "No ApiListener instance available."); - Dictionary::Ptr updateParams = new Dictionary(); - updateParams->Set("host", host->GetName()); - if (service) - updateParams->Set("service", service->GetShortName()); - updateParams->Set("executions", executions); + Dictionary::Ptr updateParams = new Dictionary(); + updateParams->Set("host", host->GetName()); + if (service) + updateParams->Set("service", service->GetShortName()); + updateParams->Set("executions", executions); - Dictionary::Ptr updateMessage = new Dictionary(); - updateMessage->Set("jsonrpc", "2.0"); - updateMessage->Set("method", "event::UpdateExecutions"); - updateMessage->Set("params", updateParams); + Dictionary::Ptr updateMessage = new Dictionary(); + updateMessage->Set("jsonrpc", "2.0"); + updateMessage->Set("method", "event::UpdateExecutions"); + updateMessage->Set("params", updateParams); - /* FIXME origin? */ - MessageOrigin::Ptr origin = new MessageOrigin(); - listener->RelayMessage(origin, checkable, updateMessage, true); + MessageOrigin::Ptr origin = new MessageOrigin(); + listener->SyncSendMessage(endpointPtr, updateMessage); - /* Execute command */ - Dictionary::Ptr execMessage = new Dictionary(); - execMessage->Set("jsonrpc", "2.0"); - execMessage->Set("method", "event::ExecuteCommand"); + /* Execute command */ + Dictionary::Ptr execMessage = new Dictionary(); + execMessage->Set("jsonrpc", "2.0"); + execMessage->Set("method", "event::ExecuteCommand"); - /* TODO set the right params */ - Dictionary::Ptr execParams = new Dictionary(); - execMessage->Set("params", execParams); - execParams->Set("command_type", command_type); - execParams->Set("command", resolved_command); - execParams->Set("host", host->GetName()); - if (service) - execParams->Set("service", service->GetShortName()); + /* TODO set the right params */ + Dictionary::Ptr execParams = new Dictionary(); + execMessage->Set("params", execParams); + execParams->Set("command_type", command_type); + execParams->Set("command", resolved_command); + execParams->Set("host", host->GetName()); + if (service) + execParams->Set("service", service->GetShortName()); - /* - * FIXME? - * If the host/service object specifies the 'check_timeout' attribute, - * forward this to the remote endpoint to limit the command execution time. - */ - if (!checkable->GetCheckTimeout().IsEmpty()) - execParams->Set("check_timeout", checkable->GetCheckTimeout()); + /* + * If the host/service object specifies the 'check_timeout' attribute, + * forward this to the remote endpoint to limit the command execution time. + */ + if (!checkable->GetCheckTimeout().IsEmpty()) + execParams->Set("check_timeout", checkable->GetCheckTimeout()); - execParams->Set("macros", resolvedMacros); - execParams->Set("source", uuid); - execParams->Set("deadline", deadline); + execParams->Set("source", uuid); + execParams->Set("deadline", deadline); - listener->SyncSendMessage(Endpoint::GetByName(resolved_endpoint), execMessage); + listener->SyncSendMessage(endpointPtr, execMessage); + } Dictionary::Ptr result = new Dictionary(); result->Set(checkable->GetName(), uuid); return ApiActions::CreateResult(202, "Accepted", result); -} +} \ No newline at end of file From 35bb329a599da926b66b2ce48b6cc6d6484a68be Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 26 Jun 2020 12:33:44 +0200 Subject: [PATCH 014/126] Implement ClusterEvents::UpdateExecutionsAPIHandler --- lib/icinga/clusterevents.cpp | 46 ++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 29fe71702..e5c709bda 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -949,5 +949,47 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { - return "Not implemented"; -} + Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint(); + if (!endpoint) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'update executions API handler' message from '" << origin->FromClient->GetIdentity() + << "': Invalid endpoint origin (client not allowed)."; + + return Empty; + } + + Host::Ptr host = Host::GetByName(params->Get("host")); + if (!host) + return Empty; + + Checkable::Ptr checkable; + + if (params->Contains("service")) + checkable = host->GetServiceByShortName(params->Get("service")); + else + checkable = host; + + if (!checkable) + return Empty; + + ObjectLock oLock (checkable); + + if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; + return Empty; + } + + if (!params->Contains("executions")) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': missing executions."; + return Empty; + } + + Dictionary::Ptr executions = params->Get("executions"); + checkable->SetExecutions(executions); + + return Empty; +} \ No newline at end of file From 899076c77ba7d9d2c40c3277fca6aaaf7341fff4 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 26 Jun 2020 14:29:03 +0200 Subject: [PATCH 015/126] Code format --- lib/icinga/apiactions.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 199eb68be..72053c093 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -584,8 +584,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, String resolved_endpoint = MacroProcessor::ResolveMacros( endpoint, resolvers, checkable->GetLastCheckResult(), - nullptr, MacroProcessor::EscapeCallback(), nullptr, - false + nullptr, MacroProcessor::EscapeCallback(), nullptr, false ); /* Check if endpoint exists */ From fd761397ac9f5efa932fee45d4e42300fd4c5e95 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 26 Jun 2020 15:39:42 +0200 Subject: [PATCH 016/126] Unify result status strings --- lib/icinga/apiactions.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 72053c093..0f73e4a92 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -574,7 +574,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, if (params->Get("macros").IsObjectType()) resolvers.emplace_back("override",HttpUtility::GetLastParameter(params, "macros")); else - return ApiActions::CreateResult(400, "macros must be a dictionary"); + return ApiActions::CreateResult(400, "Parameter macros must be a dictionary."); } if (service) @@ -638,11 +638,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Get TTL param */ if (!params->Contains("ttl")) - return ApiActions::CreateResult(400, "ttl is required"); + return ApiActions::CreateResult(400, "Parameter ttl is required."); double ttl = HttpUtility::GetLastParameter(params, "ttl"); if (ttl <= 0) - return ApiActions::CreateResult(400, "ttl must be greater than 0"); + return ApiActions::CreateResult(400, "Parameter ttl must be greater than 0."); /* This generates a UUID */ String uuid = Utility::NewUniqueID(); From 50693d7ea9e3d029a0876924abbb1eaa0cea724f Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 26 Jun 2020 16:36:57 +0200 Subject: [PATCH 017/126] Add newline at the end of file --- lib/icinga/apiactions.cpp | 2 +- lib/icinga/clusterevents.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 0f73e4a92..1dca92f3d 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -714,4 +714,4 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, Dictionary::Ptr result = new Dictionary(); result->Set(checkable->GetName(), uuid); return ApiActions::CreateResult(202, "Accepted", result); -} \ No newline at end of file +} diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index e5c709bda..4dc6f8bc8 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -992,4 +992,4 @@ Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin checkable->SetExecutions(executions); return Empty; -} \ No newline at end of file +} From da82c1789163a8486142d7133e4072b62d7a9088 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 26 Jun 2020 16:38:40 +0200 Subject: [PATCH 018/126] Call Endpoint::GetByName only once --- lib/icinga/apiactions.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 1dca92f3d..a2002082a 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -588,7 +588,8 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, ); /* Check if endpoint exists */ - if (!Endpoint::GetByName(resolved_endpoint)) + Endpoint::Ptr endpointPtr = Endpoint::GetByName(resolved_endpoint); + if (!endpointPtr) return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); /* Get command_type */ @@ -660,8 +661,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, executions->Set(uuid, pending_execution); checkable->SetExecutions(executions); - Endpoint::Ptr endpointPtr = Endpoint::GetByName(resolved_endpoint); - bool local = !endpointPtr || endpointPtr == Endpoint::GetLocalEndpoint(); + bool local = endpointPtr == Endpoint::GetLocalEndpoint(); if (local) { /* TODO */ } else { From 3f1b51b355e765c04e0187982e2dd4bb1c86c688 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 29 Jun 2020 09:28:02 +0200 Subject: [PATCH 019/126] Use RelayMessage instead of SyncSendMessage --- lib/icinga/apiactions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index a2002082a..4779e8b46 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -682,7 +682,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, updateMessage->Set("params", updateParams); MessageOrigin::Ptr origin = new MessageOrigin(); - listener->SyncSendMessage(endpointPtr, updateMessage); + listener->RelayMessage(origin, checkable, updateMessage, true); /* Execute command */ Dictionary::Ptr execMessage = new Dictionary(); @@ -708,7 +708,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, execParams->Set("source", uuid); execParams->Set("deadline", deadline); - listener->SyncSendMessage(endpointPtr, execMessage); + listener->RelayMessage(origin, checkable, execMessage, true); } Dictionary::Ptr result = new Dictionary(); From 7077605880a16514d6ad63a4173b65aede14c322 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 29 Jun 2020 13:55:20 +0200 Subject: [PATCH 020/126] Use the right method to get the notification command --- lib/icinga/apiactions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 4779e8b46..1aa356dc0 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -633,7 +633,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, if (!EventCommand::GetByName(resolved_command)) return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); } else if (command_type == "NotificationCommand") { - if (!EventCommand::GetByName(resolved_command)) + if (!NotificationCommand::GetByName(resolved_command)) return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); } From 22dc2f23930ffd41d069177fa0967e76c7a1ccc2 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 13:43:00 +0200 Subject: [PATCH 021/126] Use SyncSendMessage for remote endpoint and call ExecuteCommandAPIHandler for local endpoint --- lib/icinga/apiactions.cpp | 45 ++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 1aa356dc0..677c00809 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -8,6 +8,7 @@ #include "icinga/checkcommand.hpp" #include "icinga/eventcommand.hpp" #include "icinga/notificationcommand.hpp" +#include "icinga/clusterevents.hpp" #include "remote/apiaction.hpp" #include "remote/apilistener.hpp" #include "remote/pkiutility.hpp" @@ -661,9 +662,28 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, executions->Set(uuid, pending_execution); checkable->SetExecutions(executions); + /* Create execution parameters */ + Dictionary::Ptr execParams = new Dictionary(); + execParams->Set("command_type", command_type); + execParams->Set("command", resolved_command); + execParams->Set("host", host->GetName()); + if (service) + execParams->Set("service", service->GetShortName()); + + /* + * If the host/service object specifies the 'check_timeout' attribute, + * forward this to the remote endpoint to limit the command execution time. + */ + if (!checkable->GetCheckTimeout().IsEmpty()) + execParams->Set("check_timeout", checkable->GetCheckTimeout()); + + execParams->Set("source", uuid); + execParams->Set("deadline", deadline); + bool local = endpointPtr == Endpoint::GetLocalEndpoint(); if (local) { - /* TODO */ + MessageOrigin::Ptr origin = new MessageOrigin(); + ClusterEvents::ExecuteCommandAPIHandler(origin, execParams); } else { /* Broadcast the update */ ApiListener::Ptr listener = ApiListener::GetInstance(); @@ -681,34 +701,15 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, updateMessage->Set("method", "event::UpdateExecutions"); updateMessage->Set("params", updateParams); - MessageOrigin::Ptr origin = new MessageOrigin(); - listener->RelayMessage(origin, checkable, updateMessage, true); + listener->SyncSendMessage(endpointPtr, updateMessage); /* Execute command */ Dictionary::Ptr execMessage = new Dictionary(); execMessage->Set("jsonrpc", "2.0"); execMessage->Set("method", "event::ExecuteCommand"); - - /* TODO set the right params */ - Dictionary::Ptr execParams = new Dictionary(); execMessage->Set("params", execParams); - execParams->Set("command_type", command_type); - execParams->Set("command", resolved_command); - execParams->Set("host", host->GetName()); - if (service) - execParams->Set("service", service->GetShortName()); - /* - * If the host/service object specifies the 'check_timeout' attribute, - * forward this to the remote endpoint to limit the command execution time. - */ - if (!checkable->GetCheckTimeout().IsEmpty()) - execParams->Set("check_timeout", checkable->GetCheckTimeout()); - - execParams->Set("source", uuid); - execParams->Set("deadline", deadline); - - listener->RelayMessage(origin, checkable, execMessage, true); + listener->SyncSendMessage(endpointPtr, execMessage); } Dictionary::Ptr result = new Dictionary(); From e4e0115c9d635be169b2eb95a790af727be59112 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 13:47:13 +0200 Subject: [PATCH 022/126] Get local endpoint if FromClient is not set and origin is local --- lib/icinga/clusterevents-check.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 96b3de146..515003661 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -77,7 +77,12 @@ void ClusterEvents::EnqueueCheck(const MessageOrigin::Ptr& origin, const Diction void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { - Endpoint::Ptr sourceEndpoint = origin->FromClient->GetEndpoint(); + Endpoint::Ptr sourceEndpoint; + if (origin->FromClient) { + sourceEndpoint = origin->FromClient->GetEndpoint(); + } else if (origin->IsLocal()){ + sourceEndpoint = Endpoint::GetLocalEndpoint(); + } if (!sourceEndpoint || (origin->FromZone && !Zone::GetLocalZone()->IsChildOf(origin->FromZone))) { Log(LogNotice, "ClusterEvents") From 94be948ff6c7cc7d6fcfebdf6f44581de28f4a9f Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 13:51:42 +0200 Subject: [PATCH 023/126] Unify how to take macros parameter --- lib/icinga/apiactions.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 677c00809..8c20a1259 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -572,8 +572,9 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::ResolverList resolvers; if (params->Contains("macros")) { - if (params->Get("macros").IsObjectType()) - resolvers.emplace_back("override",HttpUtility::GetLastParameter(params, "macros")); + Value macros = HttpUtility::GetLastParameter(params, "macros"); + if (macros.IsObjectType()) + resolvers.emplace_back("override", macros); else return ApiActions::CreateResult(400, "Parameter macros must be a dictionary."); } From 5c06256ac56bc8535e9c34a15ffd768618db385f Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 15:41:47 +0200 Subject: [PATCH 024/126] Use dynamic_pointer_cast --- lib/icinga/apiactions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 8c20a1259..31ba6e184 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -556,8 +556,8 @@ Dictionary::Ptr ApiActions::GenerateTicket(const ConfigObject::Ptr&, Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, const Dictionary::Ptr& params) { - Checkable::Ptr checkable = static_pointer_cast(object); - if (! checkable) + Checkable::Ptr checkable = dynamic_pointer_cast(object); + if (!checkable) return ApiActions::CreateResult(404, "Can't schedule a command execution for non-existent object."); ObjectLock oLock (checkable); From 8f3617d0743b6470654a4dad4f20cf5e53cba8b5 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 15:48:29 +0200 Subject: [PATCH 025/126] Fix response strings --- lib/icinga/apiactions.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 31ba6e184..6df22d82b 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -558,7 +558,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, { Checkable::Ptr checkable = dynamic_pointer_cast(object); if (!checkable) - return ApiActions::CreateResult(404, "Can't schedule a command execution for non-existent object."); + return ApiActions::CreateResult(404, "Can't start a command execution for a non-existent object."); ObjectLock oLock (checkable); @@ -630,13 +630,13 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Check if resolved_command exists and it is of type command_type */ if (command_type == "CheckCommand") { if (!CheckCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + return ApiActions::CreateResult(400, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); } else if (command_type == "EventCommand") { if (!EventCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + return ApiActions::CreateResult(400, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); } else if (command_type == "NotificationCommand") { if (!NotificationCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Command '" + resolved_command + "' is not of type '" + command_type + "'."); + return ApiActions::CreateResult(400, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); } /* Get TTL param */ From 15dac855834cd8c9b6c38f77f6b9ab6a2a1b1b5a Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 15:51:15 +0200 Subject: [PATCH 026/126] Use HttpUtility::GetLastParameter to get endpoint --- lib/icinga/apiactions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 6df22d82b..f94289af5 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -568,7 +568,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, String endpoint = "$command_endpoint$"; if (params->Contains("endpoint")) - endpoint = params->Get("endpoint"); + endpoint = HttpUtility::GetLastParameter(params, "endpoint"); MacroProcessor::ResolverList resolvers; if (params->Contains("macros")) { From 626080f6103689d4c9fc3c3ce14effeb97b08f07 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 15:53:25 +0200 Subject: [PATCH 027/126] Remove unnecessary check --- lib/icinga/apiactions.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index f94289af5..b341def92 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -623,10 +623,6 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::EscapeCallback(), nullptr, false ); - /* Check if resolved_command is not empty */ - if (resolved_command.IsEmpty()) - return ApiActions::CreateResult(400, "Command must not be empty."); - /* Check if resolved_command exists and it is of type command_type */ if (command_type == "CheckCommand") { if (!CheckCommand::GetByName(resolved_command)) From cb32786880b16d2cfc82c363d65afccd134b70ec Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 30 Jun 2020 17:50:08 +0200 Subject: [PATCH 028/126] Use RelayMessage to broadcast the updated executions --- lib/icinga/apiactions.cpp | 40 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index b341def92..67ae389cd 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -659,6 +659,25 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, executions->Set(uuid, pending_execution); checkable->SetExecutions(executions); + /* Broadcast the update */ + ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) + return ApiActions::CreateResult(404, "No ApiListener instance available."); + + Dictionary::Ptr updateParams = new Dictionary(); + updateParams->Set("host", host->GetName()); + if (service) + updateParams->Set("service", service->GetShortName()); + updateParams->Set("executions", executions); + + Dictionary::Ptr updateMessage = new Dictionary(); + updateMessage->Set("jsonrpc", "2.0"); + updateMessage->Set("method", "event::UpdateExecutions"); + updateMessage->Set("params", updateParams); + + MessageOrigin::Ptr origin = new MessageOrigin(); + listener->RelayMessage(origin, checkable, updateMessage, true); + /* Create execution parameters */ Dictionary::Ptr execParams = new Dictionary(); execParams->Set("command_type", command_type); @@ -677,30 +696,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, execParams->Set("source", uuid); execParams->Set("deadline", deadline); + /* Execute command */ bool local = endpointPtr == Endpoint::GetLocalEndpoint(); if (local) { - MessageOrigin::Ptr origin = new MessageOrigin(); ClusterEvents::ExecuteCommandAPIHandler(origin, execParams); } else { - /* Broadcast the update */ - ApiListener::Ptr listener = ApiListener::GetInstance(); - if (!listener) - return ApiActions::CreateResult(404, "No ApiListener instance available."); - - Dictionary::Ptr updateParams = new Dictionary(); - updateParams->Set("host", host->GetName()); - if (service) - updateParams->Set("service", service->GetShortName()); - updateParams->Set("executions", executions); - - Dictionary::Ptr updateMessage = new Dictionary(); - updateMessage->Set("jsonrpc", "2.0"); - updateMessage->Set("method", "event::UpdateExecutions"); - updateMessage->Set("params", updateParams); - - listener->SyncSendMessage(endpointPtr, updateMessage); - - /* Execute command */ Dictionary::Ptr execMessage = new Dictionary(); execMessage->Set("jsonrpc", "2.0"); execMessage->Set("method", "event::ExecuteCommand"); From edbd8688fbca469663ba8c4b10d0bfc3ab790a36 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 1 Jul 2020 08:19:35 +0200 Subject: [PATCH 029/126] Update status code --- lib/icinga/apiactions.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 67ae389cd..432fae212 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -626,13 +626,13 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Check if resolved_command exists and it is of type command_type */ if (command_type == "CheckCommand") { if (!CheckCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); + return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); } else if (command_type == "EventCommand") { if (!EventCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); + return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); } else if (command_type == "NotificationCommand") { if (!NotificationCommand::GetByName(resolved_command)) - return ApiActions::CreateResult(400, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); + return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); } /* Get TTL param */ From ee2f7bafdddbabcdbe999ef7b8332f1e47a3fa01 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 1 Jul 2020 14:27:00 +0200 Subject: [PATCH 030/126] Move some checks. Add macros parameters to ExecuteCommand message --- lib/icinga/apiactions.cpp | 57 ++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 432fae212..4f0a6f704 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -556,10 +556,31 @@ Dictionary::Ptr ApiActions::GenerateTicket(const ConfigObject::Ptr&, Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, const Dictionary::Ptr& params) { + ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) + BOOST_THROW_EXCEPTION(std::invalid_argument("No ApiListener instance configured.")); + + /* Get command_type */ + String command_type = "EventCommand"; + if (params->Contains("command_type")) + command_type = HttpUtility::GetLastParameter(params, "command_type"); + + /* Validate command_type */ + if (command_type != "EventCommand" && command_type != "CheckCommand" && command_type != "NotificationCommand") + return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); + Checkable::Ptr checkable = dynamic_pointer_cast(object); if (!checkable) return ApiActions::CreateResult(404, "Can't start a command execution for a non-existent object."); + /* Get TTL param */ + if (!params->Contains("ttl")) + return ApiActions::CreateResult(400, "Parameter ttl is required."); + + double ttl = HttpUtility::GetLastParameter(params, "ttl"); + if (ttl <= 0) + return ApiActions::CreateResult(400, "Parameter ttl must be greater than 0."); + ObjectLock oLock (checkable); Host::Ptr host; @@ -571,10 +592,13 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, endpoint = HttpUtility::GetLastParameter(params, "endpoint"); MacroProcessor::ResolverList resolvers; + Dictionary::Ptr macros; if (params->Contains("macros")) { - Value macros = HttpUtility::GetLastParameter(params, "macros"); - if (macros.IsObjectType()) - resolvers.emplace_back("override", macros); + Value vmacros = HttpUtility::GetLastParameter(params, "macros"); + if (vmacros.IsObjectType()) { + resolvers.emplace_back("override", vmacros); + macros = vmacros; + } else return ApiActions::CreateResult(400, "Parameter macros must be a dictionary."); } @@ -594,15 +618,6 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, if (!endpointPtr) return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); - /* Get command_type */ - String command_type = "EventCommand"; - if (params->Contains("command_type")) - command_type = HttpUtility::GetLastParameter(params, "command_type"); - - /* Validate command_type */ - if (command_type != "EventCommand" && command_type != "CheckCommand" && command_type != "NotificationCommand") - return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); - /* Get command */ String command; if (!params->Contains("command")) { @@ -635,14 +650,6 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); } - /* Get TTL param */ - if (!params->Contains("ttl")) - return ApiActions::CreateResult(400, "Parameter ttl is required."); - - double ttl = HttpUtility::GetLastParameter(params, "ttl"); - if (ttl <= 0) - return ApiActions::CreateResult(400, "Parameter ttl must be greater than 0."); - /* This generates a UUID */ String uuid = Utility::NewUniqueID(); @@ -660,15 +667,13 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, checkable->SetExecutions(executions); /* Broadcast the update */ - ApiListener::Ptr listener = ApiListener::GetInstance(); - if (!listener) - return ApiActions::CreateResult(404, "No ApiListener instance available."); - + Dictionary::Ptr executionsToBroadcast = new Dictionary(); + executionsToBroadcast->Set(uuid, pending_execution); Dictionary::Ptr updateParams = new Dictionary(); updateParams->Set("host", host->GetName()); if (service) updateParams->Set("service", service->GetShortName()); - updateParams->Set("executions", executions); + updateParams->Set("executions", executionsToBroadcast); Dictionary::Ptr updateMessage = new Dictionary(); updateMessage->Set("jsonrpc", "2.0"); @@ -695,6 +700,8 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, execParams->Set("source", uuid); execParams->Set("deadline", deadline); + if (macros) + execParams->Set("macros", macros); /* Execute command */ bool local = endpointPtr == Endpoint::GetLocalEndpoint(); From f2e9aee158ffa274e007b80410ded3c7c4b4252a Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Thu, 2 Jul 2020 10:31:24 +0200 Subject: [PATCH 031/126] Update execMacros --- lib/icinga/apiactions.cpp | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 4f0a6f704..72e612f68 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -592,12 +592,10 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, endpoint = HttpUtility::GetLastParameter(params, "endpoint"); MacroProcessor::ResolverList resolvers; - Dictionary::Ptr macros; if (params->Contains("macros")) { - Value vmacros = HttpUtility::GetLastParameter(params, "macros"); - if (vmacros.IsObjectType()) { - resolvers.emplace_back("override", vmacros); - macros = vmacros; + Value macros = HttpUtility::GetLastParameter(params, "macros"); + if (macros.IsObjectType()) { + resolvers.emplace_back("override", macros); } else return ApiActions::CreateResult(400, "Parameter macros must be a dictionary."); @@ -683,6 +681,25 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MessageOrigin::Ptr origin = new MessageOrigin(); listener->RelayMessage(origin, checkable, updateMessage, true); + double scheduled_start = checkable->GetNextCheck(); + double before_check = Utility::GetTime(); + + CheckResult::Ptr cr = new CheckResult(); + cr->SetScheduleStart(scheduled_start); + cr->SetExecutionStart(before_check); + + Dictionary::Ptr execMacros = new Dictionary(); + if (command_type == "CheckCommand") { + CheckCommand::Ptr cmd = CheckCommand::GetByName(resolved_command); + cmd->Execute(checkable, cr, execMacros, false); + } else if (command_type == "EventCommand") { + EventCommand::Ptr cmd = EventCommand::GetByName(resolved_command); + cmd->Execute(checkable, execMacros, false); + } else if (command_type == "NotificationCommand") { + /* TODO */ + return ApiActions::CreateResult(501, "Not implementd."); + } + /* Create execution parameters */ Dictionary::Ptr execParams = new Dictionary(); execParams->Set("command_type", command_type); @@ -700,8 +717,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, execParams->Set("source", uuid); execParams->Set("deadline", deadline); - if (macros) - execParams->Set("macros", macros); + execParams->Set("macros", execMacros); /* Execute command */ bool local = endpointPtr == Endpoint::GetLocalEndpoint(); From 6102cf0625541de5cd19b5a9137def1127bdf194 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Thu, 2 Jul 2020 12:20:16 +0200 Subject: [PATCH 032/126] Call notificationCommand->Execute --- lib/icinga/apiactions.cpp | 78 ++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 72e612f68..5f2534fd2 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -636,16 +636,69 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::EscapeCallback(), nullptr, false ); + double scheduled_start = checkable->GetNextCheck(); + double before_check = Utility::GetTime(); + + CheckResult::Ptr cr = new CheckResult(); + cr->SetScheduleStart(scheduled_start); + cr->SetExecutionStart(before_check); + /* Check if resolved_command exists and it is of type command_type */ + Dictionary::Ptr execMacros = new Dictionary(); if (command_type == "CheckCommand") { - if (!CheckCommand::GetByName(resolved_command)) + CheckCommand::Ptr cmd = CheckCommand::GetByName(resolved_command); + if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); + else + cmd->Execute(checkable, cr, execMacros, false); } else if (command_type == "EventCommand") { - if (!EventCommand::GetByName(resolved_command)) + EventCommand::Ptr cmd = EventCommand::GetByName(resolved_command); + if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); + else + cmd->Execute(checkable, execMacros, false); } else if (command_type == "NotificationCommand") { - if (!NotificationCommand::GetByName(resolved_command)) + NotificationCommand::Ptr cmd = NotificationCommand::GetByName(resolved_command); + if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); + else { + /* Get user */ + String user_string = ""; + if (params->Contains("user")) + user_string = HttpUtility::GetLastParameter(params, "user"); + + /* Resolve user macro */ + String resolved_user = MacroProcessor::ResolveMacros( + user_string, resolvers, checkable->GetLastCheckResult(), nullptr, + MacroProcessor::EscapeCallback(), nullptr, false + ); + + User::Ptr user = User::GetByName(resolved_user); + if (!user) + return ApiActions::CreateResult(404, "Can't find a valid user for '" + resolved_user + "'."); + + /* Get notification */ + String notification_string = ""; + if (params->Contains("notification")) + notification_string = HttpUtility::GetLastParameter(params, "notification"); + + /* Resolve notification macro */ + String resolved_notification = MacroProcessor::ResolveMacros( + notification_string, resolvers, checkable->GetLastCheckResult(), nullptr, + MacroProcessor::EscapeCallback(), nullptr, false + ); + + Notification::Ptr notification = Notification::GetByName(resolved_notification); + if (!user) + return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'."); + + /* Get author */ + String author = ""; + if (params->Contains("author")) + author = HttpUtility::GetLastParameter(params, "author"); + + cmd->Execute(notification, user, cr, NotificationType::NotificationCustom, author, "", execMacros, false); + } } /* This generates a UUID */ @@ -681,25 +734,6 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MessageOrigin::Ptr origin = new MessageOrigin(); listener->RelayMessage(origin, checkable, updateMessage, true); - double scheduled_start = checkable->GetNextCheck(); - double before_check = Utility::GetTime(); - - CheckResult::Ptr cr = new CheckResult(); - cr->SetScheduleStart(scheduled_start); - cr->SetExecutionStart(before_check); - - Dictionary::Ptr execMacros = new Dictionary(); - if (command_type == "CheckCommand") { - CheckCommand::Ptr cmd = CheckCommand::GetByName(resolved_command); - cmd->Execute(checkable, cr, execMacros, false); - } else if (command_type == "EventCommand") { - EventCommand::Ptr cmd = EventCommand::GetByName(resolved_command); - cmd->Execute(checkable, execMacros, false); - } else if (command_type == "NotificationCommand") { - /* TODO */ - return ApiActions::CreateResult(501, "Not implementd."); - } - /* Create execution parameters */ Dictionary::Ptr execParams = new Dictionary(); execParams->Set("command_type", command_type); From 41b379523e3a12badd12acdcfcd985d5b7debb91 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 08:38:39 +0200 Subject: [PATCH 033/126] Fix indentation --- lib/icinga/apiactions.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 5f2534fd2..1698133f9 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -669,8 +669,8 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Resolve user macro */ String resolved_user = MacroProcessor::ResolveMacros( - user_string, resolvers, checkable->GetLastCheckResult(), nullptr, - MacroProcessor::EscapeCallback(), nullptr, false + user_string, resolvers, checkable->GetLastCheckResult(), nullptr, + MacroProcessor::EscapeCallback(), nullptr, false ); User::Ptr user = User::GetByName(resolved_user); @@ -684,8 +684,8 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Resolve notification macro */ String resolved_notification = MacroProcessor::ResolveMacros( - notification_string, resolvers, checkable->GetLastCheckResult(), nullptr, - MacroProcessor::EscapeCallback(), nullptr, false + notification_string, resolvers, checkable->GetLastCheckResult(), nullptr, + MacroProcessor::EscapeCallback(), nullptr, false ); Notification::Ptr notification = Notification::GetByName(resolved_notification); From 670835fd9a21272596128b12853722ab571c5849 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 08:43:50 +0200 Subject: [PATCH 034/126] Get last CheckResult --- lib/icinga/apiactions.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 1698133f9..97f93ca5a 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -636,12 +636,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::EscapeCallback(), nullptr, false ); - double scheduled_start = checkable->GetNextCheck(); - double before_check = Utility::GetTime(); - - CheckResult::Ptr cr = new CheckResult(); - cr->SetScheduleStart(scheduled_start); - cr->SetExecutionStart(before_check); + CheckResult::Ptr cr = checkable->GetLastCheckResult(); /* Check if resolved_command exists and it is of type command_type */ Dictionary::Ptr execMacros = new Dictionary(); From 27a6fd6b40afe0e1c5d496e37d0dd419f7585924 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 10:16:23 +0200 Subject: [PATCH 035/126] Get API user from ActionsHandler --- lib/icinga/apiactions.cpp | 8 +++++--- lib/remote/actionshandler.cpp | 8 ++++++++ lib/remote/actionshandler.hpp | 2 ++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 97f93ca5a..f1661bfc3 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -15,6 +15,7 @@ #include "remote/httputility.hpp" #include "base/utility.hpp" #include "base/convert.hpp" +#include "remote/actionshandler.hpp" #include using namespace icinga; @@ -688,9 +689,10 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'."); /* Get author */ - String author = ""; - if (params->Contains("author")) - author = HttpUtility::GetLastParameter(params, "author"); + if (!authenticatedApiUser) + return ApiActions::CreateResult(401, "Can't find API user"); + + String author = authenticatedApiUser->GetName(); cmd->Execute(notification, user, cr, NotificationType::NotificationCustom, author, "", execMacros, false); } diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp index e94debb63..8668b8ea6 100644 --- a/lib/remote/actionshandler.cpp +++ b/lib/remote/actionshandler.cpp @@ -4,12 +4,15 @@ #include "remote/httputility.hpp" #include "remote/filterutility.hpp" #include "remote/apiaction.hpp" +#include "base/defer.hpp" #include "base/exception.hpp" #include "base/logger.hpp" #include using namespace icinga; +thread_local ApiUser::Ptr authenticatedApiUser; + REGISTER_URLHANDLER("/v1/actions", ActionsHandler); bool ActionsHandler::HandleRequest( @@ -71,6 +74,11 @@ bool ActionsHandler::HandleRequest( bool verbose = false; + authenticatedApiUser = user; + Defer a ([&]() { + authenticatedApiUser = nullptr; + }); + if (params) verbose = HttpUtility::GetLastParameter(params, "verbose"); diff --git a/lib/remote/actionshandler.hpp b/lib/remote/actionshandler.hpp index c2465cf7e..a7b70ff0d 100644 --- a/lib/remote/actionshandler.hpp +++ b/lib/remote/actionshandler.hpp @@ -5,6 +5,8 @@ #include "remote/httphandler.hpp" +extern thread_local icinga::ApiUser::Ptr authenticatedApiUser; + namespace icinga { From 08c6e9bc42833824510740a2732e080c42317b9f Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 11:00:40 +0200 Subject: [PATCH 036/126] Make authenticatedApiUser a static property of ActionsHandler --- lib/icinga/apiactions.cpp | 9 ++++----- lib/remote/actionshandler.cpp | 6 +++--- lib/remote/actionshandler.hpp | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index f1661bfc3..20306a74a 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -689,12 +689,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'."); /* Get author */ - if (!authenticatedApiUser) - return ApiActions::CreateResult(401, "Can't find API user"); + if (!ActionsHandler::authenticatedApiUser) + BOOST_THROW_EXCEPTION(std::invalid_argument("Can't find API user.")); - String author = authenticatedApiUser->GetName(); - - cmd->Execute(notification, user, cr, NotificationType::NotificationCustom, author, "", execMacros, false); + cmd->Execute(notification, user, cr, NotificationType::NotificationCustom, + ActionsHandler::authenticatedApiUser->GetName(), "", execMacros, false); } } diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp index 8668b8ea6..175e38a3a 100644 --- a/lib/remote/actionshandler.cpp +++ b/lib/remote/actionshandler.cpp @@ -11,7 +11,7 @@ using namespace icinga; -thread_local ApiUser::Ptr authenticatedApiUser; +thread_local ApiUser::Ptr ActionsHandler::authenticatedApiUser; REGISTER_URLHANDLER("/v1/actions", ActionsHandler); @@ -74,9 +74,9 @@ bool ActionsHandler::HandleRequest( bool verbose = false; - authenticatedApiUser = user; + ActionsHandler::authenticatedApiUser = user; Defer a ([&]() { - authenticatedApiUser = nullptr; + ActionsHandler::authenticatedApiUser = nullptr; }); if (params) diff --git a/lib/remote/actionshandler.hpp b/lib/remote/actionshandler.hpp index a7b70ff0d..e99b40cd9 100644 --- a/lib/remote/actionshandler.hpp +++ b/lib/remote/actionshandler.hpp @@ -5,8 +5,6 @@ #include "remote/httphandler.hpp" -extern thread_local icinga::ApiUser::Ptr authenticatedApiUser; - namespace icinga { @@ -15,6 +13,8 @@ class ActionsHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ActionsHandler); + static thread_local ApiUser::Ptr authenticatedApiUser; + bool HandleRequest( AsioTlsStream& stream, const ApiUser::Ptr& user, From b07471d803fbdf46253c6239fcb3067041431ce3 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 11:17:36 +0200 Subject: [PATCH 037/126] AuthenticatedApiUser name refactor --- lib/icinga/apiactions.cpp | 4 ++-- lib/remote/actionshandler.cpp | 8 ++++---- lib/remote/actionshandler.hpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 20306a74a..ff8b68a32 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -689,11 +689,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'."); /* Get author */ - if (!ActionsHandler::authenticatedApiUser) + if (!ActionsHandler::AuthenticatedApiUser) BOOST_THROW_EXCEPTION(std::invalid_argument("Can't find API user.")); cmd->Execute(notification, user, cr, NotificationType::NotificationCustom, - ActionsHandler::authenticatedApiUser->GetName(), "", execMacros, false); + ActionsHandler::AuthenticatedApiUser->GetName(), "", execMacros, false); } } diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp index 175e38a3a..f8a823632 100644 --- a/lib/remote/actionshandler.cpp +++ b/lib/remote/actionshandler.cpp @@ -11,7 +11,7 @@ using namespace icinga; -thread_local ApiUser::Ptr ActionsHandler::authenticatedApiUser; +thread_local ApiUser::Ptr ActionsHandler::AuthenticatedApiUser; REGISTER_URLHANDLER("/v1/actions", ActionsHandler); @@ -74,9 +74,9 @@ bool ActionsHandler::HandleRequest( bool verbose = false; - ActionsHandler::authenticatedApiUser = user; - Defer a ([&]() { - ActionsHandler::authenticatedApiUser = nullptr; + ActionsHandler::AuthenticatedApiUser = user; + Defer a ([]() { + ActionsHandler::AuthenticatedApiUser = nullptr; }); if (params) diff --git a/lib/remote/actionshandler.hpp b/lib/remote/actionshandler.hpp index e99b40cd9..ca662caba 100644 --- a/lib/remote/actionshandler.hpp +++ b/lib/remote/actionshandler.hpp @@ -13,7 +13,7 @@ class ActionsHandler final : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ActionsHandler); - static thread_local ApiUser::Ptr authenticatedApiUser; + static thread_local ApiUser::Ptr AuthenticatedApiUser; bool HandleRequest( AsioTlsStream& stream, From be2701bc8acf75f8210b7ae885704337090d723a Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 15:13:51 +0200 Subject: [PATCH 038/126] Add overrideMacros property to MacroResolver --- lib/icinga/apiactions.cpp | 7 +++++++ lib/icinga/macroprocessor.cpp | 2 ++ lib/icinga/macroresolver.hpp | 2 ++ lib/methods/pluginchecktask.cpp | 3 +++ 4 files changed, 14 insertions(+) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index ff8b68a32..ef3690847 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -15,6 +15,7 @@ #include "remote/httputility.hpp" #include "base/utility.hpp" #include "base/convert.hpp" +#include "base/defer.hpp" #include "remote/actionshandler.hpp" #include @@ -641,6 +642,12 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Check if resolved_command exists and it is of type command_type */ Dictionary::Ptr execMacros = new Dictionary(); + + MacroResolver::overrideMacros = execMacros; + Defer o ([]() { + MacroResolver::overrideMacros = nullptr; + }); + if (command_type == "CheckCommand") { CheckCommand::Ptr cmd = CheckCommand::GetByName(resolved_command); if (!cmd) diff --git a/lib/icinga/macroprocessor.cpp b/lib/icinga/macroprocessor.cpp index c92ab19d4..8fb014efc 100644 --- a/lib/icinga/macroprocessor.cpp +++ b/lib/icinga/macroprocessor.cpp @@ -15,6 +15,8 @@ using namespace icinga; +thread_local Dictionary::Ptr MacroResolver::overrideMacros; + Value MacroProcessor::ResolveMacros(const Value& str, const ResolverList& resolvers, const CheckResult::Ptr& cr, String *missingMacro, const MacroProcessor::EscapeCallback& escapeFn, const Dictionary::Ptr& resolvedMacros, diff --git a/lib/icinga/macroresolver.hpp b/lib/icinga/macroresolver.hpp index ea29ce1e4..65dc69679 100644 --- a/lib/icinga/macroresolver.hpp +++ b/lib/icinga/macroresolver.hpp @@ -21,6 +21,8 @@ class MacroResolver public: DECLARE_PTR_TYPEDEFS(MacroResolver); + static thread_local Dictionary::Ptr overrideMacros; + virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, Value *result) const = 0; }; diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp index 212d7447c..68458d3d9 100644 --- a/lib/methods/pluginchecktask.cpp +++ b/lib/methods/pluginchecktask.cpp @@ -29,6 +29,9 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes 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); From a33560548c3506a47caa3fa6b13477eade4dcb63 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 15:53:51 +0200 Subject: [PATCH 039/126] Refactor MacroResolver::OverrideMacros --- lib/icinga/apiactions.cpp | 4 ++-- lib/icinga/macroprocessor.cpp | 2 +- lib/icinga/macroresolver.hpp | 2 +- lib/methods/pluginchecktask.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index ef3690847..4cd3173e8 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -643,9 +643,9 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Check if resolved_command exists and it is of type command_type */ Dictionary::Ptr execMacros = new Dictionary(); - MacroResolver::overrideMacros = execMacros; + MacroResolver::OverrideMacros = execMacros; Defer o ([]() { - MacroResolver::overrideMacros = nullptr; + MacroResolver::OverrideMacros = nullptr; }); if (command_type == "CheckCommand") { diff --git a/lib/icinga/macroprocessor.cpp b/lib/icinga/macroprocessor.cpp index 8fb014efc..67e7b7e78 100644 --- a/lib/icinga/macroprocessor.cpp +++ b/lib/icinga/macroprocessor.cpp @@ -15,7 +15,7 @@ using namespace icinga; -thread_local Dictionary::Ptr MacroResolver::overrideMacros; +thread_local Dictionary::Ptr MacroResolver::OverrideMacros; Value MacroProcessor::ResolveMacros(const Value& str, const ResolverList& resolvers, const CheckResult::Ptr& cr, String *missingMacro, diff --git a/lib/icinga/macroresolver.hpp b/lib/icinga/macroresolver.hpp index 65dc69679..62cd41d3c 100644 --- a/lib/icinga/macroresolver.hpp +++ b/lib/icinga/macroresolver.hpp @@ -21,7 +21,7 @@ class MacroResolver public: DECLARE_PTR_TYPEDEFS(MacroResolver); - static thread_local Dictionary::Ptr overrideMacros; + static thread_local Dictionary::Ptr OverrideMacros; virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, Value *result) const = 0; }; diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp index 68458d3d9..70523b49f 100644 --- a/lib/methods/pluginchecktask.cpp +++ b/lib/methods/pluginchecktask.cpp @@ -29,8 +29,8 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes tie(host, service) = GetHostService(checkable); MacroProcessor::ResolverList resolvers; - if (MacroResolver::overrideMacros) - resolvers.emplace_back("override", MacroResolver::overrideMacros); + if (MacroResolver::OverrideMacros) + resolvers.emplace_back("override", MacroResolver::OverrideMacros); if (service) resolvers.emplace_back("service", service); From 6a5deaf7d4d4e53e6f9e5af7e11dc5d2180cfe55 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 3 Jul 2020 16:36:58 +0200 Subject: [PATCH 040/126] Fix update executions. Fix code indentation --- lib/icinga/clusterevents.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 4dc6f8bc8..5c65d5720 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -952,8 +952,8 @@ Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint(); if (!endpoint) { Log(LogNotice, "ClusterEvents") - << "Discarding 'update executions API handler' message from '" << origin->FromClient->GetIdentity() - << "': Invalid endpoint origin (client not allowed)."; + << "Discarding 'update executions API handler' message from '" << origin->FromClient->GetIdentity() + << "': Invalid endpoint origin (client not allowed)."; return Empty; } @@ -976,19 +976,14 @@ Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) { Log(LogNotice, "ClusterEvents") - << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() - << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; return Empty; } - if (!params->Contains("executions")) { - Log(LogNotice, "ClusterEvents") - << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() - << "' from '" << origin->FromClient->GetIdentity() << "': missing executions."; - return Empty; - } - - Dictionary::Ptr executions = params->Get("executions"); + Dictionary::Ptr executions = checkable->GetExecutions(); + Dictionary::Ptr newExecutions = params->Get("executions"); + newExecutions->CopyTo(executions); checkable->SetExecutions(executions); return Empty; From 33926ba983a8897e3187cd194085df8da7ebeb5b Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 6 Jul 2020 16:21:05 +0200 Subject: [PATCH 041/126] Check if executions are not null --- lib/icinga/clusterevents.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 5c65d5720..4146d2852 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -982,6 +982,8 @@ Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin } Dictionary::Ptr executions = checkable->GetExecutions(); + if (!executions) + executions = new Dictionary(); Dictionary::Ptr newExecutions = params->Get("executions"); newExecutions->CopyTo(executions); checkable->SetExecutions(executions); From 704aa0f685e6cd8a4644ae9a62289656d987cf8e Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 6 Jul 2020 17:30:18 +0200 Subject: [PATCH 042/126] Change result --- lib/icinga/apiactions.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 4cd3173e8..f3269d6f9 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -770,6 +770,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, } Dictionary::Ptr result = new Dictionary(); - result->Set(checkable->GetName(), uuid); + result->Set("checkable", checkable->GetName()); + result->Set("execution", uuid); return ApiActions::CreateResult(202, "Accepted", result); } From c304b59990b44cf0500721ef523228e3f44027cd Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 7 Jul 2020 14:23:36 +0200 Subject: [PATCH 043/126] Add GetSingleObjectByNameUsingPermissions function --- lib/icinga/apiactions.cpp | 48 +++++++++++++++++++++++++++++---------- lib/icinga/apiactions.hpp | 2 ++ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index f3269d6f9..9f65186f2 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -11,6 +11,7 @@ #include "icinga/clusterevents.hpp" #include "remote/apiaction.hpp" #include "remote/apilistener.hpp" +#include "remote/filterutility.hpp" #include "remote/pkiutility.hpp" #include "remote/httputility.hpp" #include "base/utility.hpp" @@ -555,6 +556,30 @@ Dictionary::Ptr ApiActions::GenerateTicket(const ConfigObject::Ptr&, + cn + "'.", additional); } +Value ApiActions::GetSingleObjectByNameUsingPermissions(String type, String value, ApiUser::Ptr user) +{ + Dictionary::Ptr queryParams = new Dictionary(); + queryParams->Set("type", type); + queryParams->Set(type.ToLower(), value); + + QueryDescription qd; + qd.Types.insert(type); + qd.Permission = "objects/query/" + type; + + std::vector objs; + try { + objs = FilterUtility::GetFilterTargets(qd, queryParams, ActionsHandler::AuthenticatedApiUser); + } catch (const std::exception& ex) { + Log(LogCritical, "ApiActions") << DiagnosticInformation(ex); + return nullptr; + } + + if (objs.empty()) + return nullptr; + + return objs.at(0); +}; + Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, const Dictionary::Ptr& params) { @@ -613,8 +638,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, nullptr, MacroProcessor::EscapeCallback(), nullptr, false ); - /* Check if endpoint exists */ - Endpoint::Ptr endpointPtr = Endpoint::GetByName(resolved_endpoint); + if (!ActionsHandler::AuthenticatedApiUser) + BOOST_THROW_EXCEPTION(std::invalid_argument("Can't find API user.")); + + /* Get endpoint */ + Endpoint::Ptr endpointPtr = ApiActions::GetSingleObjectByNameUsingPermissions(Endpoint::GetTypeName(), resolved_endpoint, ActionsHandler::AuthenticatedApiUser); if (!endpointPtr) return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); @@ -649,19 +677,19 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, }); if (command_type == "CheckCommand") { - CheckCommand::Ptr cmd = CheckCommand::GetByName(resolved_command); + CheckCommand::Ptr cmd = ApiActions::GetSingleObjectByNameUsingPermissions(CheckCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else cmd->Execute(checkable, cr, execMacros, false); } else if (command_type == "EventCommand") { - EventCommand::Ptr cmd = EventCommand::GetByName(resolved_command); + EventCommand::Ptr cmd = ApiActions::GetSingleObjectByNameUsingPermissions(EventCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else cmd->Execute(checkable, execMacros, false); } else if (command_type == "NotificationCommand") { - NotificationCommand::Ptr cmd = NotificationCommand::GetByName(resolved_command); + NotificationCommand::Ptr cmd = ApiActions::GetSingleObjectByNameUsingPermissions(NotificationCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else { @@ -676,7 +704,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::EscapeCallback(), nullptr, false ); - User::Ptr user = User::GetByName(resolved_user); + User::Ptr user = ApiActions::GetSingleObjectByNameUsingPermissions(User::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!user) return ApiActions::CreateResult(404, "Can't find a valid user for '" + resolved_user + "'."); @@ -691,14 +719,10 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::EscapeCallback(), nullptr, false ); - Notification::Ptr notification = Notification::GetByName(resolved_notification); - if (!user) + Notification::Ptr notification = ApiActions::GetSingleObjectByNameUsingPermissions(Notification::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); + if (!notification) return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'."); - /* Get author */ - if (!ActionsHandler::AuthenticatedApiUser) - BOOST_THROW_EXCEPTION(std::invalid_argument("Can't find API user.")); - cmd->Execute(notification, user, cr, NotificationType::NotificationCustom, ActionsHandler::AuthenticatedApiUser->GetName(), "", execMacros, false); } diff --git a/lib/icinga/apiactions.hpp b/lib/icinga/apiactions.hpp index 46e03c02f..c588c7ddc 100644 --- a/lib/icinga/apiactions.hpp +++ b/lib/icinga/apiactions.hpp @@ -6,6 +6,7 @@ #include "icinga/i2-icinga.hpp" #include "base/configobject.hpp" #include "base/dictionary.hpp" +#include "remote/apiuser.hpp" namespace icinga { @@ -33,6 +34,7 @@ public: private: static Dictionary::Ptr CreateResult(int code, const String& status, const Dictionary::Ptr& additional = nullptr); + static Value GetSingleObjectByNameUsingPermissions(String type, String value, ApiUser::Ptr user); }; } From e8c8a7d9ddc631ede9dce18e83514238339e3475 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 7 Jul 2020 15:45:58 +0200 Subject: [PATCH 044/126] Refactor GetSingleObjectByNameUsingPermissions --- lib/icinga/apiactions.cpp | 20 ++++++++++---------- lib/icinga/apiactions.hpp | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 9f65186f2..e1d25cefd 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -556,11 +556,11 @@ Dictionary::Ptr ApiActions::GenerateTicket(const ConfigObject::Ptr&, + cn + "'.", additional); } -Value ApiActions::GetSingleObjectByNameUsingPermissions(String type, String value, ApiUser::Ptr user) +Value ApiActions::GetSingleObjectByNameUsingPermissions(const String& type, const String& objectName, const ApiUser::Ptr& user) { Dictionary::Ptr queryParams = new Dictionary(); queryParams->Set("type", type); - queryParams->Set(type.ToLower(), value); + queryParams->Set(type.ToLower(), objectName); QueryDescription qd; qd.Types.insert(type); @@ -568,9 +568,9 @@ Value ApiActions::GetSingleObjectByNameUsingPermissions(String type, String valu std::vector objs; try { - objs = FilterUtility::GetFilterTargets(qd, queryParams, ActionsHandler::AuthenticatedApiUser); + objs = FilterUtility::GetFilterTargets(qd, queryParams, user); } catch (const std::exception& ex) { - Log(LogCritical, "ApiActions") << DiagnosticInformation(ex); + Log(LogWarning, "ApiActions") << DiagnosticInformation(ex); return nullptr; } @@ -642,7 +642,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, BOOST_THROW_EXCEPTION(std::invalid_argument("Can't find API user.")); /* Get endpoint */ - Endpoint::Ptr endpointPtr = ApiActions::GetSingleObjectByNameUsingPermissions(Endpoint::GetTypeName(), resolved_endpoint, ActionsHandler::AuthenticatedApiUser); + Endpoint::Ptr endpointPtr = GetSingleObjectByNameUsingPermissions(Endpoint::GetTypeName(), resolved_endpoint, ActionsHandler::AuthenticatedApiUser); if (!endpointPtr) return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); @@ -677,19 +677,19 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, }); if (command_type == "CheckCommand") { - CheckCommand::Ptr cmd = ApiActions::GetSingleObjectByNameUsingPermissions(CheckCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); + CheckCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(CheckCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else cmd->Execute(checkable, cr, execMacros, false); } else if (command_type == "EventCommand") { - EventCommand::Ptr cmd = ApiActions::GetSingleObjectByNameUsingPermissions(EventCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); + EventCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(EventCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else cmd->Execute(checkable, execMacros, false); } else if (command_type == "NotificationCommand") { - NotificationCommand::Ptr cmd = ApiActions::GetSingleObjectByNameUsingPermissions(NotificationCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); + NotificationCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(NotificationCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else { @@ -704,7 +704,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::EscapeCallback(), nullptr, false ); - User::Ptr user = ApiActions::GetSingleObjectByNameUsingPermissions(User::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); + User::Ptr user = GetSingleObjectByNameUsingPermissions(User::GetTypeName(), resolved_user, ActionsHandler::AuthenticatedApiUser); if (!user) return ApiActions::CreateResult(404, "Can't find a valid user for '" + resolved_user + "'."); @@ -719,7 +719,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroProcessor::EscapeCallback(), nullptr, false ); - Notification::Ptr notification = ApiActions::GetSingleObjectByNameUsingPermissions(Notification::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); + Notification::Ptr notification = GetSingleObjectByNameUsingPermissions(Notification::GetTypeName(), resolved_notification, ActionsHandler::AuthenticatedApiUser); if (!notification) return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'."); diff --git a/lib/icinga/apiactions.hpp b/lib/icinga/apiactions.hpp index c588c7ddc..b6ba83500 100644 --- a/lib/icinga/apiactions.hpp +++ b/lib/icinga/apiactions.hpp @@ -34,7 +34,7 @@ public: private: static Dictionary::Ptr CreateResult(int code, const String& status, const Dictionary::Ptr& additional = nullptr); - static Value GetSingleObjectByNameUsingPermissions(String type, String value, ApiUser::Ptr user); + static Value GetSingleObjectByNameUsingPermissions(const String& type, const String& value, const ApiUser::Ptr& user); }; } From bbe0f2d8c48510998bfaebe3f241d2926165d6ab Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Tue, 10 Mar 2020 13:19:56 +0100 Subject: [PATCH 045/126] mkclass: make .ti class members atomic if possible ... not to have to lock the objects while setting attributes. --- lib/base/atomic.hpp | 76 +++++++++++++++++++++++++++++++++ lib/base/configobject.ti | 4 +- lib/icinga/host.ti | 4 +- lib/icinga/hostgroup.ti | 4 +- lib/icinga/service.ti | 4 +- lib/icinga/servicegroup.ti | 4 +- lib/icinga/timeperiod.ti | 4 +- lib/icinga/user.ti | 4 +- lib/icinga/usergroup.ti | 4 +- tools/mkclass/classcompiler.cpp | 7 +-- 10 files changed, 96 insertions(+), 19 deletions(-) diff --git a/lib/base/atomic.hpp b/lib/base/atomic.hpp index 0ebcddefb..62fa37638 100644 --- a/lib/base/atomic.hpp +++ b/lib/base/atomic.hpp @@ -4,6 +4,8 @@ #define ATOMIC_H #include +#include +#include namespace icinga { @@ -38,6 +40,80 @@ public: } }; +/** + * Wraps T into a std::atomic-like interface. + * + * @ingroup base + */ +template +class NotAtomic +{ +public: + inline T load() const + { + return m_Value; + } + + inline void store(T desired) + { + m_Value = std::move(desired); + } + + T m_Value; +}; + +/** + * Tells whether to use std::atomic or NotAtomic. + * + * @ingroup base + */ +template +struct Atomicable +{ + // Doesn't work with too old compilers. + //static constexpr bool value = std::is_trivially_copyable::value && sizeof(T) <= sizeof(void*); + static constexpr bool value = (std::is_fundamental::value || std::is_pointer::value) && sizeof(T) <= sizeof(void*); +}; + +/** + * Uses either std::atomic or NotAtomic depending on atomicable. + * + * @ingroup base + */ +template +struct AtomicTemplate; + +template<> +struct AtomicTemplate +{ + template + struct tmplt + { + typedef NotAtomic type; + }; +}; + +template<> +struct AtomicTemplate +{ + template + struct tmplt + { + typedef std::atomic type; + }; +}; + +/** + * Uses either std::atomic or NotAtomic depending on T. + * + * @ingroup base + */ +template +struct EventuallyAtomic +{ + typedef typename AtomicTemplate::value>::template tmplt::type type; +}; + } #endif /* ATOMIC_H */ diff --git a/lib/base/configobject.ti b/lib/base/configobject.ti index fcfcb0223..3e5859c81 100644 --- a/lib/base/configobject.ti +++ b/lib/base/configobject.ti @@ -59,10 +59,10 @@ abstract class ConfigObject : ConfigObjectBase < ConfigType [config, no_user_modify] String __name (Name); [config, no_user_modify] String "name" (ShortName) { get {{{ - if (m_ShortName.IsEmpty()) + if (m_ShortName.m_Value.IsEmpty()) return GetName(); else - return m_ShortName; + return m_ShortName.m_Value; }}} }; [config, no_user_modify] name(Zone) zone (ZoneName); diff --git a/lib/icinga/host.ti b/lib/icinga/host.ti index 03c606228..1c8a6c9fe 100644 --- a/lib/icinga/host.ti +++ b/lib/icinga/host.ti @@ -21,10 +21,10 @@ class Host : Checkable [config] String display_name { get {{{ - if (m_DisplayName.IsEmpty()) + if (m_DisplayName.m_Value.IsEmpty()) return GetName(); else - return m_DisplayName; + return m_DisplayName.m_Value; }}} }; diff --git a/lib/icinga/hostgroup.ti b/lib/icinga/hostgroup.ti index a4404eafe..efcb45e54 100644 --- a/lib/icinga/hostgroup.ti +++ b/lib/icinga/hostgroup.ti @@ -11,10 +11,10 @@ class HostGroup : CustomVarObject { [config] String display_name { get {{{ - if (m_DisplayName.IsEmpty()) + if (m_DisplayName.m_Value.IsEmpty()) return GetName(); else - return m_DisplayName; + return m_DisplayName.m_Value; }}} }; diff --git a/lib/icinga/service.ti b/lib/icinga/service.ti index bab1ebc8f..f76750ef4 100644 --- a/lib/icinga/service.ti +++ b/lib/icinga/service.ti @@ -33,10 +33,10 @@ class Service : Checkable < ServiceNameComposer [config] String display_name { get {{{ - if (m_DisplayName.IsEmpty()) + if (m_DisplayName.m_Value.IsEmpty()) return GetShortName(); else - return m_DisplayName; + return m_DisplayName.m_Value; }}} }; [config, required] name(Host) host_name; diff --git a/lib/icinga/servicegroup.ti b/lib/icinga/servicegroup.ti index 69f300508..69a0887b9 100644 --- a/lib/icinga/servicegroup.ti +++ b/lib/icinga/servicegroup.ti @@ -11,10 +11,10 @@ class ServiceGroup : CustomVarObject { [config] String display_name { get {{{ - if (m_DisplayName.IsEmpty()) + if (m_DisplayName.m_Value.IsEmpty()) return GetName(); else - return m_DisplayName; + return m_DisplayName.m_Value; }}} }; diff --git a/lib/icinga/timeperiod.ti b/lib/icinga/timeperiod.ti index 27365988e..ccfbcc3d4 100644 --- a/lib/icinga/timeperiod.ti +++ b/lib/icinga/timeperiod.ti @@ -12,10 +12,10 @@ class TimePeriod : CustomVarObject { [config] String display_name { get {{{ - if (m_DisplayName.IsEmpty()) + if (m_DisplayName.m_Value.IsEmpty()) return GetName(); else - return m_DisplayName; + return m_DisplayName.m_Value; }}} }; [config] Dictionary::Ptr ranges; diff --git a/lib/icinga/user.ti b/lib/icinga/user.ti index 450d95358..5de35b523 100644 --- a/lib/icinga/user.ti +++ b/lib/icinga/user.ti @@ -13,10 +13,10 @@ class User : CustomVarObject { [config] String display_name { get {{{ - if (m_DisplayName.IsEmpty()) + if (m_DisplayName.m_Value.IsEmpty()) return GetName(); else - return m_DisplayName; + return m_DisplayName.m_Value; }}} }; [config, no_user_modify, required] array(name(UserGroup)) groups { diff --git a/lib/icinga/usergroup.ti b/lib/icinga/usergroup.ti index 311932171..35c7ccff4 100644 --- a/lib/icinga/usergroup.ti +++ b/lib/icinga/usergroup.ti @@ -11,10 +11,10 @@ class UserGroup : CustomVarObject { [config] String display_name { get {{{ - if (m_DisplayName.IsEmpty()) + if (m_DisplayName.m_Value.IsEmpty()) return GetName(); else - return m_DisplayName; + return m_DisplayName.m_Value; }}} }; diff --git a/tools/mkclass/classcompiler.cpp b/tools/mkclass/classcompiler.cpp index fd30df8e1..f6793da50 100644 --- a/tools/mkclass/classcompiler.cpp +++ b/tools/mkclass/classcompiler.cpp @@ -797,7 +797,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) << "{" << std::endl; if (field.GetAccessor.empty() && !(field.Attributes & FANoStorage)) - m_Impl << "\t" << "return m_" << field.GetFriendlyName() << ";" << std::endl; + m_Impl << "\t" << "return m_" << field.GetFriendlyName() << ".load();" << std::endl; else m_Impl << field.GetAccessor << std::endl; @@ -835,7 +835,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) if (field.SetAccessor.empty() && !(field.Attributes & FANoStorage)) - m_Impl << "\t" << "m_" << field.GetFriendlyName() << " = value;" << std::endl; + m_Impl << "\t" << "m_" << field.GetFriendlyName() << ".store(value);" << std::endl; else m_Impl << field.SetAccessor << std::endl << std::endl; @@ -1044,7 +1044,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) if (field.Attributes & FANoStorage) continue; - m_Header << "\t" << field.Type.GetRealType() << " m_" << field.GetFriendlyName() << ";" << std::endl; + m_Header << "\tEventuallyAtomic<" << field.Type.GetRealType() << ">::type m_" << field.GetFriendlyName() << ";" << std::endl; } /* signal */ @@ -1431,6 +1431,7 @@ void ClassCompiler::CompileStream(const std::string& path, std::istream& input, << "#include \"base/type.hpp\"" << std::endl << "#include \"base/value.hpp\"" << std::endl << "#include \"base/array.hpp\"" << std::endl + << "#include \"base/atomic.hpp\"" << std::endl << "#include \"base/dictionary.hpp\"" << std::endl << "#include " << std::endl << std::endl; From 645dcbdc9e205c9880b4e723447598f16385582b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 3 Apr 2020 11:10:08 +0200 Subject: [PATCH 046/126] Introduce Endpoint#icinga_version ... and set it to e.g. 21200 via icinga::Hello. --- lib/remote/apilistener.cpp | 42 +++++++++++++++++++++++++++++++++++++- lib/remote/endpoint.ti | 3 +++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index cb024e1fd..49ed29198 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -506,6 +508,20 @@ void ApiListener::NewClientHandler( } } +static const auto l_AppVersionInt (([]() -> unsigned long { + auto appVersion (Application::GetAppVersion()); + boost::regex rgx (R"EOF(^v?(\d+)\.(\d+)\.(\d+))EOF"); + boost::smatch match; + + if (!boost::regex_search(appVersion.GetData(), match, rgx)) { + return 0; + } + + return 100u * 100u * boost::lexical_cast(match[1].str()) + + 100u * boost::lexical_cast(match[2].str()) + + boost::lexical_cast(match[3].str()); +})()); + /** * Processes a new client connection. * @@ -650,7 +666,9 @@ void ApiListener::NewClientHandlerInternal( JsonRpc::SendMessage(client, new Dictionary({ { "jsonrpc", "2.0" }, { "method", "icinga::Hello" }, - { "params", new Dictionary() } + { "params", new Dictionary({ + { "version", (double)l_AppVersionInt } + }) } }), yc); client->async_flush(yc); @@ -683,6 +701,16 @@ void ApiListener::NewClientHandlerInternal( } if (firstByte >= '0' && firstByte <= '9') { + JsonRpc::SendMessage(client, new Dictionary({ + { "jsonrpc", "2.0" }, + { "method", "icinga::Hello" }, + { "params", new Dictionary({ + { "version", (double)l_AppVersionInt } + }) } + }), yc); + + client->async_flush(yc); + ctype = ClientJsonRpc; } else { ctype = ClientHttp; @@ -1607,6 +1635,18 @@ std::set ApiListener::GetHttpClients() const Value ApiListener::HelloAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { + if (origin) { + auto client (origin->FromClient); + + if (client) { + auto endpoint (client->GetEndpoint()); + + if (endpoint) { + endpoint->SetIcingaVersion((double)params->Get("version")); + } + } + } + return Empty; } diff --git a/lib/remote/endpoint.ti b/lib/remote/endpoint.ti index 1c3b654d1..31a00b89d 100644 --- a/lib/remote/endpoint.ti +++ b/lib/remote/endpoint.ti @@ -21,6 +21,9 @@ class Endpoint : ConfigObject [state] Timestamp local_log_position; [state] Timestamp remote_log_position; + [state] "unsigned long" icinga_version { + default {{{ return 0; }}} + }; [no_user_modify] bool connecting; [no_user_modify] bool syncing; From 93711542a708cd0c3a24fcf5afd5ab645a59dbc4 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 10 Jul 2020 10:05:38 +0200 Subject: [PATCH 047/126] Get UUID from params --- lib/icinga/clusterevents-check.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 515003661..5db8c009b 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -99,6 +99,9 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (params->Contains("source")) { Log(LogCritical, "ApiListener", "Not implemented."); + + String uuid = params->Get("source"); + return; } From 39479b666fdf05dba4cc60225106b1f878321e51 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 10 Jul 2020 11:21:13 +0200 Subject: [PATCH 048/126] Implement ClusterEvents::ExecutedCommandAPIHandler --- lib/icinga/clusterevents.cpp | 86 ++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 4146d2852..46a60fec2 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -944,6 +944,92 @@ Value ClusterEvents::NotificationSentToAllUsersAPIHandler(const MessageOrigin::P Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { + ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) + BOOST_THROW_EXCEPTION(std::invalid_argument("No ApiListener instance configured.")); + + Endpoint::Ptr endpoint; + if (origin->FromClient) { + endpoint = origin->FromClient->GetEndpoint(); + } else if (origin->IsLocal()){ + endpoint = Endpoint::GetLocalEndpoint(); + } + + if (!endpoint) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'update executions API handler' message from '" << origin->FromClient->GetIdentity() + << "': Invalid endpoint origin (client not allowed)."; + + return Empty; + } + + Host::Ptr host = Host::GetByName(params->Get("host")); + if (!host) + return Empty; + + Checkable::Ptr checkable; + + if (params->Contains("service")) + checkable = host->GetServiceByShortName(params->Get("service")); + else + checkable = host; + + if (!checkable) + return Empty; + + ObjectLock oLock (checkable); + + if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; + return Empty; + } + + if (!params->Contains("execution")) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': Execution UUID not found."; + return Empty; + } + String uuid = params->Get("execution"); + + Dictionary::Ptr executions = checkable->GetExecutions(); + if (!executions) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': No executions available."; + return Empty; + } + + Dictionary::Ptr execution = executions->Get(uuid); + if (!execution) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': Execution '" << uuid << "' not found."; + return Empty; + } + + execution->Set("pending", false); + + /* TODO update execution with the command result */ + + /* Broadcast the update */ + Dictionary::Ptr executionsToBroadcast = new Dictionary(); + executionsToBroadcast->Set(uuid, execution); + Dictionary::Ptr updateParams = new Dictionary(); + updateParams->Set("host", host->GetName()); + if (params->Contains("service")) + updateParams->Set("service", params->Get("service")); + updateParams->Set("executions", executionsToBroadcast); + + Dictionary::Ptr updateMessage = new Dictionary(); + updateMessage->Set("jsonrpc", "2.0"); + updateMessage->Set("method", "event::UpdateExecutions"); + updateMessage->Set("params", updateParams); + + listener->RelayMessage(origin, checkable, updateMessage, true); + return "Not implemented"; } From b6c84982f2bb14498d263228948e050cc022bd36 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 10 Jul 2020 16:51:13 +0200 Subject: [PATCH 049/126] Uniform command_type strings --- lib/icinga/apiactions.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index e1d25cefd..454c50f07 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -588,12 +588,12 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, BOOST_THROW_EXCEPTION(std::invalid_argument("No ApiListener instance configured.")); /* Get command_type */ - String command_type = "EventCommand"; + String command_type = "event_command"; if (params->Contains("command_type")) command_type = HttpUtility::GetLastParameter(params, "command_type"); /* Validate command_type */ - if (command_type != "EventCommand" && command_type != "CheckCommand" && command_type != "NotificationCommand") + if (command_type != "event_command" && command_type != "check_command" && command_type != "notification_command") return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); Checkable::Ptr checkable = dynamic_pointer_cast(object); @@ -649,11 +649,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Get command */ String command; if (!params->Contains("command")) { - if (command_type == "CheckCommand" ) { + if (command_type == "check_command" ) { command = "$check_command$"; - } else if (command_type == "EventCommand") { + } else if (command_type == "event_command") { command = "$event_command$"; - } else if (command_type == "NotificationCommand") { + } else if (command_type == "notification_command") { command = "$notification_command$"; } } else { @@ -676,19 +676,19 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroResolver::OverrideMacros = nullptr; }); - if (command_type == "CheckCommand") { + if (command_type == "check_command") { CheckCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(CheckCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else cmd->Execute(checkable, cr, execMacros, false); - } else if (command_type == "EventCommand") { + } else if (command_type == "event_command") { EventCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(EventCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else cmd->Execute(checkable, execMacros, false); - } else if (command_type == "NotificationCommand") { + } else if (command_type == "notification_command") { NotificationCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(NotificationCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); From c3d9f6c17b9113a64f6892e8fde127bf8654f70b Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 10 Jul 2020 16:51:45 +0200 Subject: [PATCH 050/126] Add ExecuteActionTask --- lib/methods/CMakeLists.txt | 1 + lib/methods/executeactiontask.cpp | 77 +++++++++++++++++++++++++++++++ lib/methods/executeactiontask.hpp | 32 +++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 lib/methods/executeactiontask.cpp create mode 100644 lib/methods/executeactiontask.hpp diff --git a/lib/methods/CMakeLists.txt b/lib/methods/CMakeLists.txt index 9fde9fddb..7bb2fbf6a 100644 --- a/lib/methods/CMakeLists.txt +++ b/lib/methods/CMakeLists.txt @@ -17,6 +17,7 @@ set(methods_SOURCES randomchecktask.cpp randomchecktask.hpp timeperiodtask.cpp timeperiodtask.hpp sleepchecktask.cpp sleepchecktask.hpp + executeactiontask.cpp exceptionchecktask.hpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/methods/executeactiontask.cpp b/lib/methods/executeactiontask.cpp new file mode 100644 index 000000000..c40a7ba24 --- /dev/null +++ b/lib/methods/executeactiontask.cpp @@ -0,0 +1,77 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "methods/executeactiontask.hpp" +#include "icinga/pluginutility.hpp" +#include "icinga/checkcommand.hpp" +#include "icinga/clusterevents.hpp" +#include "icinga/macroprocessor.hpp" +#include "icinga/icingaapplication.hpp" +#include "remote/apilistener.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; + +void ExecuteActionTask::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 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); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + Dictionary::Ptr executedParams = new Dictionary(); + executedParams->Set("host", host->GetName()); + if (service) + executedParams->Set("service", service->GetShortName()); + + /* TODO set the execution UUID */ + /*executedParams->Set("execution", uuid);*/ + + executedParams->Set("check_result", cr); + + /* FIXME command endpoint was overwrite by macro? */ + Endpoint::Ptr commandEndpoint = checkable->GetCommandEndpoint(); + bool local = !commandEndpoint || commandEndpoint == Endpoint::GetLocalEndpoint(); + if (local) { + MessageOrigin::Ptr origin = new MessageOrigin(); + ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); + } else { + ApiListener::Ptr listener = ApiListener::GetInstance(); + + if (listener) { + Dictionary::Ptr executedMessage = new Dictionary(); + executedMessage->Set("jsonrpc", "2.0"); + executedMessage->Set("method", "event::ExecutedCommand"); + executedMessage->Set("params", executedParams); + + listener->SyncSendMessage(commandEndpoint, executedMessage); + } else { + Log(LogCritical, "ExecuteActionTask") << "Api listener not found"; + } + } +} diff --git a/lib/methods/executeactiontask.hpp b/lib/methods/executeactiontask.hpp new file mode 100644 index 000000000..6717bef31 --- /dev/null +++ b/lib/methods/executeactiontask.hpp @@ -0,0 +1,32 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef EXECUTEACTIONTASK_H +#define EXECUTEACTIONTASK_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 ExecuteActionTask +{ +public: + + static void ProcessFinishedHandler(const Checkable::Ptr& service, + const CheckResult::Ptr& cr, const Value& commandLine, const ProcessResult& pr); + static thread_local String ExecutionUUID; +private: + + ExecuteActionTask(); +}; + +} + +#endif /* EXECUTEACTIONTASK_H */ From 15159b1632d52fac18b7ed98ae09f7a9afe83ecb Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 10 Jul 2020 16:56:07 +0200 Subject: [PATCH 051/126] Add ExecuteCommandProcessFinishedHandler and checkable param to ExecuteRemoteCheck --- lib/icinga/checkable-check.cpp | 8 +++++-- lib/icinga/checkable.cpp | 1 + lib/icinga/checkable.hpp | 5 ++++- lib/icinga/clusterevents-check.cpp | 35 ++++++++++++++++++++++++++---- lib/methods/executeactiontask.cpp | 2 +- lib/methods/pluginchecktask.cpp | 13 ++++++++--- 6 files changed, 53 insertions(+), 11 deletions(-) diff --git a/lib/icinga/checkable-check.cpp b/lib/icinga/checkable-check.cpp index 68361fe17..cc32f09ee 100644 --- a/lib/icinga/checkable-check.cpp +++ b/lib/icinga/checkable-check.cpp @@ -492,7 +492,7 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrig } } -void Checkable::ExecuteRemoteCheck(const Dictionary::Ptr& resolvedMacros) +void Checkable::ExecuteRemoteCheck(const Dictionary::Ptr& resolvedMacros, const Checkable::Ptr& checkable) { CONTEXT("Executing remote check for object '" + GetName() + "'"); @@ -503,7 +503,11 @@ void Checkable::ExecuteRemoteCheck(const Dictionary::Ptr& resolvedMacros) cr->SetScheduleStart(scheduled_start); cr->SetExecutionStart(before_check); - GetCheckCommand()->Execute(this, cr, resolvedMacros, true); + if (!checkable) { + GetCheckCommand()->Execute(this, cr, resolvedMacros, true); + } else { + GetCheckCommand()->Execute(checkable, cr, resolvedMacros, true); + } } void Checkable::ExecuteCheck() diff --git a/lib/icinga/checkable.cpp b/lib/icinga/checkable.cpp index 0f4d1399b..3f31d5667 100644 --- a/lib/icinga/checkable.cpp +++ b/lib/icinga/checkable.cpp @@ -20,6 +20,7 @@ boost::signals2::signal Checkable::OnFlappingChange; static Timer::Ptr l_CheckablesFireSuppressedNotifications; +thread_local std::function Checkable::ExecuteCommandProcessFinishedHandler; void Checkable::StaticInitialize() { diff --git a/lib/icinga/checkable.hpp b/lib/icinga/checkable.hpp index 0eb1c5950..38ecf0106 100644 --- a/lib/icinga/checkable.hpp +++ b/lib/icinga/checkable.hpp @@ -5,6 +5,7 @@ #include "base/atomic.hpp" #include "base/timer.hpp" +#include "base/process.hpp" #include "icinga/i2-icinga.hpp" #include "icinga/checkable-ti.hpp" #include "icinga/timeperiod.hpp" @@ -14,6 +15,7 @@ #include "remote/endpoint.hpp" #include "remote/messageorigin.hpp" #include +#include namespace icinga { @@ -55,6 +57,7 @@ public: DECLARE_OBJECTNAME(Checkable); static void StaticInitialize(); + static thread_local std::function ExecuteCommandProcessFinishedHandler; Checkable(); @@ -94,7 +97,7 @@ public: static void UpdateStatistics(const CheckResult::Ptr& cr, CheckableType type); - void ExecuteRemoteCheck(const Dictionary::Ptr& resolvedMacros = nullptr); + void ExecuteRemoteCheck(const Dictionary::Ptr& resolvedMacros = nullptr, const Checkable::Ptr& checkbale = nullptr); void ExecuteCheck(); void ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrigin::Ptr& origin = nullptr); diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 5db8c009b..8ab03f655 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -6,6 +6,7 @@ #include "base/configuration.hpp" #include "base/serializer.hpp" #include "base/exception.hpp" +#include "methods/executeactiontask.hpp" #include #include @@ -97,12 +98,38 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons return; } + Checkable::Ptr checkable = nullptr; if (params->Contains("source")) { - Log(LogCritical, "ApiListener", "Not implemented."); - String uuid = params->Get("source"); - return; + Host::Ptr host = Host::GetByName(params->Get("host")); + if (!host) { + Log(LogCritical, "ApiListener", "Host not found."); + return; + } + + if (params->Contains("service")) + checkable = host->GetServiceByShortName(params->Get("service")); + else + checkable = host; + + if (!checkable) { + Log(LogCritical, "ApiListener", "Checkable not found."); + return; + } + + ObjectLock oLock (checkable); + + if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) { + Log(LogNotice, "ApiListener") + << "Discarding 'ExecuteCheckFromQueue' event for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; + return; + } + + Checkable::ExecuteCommandProcessFinishedHandler = ExecuteActionTask::ProcessFinishedHandler; + } else { + Checkable::ExecuteCommandProcessFinishedHandler = nullptr; } if (!listener->GetAcceptCommands()) { @@ -182,7 +209,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (command_type == "check_command") { try { - host->ExecuteRemoteCheck(macros); + host->ExecuteRemoteCheck(macros, checkable); } catch (const std::exception& ex) { CheckResult::Ptr cr = new CheckResult(); cr->SetState(ServiceUnknown); diff --git a/lib/methods/executeactiontask.cpp b/lib/methods/executeactiontask.cpp index c40a7ba24..305bc3213 100644 --- a/lib/methods/executeactiontask.cpp +++ b/lib/methods/executeactiontask.cpp @@ -54,7 +54,7 @@ void ExecuteActionTask::ProcessFinishedHandler(const Checkable::Ptr& checkable, executedParams->Set("check_result", cr); - /* FIXME command endpoint was overwrite by macro? */ + /* FIXME command endpoint overwritten by macro? */ Endpoint::Ptr commandEndpoint = checkable->GetCommandEndpoint(); bool local = !commandEndpoint || commandEndpoint == Endpoint::GetLocalEndpoint(); if (local) { diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp index 70523b49f..43851f31d 100644 --- a/lib/methods/pluginchecktask.cpp +++ b/lib/methods/pluginchecktask.cpp @@ -43,9 +43,16 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (!checkable->GetCheckTimeout().IsEmpty()) timeout = checkable->GetCheckTimeout(); - PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(), - resolvers, resolvedMacros, useResolvedMacros, timeout, - std::bind(&PluginCheckTask::ProcessFinishedHandler, checkable, cr, _1, _2)); + + if (Checkable::ExecuteCommandProcessFinishedHandler) { + PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(), + resolvers, resolvedMacros, useResolvedMacros, timeout, + std::bind(Checkable::ExecuteCommandProcessFinishedHandler, checkable, cr, _1, _2)); + } else { + PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(), + resolvers, resolvedMacros, useResolvedMacros, timeout, + std::bind(&PluginCheckTask::ProcessFinishedHandler, checkable, cr, _1, _2)); + } if (!resolvedMacros || useResolvedMacros) { Checkable::CurrentConcurrentChecks.fetch_add(1); From fa4aebbfd2d10d8d02c07b52f8118f5ae3810f6e Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 10 Jul 2020 16:56:35 +0200 Subject: [PATCH 052/126] Set CheckResult to execeution --- lib/icinga/clusterevents.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 46a60fec2..65d1134bb 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -1002,6 +1002,14 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, return Empty; } + if (!params->Contains("check_result")) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': No check result available."; + return Empty; + } + CheckResult::Ptr cr = params->Get("check_result"); + Dictionary::Ptr execution = executions->Get(uuid); if (!execution) { Log(LogNotice, "ClusterEvents") @@ -1011,8 +1019,7 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, } execution->Set("pending", false); - - /* TODO update execution with the command result */ + execution->Set("check_result", cr); /* Broadcast the update */ Dictionary::Ptr executionsToBroadcast = new Dictionary(); @@ -1030,7 +1037,7 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, listener->RelayMessage(origin, checkable, updateMessage, true); - return "Not implemented"; + return Empty; } Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) From 6bad8bbc0fd4ce5a9fabb82b770631d2449dcc1a Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 13 Jul 2020 09:47:19 +0200 Subject: [PATCH 053/126] Replace executeactiontask with a lambda function --- lib/icinga/clusterevents-check.cpp | 45 +++++++++++++++-- lib/icinga/clusterevents.cpp | 17 ++++--- lib/methods/CMakeLists.txt | 1 - lib/methods/executeactiontask.cpp | 77 ------------------------------ lib/methods/executeactiontask.hpp | 32 ------------- 5 files changed, 49 insertions(+), 123 deletions(-) delete mode 100644 lib/methods/executeactiontask.cpp delete mode 100644 lib/methods/executeactiontask.hpp diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 8ab03f655..7d86a17ec 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -6,7 +6,6 @@ #include "base/configuration.hpp" #include "base/serializer.hpp" #include "base/exception.hpp" -#include "methods/executeactiontask.hpp" #include #include @@ -122,12 +121,50 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) { Log(LogNotice, "ApiListener") - << "Discarding 'ExecuteCheckFromQueue' event for checkable '" << checkable->GetName() - << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; + << "Discarding 'ExecuteCheckFromQueue' event for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; return; } - Checkable::ExecuteCommandProcessFinishedHandler = ExecuteActionTask::ProcessFinishedHandler; + Checkable::ExecuteCommandProcessFinishedHandler = [listener, sourceEndpoint, origin, params] (const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Value& commandLine, const ProcessResult& pr) -> void { + Checkable::CurrentConcurrentChecks.fetch_sub(1); + Checkable::DecreasePendingChecks(); + + if (pr.ExitStatus > 3) { + Process::Arguments parguments = Process::PrepareCommand(commandLine); + Log(LogWarning, "ApiListener") + << "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 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); + + Dictionary::Ptr executedParams = new Dictionary(); + params->CopyTo(executedParams); + executedParams->Set("execution", params->Get("source")); + executedParams->Set("check_result", Serialize(cr)); + + if (origin->IsLocal()) { + ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); + } else { + Dictionary::Ptr executedMessage = new Dictionary(); + executedMessage->Set("jsonrpc", "2.0"); + executedMessage->Set("method", "event::ExecutedCommand"); + executedMessage->Set("params", executedParams); + + listener->SyncSendMessage(sourceEndpoint, executedMessage); + } + }; } else { Checkable::ExecuteCommandProcessFinishedHandler = nullptr; } diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 65d1134bb..83b14d5d4 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -1002,14 +1002,6 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, return Empty; } - if (!params->Contains("check_result")) { - Log(LogNotice, "ClusterEvents") - << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() - << "' from '" << origin->FromClient->GetIdentity() << "': No check result available."; - return Empty; - } - CheckResult::Ptr cr = params->Get("check_result"); - Dictionary::Ptr execution = executions->Get(uuid); if (!execution) { Log(LogNotice, "ClusterEvents") @@ -1018,8 +1010,15 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, return Empty; } + if (!params->Contains("check_result")) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': No check result available."; + return Empty; + } + + execution->Set("check_result", params->Get("check_result")); execution->Set("pending", false); - execution->Set("check_result", cr); /* Broadcast the update */ Dictionary::Ptr executionsToBroadcast = new Dictionary(); diff --git a/lib/methods/CMakeLists.txt b/lib/methods/CMakeLists.txt index 7bb2fbf6a..9fde9fddb 100644 --- a/lib/methods/CMakeLists.txt +++ b/lib/methods/CMakeLists.txt @@ -17,7 +17,6 @@ set(methods_SOURCES randomchecktask.cpp randomchecktask.hpp timeperiodtask.cpp timeperiodtask.hpp sleepchecktask.cpp sleepchecktask.hpp - executeactiontask.cpp exceptionchecktask.hpp ) if(ICINGA2_UNITY_BUILD) diff --git a/lib/methods/executeactiontask.cpp b/lib/methods/executeactiontask.cpp deleted file mode 100644 index 305bc3213..000000000 --- a/lib/methods/executeactiontask.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ - -#include "methods/executeactiontask.hpp" -#include "icinga/pluginutility.hpp" -#include "icinga/checkcommand.hpp" -#include "icinga/clusterevents.hpp" -#include "icinga/macroprocessor.hpp" -#include "icinga/icingaapplication.hpp" -#include "remote/apilistener.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; - -void ExecuteActionTask::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 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); - - Host::Ptr host; - Service::Ptr service; - tie(host, service) = GetHostService(checkable); - - Dictionary::Ptr executedParams = new Dictionary(); - executedParams->Set("host", host->GetName()); - if (service) - executedParams->Set("service", service->GetShortName()); - - /* TODO set the execution UUID */ - /*executedParams->Set("execution", uuid);*/ - - executedParams->Set("check_result", cr); - - /* FIXME command endpoint overwritten by macro? */ - Endpoint::Ptr commandEndpoint = checkable->GetCommandEndpoint(); - bool local = !commandEndpoint || commandEndpoint == Endpoint::GetLocalEndpoint(); - if (local) { - MessageOrigin::Ptr origin = new MessageOrigin(); - ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); - } else { - ApiListener::Ptr listener = ApiListener::GetInstance(); - - if (listener) { - Dictionary::Ptr executedMessage = new Dictionary(); - executedMessage->Set("jsonrpc", "2.0"); - executedMessage->Set("method", "event::ExecutedCommand"); - executedMessage->Set("params", executedParams); - - listener->SyncSendMessage(commandEndpoint, executedMessage); - } else { - Log(LogCritical, "ExecuteActionTask") << "Api listener not found"; - } - } -} diff --git a/lib/methods/executeactiontask.hpp b/lib/methods/executeactiontask.hpp deleted file mode 100644 index 6717bef31..000000000 --- a/lib/methods/executeactiontask.hpp +++ /dev/null @@ -1,32 +0,0 @@ -/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ - -#ifndef EXECUTEACTIONTASK_H -#define EXECUTEACTIONTASK_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 ExecuteActionTask -{ -public: - - static void ProcessFinishedHandler(const Checkable::Ptr& service, - const CheckResult::Ptr& cr, const Value& commandLine, const ProcessResult& pr); - static thread_local String ExecutionUUID; -private: - - ExecuteActionTask(); -}; - -} - -#endif /* EXECUTEACTIONTASK_H */ From 3414acbec184770f609bb3c77f6a5bfecf214413 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 13 Jul 2020 10:14:30 +0200 Subject: [PATCH 054/126] Remove unuseful parameter from ExecuteRemoteCheck --- lib/icinga/checkable-check.cpp | 8 ++------ lib/icinga/checkable.hpp | 2 +- lib/icinga/clusterevents-check.cpp | 4 ++-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/icinga/checkable-check.cpp b/lib/icinga/checkable-check.cpp index cc32f09ee..68361fe17 100644 --- a/lib/icinga/checkable-check.cpp +++ b/lib/icinga/checkable-check.cpp @@ -492,7 +492,7 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrig } } -void Checkable::ExecuteRemoteCheck(const Dictionary::Ptr& resolvedMacros, const Checkable::Ptr& checkable) +void Checkable::ExecuteRemoteCheck(const Dictionary::Ptr& resolvedMacros) { CONTEXT("Executing remote check for object '" + GetName() + "'"); @@ -503,11 +503,7 @@ void Checkable::ExecuteRemoteCheck(const Dictionary::Ptr& resolvedMacros, const cr->SetScheduleStart(scheduled_start); cr->SetExecutionStart(before_check); - if (!checkable) { - GetCheckCommand()->Execute(this, cr, resolvedMacros, true); - } else { - GetCheckCommand()->Execute(checkable, cr, resolvedMacros, true); - } + GetCheckCommand()->Execute(this, cr, resolvedMacros, true); } void Checkable::ExecuteCheck() diff --git a/lib/icinga/checkable.hpp b/lib/icinga/checkable.hpp index 38ecf0106..dd74b85db 100644 --- a/lib/icinga/checkable.hpp +++ b/lib/icinga/checkable.hpp @@ -97,7 +97,7 @@ public: static void UpdateStatistics(const CheckResult::Ptr& cr, CheckableType type); - void ExecuteRemoteCheck(const Dictionary::Ptr& resolvedMacros = nullptr, const Checkable::Ptr& checkbale = nullptr); + void ExecuteRemoteCheck(const Dictionary::Ptr& resolvedMacros = nullptr); void ExecuteCheck(); void ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrigin::Ptr& origin = nullptr); diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 7d86a17ec..ffa0b6a3a 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -97,7 +97,6 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons return; } - Checkable::Ptr checkable = nullptr; if (params->Contains("source")) { String uuid = params->Get("source"); @@ -107,6 +106,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons return; } + Checkable::Ptr checkable; if (params->Contains("service")) checkable = host->GetServiceByShortName(params->Get("service")); else @@ -246,7 +246,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (command_type == "check_command") { try { - host->ExecuteRemoteCheck(macros, checkable); + host->ExecuteRemoteCheck(macros); } catch (const std::exception& ex) { CheckResult::Ptr cr = new CheckResult(); cr->SetState(ServiceUnknown); From b9510e72ddcf141c4031bc0915e368224503d3de Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 13 Jul 2020 10:20:23 +0200 Subject: [PATCH 055/126] Check deadline --- lib/icinga/clusterevents-check.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index ffa0b6a3a..3564c99b7 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -126,6 +126,15 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons return; } + /* Check deadline */ + double deadline = params->Get("deadline"); + if (Utility::GetTime() > deadline) { + Log(LogNotice, "ApiListener") + << "Discarding 'ExecuteCheckFromQueue' event for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': Deadline has expired."; + return; + } + Checkable::ExecuteCommandProcessFinishedHandler = [listener, sourceEndpoint, origin, params] (const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Value& commandLine, const ProcessResult& pr) -> void { Checkable::CurrentConcurrentChecks.fetch_sub(1); Checkable::DecreasePendingChecks(); From a3027d717730dca65121930b95a408f71a0aa50e Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 13 Jul 2020 16:54:37 +0200 Subject: [PATCH 056/126] Fix ExecuteCommandProcessFinishedHandler parameters --- lib/icinga/checkable.cpp | 2 +- lib/icinga/checkable.hpp | 2 +- lib/icinga/clusterevents-check.cpp | 7 +++---- lib/methods/pluginchecktask.cpp | 11 +++++------ 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/icinga/checkable.cpp b/lib/icinga/checkable.cpp index 3f31d5667..22d1d0cf4 100644 --- a/lib/icinga/checkable.cpp +++ b/lib/icinga/checkable.cpp @@ -20,7 +20,7 @@ boost::signals2::signal Checkable::OnFlappingChange; static Timer::Ptr l_CheckablesFireSuppressedNotifications; -thread_local std::function Checkable::ExecuteCommandProcessFinishedHandler; +thread_local std::function Checkable::ExecuteCommandProcessFinishedHandler; void Checkable::StaticInitialize() { diff --git a/lib/icinga/checkable.hpp b/lib/icinga/checkable.hpp index dd74b85db..cfa04339a 100644 --- a/lib/icinga/checkable.hpp +++ b/lib/icinga/checkable.hpp @@ -57,7 +57,7 @@ public: DECLARE_OBJECTNAME(Checkable); static void StaticInitialize(); - static thread_local std::function ExecuteCommandProcessFinishedHandler; + static thread_local std::function ExecuteCommandProcessFinishedHandler; Checkable(); diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 3564c99b7..b6f0e17ae 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -135,7 +135,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons return; } - Checkable::ExecuteCommandProcessFinishedHandler = [listener, sourceEndpoint, origin, params] (const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Value& commandLine, const ProcessResult& pr) -> void { + Checkable::ExecuteCommandProcessFinishedHandler = [checkable, listener, sourceEndpoint, origin, params] (const Value& commandLine, const ProcessResult& pr) { Checkable::CurrentConcurrentChecks.fetch_sub(1); Checkable::DecreasePendingChecks(); @@ -150,6 +150,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons String output = pr.Output.Trim(); std::pair co = PluginUtility::ParseCheckOutput(output); + CheckResult::Ptr cr = new CheckResult(); cr->SetCommand(commandLine); cr->SetOutput(co.first); cr->SetPerformanceData(PluginUtility::SplitPerfdata(co.second)); @@ -174,10 +175,8 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons listener->SyncSendMessage(sourceEndpoint, executedMessage); } }; - } else { - Checkable::ExecuteCommandProcessFinishedHandler = nullptr; } - + if (!listener->GetAcceptCommands()) { Log(LogWarning, "ApiListener") << "Ignoring command. '" << listener->GetName() << "' does not accept commands."; diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp index 43851f31d..9dcbd7936 100644 --- a/lib/methods/pluginchecktask.cpp +++ b/lib/methods/pluginchecktask.cpp @@ -44,15 +44,14 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes timeout = checkable->GetCheckTimeout(); + std::function callback; if (Checkable::ExecuteCommandProcessFinishedHandler) { - PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(), - resolvers, resolvedMacros, useResolvedMacros, timeout, - std::bind(Checkable::ExecuteCommandProcessFinishedHandler, checkable, cr, _1, _2)); + callback = Checkable::ExecuteCommandProcessFinishedHandler; } else { - PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(), - resolvers, resolvedMacros, useResolvedMacros, timeout, - std::bind(&PluginCheckTask::ProcessFinishedHandler, checkable, cr, _1, _2)); + callback = std::bind(&PluginCheckTask::ProcessFinishedHandler, checkable, cr, _1, _2); } + PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(), + resolvers, resolvedMacros, useResolvedMacros, timeout, callback); if (!resolvedMacros || useResolvedMacros) { Checkable::CurrentConcurrentChecks.fetch_add(1); From 134a43d48a586ad27297f277d9e6d88a23499899 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 13 Jul 2020 17:06:08 +0200 Subject: [PATCH 057/126] Improve logs --- lib/icinga/clusterevents-check.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index b6f0e17ae..a1b2f89ee 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -102,7 +102,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons Host::Ptr host = Host::GetByName(params->Get("host")); if (!host) { - Log(LogCritical, "ApiListener", "Host not found."); + Log(LogCritical, "ApiListener") << "Host '" << params->Get("host") << "' not found."; return; } @@ -113,7 +113,8 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons checkable = host; if (!checkable) { - Log(LogCritical, "ApiListener", "Checkable not found."); + Log(LogCritical, "ApiListener") << "Checkable '" << params->Get("host") + << " " << params->Get("service") << "' not found."; return; } @@ -176,7 +177,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons } }; } - + if (!listener->GetAcceptCommands()) { Log(LogWarning, "ApiListener") << "Ignoring command. '" << listener->GetName() << "' does not accept commands."; From 2db7b10ff0d724adabfe2ba5674859c29f205082 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 13 Jul 2020 17:49:38 +0200 Subject: [PATCH 058/126] Defer ExecuteCommandProcessFinishedHandler --- lib/icinga/clusterevents-check.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index a1b2f89ee..e513f1fef 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -97,6 +97,14 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons return; } + bool executeCommandProcessFinishedHandlerToBeReset = false; + Defer resetExecuteCommandProcessFinishedHandler ([&executeCommandProcessFinishedHandlerToBeReset]() { + if (executeCommandProcessFinishedHandlerToBeReset) { + Checkable::ExecuteCommandProcessFinishedHandler = nullptr; + executeCommandProcessFinishedHandlerToBeReset = false; + } + }); + if (params->Contains("source")) { String uuid = params->Get("source"); @@ -176,6 +184,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons listener->SyncSendMessage(sourceEndpoint, executedMessage); } }; + executeCommandProcessFinishedHandlerToBeReset = true; } if (!listener->GetAcceptCommands()) { From 59dfe6ad415c27e231074f38262c8acc56da2ca9 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 14 Jul 2020 11:23:39 +0200 Subject: [PATCH 059/126] Fix log --- lib/icinga/clusterevents-check.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index e513f1fef..85b52ebf4 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -122,7 +122,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (!checkable) { Log(LogCritical, "ApiListener") << "Checkable '" << params->Get("host") - << " " << params->Get("service") << "' not found."; + << "!" << params->Get("service") << "' not found."; return; } From 26685a93609fa238c7ea0bd974e2adcf2f8d3cc3 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 14 Jul 2020 11:57:12 +0200 Subject: [PATCH 060/126] Remove unuseful variable. Fix log --- lib/icinga/clusterevents-check.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 85b52ebf4..0980a0c2c 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -93,16 +93,12 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons ApiListener::Ptr listener = ApiListener::GetInstance(); if (!listener) { - Log(LogCritical, "ApiListener", "No instance available."); + Log(LogCritical, "ApiListener") << "No instance available."; return; } - bool executeCommandProcessFinishedHandlerToBeReset = false; - Defer resetExecuteCommandProcessFinishedHandler ([&executeCommandProcessFinishedHandlerToBeReset]() { - if (executeCommandProcessFinishedHandlerToBeReset) { - Checkable::ExecuteCommandProcessFinishedHandler = nullptr; - executeCommandProcessFinishedHandlerToBeReset = false; - } + Defer resetExecuteCommandProcessFinishedHandler ([]() { + Checkable::ExecuteCommandProcessFinishedHandler = nullptr; }); if (params->Contains("source")) { @@ -184,7 +180,6 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons listener->SyncSendMessage(sourceEndpoint, executedMessage); } }; - executeCommandProcessFinishedHandlerToBeReset = true; } if (!listener->GetAcceptCommands()) { From 96c34528a0782e2f168e309650c998599a92d8fe Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 14 Jul 2020 12:01:12 +0200 Subject: [PATCH 061/126] Change command type 'notification_command' with 'NotificationCommand' --- lib/icinga/apiactions.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 454c50f07..cddadb488 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -593,7 +593,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, command_type = HttpUtility::GetLastParameter(params, "command_type"); /* Validate command_type */ - if (command_type != "event_command" && command_type != "check_command" && command_type != "notification_command") + if (command_type != "event_command" && command_type != "check_command" && command_type != "NotificationCommand") return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); Checkable::Ptr checkable = dynamic_pointer_cast(object); @@ -653,7 +653,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, command = "$check_command$"; } else if (command_type == "event_command") { command = "$event_command$"; - } else if (command_type == "notification_command") { + } else if (command_type == "NotificationCommand") { command = "$notification_command$"; } } else { @@ -688,7 +688,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else cmd->Execute(checkable, execMacros, false); - } else if (command_type == "notification_command") { + } else if (command_type == "NotificationCommand") { NotificationCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(NotificationCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); From 12da369ee188eae12ad1a2fc1dc6289bbb20bf6d Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 14 Jul 2020 17:23:03 +0200 Subject: [PATCH 062/126] Change check_command and event_command strings to CheckCommand and EventCommand --- lib/icinga/apiactions.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index cddadb488..2cfd8f827 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -588,12 +588,12 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, BOOST_THROW_EXCEPTION(std::invalid_argument("No ApiListener instance configured.")); /* Get command_type */ - String command_type = "event_command"; + String command_type = "EventCommand"; if (params->Contains("command_type")) command_type = HttpUtility::GetLastParameter(params, "command_type"); /* Validate command_type */ - if (command_type != "event_command" && command_type != "check_command" && command_type != "NotificationCommand") + if (command_type != "EventCommand" && command_type != "CheckCommand" && command_type != "NotificationCommand") return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); Checkable::Ptr checkable = dynamic_pointer_cast(object); @@ -649,9 +649,9 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Get command */ String command; if (!params->Contains("command")) { - if (command_type == "check_command" ) { + if (command_type == "CheckCommand" ) { command = "$check_command$"; - } else if (command_type == "event_command") { + } else if (command_type == "EventCommand") { command = "$event_command$"; } else if (command_type == "NotificationCommand") { command = "$notification_command$"; @@ -676,13 +676,13 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroResolver::OverrideMacros = nullptr; }); - if (command_type == "check_command") { + if (command_type == "CheckCommand") { CheckCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(CheckCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else cmd->Execute(checkable, cr, execMacros, false); - } else if (command_type == "event_command") { + } else if (command_type == "EventCommand") { EventCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(EventCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); @@ -763,7 +763,12 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Create execution parameters */ Dictionary::Ptr execParams = new Dictionary(); - execParams->Set("command_type", command_type); + if (command_type == "CheckCommand") + execParams->Set("command_type", "check_command"); + else if (command_type == "EventCommand") + execParams->Set("command_type", "event_command"); + else if (command_type == "NotificationCommand") + execParams->Set("command_type", "notification_command"); execParams->Set("command", resolved_command); execParams->Set("host", host->GetName()); if (service) From 3f490ac0e28191cf22baf64c8700c7abb8c2b8c6 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 15 Jul 2020 08:37:28 +0200 Subject: [PATCH 063/126] Set the right prams to send to ExecutedCommand --- lib/icinga/clusterevents-check.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 0980a0c2c..555037d17 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -165,8 +165,10 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons cr->SetExecutionEnd(pr.ExecutionEnd); Dictionary::Ptr executedParams = new Dictionary(); - params->CopyTo(executedParams); executedParams->Set("execution", params->Get("source")); + executedParams->Set("host", params->Get("host")); + if (params->Contains("service")) + executedParams->Set("service", params->Get("service")); executedParams->Set("check_result", Serialize(cr)); if (origin->IsLocal()) { From 300bc89cb62a1da4e1117016e201c97f94644170 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 15 Jul 2020 08:53:28 +0200 Subject: [PATCH 064/126] Handle when listener doesn't accept commands --- lib/icinga/clusterevents-check.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 555037d17..5e514c19e 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -203,8 +203,29 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons CheckResult::Ptr cr = new CheckResult(); cr->SetState(ServiceUnknown); cr->SetOutput("Endpoint '" + Endpoint::GetLocalEndpoint()->GetName() + "' does not accept commands."); - Dictionary::Ptr message = MakeCheckResultMessage(host, cr); - listener->SyncSendMessage(sourceEndpoint, message); + + if (params->Contains("source")) { + Dictionary::Ptr executedParams = new Dictionary(); + executedParams->Set("execution", params->Get("source")); + executedParams->Set("host", params->Get("host")); + if (params->Contains("service")) + executedParams->Set("service", params->Get("service")); + executedParams->Set("check_result", Serialize(cr)); + + if (origin->IsLocal()) { + ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); + } else { + Dictionary::Ptr executedMessage = new Dictionary(); + executedMessage->Set("jsonrpc", "2.0"); + executedMessage->Set("method", "event::ExecutedCommand"); + executedMessage->Set("params", executedParams); + + listener->SyncSendMessage(sourceEndpoint, executedMessage); + } + } else { + Dictionary::Ptr message = MakeCheckResultMessage(host, cr); + listener->SyncSendMessage(sourceEndpoint, message); + } return; } From 27f8bc5920eebefefa5bcc0827efbe2c77e27348 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 15 Jul 2020 09:14:01 +0200 Subject: [PATCH 065/126] Call ExecuteCommandProcessFinishedHandler for notification and event commands --- lib/methods/plugineventtask.cpp | 9 +++++++-- lib/methods/pluginnotificationtask.cpp | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/methods/plugineventtask.cpp b/lib/methods/plugineventtask.cpp index b2bb466b8..9296df0f1 100644 --- a/lib/methods/plugineventtask.cpp +++ b/lib/methods/plugineventtask.cpp @@ -36,9 +36,14 @@ void PluginEventTask::ScriptFunc(const Checkable::Ptr& checkable, int timeout = commandObj->GetTimeout(); + std::function callback; + if (Checkable::ExecuteCommandProcessFinishedHandler) { + callback = Checkable::ExecuteCommandProcessFinishedHandler; + } else { + callback = std::bind(&PluginEventTask::ProcessFinishedHandler, checkable, _1, _2); + } PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(), - resolvers, resolvedMacros, useResolvedMacros, timeout, - std::bind(&PluginEventTask::ProcessFinishedHandler, checkable, _1, _2)); + resolvers, resolvedMacros, useResolvedMacros, timeout, callback); } void PluginEventTask::ProcessFinishedHandler(const Checkable::Ptr& checkable, const Value& commandLine, const ProcessResult& pr) diff --git a/lib/methods/pluginnotificationtask.cpp b/lib/methods/pluginnotificationtask.cpp index 0fcc95dc2..b81708cf3 100644 --- a/lib/methods/pluginnotificationtask.cpp +++ b/lib/methods/pluginnotificationtask.cpp @@ -53,9 +53,14 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification, int timeout = commandObj->GetTimeout(); + std::function callback; + if (Checkable::ExecuteCommandProcessFinishedHandler) { + callback = Checkable::ExecuteCommandProcessFinishedHandler; + } else { + callback = std::bind(&PluginNotificationTask::ProcessFinishedHandler, checkable, _1, _2); + } PluginUtility::ExecuteCommand(commandObj, checkable, cr, resolvers, - resolvedMacros, useResolvedMacros, timeout, - std::bind(&PluginNotificationTask::ProcessFinishedHandler, checkable, _1, _2)); + resolvedMacros, useResolvedMacros, timeout, callback); } void PluginNotificationTask::ProcessFinishedHandler(const Checkable::Ptr& checkable, const Value& commandLine, const ProcessResult& pr) From 4255fd9494e9b1c421875657a7ba4cca991fe029 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 15 Jul 2020 13:50:49 +0200 Subject: [PATCH 066/126] Get host by Deserialize --- lib/icinga/clusterevents-check.cpp | 34 ++++++++++++++++++------------ 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 5e514c19e..e294d224b 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -104,24 +104,30 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (params->Contains("source")) { String uuid = params->Get("source"); - Host::Ptr host = Host::GetByName(params->Get("host")); - if (!host) { - Log(LogCritical, "ApiListener") << "Host '" << params->Get("host") << "' not found."; - return; - } + Host::Ptr host = new Host(); + Dictionary::Ptr attrs = new Dictionary(); + + attrs->Set("__name", params->Get("host")); + attrs->Set("type", "Host"); + + /* + * Override the check timeout if the parent caller provided the value. Compatible with older versions not + * passing this inside the cluster message. + * This happens with host/service command_endpoint agents and the 'check_timeout' attribute being specified. + */ + if (params->Contains("check_timeout")) + attrs->Set("check_timeout", params->Get("check_timeout")); + + Deserialize(host, attrs, false, FAConfig); - Checkable::Ptr checkable; if (params->Contains("service")) - checkable = host->GetServiceByShortName(params->Get("service")); - else - checkable = host; + host->SetExtension("agent_service_name", params->Get("service")); - if (!checkable) { - Log(LogCritical, "ApiListener") << "Checkable '" << params->Get("host") - << "!" << params->Get("service") << "' not found."; - return; - } + attrs->Set(params->Get("command_type"), params->Get("command")); + attrs->Set("command_endpoint", sourceEndpoint->GetName()); + Deserialize(host, attrs, false, FAConfig); + Checkable::Ptr checkable = host; ObjectLock oLock (checkable); if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) { From 50f803c0422c1b4ef14f4c20f9c5a4400e94592f Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 15 Jul 2020 18:40:37 +0200 Subject: [PATCH 067/126] Return the process result parameters instead of the check result --- lib/icinga/clusterevents-check.cpp | 22 ++++++++-------------- lib/icinga/clusterevents.cpp | 21 ++++++++++++++------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index e294d224b..3c8261446 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -158,24 +158,15 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons << pr.ExitStatus << ", output: " << pr.Output; } - String output = pr.Output.Trim(); - - std::pair co = PluginUtility::ParseCheckOutput(output); - CheckResult::Ptr cr = new CheckResult(); - 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); - Dictionary::Ptr executedParams = new Dictionary(); executedParams->Set("execution", params->Get("source")); executedParams->Set("host", params->Get("host")); if (params->Contains("service")) executedParams->Set("service", params->Get("service")); - executedParams->Set("check_result", Serialize(cr)); + executedParams->Set("exit", pr.ExitStatus); + executedParams->Set("output", pr.Output); + executedParams->Set("start", pr.ExecutionStart); + executedParams->Set("end", pr.ExecutionEnd); if (origin->IsLocal()) { ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); @@ -216,7 +207,10 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons executedParams->Set("host", params->Get("host")); if (params->Contains("service")) executedParams->Set("service", params->Get("service")); - executedParams->Set("check_result", Serialize(cr)); + double now = Utility::GetTime(); + executedParams->Set("error", cr->GetOutput()); + executedParams->Set("start", now); + executedParams->Set("end", now); if (origin->IsLocal()) { ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 83b14d5d4..8bca1f1b4 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -1010,14 +1010,21 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, return Empty; } - if (!params->Contains("check_result")) { - Log(LogNotice, "ClusterEvents") - << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() - << "' from '" << origin->FromClient->GetIdentity() << "': No check result available."; - return Empty; - } + if (params->Contains("error")) + execution->Set("error", params->Get("error")); + + if (params->Contains("exit")) + execution->Set("exit", params->Get("exit")); + + if (params->Contains("output")) + execution->Set("output", params->Get("output")); + + if (params->Contains("start")) + execution->Set("start", params->Get("start")); + + if (params->Contains("end")) + execution->Set("end", params->Get("end")); - execution->Set("check_result", params->Get("check_result")); execution->Set("pending", false); /* Broadcast the update */ From d4fb5a0656edee0f80eb265baa77d518375d76d2 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Thu, 16 Jul 2020 16:41:47 +0200 Subject: [PATCH 068/126] Remove unuseful check --- lib/icinga/clusterevents-check.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 3c8261446..6805fe351 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -130,13 +130,6 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons Checkable::Ptr checkable = host; ObjectLock oLock (checkable); - if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) { - Log(LogNotice, "ApiListener") - << "Discarding 'ExecuteCheckFromQueue' event for checkable '" << checkable->GetName() - << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; - return; - } - /* Check deadline */ double deadline = params->Get("deadline"); if (Utility::GetTime() > deadline) { From 461b4e21763354a058e40c96cccfa811b4c3690d Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Thu, 16 Jul 2020 16:52:48 +0200 Subject: [PATCH 069/126] Do CurrentConcurrentChecks and DecreasePendingChecks only for check_command --- lib/icinga/clusterevents-check.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 6805fe351..4579b2878 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -140,13 +140,15 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons } Checkable::ExecuteCommandProcessFinishedHandler = [checkable, listener, sourceEndpoint, origin, params] (const Value& commandLine, const ProcessResult& pr) { - Checkable::CurrentConcurrentChecks.fetch_sub(1); - Checkable::DecreasePendingChecks(); + if (params->Get("command_type") == "check_command") { + Checkable::CurrentConcurrentChecks.fetch_sub(1); + Checkable::DecreasePendingChecks(); + } if (pr.ExitStatus > 3) { Process::Arguments parguments = Process::PrepareCommand(commandLine); Log(LogWarning, "ApiListener") - << "Check command for object '" << checkable->GetName() << "' (PID: " << pr.PID + << "Command for object '" << checkable->GetName() << "' (PID: " << pr.PID << ", arguments: " << Process::PrettyPrintArguments(parguments) << ") terminated with exit code " << pr.ExitStatus << ", output: " << pr.Output; } From f59c60f85f9c14196d73f6e61691e06248fbb9c5 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 20 Jul 2020 08:18:53 +0200 Subject: [PATCH 070/126] Send error as command execution result --- lib/icinga/clusterevents-check.cpp | 35 ++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 4579b2878..2fa75af11 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -180,21 +180,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons Log(LogWarning, "ApiListener") << "Ignoring command. '" << listener->GetName() << "' does not accept commands."; - Host::Ptr host = new Host(); - Dictionary::Ptr attrs = new Dictionary(); - - attrs->Set("__name", params->Get("host")); - attrs->Set("type", "Host"); - attrs->Set("enable_active_checks", false); - - Deserialize(host, attrs, false, FAConfig); - - if (params->Contains("service")) - host->SetExtension("agent_service_name", params->Get("service")); - - CheckResult::Ptr cr = new CheckResult(); - cr->SetState(ServiceUnknown); - cr->SetOutput("Endpoint '" + Endpoint::GetLocalEndpoint()->GetName() + "' does not accept commands."); + String output = "Endpoint '" + Endpoint::GetLocalEndpoint()->GetName() + "' does not accept commands."; if (params->Contains("source")) { Dictionary::Ptr executedParams = new Dictionary(); @@ -203,7 +189,8 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (params->Contains("service")) executedParams->Set("service", params->Get("service")); double now = Utility::GetTime(); - executedParams->Set("error", cr->GetOutput()); + executedParams->Set("exit", 126); + executedParams->Set("output", output); executedParams->Set("start", now); executedParams->Set("end", now); @@ -218,6 +205,22 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons listener->SyncSendMessage(sourceEndpoint, executedMessage); } } else { + Host::Ptr host = new Host(); + Dictionary::Ptr attrs = new Dictionary(); + + attrs->Set("__name", params->Get("host")); + attrs->Set("type", "Host"); + attrs->Set("enable_active_checks", false); + + Deserialize(host, attrs, false, FAConfig); + + if (params->Contains("service")) + host->SetExtension("agent_service_name", params->Get("service")); + + CheckResult::Ptr cr = new CheckResult(); + cr->SetState(ServiceUnknown); + cr->SetOutput(output); + Dictionary::Ptr message = MakeCheckResultMessage(host, cr); listener->SyncSendMessage(sourceEndpoint, message); } From c6d4a9ac478ffe1a410394349baada6c14e4f69e Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 20 Jul 2020 12:46:22 +0200 Subject: [PATCH 071/126] Get checakble name from params --- lib/icinga/clusterevents-check.cpp | 34 ++++++------------------------ 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 2fa75af11..6d437ef60 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -104,42 +104,20 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (params->Contains("source")) { String uuid = params->Get("source"); - Host::Ptr host = new Host(); - Dictionary::Ptr attrs = new Dictionary(); - - attrs->Set("__name", params->Get("host")); - attrs->Set("type", "Host"); - - /* - * Override the check timeout if the parent caller provided the value. Compatible with older versions not - * passing this inside the cluster message. - * This happens with host/service command_endpoint agents and the 'check_timeout' attribute being specified. - */ - if (params->Contains("check_timeout")) - attrs->Set("check_timeout", params->Get("check_timeout")); - - Deserialize(host, attrs, false, FAConfig); - + String checkableName = params->Get("host"); if (params->Contains("service")) - host->SetExtension("agent_service_name", params->Get("service")); - - attrs->Set(params->Get("command_type"), params->Get("command")); - attrs->Set("command_endpoint", sourceEndpoint->GetName()); - - Deserialize(host, attrs, false, FAConfig); - Checkable::Ptr checkable = host; - ObjectLock oLock (checkable); + checkableName += "!" + params->Get("service"); /* Check deadline */ double deadline = params->Get("deadline"); - if (Utility::GetTime() > deadline) { + if (Utility::GetTime() > deadline) {\ Log(LogNotice, "ApiListener") - << "Discarding 'ExecuteCheckFromQueue' event for checkable '" << checkable->GetName() + << "Discarding 'ExecuteCheckFromQueue' event for checkable '" << checkableName << "' from '" << origin->FromClient->GetIdentity() << "': Deadline has expired."; return; } - Checkable::ExecuteCommandProcessFinishedHandler = [checkable, listener, sourceEndpoint, origin, params] (const Value& commandLine, const ProcessResult& pr) { + Checkable::ExecuteCommandProcessFinishedHandler = [checkableName, listener, sourceEndpoint, origin, params] (const Value& commandLine, const ProcessResult& pr) { if (params->Get("command_type") == "check_command") { Checkable::CurrentConcurrentChecks.fetch_sub(1); Checkable::DecreasePendingChecks(); @@ -148,7 +126,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (pr.ExitStatus > 3) { Process::Arguments parguments = Process::PrepareCommand(commandLine); Log(LogWarning, "ApiListener") - << "Command for object '" << checkable->GetName() << "' (PID: " << pr.PID + << "Command for object '" << checkableName << "' (PID: " << pr.PID << ", arguments: " << Process::PrettyPrintArguments(parguments) << ") terminated with exit code " << pr.ExitStatus << ", output: " << pr.Output; } From acc986afd0da3a7640634598b1b4b0cc4cf02133 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 20 Jul 2020 13:15:12 +0200 Subject: [PATCH 072/126] Remove extra char --- lib/icinga/clusterevents-check.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 6d437ef60..70c647371 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -110,7 +110,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons /* Check deadline */ double deadline = params->Get("deadline"); - if (Utility::GetTime() > deadline) {\ + if (Utility::GetTime() > deadline) { Log(LogNotice, "ApiListener") << "Discarding 'ExecuteCheckFromQueue' event for checkable '" << checkableName << "' from '" << origin->FromClient->GetIdentity() << "': Deadline has expired."; From 8758e58b926df4834b3b362e72d5283376f0485b Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 20 Jul 2020 16:29:26 +0200 Subject: [PATCH 073/126] Use ExecuteCommandProcessFinishedHandler for all lib/methods/*task.cpp --- lib/methods/clusterchecktask.cpp | 54 +++++++---- lib/methods/clusterzonechecktask.cpp | 128 ++++++++++++++++++++------- lib/methods/dummychecktask.cpp | 28 ++++-- lib/methods/exceptionchecktask.cpp | 16 +++- lib/methods/icingachecktask.cpp | 28 ++++-- lib/methods/nullchecktask.cpp | 25 ++++-- lib/methods/randomchecktask.cpp | 45 ++++++---- lib/methods/sleepchecktask.cpp | 23 +++-- 8 files changed, 256 insertions(+), 91 deletions(-) diff --git a/lib/methods/clusterchecktask.cpp b/lib/methods/clusterchecktask.cpp index a9be0414d..ddefa9f84 100644 --- a/lib/methods/clusterchecktask.cpp +++ b/lib/methods/clusterchecktask.cpp @@ -29,45 +29,69 @@ void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRe return; CheckCommand::Ptr command = checkable->GetCheckCommand(); - cr->SetCommand(command->GetName()); + String commandName = command->GetName(); ApiListener::Ptr listener = ApiListener::GetInstance(); - if (!listener) { - cr->SetOutput("No API listener is configured for this instance."); - cr->SetState(ServiceUnknown); - checkable->ProcessCheckResult(cr); + 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 stats = listener->GetStatus(); - Dictionary::Ptr status = stats.first; - - /* use feature stats perfdata */ - std::pair feature_stats = CIB::GetFeatureStats(); - cr->SetPerformanceData(feature_stats.second); - 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")) + ")"; - cr->SetState(ServiceCritical); + state = ServiceCritical; } else { output += " OK: " + Convert::ToString(numConnEndpoints) + " endpoints are connected."; output += "\n(" + FormatArray(status->Get("conn_endpoints")) + ")"; - cr->SetState(ServiceOK); + state = ServiceOK; } - cr->SetOutput(output); + 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->ProcessCheckResult(cr); + Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr); + } else { + /* use feature stats perfdata */ + std::pair 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) diff --git a/lib/methods/clusterzonechecktask.cpp b/lib/methods/clusterzonechecktask.cpp index c955b7dd9..af0c66053 100644 --- a/lib/methods/clusterzonechecktask.cpp +++ b/lib/methods/clusterzonechecktask.cpp @@ -21,15 +21,32 @@ void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Che REQUIRE_NOT_NULL(cr); ApiListener::Ptr listener = ApiListener::GetInstance(); + CheckCommand::Ptr command = checkable->GetCheckCommand(); + String commandName = command->GetName(); if (!listener) { - cr->SetOutput("No API listener is configured for this instance."); - cr->SetState(ServiceUnknown); - checkable->ProcessCheckResult(cr); + 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; } - CheckCommand::Ptr command = checkable->GetCheckCommand(); Value raw_command = command->GetCommandLine(); Host::Ptr host; @@ -58,21 +75,51 @@ void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Che if (resolvedMacros && !useResolvedMacros) return; - cr->SetCommand(command->GetName()); - if (zoneName.IsEmpty()) { - cr->SetOutput("Macro 'cluster_zone' must be set."); - cr->SetState(ServiceUnknown); - checkable->ProcessCheckResult(cr); + 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) { - cr->SetOutput("Zone '" + zoneName + "' does not exist."); - cr->SetState(ServiceUnknown); - checkable->ProcessCheckResult(cr); + 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; } @@ -107,34 +154,51 @@ void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Che bytesReceivedPerSecond += endpoint->GetBytesReceivedPerSecond(); } + ServiceState state; + String output; if (connected) { - cr->SetState(ServiceOK); - cr->SetOutput("Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag)); + 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) { - cr->SetState(ServiceCritical); - cr->SetOutput("Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag) - + " greater than critical threshold: " + Utility::FormatDuration(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) { - cr->SetState(ServiceWarning); - cr->SetOutput("Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag) - + " greater than warning threshold: " + Utility::FormatDuration(lagWarning)); + state = ServiceWarning; + output = "Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag) + + " greater than warning threshold: " + Utility::FormatDuration(lagWarning); } } else { - cr->SetState(ServiceCritical); - cr->SetOutput("Zone '" + zoneName + "' is not connected. Log lag: " + Utility::FormatDuration(zoneLag)); + state = ServiceCritical; + output = "Zone '" + zoneName + "' is not connected. Log lag: " + Utility::FormatDuration(zoneLag); } - 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) - })); + 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->ProcessCheckResult(cr); + 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/dummychecktask.cpp b/lib/methods/dummychecktask.cpp index e42754f31..09398debe 100644 --- a/lib/methods/dummychecktask.cpp +++ b/lib/methods/dummychecktask.cpp @@ -48,14 +48,26 @@ void DummyCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu std::pair co = PluginUtility::ParseCheckOutput(dummyText); double now = Utility::GetTime(); + String commandName = command->GetName(); - 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(command->GetName()); + if (Checkable::ExecuteCommandProcessFinishedHandler) { + ProcessResult pr; + pr.PID = -1; + pr.Output = co.first; + pr.ExecutionStart = now; + pr.ExecutionEnd = now; + pr.ExitStatus = dummyState; - checkable->ProcessCheckResult(cr); + 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/exceptionchecktask.cpp b/lib/methods/exceptionchecktask.cpp index 5ce3a9262..6efbd51b3 100644 --- a/lib/methods/exceptionchecktask.cpp +++ b/lib/methods/exceptionchecktask.cpp @@ -23,5 +23,19 @@ void ExceptionCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Check if (resolvedMacros && !useResolvedMacros) return; - BOOST_THROW_EXCEPTION(ScriptError("Test") << boost::errinfo_api_function("Test")); + 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 = 0; + + Checkable::ExecuteCommandProcessFinishedHandler("", pr); + } else { + BOOST_THROW_EXCEPTION(ScriptError("Test") << boost::errinfo_api_function("Test")); + } } diff --git a/lib/methods/icingachecktask.cpp b/lib/methods/icingachecktask.cpp index 2c2c5c592..a5da4c801 100644 --- a/lib/methods/icingachecktask.cpp +++ b/lib/methods/icingachecktask.cpp @@ -148,7 +148,7 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes perfdata->Add(new PerfdataValue("sum_bytes_received_per_second", bytesReceivedPerSecond)); cr->SetPerformanceData(perfdata); - cr->SetState(ServiceOK); + ServiceState state = ServiceOK; String appVersion = Application::GetAppVersion(); @@ -160,7 +160,7 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (lastReloadFailed > 0) { output += "; Last reload attempt failed at " + Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", lastReloadFailed); - cr->SetState(ServiceWarning); + state =ServiceWarning; } /* Indicate a warning when the last synced config caused a stage validation error. */ @@ -173,7 +173,7 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes output += "; Last zone sync stage validation failed at " + Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", validationResult->Get("ts")); - cr->SetState(ServiceWarning); + state = ServiceWarning; } } @@ -182,11 +182,25 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes /* 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."; - cr->SetState(ServiceCritical); + state = ServiceCritical; } - cr->SetOutput(output); - cr->SetCommand(command->GetName()); + 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->ProcessCheckResult(cr); + Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr); + } else { + cr->SetState(state); + cr->SetOutput(output); + cr->SetCommand(commandName); + + checkable->ProcessCheckResult(cr); + } } diff --git a/lib/methods/nullchecktask.cpp b/lib/methods/nullchecktask.cpp index b56087b2e..ee660294e 100644 --- a/lib/methods/nullchecktask.cpp +++ b/lib/methods/nullchecktask.cpp @@ -26,12 +26,25 @@ void NullCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResul String output = "Hello from "; output += IcingaApplication::GetInstance()->GetNodeName(); + ServiceState state = ServiceOK; - cr->SetOutput(output); - cr->SetPerformanceData(new Array({ - new PerfdataValue("time", Convert::ToDouble(Utility::GetTime())) - })); - cr->SetState(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->ProcessCheckResult(cr); + 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/randomchecktask.cpp b/lib/methods/randomchecktask.cpp index 35e10d2a6..0e138ffa5 100644 --- a/lib/methods/randomchecktask.cpp +++ b/lib/methods/randomchecktask.cpp @@ -31,22 +31,35 @@ void RandomCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes + ". Icinga 2 has been running for " + Utility::FormatDuration(uptime) + ". Version: " + Application::GetAppVersion(); - 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(static_cast(Utility::Random() % 4)); - CheckCommand::Ptr command = checkable->GetCheckCommand(); - cr->SetCommand(command->GetName()); + String commandName = command->GetName(); + ServiceState state = static_cast(Utility::Random() % 4); - checkable->ProcessCheckResult(cr); + 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/sleepchecktask.cpp b/lib/methods/sleepchecktask.cpp index 47f735c36..b5f3d8891 100644 --- a/lib/methods/sleepchecktask.cpp +++ b/lib/methods/sleepchecktask.cpp @@ -42,13 +42,24 @@ void SleepCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu String output = "Slept for " + Convert::ToString(sleepTime) + " seconds."; double now = Utility::GetTime(); + CheckCommand::Ptr command = checkable->GetCheckCommand(); + String commandName = command->GetName(); - cr->SetOutput(output); - cr->SetExecutionStart(now); - cr->SetExecutionEnd(now); + if (Checkable::ExecuteCommandProcessFinishedHandler) { + ProcessResult pr; + pr.PID = -1; + pr.Output = output; + pr.ExecutionStart = now; + pr.ExecutionEnd = now; + pr.ExitStatus = 0; - CheckCommand::Ptr command = checkable->GetCheckCommand(); - cr->SetCommand(command->GetName()); + Checkable::ExecuteCommandProcessFinishedHandler("", pr); + } else { + cr->SetOutput(output); + cr->SetExecutionStart(now); + cr->SetExecutionEnd(now); + cr->SetCommand(commandName); - checkable->ProcessCheckResult(cr); + checkable->ProcessCheckResult(cr); + } } \ No newline at end of file From 986c07fd7dcec44037fe3e8360a7ceaf8119ea30 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 21 Jul 2020 08:33:43 +0200 Subject: [PATCH 074/126] Use ExecuteCommandProcessFinishedHandler in NullEventTask --- lib/methods/nulleventtask.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/methods/nulleventtask.cpp b/lib/methods/nulleventtask.cpp index 0755a4d01..3c02f234f 100644 --- a/lib/methods/nulleventtask.cpp +++ b/lib/methods/nulleventtask.cpp @@ -11,4 +11,16 @@ REGISTER_FUNCTION_NONCONST(Internal, NullEvent, &NullEventTask::ScriptFunc, "che 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); + } } From abf164bceb1c8c3480537411cbc9d54dc27087d4 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 21 Jul 2020 09:57:59 +0200 Subject: [PATCH 075/126] Use ExecuteCommandProcessFinishedHandler in TimePeriodTask --- lib/methods/timeperiodtask.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/methods/timeperiodtask.cpp b/lib/methods/timeperiodtask.cpp index bb3f1bb00..8d014d5b6 100644 --- a/lib/methods/timeperiodtask.cpp +++ b/lib/methods/timeperiodtask.cpp @@ -12,6 +12,18 @@ Array::Ptr TimePeriodTask::EmptyTimePeriodUpdate(const TimePeriod::Ptr& tp, doub { REQUIRE_NOT_NULL(tp); + 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); + } + Array::Ptr segments = new Array(); return segments; } @@ -31,5 +43,17 @@ Array::Ptr TimePeriodTask::EvenMinutesTimePeriodUpdate(const TimePeriod::Ptr& tp } } + 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); + } + return new Array(std::move(segments)); } From 867da09fb495643a585e73659d58b9dea4dddec6 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 21 Jul 2020 10:52:59 +0200 Subject: [PATCH 076/126] Revert "Use ExecuteCommandProcessFinishedHandler in TimePeriodTask" This reverts commit abf164bceb1c8c3480537411cbc9d54dc27087d4. --- lib/methods/timeperiodtask.cpp | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/lib/methods/timeperiodtask.cpp b/lib/methods/timeperiodtask.cpp index 8d014d5b6..bb3f1bb00 100644 --- a/lib/methods/timeperiodtask.cpp +++ b/lib/methods/timeperiodtask.cpp @@ -12,18 +12,6 @@ Array::Ptr TimePeriodTask::EmptyTimePeriodUpdate(const TimePeriod::Ptr& tp, doub { REQUIRE_NOT_NULL(tp); - 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); - } - Array::Ptr segments = new Array(); return segments; } @@ -43,17 +31,5 @@ Array::Ptr TimePeriodTask::EvenMinutesTimePeriodUpdate(const TimePeriod::Ptr& tp } } - 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); - } - return new Array(std::move(segments)); } From 2e8b492f2945643664b0ee42dab892ecc93aec88 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 21 Jul 2020 13:27:03 +0200 Subject: [PATCH 077/126] Implement notification execution --- lib/icinga/apiactions.cpp | 8 +++-- lib/icinga/clusterevents-check.cpp | 56 ++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 2cfd8f827..65f462499 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -676,6 +676,9 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MacroResolver::OverrideMacros = nullptr; }); + /* Create execution parameters */ + Dictionary::Ptr execParams = new Dictionary(); + if (command_type == "CheckCommand") { CheckCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(CheckCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) @@ -707,6 +710,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, User::Ptr user = GetSingleObjectByNameUsingPermissions(User::GetTypeName(), resolved_user, ActionsHandler::AuthenticatedApiUser); if (!user) return ApiActions::CreateResult(404, "Can't find a valid user for '" + resolved_user + "'."); + execParams->Set("user", user->GetName()); /* Get notification */ String notification_string = ""; @@ -722,6 +726,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, Notification::Ptr notification = GetSingleObjectByNameUsingPermissions(Notification::GetTypeName(), resolved_notification, ActionsHandler::AuthenticatedApiUser); if (!notification) return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'."); + execParams->Set("notification", notification->GetName()); cmd->Execute(notification, user, cr, NotificationType::NotificationCustom, ActionsHandler::AuthenticatedApiUser->GetName(), "", execMacros, false); @@ -761,8 +766,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, MessageOrigin::Ptr origin = new MessageOrigin(); listener->RelayMessage(origin, checkable, updateMessage, true); - /* Create execution parameters */ - Dictionary::Ptr execParams = new Dictionary(); + /* Populate execution parameters */ if (command_type == "CheckCommand") execParams->Set("command_type", "check_command"); else if (command_type == "EventCommand") diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 70c647371..9e7381ffd 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -241,11 +241,16 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons } else if (command_type == "event_command") { if (!EventCommand::GetByName(command)) { Log(LogWarning, "ClusterEvents") - << "Event command '" << command << "' does not exist."; + << "Event command '" << command << "' does not exist."; return; } - } else - return; + } else if (command_type == "notification_command") { + if (!NotificationCommand::GetByName(command)) { + Log(LogWarning, "ClusterEvents") + << "Notification command '" << command << "' does not exist."; + return; + } + } attrs->Set(command_type, params->Get("command")); attrs->Set("command_endpoint", sourceEndpoint->GetName()); @@ -279,6 +284,51 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons } } else if (command_type == "event_command") { host->ExecuteEventHandler(macros, true); + } else if (command_type == "notification_command") { + /* Get user */ + User::Ptr user = new User(); + Dictionary::Ptr attrs = new Dictionary(); + attrs->Set("__name", params->Get("user")); + attrs->Set("type", User::GetTypeName()); + + Deserialize(user, attrs, false, FAConfig); + + /* Get notification */ + Notification::Ptr notification = new Notification(); + attrs->Clear(); + attrs->Set("__name", params->Get("notification")); + attrs->Set("type", Notification::GetTypeName()); + attrs->Set("command", command); + + Deserialize(notification, attrs, false, FAConfig); + + try { + CheckResult::Ptr cr = new CheckResult(); + String author = macros->Get("notification_author"); + NotificationCommand::Ptr notificationCommand = NotificationCommand::GetByName(command); + + notificationCommand->Execute(notification, user, cr, NotificationType::NotificationCustom, + author, ""); + } catch (const std::exception& ex) { + String output = "Exception occurred during notification '" + notification->GetName() + + "' for checkable '" + notification->GetCheckable()->GetName() + + "' and user '" + user->GetName() + "' using command '" + command + "': " + + DiagnosticInformation(ex, false); + double now = Utility::GetTime(); + + CheckResult::Ptr cr = new CheckResult(); + cr->SetState(ServiceUnknown); + cr->SetOutput(output); + cr->SetScheduleStart(now); + cr->SetScheduleEnd(now); + cr->SetExecutionStart(now); + cr->SetExecutionEnd(now); + + Dictionary::Ptr message = MakeCheckResultMessage(host, cr); + listener->SyncSendMessage(sourceEndpoint, message); + + Log(LogCritical, "checker", output); + } } } From 05b1beb2ff772b2596a37a915b1b65ce225da233 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 21 Jul 2020 15:51:57 +0200 Subject: [PATCH 078/126] Send event::ExecutedCommand where necessary --- lib/icinga/clusterevents-check.cpp | 128 +++++++++++++++++++++++------ 1 file changed, 103 insertions(+), 25 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 9e7381ffd..83d73c487 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -231,11 +231,42 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (command_type == "check_command") { if (!CheckCommand::GetByName(command)) { - CheckResult::Ptr cr = new CheckResult(); - cr->SetState(ServiceUnknown); - cr->SetOutput("Check command '" + command + "' does not exist."); - Dictionary::Ptr message = MakeCheckResultMessage(host, cr); - listener->SyncSendMessage(sourceEndpoint, message); + ServiceState state = ServiceUnknown; + String output = "Check command '" + command + "' does not exist."; + double now = Utility::GetTime(); + + if (params->Contains("source")) { + Dictionary::Ptr executedParams = new Dictionary(); + executedParams->Set("execution", params->Get("source")); + executedParams->Set("host", params->Get("host")); + if (params->Contains("service")) + executedParams->Set("service", params->Get("service")); + executedParams->Set("exit", state); + executedParams->Set("output", output); + executedParams->Set("start", now); + executedParams->Set("end", now); + + if (origin->IsLocal()) { + ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); + } else { + Dictionary::Ptr executedMessage = new Dictionary(); + executedMessage->Set("jsonrpc", "2.0"); + executedMessage->Set("method", "event::ExecutedCommand"); + executedMessage->Set("params", executedParams); + + listener->SyncSendMessage(sourceEndpoint, executedMessage); + } + } else { + CheckResult::Ptr cr = new CheckResult(); + cr->SetState(state); + cr->SetOutput(output); + cr->SetScheduleStart(now); + cr->SetScheduleEnd(now); + cr->SetExecutionStart(now); + cr->SetExecutionEnd(now); + Dictionary::Ptr message = MakeCheckResultMessage(host, cr); + listener->SyncSendMessage(sourceEndpoint, message); + } return; } } else if (command_type == "event_command") { @@ -265,20 +296,43 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons try { host->ExecuteRemoteCheck(macros); } catch (const std::exception& ex) { - CheckResult::Ptr cr = new CheckResult(); - cr->SetState(ServiceUnknown); - String output = "Exception occurred while checking '" + host->GetName() + "': " + DiagnosticInformation(ex); - cr->SetOutput(output); - + ServiceState state = ServiceUnknown; double now = Utility::GetTime(); - cr->SetScheduleStart(now); - cr->SetScheduleEnd(now); - cr->SetExecutionStart(now); - cr->SetExecutionEnd(now); - Dictionary::Ptr message = MakeCheckResultMessage(host, cr); - listener->SyncSendMessage(sourceEndpoint, message); + if (params->Contains("source")) { + Dictionary::Ptr executedParams = new Dictionary(); + executedParams->Set("execution", params->Get("source")); + executedParams->Set("host", params->Get("host")); + if (params->Contains("service")) + executedParams->Set("service", params->Get("service")); + executedParams->Set("exit", state); + executedParams->Set("output", output); + executedParams->Set("start", now); + executedParams->Set("end", now); + + if (origin->IsLocal()) { + ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); + } else { + Dictionary::Ptr executedMessage = new Dictionary(); + executedMessage->Set("jsonrpc", "2.0"); + executedMessage->Set("method", "event::ExecutedCommand"); + executedMessage->Set("params", executedParams); + + listener->SyncSendMessage(sourceEndpoint, executedMessage); + } + } else { + CheckResult::Ptr cr = new CheckResult(); + cr->SetState(state); + cr->SetOutput(output); + cr->SetScheduleStart(now); + cr->SetScheduleEnd(now); + cr->SetExecutionStart(now); + cr->SetExecutionEnd(now); + + Dictionary::Ptr message = MakeCheckResultMessage(host, cr); + listener->SyncSendMessage(sourceEndpoint, message); + } Log(LogCritical, "checker", output); } @@ -315,17 +369,41 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons + "' and user '" + user->GetName() + "' using command '" + command + "': " + DiagnosticInformation(ex, false); double now = Utility::GetTime(); + ServiceState state = ServiceUnknown; - CheckResult::Ptr cr = new CheckResult(); - cr->SetState(ServiceUnknown); - cr->SetOutput(output); - cr->SetScheduleStart(now); - cr->SetScheduleEnd(now); - cr->SetExecutionStart(now); - cr->SetExecutionEnd(now); + if (params->Contains("source")) { + Dictionary::Ptr executedParams = new Dictionary(); + executedParams->Set("execution", params->Get("source")); + executedParams->Set("host", params->Get("host")); + if (params->Contains("service")) + executedParams->Set("service", params->Get("service")); + executedParams->Set("exit", state); + executedParams->Set("output", output); + executedParams->Set("start", now); + executedParams->Set("end", now); - Dictionary::Ptr message = MakeCheckResultMessage(host, cr); - listener->SyncSendMessage(sourceEndpoint, message); + if (origin->IsLocal()) { + ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); + } else { + Dictionary::Ptr executedMessage = new Dictionary(); + executedMessage->Set("jsonrpc", "2.0"); + executedMessage->Set("method", "event::ExecutedCommand"); + executedMessage->Set("params", executedParams); + + listener->SyncSendMessage(sourceEndpoint, executedMessage); + } + } else { + CheckResult::Ptr cr = new CheckResult(); + cr->SetState(state); + cr->SetOutput(output); + cr->SetScheduleStart(now); + cr->SetScheduleEnd(now); + cr->SetExecutionStart(now); + cr->SetExecutionEnd(now); + + Dictionary::Ptr message = MakeCheckResultMessage(host, cr); + listener->SyncSendMessage(sourceEndpoint, message); + } Log(LogCritical, "checker", output); } From b4d6fe2c8dc7c97477b870abd060023cc8210428 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 21 Jul 2020 16:33:52 +0200 Subject: [PATCH 079/126] Check if the node accept commands only if the origin is not local --- lib/icinga/clusterevents-check.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 83d73c487..69cdf7636 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -154,7 +154,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons }; } - if (!listener->GetAcceptCommands()) { + if (!listener->GetAcceptCommands() && !origin->IsLocal()) { Log(LogWarning, "ApiListener") << "Ignoring command. '" << listener->GetName() << "' does not accept commands."; @@ -172,16 +172,12 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons executedParams->Set("start", now); executedParams->Set("end", now); - if (origin->IsLocal()) { - ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); - } else { - Dictionary::Ptr executedMessage = new Dictionary(); - executedMessage->Set("jsonrpc", "2.0"); - executedMessage->Set("method", "event::ExecutedCommand"); - executedMessage->Set("params", executedParams); + Dictionary::Ptr executedMessage = new Dictionary(); + executedMessage->Set("jsonrpc", "2.0"); + executedMessage->Set("method", "event::ExecutedCommand"); + executedMessage->Set("params", executedParams); - listener->SyncSendMessage(sourceEndpoint, executedMessage); - } + listener->SyncSendMessage(sourceEndpoint, executedMessage); } else { Host::Ptr host = new Host(); Dictionary::Ptr attrs = new Dictionary(); From 96dc3492404279a2f5897e743ce422b7bd2f70d5 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 22 Jul 2020 11:38:41 +0200 Subject: [PATCH 080/126] Fix scriptfunc for dummychecktask, exceptionchecktask and sleepchecktask --- lib/methods/dummychecktask.cpp | 2 +- lib/methods/exceptionchecktask.cpp | 2 +- lib/methods/sleepchecktask.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/methods/dummychecktask.cpp b/lib/methods/dummychecktask.cpp index 09398debe..ce72ca233 100644 --- a/lib/methods/dummychecktask.cpp +++ b/lib/methods/dummychecktask.cpp @@ -53,7 +53,7 @@ void DummyCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu if (Checkable::ExecuteCommandProcessFinishedHandler) { ProcessResult pr; pr.PID = -1; - pr.Output = co.first; + pr.Output = dummyText; pr.ExecutionStart = now; pr.ExecutionEnd = now; pr.ExitStatus = dummyState; diff --git a/lib/methods/exceptionchecktask.cpp b/lib/methods/exceptionchecktask.cpp index 6efbd51b3..47707f26f 100644 --- a/lib/methods/exceptionchecktask.cpp +++ b/lib/methods/exceptionchecktask.cpp @@ -32,7 +32,7 @@ void ExceptionCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Check pr.Output = scriptError.what(); pr.ExecutionStart = now; pr.ExecutionEnd = now; - pr.ExitStatus = 0; + pr.ExitStatus = 3; Checkable::ExecuteCommandProcessFinishedHandler("", pr); } else { diff --git a/lib/methods/sleepchecktask.cpp b/lib/methods/sleepchecktask.cpp index b5f3d8891..55f9f8fa1 100644 --- a/lib/methods/sleepchecktask.cpp +++ b/lib/methods/sleepchecktask.cpp @@ -49,7 +49,7 @@ void SleepCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu ProcessResult pr; pr.PID = -1; pr.Output = output; - pr.ExecutionStart = now; + pr.ExecutionStart = now - sleepTime; pr.ExecutionEnd = now; pr.ExitStatus = 0; From 4e3a38f3201c84ecbc42a7cd1415d7e20d39dd6f Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 22 Jul 2020 14:16:41 +0200 Subject: [PATCH 081/126] Unify code to check if command exists --- lib/icinga/clusterevents-check.cpp | 84 +++++++++++++----------------- 1 file changed, 36 insertions(+), 48 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 69cdf7636..737af21f3 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -225,61 +225,49 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons String command = params->Get("command"); String command_type = params->Get("command_type"); - if (command_type == "check_command") { - if (!CheckCommand::GetByName(command)) { - ServiceState state = ServiceUnknown; - String output = "Check command '" + command + "' does not exist."; - double now = Utility::GetTime(); - - if (params->Contains("source")) { - Dictionary::Ptr executedParams = new Dictionary(); - executedParams->Set("execution", params->Get("source")); - executedParams->Set("host", params->Get("host")); - if (params->Contains("service")) - executedParams->Set("service", params->Get("service")); - executedParams->Set("exit", state); - executedParams->Set("output", output); - executedParams->Set("start", now); - executedParams->Set("end", now); + if (command_type == "check_command" && !CheckCommand::GetByName(command) || + command_type == "event_command" && !EventCommand::GetByName(command) || + command_type == "notification_command" && !NotificationCommand::GetByName(command)) { + ServiceState state = ServiceUnknown; + String output = command_type + " '" + command + "' does not exist."; + double now = Utility::GetTime(); - if (origin->IsLocal()) { - ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); - } else { - Dictionary::Ptr executedMessage = new Dictionary(); - executedMessage->Set("jsonrpc", "2.0"); - executedMessage->Set("method", "event::ExecutedCommand"); - executedMessage->Set("params", executedParams); + if (params->Contains("source")) { + Dictionary::Ptr executedParams = new Dictionary(); + executedParams->Set("execution", params->Get("source")); + executedParams->Set("host", params->Get("host")); + if (params->Contains("service")) + executedParams->Set("service", params->Get("service")); + executedParams->Set("exit", state); + executedParams->Set("output", output); + executedParams->Set("start", now); + executedParams->Set("end", now); - listener->SyncSendMessage(sourceEndpoint, executedMessage); - } + if (origin->IsLocal()) { + ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); } else { - CheckResult::Ptr cr = new CheckResult(); - cr->SetState(state); - cr->SetOutput(output); - cr->SetScheduleStart(now); - cr->SetScheduleEnd(now); - cr->SetExecutionStart(now); - cr->SetExecutionEnd(now); - Dictionary::Ptr message = MakeCheckResultMessage(host, cr); - listener->SyncSendMessage(sourceEndpoint, message); + Dictionary::Ptr executedMessage = new Dictionary(); + executedMessage->Set("jsonrpc", "2.0"); + executedMessage->Set("method", "event::ExecutedCommand"); + executedMessage->Set("params", executedParams); + + listener->SyncSendMessage(sourceEndpoint, executedMessage); } - return; - } - } else if (command_type == "event_command") { - if (!EventCommand::GetByName(command)) { - Log(LogWarning, "ClusterEvents") - << "Event command '" << command << "' does not exist."; - return; - } - } else if (command_type == "notification_command") { - if (!NotificationCommand::GetByName(command)) { - Log(LogWarning, "ClusterEvents") - << "Notification command '" << command << "' does not exist."; - return; + } else { + CheckResult::Ptr cr = new CheckResult(); + cr->SetState(state); + cr->SetOutput(output); + cr->SetScheduleStart(now); + cr->SetScheduleEnd(now); + cr->SetExecutionStart(now); + cr->SetExecutionEnd(now); + Dictionary::Ptr message = MakeCheckResultMessage(host, cr); + listener->SyncSendMessage(sourceEndpoint, message); } + return; } - attrs->Set(command_type, params->Get("command")); + attrs->Set(command_type, command); attrs->Set("command_endpoint", sourceEndpoint->GetName()); Deserialize(host, attrs, false, FAConfig); From e690eaf3c43954de1cfd3adb0c4fa7a38927c069 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 22 Jul 2020 17:22:50 +0200 Subject: [PATCH 082/126] Run ExecutedCommandAPIHandler in case of command not found and if source is set --- lib/icinga/clusterevents-check.cpp | 136 +++++++++++++++++++++-------- 1 file changed, 100 insertions(+), 36 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 737af21f3..2242b3964 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -225,49 +225,113 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons String command = params->Get("command"); String command_type = params->Get("command_type"); - if (command_type == "check_command" && !CheckCommand::GetByName(command) || - command_type == "event_command" && !EventCommand::GetByName(command) || - command_type == "notification_command" && !NotificationCommand::GetByName(command)) { - ServiceState state = ServiceUnknown; - String output = command_type + " '" + command + "' does not exist."; - double now = Utility::GetTime(); + if (command_type == "check_command") { + if (!CheckCommand::GetByName(command)) { + ServiceState state = ServiceUnknown; + String output = "Check command '" + command + "' does not exist."; + double now = Utility::GetTime(); + + if (params->Contains("source")) { + Dictionary::Ptr executedParams = new Dictionary(); + executedParams->Set("execution", params->Get("source")); + executedParams->Set("host", params->Get("host")); + if (params->Contains("service")) + executedParams->Set("service", params->Get("service")); + executedParams->Set("exit", state); + executedParams->Set("output", output); + executedParams->Set("start", now); + executedParams->Set("end", now); - if (params->Contains("source")) { - Dictionary::Ptr executedParams = new Dictionary(); - executedParams->Set("execution", params->Get("source")); - executedParams->Set("host", params->Get("host")); - if (params->Contains("service")) - executedParams->Set("service", params->Get("service")); - executedParams->Set("exit", state); - executedParams->Set("output", output); - executedParams->Set("start", now); - executedParams->Set("end", now); + if (origin->IsLocal()) { + ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); + } else { + Dictionary::Ptr executedMessage = new Dictionary(); + executedMessage->Set("jsonrpc", "2.0"); + executedMessage->Set("method", "event::ExecutedCommand"); + executedMessage->Set("params", executedParams); - if (origin->IsLocal()) { - ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); + listener->SyncSendMessage(sourceEndpoint, executedMessage); + } } else { - Dictionary::Ptr executedMessage = new Dictionary(); - executedMessage->Set("jsonrpc", "2.0"); - executedMessage->Set("method", "event::ExecutedCommand"); - executedMessage->Set("params", executedParams); - - listener->SyncSendMessage(sourceEndpoint, executedMessage); + CheckResult::Ptr cr = new CheckResult(); + cr->SetState(state); + cr->SetOutput(output); + cr->SetScheduleStart(now); + cr->SetScheduleEnd(now); + cr->SetExecutionStart(now); + cr->SetExecutionEnd(now); + Dictionary::Ptr message = MakeCheckResultMessage(host, cr); + listener->SyncSendMessage(sourceEndpoint, message); } - } else { - CheckResult::Ptr cr = new CheckResult(); - cr->SetState(state); - cr->SetOutput(output); - cr->SetScheduleStart(now); - cr->SetScheduleEnd(now); - cr->SetExecutionStart(now); - cr->SetExecutionEnd(now); - Dictionary::Ptr message = MakeCheckResultMessage(host, cr); - listener->SyncSendMessage(sourceEndpoint, message); + return; + } + } else if (command_type == "event_command") { + if (!EventCommand::GetByName(command)) { + String output = "Event command '" + command + "' does not exist."; + Log(LogWarning, "ClusterEvents") << output; + + if (params->Contains("source")) { + double now = Utility::GetTime(); + ServiceState state = ServiceUnknown; + + Dictionary::Ptr executedParams = new Dictionary(); + executedParams->Set("execution", params->Get("source")); + executedParams->Set("host", params->Get("host")); + if (params->Contains("service")) + executedParams->Set("service", params->Get("service")); + executedParams->Set("exit", state); + executedParams->Set("output", output); + executedParams->Set("start", now); + executedParams->Set("end", now); + + if (origin->IsLocal()) { + ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); + } else { + Dictionary::Ptr executedMessage = new Dictionary(); + executedMessage->Set("jsonrpc", "2.0"); + executedMessage->Set("method", "event::ExecutedCommand"); + executedMessage->Set("params", executedParams); + + listener->SyncSendMessage(sourceEndpoint, executedMessage); + } + } + return; + } + } else if (command_type == "notification_command") { + if (!NotificationCommand::GetByName(command)) { + String output = "Notification command '" + command + "' does not exist."; + Log(LogWarning, "ClusterEvents") << output; + + if (params->Contains("source")) { + double now = Utility::GetTime(); + ServiceState state = ServiceUnknown; + + Dictionary::Ptr executedParams = new Dictionary(); + executedParams->Set("execution", params->Get("source")); + executedParams->Set("host", params->Get("host")); + if (params->Contains("service")) + executedParams->Set("service", params->Get("service")); + executedParams->Set("exit", state); + executedParams->Set("output", output); + executedParams->Set("start", now); + executedParams->Set("end", now); + + if (origin->IsLocal()) { + ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); + } else { + Dictionary::Ptr executedMessage = new Dictionary(); + executedMessage->Set("jsonrpc", "2.0"); + executedMessage->Set("method", "event::ExecutedCommand"); + executedMessage->Set("params", executedParams); + + listener->SyncSendMessage(sourceEndpoint, executedMessage); + } + } + return; } - return; } - attrs->Set(command_type, command); + attrs->Set(command_type, params->Get("command")); attrs->Set("command_endpoint", sourceEndpoint->GetName()); Deserialize(host, attrs, false, FAConfig); From 6656afb259faf02ca9d81e28c2220e2ee7ab1ab8 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Thu, 23 Jul 2020 08:26:31 +0200 Subject: [PATCH 083/126] Code refactoring: create sendEventExecuteCommand function --- lib/icinga/clusterevents-check.cpp | 174 ++++++----------------------- 1 file changed, 32 insertions(+), 142 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 2242b3964..42e04d602 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -75,6 +75,31 @@ void ClusterEvents::EnqueueCheck(const MessageOrigin::Ptr& origin, const Diction } } +static void sendEventExecuteCommand(const Dictionary::Ptr& params, const long& exit, const String& output, + const double& start, const double& end, const ApiListener::Ptr& listener, const MessageOrigin::Ptr& origin, + const Endpoint::Ptr& sourceEndpoint) { + Dictionary::Ptr executedParams = new Dictionary(); + executedParams->Set("execution", params->Get("source")); + executedParams->Set("host", params->Get("host")); + if (params->Contains("service")) + executedParams->Set("service", params->Get("service")); + executedParams->Set("exit", exit); + executedParams->Set("output", output); + executedParams->Set("start", start); + executedParams->Set("end", end); + + if (origin->IsLocal()) { + ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); + } else { + Dictionary::Ptr executedMessage = new Dictionary(); + executedMessage->Set("jsonrpc", "2.0"); + executedMessage->Set("method", "event::ExecutedCommand"); + executedMessage->Set("params", executedParams); + + listener->SyncSendMessage(sourceEndpoint, executedMessage); + } +} + void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { Endpoint::Ptr sourceEndpoint; @@ -131,26 +156,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons << pr.ExitStatus << ", output: " << pr.Output; } - Dictionary::Ptr executedParams = new Dictionary(); - executedParams->Set("execution", params->Get("source")); - executedParams->Set("host", params->Get("host")); - if (params->Contains("service")) - executedParams->Set("service", params->Get("service")); - executedParams->Set("exit", pr.ExitStatus); - executedParams->Set("output", pr.Output); - executedParams->Set("start", pr.ExecutionStart); - executedParams->Set("end", pr.ExecutionEnd); - - if (origin->IsLocal()) { - ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); - } else { - Dictionary::Ptr executedMessage = new Dictionary(); - executedMessage->Set("jsonrpc", "2.0"); - executedMessage->Set("method", "event::ExecutedCommand"); - executedMessage->Set("params", executedParams); - - listener->SyncSendMessage(sourceEndpoint, executedMessage); - } + sendEventExecuteCommand(params, pr.ExitStatus, pr.Output, pr.ExecutionStart, pr.ExecutionEnd, listener, origin, sourceEndpoint); }; } @@ -161,23 +167,8 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons String output = "Endpoint '" + Endpoint::GetLocalEndpoint()->GetName() + "' does not accept commands."; if (params->Contains("source")) { - Dictionary::Ptr executedParams = new Dictionary(); - executedParams->Set("execution", params->Get("source")); - executedParams->Set("host", params->Get("host")); - if (params->Contains("service")) - executedParams->Set("service", params->Get("service")); double now = Utility::GetTime(); - executedParams->Set("exit", 126); - executedParams->Set("output", output); - executedParams->Set("start", now); - executedParams->Set("end", now); - - Dictionary::Ptr executedMessage = new Dictionary(); - executedMessage->Set("jsonrpc", "2.0"); - executedMessage->Set("method", "event::ExecutedCommand"); - executedMessage->Set("params", executedParams); - - listener->SyncSendMessage(sourceEndpoint, executedMessage); + sendEventExecuteCommand(params, 126, output, now, now, listener, origin, sourceEndpoint); } else { Host::Ptr host = new Host(); Dictionary::Ptr attrs = new Dictionary(); @@ -232,34 +223,11 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons double now = Utility::GetTime(); if (params->Contains("source")) { - Dictionary::Ptr executedParams = new Dictionary(); - executedParams->Set("execution", params->Get("source")); - executedParams->Set("host", params->Get("host")); - if (params->Contains("service")) - executedParams->Set("service", params->Get("service")); - executedParams->Set("exit", state); - executedParams->Set("output", output); - executedParams->Set("start", now); - executedParams->Set("end", now); - - if (origin->IsLocal()) { - ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); - } else { - Dictionary::Ptr executedMessage = new Dictionary(); - executedMessage->Set("jsonrpc", "2.0"); - executedMessage->Set("method", "event::ExecutedCommand"); - executedMessage->Set("params", executedParams); - - listener->SyncSendMessage(sourceEndpoint, executedMessage); - } + sendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); } else { CheckResult::Ptr cr = new CheckResult(); cr->SetState(state); cr->SetOutput(output); - cr->SetScheduleStart(now); - cr->SetScheduleEnd(now); - cr->SetExecutionStart(now); - cr->SetExecutionEnd(now); Dictionary::Ptr message = MakeCheckResultMessage(host, cr); listener->SyncSendMessage(sourceEndpoint, message); } @@ -273,27 +241,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (params->Contains("source")) { double now = Utility::GetTime(); ServiceState state = ServiceUnknown; - - Dictionary::Ptr executedParams = new Dictionary(); - executedParams->Set("execution", params->Get("source")); - executedParams->Set("host", params->Get("host")); - if (params->Contains("service")) - executedParams->Set("service", params->Get("service")); - executedParams->Set("exit", state); - executedParams->Set("output", output); - executedParams->Set("start", now); - executedParams->Set("end", now); - - if (origin->IsLocal()) { - ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); - } else { - Dictionary::Ptr executedMessage = new Dictionary(); - executedMessage->Set("jsonrpc", "2.0"); - executedMessage->Set("method", "event::ExecutedCommand"); - executedMessage->Set("params", executedParams); - - listener->SyncSendMessage(sourceEndpoint, executedMessage); - } + sendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); } return; } @@ -305,27 +253,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (params->Contains("source")) { double now = Utility::GetTime(); ServiceState state = ServiceUnknown; - - Dictionary::Ptr executedParams = new Dictionary(); - executedParams->Set("execution", params->Get("source")); - executedParams->Set("host", params->Get("host")); - if (params->Contains("service")) - executedParams->Set("service", params->Get("service")); - executedParams->Set("exit", state); - executedParams->Set("output", output); - executedParams->Set("start", now); - executedParams->Set("end", now); - - if (origin->IsLocal()) { - ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); - } else { - Dictionary::Ptr executedMessage = new Dictionary(); - executedMessage->Set("jsonrpc", "2.0"); - executedMessage->Set("method", "event::ExecutedCommand"); - executedMessage->Set("params", executedParams); - - listener->SyncSendMessage(sourceEndpoint, executedMessage); - } + sendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); } return; } @@ -349,26 +277,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons double now = Utility::GetTime(); if (params->Contains("source")) { - Dictionary::Ptr executedParams = new Dictionary(); - executedParams->Set("execution", params->Get("source")); - executedParams->Set("host", params->Get("host")); - if (params->Contains("service")) - executedParams->Set("service", params->Get("service")); - executedParams->Set("exit", state); - executedParams->Set("output", output); - executedParams->Set("start", now); - executedParams->Set("end", now); - - if (origin->IsLocal()) { - ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); - } else { - Dictionary::Ptr executedMessage = new Dictionary(); - executedMessage->Set("jsonrpc", "2.0"); - executedMessage->Set("method", "event::ExecutedCommand"); - executedMessage->Set("params", executedParams); - - listener->SyncSendMessage(sourceEndpoint, executedMessage); - } + sendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); } else { CheckResult::Ptr cr = new CheckResult(); cr->SetState(state); @@ -420,26 +329,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons ServiceState state = ServiceUnknown; if (params->Contains("source")) { - Dictionary::Ptr executedParams = new Dictionary(); - executedParams->Set("execution", params->Get("source")); - executedParams->Set("host", params->Get("host")); - if (params->Contains("service")) - executedParams->Set("service", params->Get("service")); - executedParams->Set("exit", state); - executedParams->Set("output", output); - executedParams->Set("start", now); - executedParams->Set("end", now); - - if (origin->IsLocal()) { - ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); - } else { - Dictionary::Ptr executedMessage = new Dictionary(); - executedMessage->Set("jsonrpc", "2.0"); - executedMessage->Set("method", "event::ExecutedCommand"); - executedMessage->Set("params", executedParams); - - listener->SyncSendMessage(sourceEndpoint, executedMessage); - } + sendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); } else { CheckResult::Ptr cr = new CheckResult(); cr->SetState(state); From 9940aaf90c0752f745c80e62369c02bac61c0438 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Thu, 23 Jul 2020 11:31:44 +0200 Subject: [PATCH 084/126] Refactor SendEventExecuteCommand function --- lib/icinga/clusterevents-check.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 42e04d602..17e8e9649 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -75,15 +75,15 @@ void ClusterEvents::EnqueueCheck(const MessageOrigin::Ptr& origin, const Diction } } -static void sendEventExecuteCommand(const Dictionary::Ptr& params, const long& exit, const String& output, - const double& start, const double& end, const ApiListener::Ptr& listener, const MessageOrigin::Ptr& origin, - const Endpoint::Ptr& sourceEndpoint) { +static void SendEventExecuteCommand(const Dictionary::Ptr& params, long exitStatus, const String& output, + double start, double end, const ApiListener::Ptr& listener, const MessageOrigin::Ptr& origin, + const Endpoint::Ptr& sourceEndpoint) { Dictionary::Ptr executedParams = new Dictionary(); executedParams->Set("execution", params->Get("source")); executedParams->Set("host", params->Get("host")); if (params->Contains("service")) executedParams->Set("service", params->Get("service")); - executedParams->Set("exit", exit); + executedParams->Set("exit", exitStatus); executedParams->Set("output", output); executedParams->Set("start", start); executedParams->Set("end", end); @@ -156,7 +156,8 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons << pr.ExitStatus << ", output: " << pr.Output; } - sendEventExecuteCommand(params, pr.ExitStatus, pr.Output, pr.ExecutionStart, pr.ExecutionEnd, listener, origin, sourceEndpoint); + SendEventExecuteCommand(params, pr.ExitStatus, pr.Output, pr.ExecutionStart, pr.ExecutionEnd, listener, + origin, sourceEndpoint); }; } @@ -168,7 +169,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (params->Contains("source")) { double now = Utility::GetTime(); - sendEventExecuteCommand(params, 126, output, now, now, listener, origin, sourceEndpoint); + SendEventExecuteCommand(params, 126, output, now, now, listener, origin, sourceEndpoint); } else { Host::Ptr host = new Host(); Dictionary::Ptr attrs = new Dictionary(); @@ -223,7 +224,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons double now = Utility::GetTime(); if (params->Contains("source")) { - sendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); + SendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); } else { CheckResult::Ptr cr = new CheckResult(); cr->SetState(state); @@ -241,7 +242,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (params->Contains("source")) { double now = Utility::GetTime(); ServiceState state = ServiceUnknown; - sendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); + SendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); } return; } @@ -253,7 +254,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (params->Contains("source")) { double now = Utility::GetTime(); ServiceState state = ServiceUnknown; - sendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); + SendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); } return; } @@ -277,7 +278,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons double now = Utility::GetTime(); if (params->Contains("source")) { - sendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); + SendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); } else { CheckResult::Ptr cr = new CheckResult(); cr->SetState(state); @@ -329,7 +330,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons ServiceState state = ServiceUnknown; if (params->Contains("source")) { - sendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); + SendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); } else { CheckResult::Ptr cr = new CheckResult(); cr->SetState(state); From 14c02ec654f006d386e60c30d819667c454e5f6f Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Thu, 23 Jul 2020 12:58:50 +0200 Subject: [PATCH 085/126] Remove unuseful check result for notification command --- lib/icinga/clusterevents-check.cpp | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 17e8e9649..af124cb32 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -322,29 +322,15 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons notificationCommand->Execute(notification, user, cr, NotificationType::NotificationCustom, author, ""); } catch (const std::exception& ex) { - String output = "Exception occurred during notification '" + notification->GetName() - + "' for checkable '" + notification->GetCheckable()->GetName() - + "' and user '" + user->GetName() + "' using command '" + command + "': " - + DiagnosticInformation(ex, false); - double now = Utility::GetTime(); - ServiceState state = ServiceUnknown; - if (params->Contains("source")) { + String output = "Exception occurred during notification '" + notification->GetName() + + "' for checkable '" + notification->GetCheckable()->GetName() + + "' and user '" + user->GetName() + "' using command '" + command + "': " + + DiagnosticInformation(ex, false); + double now = Utility::GetTime(); + ServiceState state = ServiceUnknown; SendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); - } else { - CheckResult::Ptr cr = new CheckResult(); - cr->SetState(state); - cr->SetOutput(output); - cr->SetScheduleStart(now); - cr->SetScheduleEnd(now); - cr->SetExecutionStart(now); - cr->SetExecutionEnd(now); - - Dictionary::Ptr message = MakeCheckResultMessage(host, cr); - listener->SyncSendMessage(sourceEndpoint, message); } - - Log(LogCritical, "checker", output); } } } From b30960fda5d1245a997a81a35100adea8f2ac4d3 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Thu, 23 Jul 2020 13:03:30 +0200 Subject: [PATCH 086/126] Add try catch for ExecuteEventHandler --- lib/icinga/clusterevents-check.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index af124cb32..646a7bd39 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -295,7 +295,18 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons Log(LogCritical, "checker", output); } } else if (command_type == "event_command") { - host->ExecuteEventHandler(macros, true); + try { + host->ExecuteEventHandler(macros, true); + } catch (const std::exception& ex) { + if (params->Contains("source")) { + String output = "Exception occurred while executing event command '" + command + "' for '" + + host->GetName() + "': " + DiagnosticInformation(ex); + + double now = Utility::GetTime(); + ServiceState state = ServiceUnknown; + SendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); + } + } } else if (command_type == "notification_command") { /* Get user */ User::Ptr user = new User(); From 53659066f2e56075f413755f3833d89ead3d2742 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Thu, 23 Jul 2020 13:20:31 +0200 Subject: [PATCH 087/126] Remove unuseful variables --- lib/icinga/clusterevents-check.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 646a7bd39..7058a68f6 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -241,8 +241,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (params->Contains("source")) { double now = Utility::GetTime(); - ServiceState state = ServiceUnknown; - SendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); + SendEventExecuteCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); } return; } @@ -253,8 +252,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (params->Contains("source")) { double now = Utility::GetTime(); - ServiceState state = ServiceUnknown; - SendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); + SendEventExecuteCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); } return; } @@ -303,8 +301,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons host->GetName() + "': " + DiagnosticInformation(ex); double now = Utility::GetTime(); - ServiceState state = ServiceUnknown; - SendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); + SendEventExecuteCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); } } } else if (command_type == "notification_command") { @@ -339,8 +336,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons + "' and user '" + user->GetName() + "' using command '" + command + "': " + DiagnosticInformation(ex, false); double now = Utility::GetTime(); - ServiceState state = ServiceUnknown; - SendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); + SendEventExecuteCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); } } } From aadedc0d1ce21152c9c90040ac4541304deba637 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Thu, 23 Jul 2020 13:23:13 +0200 Subject: [PATCH 088/126] Throw an exception in case of error during ExecuteEventHandler if source is not set --- lib/icinga/clusterevents-check.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 7058a68f6..13bf02dac 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -302,6 +302,8 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons double now = Utility::GetTime(); SendEventExecuteCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); + } else { + throw ex; } } } else if (command_type == "notification_command") { From bf07ada3df1081d5c9c12ae8c46d98456e56709c Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Thu, 23 Jul 2020 13:34:26 +0200 Subject: [PATCH 089/126] Remove arguments from 'throw' inc case of ExecuteEventHandler error --- lib/icinga/clusterevents-check.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 13bf02dac..ac5aed797 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -303,7 +303,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons double now = Utility::GetTime(); SendEventExecuteCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); } else { - throw ex; + throw; } } } else if (command_type == "notification_command") { From 1e59810dddfc0bef70b25fb92770197decf0e654 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 29 Jul 2020 08:38:49 +0200 Subject: [PATCH 090/126] Fix indentation --- lib/icinga/clusterevents.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 8bca1f1b4..4e453257f 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -957,8 +957,8 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, if (!endpoint) { Log(LogNotice, "ClusterEvents") - << "Discarding 'update executions API handler' message from '" << origin->FromClient->GetIdentity() - << "': Invalid endpoint origin (client not allowed)."; + << "Discarding 'update executions API handler' message from '" << origin->FromClient->GetIdentity() + << "': Invalid endpoint origin (client not allowed)."; return Empty; } @@ -981,15 +981,15 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, if (origin->FromZone && !origin->FromZone->CanAccessObject(checkable)) { Log(LogNotice, "ClusterEvents") - << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() - << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; return Empty; } if (!params->Contains("execution")) { Log(LogNotice, "ClusterEvents") - << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() - << "' from '" << origin->FromClient->GetIdentity() << "': Execution UUID not found."; + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': Execution UUID not found."; return Empty; } String uuid = params->Get("execution"); @@ -997,16 +997,16 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, Dictionary::Ptr executions = checkable->GetExecutions(); if (!executions) { Log(LogNotice, "ClusterEvents") - << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() - << "' from '" << origin->FromClient->GetIdentity() << "': No executions available."; + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': No executions available."; return Empty; } Dictionary::Ptr execution = executions->Get(uuid); if (!execution) { Log(LogNotice, "ClusterEvents") - << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() - << "' from '" << origin->FromClient->GetIdentity() << "': Execution '" << uuid << "' not found."; + << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() + << "' from '" << origin->FromClient->GetIdentity() << "': Execution '" << uuid << "' not found."; return Empty; } From 5a4fa6965031332e91f1c73ed2b3826547f725fe Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 29 Jul 2020 08:43:30 +0200 Subject: [PATCH 091/126] Fix logs --- lib/icinga/clusterevents.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 4e453257f..4d8783a39 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -989,7 +989,7 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, if (!params->Contains("execution")) { Log(LogNotice, "ClusterEvents") << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() - << "' from '" << origin->FromClient->GetIdentity() << "': Execution UUID not found."; + << "' from '" << origin->FromClient->GetIdentity() << "': Execution UUID missing."; return Empty; } String uuid = params->Get("execution"); @@ -998,7 +998,7 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, if (!executions) { Log(LogNotice, "ClusterEvents") << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() - << "' from '" << origin->FromClient->GetIdentity() << "': No executions available."; + << "' from '" << origin->FromClient->GetIdentity() << "': Execution '" << uuid << "' missing."; return Empty; } @@ -1006,7 +1006,7 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, if (!execution) { Log(LogNotice, "ClusterEvents") << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() - << "' from '" << origin->FromClient->GetIdentity() << "': Execution '" << uuid << "' not found."; + << "' from '" << origin->FromClient->GetIdentity() << "': Execution '" << uuid << "' missing."; return Empty; } From 2cda17a99bb09ea86808fa3b7c50f616928d5d09 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 29 Jul 2020 08:45:19 +0200 Subject: [PATCH 092/126] Remove pending key when the execution is completed --- lib/icinga/clusterevents.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 4d8783a39..44bb6bc6a 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -1025,7 +1025,7 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, if (params->Contains("end")) execution->Set("end", params->Get("end")); - execution->Set("pending", false); + execution->Remove("pending"); /* Broadcast the update */ Dictionary::Ptr executionsToBroadcast = new Dictionary(); From a834b5cecd7026f209a1b2905e9394a6cb75ce33 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 29 Jul 2020 08:46:09 +0200 Subject: [PATCH 093/126] Remove unused key in the execution --- lib/icinga/clusterevents.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 44bb6bc6a..66eec6da1 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -1010,9 +1010,6 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, return Empty; } - if (params->Contains("error")) - execution->Set("error", params->Get("error")); - if (params->Contains("exit")) execution->Set("exit", params->Get("exit")); From edb5c47a9dbb337891cc4d7b2c676facc5ed157b Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 29 Jul 2020 08:52:15 +0200 Subject: [PATCH 094/126] Execute notification command only if there is a source param --- lib/icinga/clusterevents-check.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index ac5aed797..5be2b43bb 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -306,7 +306,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons throw; } } - } else if (command_type == "notification_command") { + } else if (command_type == "notification_command" && params->Contains("source")) { /* Get user */ User::Ptr user = new User(); Dictionary::Ptr attrs = new Dictionary(); @@ -332,14 +332,12 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons notificationCommand->Execute(notification, user, cr, NotificationType::NotificationCustom, author, ""); } catch (const std::exception& ex) { - if (params->Contains("source")) { - String output = "Exception occurred during notification '" + notification->GetName() - + "' for checkable '" + notification->GetCheckable()->GetName() - + "' and user '" + user->GetName() + "' using command '" + command + "': " - + DiagnosticInformation(ex, false); - double now = Utility::GetTime(); - SendEventExecuteCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); - } + String output = "Exception occurred during notification '" + notification->GetName() + + "' for checkable '" + notification->GetCheckable()->GetName() + + "' and user '" + user->GetName() + "' using command '" + command + "': " + + DiagnosticInformation(ex, false); + double now = Utility::GetTime(); + SendEventExecuteCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); } } } From 064d5e91947c7d24883595337a94a6bc172d3881 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 29 Jul 2020 08:57:22 +0200 Subject: [PATCH 095/126] Keep consistency with other methods in case of missing API listener --- lib/icinga/clusterevents.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 66eec6da1..70aca9765 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -946,7 +946,7 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, { ApiListener::Ptr listener = ApiListener::GetInstance(); if (!listener) - BOOST_THROW_EXCEPTION(std::invalid_argument("No ApiListener instance configured.")); + return Empty; Endpoint::Ptr endpoint; if (origin->FromClient) { From 75e0e164e4d7fc22f004f11cab7316ce0a9b24cf Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 29 Jul 2020 09:54:18 +0200 Subject: [PATCH 096/126] Fix indentation --- lib/icinga/clusterevents-check.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 5be2b43bb..0ca53030c 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -111,7 +111,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (!sourceEndpoint || (origin->FromZone && !Zone::GetLocalZone()->IsChildOf(origin->FromZone))) { Log(LogNotice, "ClusterEvents") - << "Discarding 'execute command' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed)."; + << "Discarding 'execute command' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed)."; return; } @@ -163,7 +163,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (!listener->GetAcceptCommands() && !origin->IsLocal()) { Log(LogWarning, "ApiListener") - << "Ignoring command. '" << listener->GetName() << "' does not accept commands."; + << "Ignoring command. '" << listener->GetName() << "' does not accept commands."; String output = "Endpoint '" + Endpoint::GetLocalEndpoint()->GetName() + "' does not accept commands."; From a5698d5fdee6079a09022a9d891d8cfcc3b68e12 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 31 Jul 2020 08:53:10 +0200 Subject: [PATCH 097/126] Check if last check result is null before executing the command --- lib/icinga/apiactions.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 65f462499..fa5fdf7a2 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -667,6 +667,8 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, ); CheckResult::Ptr cr = checkable->GetLastCheckResult(); + if (!cr) + cr = new CheckResult(); /* Check if resolved_command exists and it is of type command_type */ Dictionary::Ptr execMacros = new Dictionary(); From 44fc841ee1c9a64b4eaa38d1e93b8e867b06fbd0 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 31 Jul 2020 10:42:01 +0200 Subject: [PATCH 098/126] Notify to all nodes that execution has completed --- lib/icinga/clusterevents-check.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 0ca53030c..63b2478f9 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -88,9 +88,9 @@ static void SendEventExecuteCommand(const Dictionary::Ptr& params, long exitStat executedParams->Set("start", start); executedParams->Set("end", end); - if (origin->IsLocal()) { - ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); - } else { + ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); + + if (!origin->IsLocal()) { Dictionary::Ptr executedMessage = new Dictionary(); executedMessage->Set("jsonrpc", "2.0"); executedMessage->Set("method", "event::ExecutedCommand"); From 604b938ade23e3cbd1cd8c29b37a21768510e2b7 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 31 Jul 2020 11:15:17 +0200 Subject: [PATCH 099/126] Fix macros substitutions --- lib/icinga/apiactions.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index fa5fdf7a2..97561397e 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -619,8 +619,9 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, endpoint = HttpUtility::GetLastParameter(params, "endpoint"); MacroProcessor::ResolverList resolvers; + Value macros; if (params->Contains("macros")) { - Value macros = HttpUtility::GetLastParameter(params, "macros"); + macros = HttpUtility::GetLastParameter(params, "macros"); if (macros.IsObjectType()) { resolvers.emplace_back("override", macros); } @@ -673,7 +674,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Check if resolved_command exists and it is of type command_type */ Dictionary::Ptr execMacros = new Dictionary(); - MacroResolver::OverrideMacros = execMacros; + MacroResolver::OverrideMacros = macros; Defer o ([]() { MacroResolver::OverrideMacros = nullptr; }); From cf1430c40978fa89fe1a48ef44e8ba3d4d3966fc Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 31 Jul 2020 14:07:48 +0200 Subject: [PATCH 100/126] Fix update execution --- lib/icinga/clusterevents-check.cpp | 6 +++--- lib/icinga/clusterevents.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 63b2478f9..0ca53030c 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -88,9 +88,9 @@ static void SendEventExecuteCommand(const Dictionary::Ptr& params, long exitStat executedParams->Set("start", start); executedParams->Set("end", end); - ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); - - if (!origin->IsLocal()) { + if (origin->IsLocal()) { + ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); + } else { Dictionary::Ptr executedMessage = new Dictionary(); executedMessage->Set("jsonrpc", "2.0"); executedMessage->Set("method", "event::ExecutedCommand"); diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 70aca9765..5817d8cc8 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -1038,7 +1038,7 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, updateMessage->Set("method", "event::UpdateExecutions"); updateMessage->Set("params", updateParams); - listener->RelayMessage(origin, checkable, updateMessage, true); + listener->RelayMessage(nullptr, checkable, updateMessage, true); return Empty; } From d7dadbfc661b80b00c4c71a721943e186f8f1eda Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 31 Jul 2020 14:21:09 +0200 Subject: [PATCH 101/126] Use MacroResolver::OverrideMacros on *task files --- lib/methods/clusterzonechecktask.cpp | 3 +++ lib/methods/dummychecktask.cpp | 3 +++ lib/methods/icingachecktask.cpp | 3 +++ lib/methods/plugineventtask.cpp | 3 +++ lib/methods/pluginnotificationtask.cpp | 3 +++ lib/methods/sleepchecktask.cpp | 5 ++++- 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/methods/clusterzonechecktask.cpp b/lib/methods/clusterzonechecktask.cpp index af0c66053..8b9d771e4 100644 --- a/lib/methods/clusterzonechecktask.cpp +++ b/lib/methods/clusterzonechecktask.cpp @@ -54,6 +54,9 @@ void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Che 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); diff --git a/lib/methods/dummychecktask.cpp b/lib/methods/dummychecktask.cpp index ce72ca233..56bdb790a 100644 --- a/lib/methods/dummychecktask.cpp +++ b/lib/methods/dummychecktask.cpp @@ -29,6 +29,9 @@ void DummyCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu 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); diff --git a/lib/methods/icingachecktask.cpp b/lib/methods/icingachecktask.cpp index a5da4c801..9901ebd3a 100644 --- a/lib/methods/icingachecktask.cpp +++ b/lib/methods/icingachecktask.cpp @@ -33,6 +33,9 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes 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); diff --git a/lib/methods/plugineventtask.cpp b/lib/methods/plugineventtask.cpp index 9296df0f1..f511480f7 100644 --- a/lib/methods/plugineventtask.cpp +++ b/lib/methods/plugineventtask.cpp @@ -28,6 +28,9 @@ void PluginEventTask::ScriptFunc(const Checkable::Ptr& checkable, 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); diff --git a/lib/methods/pluginnotificationtask.cpp b/lib/methods/pluginnotificationtask.cpp index b81708cf3..c1eb2e3d4 100644 --- a/lib/methods/pluginnotificationtask.cpp +++ b/lib/methods/pluginnotificationtask.cpp @@ -42,6 +42,9 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification, 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); diff --git a/lib/methods/sleepchecktask.cpp b/lib/methods/sleepchecktask.cpp index 55f9f8fa1..30a395c00 100644 --- a/lib/methods/sleepchecktask.cpp +++ b/lib/methods/sleepchecktask.cpp @@ -24,7 +24,10 @@ void SleepCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu Service::Ptr service; tie(host, service) = GetHostService(checkable); - MacroProcessor::ResolverList resolvers; + MacroProcessor::ResolverList resolvers; + if (MacroResolver::OverrideMacros) + resolvers.emplace_back("override", MacroResolver::OverrideMacros); + if (service) resolvers.emplace_back("service", service); resolvers.emplace_back("host", host); From 9c4a3aed1bce9897289a9bce1c74a8ed92a3e957 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Fri, 31 Jul 2020 17:28:33 +0200 Subject: [PATCH 102/126] Use ExecuteOverride to override the command --- lib/icinga/apiactions.cpp | 19 +++++++++++++++++-- lib/icinga/checkcommand.cpp | 2 ++ lib/icinga/checkcommand.hpp | 2 ++ lib/icinga/eventcommand.cpp | 2 ++ lib/icinga/eventcommand.hpp | 2 ++ lib/icinga/notificationcommand.cpp | 2 ++ lib/icinga/notificationcommand.hpp | 2 ++ lib/methods/clusterchecktask.cpp | 6 +++++- lib/methods/clusterzonechecktask.cpp | 6 +++++- lib/methods/dummychecktask.cpp | 6 +++++- lib/methods/icingachecktask.cpp | 6 +++++- lib/methods/pluginchecktask.cpp | 6 +++++- lib/methods/plugineventtask.cpp | 6 +++++- lib/methods/pluginnotificationtask.cpp | 6 +++++- lib/methods/randomchecktask.cpp | 6 +++++- lib/methods/sleepchecktask.cpp | 6 +++++- 16 files changed, 74 insertions(+), 11 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 97561397e..e7d8acd88 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -686,14 +686,24 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, CheckCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(CheckCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); - else + else { + CheckCommand::ExecuteOverride = cmd; + Defer resetCheckCommandOverride([]() { + CheckCommand::ExecuteOverride = nullptr; + }); cmd->Execute(checkable, cr, execMacros, false); + } } else if (command_type == "EventCommand") { EventCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(EventCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); - else + else { + EventCommand::ExecuteOverride = cmd; + Defer resetCheckCommandOverride([]() { + EventCommand::ExecuteOverride = nullptr; + }); cmd->Execute(checkable, execMacros, false); + } } else if (command_type == "NotificationCommand") { NotificationCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(NotificationCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); if (!cmd) @@ -731,6 +741,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'."); execParams->Set("notification", notification->GetName()); + NotificationCommand::ExecuteOverride = cmd; + Defer resetCheckCommandOverride([]() { + NotificationCommand::ExecuteOverride = nullptr; + }); + cmd->Execute(notification, user, cr, NotificationType::NotificationCustom, ActionsHandler::AuthenticatedApiUser->GetName(), "", execMacros, false); } diff --git a/lib/icinga/checkcommand.cpp b/lib/icinga/checkcommand.cpp index e0da41547..fb8032a19 100644 --- a/lib/icinga/checkcommand.cpp +++ b/lib/icinga/checkcommand.cpp @@ -8,6 +8,8 @@ using namespace icinga; REGISTER_TYPE(CheckCommand); +thread_local CheckCommand::Ptr CheckCommand::ExecuteOverride; + void CheckCommand::Execute(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) { diff --git a/lib/icinga/checkcommand.hpp b/lib/icinga/checkcommand.hpp index 6eb6119a3..eb8f5a012 100644 --- a/lib/icinga/checkcommand.hpp +++ b/lib/icinga/checkcommand.hpp @@ -20,6 +20,8 @@ public: DECLARE_OBJECT(CheckCommand); DECLARE_OBJECTNAME(CheckCommand); + static thread_local CheckCommand::Ptr ExecuteOverride; + virtual void Execute(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros = nullptr, bool useResolvedMacros = false); diff --git a/lib/icinga/eventcommand.cpp b/lib/icinga/eventcommand.cpp index f9ab3be19..39f2d3126 100644 --- a/lib/icinga/eventcommand.cpp +++ b/lib/icinga/eventcommand.cpp @@ -7,6 +7,8 @@ using namespace icinga; REGISTER_TYPE(EventCommand); +thread_local EventCommand::Ptr EventCommand::ExecuteOverride; + void EventCommand::Execute(const Checkable::Ptr& checkable, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) { diff --git a/lib/icinga/eventcommand.hpp b/lib/icinga/eventcommand.hpp index 95bd1095a..064cb5ad0 100644 --- a/lib/icinga/eventcommand.hpp +++ b/lib/icinga/eventcommand.hpp @@ -20,6 +20,8 @@ public: DECLARE_OBJECT(EventCommand); DECLARE_OBJECTNAME(EventCommand); + static thread_local EventCommand::Ptr ExecuteOverride; + virtual void Execute(const Checkable::Ptr& checkable, const Dictionary::Ptr& resolvedMacros = nullptr, bool useResolvedMacros = false); diff --git a/lib/icinga/notificationcommand.cpp b/lib/icinga/notificationcommand.cpp index 8ae3e82a5..d4a5fd6ab 100644 --- a/lib/icinga/notificationcommand.cpp +++ b/lib/icinga/notificationcommand.cpp @@ -7,6 +7,8 @@ using namespace icinga; REGISTER_TYPE(NotificationCommand); +thread_local NotificationCommand::Ptr NotificationCommand::ExecuteOverride; + Dictionary::Ptr NotificationCommand::Execute(const Notification::Ptr& notification, const User::Ptr& user, const CheckResult::Ptr& cr, const NotificationType& type, const String& author, const String& comment, const Dictionary::Ptr& resolvedMacros, diff --git a/lib/icinga/notificationcommand.hpp b/lib/icinga/notificationcommand.hpp index 210c91e86..f0f6899e3 100644 --- a/lib/icinga/notificationcommand.hpp +++ b/lib/icinga/notificationcommand.hpp @@ -22,6 +22,8 @@ public: DECLARE_OBJECT(NotificationCommand); DECLARE_OBJECTNAME(NotificationCommand); + static thread_local NotificationCommand::Ptr ExecuteOverride; + virtual Dictionary::Ptr Execute(const intrusive_ptr& notification, const User::Ptr& user, const CheckResult::Ptr& cr, const NotificationType& type, const String& author, const String& comment, diff --git a/lib/methods/clusterchecktask.cpp b/lib/methods/clusterchecktask.cpp index ddefa9f84..1fb02633d 100644 --- a/lib/methods/clusterchecktask.cpp +++ b/lib/methods/clusterchecktask.cpp @@ -28,7 +28,11 @@ void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRe if (resolvedMacros && !useResolvedMacros) return; - CheckCommand::Ptr command = checkable->GetCheckCommand(); + CheckCommand::Ptr command; + if (CheckCommand::ExecuteOverride) + command = CheckCommand::ExecuteOverride; + else + command = checkable->GetCheckCommand(); String commandName = command->GetName(); ApiListener::Ptr listener = ApiListener::GetInstance(); diff --git a/lib/methods/clusterzonechecktask.cpp b/lib/methods/clusterzonechecktask.cpp index 8b9d771e4..024288ed7 100644 --- a/lib/methods/clusterzonechecktask.cpp +++ b/lib/methods/clusterzonechecktask.cpp @@ -21,7 +21,11 @@ void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Che REQUIRE_NOT_NULL(cr); ApiListener::Ptr listener = ApiListener::GetInstance(); - CheckCommand::Ptr command = checkable->GetCheckCommand(); + CheckCommand::Ptr command; + if (CheckCommand::ExecuteOverride) + command = CheckCommand::ExecuteOverride; + else + command = checkable->GetCheckCommand(); String commandName = command->GetName(); if (!listener) { diff --git a/lib/methods/dummychecktask.cpp b/lib/methods/dummychecktask.cpp index 56bdb790a..ae44a1d63 100644 --- a/lib/methods/dummychecktask.cpp +++ b/lib/methods/dummychecktask.cpp @@ -22,7 +22,11 @@ void DummyCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu REQUIRE_NOT_NULL(checkable); REQUIRE_NOT_NULL(cr); - CheckCommand::Ptr command = checkable->GetCheckCommand(); + CheckCommand::Ptr command; + if (CheckCommand::ExecuteOverride) + command = CheckCommand::ExecuteOverride; + else + command = checkable->GetCheckCommand(); Host::Ptr host; Service::Ptr service; diff --git a/lib/methods/icingachecktask.cpp b/lib/methods/icingachecktask.cpp index 9901ebd3a..efbdcc3c9 100644 --- a/lib/methods/icingachecktask.cpp +++ b/lib/methods/icingachecktask.cpp @@ -26,7 +26,11 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes REQUIRE_NOT_NULL(checkable); REQUIRE_NOT_NULL(cr); - CheckCommand::Ptr command = checkable->GetCheckCommand(); + CheckCommand::Ptr command; + if (CheckCommand::ExecuteOverride) + command = CheckCommand::ExecuteOverride; + else + command = checkable->GetCheckCommand(); Host::Ptr host; Service::Ptr service; diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp index 9dcbd7936..0825bf1b4 100644 --- a/lib/methods/pluginchecktask.cpp +++ b/lib/methods/pluginchecktask.cpp @@ -22,7 +22,11 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes REQUIRE_NOT_NULL(checkable); REQUIRE_NOT_NULL(cr); - CheckCommand::Ptr commandObj = checkable->GetCheckCommand(); + CheckCommand::Ptr commandObj; + if (CheckCommand::ExecuteOverride) + commandObj = CheckCommand::ExecuteOverride; + else + commandObj = checkable->GetCheckCommand(); Host::Ptr host; Service::Ptr service; diff --git a/lib/methods/plugineventtask.cpp b/lib/methods/plugineventtask.cpp index f511480f7..7a7932a9d 100644 --- a/lib/methods/plugineventtask.cpp +++ b/lib/methods/plugineventtask.cpp @@ -21,7 +21,11 @@ void PluginEventTask::ScriptFunc(const Checkable::Ptr& checkable, { REQUIRE_NOT_NULL(checkable); - EventCommand::Ptr commandObj = checkable->GetEventCommand(); + EventCommand::Ptr commandObj; + if (EventCommand::ExecuteOverride) + commandObj = EventCommand::ExecuteOverride; + else + commandObj = checkable->GetEventCommand(); Host::Ptr host; Service::Ptr service; diff --git a/lib/methods/pluginnotificationtask.cpp b/lib/methods/pluginnotificationtask.cpp index c1eb2e3d4..79485bab2 100644 --- a/lib/methods/pluginnotificationtask.cpp +++ b/lib/methods/pluginnotificationtask.cpp @@ -25,7 +25,11 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification, REQUIRE_NOT_NULL(notification); REQUIRE_NOT_NULL(user); - NotificationCommand::Ptr commandObj = notification->GetCommand(); + NotificationCommand::Ptr commandObj; + if (NotificationCommand::ExecuteOverride) + commandObj = NotificationCommand::ExecuteOverride; + else + commandObj = notification->GetCommand(); auto type = static_cast(itype); diff --git a/lib/methods/randomchecktask.cpp b/lib/methods/randomchecktask.cpp index 0e138ffa5..083d07516 100644 --- a/lib/methods/randomchecktask.cpp +++ b/lib/methods/randomchecktask.cpp @@ -31,7 +31,11 @@ void RandomCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes + ". Icinga 2 has been running for " + Utility::FormatDuration(uptime) + ". Version: " + Application::GetAppVersion(); - CheckCommand::Ptr command = checkable->GetCheckCommand(); + CheckCommand::Ptr command; + if (CheckCommand::ExecuteOverride) + command = CheckCommand::ExecuteOverride; + else + command = checkable->GetCheckCommand(); String commandName = command->GetName(); ServiceState state = static_cast(Utility::Random() % 4); diff --git a/lib/methods/sleepchecktask.cpp b/lib/methods/sleepchecktask.cpp index 30a395c00..a3e62d133 100644 --- a/lib/methods/sleepchecktask.cpp +++ b/lib/methods/sleepchecktask.cpp @@ -18,7 +18,11 @@ void SleepCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu REQUIRE_NOT_NULL(checkable); REQUIRE_NOT_NULL(cr); - CheckCommand::Ptr commandObj = checkable->GetCheckCommand(); + CheckCommand::Ptr commandObj; + if (CheckCommand::ExecuteOverride) + commandObj = CheckCommand::ExecuteOverride; + else + commandObj = checkable->GetCheckCommand(); Host::Ptr host; Service::Ptr service; From c2f8d6df4486843c2c4b54b6e7125cb61c0842d5 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 3 Aug 2020 08:07:32 +0200 Subject: [PATCH 103/126] Use ternary operator to get ExecuteOverride or checkable command --- lib/methods/clusterchecktask.cpp | 6 +----- lib/methods/clusterzonechecktask.cpp | 6 +----- lib/methods/dummychecktask.cpp | 6 +----- lib/methods/icingachecktask.cpp | 6 +----- lib/methods/pluginchecktask.cpp | 6 +----- lib/methods/plugineventtask.cpp | 6 +----- lib/methods/pluginnotificationtask.cpp | 6 +----- lib/methods/randomchecktask.cpp | 6 +----- lib/methods/sleepchecktask.cpp | 6 +----- 9 files changed, 9 insertions(+), 45 deletions(-) diff --git a/lib/methods/clusterchecktask.cpp b/lib/methods/clusterchecktask.cpp index 1fb02633d..eef3900ea 100644 --- a/lib/methods/clusterchecktask.cpp +++ b/lib/methods/clusterchecktask.cpp @@ -28,11 +28,7 @@ void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRe if (resolvedMacros && !useResolvedMacros) return; - CheckCommand::Ptr command; - if (CheckCommand::ExecuteOverride) - command = CheckCommand::ExecuteOverride; - else - command = checkable->GetCheckCommand(); + CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); String commandName = command->GetName(); ApiListener::Ptr listener = ApiListener::GetInstance(); diff --git a/lib/methods/clusterzonechecktask.cpp b/lib/methods/clusterzonechecktask.cpp index 024288ed7..0d5889a40 100644 --- a/lib/methods/clusterzonechecktask.cpp +++ b/lib/methods/clusterzonechecktask.cpp @@ -21,11 +21,7 @@ void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Che REQUIRE_NOT_NULL(cr); ApiListener::Ptr listener = ApiListener::GetInstance(); - CheckCommand::Ptr command; - if (CheckCommand::ExecuteOverride) - command = CheckCommand::ExecuteOverride; - else - command = checkable->GetCheckCommand(); + CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); String commandName = command->GetName(); if (!listener) { diff --git a/lib/methods/dummychecktask.cpp b/lib/methods/dummychecktask.cpp index ae44a1d63..45c773fcb 100644 --- a/lib/methods/dummychecktask.cpp +++ b/lib/methods/dummychecktask.cpp @@ -22,11 +22,7 @@ void DummyCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu REQUIRE_NOT_NULL(checkable); REQUIRE_NOT_NULL(cr); - CheckCommand::Ptr command; - if (CheckCommand::ExecuteOverride) - command = CheckCommand::ExecuteOverride; - else - command = checkable->GetCheckCommand(); + CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); Host::Ptr host; Service::Ptr service; diff --git a/lib/methods/icingachecktask.cpp b/lib/methods/icingachecktask.cpp index efbdcc3c9..d697b6925 100644 --- a/lib/methods/icingachecktask.cpp +++ b/lib/methods/icingachecktask.cpp @@ -26,11 +26,7 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes REQUIRE_NOT_NULL(checkable); REQUIRE_NOT_NULL(cr); - CheckCommand::Ptr command; - if (CheckCommand::ExecuteOverride) - command = CheckCommand::ExecuteOverride; - else - command = checkable->GetCheckCommand(); + CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); Host::Ptr host; Service::Ptr service; diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp index 0825bf1b4..83d36c96d 100644 --- a/lib/methods/pluginchecktask.cpp +++ b/lib/methods/pluginchecktask.cpp @@ -22,11 +22,7 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes REQUIRE_NOT_NULL(checkable); REQUIRE_NOT_NULL(cr); - CheckCommand::Ptr commandObj; - if (CheckCommand::ExecuteOverride) - commandObj = CheckCommand::ExecuteOverride; - else - commandObj = checkable->GetCheckCommand(); + CheckCommand::Ptr commandObj = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); Host::Ptr host; Service::Ptr service; diff --git a/lib/methods/plugineventtask.cpp b/lib/methods/plugineventtask.cpp index 7a7932a9d..9c4401303 100644 --- a/lib/methods/plugineventtask.cpp +++ b/lib/methods/plugineventtask.cpp @@ -21,11 +21,7 @@ void PluginEventTask::ScriptFunc(const Checkable::Ptr& checkable, { REQUIRE_NOT_NULL(checkable); - EventCommand::Ptr commandObj; - if (EventCommand::ExecuteOverride) - commandObj = EventCommand::ExecuteOverride; - else - commandObj = checkable->GetEventCommand(); + EventCommand::Ptr commandObj = EventCommand::ExecuteOverride ? EventCommand::ExecuteOverride : checkable->GetEventCommand(); Host::Ptr host; Service::Ptr service; diff --git a/lib/methods/pluginnotificationtask.cpp b/lib/methods/pluginnotificationtask.cpp index 79485bab2..ae89cc245 100644 --- a/lib/methods/pluginnotificationtask.cpp +++ b/lib/methods/pluginnotificationtask.cpp @@ -25,11 +25,7 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification, REQUIRE_NOT_NULL(notification); REQUIRE_NOT_NULL(user); - NotificationCommand::Ptr commandObj; - if (NotificationCommand::ExecuteOverride) - commandObj = NotificationCommand::ExecuteOverride; - else - commandObj = notification->GetCommand(); + NotificationCommand::Ptr commandObj = NotificationCommand::ExecuteOverride ? NotificationCommand::ExecuteOverride : notification->GetCommand(); auto type = static_cast(itype); diff --git a/lib/methods/randomchecktask.cpp b/lib/methods/randomchecktask.cpp index 083d07516..9b133ef0e 100644 --- a/lib/methods/randomchecktask.cpp +++ b/lib/methods/randomchecktask.cpp @@ -31,11 +31,7 @@ void RandomCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes + ". Icinga 2 has been running for " + Utility::FormatDuration(uptime) + ". Version: " + Application::GetAppVersion(); - CheckCommand::Ptr command; - if (CheckCommand::ExecuteOverride) - command = CheckCommand::ExecuteOverride; - else - command = checkable->GetCheckCommand(); + CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); String commandName = command->GetName(); ServiceState state = static_cast(Utility::Random() % 4); diff --git a/lib/methods/sleepchecktask.cpp b/lib/methods/sleepchecktask.cpp index a3e62d133..ed3a5b6ee 100644 --- a/lib/methods/sleepchecktask.cpp +++ b/lib/methods/sleepchecktask.cpp @@ -18,11 +18,7 @@ void SleepCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu REQUIRE_NOT_NULL(checkable); REQUIRE_NOT_NULL(cr); - CheckCommand::Ptr commandObj; - if (CheckCommand::ExecuteOverride) - commandObj = CheckCommand::ExecuteOverride; - else - commandObj = checkable->GetCheckCommand(); + CheckCommand::Ptr commandObj = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); Host::Ptr host; Service::Ptr service; From 951388797a00b7144c2ec1fb29d093212006e29a Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 3 Aug 2020 21:09:57 +0200 Subject: [PATCH 104/126] Forward the execute command through the zones --- lib/icinga/apiactions.cpp | 3 ++- lib/icinga/clusterevents.cpp | 40 +++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index e7d8acd88..977d0ee3e 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -806,6 +806,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, execParams->Set("source", uuid); execParams->Set("deadline", deadline); execParams->Set("macros", execMacros); + execParams->Set("endpoint", resolved_endpoint); /* Execute command */ bool local = endpointPtr == Endpoint::GetLocalEndpoint(); @@ -817,7 +818,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, execMessage->Set("method", "event::ExecuteCommand"); execMessage->Set("params", execParams); - listener->SyncSendMessage(endpointPtr, execMessage); + listener->RelayMessage(origin, checkable, execMessage, true); } Dictionary::Ptr result = new Dictionary(); diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 5817d8cc8..bc1d2d126 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -619,7 +619,29 @@ Value ClusterEvents::AcknowledgementClearedAPIHandler(const MessageOrigin::Ptr& Value ClusterEvents::ExecuteCommandAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { - EnqueueCheck(origin, params); + ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) + return Empty; + + Endpoint::Ptr execEndpoint = Endpoint::GetByName(params->Get("endpoint")); + if (execEndpoint != Endpoint::GetLocalEndpoint()) { + + Zone::Ptr endpointZone = execEndpoint->GetZone(); + Zone::Ptr localZone = Zone::GetLocalZone(); + + if (!endpointZone->IsChildOf(localZone)) { + return Empty; + } + + Dictionary::Ptr execMessage = new Dictionary(); + execMessage->Set("jsonrpc", "2.0"); + execMessage->Set("method", "event::ExecuteCommand"); + execMessage->Set("params", params); + + listener->RelayMessage(origin, endpointZone, execMessage, true); + } else { + EnqueueCheck(origin, params); + } return Empty; } @@ -1084,5 +1106,21 @@ Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin newExecutions->CopyTo(executions); checkable->SetExecutions(executions); + ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) + return Empty; + + Dictionary::Ptr updateMessage = new Dictionary(); + updateMessage->Set("jsonrpc", "2.0"); + updateMessage->Set("method", "event::UpdateExecutions"); + updateMessage->Set("params", params); + + Zone::Ptr localZone = Zone::GetLocalZone(); + Zone::Ptr parentZone = localZone->GetParent(); + + if (localZone != parentZone) { + listener->RelayMessage(origin, parentZone, updateMessage, true); + } + return Empty; } From c6c18491067d04d35148a513c486397e9d078671 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 4 Aug 2020 14:32:36 +0200 Subject: [PATCH 105/126] Change checkable with the endpoint zone for execute command relay message --- lib/icinga/apiactions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 977d0ee3e..9f139e540 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -818,7 +818,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, execMessage->Set("method", "event::ExecuteCommand"); execMessage->Set("params", execParams); - listener->RelayMessage(origin, checkable, execMessage, true); + listener->RelayMessage(origin, endpointPtr->GetZone(), execMessage, true); } Dictionary::Ptr result = new Dictionary(); From ac71cc67f8cfde9e178bf3853ff51ce2a0c0648f Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 4 Aug 2020 16:09:21 +0200 Subject: [PATCH 106/126] Use local zone for update executions --- lib/icinga/clusterevents.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index bc1d2d126..93d2a762e 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -1115,12 +1115,7 @@ Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin updateMessage->Set("method", "event::UpdateExecutions"); updateMessage->Set("params", params); - Zone::Ptr localZone = Zone::GetLocalZone(); - Zone::Ptr parentZone = localZone->GetParent(); - - if (localZone != parentZone) { - listener->RelayMessage(origin, parentZone, updateMessage, true); - } + listener->RelayMessage(origin, Zone::GetLocalZone(), updateMessage, true); return Empty; } From a90068cc783dd19db28543492f84acf742ce64a2 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 5 Aug 2020 10:01:29 +0200 Subject: [PATCH 107/126] Check satellites Icinga version before relay the execute command message --- lib/icinga/apiactions.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 9f139e540..86c8616d2 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -813,6 +813,21 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, if (local) { ClusterEvents::ExecuteCommandAPIHandler(origin, execParams); } else { + /* Check if the child endpoints have Icinga version >= 2.13 */ + Zone::Ptr localZone = Zone::GetLocalZone(); + for (const Zone::Ptr& zone : ConfigType::GetObjectsByType()) { + /* Fetch immediate child zone members */ + if (zone->GetParent() == localZone) { + std::set endpoints = zone->GetEndpoints(); + + for (const Endpoint::Ptr& childEndpoint : endpoints) { + if (childEndpoint->GetIcingaVersion() < 21300) { + return ApiActions::CreateResult(400, "Endpoint '" + childEndpoint->GetName() + "' has version < 2.13."); + } + } + } + } + Dictionary::Ptr execMessage = new Dictionary(); execMessage->Set("jsonrpc", "2.0"); execMessage->Set("method", "event::ExecuteCommand"); From 7c004af6beb34e66a153dc3fa0c6bde2309d8092 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 5 Aug 2020 14:08:54 +0200 Subject: [PATCH 108/126] Check child endpoint versions and check child zone can access to the target endpoint --- lib/icinga/apiactions.cpp | 18 +++++++++++++++-- lib/icinga/clusterevents.cpp | 38 ++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 86c8616d2..cf77aea4b 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -817,12 +817,26 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, Zone::Ptr localZone = Zone::GetLocalZone(); for (const Zone::Ptr& zone : ConfigType::GetObjectsByType()) { /* Fetch immediate child zone members */ - if (zone->GetParent() == localZone) { + if (zone->GetParent() == localZone && zone->CanAccessObject(endpointPtr->GetZone())) { std::set endpoints = zone->GetEndpoints(); for (const Endpoint::Ptr& childEndpoint : endpoints) { if (childEndpoint->GetIcingaVersion() < 21300) { - return ApiActions::CreateResult(400, "Endpoint '" + childEndpoint->GetName() + "' has version < 2.13."); + /* Update execution */ + double now = Utility::GetTime(); + pending_execution->Set("exit", 2); + pending_execution->Set("output", "Endpoint '" + childEndpoint->GetName() + "' has version < 2.13."); + pending_execution->Set("start", now); + pending_execution->Set("end", now); + pending_execution->Remove("pending"); + + checkable->SetExecutions(executions); + listener->RelayMessage(origin, checkable, updateMessage, true); + + Dictionary::Ptr result = new Dictionary(); + result->Set("checkable", checkable->GetName()); + result->Set("execution", uuid); + return ApiActions::CreateResult(202, "Accepted", result); } } } diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 93d2a762e..b1426ae76 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -633,6 +633,44 @@ Value ClusterEvents::ExecuteCommandAPIHandler(const MessageOrigin::Ptr& origin, return Empty; } + /* Check if the child endpoints have Icinga version >= 2.13 */ + for (const Zone::Ptr& zone : ConfigType::GetObjectsByType()) { + /* Fetch immediate child zone members */ + if (zone->GetParent() == localZone && zone->CanAccessObject(endpointZone)) { + std::set endpoints = zone->GetEndpoints(); + + for (const Endpoint::Ptr& childEndpoint : endpoints) { + if (childEndpoint->GetIcingaVersion() < 21300) { + /* Update execution */ + double now = Utility::GetTime(); + Dictionary::Ptr execution = new Dictionary(); + execution->Set("exit", 2); + execution->Set("output", "Endpoint '" + childEndpoint->GetName() + "' has version < 2.13."); + execution->Set("start", now); + execution->Set("end", now); + + Dictionary::Ptr executionsToBroadcast = new Dictionary(); + executionsToBroadcast->Set(params->Get("source"), execution); + + Dictionary::Ptr updateParams = new Dictionary(); + updateParams->Set("host", params->Get("host")); + if (params->Contains("service")) + updateParams->Set("service", params->Get("service")); + updateParams->Set("executions", executionsToBroadcast); + + Dictionary::Ptr updateMessage = new Dictionary(); + updateMessage->Set("jsonrpc", "2.0"); + updateMessage->Set("method", "event::UpdateExecutions"); + updateMessage->Set("params", executionsToBroadcast); + + listener->RelayMessage(nullptr, nullptr, updateMessage, true); + + return Empty; + } + } + } + } + Dictionary::Ptr execMessage = new Dictionary(); execMessage->Set("jsonrpc", "2.0"); execMessage->Set("method", "event::ExecuteCommand"); From 7474ab6de570cb5bdd44acfe26716fe95ad6d3b7 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 5 Aug 2020 15:53:34 +0200 Subject: [PATCH 109/126] Set exit code 126 if endpoint doens't support the new executeCommand API --- lib/icinga/apiactions.cpp | 3 +- lib/icinga/clusterevents.cpp | 92 ++++++++++++++++++------------------ 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index cf77aea4b..67c3990c0 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -824,13 +824,12 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, if (childEndpoint->GetIcingaVersion() < 21300) { /* Update execution */ double now = Utility::GetTime(); - pending_execution->Set("exit", 2); + pending_execution->Set("exit", 126); pending_execution->Set("output", "Endpoint '" + childEndpoint->GetName() + "' has version < 2.13."); pending_execution->Set("start", now); pending_execution->Set("end", now); pending_execution->Remove("pending"); - checkable->SetExecutions(executions); listener->RelayMessage(origin, checkable, updateMessage, true); Dictionary::Ptr result = new Dictionary(); diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index b1426ae76..4e3b3aa5f 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -623,64 +623,66 @@ Value ClusterEvents::ExecuteCommandAPIHandler(const MessageOrigin::Ptr& origin, if (!listener) return Empty; - Endpoint::Ptr execEndpoint = Endpoint::GetByName(params->Get("endpoint")); - if (execEndpoint != Endpoint::GetLocalEndpoint()) { + if (params->Contains("endpoint")) { + Endpoint::Ptr execEndpoint = Endpoint::GetByName(params->Get("endpoint")); + if (execEndpoint != Endpoint::GetLocalEndpoint()) { - Zone::Ptr endpointZone = execEndpoint->GetZone(); - Zone::Ptr localZone = Zone::GetLocalZone(); + Zone::Ptr endpointZone = execEndpoint->GetZone(); + Zone::Ptr localZone = Zone::GetLocalZone(); - if (!endpointZone->IsChildOf(localZone)) { - return Empty; - } + if (!endpointZone->IsChildOf(localZone)) { + return Empty; + } - /* Check if the child endpoints have Icinga version >= 2.13 */ - for (const Zone::Ptr& zone : ConfigType::GetObjectsByType()) { - /* Fetch immediate child zone members */ - if (zone->GetParent() == localZone && zone->CanAccessObject(endpointZone)) { - std::set endpoints = zone->GetEndpoints(); + /* Check if the child endpoints have Icinga version >= 2.13 */ + for (const Zone::Ptr &zone : ConfigType::GetObjectsByType()) { + /* Fetch immediate child zone members */ + if (zone->GetParent() == localZone && zone->CanAccessObject(endpointZone)) { + std::set endpoints = zone->GetEndpoints(); - for (const Endpoint::Ptr& childEndpoint : endpoints) { - if (childEndpoint->GetIcingaVersion() < 21300) { - /* Update execution */ - double now = Utility::GetTime(); - Dictionary::Ptr execution = new Dictionary(); - execution->Set("exit", 2); - execution->Set("output", "Endpoint '" + childEndpoint->GetName() + "' has version < 2.13."); - execution->Set("start", now); - execution->Set("end", now); + for (const Endpoint::Ptr &childEndpoint : endpoints) { + if (childEndpoint->GetIcingaVersion() < 21300) { + double now = Utility::GetTime(); + Dictionary::Ptr executedParams = new Dictionary(); + executedParams->Set("execution", params->Get("source")); + executedParams->Set("host", params->Get("host")); + if (params->Contains("service")) + executedParams->Set("service", params->Get("service")); + executedParams->Set("exit", 126); + executedParams->Set("output", + "Endpoint '" + childEndpoint->GetName() + "' has version < 2.13."); + executedParams->Set("start", now); + executedParams->Set("end", now); - Dictionary::Ptr executionsToBroadcast = new Dictionary(); - executionsToBroadcast->Set(params->Get("source"), execution); + if (origin->IsLocal()) { + ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); + } else { + Dictionary::Ptr executedMessage = new Dictionary(); + executedMessage->Set("jsonrpc", "2.0"); + executedMessage->Set("method", "event::ExecutedCommand"); + executedMessage->Set("params", executedParams); - Dictionary::Ptr updateParams = new Dictionary(); - updateParams->Set("host", params->Get("host")); - if (params->Contains("service")) - updateParams->Set("service", params->Get("service")); - updateParams->Set("executions", executionsToBroadcast); + listener->RelayMessage(nullptr, nullptr, executedMessage, true); + } - Dictionary::Ptr updateMessage = new Dictionary(); - updateMessage->Set("jsonrpc", "2.0"); - updateMessage->Set("method", "event::UpdateExecutions"); - updateMessage->Set("params", executionsToBroadcast); - - listener->RelayMessage(nullptr, nullptr, updateMessage, true); - - return Empty; + return Empty; + } } } } + + Dictionary::Ptr execMessage = new Dictionary(); + execMessage->Set("jsonrpc", "2.0"); + execMessage->Set("method", "event::ExecuteCommand"); + execMessage->Set("params", params); + + listener->RelayMessage(origin, endpointZone, execMessage, true); + return Empty; } - - Dictionary::Ptr execMessage = new Dictionary(); - execMessage->Set("jsonrpc", "2.0"); - execMessage->Set("method", "event::ExecuteCommand"); - execMessage->Set("params", params); - - listener->RelayMessage(origin, endpointZone, execMessage, true); - } else { - EnqueueCheck(origin, params); } + EnqueueCheck(origin, params); + return Empty; } From df2b82f7fe791b98f03acd766ce15db245dba6e9 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 5 Aug 2020 16:14:57 +0200 Subject: [PATCH 110/126] Remove an useless check --- lib/icinga/clusterevents.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 4e3b3aa5f..56e8ef976 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -654,17 +654,12 @@ Value ClusterEvents::ExecuteCommandAPIHandler(const MessageOrigin::Ptr& origin, executedParams->Set("start", now); executedParams->Set("end", now); - if (origin->IsLocal()) { - ClusterEvents::ExecutedCommandAPIHandler(origin, executedParams); - } else { - Dictionary::Ptr executedMessage = new Dictionary(); - executedMessage->Set("jsonrpc", "2.0"); - executedMessage->Set("method", "event::ExecutedCommand"); - executedMessage->Set("params", executedParams); - - listener->RelayMessage(nullptr, nullptr, executedMessage, true); - } + Dictionary::Ptr executedMessage = new Dictionary(); + executedMessage->Set("jsonrpc", "2.0"); + executedMessage->Set("method", "event::ExecutedCommand"); + executedMessage->Set("params", executedParams); + listener->RelayMessage(nullptr, nullptr, executedMessage, true); return Empty; } } From 80dc908fcac4e77bcc7a212c6e6e37c9f57ec257 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Thu, 13 Aug 2020 09:26:19 +0200 Subject: [PATCH 111/126] Use ExecuteCommand::ExecuteOverride also for ido check --- lib/db_ido/idochecktask.cpp | 180 ++++++++++++++++++++++++++++-------- 1 file changed, 144 insertions(+), 36 deletions(-) diff --git a/lib/db_ido/idochecktask.cpp b/lib/db_ido/idochecktask.cpp index 8a9536dc3..22febdaf5 100644 --- a/lib/db_ido/idochecktask.cpp +++ b/lib/db_ido/idochecktask.cpp @@ -20,7 +20,8 @@ REGISTER_FUNCTION_NONCONST(Internal, IdoCheck, &IdoCheckTask::ScriptFunc, "check void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) { - CheckCommand::Ptr commandObj = checkable->GetCheckCommand(); + ServiceState state; + CheckCommand::Ptr commandObj = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand(); Value raw_command = commandObj->GetCommandLine(); Host::Ptr host; @@ -28,6 +29,9 @@ void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult 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); @@ -61,25 +65,70 @@ void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult return; if (idoType.IsEmpty()) { - cr->SetOutput("Attribute 'ido_type' must be set."); - cr->SetState(ServiceUnknown); - checkable->ProcessCheckResult(cr); + String output = "Attribute 'ido_type' must be set."; + 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(commandObj->GetName(), pr); + } else { + cr->SetState(state); + cr->SetOutput(output); + checkable->ProcessCheckResult(cr); + } return; } if (idoName.IsEmpty()) { - cr->SetOutput("Attribute 'ido_name' must be set."); - cr->SetState(ServiceUnknown); - checkable->ProcessCheckResult(cr); + String output = "Attribute 'ido_name' must be set."; + 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(commandObj->GetName(), pr); + } else { + cr->SetState(state); + cr->SetOutput(output); + checkable->ProcessCheckResult(cr); + } return; } Type::Ptr type = Type::GetByName(idoType); if (!type || !DbConnection::TypeInstance->IsAssignableFrom(type)) { - cr->SetOutput("DB IDO type '" + idoType + "' is invalid."); - cr->SetState(ServiceUnknown); - checkable->ProcessCheckResult(cr); + String output = "DB IDO type '" + idoType + "' is invalid."; + 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(commandObj->GetName(), pr); + } else { + cr->SetState(state); + cr->SetOutput(output); + checkable->ProcessCheckResult(cr); + } return; } @@ -89,34 +138,78 @@ void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult DbConnection::Ptr conn = static_pointer_cast(dtype->GetObject(idoName)); if (!conn) { - cr->SetOutput("DB IDO connection '" + idoName + "' does not exist."); - cr->SetState(ServiceUnknown); - checkable->ProcessCheckResult(cr); + String output = "DB IDO connection '" + idoName + "' does not exist."; + 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(commandObj->GetName(), pr); + } else { + cr->SetState(state); + cr->SetOutput(output); + checkable->ProcessCheckResult(cr); + } return; } double qps = conn->GetQueryCount(60) / 60.0; if (conn->IsPaused()) { - cr->SetOutput("DB IDO connection is temporarily disabled on this cluster instance."); - cr->SetState(ServiceOK); - checkable->ProcessCheckResult(cr); + String output = "DB IDO connection is temporarily disabled on this cluster instance."; + 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(commandObj->GetName(), pr); + } else { + cr->SetState(state); + cr->SetOutput(output); + checkable->ProcessCheckResult(cr); + } return; } double pendingQueries = conn->GetPendingQueryCount(); if (!conn->GetConnected()) { + String output; if (conn->GetShouldConnect()) { - cr->SetOutput("Could not connect to the database server."); - cr->SetState(ServiceCritical); + output ="Could not connect to the database server."; + state = ServiceCritical; } else { - cr->SetOutput("Not currently enabled: Another cluster instance is responsible for the IDO database."); - cr->SetState(ServiceOK); + output = "Not currently enabled: Another cluster instance is responsible for the IDO database."; + state = ServiceOK; } - checkable->ProcessCheckResult(cr); + 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(commandObj->GetName(), pr); + } else { + cr->SetState(state); + cr->SetOutput(output); + checkable->ProcessCheckResult(cr); + } return; } @@ -130,13 +223,13 @@ void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult << " Queries per second: " << std::fixed << std::setprecision(3) << qps << " Pending queries: " << std::fixed << std::setprecision(3) << pendingQueries << "."; - cr->SetState(ServiceWarning); + state = ServiceWarning; } else { msgbuf << "Connected to the database server (Schema version: '" << schema_version << "')." << " Queries per second: " << std::fixed << std::setprecision(3) << qps << " Pending queries: " << std::fixed << std::setprecision(3) << pendingQueries << "."; - cr->SetState(ServiceOK); + state = ServiceOK; } if (conn->GetEnableHa()) { @@ -149,34 +242,49 @@ void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult if (missingQueriesCritical.IsEmpty() && qps < queriesCritical) { msgbuf << " " << qps << " queries/s lower than critical threshold (" << queriesCritical << " queries/s)."; - cr->SetState(ServiceCritical); + state= ServiceCritical; } else if (missingQueriesWarning.IsEmpty() && qps < queriesWarning) { msgbuf << " " << qps << " queries/s lower than warning threshold (" << queriesWarning << " queries/s)."; - cr->SetState(ServiceWarning); + state = ServiceWarning; } if (missingPendingQueriesCritical.IsEmpty() && pendingQueries > pendingQueriesCritical) { msgbuf << " " << pendingQueries << " pending queries greater than critical threshold (" << pendingQueriesCritical << " queries)."; - cr->SetState(ServiceCritical); + state = ServiceCritical; } else if (missingPendingQueriesWarning.IsEmpty() && pendingQueries > pendingQueriesWarning) { msgbuf << " " << pendingQueries << " pending queries greater than warning threshold (" << pendingQueriesWarning << " queries)."; - cr->SetState(ServiceWarning); + state = ServiceWarning; } - cr->SetOutput(msgbuf.str()); + String output = msgbuf.str(); - cr->SetPerformanceData(new Array({ - { new PerfdataValue("queries", qps, false, "", queriesWarning, queriesCritical) }, - { new PerfdataValue("queries_1min", conn->GetQueryCount(60)) }, - { new PerfdataValue("queries_5mins", conn->GetQueryCount(5 * 60)) }, - { new PerfdataValue("queries_15mins", conn->GetQueryCount(15 * 60)) }, - { new PerfdataValue("pending_queries", pendingQueries, false, "", pendingQueriesWarning, pendingQueriesCritical) } - })); + 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->ProcessCheckResult(cr); + Checkable::ExecuteCommandProcessFinishedHandler(commandObj->GetName(), pr); + } else { + cr->SetState(state); + cr->SetOutput(output); + + cr->SetPerformanceData(new Array({ + { new PerfdataValue("queries", qps, false, "", queriesWarning, queriesCritical) }, + { new PerfdataValue("queries_1min", conn->GetQueryCount(60)) }, + { new PerfdataValue("queries_5mins", conn->GetQueryCount(5 * 60)) }, + { new PerfdataValue("queries_15mins", conn->GetQueryCount(15 * 60)) }, + { new PerfdataValue("pending_queries", pendingQueries, false, "", pendingQueriesWarning, pendingQueriesCritical) } + })); + + checkable->ProcessCheckResult(cr); + } } From 49efe009bd99cf49cdbc91509cbfd73ffea3d94f Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 24 Aug 2020 14:37:06 +0200 Subject: [PATCH 112/126] Update doc --- doc/12-icinga2-api.md | 35 +++++++++++++++++ doc/19-technical-concepts.md | 76 +++++++++++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/doc/12-icinga2-api.md b/doc/12-icinga2-api.md index 6f8f296c6..0a9ac41a5 100644 --- a/doc/12-icinga2-api.md +++ b/doc/12-icinga2-api.md @@ -1535,6 +1535,41 @@ $ curl -k -s -u root:icinga -H 'Accept: application/json' \ } ``` +### execute-command + +This API can e used to execute a particular check/notification/event-command on a particular +endpoint in the context of a particular checkable. + +Send a `POST` request to the URL endpoint `/v1/actions/execute-command`. + + Parameter | Type | Description + --------------|------------|-------------- + ttl | Number | **Required.** The time to live of the execution expressed in seconds. + command_type | String | **Optional.** The command type: `CheckCommand` or `EventCommand` or `NotificationCommand`. Default: `EventCommand` + command | String | **Optional.** The command to execute. Its type must the same as `command_type`. It can be a macro string. Default: depending on the `command_type` it can be `$check_command$`, `$event_command$` or `$notification_command$` + endpoint | String | **Optional.** The endpoint execute the command on. It can be a macro string. Default: `$command_endpoint`. + macros | Dictionary | **Optional**. A serialized object used to resolve the macro strings. Default: `{}` + user | String | **Optional.** The user used for the notification command. + notification | String | **Optional.** The notification used for the notification command. + + Example: + + ``` + $ curl -k -s -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/execute-command' \ + -d '{"type":"Service", "service": "agent!custom_service", "ttl":"15", "macros": { "command_endpoint":"master", "ls_dir":"/tmp/foo" }, "command": "custom_command", "command_type": "CheckCommand" }' + { + "results": [ + { + "checkable": "agent!custom_service", + "code": 202.0, + "execution": "3541d906-9afe-4c0e-ae6d-f549ee9bb3e7", + "status": "Accepted" + } + ] + } + ``` + ## Event Streams Event streams can be used to receive check results, downtimes, comments, diff --git a/doc/19-technical-concepts.md b/doc/19-technical-concepts.md index 4586410f6..9a3229ca4 100644 --- a/doc/19-technical-concepts.md +++ b/doc/19-technical-concepts.md @@ -1750,11 +1750,12 @@ command\_type | String | `check_command` or `event_command`. command | String | CheckCommand or EventCommand name. check\_timeout | Number | Check timeout of the checkable object, if specified as `check_timeout` attribute. macros | Dictionary | Command arguments as key/value pairs for remote execution. +endpoint | String | The endpoint execute the command on. ##### Functions -**Event Sender:** This gets constructed directly in `Checkable::ExecuteCheck()` or `Checkable::ExecuteEventHandler()` when a remote command endpoint is configured. +**Event Sender:** This gets constructed directly in `Checkable::ExecuteCheck()`, `Checkable::ExecuteEventHandler()` or `ApiActions::ExecuteCommand()` when a remote command endpoint is configured. * `Get{CheckCommand,EventCommand}()->Execute()` simulates an execution and extracts all command arguments into the `macro` dictionary (inside lib/methods tasks). * When the endpoint is connected, the message is constructed and sent directly. @@ -1764,6 +1765,7 @@ macros | Dictionary | Command arguments as key/value pairs for remote Special handling, calls `ClusterEvents::EnqueueCheck()` for command endpoint checks. This function enqueues check tasks into a queue which is controlled in `RemoteCheckThreadProc()`. +If the `endpoint` parameter is specified and is not equal to the local endpoint then the message is forwarded to the correct endpoint zone. ##### Permissions @@ -1785,6 +1787,78 @@ The returned messages are synced directly to the sender's endpoint, no cluster b > **Note**: EventCommand errors are just logged on the remote endpoint. +### event::UpdateExecutions + +> Location: `clusterevents.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | event::ExecuteCommand +params | Dictionary + +##### Params + +Key | Type | Description +---------------|---------------|------------------ +host | String | Host name. +service | String | Service name. +execution | Dictionary | Executions to be updated + +##### Functions + +**Event Sender:** `ClusterEvents::ExecutedCommandAPIHandler`, `ClusterEvents::UpdateExecutionsAPIHandler`, `ApiActions::ExecuteCommand` +**Event Receiver:** `ClusterEvents::UpdateExecutionsAPIHandler` + +##### Permissions + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Checkable does not exist. +* Origin endpoint's zone is not allowed to access this checkable. + +### event::ExecutedCommand + +> Location: `clusterevents.cpp` + +##### Message Body + +Key | Value +----------|--------- +jsonrpc | 2.0 +method | event::ExecuteCommand +params | Dictionary + +##### Params + +Key | Type | Description +---------------|---------------|------------------ +host | String | Host name. +service | String | Service name. +execution | String | The execution ID executed. +exitStatus | Number | The command exit status. +output | String | The command output. +start | Number | The unix timestamp at the start of the command execution +end | Number | The unix timestamp at the end of the command execution + +##### Functions + +**Event Sender:** `ClusterEvents::ExecuteCheckFromQueue`, `ClusterEvents::ExecuteCommandAPIHandler` +**Event Receiver:** `ClusterEvents::ExecutedCommandAPIHandler` + +##### Permissions + +The receiver will not process messages from not configured endpoints. + +Message updates will be dropped when: + +* Checkable does not exist. +* Origin endpoint's zone is not allowed to access this checkable. + #### config::Update > Location: `apilistener-filesync.cpp` From 078ae38661514962370908a346b78645b394e8a5 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Mon, 24 Aug 2020 14:45:01 +0200 Subject: [PATCH 113/126] Fix typo --- doc/12-icinga2-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/12-icinga2-api.md b/doc/12-icinga2-api.md index 0a9ac41a5..da38b31ca 100644 --- a/doc/12-icinga2-api.md +++ b/doc/12-icinga2-api.md @@ -1537,7 +1537,7 @@ $ curl -k -s -u root:icinga -H 'Accept: application/json' \ ### execute-command -This API can e used to execute a particular check/notification/event-command on a particular +This API can be used to execute a particular check/notification/event-command on a particular endpoint in the context of a particular checkable. Send a `POST` request to the URL endpoint `/v1/actions/execute-command`. From 9c2cc1169bbf9fa5dfe9f57444b834422c94672b Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 25 Aug 2020 09:24:28 +0200 Subject: [PATCH 114/126] Fix typos --- doc/12-icinga2-api.md | 2 +- doc/19-technical-concepts.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/12-icinga2-api.md b/doc/12-icinga2-api.md index da38b31ca..e5720e0c8 100644 --- a/doc/12-icinga2-api.md +++ b/doc/12-icinga2-api.md @@ -1547,7 +1547,7 @@ Send a `POST` request to the URL endpoint `/v1/actions/execute-command`. ttl | Number | **Required.** The time to live of the execution expressed in seconds. command_type | String | **Optional.** The command type: `CheckCommand` or `EventCommand` or `NotificationCommand`. Default: `EventCommand` command | String | **Optional.** The command to execute. Its type must the same as `command_type`. It can be a macro string. Default: depending on the `command_type` it can be `$check_command$`, `$event_command$` or `$notification_command$` - endpoint | String | **Optional.** The endpoint execute the command on. It can be a macro string. Default: `$command_endpoint`. + endpoint | String | **Optional.** The endpoint to execute the command on. It can be a macro string. Default: `$command_endpoint`. macros | Dictionary | **Optional**. A serialized object used to resolve the macro strings. Default: `{}` user | String | **Optional.** The user used for the notification command. notification | String | **Optional.** The notification used for the notification command. diff --git a/doc/19-technical-concepts.md b/doc/19-technical-concepts.md index 9a3229ca4..272381181 100644 --- a/doc/19-technical-concepts.md +++ b/doc/19-technical-concepts.md @@ -1750,7 +1750,7 @@ command\_type | String | `check_command` or `event_command`. command | String | CheckCommand or EventCommand name. check\_timeout | Number | Check timeout of the checkable object, if specified as `check_timeout` attribute. macros | Dictionary | Command arguments as key/value pairs for remote execution. -endpoint | String | The endpoint execute the command on. +endpoint | String | The endpoint to execute the command on. ##### Functions From 3b4c008c8109976944655a2f232d1151ab38858d Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Tue, 25 Aug 2020 17:35:34 +0200 Subject: [PATCH 115/126] Fix doc --- doc/12-icinga2-api.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/12-icinga2-api.md b/doc/12-icinga2-api.md index e5720e0c8..379153bd0 100644 --- a/doc/12-icinga2-api.md +++ b/doc/12-icinga2-api.md @@ -1535,9 +1535,9 @@ $ curl -k -s -u root:icinga -H 'Accept: application/json' \ } ``` -### execute-command +### execute-command -This API can be used to execute a particular check/notification/event-command on a particular +Executes a particular check/notification/event-command on a particular endpoint in the context of a particular checkable. Send a `POST` request to the URL endpoint `/v1/actions/execute-command`. @@ -1546,18 +1546,18 @@ Send a `POST` request to the URL endpoint `/v1/actions/execute-command`. --------------|------------|-------------- ttl | Number | **Required.** The time to live of the execution expressed in seconds. command_type | String | **Optional.** The command type: `CheckCommand` or `EventCommand` or `NotificationCommand`. Default: `EventCommand` - command | String | **Optional.** The command to execute. Its type must the same as `command_type`. It can be a macro string. Default: depending on the `command_type` it can be `$check_command$`, `$event_command$` or `$notification_command$` - endpoint | String | **Optional.** The endpoint to execute the command on. It can be a macro string. Default: `$command_endpoint`. - macros | Dictionary | **Optional**. A serialized object used to resolve the macro strings. Default: `{}` + command | String | **Optional.** The command to execute. Its type must the same as `command_type`. It can be a macro string. Default: depending on the `command_type` it's either `$check_command$`, `$event_command$` or `$notification_command$` + endpoint | String | **Optional.** The endpoint to execute the command on. It can be a macro string. Default: `$command_endpoint$`. + macros | Dictionary | **Optional**. A serialized object used to resolve the macro strings. It overrides also macros of other parameter. e.g. {"http_ssl": false}. Default: `{}` user | String | **Optional.** The user used for the notification command. notification | String | **Optional.** The notification used for the notification command. Example: ``` - $ curl -k -s -u root:icinga -H 'Accept: application/json' \ + $ curl -k -s -S -i -u root:icinga -H 'Accept: application/json' \ -X POST 'https://localhost:5665/v1/actions/execute-command' \ - -d '{"type":"Service", "service": "agent!custom_service", "ttl":"15", "macros": { "command_endpoint":"master", "ls_dir":"/tmp/foo" }, "command": "custom_command", "command_type": "CheckCommand" }' + -d '{"type": "Service", "service": "agent!custom_service", "ttl": 15, "macros": { "command_endpoint": "master", "ls_dir": "/tmp/foo" }, "command": "custom_command", "command_type": "CheckCommand" }' { "results": [ { From dd2bc681fea09f4b66effa92272d9cda610635ea Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 26 Aug 2020 09:20:06 +0200 Subject: [PATCH 116/126] Fix doc execute-command --- doc/12-icinga2-api.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/12-icinga2-api.md b/doc/12-icinga2-api.md index 379153bd0..7ee51e69b 100644 --- a/doc/12-icinga2-api.md +++ b/doc/12-icinga2-api.md @@ -1548,7 +1548,7 @@ Send a `POST` request to the URL endpoint `/v1/actions/execute-command`. command_type | String | **Optional.** The command type: `CheckCommand` or `EventCommand` or `NotificationCommand`. Default: `EventCommand` command | String | **Optional.** The command to execute. Its type must the same as `command_type`. It can be a macro string. Default: depending on the `command_type` it's either `$check_command$`, `$event_command$` or `$notification_command$` endpoint | String | **Optional.** The endpoint to execute the command on. It can be a macro string. Default: `$command_endpoint$`. - macros | Dictionary | **Optional**. A serialized object used to resolve the macro strings. It overrides also macros of other parameter. e.g. {"http_ssl": false}. Default: `{}` + macros | Dictionary | **Optional**. Macro overrides. Default: `{}` user | String | **Optional.** The user used for the notification command. notification | String | **Optional.** The notification used for the notification command. @@ -1558,6 +1558,9 @@ Send a `POST` request to the URL endpoint `/v1/actions/execute-command`. $ curl -k -s -S -i -u root:icinga -H 'Accept: application/json' \ -X POST 'https://localhost:5665/v1/actions/execute-command' \ -d '{"type": "Service", "service": "agent!custom_service", "ttl": 15, "macros": { "command_endpoint": "master", "ls_dir": "/tmp/foo" }, "command": "custom_command", "command_type": "CheckCommand" }' +``` + +``` { "results": [ { From 46b4e72c8ce4ebd0b084954d386542462641e628 Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 26 Aug 2020 11:43:18 +0200 Subject: [PATCH 117/126] Fix doc for cluster events --- doc/19-technical-concepts.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/19-technical-concepts.md b/doc/19-technical-concepts.md index 272381181..9ab347454 100644 --- a/doc/19-technical-concepts.md +++ b/doc/19-technical-concepts.md @@ -1751,6 +1751,8 @@ command | String | CheckCommand or EventCommand name. check\_timeout | Number | Check timeout of the checkable object, if specified as `check_timeout` attribute. macros | Dictionary | Command arguments as key/value pairs for remote execution. endpoint | String | The endpoint to execute the command on. +deadline | Number | A Unix timestamp indicating the execution deadline +source | String | The execution UUID ##### Functions @@ -1796,7 +1798,7 @@ The returned messages are synced directly to the sender's endpoint, no cluster b Key | Value ----------|--------- jsonrpc | 2.0 -method | event::ExecuteCommand +method | event::UpdateExecutions params | Dictionary ##### Params @@ -1805,7 +1807,7 @@ Key | Type | Description ---------------|---------------|------------------ host | String | Host name. service | String | Service name. -execution | Dictionary | Executions to be updated +executions | Dictionary | Executions to be updated ##### Functions @@ -1830,7 +1832,7 @@ Message updates will be dropped when: Key | Value ----------|--------- jsonrpc | 2.0 -method | event::ExecuteCommand +method | event::ExecutedCommand params | Dictionary ##### Params From 19628252f87f33b62c133eb007010c7f3db29bec Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 26 Aug 2020 15:48:04 +0200 Subject: [PATCH 118/126] Add timer to clean deadlined executions --- lib/icinga/checkable.cpp | 37 +++++++++++++++++++++++++++++++++++++ lib/icinga/checkable.hpp | 1 + 2 files changed, 38 insertions(+) diff --git a/lib/icinga/checkable.cpp b/lib/icinga/checkable.cpp index 0f4d1399b..9d6493351 100644 --- a/lib/icinga/checkable.cpp +++ b/lib/icinga/checkable.cpp @@ -20,6 +20,7 @@ boost::signals2::signal Checkable::OnFlappingChange; static Timer::Ptr l_CheckablesFireSuppressedNotifications; +static Timer::Ptr l_CleanDeadlinedExecutions; void Checkable::StaticInitialize() { @@ -78,6 +79,11 @@ void Checkable::Start(bool runtimeCreated) l_CheckablesFireSuppressedNotifications->SetInterval(5); l_CheckablesFireSuppressedNotifications->OnTimerExpired.connect(&Checkable::FireSuppressedNotifications); l_CheckablesFireSuppressedNotifications->Start(); + + l_CleanDeadlinedExecutions = new Timer(); + l_CleanDeadlinedExecutions->SetInterval(300); + l_CleanDeadlinedExecutions->OnTimerExpired.connect(&Checkable::CleanDeadlinedExecutions); + l_CleanDeadlinedExecutions->Start(); }); } @@ -257,3 +263,34 @@ void Checkable::ValidateMaxCheckAttempts(const Lazy& lvalue, const Validati if (lvalue() <= 0) BOOST_THROW_EXCEPTION(ValidationError(this, { "max_check_attempts" }, "Value must be greater than 0.")); } + +void Checkable::CleanDeadlinedExecutions(const Timer * const&) +{ + double now = Utility::GetTime(); + Dictionary::Ptr executions; + Dictionary::Ptr execution; + + for (auto& host : ConfigType::GetObjectsByType()) { + executions = host->GetExecutions(); + if (executions) { + for (const String& key : executions->GetKeys()) { + execution = executions->Get(key); + if (execution->Contains("deadline") && now > execution->Get("deadline")) { + executions->Remove(key); + } + } + } + } + + for (auto& service : ConfigType::GetObjectsByType()) { + executions = service->GetExecutions(); + if (executions) { + for (const String& key : executions->GetKeys()) { + execution = executions->Get(key); + if (execution->Contains("deadline") && now > execution->Get("deadline")) { + executions->Remove(key); + } + } + } + } +} \ No newline at end of file diff --git a/lib/icinga/checkable.hpp b/lib/icinga/checkable.hpp index 0eb1c5950..853c76a8a 100644 --- a/lib/icinga/checkable.hpp +++ b/lib/icinga/checkable.hpp @@ -202,6 +202,7 @@ private: static void NotifyDowntimeEnd(const Downtime::Ptr& downtime); static void FireSuppressedNotifications(const Timer * const&); + static void CleanDeadlinedExecutions(const Timer * const&); /* Comments */ std::set m_Comments; From 6801419bd6e67b54f327afe0a910c85ae915709e Mon Sep 17 00:00:00 2001 From: Mattia Codato Date: Wed, 26 Aug 2020 16:41:02 +0200 Subject: [PATCH 119/126] Add newline ad the end of file --- lib/icinga/checkable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/icinga/checkable.cpp b/lib/icinga/checkable.cpp index 9d6493351..c0954a5e4 100644 --- a/lib/icinga/checkable.cpp +++ b/lib/icinga/checkable.cpp @@ -293,4 +293,4 @@ void Checkable::CleanDeadlinedExecutions(const Timer * const&) } } } -} \ No newline at end of file +} From 8c6998b66d68f243ee5e954840e84dfcf3b1bcfa Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 26 Aug 2020 17:28:57 +0200 Subject: [PATCH 120/126] icinga::Hello: document version param --- doc/19-technical-concepts.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/19-technical-concepts.md b/doc/19-technical-concepts.md index 4586410f6..41f0a537d 100644 --- a/doc/19-technical-concepts.md +++ b/doc/19-technical-concepts.md @@ -1286,7 +1286,9 @@ params | Dictionary ##### Params -Currently empty. +Key | Type | Description +---------------------|-------------|------------------ +version | Number | Icinga 2 version, e.g. 21300 for v2.13.0. ##### Functions From 9e29936b8fa5ea4773b7659ab1b16a068b249382 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 19 Oct 2020 12:32:02 +0200 Subject: [PATCH 121/126] Fix missing include refs #8034 --- lib/icinga/clusterevents-check.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 0ca53030c..943a3c2d7 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -4,6 +4,7 @@ #include "icinga/icingaapplication.hpp" #include "remote/apilistener.hpp" #include "base/configuration.hpp" +#include "base/defer.hpp" #include "base/serializer.hpp" #include "base/exception.hpp" #include From 40ac05c182d9602d669ad628094d7e45c49cb120 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 19 Oct 2020 12:31:57 +0200 Subject: [PATCH 122/126] Introduce Endpoint#capabilities refs #8034 --- doc/19-technical-concepts.md | 1 + lib/icinga/apiactions.cpp | 4 ++-- lib/icinga/clusterevents.cpp | 4 ++-- lib/remote/apilistener.cpp | 9 +++++++-- lib/remote/apilistener.hpp | 30 ++++++++++++++++++++++++++++++ lib/remote/endpoint.ti | 4 ++++ 6 files changed, 46 insertions(+), 6 deletions(-) diff --git a/doc/19-technical-concepts.md b/doc/19-technical-concepts.md index 728acfc59..4b3b58a4e 100644 --- a/doc/19-technical-concepts.md +++ b/doc/19-technical-concepts.md @@ -1288,6 +1288,7 @@ params | Dictionary Key | Type | Description ---------------------|-------------|------------------ +capabilities | Number | Bitmask, see `lib/remote/apilistener.hpp`. version | Number | Icinga 2 version, e.g. 21300 for v2.13.0. ##### Functions diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 67c3990c0..73a28d950 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -821,11 +821,11 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, std::set endpoints = zone->GetEndpoints(); for (const Endpoint::Ptr& childEndpoint : endpoints) { - if (childEndpoint->GetIcingaVersion() < 21300) { + if (!(childEndpoint->GetCapabilities() & (uint_fast64_t)ApiCapabilities::ExecuteArbitraryCommand)) { /* Update execution */ double now = Utility::GetTime(); pending_execution->Set("exit", 126); - pending_execution->Set("output", "Endpoint '" + childEndpoint->GetName() + "' has version < 2.13."); + pending_execution->Set("output", "Endpoint '" + childEndpoint->GetName() + "' doesn't support executing arbitrary commands."); pending_execution->Set("start", now); pending_execution->Set("end", now); pending_execution->Remove("pending"); diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 56e8ef976..69a5d6984 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -641,7 +641,7 @@ Value ClusterEvents::ExecuteCommandAPIHandler(const MessageOrigin::Ptr& origin, std::set endpoints = zone->GetEndpoints(); for (const Endpoint::Ptr &childEndpoint : endpoints) { - if (childEndpoint->GetIcingaVersion() < 21300) { + if (!(childEndpoint->GetCapabilities() & (uint_fast64_t)ApiCapabilities::ExecuteArbitraryCommand)) { double now = Utility::GetTime(); Dictionary::Ptr executedParams = new Dictionary(); executedParams->Set("execution", params->Get("source")); @@ -650,7 +650,7 @@ Value ClusterEvents::ExecuteCommandAPIHandler(const MessageOrigin::Ptr& origin, executedParams->Set("service", params->Get("service")); executedParams->Set("exit", 126); executedParams->Set("output", - "Endpoint '" + childEndpoint->GetName() + "' has version < 2.13."); + "Endpoint '" + childEndpoint->GetName() + "' doesn't support executing arbitrary commands."); executedParams->Set("start", now); executedParams->Set("end", now); diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index 49ed29198..7ce07c38d 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -522,6 +522,8 @@ static const auto l_AppVersionInt (([]() -> unsigned long { + boost::lexical_cast(match[3].str()); })()); +static const auto l_MyCapabilities (ApiCapabilities::ExecuteArbitraryCommand); + /** * Processes a new client connection. * @@ -667,7 +669,8 @@ void ApiListener::NewClientHandlerInternal( { "jsonrpc", "2.0" }, { "method", "icinga::Hello" }, { "params", new Dictionary({ - { "version", (double)l_AppVersionInt } + { "version", (double)l_AppVersionInt }, + { "capabilities", (double)l_MyCapabilities } }) } }), yc); @@ -705,7 +708,8 @@ void ApiListener::NewClientHandlerInternal( { "jsonrpc", "2.0" }, { "method", "icinga::Hello" }, { "params", new Dictionary({ - { "version", (double)l_AppVersionInt } + { "version", (double)l_AppVersionInt }, + { "capabilities", (double)l_MyCapabilities } }) } }), yc); @@ -1643,6 +1647,7 @@ Value ApiListener::HelloAPIHandler(const MessageOrigin::Ptr& origin, const Dicti if (endpoint) { endpoint->SetIcingaVersion((double)params->Get("version")); + endpoint->SetCapabilities((double)params->Get("capabilities")); } } } diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index 4a3623a68..a5f4618a6 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include namespace icinga @@ -38,6 +39,35 @@ struct ConfigDirInformation Dictionary::Ptr Checksums; }; +/** + * If the version reported by icinga::Hello is not enough to tell whether + * the peer has a specific capability, add the latter to this bitmask. + * + * Note that due to the capability exchange via JSON-RPC and the state storage via JSON + * the bitmask numbers are stored in IEEE 754 64-bit floats. + * The latter have 53 digit bits which limit the bitmask. + * Not to run out of bits: + * + * Once all Icinga versions which don't have a specific capability are completely EOL, + * remove the respective capability checks and assume the peer has the capability. + * Once all Icinga versions which still check for the capability are completely EOL, + * remove the respective bit from icinga::Hello. + * Once all Icinga versions which still have the respective bit in icinga::Hello + * are completely EOL, remove the bit here. + * Once all Icinga versions which still have the respective bit here + * are completely EOL, feel free to re-use the bit. + * + * completely EOL = not supported, even if an important customer of us used it and + * not expected to appear in a multi-level cluster, e.g. a 4 level cluster with + * v2.11 -> v2.10 -> v2.9 -> v2.8 - v2.7 isn't here + * + * @ingroup remote + */ +enum class ApiCapabilities : uint_fast64_t +{ + ExecuteArbitraryCommand = 1u +}; + /** * @ingroup remote */ diff --git a/lib/remote/endpoint.ti b/lib/remote/endpoint.ti index 31a00b89d..78551ecf0 100644 --- a/lib/remote/endpoint.ti +++ b/lib/remote/endpoint.ti @@ -1,6 +1,7 @@ /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "base/configobject.hpp" +#include library remote; @@ -24,6 +25,9 @@ class Endpoint : ConfigObject [state] "unsigned long" icinga_version { default {{{ return 0; }}} }; + [state] uint_fast64_t capabilities { + default {{{ return 0; }}} + }; [no_user_modify] bool connecting; [no_user_modify] bool syncing; From 0ad1ab20aad431a4557e90542f82ad822bc202ed Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 23 Nov 2020 16:39:24 +0100 Subject: [PATCH 123/126] Fix code style refs #8034 --- lib/db_ido/idochecktask.cpp | 1 + lib/icinga/apiactions.cpp | 34 +++++++++++++++++++++++--- lib/icinga/clusterevents-check.cpp | 11 ++++++++- lib/icinga/clusterevents.cpp | 20 +++++++++++++-- lib/methods/clusterchecktask.cpp | 2 ++ lib/methods/clusterzonechecktask.cpp | 4 +++ lib/methods/dummychecktask.cpp | 1 + lib/methods/icingachecktask.cpp | 2 ++ lib/methods/pluginchecktask.cpp | 4 ++- lib/methods/plugineventtask.cpp | 4 ++- lib/methods/pluginnotificationtask.cpp | 4 ++- lib/methods/sleepchecktask.cpp | 1 + 12 files changed, 78 insertions(+), 10 deletions(-) diff --git a/lib/db_ido/idochecktask.cpp b/lib/db_ido/idochecktask.cpp index 22febdaf5..aaff48f7b 100644 --- a/lib/db_ido/idochecktask.cpp +++ b/lib/db_ido/idochecktask.cpp @@ -29,6 +29,7 @@ void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult tie(host, service) = GetHostService(checkable); MacroProcessor::ResolverList resolvers; + if (MacroResolver::OverrideMacros) resolvers.emplace_back("override", MacroResolver::OverrideMacros); diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 73a28d950..2a19605ca 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -567,6 +567,7 @@ Value ApiActions::GetSingleObjectByNameUsingPermissions(const String& type, cons qd.Permission = "objects/query/" + type; std::vector objs; + try { objs = FilterUtility::GetFilterTargets(qd, queryParams, user); } catch (const std::exception& ex) { @@ -580,15 +581,16 @@ Value ApiActions::GetSingleObjectByNameUsingPermissions(const String& type, cons return objs.at(0); }; -Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, - const Dictionary::Ptr& params) +Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, const Dictionary::Ptr& params) { ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) BOOST_THROW_EXCEPTION(std::invalid_argument("No ApiListener instance configured.")); /* Get command_type */ String command_type = "EventCommand"; + if (params->Contains("command_type")) command_type = HttpUtility::GetLastParameter(params, "command_type"); @@ -597,6 +599,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, return ApiActions::CreateResult(400, "Invalid command_type '" + command_type + "'."); Checkable::Ptr checkable = dynamic_pointer_cast(object); + if (!checkable) return ApiActions::CreateResult(404, "Can't start a command execution for a non-existent object."); @@ -605,6 +608,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, return ApiActions::CreateResult(400, "Parameter ttl is required."); double ttl = HttpUtility::GetLastParameter(params, "ttl"); + if (ttl <= 0) return ApiActions::CreateResult(400, "Parameter ttl must be greater than 0."); @@ -615,22 +619,25 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, tie(host, service) = GetHostService(checkable); String endpoint = "$command_endpoint$"; + if (params->Contains("endpoint")) endpoint = HttpUtility::GetLastParameter(params, "endpoint"); MacroProcessor::ResolverList resolvers; Value macros; + if (params->Contains("macros")) { macros = HttpUtility::GetLastParameter(params, "macros"); if (macros.IsObjectType()) { resolvers.emplace_back("override", macros); - } - else + } else { return ApiActions::CreateResult(400, "Parameter macros must be a dictionary."); + } } if (service) resolvers.emplace_back("service", service); + resolvers.emplace_back("host", host); resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); @@ -644,11 +651,13 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Get endpoint */ Endpoint::Ptr endpointPtr = GetSingleObjectByNameUsingPermissions(Endpoint::GetTypeName(), resolved_endpoint, ActionsHandler::AuthenticatedApiUser); + if (!endpointPtr) return ApiActions::CreateResult(404, "Can't find a valid endpoint for '" + resolved_endpoint + "'."); /* Get command */ String command; + if (!params->Contains("command")) { if (command_type == "CheckCommand" ) { command = "$check_command$"; @@ -668,6 +677,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, ); CheckResult::Ptr cr = checkable->GetLastCheckResult(); + if (!cr) cr = new CheckResult(); @@ -684,6 +694,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, if (command_type == "CheckCommand") { CheckCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(CheckCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); + if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else { @@ -695,6 +706,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, } } else if (command_type == "EventCommand") { EventCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(EventCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); + if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else { @@ -706,11 +718,13 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, } } else if (command_type == "NotificationCommand") { NotificationCommand::Ptr cmd = GetSingleObjectByNameUsingPermissions(NotificationCommand::GetTypeName(), resolved_command, ActionsHandler::AuthenticatedApiUser); + if (!cmd) return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else { /* Get user */ String user_string = ""; + if (params->Contains("user")) user_string = HttpUtility::GetLastParameter(params, "user"); @@ -721,12 +735,15 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, ); User::Ptr user = GetSingleObjectByNameUsingPermissions(User::GetTypeName(), resolved_user, ActionsHandler::AuthenticatedApiUser); + if (!user) return ApiActions::CreateResult(404, "Can't find a valid user for '" + resolved_user + "'."); + execParams->Set("user", user->GetName()); /* Get notification */ String notification_string = ""; + if (params->Contains("notification")) notification_string = HttpUtility::GetLastParameter(params, "notification"); @@ -737,8 +754,10 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, ); Notification::Ptr notification = GetSingleObjectByNameUsingPermissions(Notification::GetTypeName(), resolved_notification, ActionsHandler::AuthenticatedApiUser); + if (!notification) return ApiActions::CreateResult(404, "Can't find a valid notification for '" + resolved_notification + "'."); + execParams->Set("notification", notification->GetName()); NotificationCommand::ExecuteOverride = cmd; @@ -762,8 +781,10 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, pending_execution->Set("pending", true); pending_execution->Set("deadline", deadline); Dictionary::Ptr executions = checkable->GetExecutions(); + if (!executions) executions = new Dictionary(); + executions->Set(uuid, pending_execution); checkable->SetExecutions(executions); @@ -772,8 +793,10 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, executionsToBroadcast->Set(uuid, pending_execution); Dictionary::Ptr updateParams = new Dictionary(); updateParams->Set("host", host->GetName()); + if (service) updateParams->Set("service", service->GetShortName()); + updateParams->Set("executions", executionsToBroadcast); Dictionary::Ptr updateMessage = new Dictionary(); @@ -791,8 +814,10 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, execParams->Set("command_type", "event_command"); else if (command_type == "NotificationCommand") execParams->Set("command_type", "notification_command"); + execParams->Set("command", resolved_command); execParams->Set("host", host->GetName()); + if (service) execParams->Set("service", service->GetShortName()); @@ -810,6 +835,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, /* Execute command */ bool local = endpointPtr == Endpoint::GetLocalEndpoint(); + if (local) { ClusterEvents::ExecuteCommandAPIHandler(origin, execParams); } else { diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 943a3c2d7..468b0814e 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -78,12 +78,15 @@ void ClusterEvents::EnqueueCheck(const MessageOrigin::Ptr& origin, const Diction static void SendEventExecuteCommand(const Dictionary::Ptr& params, long exitStatus, const String& output, double start, double end, const ApiListener::Ptr& listener, const MessageOrigin::Ptr& origin, - const Endpoint::Ptr& sourceEndpoint) { + const Endpoint::Ptr& sourceEndpoint) +{ Dictionary::Ptr executedParams = new Dictionary(); executedParams->Set("execution", params->Get("source")); executedParams->Set("host", params->Get("host")); + if (params->Contains("service")) executedParams->Set("service", params->Get("service")); + executedParams->Set("exit", exitStatus); executedParams->Set("output", output); executedParams->Set("start", start); @@ -104,6 +107,7 @@ static void SendEventExecuteCommand(const Dictionary::Ptr& params, long exitStat void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { Endpoint::Ptr sourceEndpoint; + if (origin->FromClient) { sourceEndpoint = origin->FromClient->GetEndpoint(); } else if (origin->IsLocal()){ @@ -131,11 +135,13 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons String uuid = params->Get("source"); String checkableName = params->Get("host"); + if (params->Contains("service")) checkableName += "!" + params->Get("service"); /* Check deadline */ double deadline = params->Get("deadline"); + if (Utility::GetTime() > deadline) { Log(LogNotice, "ApiListener") << "Discarding 'ExecuteCheckFromQueue' event for checkable '" << checkableName @@ -233,6 +239,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons Dictionary::Ptr message = MakeCheckResultMessage(host, cr); listener->SyncSendMessage(sourceEndpoint, message); } + return; } } else if (command_type == "event_command") { @@ -244,6 +251,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons double now = Utility::GetTime(); SendEventExecuteCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); } + return; } } else if (command_type == "notification_command") { @@ -255,6 +263,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons double now = Utility::GetTime(); SendEventExecuteCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); } + return; } } diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 2602184db..e8f9bde2a 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -732,13 +732,14 @@ Value ClusterEvents::AcknowledgementClearedAPIHandler(const MessageOrigin::Ptr& Value ClusterEvents::ExecuteCommandAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) return Empty; if (params->Contains("endpoint")) { Endpoint::Ptr execEndpoint = Endpoint::GetByName(params->Get("endpoint")); - if (execEndpoint != Endpoint::GetLocalEndpoint()) { + if (execEndpoint != Endpoint::GetLocalEndpoint()) { Zone::Ptr endpointZone = execEndpoint->GetZone(); Zone::Ptr localZone = Zone::GetLocalZone(); @@ -758,8 +759,10 @@ Value ClusterEvents::ExecuteCommandAPIHandler(const MessageOrigin::Ptr& origin, Dictionary::Ptr executedParams = new Dictionary(); executedParams->Set("execution", params->Get("source")); executedParams->Set("host", params->Get("host")); + if (params->Contains("service")) executedParams->Set("service", params->Get("service")); + executedParams->Set("exit", 126); executedParams->Set("output", "Endpoint '" + childEndpoint->GetName() + "' doesn't support executing arbitrary commands."); @@ -1114,13 +1117,15 @@ Value ClusterEvents::NotificationSentToAllUsersAPIHandler(const MessageOrigin::P Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) return Empty; Endpoint::Ptr endpoint; + if (origin->FromClient) { endpoint = origin->FromClient->GetEndpoint(); - } else if (origin->IsLocal()){ + } else if (origin->IsLocal()) { endpoint = Endpoint::GetLocalEndpoint(); } @@ -1133,6 +1138,7 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, } Host::Ptr host = Host::GetByName(params->Get("host")); + if (!host) return Empty; @@ -1161,9 +1167,11 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, << "' from '" << origin->FromClient->GetIdentity() << "': Execution UUID missing."; return Empty; } + String uuid = params->Get("execution"); Dictionary::Ptr executions = checkable->GetExecutions(); + if (!executions) { Log(LogNotice, "ClusterEvents") << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() @@ -1172,6 +1180,7 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, } Dictionary::Ptr execution = executions->Get(uuid); + if (!execution) { Log(LogNotice, "ClusterEvents") << "Discarding 'update executions API handler' message for checkable '" << checkable->GetName() @@ -1198,8 +1207,10 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, executionsToBroadcast->Set(uuid, execution); Dictionary::Ptr updateParams = new Dictionary(); updateParams->Set("host", host->GetName()); + if (params->Contains("service")) updateParams->Set("service", params->Get("service")); + updateParams->Set("executions", executionsToBroadcast); Dictionary::Ptr updateMessage = new Dictionary(); @@ -1215,6 +1226,7 @@ Value ClusterEvents::ExecutedCommandAPIHandler(const MessageOrigin::Ptr& origin, Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) { Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint(); + if (!endpoint) { Log(LogNotice, "ClusterEvents") << "Discarding 'update executions API handler' message from '" << origin->FromClient->GetIdentity() @@ -1224,6 +1236,7 @@ Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin } Host::Ptr host = Host::GetByName(params->Get("host")); + if (!host) return Empty; @@ -1247,13 +1260,16 @@ Value ClusterEvents::UpdateExecutionsAPIHandler(const MessageOrigin::Ptr& origin } Dictionary::Ptr executions = checkable->GetExecutions(); + if (!executions) executions = new Dictionary(); + Dictionary::Ptr newExecutions = params->Get("executions"); newExecutions->CopyTo(executions); checkable->SetExecutions(executions); ApiListener::Ptr listener = ApiListener::GetInstance(); + if (!listener) return Empty; diff --git a/lib/methods/clusterchecktask.cpp b/lib/methods/clusterchecktask.cpp index eef3900ea..6ce28cac4 100644 --- a/lib/methods/clusterchecktask.cpp +++ b/lib/methods/clusterchecktask.cpp @@ -34,6 +34,7 @@ void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRe 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; @@ -48,6 +49,7 @@ void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRe cr->SetState(ServiceUnknown); checkable->ProcessCheckResult(cr); } + return; } diff --git a/lib/methods/clusterzonechecktask.cpp b/lib/methods/clusterzonechecktask.cpp index 0d5889a40..c5b6bbbf3 100644 --- a/lib/methods/clusterzonechecktask.cpp +++ b/lib/methods/clusterzonechecktask.cpp @@ -44,6 +44,7 @@ void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Che cr->SetState(state); checkable->ProcessCheckResult(cr); } + return; } @@ -54,6 +55,7 @@ void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Che tie(host, service) = GetHostService(checkable); MacroProcessor::ResolverList resolvers; + if (MacroResolver::OverrideMacros) resolvers.emplace_back("override", MacroResolver::OverrideMacros); @@ -98,6 +100,7 @@ void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Che cr->SetState(state); checkable->ProcessCheckResult(cr); } + return; } @@ -159,6 +162,7 @@ void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Che ServiceState state; String output; + if (connected) { state = ServiceOK; output = "Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag); diff --git a/lib/methods/dummychecktask.cpp b/lib/methods/dummychecktask.cpp index 45c773fcb..561415c64 100644 --- a/lib/methods/dummychecktask.cpp +++ b/lib/methods/dummychecktask.cpp @@ -29,6 +29,7 @@ void DummyCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu tie(host, service) = GetHostService(checkable); MacroProcessor::ResolverList resolvers; + if (MacroResolver::OverrideMacros) resolvers.emplace_back("override", MacroResolver::OverrideMacros); diff --git a/lib/methods/icingachecktask.cpp b/lib/methods/icingachecktask.cpp index d697b6925..40795495d 100644 --- a/lib/methods/icingachecktask.cpp +++ b/lib/methods/icingachecktask.cpp @@ -33,6 +33,7 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes tie(host, service) = GetHostService(checkable); MacroProcessor::ResolverList resolvers; + if (MacroResolver::OverrideMacros) resolvers.emplace_back("override", MacroResolver::OverrideMacros); @@ -189,6 +190,7 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes } String commandName = command->GetName(); + if (Checkable::ExecuteCommandProcessFinishedHandler) { double now = Utility::GetTime(); ProcessResult pr; diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp index 83d36c96d..488c2185e 100644 --- a/lib/methods/pluginchecktask.cpp +++ b/lib/methods/pluginchecktask.cpp @@ -29,6 +29,7 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes tie(host, service) = GetHostService(checkable); MacroProcessor::ResolverList resolvers; + if (MacroResolver::OverrideMacros) resolvers.emplace_back("override", MacroResolver::OverrideMacros); @@ -43,13 +44,14 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes if (!checkable->GetCheckTimeout().IsEmpty()) timeout = checkable->GetCheckTimeout(); - std::function callback; + if (Checkable::ExecuteCommandProcessFinishedHandler) { callback = Checkable::ExecuteCommandProcessFinishedHandler; } else { callback = std::bind(&PluginCheckTask::ProcessFinishedHandler, checkable, cr, _1, _2); } + PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(), resolvers, resolvedMacros, useResolvedMacros, timeout, callback); diff --git a/lib/methods/plugineventtask.cpp b/lib/methods/plugineventtask.cpp index 9c4401303..7a96901ff 100644 --- a/lib/methods/plugineventtask.cpp +++ b/lib/methods/plugineventtask.cpp @@ -28,6 +28,7 @@ void PluginEventTask::ScriptFunc(const Checkable::Ptr& checkable, tie(host, service) = GetHostService(checkable); MacroProcessor::ResolverList resolvers; + if (MacroResolver::OverrideMacros) resolvers.emplace_back("override", MacroResolver::OverrideMacros); @@ -38,13 +39,14 @@ void PluginEventTask::ScriptFunc(const Checkable::Ptr& checkable, resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); int timeout = commandObj->GetTimeout(); - std::function callback; + if (Checkable::ExecuteCommandProcessFinishedHandler) { callback = Checkable::ExecuteCommandProcessFinishedHandler; } else { callback = std::bind(&PluginEventTask::ProcessFinishedHandler, checkable, _1, _2); } + PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(), resolvers, resolvedMacros, useResolvedMacros, timeout, callback); } diff --git a/lib/methods/pluginnotificationtask.cpp b/lib/methods/pluginnotificationtask.cpp index ae89cc245..8dde4ff37 100644 --- a/lib/methods/pluginnotificationtask.cpp +++ b/lib/methods/pluginnotificationtask.cpp @@ -42,6 +42,7 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification, tie(host, service) = GetHostService(checkable); MacroProcessor::ResolverList resolvers; + if (MacroResolver::OverrideMacros) resolvers.emplace_back("override", MacroResolver::OverrideMacros); @@ -55,13 +56,14 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification, resolvers.emplace_back("icinga", IcingaApplication::GetInstance()); int timeout = commandObj->GetTimeout(); - std::function callback; + if (Checkable::ExecuteCommandProcessFinishedHandler) { callback = Checkable::ExecuteCommandProcessFinishedHandler; } else { callback = std::bind(&PluginNotificationTask::ProcessFinishedHandler, checkable, _1, _2); } + PluginUtility::ExecuteCommand(commandObj, checkable, cr, resolvers, resolvedMacros, useResolvedMacros, timeout, callback); } diff --git a/lib/methods/sleepchecktask.cpp b/lib/methods/sleepchecktask.cpp index ed3a5b6ee..a018de46b 100644 --- a/lib/methods/sleepchecktask.cpp +++ b/lib/methods/sleepchecktask.cpp @@ -25,6 +25,7 @@ void SleepCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu tie(host, service) = GetHostService(checkable); MacroProcessor::ResolverList resolvers; + if (MacroResolver::OverrideMacros) resolvers.emplace_back("override", MacroResolver::OverrideMacros); From fa61711c219cbe10573e1dc718b53b0af0f86329 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 23 Nov 2020 16:40:32 +0100 Subject: [PATCH 124/126] Introduce ReportIdoCheck() ... for code deduplication refs #8034 --- lib/db_ido/idochecktask.cpp | 177 +++++++++--------------------------- 1 file changed, 41 insertions(+), 136 deletions(-) diff --git a/lib/db_ido/idochecktask.cpp b/lib/db_ido/idochecktask.cpp index aaff48f7b..d16c91b0c 100644 --- a/lib/db_ido/idochecktask.cpp +++ b/lib/db_ido/idochecktask.cpp @@ -12,11 +12,34 @@ #include "base/perfdatavalue.hpp" #include "base/configtype.hpp" #include "base/convert.hpp" +#include using namespace icinga; REGISTER_FUNCTION_NONCONST(Internal, IdoCheck, &IdoCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros"); +static void ReportIdoCheck( + const Checkable::Ptr& checkable, const CheckCommand::Ptr& commandObj, + const CheckResult::Ptr& cr, String output, ServiceState state = ServiceUnknown +) +{ + if (Checkable::ExecuteCommandProcessFinishedHandler) { + double now = Utility::GetTime(); + ProcessResult pr; + pr.PID = -1; + pr.Output = std::move(output); + pr.ExecutionStart = now; + pr.ExecutionEnd = now; + pr.ExitStatus = state; + + Checkable::ExecuteCommandProcessFinishedHandler(commandObj->GetName(), pr); + } else { + cr->SetState(state); + cr->SetOutput(output); + checkable->ProcessCheckResult(cr); + } +} + void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros) { @@ -66,70 +89,19 @@ void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult return; if (idoType.IsEmpty()) { - String output = "Attribute 'ido_type' must be set."; - 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(commandObj->GetName(), pr); - } else { - cr->SetState(state); - cr->SetOutput(output); - checkable->ProcessCheckResult(cr); - } + ReportIdoCheck(checkable, commandObj, cr, "Attribute 'ido_type' must be set."); return; } if (idoName.IsEmpty()) { - String output = "Attribute 'ido_name' must be set."; - 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(commandObj->GetName(), pr); - } else { - cr->SetState(state); - cr->SetOutput(output); - checkable->ProcessCheckResult(cr); - } + ReportIdoCheck(checkable, commandObj, cr, "Attribute 'ido_name' must be set."); return; } Type::Ptr type = Type::GetByName(idoType); if (!type || !DbConnection::TypeInstance->IsAssignableFrom(type)) { - String output = "DB IDO type '" + idoType + "' is invalid."; - 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(commandObj->GetName(), pr); - } else { - cr->SetState(state); - cr->SetOutput(output); - checkable->ProcessCheckResult(cr); - } + ReportIdoCheck(checkable, commandObj, cr, "DB IDO type '" + idoType + "' is invalid."); return; } @@ -139,77 +111,27 @@ void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult DbConnection::Ptr conn = static_pointer_cast(dtype->GetObject(idoName)); if (!conn) { - String output = "DB IDO connection '" + idoName + "' does not exist."; - 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(commandObj->GetName(), pr); - } else { - cr->SetState(state); - cr->SetOutput(output); - checkable->ProcessCheckResult(cr); - } + ReportIdoCheck(checkable, commandObj, cr, "DB IDO connection '" + idoName + "' does not exist."); return; } double qps = conn->GetQueryCount(60) / 60.0; if (conn->IsPaused()) { - String output = "DB IDO connection is temporarily disabled on this cluster instance."; - 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(commandObj->GetName(), pr); - } else { - cr->SetState(state); - cr->SetOutput(output); - checkable->ProcessCheckResult(cr); - } + ReportIdoCheck(checkable, commandObj, cr, "DB IDO connection is temporarily disabled on this cluster instance.", ServiceOK); return; } double pendingQueries = conn->GetPendingQueryCount(); if (!conn->GetConnected()) { - String output; if (conn->GetShouldConnect()) { - output ="Could not connect to the database server."; - state = ServiceCritical; + ReportIdoCheck(checkable, commandObj, cr, "Could not connect to the database server.", ServiceCritical); } else { - output = "Not currently enabled: Another cluster instance is responsible for the IDO database."; - 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(commandObj->GetName(), pr); - } else { - cr->SetState(state); - cr->SetOutput(output); - checkable->ProcessCheckResult(cr); + ReportIdoCheck( + checkable, commandObj, cr, + "Not currently enabled: Another cluster instance is responsible for the IDO database.", ServiceOK + ); } return; } @@ -262,30 +184,13 @@ void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult state = ServiceWarning; } - String output = msgbuf.str(); + cr->SetPerformanceData(new Array({ + { new PerfdataValue("queries", qps, false, "", queriesWarning, queriesCritical) }, + { new PerfdataValue("queries_1min", conn->GetQueryCount(60)) }, + { new PerfdataValue("queries_5mins", conn->GetQueryCount(5 * 60)) }, + { new PerfdataValue("queries_15mins", conn->GetQueryCount(15 * 60)) }, + { new PerfdataValue("pending_queries", pendingQueries, false, "", pendingQueriesWarning, pendingQueriesCritical) } + })); - 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(commandObj->GetName(), pr); - } else { - cr->SetState(state); - cr->SetOutput(output); - - cr->SetPerformanceData(new Array({ - { new PerfdataValue("queries", qps, false, "", queriesWarning, queriesCritical) }, - { new PerfdataValue("queries_1min", conn->GetQueryCount(60)) }, - { new PerfdataValue("queries_5mins", conn->GetQueryCount(5 * 60)) }, - { new PerfdataValue("queries_15mins", conn->GetQueryCount(15 * 60)) }, - { new PerfdataValue("pending_queries", pendingQueries, false, "", pendingQueriesWarning, pendingQueriesCritical) } - })); - - checkable->ProcessCheckResult(cr); - } + ReportIdoCheck(checkable, commandObj, cr, msgbuf.str(), state); } From 5cfac1f643f77793cfc7048c39fb0cd885925947 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 23 Nov 2020 16:43:47 +0100 Subject: [PATCH 125/126] Fix function and variable names refs #8034 --- lib/icinga/apiactions.cpp | 4 ++-- lib/icinga/clusterevents-check.cpp | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index 2a19605ca..afd53e981 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -711,7 +711,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, cons return ApiActions::CreateResult(404, "Can't find a valid " + command_type + " for '" + resolved_command + "'."); else { EventCommand::ExecuteOverride = cmd; - Defer resetCheckCommandOverride([]() { + Defer resetEventCommandOverride ([]() { EventCommand::ExecuteOverride = nullptr; }); cmd->Execute(checkable, execMacros, false); @@ -761,7 +761,7 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, cons execParams->Set("notification", notification->GetName()); NotificationCommand::ExecuteOverride = cmd; - Defer resetCheckCommandOverride([]() { + Defer resetNotificationCommandOverride ([]() { NotificationCommand::ExecuteOverride = nullptr; }); diff --git a/lib/icinga/clusterevents-check.cpp b/lib/icinga/clusterevents-check.cpp index 468b0814e..30745dbd7 100644 --- a/lib/icinga/clusterevents-check.cpp +++ b/lib/icinga/clusterevents-check.cpp @@ -76,7 +76,7 @@ void ClusterEvents::EnqueueCheck(const MessageOrigin::Ptr& origin, const Diction } } -static void SendEventExecuteCommand(const Dictionary::Ptr& params, long exitStatus, const String& output, +static void SendEventExecutedCommand(const Dictionary::Ptr& params, long exitStatus, const String& output, double start, double end, const ApiListener::Ptr& listener, const MessageOrigin::Ptr& origin, const Endpoint::Ptr& sourceEndpoint) { @@ -163,7 +163,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons << pr.ExitStatus << ", output: " << pr.Output; } - SendEventExecuteCommand(params, pr.ExitStatus, pr.Output, pr.ExecutionStart, pr.ExecutionEnd, listener, + SendEventExecutedCommand(params, pr.ExitStatus, pr.Output, pr.ExecutionStart, pr.ExecutionEnd, listener, origin, sourceEndpoint); }; } @@ -176,7 +176,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (params->Contains("source")) { double now = Utility::GetTime(); - SendEventExecuteCommand(params, 126, output, now, now, listener, origin, sourceEndpoint); + SendEventExecutedCommand(params, 126, output, now, now, listener, origin, sourceEndpoint); } else { Host::Ptr host = new Host(); Dictionary::Ptr attrs = new Dictionary(); @@ -231,7 +231,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons double now = Utility::GetTime(); if (params->Contains("source")) { - SendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); + SendEventExecutedCommand(params, state, output, now, now, listener, origin, sourceEndpoint); } else { CheckResult::Ptr cr = new CheckResult(); cr->SetState(state); @@ -249,7 +249,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (params->Contains("source")) { double now = Utility::GetTime(); - SendEventExecuteCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); + SendEventExecutedCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); } return; @@ -261,7 +261,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons if (params->Contains("source")) { double now = Utility::GetTime(); - SendEventExecuteCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); + SendEventExecutedCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); } return; @@ -286,7 +286,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons double now = Utility::GetTime(); if (params->Contains("source")) { - SendEventExecuteCommand(params, state, output, now, now, listener, origin, sourceEndpoint); + SendEventExecutedCommand(params, state, output, now, now, listener, origin, sourceEndpoint); } else { CheckResult::Ptr cr = new CheckResult(); cr->SetState(state); @@ -311,7 +311,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons host->GetName() + "': " + DiagnosticInformation(ex); double now = Utility::GetTime(); - SendEventExecuteCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); + SendEventExecutedCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); } else { throw; } @@ -347,7 +347,7 @@ void ClusterEvents::ExecuteCheckFromQueue(const MessageOrigin::Ptr& origin, cons + "' and user '" + user->GetName() + "' using command '" + command + "': " + DiagnosticInformation(ex, false); double now = Utility::GetTime(); - SendEventExecuteCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); + SendEventExecutedCommand(params, ServiceUnknown, output, now, now, listener, origin, sourceEndpoint); } } } From 7cf01008e68f264f7c8b1303898e8934050f6cf0 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 23 Nov 2020 16:45:35 +0100 Subject: [PATCH 126/126] Improve /v1/actions/execute-command docs refs #8034 --- doc/12-icinga2-api.md | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/doc/12-icinga2-api.md b/doc/12-icinga2-api.md index 7ee51e69b..3c6f1c101 100644 --- a/doc/12-icinga2-api.md +++ b/doc/12-icinga2-api.md @@ -1538,7 +1538,11 @@ $ curl -k -s -u root:icinga -H 'Accept: application/json' \ ### execute-command Executes a particular check/notification/event-command on a particular -endpoint in the context of a particular checkable. +endpoint in the context of a particular checkable. Example use cases: + +* Test a check command without actually triggering notifications +* Reboot a node via an event command +* Test a notification command without actually reproducing the notification reason Send a `POST` request to the URL endpoint `/v1/actions/execute-command`. @@ -1548,30 +1552,32 @@ Send a `POST` request to the URL endpoint `/v1/actions/execute-command`. command_type | String | **Optional.** The command type: `CheckCommand` or `EventCommand` or `NotificationCommand`. Default: `EventCommand` command | String | **Optional.** The command to execute. Its type must the same as `command_type`. It can be a macro string. Default: depending on the `command_type` it's either `$check_command$`, `$event_command$` or `$notification_command$` endpoint | String | **Optional.** The endpoint to execute the command on. It can be a macro string. Default: `$command_endpoint$`. - macros | Dictionary | **Optional**. Macro overrides. Default: `{}` + macros | Dictionary | **Optional.** Macro overrides. Default: `{}` user | String | **Optional.** The user used for the notification command. notification | String | **Optional.** The notification used for the notification command. - Example: +Example: - ``` - $ curl -k -s -S -i -u root:icinga -H 'Accept: application/json' \ - -X POST 'https://localhost:5665/v1/actions/execute-command' \ - -d '{"type": "Service", "service": "agent!custom_service", "ttl": 15, "macros": { "command_endpoint": "master", "ls_dir": "/tmp/foo" }, "command": "custom_command", "command_type": "CheckCommand" }' +``` +$ curl -k -s -S -i -u root:icinga -H 'Accept: application/json' \ + -X POST 'https://localhost:5665/v1/actions/execute-command' \ + -d '{"type": "Service", "service": "agent!custom_service", "ttl": 15, "macros": { "command_endpoint": "master", "ls_dir": "/tmp/foo" }, "command": "custom_command", "command_type": "CheckCommand" }' ``` ``` - { - "results": [ - { - "checkable": "agent!custom_service", - "code": 202.0, - "execution": "3541d906-9afe-4c0e-ae6d-f549ee9bb3e7", - "status": "Accepted" - } - ] - } - ``` +{ + "results": [ + { + "checkable": "agent!custom_service", + "code": 202.0, + "execution": "3541d906-9afe-4c0e-ae6d-f549ee9bb3e7", + "status": "Accepted" + } + ] +} +``` + +You may poll the state of the execution by [querying](#icinga2-api-config-objects-query) the checkable's attribute `executions`. ## Event Streams