From 5ef43945a3bbce5be717c1bd9b9f7fe81ac6ecc1 Mon Sep 17 00:00:00 2001 From: Yonas Habteab Date: Thu, 26 Sep 2024 16:02:07 +0200 Subject: [PATCH 1/3] Add a `load_after` dependency where applicable --- lib/icinga/dependency.ti | 1 + lib/icinga/host.ti | 4 ++++ lib/icinga/notification.ti | 5 +++++ lib/icinga/service.ti | 4 ++++ lib/icinga/user.ti | 3 +++ 5 files changed, 17 insertions(+) diff --git a/lib/icinga/dependency.ti b/lib/icinga/dependency.ti index 41de7ba23cf..f7b8e2a51e7 100644 --- a/lib/icinga/dependency.ti +++ b/lib/icinga/dependency.ti @@ -22,6 +22,7 @@ class Dependency : CustomVarObject < DependencyNameComposer { load_after Host; load_after Service; + load_after TimePeriod; [config, no_user_modify, required, navigation(child_host)] name(Host) child_host_name { navigate {{{ diff --git a/lib/icinga/host.ti b/lib/icinga/host.ti index f6624e30764..7df78909e7c 100644 --- a/lib/icinga/host.ti +++ b/lib/icinga/host.ti @@ -14,6 +14,10 @@ class Host : Checkable load_after ApiListener; load_after Endpoint; load_after Zone; + load_after CheckCommand; + load_after EventCommand; + load_after HostGroup; + load_after TimePeriod; [config, no_user_modify, required, signal_with_old_value] array(name(HostGroup)) groups { default {{{ return new Array(); }}} diff --git a/lib/icinga/notification.ti b/lib/icinga/notification.ti index be0784613e8..b8935bc5eba 100644 --- a/lib/icinga/notification.ti +++ b/lib/icinga/notification.ti @@ -22,6 +22,11 @@ class Notification : CustomVarObject < NotificationNameComposer { load_after Host; load_after Service; + load_after Endpoint; + load_after TimePeriod; + load_after NotificationCommand; + load_after User; + load_after UserGroup; [config, protected, required, navigation] name(NotificationCommand) command (CommandRaw) { navigate {{{ diff --git a/lib/icinga/service.ti b/lib/icinga/service.ti index 12c2d8c66c9..bc2f0179cb4 100644 --- a/lib/icinga/service.ti +++ b/lib/icinga/service.ti @@ -26,6 +26,10 @@ class Service : Checkable < ServiceNameComposer load_after Endpoint; load_after Host; load_after Zone; + load_after CheckCommand; + load_after EventCommand; + load_after ServiceGroup; + load_after TimePeriod; [config, no_user_modify, required, signal_with_old_value] array(name(ServiceGroup)) groups { default {{{ return new Array(); }}} diff --git a/lib/icinga/user.ti b/lib/icinga/user.ti index 8b8c43a14d4..27ce2216719 100644 --- a/lib/icinga/user.ti +++ b/lib/icinga/user.ti @@ -11,6 +11,9 @@ namespace icinga class User : CustomVarObject { + load_after TimePeriod; + load_after UserGroup; + [config] String display_name { get {{{ String displayName = m_DisplayName.load(); From 70ccb4cb21a498a0afa5ecfda98f89b710756438 Mon Sep 17 00:00:00 2001 From: Yonas Habteab Date: Mon, 16 Sep 2024 09:20:43 +0200 Subject: [PATCH 2/3] ApiListener: Sync runtime configs in order --- lib/remote/apilistener-configsync.cpp | 75 ++++++++++++++++++++++----- lib/remote/apilistener.hpp | 2 + 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/lib/remote/apilistener-configsync.cpp b/lib/remote/apilistener-configsync.cpp index 04436ad8b99..9584990569a 100644 --- a/lib/remote/apilistener-configsync.cpp +++ b/lib/remote/apilistener-configsync.cpp @@ -5,11 +5,13 @@ #include "remote/configobjectutility.hpp" #include "remote/jsonrpc.hpp" #include "base/configtype.hpp" -#include "base/json.hpp" #include "base/convert.hpp" +#include "base/dependencygraph.hpp" +#include "base/json.hpp" #include "config/vmops.hpp" #include "remote/configobjectslock.hpp" #include +#include using namespace icinga; @@ -393,6 +395,53 @@ void ApiListener::UpdateConfigObject(const ConfigObject::Ptr& object, const Mess } } +/** + * Syncs the specified object and its direct and intermediate parents to the provided client + * in topological order of their dependency graph recursively. + * + * Objects that the client does not have access to are skipped without going through their dependency graph. + * + * Please do not use this method to forward remote generated cluster updates; it should only be used to + * send local updates to that specific non-nullptr client. + * + * @param object ConfigObject::Ptr The config object you want to sync. + * @param client JsonRpcConnection::Ptr The JsonRpc client you send the update to. + * @param syncedObjects std::unordered_set Used to cache the already synced objects. + * It won't be used if the origin parameter is set. + */ +void ApiListener::UpdateConfigObjectWithParents(const ConfigObject::Ptr& object, const JsonRpcConnection::Ptr& client, + std::unordered_set& syncedObjects) +{ + // Don't sync already synced objects. + if (syncedObjects.find(object.get()) != syncedObjects.end()) { + return; + } + + VERIFY(client); + + Endpoint::Ptr endpoint = client->GetEndpoint(); + ASSERT(endpoint); + + Zone::Ptr azone = endpoint->GetZone(); + + /* don't sync objects for non-matching parent-child zones */ + if (!azone->CanAccessObject(object)) { + return; + } + syncedObjects.emplace(object.get()); + + for (const Object::Ptr& parent : DependencyGraph::GetParents(object)) { + // Actually, the following dynamic cast should never fail, since the DependencyGraph class + // expects the types to always be of type Object::Ptr and such an object is supposed to always + // point to an instance of the specific derived ConfigObject class. See TypeHelper<>::GetFactory(). + if (ConfigObject::Ptr parentObj = dynamic_pointer_cast(parent)) { + UpdateConfigObjectWithParents(parentObj, client, syncedObjects); + } + } + + /* send the config object to the connected client */ + UpdateConfigObject(object, nullptr, client); +} void ApiListener::DeleteConfigObject(const ConfigObject::Ptr& object, const MessageOrigin::Ptr& origin, const JsonRpcConnection::Ptr& client) @@ -454,19 +503,17 @@ void ApiListener::SendRuntimeConfigObjects(const JsonRpcConnection::Ptr& aclient Log(LogInformation, "ApiListener") << "Syncing runtime objects to endpoint '" << endpoint->GetName() << "'."; - for (const Type::Ptr& type : Type::GetAllTypes()) { - auto *dtype = dynamic_cast(type.get()); - - if (!dtype) - continue; - - for (const ConfigObject::Ptr& object : dtype->GetObjects()) { - /* don't sync objects for non-matching parent-child zones */ - if (!azone->CanAccessObject(object)) - continue; - - /* send the config object to the connected client */ - UpdateConfigObject(object, nullptr, aclient); + std::unordered_set syncedObjects; + for (const Type::Ptr& type : Type::GetConfigTypesSortedByLoadDependencies()) { + if (auto *ctype = dynamic_cast(type.get()); ctype) { + for (const ConfigObject::Ptr& object : ctype->GetObjects()) { + // All objects must be synced sorted by their dependency graph. + // Otherwise, downtimes/comments etc. might get synced before their respective Checkables, which will + // result in comments and downtimes being ignored by the other endpoint since it does not yet know + // about their checkables. Given that the runtime config updates event does not trigger a reload on the + // remote endpoint, these objects won't be synced again until the next reload. + UpdateConfigObjectWithParents(object, aclient, syncedObjects); + } } } diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index fced0a8afb1..e047f643346 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -247,6 +247,8 @@ class ApiListener final : public ObjectImpl /* configsync */ void UpdateConfigObject(const ConfigObject::Ptr& object, const MessageOrigin::Ptr& origin, const JsonRpcConnection::Ptr& client = nullptr); + void UpdateConfigObjectWithParents(const ConfigObject::Ptr& object, const JsonRpcConnection::Ptr& client, + std::unordered_set& syncedObjects); void DeleteConfigObject(const ConfigObject::Ptr& object, const MessageOrigin::Ptr& origin, const JsonRpcConnection::Ptr& client = nullptr); void SendRuntimeConfigObjects(const JsonRpcConnection::Ptr& aclient); From 94f948112dd8671ad2700ff1a74cf422b5fab00a Mon Sep 17 00:00:00 2001 From: Yonas Habteab Date: Wed, 6 Nov 2024 10:09:24 +0100 Subject: [PATCH 3/3] Remove obsolete doc comment --- lib/remote/apilistener-configsync.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/remote/apilistener-configsync.cpp b/lib/remote/apilistener-configsync.cpp index 9584990569a..8bb74eeb6ef 100644 --- a/lib/remote/apilistener-configsync.cpp +++ b/lib/remote/apilistener-configsync.cpp @@ -407,7 +407,6 @@ void ApiListener::UpdateConfigObject(const ConfigObject::Ptr& object, const Mess * @param object ConfigObject::Ptr The config object you want to sync. * @param client JsonRpcConnection::Ptr The JsonRpc client you send the update to. * @param syncedObjects std::unordered_set Used to cache the already synced objects. - * It won't be used if the origin parameter is set. */ void ApiListener::UpdateConfigObjectWithParents(const ConfigObject::Ptr& object, const JsonRpcConnection::Ptr& client, std::unordered_set& syncedObjects)