From 18f810a1eae1bd872c1ac6e405743136cee9e47e Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 22 May 2025 14:59:19 +0200 Subject: [PATCH 1/4] For the same zone, call ApiListener::UpdateObjectAuthority() in icinga::Hello after setting remote capabilities. They'll become important for teamwork in a zone. --- lib/remote/apilistener.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index db024c987..98b5c4968 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -1794,6 +1794,10 @@ Value ApiListener::HelloAPIHandler(const MessageOrigin::Ptr& origin, const Dicti endpoint->SetIcingaVersion(nodeVersion); endpoint->SetCapabilities((double)params->Get("capabilities")); + if (endpoint->GetZone() == Zone::GetLocalZone()) { + UpdateObjectAuthority(); + } + if (nodeVersion == 0u) { nodeVersion = 21200; } From 6cd83ba2b8c5efd562aede1cc117f746c1abee66 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Fri, 16 May 2025 15:02:56 +0200 Subject: [PATCH 2/4] ApiListener::UpdateObjectAuthority(): distribute auth. by object's host Pin child objects of hosts (HOST!...) to the same endpoint as the host. This reduces cross-object action latency withing the same host. If all endpoints know this algorithm, we can use it. --- lib/remote/apilistener-authority.cpp | 25 ++++++++++++++++++++++--- lib/remote/apilistener.cpp | 4 +++- lib/remote/apilistener.hpp | 1 + 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/remote/apilistener-authority.cpp b/lib/remote/apilistener-authority.cpp index f33a1905b..e169ad4b1 100644 --- a/lib/remote/apilistener-authority.cpp +++ b/lib/remote/apilistener-authority.cpp @@ -25,6 +25,7 @@ void ApiListener::UpdateObjectAuthority() std::vector endpoints; Endpoint::Ptr my_endpoint; + int hostChildrenInheritObjectAuthority = 0; if (my_zone) { my_endpoint = Endpoint::GetLocalEndpoint(); @@ -38,6 +39,10 @@ void ApiListener::UpdateObjectAuthority() continue; endpoints.push_back(endpoint); + + if (endpoint == my_endpoint || endpoint->GetCapabilities() & static_cast(ApiCapabilities::HostChildrenInheritObjectAuthority)) { + ++hostChildrenInheritObjectAuthority; + } } double startTime = Application::GetStartTime(); @@ -65,10 +70,24 @@ void ApiListener::UpdateObjectAuthority() bool authority; - if (!my_zone) + if (my_zone) { + auto name (object->GetName()); + + // If all endpoints know this algorithm, we can use it. + if (hostChildrenInheritObjectAuthority == endpoints.size()) { + auto exclamation (name.FindFirstOf('!')); + + // Pin child objects of hosts (HOST!...) to the same endpoint as the host. + // This reduces cross-object action latency withing the same host. + if (exclamation != String::NPos) { + name.erase(name.Begin() + exclamation, name.End()); + } + } + + authority = endpoints[Utility::SDBM(name) % endpoints.size()] == my_endpoint; + } else { authority = true; - else - authority = endpoints[Utility::SDBM(object->GetName()) % endpoints.size()] == my_endpoint; + } #ifdef I2_DEBUG // //Enable on demand, causes heavy logging on each run. diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index 98b5c4968..4c0d006d8 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -640,7 +640,9 @@ static const auto l_AppVersionInt (([]() -> unsigned long { })()); static const auto l_MyCapabilities ( - (uint_fast64_t)ApiCapabilities::ExecuteArbitraryCommand | (uint_fast64_t)ApiCapabilities::IfwApiCheckCommand + (uint_fast64_t)ApiCapabilities::ExecuteArbitraryCommand + | (uint_fast64_t)ApiCapabilities::IfwApiCheckCommand + | (uint_fast64_t)ApiCapabilities::HostChildrenInheritObjectAuthority ); /** diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index eae1fa03e..bdb0fea52 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -69,6 +69,7 @@ enum class ApiCapabilities : uint_fast64_t { ExecuteArbitraryCommand = 1u << 0u, IfwApiCheckCommand = 1u << 1u, + HostChildrenInheritObjectAuthority = 1u << 2u, }; /** From 77b86bba52d76c051fcacd952d672cd6744d91f7 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 19 May 2025 15:25:21 +0200 Subject: [PATCH 3/4] Move l_MyCapabilities -> ApiCapabilities::MyCapabilities --- lib/remote/apilistener.cpp | 10 ++-------- lib/remote/apilistener.hpp | 2 ++ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index 4c0d006d8..1ba8ccdae 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -639,12 +639,6 @@ static const auto l_AppVersionInt (([]() -> unsigned long { + boost::lexical_cast(match[3].str()); })()); -static const auto l_MyCapabilities ( - (uint_fast64_t)ApiCapabilities::ExecuteArbitraryCommand - | (uint_fast64_t)ApiCapabilities::IfwApiCheckCommand - | (uint_fast64_t)ApiCapabilities::HostChildrenInheritObjectAuthority -); - /** * Processes a new client connection. * @@ -775,7 +769,7 @@ void ApiListener::NewClientHandlerInternal( { "method", "icinga::Hello" }, { "params", new Dictionary({ { "version", (double)l_AppVersionInt }, - { "capabilities", (double)l_MyCapabilities } + { "capabilities", (double)ApiCapabilities::MyCapabilities } }) } }), yc); @@ -814,7 +808,7 @@ void ApiListener::NewClientHandlerInternal( { "method", "icinga::Hello" }, { "params", new Dictionary({ { "version", (double)l_AppVersionInt }, - { "capabilities", (double)l_MyCapabilities } + { "capabilities", (double)ApiCapabilities::MyCapabilities } }) } }), yc); diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index bdb0fea52..9a38a57b5 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -70,6 +70,8 @@ enum class ApiCapabilities : uint_fast64_t ExecuteArbitraryCommand = 1u << 0u, IfwApiCheckCommand = 1u << 1u, HostChildrenInheritObjectAuthority = 1u << 2u, + + MyCapabilities = ExecuteArbitraryCommand | IfwApiCheckCommand | HostChildrenInheritObjectAuthority }; /** From b1e08ba7a96a917100f5f170895260712a721fe8 Mon Sep 17 00:00:00 2001 From: Yonas Habteab Date: Fri, 23 May 2025 17:24:05 +0200 Subject: [PATCH 4/4] docs: document the new object distribution behavior --- doc/19-technical-concepts.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/doc/19-technical-concepts.md b/doc/19-technical-concepts.md index de68bdd63..46819f7d6 100644 --- a/doc/19-technical-concepts.md +++ b/doc/19-technical-concepts.md @@ -632,15 +632,12 @@ The algorithm works like this: * Set the authority (true or false) The object authority calculation works "offline" without any message exchange. -Each instance alculates the SDBM hash of the config object name, puts that in contrast -modulo the connected endpoints size. -This index is used to lookup the corresponding endpoint in the connected endpoints array, -including the local endpoint. Whether the local endpoint is equal to the selected endpoint, -or not, this sets the authority to `true` or `false`. - -```cpp -authority = endpoints[Utility::SDBM(object->GetName()) % endpoints.size()] == my_endpoint; -``` +Each instance calculates the SDBM hash of the config object name. However, for objects bound to some +host, i.e. the object name is composed of `!`, the SDBM hash is calculated based +on the host name only instead of the full object name. That way, each child object like services, downtimes, +etc. will be assigned to the same endpoint as the host object itself. The resulting hash modulo (`%`) the number of +connected endpoints produces the index of the endpoint which is authoritative for this config object. If the +endpoint at this index is equal to the local endpoint, the authority is set to `true`, otherwise it is set to `false`. `ConfigObject::SetAuthority(bool authority)` triggers the following events: