Merge pull request #10161 from Icinga/authBYhost-10157

ApiListener::UpdateObjectAuthority(): distribute auth. by object's host
This commit is contained in:
Julian Brost 2025-06-06 15:03:32 +02:00 committed by GitHub
commit 4e08adc532
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 37 additions and 18 deletions

View File

@ -633,15 +633,12 @@ The algorithm works like this:
* Set the authority (true or false) * Set the authority (true or false)
The object authority calculation works "offline" without any message exchange. 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 Each instance calculates the SDBM hash of the config object name. However, for objects bound to some
modulo the connected endpoints size. host, i.e. the object name is composed of `<host_name>!<object_name>`, the SDBM hash is calculated based
This index is used to lookup the corresponding endpoint in the connected endpoints array, on the host name only instead of the full object name. That way, each child object like services, downtimes,
including the local endpoint. Whether the local endpoint is equal to the selected endpoint, etc. will be assigned to the same endpoint as the host object itself. The resulting hash modulo (`%`) the number of
or not, this sets the authority to `true` or `false`. 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`.
```cpp
authority = endpoints[Utility::SDBM(object->GetName()) % endpoints.size()] == my_endpoint;
```
`ConfigObject::SetAuthority(bool authority)` triggers the following events: `ConfigObject::SetAuthority(bool authority)` triggers the following events:

View File

@ -25,6 +25,7 @@ void ApiListener::UpdateObjectAuthority()
std::vector<Endpoint::Ptr> endpoints; std::vector<Endpoint::Ptr> endpoints;
Endpoint::Ptr my_endpoint; Endpoint::Ptr my_endpoint;
int hostChildrenInheritObjectAuthority = 0;
if (my_zone) { if (my_zone) {
my_endpoint = Endpoint::GetLocalEndpoint(); my_endpoint = Endpoint::GetLocalEndpoint();
@ -38,6 +39,10 @@ void ApiListener::UpdateObjectAuthority()
continue; continue;
endpoints.push_back(endpoint); endpoints.push_back(endpoint);
if (endpoint == my_endpoint || endpoint->GetCapabilities() & static_cast<uint_fast64_t>(ApiCapabilities::HostChildrenInheritObjectAuthority)) {
++hostChildrenInheritObjectAuthority;
}
} }
double startTime = Application::GetStartTime(); double startTime = Application::GetStartTime();
@ -65,10 +70,24 @@ void ApiListener::UpdateObjectAuthority()
bool authority; 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; authority = true;
else }
authority = endpoints[Utility::SDBM(object->GetName()) % endpoints.size()] == my_endpoint;
#ifdef I2_DEBUG #ifdef I2_DEBUG
// //Enable on demand, causes heavy logging on each run. // //Enable on demand, causes heavy logging on each run.

View File

@ -640,10 +640,6 @@ static const auto l_AppVersionInt (([]() -> unsigned long {
+ boost::lexical_cast<unsigned long>(match[3].str()); + boost::lexical_cast<unsigned long>(match[3].str());
})()); })());
static const auto l_MyCapabilities (
(uint_fast64_t)ApiCapabilities::ExecuteArbitraryCommand | (uint_fast64_t)ApiCapabilities::IfwApiCheckCommand
);
/** /**
* Processes a new client connection. * Processes a new client connection.
* *
@ -774,7 +770,7 @@ void ApiListener::NewClientHandlerInternal(
{ "method", "icinga::Hello" }, { "method", "icinga::Hello" },
{ "params", new Dictionary({ { "params", new Dictionary({
{ "version", (double)l_AppVersionInt }, { "version", (double)l_AppVersionInt },
{ "capabilities", (double)l_MyCapabilities } { "capabilities", (double)ApiCapabilities::MyCapabilities }
}) } }) }
}), yc); }), yc);
@ -813,7 +809,7 @@ void ApiListener::NewClientHandlerInternal(
{ "method", "icinga::Hello" }, { "method", "icinga::Hello" },
{ "params", new Dictionary({ { "params", new Dictionary({
{ "version", (double)l_AppVersionInt }, { "version", (double)l_AppVersionInt },
{ "capabilities", (double)l_MyCapabilities } { "capabilities", (double)ApiCapabilities::MyCapabilities }
}) } }) }
}), yc); }), yc);
@ -1801,6 +1797,10 @@ Value ApiListener::HelloAPIHandler(const MessageOrigin::Ptr& origin, const Dicti
endpoint->SetIcingaVersion(nodeVersion); endpoint->SetIcingaVersion(nodeVersion);
endpoint->SetCapabilities((double)params->Get("capabilities")); endpoint->SetCapabilities((double)params->Get("capabilities"));
if (endpoint->GetZone() == Zone::GetLocalZone()) {
UpdateObjectAuthority();
}
if (nodeVersion == 0u) { if (nodeVersion == 0u) {
nodeVersion = 21200; nodeVersion = 21200;
} }

View File

@ -70,6 +70,9 @@ enum class ApiCapabilities : uint_fast64_t
{ {
ExecuteArbitraryCommand = 1u << 0u, ExecuteArbitraryCommand = 1u << 0u,
IfwApiCheckCommand = 1u << 1u, IfwApiCheckCommand = 1u << 1u,
HostChildrenInheritObjectAuthority = 1u << 2u,
MyCapabilities = ExecuteArbitraryCommand | IfwApiCheckCommand | HostChildrenInheritObjectAuthority
}; };
/** /**