Config Sync: Properly modify attributes and object version

refs #9927
This commit is contained in:
Michael Friedrich 2015-09-11 14:09:46 +02:00
parent 81a0bc6f1b
commit b2715943c6
4 changed files with 42 additions and 19 deletions

View File

@ -115,7 +115,7 @@ public:
} }
}; };
void ConfigObject::ModifyAttribute(const String& attr, const Value& value) void ConfigObject::ModifyAttribute(const String& attr, const Value& value, bool updateVersion)
{ {
Dictionary::Ptr original_attributes = GetOriginalAttributes(); Dictionary::Ptr original_attributes = GetOriginalAttributes();
bool updated_original_attributes = false; bool updated_original_attributes = false;
@ -184,7 +184,9 @@ void ConfigObject::ModifyAttribute(const String& attr, const Value& value)
ValidateField(fid, newValue, utils); ValidateField(fid, newValue, utils);
SetField(fid, newValue); SetField(fid, newValue);
SetVersion(GetVersion() + 1);
if (updateVersion)
SetVersion(GetVersion() + 1);
if (updated_original_attributes) if (updated_original_attributes)
NotifyOriginalAttributes(); NotifyOriginalAttributes();

View File

@ -53,7 +53,7 @@ public:
Value GetExtension(const String& key); Value GetExtension(const String& key);
void ClearExtension(const String& key); void ClearExtension(const String& key);
void ModifyAttribute(const String& attr, const Value& value); void ModifyAttribute(const String& attr, const Value& value, bool updateVersion = true);
void RestoreAttribute(const String& attr); void RestoreAttribute(const String& attr);
bool IsAttributeModified(const String& attr) const; bool IsAttributeModified(const String& attr) const;

View File

@ -23,6 +23,7 @@
#include "remote/jsonrpc.hpp" #include "remote/jsonrpc.hpp"
#include "base/configtype.hpp" #include "base/configtype.hpp"
#include "base/json.hpp" #include "base/json.hpp"
#include "base/convert.hpp"
#include <fstream> #include <fstream>
using namespace icinga; using namespace icinga;
@ -85,51 +86,66 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin
} }
/* update the object */ /* update the object */
ConfigType::Ptr dtype = ConfigType::GetByName(params->Get("type")); String objType = params->Get("type");
String objName = params->Get("name");
int objVersion = Convert::ToLong(params->Get("version"));
ConfigType::Ptr dtype = ConfigType::GetByName(objType);
if (!dtype) { if (!dtype) {
Log(LogCritical, "ApiListener") Log(LogCritical, "ApiListener")
<< "Config type '" << params->Get("type") << "' does not exist."; << "Config type '" << objType << "' does not exist.";
return Empty; return Empty;
} }
ConfigObject::Ptr object = dtype->GetObject(params->Get("name")); ConfigObject::Ptr object = dtype->GetObject(objName);
if (!object) { if (!object) {
/* object does not exist, create it through the API */
Array::Ptr errors = new Array(); Array::Ptr errors = new Array();
if (!ConfigObjectUtility::CreateObject(Type::GetByName(params->Get("type")), if (!ConfigObjectUtility::CreateObject(Type::GetByName(objType),
params->Get("name"), params->Get("config"), errors)) { objName, params->Get("config"), errors)) {
Log(LogCritical, "ApiListener", "Could not create object:"); Log(LogCritical, "ApiListener")
<< "Could not create object '" << objName << "':";
ObjectLock olock(errors); ObjectLock olock(errors);
BOOST_FOREACH(const String& error, errors) { BOOST_FOREACH(const String& error, errors) {
Log(LogCritical, "ApiListener", error); Log(LogCritical, "ApiListener", error);
} }
} else {
/* object was created, update its version to its origin */
ConfigObject::Ptr newObj = dtype->GetObject(objName);
if (newObj)
newObj->SetVersion(objVersion);
} }
//TODO-MA: modified attributes, same version
} else { } else {
/* object exists, update its attributes if version was changed */ /* object exists, update its attributes if version was changed */
if (params->Get("version") > object->GetVersion()) { if (objVersion > object->GetVersion()) {
Log(LogInformation, "ApiListener") Log(LogInformation, "ApiListener")
<< "Processing config update for object '" << object->GetName() << "Processing config update for object '" << object->GetName()
<< "': Object version '" << object->GetVersion() << "': Object version " << object->GetVersion()
<< "' is older than the received version '" << params->Get("version") << "'."; << " is older than the received version " << objVersion << ".";
Dictionary::Ptr modified_attributes = params->Get("modified_attributes"); Dictionary::Ptr modified_attributes = params->Get("modified_attributes");
if (modified_attributes) { if (modified_attributes) {
ObjectLock olock(modified_attributes); ObjectLock olock(modified_attributes);
BOOST_FOREACH(const Dictionary::Pair& kv, modified_attributes) { BOOST_FOREACH(const Dictionary::Pair& kv, modified_attributes) {
int fid = object->GetReflectionType()->GetFieldId(kv.first); /* update all modified attributes
static_cast<Object::Ptr>(object)->SetField(fid, kv.second, false, origin); * but do not update the object version yet.
* This triggers cluster events otherwise.
*/
object->ModifyAttribute(kv.first, kv.second, false);
} }
} }
/* keep the object version in sync with the sender */
object->SetVersion(objVersion);
} else { } else {
Log(LogWarning, "ApiListener") Log(LogWarning, "ApiListener")
<< "Skipping config update for object '" << object->GetName() << "Discarding config update for object '" << object->GetName()
<< "': Object version '" << object->GetVersion() << "': Object version " << object->GetVersion()
<< "' is more recent than the received version '" << params->Get("version") << "'."; << " is more recent than the received version " << objVersion << ".";
return Empty; return Empty;
} }
} }

View File

@ -807,6 +807,11 @@ void ApiListener::ReplayLog(const JsonRpcConnection::Ptr& client)
logStream->Close(); logStream->Close();
} }
if (count > 0) {
Log(LogInformation, "ApiListener")
<< "Replayed " << count << " messages.";
}
Log(LogNotice, "ApiListener") Log(LogNotice, "ApiListener")
<< "Replayed " << count << " messages."; << "Replayed " << count << " messages.";