Add zone attribute influencing cluster config sync for API objects

1) No zone defined. The object will only be synced in the local zone for HA purposes.
2) Zone is set to 'master'. Only nodes in the master zone will get this object and updates synced.
3) Zone is set to 'satellite'. Only nodes in the satellite zone, or in parent zones above will get this object and updates synced.
4) Zone is set to 'client'. Only nodes in the client zone, and in parent zones (satellite, master) will get object updates.

Furthermore this commit adds a bit more security measures for syncing object
config bottom-up which is clearly restricted at this time. Clients cannot
send their config to the top, but yet we only support the top-down thing we
also have with the cluster file config sync.

The initial sync will also take the zone relation model into account
and only allow object syncs only when the same conditions apply as written
above.

refs #9927
refs #9100
This commit is contained in:
Michael Friedrich 2015-09-17 13:54:09 +02:00
parent 0fd9d3406b
commit 18d645e4ef
1 changed files with 61 additions and 18 deletions

View File

@ -36,7 +36,6 @@ REGISTER_APIFUNCTION(DeleteObject, config, &ApiListener::ConfigDeleteObjectAPIHa
void ApiListener::StaticInitialize(void)
{
//TODO: Figure out how to delete objects during runtime, but not on shutdown (object inactive)
ConfigObject::OnActiveChanged.connect(&ApiListener::ConfigUpdateObjectHandler);
ConfigObject::OnVersionChanged.connect(&ApiListener::ConfigUpdateObjectHandler);
}
@ -61,7 +60,7 @@ void ApiListener::ConfigUpdateObjectHandler(const ConfigObject::Ptr& object, con
Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
{
Log(LogWarning, "ApiListener")
Log(LogNotice, "ApiListener")
<< "Received update for object: " << JsonEncode(params);
/* check permissions */
@ -86,6 +85,19 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin
return Empty;
}
Zone::Ptr lzone = Zone::GetLocalZone();
String objZoneName = params->Get("zone");
Zone::Ptr objZone = Zone::GetByName(objZoneName);
/* discard messages if a) not the target zone and b) object zone is not a child of the local zone */
if (objZone && (lzone->GetName() != objZoneName && !objZone->IsChildOf(lzone))) {
Log(LogNotice, "ApiListener")
<< "Discarding 'config update object' message from '"
<< origin->FromClient->GetIdentity() << "': Object with zone '" << objZoneName << "' not allowed in zone '"
<< lzone->GetName() << "'.";
return Empty;
}
/* update the object */
String objType = params->Get("type");
String objName = params->Get("name");
@ -143,7 +155,7 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin
/* keep the object version in sync with the sender */
object->SetVersion(objVersion);
} else {
Log(LogWarning, "ApiListener")
Log(LogNotice, "ApiListener")
<< "Discarding config update for object '" << object->GetName()
<< "': Object version " << object->GetVersion()
<< " is more recent than the received version " << objVersion << ".";
@ -156,7 +168,7 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin
Value ApiListener::ConfigDeleteObjectAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params)
{
Log(LogWarning, "ApiListener")
Log(LogNotice, "ApiListener")
<< "Received update for object: " << JsonEncode(params);
/* check permissions */
@ -181,6 +193,19 @@ Value ApiListener::ConfigDeleteObjectAPIHandler(const MessageOrigin::Ptr& origin
return Empty;
}
Zone::Ptr lzone = Zone::GetLocalZone();
String objZoneName = params->Get("zone");
Zone::Ptr objZone = Zone::GetByName(objZoneName);
/* discard messages if a) not the target zone or b) object zone is not a child of the local zone */
if (objZone && (lzone->GetName() != objZoneName && !objZone->IsChildOf(lzone))) {
Log(LogNotice, "ApiListener")
<< "Discarding 'config update object' message from '"
<< origin->FromClient->GetIdentity() << "': Object with zone '" << objZoneName << "' not allowed in zone '"
<< lzone->GetName() << "'.";
return Empty;
}
/* delete the object */
ConfigType::Ptr dtype = ConfigType::GetByName(params->Get("type"));
@ -193,7 +218,6 @@ Value ApiListener::ConfigDeleteObjectAPIHandler(const MessageOrigin::Ptr& origin
ConfigObject::Ptr object = dtype->GetObject(params->Get("name"));
if (object) {
if (object->GetPackage() != "_api") {
Log(LogCritical, "ApiListener")
<< "Could not delete object '" << object->GetName() << "': Not created by the API.";
@ -211,7 +235,7 @@ Value ApiListener::ConfigDeleteObjectAPIHandler(const MessageOrigin::Ptr& origin
}
}
} else {
Log(LogWarning, "ApiListener")
Log(LogNotice, "ApiListener")
<< "Could not delete non-existing object '" << params->Get("name") << "'.";
}
@ -232,6 +256,8 @@ void ApiListener::UpdateConfigObject(const ConfigObject::Ptr& object, const Mess
params->Set("name", object->GetName());
params->Set("type", object->GetType()->GetName());
params->Set("version", object->GetVersion());
/* required for acceptance criteria on the client */
params->Set("zone", object->GetZoneName());
String file = ConfigObjectUtility::GetObjectConfigPath(object->GetReflectionType(), object->GetName());
@ -259,8 +285,10 @@ void ApiListener::UpdateConfigObject(const ConfigObject::Ptr& object, const Mess
message->Set("params", params);
Log(LogWarning, "ApiListener")
#ifdef I2_DEBUG
Log(LogDebug, "ApiListener")
<< "Sent update for object: " << JsonEncode(params);
#endif /* I2_DEBUG */
if (client)
JsonRpc::SendMessage(client->GetStream(), message);
@ -283,11 +311,15 @@ void ApiListener::DeleteConfigObject(const ConfigObject::Ptr& object, const Mess
params->Set("name", object->GetName());
params->Set("type", object->GetType()->GetName());
params->Set("version", object->GetVersion());
/* required for acceptance criteria on the client */
params->Set("zone", object->GetZoneName());
message->Set("params", params);
Log(LogWarning, "ApiListener")
#ifdef I2_DEBUG
Log(LogDebug, "ApiListener")
<< "Sent delete object: " << JsonEncode(params);
#endif /* I2_DEBUG */
if (client)
JsonRpc::SendMessage(client->GetStream(), message);
@ -295,6 +327,7 @@ void ApiListener::DeleteConfigObject(const ConfigObject::Ptr& object, const Mess
RelayMessage(origin, object, message, false);
}
/* Initial sync on connect for new endpoints */
void ApiListener::SendRuntimeConfigObjects(const JsonRpcConnection::Ptr& aclient)
{
Endpoint::Ptr endpoint = aclient->GetEndpoint();
@ -303,24 +336,34 @@ void ApiListener::SendRuntimeConfigObjects(const JsonRpcConnection::Ptr& aclient
Zone::Ptr azone = endpoint->GetZone();
Zone::Ptr lzone = Zone::GetLocalZone();
/* only sync objects in the same zone for now */
if (azone->GetName() != lzone->GetName()) {
Log(LogWarning, "ApiListener")
<< "Skipping object sync to endpoint '" << endpoint->GetName()
<< "' in zone '" << azone->GetName() << "'. Not in the same zone '"
<< lzone->GetName() << "'.";
return;
}
Log(LogInformation, "ApiListener")
<< "Syncing runtime objects to endpoint '" << endpoint->GetName() << "'.";
//TODO get the active stage for "_api" and all objects instead of fetching all objects in memory?
BOOST_FOREACH(const ConfigType::Ptr& dt, ConfigType::GetTypes()) {
BOOST_FOREACH(const ConfigObject::Ptr& object, dt->GetObjects()) {
if (object->GetPackage() != "_api")
continue;
String objZone = object->GetZoneName();
/* only sync objects in the same zone if no zone attribute was set */
if (objZone.IsEmpty() && azone != lzone) {
Log(LogDebug, "ApiListener")
<< "Skipping sync: No object zone specified and client zone '"
<< azone->GetName() << "' does not match local zone '"
<< lzone->GetName() << "'.";
continue;
}
/* don't sync objects for non-matching parent-child zones */
if (!objZone.IsEmpty() && !azone->IsChildOf(lzone)) {
Log(LogDebug, "ApiListener")
<< "Skipping sync: object zone '" << objZone
<< "' defined but client zone '" << azone->GetName()
<< "' is not a child of the local zone '" << lzone->GetName() << "'.";
continue;
}
/* send the config object to the connected client */
UpdateConfigObject(object, MessageOrigin::Ptr(), aclient);
}