diff --git a/lib/base/configobject.cpp b/lib/base/configobject.cpp index e07079658..8d3e07656 100644 --- a/lib/base/configobject.cpp +++ b/lib/base/configobject.cpp @@ -357,7 +357,7 @@ void ConfigObject::PreActivate() SetActive(true, true); } -void ConfigObject::Activate(bool runtimeCreated) +void ConfigObject::Activate(bool runtimeCreated, const Value& cookie) { CONTEXT("Activating object '" + GetName() + "' of type '" + GetReflectionType()->GetName() + "'"); @@ -372,7 +372,7 @@ void ConfigObject::Activate(bool runtimeCreated) SetAuthority(true); } - NotifyActive(); + NotifyActive(cookie); } void ConfigObject::Stop(bool runtimeRemoved) @@ -384,7 +384,7 @@ void ConfigObject::Stop(bool runtimeRemoved) SetStopCalled(true); } -void ConfigObject::Deactivate(bool runtimeRemoved) +void ConfigObject::Deactivate(bool runtimeRemoved, const Value& cookie) { CONTEXT("Deactivating object '" + GetName() + "' of type '" + GetReflectionType()->GetName() + "'"); @@ -403,7 +403,7 @@ void ConfigObject::Deactivate(bool runtimeRemoved) ASSERT(GetStopCalled()); - NotifyActive(); + NotifyActive(cookie); } void ConfigObject::OnConfigLoaded() diff --git a/lib/base/configobject.hpp b/lib/base/configobject.hpp index 1e89d45a9..559636370 100644 --- a/lib/base/configobject.hpp +++ b/lib/base/configobject.hpp @@ -44,8 +44,8 @@ public: void Unregister(); void PreActivate(); - void Activate(bool runtimeCreated = false); - void Deactivate(bool runtimeRemoved = false); + void Activate(bool runtimeCreated = false, const Value& cookie = Empty); + void Deactivate(bool runtimeRemoved = false, const Value& cookie = Empty); void SetAuthority(bool authority); void Start(bool runtimeCreated = false) override; diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp index a7c960c9b..5c9f0dd55 100644 --- a/lib/config/configitem.cpp +++ b/lib/config/configitem.cpp @@ -624,7 +624,8 @@ bool ConfigItem::CommitItems(const ActivationContext::Ptr& context, WorkQueue& u return true; } -bool ConfigItem::ActivateItems(WorkQueue& upq, const std::vector& newItems, bool runtimeCreated, bool silent, bool withModAttrs) +bool ConfigItem::ActivateItems(WorkQueue& upq, const std::vector& newItems, bool runtimeCreated, + bool silent, bool withModAttrs, const Value& cookie) { static boost::mutex mtx; boost::mutex::scoped_lock lock(mtx); @@ -692,7 +693,7 @@ bool ConfigItem::ActivateItems(WorkQueue& upq, const std::vectorGetActivationPriority(); #endif /* I2_DEBUG */ - object->Activate(runtimeCreated); + object->Activate(runtimeCreated, cookie); } } diff --git a/lib/config/configitem.hpp b/lib/config/configitem.hpp index 1064fb273..0b7ee86a3 100644 --- a/lib/config/configitem.hpp +++ b/lib/config/configitem.hpp @@ -53,7 +53,8 @@ public: const String& name); static bool CommitItems(const ActivationContext::Ptr& context, WorkQueue& upq, std::vector& newItems, bool silent = false); - static bool ActivateItems(WorkQueue& upq, const std::vector& newItems, bool runtimeCreated = false, bool silent = false, bool withModAttrs = false); + static bool ActivateItems(WorkQueue& upq, const std::vector& newItems, bool runtimeCreated = false, + bool silent = false, bool withModAttrs = false, const Value& cookie = Empty); static bool RunWithActivationContext(const Function::Ptr& function); diff --git a/lib/remote/apilistener-configsync.cpp b/lib/remote/apilistener-configsync.cpp index 6b4bec138..3b881afe1 100644 --- a/lib/remote/apilistener-configsync.cpp +++ b/lib/remote/apilistener-configsync.cpp @@ -99,7 +99,11 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin /* object does not exist, create it through the API */ Array::Ptr errors = new Array(); - if (!ConfigObjectUtility::CreateObject(ptype, objName, config, errors, nullptr)) { + /* + * Create the config object through our internal API. + * IMPORTANT: Pass the origin to prevent cluster sync loops. + */ + if (!ConfigObjectUtility::CreateObject(ptype, objName, config, errors, nullptr, origin)) { Log(LogCritical, "ApiListener") << "Could not create object '" << objName << "':"; @@ -238,7 +242,11 @@ Value ApiListener::ConfigDeleteObjectAPIHandler(const MessageOrigin::Ptr& origin Array::Ptr errors = new Array(); - if (!ConfigObjectUtility::DeleteObject(object, true, errors, nullptr)) { + /* + * Delete the config object through our internal API. + * IMPORTANT: Pass the origin to prevent cluster sync loops. + */ + if (!ConfigObjectUtility::DeleteObject(object, true, errors, nullptr, origin)) { Log(LogCritical, "ApiListener", "Could not delete object:"); ObjectLock olock(errors); diff --git a/lib/remote/configobjectutility.cpp b/lib/remote/configobjectutility.cpp index f7c405e86..3ccb7b926 100644 --- a/lib/remote/configobjectutility.cpp +++ b/lib/remote/configobjectutility.cpp @@ -148,7 +148,7 @@ String ConfigObjectUtility::CreateObjectConfig(const Type::Ptr& type, const Stri } bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& fullName, - const String& config, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation) + const String& config, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation, const Value& cookie) { CreateStorage(); @@ -188,9 +188,37 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full std::vector newItems; - /* Disable logging for object creation, but do so ourselves later on. */ - if (!ConfigItem::CommitItems(ascope.GetContext(), upq, newItems, true) || !ConfigItem::ActivateItems(upq, newItems, true, true)) { + /* + * Disable logging for object creation, but do so ourselves later on. + * Duplicate the error handling for better logging and debugging here. + */ + if (!ConfigItem::CommitItems(ascope.GetContext(), upq, newItems, true)) { if (errors) { + Log(LogNotice, "ConfigObjectUtility") + << "Failed to commit config item '" << fullName << "'. Aborting and emoving config path '" << path << "'."; + + Utility::Remove(path); + + for (const boost::exception_ptr& ex : upq.GetExceptions()) { + errors->Add(DiagnosticInformation(ex, false)); + + if (diagnosticInformation) + diagnosticInformation->Add(DiagnosticInformation(ex)); + } + } + + return false; + } + + /* + * Activate the config object. + * IMPORTANT: Forward the cookie aka origin in order to prevent sync loops in the same zone! + */ + if (!ConfigItem::ActivateItems(upq, newItems, true, true, cookie)) { + if (errors) { + Log(LogNotice, "ConfigObjectUtility") + << "Failed to activate config object '" << fullName << "'. Aborting and emoving config path '" << path << "'."; + Utility::Remove(path); for (const boost::exception_ptr& ex : upq.GetExceptions()) { @@ -211,7 +239,6 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full if (type->GetName() != "Comment" && type->GetName() != "Downtime") ApiListener::UpdateObjectAuthority(); - Log(LogInformation, "ConfigObjectUtility") << "Created and activated object '" << fullName << "' of type '" << type->GetName() << "'."; @@ -231,7 +258,7 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full } bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bool cascade, - const Array::Ptr& errors, const Array::Ptr& diagnosticInformation) + const Array::Ptr& errors, const Array::Ptr& diagnosticInformation, const Value& cookie) { std::vector parents = DependencyGraph::GetParents(object); @@ -255,7 +282,7 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo if (!parentObj) continue; - DeleteObjectHelper(parentObj, cascade, errors, diagnosticInformation); + DeleteObjectHelper(parentObj, cascade, errors, diagnosticInformation, cookie); } ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, name); @@ -263,8 +290,13 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo try { /* mark this object for cluster delete event */ object->SetExtension("ConfigObjectDeleted", true); - /* triggers signal for DB IDO and other interfaces */ - object->Deactivate(true); + + /* + * Trigger deactivation signal for DB IDO and runtime object delections. + * IMPORTANT: Specify the cookie aka origin in order to prevent sync loops + * in the same zone! + */ + object->Deactivate(true, cookie); if (item) item->Unregister(); @@ -298,7 +330,8 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo return true; } -bool ConfigObjectUtility::DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation) +bool ConfigObjectUtility::DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors, + const Array::Ptr& diagnosticInformation, const Value& cookie) { if (object->GetPackage() != "_api") { if (errors) @@ -307,5 +340,5 @@ bool ConfigObjectUtility::DeleteObject(const ConfigObject::Ptr& object, bool cas return false; } - return DeleteObjectHelper(object, cascade, errors, diagnosticInformation); + return DeleteObjectHelper(object, cascade, errors, diagnosticInformation, cookie); } diff --git a/lib/remote/configobjectutility.hpp b/lib/remote/configobjectutility.hpp index 404bc3bad..f383a211b 100644 --- a/lib/remote/configobjectutility.hpp +++ b/lib/remote/configobjectutility.hpp @@ -30,15 +30,15 @@ public: bool ignoreOnError, const Array::Ptr& templates, const Dictionary::Ptr& attrs); static bool CreateObject(const Type::Ptr& type, const String& fullName, - const String& config, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation); + const String& config, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation, const Value& cookie = Empty); static bool DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors, - const Array::Ptr& diagnosticInformation); + const Array::Ptr& diagnosticInformation, const Value& cookie = Empty); private: static String EscapeName(const String& name); static bool DeleteObjectHelper(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors, - const Array::Ptr& diagnosticInformation); + const Array::Ptr& diagnosticInformation, const Value& cookie = Empty); }; }