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)
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 `<host_name>!<object_name>`, 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:

View File

@ -25,6 +25,7 @@ void ApiListener::UpdateObjectAuthority()
std::vector<Endpoint::Ptr> 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<uint_fast64_t>(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.

View File

@ -640,10 +640,6 @@ static const auto l_AppVersionInt (([]() -> unsigned long {
+ 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.
*
@ -774,7 +770,7 @@ void ApiListener::NewClientHandlerInternal(
{ "method", "icinga::Hello" },
{ "params", new Dictionary({
{ "version", (double)l_AppVersionInt },
{ "capabilities", (double)l_MyCapabilities }
{ "capabilities", (double)ApiCapabilities::MyCapabilities }
}) }
}), yc);
@ -813,7 +809,7 @@ void ApiListener::NewClientHandlerInternal(
{ "method", "icinga::Hello" },
{ "params", new Dictionary({
{ "version", (double)l_AppVersionInt },
{ "capabilities", (double)l_MyCapabilities }
{ "capabilities", (double)ApiCapabilities::MyCapabilities }
}) }
}), yc);
@ -1801,6 +1797,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;
}

View File

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