From: Gunnar Beutner Date: Tue, 26 Jan 2016 09:46:27 +0000 (+0100) Subject: Make sure the updated config sync works with old versions of Icinga 2 X-Git-Tag: v2.5.0~589 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f3351bc80ebdcb75a85784d964816eae2056d62f;p=icinga2 Make sure the updated config sync works with old versions of Icinga 2 refs #11014 --- diff --git a/lib/remote/apilistener-filesync.cpp b/lib/remote/apilistener-filesync.cpp index bc1d11102..dcb9af9b0 100644 --- a/lib/remote/apilistener-filesync.cpp +++ b/lib/remote/apilistener-filesync.cpp @@ -32,7 +32,7 @@ using namespace icinga; REGISTER_APIFUNCTION(Update, config, &ApiListener::ConfigUpdateHandler); -void ApiListener::ConfigGlobHandler(Dictionary::Ptr& config, const String& path, const String& file) +void ApiListener::ConfigGlobHandler(ConfigDirInformation& config, const String& path, const String& file) { CONTEXT("Creating config update for file '" + file + "'"); @@ -44,20 +44,46 @@ void ApiListener::ConfigGlobHandler(Dictionary::Ptr& config, const String& path, return; String content((std::istreambuf_iterator(fp)), std::istreambuf_iterator()); - config->Set(file.SubStr(path.GetLength()), content); + + Dictionary::Ptr update; + + if (Utility::Match("*.conf", file)) + update = config.UpdateV1; + else + update = config.UpdateV2; + + update->Set(file.SubStr(path.GetLength()), content); +} + +Dictionary::Ptr ApiListener::MergeConfigUpdate(const ConfigDirInformation& config) +{ + Dictionary::Ptr result = new Dictionary(); + + if (config.UpdateV1) + config.UpdateV1->CopyTo(result); + + if (config.UpdateV2) + config.UpdateV2->CopyTo(result); + + return result; } -Dictionary::Ptr ApiListener::LoadConfigDir(const String& dir) +ConfigDirInformation ApiListener::LoadConfigDir(const String& dir) { - Dictionary::Ptr config = new Dictionary(); + ConfigDirInformation config; + config.UpdateV1 = new Dictionary(); + config.UpdateV2 = new Dictionary(); Utility::GlobRecursive(dir, "*", boost::bind(&ApiListener::ConfigGlobHandler, boost::ref(config), dir, _1), GlobFile); return config; } -bool ApiListener::UpdateConfigDir(const Dictionary::Ptr& oldConfig, const Dictionary::Ptr& newConfig, const String& configDir, bool authoritative) +bool ApiListener::UpdateConfigDir(const ConfigDirInformation& oldConfigInfo, const ConfigDirInformation& newConfigInfo, const String& configDir, bool authoritative) { bool configChange = false; + Dictionary::Ptr oldConfig = MergeConfigUpdate(oldConfigInfo); + Dictionary::Ptr newConfig = MergeConfigUpdate(newConfigInfo); + double oldTimestamp; if (!oldConfig->Contains(".timestamp")) @@ -125,29 +151,43 @@ bool ApiListener::UpdateConfigDir(const Dictionary::Ptr& oldConfig, const Dictio void ApiListener::SyncZoneDir(const Zone::Ptr& zone) const { - Dictionary::Ptr newConfig = new Dictionary(); + ConfigDirInformation newConfigInfo; + newConfigInfo.UpdateV1 = new Dictionary(); + newConfigInfo.UpdateV2 = new Dictionary(); + BOOST_FOREACH(const ZoneFragment& zf, ConfigCompiler::GetZoneDirs(zone->GetName())) { - Dictionary::Ptr newConfigPart = LoadConfigDir(zf.Path); + ConfigDirInformation newConfigPart = LoadConfigDir(zf.Path); - ObjectLock olock(newConfigPart); - BOOST_FOREACH(const Dictionary::Pair& kv, newConfigPart) { - newConfig->Set("/" + zf.Tag + kv.first, kv.second); + { + ObjectLock olock(newConfigPart.UpdateV1); + BOOST_FOREACH(const Dictionary::Pair& kv, newConfigPart.UpdateV1) { + newConfigInfo.UpdateV1->Set("/" + zf.Tag + kv.first, kv.second); + } + } + + { + ObjectLock olock(newConfigPart.UpdateV2); + BOOST_FOREACH(const Dictionary::Pair& kv, newConfigPart.UpdateV2) { + newConfigInfo.UpdateV2->Set("/" + zf.Tag + kv.first, kv.second); + } } } - if (newConfig->GetLength() == 0) + int sumUpdates = newConfigInfo.UpdateV1->GetLength() + newConfigInfo.UpdateV2->GetLength(); + + if (sumUpdates == 0) return; String oldDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones/" + zone->GetName(); Log(LogInformation, "ApiListener") - << "Copying " << newConfig->GetLength() << " zone configuration files for zone '" << zone->GetName() << "' to '" << oldDir << "'."; + << "Copying " << sumUpdates << " zone configuration files for zone '" << zone->GetName() << "' to '" << oldDir << "'."; Utility::MkDirP(oldDir, 0700); - Dictionary::Ptr oldConfig = LoadConfigDir(oldDir); + ConfigDirInformation oldConfigInfo = LoadConfigDir(oldDir); - UpdateConfigDir(oldConfig, newConfig, oldDir, true); + UpdateConfigDir(oldConfigInfo, newConfigInfo, oldDir, true); } void ApiListener::SyncZoneDirs(void) const @@ -173,7 +213,8 @@ void ApiListener::SendConfigUpdate(const JsonRpcConnection::Ptr& aclient) if (!azone->IsChildOf(lzone)) return; - Dictionary::Ptr configUpdate = new Dictionary(); + Dictionary::Ptr configUpdateV1 = new Dictionary(); + Dictionary::Ptr configUpdateV2 = new Dictionary(); String zonesDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones"; @@ -190,11 +231,14 @@ void ApiListener::SendConfigUpdate(const JsonRpcConnection::Ptr& aclient) << "Syncing " << (zone->IsGlobal() ? "global " : "") << "zone '" << zone->GetName() << "' to endpoint '" << endpoint->GetName() << "'."; - configUpdate->Set(zone->GetName(), LoadConfigDir(zonesDir + "/" + zone->GetName())); + ConfigDirInformation config = LoadConfigDir(zonesDir + "/" + zone->GetName()); + configUpdateV1->Set(zone->GetName(), config.UpdateV1); + configUpdateV2->Set(zone->GetName(), config.UpdateV2); } Dictionary::Ptr params = new Dictionary(); - params->Set("update", configUpdate); + params->Set("update", configUpdateV1); + params->Set("update_v2", configUpdateV2); Dictionary::Ptr message = new Dictionary(); message->Set("jsonrpc", "2.0"); @@ -222,12 +266,13 @@ Value ApiListener::ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const D return Empty; } - Dictionary::Ptr update = params->Get("update"); + Dictionary::Ptr updateV1 = params->Get("update"); + Dictionary::Ptr updateV2 = params->Get("update_v2"); bool configChange = false; - ObjectLock olock(update); - BOOST_FOREACH(const Dictionary::Pair& kv, update) { + ObjectLock olock(updateV1); + BOOST_FOREACH(const Dictionary::Pair& kv, updateV1) { Zone::Ptr zone = Zone::GetByName(kv.first); if (!zone) { @@ -246,10 +291,16 @@ Value ApiListener::ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const D Utility::MkDirP(oldDir, 0700); + ConfigDirInformation newConfigInfo; + newConfigInfo.UpdateV1 = kv.second; + + if (updateV2) + newConfigInfo.UpdateV2 = updateV2->Get(kv.first); + Dictionary::Ptr newConfig = kv.second; - Dictionary::Ptr oldConfig = LoadConfigDir(oldDir); + ConfigDirInformation oldConfigInfo = LoadConfigDir(oldDir); - if (UpdateConfigDir(oldConfig, newConfig, oldDir, false)) + if (UpdateConfigDir(oldConfigInfo, newConfigInfo, oldDir, false)) configChange = true; } diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index f5fa65b80..3b02a3bb0 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -37,6 +37,15 @@ namespace icinga class JsonRpcConnection; +/** + * @ingroup remote + */ +struct ConfigDirInformation +{ + Dictionary::Ptr UpdateV1; + Dictionary::Ptr UpdateV2; +}; + /** * @ingroup remote */ @@ -129,13 +138,14 @@ private: void ReplayLog(const JsonRpcConnection::Ptr& client); /* filesync */ - static Dictionary::Ptr LoadConfigDir(const String& dir); - static bool UpdateConfigDir(const Dictionary::Ptr& oldConfig, const Dictionary::Ptr& newConfig, const String& configDir, bool authoritative); + static ConfigDirInformation LoadConfigDir(const String& dir); + static Dictionary::Ptr MergeConfigUpdate(const ConfigDirInformation& config); + static bool UpdateConfigDir(const ConfigDirInformation& oldConfig, const ConfigDirInformation& newConfig, const String& configDir, bool authoritative); void SyncZoneDirs(void) const; void SyncZoneDir(const Zone::Ptr& zone) const; - static void ConfigGlobHandler(Dictionary::Ptr& config, const String& path, const String& file); + static void ConfigGlobHandler(ConfigDirInformation& config, const String& path, const String& file); void SendConfigUpdate(const JsonRpcConnection::Ptr& aclient); /* configsync */