]> granicus.if.org Git - icinga2/commitdiff
Implement the config::Update message.
authorGunnar Beutner <gunnar.beutner@netways.de>
Tue, 13 May 2014 13:57:02 +0000 (15:57 +0200)
committerGunnar Beutner <gunnar.beutner@netways.de>
Tue, 13 May 2014 13:57:02 +0000 (15:57 +0200)
Refs #6191

icinga-app/icinga.cpp
lib/icinga/apievents.cpp
lib/remote/apilistener-sync.cpp
lib/remote/apilistener.cpp
lib/remote/apilistener.h
lib/remote/zone.cpp
lib/remote/zone.h

index eed73d86fb18a3c46f415d3db13eefdd09d7162b..6d8d2e0d990c2737dbae3bac92766e8e8ded7d8c 100644 (file)
@@ -93,7 +93,8 @@ static bool LoadConfigFiles(const String& appType)
 
        String zonesDir = Application::GetZonesDir();
 
-       Utility::Glob(Application::GetZonesDir() + "/*", &IncludeZoneDirRecursive, GlobDirectory);
+       if (!zonesDir.IsEmpty())
+               Utility::Glob(Application::GetZonesDir() + "/*", &IncludeZoneDirRecursive, GlobDirectory);
        Utility::Glob(Application::GetLocalStateDir() + "/lib/icinga2/api/zones/*", &IncludeNonLocalZone, GlobDirectory);
 
        /* Load cluster config files - this should probably be in libremote but
index 278bb5311c887960feb0a2005a28aaaa10118259..37811a52feebf27ccd00869ebb121a4fb90a9e27 100644 (file)
@@ -129,7 +129,7 @@ Value ApiEvents::CheckResultAPIHandler(const MessageOrigin& origin, const Dictio
        if (!checkable)
                return Empty;
 
-       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+       if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
                return Empty;
 
        checkable->ProcessCheckResult(cr, origin);
@@ -182,7 +182,7 @@ Value ApiEvents::NextCheckChangedAPIHandler(const MessageOrigin& origin, const D
        if (!checkable)
                return Empty;
 
-       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+       if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
                return Empty;
 
        checkable->SetNextCheck(params->Get("next_check"), origin);
@@ -219,7 +219,7 @@ Value ApiEvents::NextNotificationChangedAPIHandler(const MessageOrigin& origin,
        if (!notification)
                return Empty;
 
-       if (origin.FromZone && !origin.FromZone->CanAccessObject(notification))
+       if (!origin.FromZone || !origin.FromZone->CanAccessObject(notification))
                return Empty;
 
        notification->SetNextNotification(params->Get("next_notification"), origin);
@@ -272,7 +272,7 @@ Value ApiEvents::ForceNextCheckChangedAPIHandler(const MessageOrigin& origin, co
        if (!checkable)
                return Empty;
 
-       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+       if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
                return Empty;
 
        checkable->SetForceNextCheck(params->Get("forced"), origin);
@@ -325,7 +325,7 @@ Value ApiEvents::ForceNextNotificationChangedAPIHandler(const MessageOrigin& ori
        if (!checkable)
                return Empty;
 
-       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+       if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
                return Empty;
 
        checkable->SetForceNextNotification(params->Get("forced"), origin);
@@ -378,7 +378,7 @@ Value ApiEvents::EnableActiveChecksChangedAPIHandler(const MessageOrigin& origin
        if (!checkable)
                return Empty;
 
-       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+       if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
                return Empty;
 
        checkable->SetEnableActiveChecks(params->Get("enabled"), origin);
@@ -431,7 +431,7 @@ Value ApiEvents::EnablePassiveChecksChangedAPIHandler(const MessageOrigin& origi
        if (!checkable)
                return Empty;
 
-       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+       if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
                return Empty;
 
        checkable->SetEnablePassiveChecks(params->Get("enabled"), origin);
@@ -484,7 +484,7 @@ Value ApiEvents::EnableNotificationsChangedAPIHandler(const MessageOrigin& origi
        if (!checkable)
                return Empty;
 
-       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+       if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
                return Empty;
 
        checkable->SetEnableNotifications(params->Get("enabled"), origin);
@@ -537,7 +537,7 @@ Value ApiEvents::EnableFlappingChangedAPIHandler(const MessageOrigin& origin, co
        if (!checkable)
                return Empty;
 
-       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+       if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
                return Empty;
 
        checkable->SetEnableFlapping(params->Get("enabled"), origin);
@@ -590,7 +590,7 @@ Value ApiEvents::CommentAddedAPIHandler(const MessageOrigin& origin, const Dicti
        if (!checkable)
                return Empty;
 
-       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+       if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
                return Empty;
 
        Comment::Ptr comment = Deserialize(params->Get("comment"), true);
@@ -646,7 +646,7 @@ Value ApiEvents::CommentRemovedAPIHandler(const MessageOrigin& origin, const Dic
        if (!checkable)
                return Empty;
 
-       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+       if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
                return Empty;
 
        checkable->RemoveComment(params->Get("id"), origin);
@@ -699,7 +699,7 @@ Value ApiEvents::DowntimeAddedAPIHandler(const MessageOrigin& origin, const Dict
        if (!checkable)
                return Empty;
 
-       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+       if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
                return Empty;
 
        Downtime::Ptr downtime = Deserialize(params->Get("downtime"), true);
@@ -758,7 +758,7 @@ Value ApiEvents::DowntimeRemovedAPIHandler(const MessageOrigin& origin, const Di
        if (!checkable)
                return Empty;
 
-       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+       if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
                return Empty;
 
        checkable->RemoveDowntime(params->Get("id"), false, origin);
@@ -816,7 +816,7 @@ Value ApiEvents::AcknowledgementSetAPIHandler(const MessageOrigin& origin, const
        if (!checkable)
                return Empty;
 
-       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+       if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
                return Empty;
 
        checkable->AcknowledgeProblem(params->Get("author"), params->Get("comment"),
@@ -870,7 +870,7 @@ Value ApiEvents::AcknowledgementClearedAPIHandler(const MessageOrigin& origin, c
        if (!checkable)
                return Empty;
 
-       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+       if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
                return Empty;
 
        checkable->ClearAcknowledgement(origin);
index 556d1289f35192fda81b7edd70a8950d437f854f..6938d24ab9c3115b03c1262a2fabba09551e6604 100644 (file)
@@ -18,6 +18,7 @@
  ******************************************************************************/
 
 #include "remote/apilistener.h"
+#include "remote/apifunction.h"
 #include "base/dynamictype.h"
 #include "base/logger_fwd.h"
 #include <boost/foreach.hpp>
@@ -25,6 +26,8 @@
 
 using namespace icinga;
 
+REGISTER_APIFUNCTION(Update, config, &ApiListener::ConfigUpdateHandler);
+
 bool ApiListener::IsConfigMaster(const Zone::Ptr& zone) const
 {
        String path = Application::GetZonesDir() + "/" + zone->GetName();
@@ -43,33 +46,23 @@ void ApiListener::ConfigGlobHandler(const Dictionary::Ptr& config, const String&
        config->Set(file.SubStr(path.GetLength()), content);
 }
 
-void ApiListener::SyncZoneDir(const Zone::Ptr& zone) const
+Dictionary::Ptr ApiListener::LoadConfigDir(const String& dir)
 {
-       Log(LogInformation, "remote", "Syncing zone: " + zone->GetName());
-
-       String dirNew = Application::GetZonesDir() + "/" + zone->GetName();
-       String dirOld = Application::GetLocalStateDir() + "/lib/icinga2/api/zones/" + zone->GetName();
+       Dictionary::Ptr config = make_shared<Dictionary>();
+       Utility::GlobRecursive(dir, "*.conf", boost::bind(&ApiListener::ConfigGlobHandler, config, dir, _1), GlobFile);
+       return config;
+}
 
-#ifndef _WIN32
-       if (mkdir(dirOld.CStr(), 0700) < 0 && errno != EEXIST) {
-#else /*_ WIN32 */
-       if (mkdir(dirOld.CStr()) < 0 && errno != EEXIST) {
-#endif /* _WIN32 */
-               BOOST_THROW_EXCEPTION(posix_error()
-                       << boost::errinfo_api_function("mkdir")
-                       << boost::errinfo_errno(errno)
-                       << boost::errinfo_file_name(dirOld));
-       }
+bool ApiListener::UpdateConfigDir(const Dictionary::Ptr& oldConfig, const Dictionary::Ptr& newConfig, const String& configDir)
+{
+       bool configChange = false;
 
-       Dictionary::Ptr configNew = make_shared<Dictionary>();
-       Utility::GlobRecursive(dirNew, "*.conf", boost::bind(&ApiListener::ConfigGlobHandler, configNew, dirNew, _1), GlobFile);
 
-       Dictionary::Ptr configOld = make_shared<Dictionary>();
-       Utility::GlobRecursive(dirOld, "*.conf", boost::bind(&ApiListener::ConfigGlobHandler, configOld, dirOld, _1), GlobFile);
+       BOOST_FOREACH(const Dictionary::Pair& kv, newConfig) {
+               if (oldConfig->Get(kv.first) != kv.second) {
+                       configChange = true;
 
-       BOOST_FOREACH(const Dictionary::Pair& kv, configNew) {
-               if (configOld->Get(kv.first) != kv.second) {
-                       String path = dirOld + "/" + kv.first;
+                       String path = configDir + "/" + kv.first;
                        Log(LogInformation, "remote", "Updating configuration file: " + path);
 
                        std::ofstream fp(path.CStr(), std::ofstream::out | std::ostream::trunc);
@@ -78,12 +71,40 @@ void ApiListener::SyncZoneDir(const Zone::Ptr& zone) const
                }
        }
 
-       BOOST_FOREACH(const Dictionary::Pair& kv, configOld) {
-               if (!configNew->Contains(kv.first)) {
-                       String path = dirOld + "/" + kv.first;
+       BOOST_FOREACH(const Dictionary::Pair& kv, oldConfig) {
+               if (!newConfig->Contains(kv.first)) {
+                       configChange = true;
+
+                       String path = configDir + "/" + kv.first;
                        (void) unlink(path.CStr());
                }
        }
+
+       return configChange;
+}
+
+void ApiListener::SyncZoneDir(const Zone::Ptr& zone) const
+{
+       Log(LogInformation, "remote", "Syncing zone: " + zone->GetName());
+
+       String newDir = Application::GetZonesDir() + "/" + zone->GetName();
+       String oldDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones/" + zone->GetName();
+
+#ifndef _WIN32
+       if (mkdir(oldDir.CStr(), 0700) < 0 && errno != EEXIST) {
+#else /*_ WIN32 */
+       if (mkdir(oldDir.CStr()) < 0 && errno != EEXIST) {
+#endif /* _WIN32 */
+               BOOST_THROW_EXCEPTION(posix_error()
+                       << boost::errinfo_api_function("mkdir")
+                       << boost::errinfo_errno(errno)
+                       << boost::errinfo_file_name(oldDir));
+       }
+
+       Dictionary::Ptr newConfig = LoadConfigDir(newDir);
+       Dictionary::Ptr oldConfig = LoadConfigDir(oldDir);
+
+       UpdateConfigDir(oldConfig, newConfig, oldDir);
 }
 
 void ApiListener::SyncZoneDirs(void) const
@@ -94,13 +115,85 @@ void ApiListener::SyncZoneDirs(void) const
 
                SyncZoneDir(zone);
        }
+}
+
+void ApiListener::SendConfigUpdate(const ApiClient::Ptr& aclient)
+{
+       Endpoint::Ptr endpoint = aclient->GetEndpoint();
+       ASSERT(endpoint);
+
+       Zone::Ptr azone = endpoint->GetZone();
+       Zone::Ptr lzone = Zone::GetLocalZone();
+
+       /* don't try to send config updates to our master */
+       if (lzone->IsChildOf(azone))
+               return;
+
+       Dictionary::Ptr configUpdate = make_shared<Dictionary>();
+
+       String zonesDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones";
+
+       BOOST_FOREACH(const Zone::Ptr& zone, DynamicType::GetObjects<Zone>()) {
+               String zoneDir = zonesDir + "/" + zone->GetName();
+
+               if (!zone->IsChildOf(azone) || !Utility::PathExists(zoneDir))
+                       continue;
+
+               configUpdate->Set(zone->GetName(), LoadConfigDir(zonesDir + "/" + zone->GetName()));
+       }
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("update", configUpdate);
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "config::Update");
+       message->Set("params", params);
+
+       aclient->SendMessage(message);
+}
+
+Value ApiListener::ConfigUpdateHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!origin.FromZone || !Zone::GetLocalZone()->IsChildOf(origin.FromZone))
+               return Empty;
+
+       Dictionary::Ptr update = params->Get("update");
 
        bool configChange = false;
 
-       // TODO: remove configuration files for zones which don't exist anymore (i.e. don't have a Zone object)
+       BOOST_FOREACH(const Dictionary::Pair& kv, update) {
+               Zone::Ptr zone = Zone::GetByName(kv.first);
+
+               if (!zone) {
+                       Log(LogWarning, "remote", "Ignoring config update for unknown zone: " + kv.first);
+                       continue;
+               }
+
+               String oldDir = Application::GetLocalStateDir() + "/lib/icinga2/api/zones/" + zone->GetName();
+
+#ifndef _WIN32
+               if (mkdir(oldDir.CStr(), 0700) < 0 && errno != EEXIST) {
+#else /*_ WIN32 */
+               if (mkdir(oldDir.CStr()) < 0 && errno != EEXIST) {
+#endif /* _WIN32 */
+                       BOOST_THROW_EXCEPTION(posix_error()
+                               << boost::errinfo_api_function("mkdir")
+                               << boost::errinfo_errno(errno)
+                               << boost::errinfo_file_name(oldDir));
+               }
+
+               Dictionary::Ptr newConfig = kv.second;
+               Dictionary::Ptr oldConfig = LoadConfigDir(oldDir);
+
+               if (UpdateConfigDir(oldConfig, newConfig, oldDir))
+                       configChange = true;
+       }
 
        if (configChange) {
                Log(LogInformation, "remote", "Restarting after configuration change.");
                Application::RequestRestart();
        }
+
+       return Empty;
 }
index 9f659d82b87c9281a349122bdc611de3ff3e5d94..9135ff36cec90c4a5a330e6b3eff8523c58f7f95 100644 (file)
@@ -223,6 +223,8 @@ void ApiListener::NewClientHandler(const Socket::Ptr& client, ConnectionRole rol
                        ReplayLog(aclient);
                }
 
+               SendConfigUpdate(aclient);
+
                endpoint->AddClient(aclient);
        } else
                AddAnonymousClient(aclient);
index c7cdb7070caee01f1800822c4f4b337b873f1486..95638bb2e5797f5e7246964ce05ae6a0c4ed2136 100644 (file)
@@ -65,6 +65,8 @@ public:
        void RemoveAnonymousClient(const ApiClient::Ptr& aclient);
        std::set<ApiClient::Ptr> GetAnonymousClients(void) const;
 
+       static Value ConfigUpdateHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
 protected:
        virtual void OnConfigLoaded(void);
        virtual void Start(void);
@@ -101,11 +103,13 @@ private:
        static void LogGlobHandler(std::vector<int>& files, const String& file);
        void ReplayLog(const ApiClient::Ptr& client);
 
+       static Dictionary::Ptr LoadConfigDir(const String& dir);
+       static bool UpdateConfigDir(const Dictionary::Ptr& oldConfig, const Dictionary::Ptr& newConfig, const String& configDir);
        void SyncZoneDirs(void) const;
        void SyncZoneDir(const Zone::Ptr& zone) const;
        bool IsConfigMaster(const Zone::Ptr& zone) const;
        static void ConfigGlobHandler(const Dictionary::Ptr& config, const String& path, const String& file);
-
+       void SendConfigUpdate(const ApiClient::Ptr& aclient);
 };
 
 }
index 7ec7607512201cc81e888f27587110870c59ebb5..6337744a933cf2d0a1e78db94a087877fe947fb4 100644 (file)
@@ -39,7 +39,7 @@ std::set<Endpoint::Ptr> Zone::GetEndpoints(void) const
        return result;
 }
 
-bool Zone::CanAccessObject(const DynamicObject::Ptr& object) const
+bool Zone::CanAccessObject(const DynamicObject::Ptr& object)
 {
        Zone::Ptr object_zone;
 
@@ -51,11 +51,18 @@ bool Zone::CanAccessObject(const DynamicObject::Ptr& object) const
        if (!object_zone)
                object_zone = Zone::GetLocalZone();
 
-       while (object_zone) {
-               if (object_zone.get() == this)
+       return object_zone->IsChildOf(GetSelf());
+}
+
+bool Zone::IsChildOf(const Zone::Ptr& zone)
+{
+       Zone::Ptr azone = GetSelf();
+
+       while (azone) {
+               if (azone == zone)
                        return true;
 
-               object_zone = object_zone->GetParent();
+               azone = azone->GetParent();
        }
 
        return false;
index 1490f0fb5da981b8102071c419e88b80b39bbd63..b7222e0a6992935eed694d797980fb0a97648950 100644 (file)
@@ -39,7 +39,8 @@ public:
        Zone::Ptr GetParent(void) const;
        std::set<Endpoint::Ptr> GetEndpoints(void) const;
 
-       bool CanAccessObject(const DynamicObject::Ptr& object) const;
+       bool CanAccessObject(const DynamicObject::Ptr& object);
+       bool IsChildOf(const Zone::Ptr& zone);
 
        static Zone::Ptr GetLocalZone(void);
 };