]> granicus.if.org Git - icinga2/commitdiff
Fix cluster config sync for (non-)authoritative configs
authorMichael Friedrich <michael.friedrich@gmail.com>
Fri, 11 Dec 2015 18:54:17 +0000 (19:54 +0100)
committerMichael Friedrich <michael.friedrich@netways.de>
Sat, 12 Dec 2015 11:16:00 +0000 (12:16 +0100)
Details: https://dev.icinga.org/issues/10819#note-39

refs #10819

lib/cli/daemonutility.cpp
lib/config/configcompiler.cpp
lib/config/configcompiler.hpp
lib/remote/apilistener-filesync.cpp
lib/remote/apilistener.hpp

index 5c576e17c1929b854ed19886c0f529964a124e37..26d37c78570d4184c4d1eca5098b73d119df4739 100644 (file)
@@ -60,25 +60,40 @@ static void IncludeZoneDirRecursive(const String& path, const String& package, b
 
 static void IncludeNonLocalZone(const String& zonePath, const String& package, bool& success)
 {
-       String etcPath = Application::GetZonesDir() + "/" + Utility::BaseName(zonePath);
+       /* Note: This include function must not call RegisterZoneDir().
+        * We do not need to copy it for cluster config sync. */
 
-       if (Utility::PathExists(etcPath) || Utility::PathExists(zonePath + "/.authoritative"))
+       String zoneName = Utility::BaseName(zonePath);
+
+       /* Check whether this node already has an authoritative config version
+        * from zones.d in etc or api package directory, or a local marker file)
+        */
+       if (ConfigCompiler::HasZoneConfigAuthority(zoneName) || Utility::PathExists(zonePath + "/.authoritative")) {
+               Log(LogWarning, "config")
+                   << "Ignoring non local config include for zone '" << zoneName << "': We already have an authoritative copy included.";
                return;
+       }
 
-       IncludeZoneDirRecursive(zonePath, package, success);
+       std::vector<Expression *> expressions;
+       Utility::GlobRecursive(zonePath, "*.conf", boost::bind(&ConfigCompiler::CollectIncludes, boost::ref(expressions), _1, zoneName, package), GlobFile);
+       DictExpression expr(expressions);
+       if (!ExecuteExpression(&expr))
+               success = false;
 }
 
 static void IncludePackage(const String& packagePath, bool& success)
 {
+       /* Note: Package includes will register their zones
+        * for config sync inside their generated config. */
        String packageName = Utility::BaseName(packagePath);
-       
+
        if (Utility::PathExists(packagePath + "/include.conf")) {
                Expression *expr = ConfigCompiler::CompileFile(packagePath + "/include.conf",
                    String(), packageName);
-               
+
                if (!ExecuteExpression(expr))
                        success = false;
-                       
+
                delete expr;
        }
 }
@@ -99,8 +114,9 @@ bool DaemonUtility::ValidateConfigFiles(const std::vector<std::string>& configs,
                }
        }
 
-       /* Load cluster config files - this should probably be in libremote but
-       * unfortunately moving it there is somewhat non-trivial. */
+       /* Load cluster config files from /etc/icinga2/zones.d.
+        * This should probably be in libremote but
+        * unfortunately moving it there is somewhat non-trivial. */
        success = true;
 
        String zonesEtcDir = Application::GetZonesDir();
@@ -110,16 +126,19 @@ bool DaemonUtility::ValidateConfigFiles(const std::vector<std::string>& configs,
        if (!success)
                return false;
 
-       String zonesVarDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones";
-       if (Utility::PathExists(zonesVarDir))
-               Utility::Glob(zonesVarDir + "/*", boost::bind(&IncludeNonLocalZone, _1, "_cluster", boost::ref(success)), GlobDirectory);
+       /* Load package config files - they may contain additional zones which
+        * are authoritative on this node and are checked in HasZoneConfigAuthority(). */
+       String packagesVarDir = Application::GetLocalStateDir() + "/lib/icinga2/api/packages";
+       if (Utility::PathExists(packagesVarDir))
+               Utility::Glob(packagesVarDir + "/*", boost::bind(&IncludePackage, _1, boost::ref(success)), GlobDirectory);
 
        if (!success)
                return false;
 
-       String packagesVarDir = Application::GetLocalStateDir() + "/lib/icinga2/api/packages";
-       if (Utility::PathExists(packagesVarDir))
-               Utility::Glob(packagesVarDir + "/*", boost::bind(&IncludePackage, _1, boost::ref(success)), GlobDirectory);
+       /* Load cluster synchronized configuration files */
+       String zonesVarDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones";
+       if (Utility::PathExists(zonesVarDir))
+               Utility::Glob(zonesVarDir + "/*", boost::bind(&IncludeNonLocalZone, _1, "_cluster", boost::ref(success)), GlobDirectory);
 
        if (!success)
                return false;
index 1bda22670a507a22913f8c938a5219df24ec32b3..8eb523711d6da2e824107e98d5ed650c3facf473 100644 (file)
@@ -323,3 +323,22 @@ void ConfigCompiler::RegisterZoneDir(const String& tag, const String& ppath, con
        m_ZoneDirs[zoneName].push_back(zf);
 }
 
+bool ConfigCompiler::HasZoneConfigAuthority(const String& zoneName)
+{
+       std::vector<ZoneFragment> zoneDirs = m_ZoneDirs[zoneName];
+
+       bool empty = zoneDirs.empty();
+
+       if (!empty) {
+               std::vector<String> paths;
+               BOOST_FOREACH(const ZoneFragment& zf, zoneDirs) {
+                       paths.push_back(zf.Path);
+               }
+
+               Log(LogNotice, "ConfigCompiler")
+                   << "Registered authoritative config directories for zone '" << zoneName << "': " << Utility::NaturalJoin(paths);
+       }
+
+       return !empty;
+}
+
index 154717766043e8fcfb279d91716a2622f7b6576e..fe8826abcf065e12de1223268bd7e2d17c66c800 100644 (file)
@@ -118,6 +118,8 @@ public:
        static std::vector<ZoneFragment> GetZoneDirs(const String& zone);
        static void RegisterZoneDir(const String& tag, const String& ppath, const String& zoneName);
 
+       static bool HasZoneConfigAuthority(const String& zoneName);
+
 private:
        boost::promise<boost::shared_ptr<Expression> > m_Promise;
 
index 303cc96ef7db3e2157133706f557db683489deff..02f29934c9ada9cbfb4b9b8008c1918508817244 100644 (file)
@@ -32,21 +32,6 @@ using namespace icinga;
 
 REGISTER_APIFUNCTION(Update, config, &ApiListener::ConfigUpdateHandler);
 
-bool ApiListener::IsConfigMaster(const Zone::Ptr& zone)
-{
-       std::vector<ZoneFragment> zoneDirs = ConfigCompiler::GetZoneDirs(zone->GetName());
-
-       std::vector<String> paths;
-       BOOST_FOREACH(const ZoneFragment& zf, zoneDirs) {
-               paths.push_back(zf.Path);
-       }
-
-       Log(LogNotice, "ApiListener")
-           << "Registered config directories for zone '" << zone->GetName() << "': " << Utility::NaturalJoin(paths);
-
-       return zoneDirs.size() > 0;
-}
-
 void ApiListener::ConfigGlobHandler(Dictionary::Ptr& config, const String& path, const String& file)
 {
        CONTEXT("Creating config update for file '" + file + "'");
@@ -141,10 +126,13 @@ void ApiListener::SyncZoneDir(const Zone::Ptr& zone) const
                }
        }
 
+       if (newConfig->GetLength() == 0)
+               return;
+
        String oldDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones/" + zone->GetName();
 
        Log(LogInformation, "ApiListener")
-           << "Copying zone configuration files for zone '" << zone->GetName() << "' to  '" << oldDir << "'.";
+           << "Copying " << newConfig->GetLength() << " zone configuration files for zone '" << zone->GetName() << "' to '" << oldDir << "'.";
 
        Utility::MkDir(oldDir, 0700);
 
@@ -156,12 +144,6 @@ void ApiListener::SyncZoneDir(const Zone::Ptr& zone) const
 void ApiListener::SyncZoneDirs(void) const
 {
        BOOST_FOREACH(const Zone::Ptr& zone, ConfigType::GetObjectsByType<Zone>()) {
-               if (!IsConfigMaster(zone)) {
-                       Log(LogWarning, "ApiListener")
-                           << "Not syncing config update for zone '" << zone->GetName() << "' because we do not have an authoritative version of the zone's config.";
-                       continue;
-               }
-
                try {
                        SyncZoneDir(zone);
                } catch (const std::exception&) {
@@ -250,7 +232,7 @@ Value ApiListener::ConfigUpdateHandler(const MessageOrigin::Ptr& origin, const D
                        continue;
                }
 
-               if (IsConfigMaster(zone)) {
+               if (ConfigCompiler::HasZoneConfigAuthority(kv.first)) {
                        Log(LogWarning, "ApiListener")
                            << "Ignoring config update for zone '" << kv.first << "' because we have an authoritative version of the zone's config.";
                        continue;
index 7ddea81bdbcdfb3d53eda0667ff38ce3cc7cbd9b..0e628e56045b267a3557e61ba9aaaedff31f4042 100644 (file)
@@ -135,7 +135,6 @@ private:
        void SyncZoneDirs(void) const;
        void SyncZoneDir(const Zone::Ptr& zone) const;
 
-       static bool IsConfigMaster(const Zone::Ptr& zone);
        static void ConfigGlobHandler(Dictionary::Ptr& config, const String& path, const String& file);
        void SendConfigUpdate(const JsonRpcConnection::Ptr& aclient);