]> granicus.if.org Git - icinga2/commitdiff
Implement support for the zones.d config directory.
authorGunnar Beutner <gunnar.beutner@netways.de>
Tue, 13 May 2014 11:18:27 +0000 (13:18 +0200)
committerGunnar Beutner <gunnar.beutner@netways.de>
Tue, 13 May 2014 11:19:43 +0000 (13:19 +0200)
Refs #6191

doc/5-configuring-icinga-2.md
etc/CMakeLists.txt
icinga-app/icinga.cpp
lib/base/application.cpp
lib/base/application.h
lib/remote/CMakeLists.txt
lib/remote/apilistener-sync.cpp [new file with mode: 0644]
lib/remote/apilistener.cpp
lib/remote/apilistener.h
lib/remote/apilistener.ti
lib/remote/remote-type.conf

index 35218ae92a6e0ebee496f8430bf9ff1b22612fb5..8f277437a13602db6214cd5716e649909b1911c5 100644 (file)
@@ -516,6 +516,7 @@ Variable            |Description
 --------------------|-------------------
 PrefixDir           |**Read-only.** Contains the installation prefix that was specified with cmake -DCMAKE_INSTALL_PREFIX. Defaults to "/usr/local".
 SysconfDir          |**Read-only.** Contains the path of the sysconf directory. Defaults to PrefixDir + "/etc".
+ZonesDir            |**Read-only.** Contains the path of the zones.d directory. Defaults to SysconfDir + "/zones.d".
 LocalStateDir       |**Read-only.** Contains the path of the local state directory. Defaults to PrefixDir + "/var".
 PkgDataDir          |**Read-only.** Contains the path of the package data directory. Defaults to PrefixDir + "/share/icinga2".
 StatePath           |**Read-write.** Contains the path of the Icinga 2 state file. Defaults to LocalStateDir + "/lib/icinga2/icinga2.state".
index 154fab4ae1537b0fe1c5ebffa1a0b6a0bf7cbfde..133d1cd3aeb736afa0b5a6e2c46408b46df597ce 100644 (file)
@@ -56,6 +56,7 @@ install_if_not_exists(icinga2/features-available/syslog.conf ${CMAKE_INSTALL_SYS
 install_if_not_exists(icinga2/scripts/check_kernel ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/scripts)
 install_if_not_exists(icinga2/scripts/mail-host-notification.sh ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/scripts)
 install_if_not_exists(icinga2/scripts/mail-service-notification.sh ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/scripts)
+install_if_not_exists(icinga2/zones.d/README ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/zones.d)
 install_if_not_exists(logrotate.d/icinga2 ${CMAKE_INSTALL_SYSCONFDIR}/logrotate.d)
 
 install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/pki\")")
index 7084355bac9b7caf2c1d253faeb8c3d374a3c3ba..88e548129c44f27c6888f5d48daba691fbe26aa8 100644 (file)
@@ -65,6 +65,27 @@ static String LoadAppType(const String& typeSpec)
        return typeSpec.SubStr(index + 1);
 }
 
+static void IncludeDirRecursive(const String& path)
+{
+       Utility::GlobRecursive(path, "*.conf", &ConfigCompiler::CompileFile, GlobFile);
+}
+
+static void IncludeNonLocalZone(const String& zonePath)
+{
+       String etcPath = Application::GetZonesDir() + "/" + Utility::BaseName(zonePath);
+
+#ifndef _WIN32
+       struct stat statbuf;
+       if (lstat(etcPath.CStr(), &statbuf) >= 0)
+#else /* _WIN32 */
+       struct _stat statbuf;
+       if (_stat(etcPath.CStr(), &statbuf) >= 0)
+#endif /* _WIN32 */
+               return;
+
+       IncludeDirRecursive(zonePath);
+}
+
 static bool LoadConfigFiles(const String& appType)
 {
        ConfigCompilerContext::GetInstance()->Reset();
@@ -75,6 +96,12 @@ static bool LoadConfigFiles(const String& appType)
                }
        }
 
+       IncludeDirRecursive(Application::GetZonesDir());
+       Utility::Glob(Application::GetLocalStateDir() + "/lib/icinga2/api/zones/*", &IncludeNonLocalZone, GlobDirectory);
+
+       /* Load cluster config files - this should probably be in libremote but
+        * unfortunately moving it there is somewhat non-trivial. */
+
        String name, fragment;
        BOOST_FOREACH(boost::tie(name, fragment), ConfigFragmentRegistry::GetInstance()->GetItems()) {
                ConfigCompiler::CompileText(name, fragment);
@@ -265,6 +292,7 @@ int Main(void)
        }
 #endif /* _WIN32 */
 
+       Application::DeclareZonesDir(Application::GetSysconfDir() + "/icinga2/zones.d");
        Application::DeclareApplicationType("icinga/IcingaApplication");
 
        po::options_description desc("Supported options");
index 754a9f77a599b0b1a8dfe00f7ae3044e506e2673..4b8672958f9f1d634e6d08b9b810dce64eed433e 100644 (file)
@@ -790,6 +790,26 @@ String Application::GetLocalStateDir(void)
        return ScriptVariable::Get("LocalStateDir");
 }
 
+/**
+ * Sets the path of the zones dir.
+ *
+ * @param path The new path.
+ */
+void Application::DeclareZonesDir(const String& path)
+{
+       ScriptVariable::Set("ZonesDir", path, false);
+}
+
+/**
+ * Retrieves the path for the local state dir.
+ *
+ * @returns The path.
+ */
+String Application::GetZonesDir(void)
+{
+       return ScriptVariable::Get("ZonesDir");
+}
+
 /**
  * Sets the path for the local state dir.
  *
index 4b053c10f74e39d95d51743bcabd1aebfd27a949..1c15f3ceadf34cf1d403b487e5f61c3bbd33fd75 100644 (file)
@@ -79,6 +79,9 @@ public:
        static String GetSysconfDir(void);
        static void DeclareSysconfDir(const String& path);
 
+       static String GetZonesDir(void);
+       static void DeclareZonesDir(const String& path);
+
        static String GetLocalStateDir(void);
        static void DeclareLocalStateDir(const String& path);
 
index ed9fdc9fcba1fbb29324a7dfe1a7e964be226c62..f2c1ff0c3394d32a8fa3772780a81c20f5e7a427 100644 (file)
@@ -22,9 +22,9 @@ mkclass_target(zone.ti zone.th)
 mkembedconfig_target(remote-type.conf remote-type.cpp)
 
 add_library(remote SHARED
-  apiclient.cpp apifunction.cpp apilistener.cpp apilistener.th authority.cpp
-  endpoint.cpp endpoint.th jsonrpc.cpp messageorigin.cpp remote-type.cpp
-  zone.cpp zone.th
+  apiclient.cpp apifunction.cpp apilistener.cpp apilistener-sync.cpp
+  apilistener.th authority.cpp endpoint.cpp endpoint.th jsonrpc.cpp
+  messageorigin.cpp remote-type.cpp zone.cpp zone.th
 )
 
 include_directories(${Boost_INCLUDE_DIRS})
@@ -46,4 +46,6 @@ install(
 #install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api\")")
 install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/log\")")
 install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/repository\")")
+install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/zones\")")
+
 
diff --git a/lib/remote/apilistener-sync.cpp b/lib/remote/apilistener-sync.cpp
new file mode 100644 (file)
index 0000000..6bfc9e5
--- /dev/null
@@ -0,0 +1,113 @@
+/******************************************************************************
+ * Icinga 2                                                                   *
+ * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org)    *
+ *                                                                            *
+ * This program is free software; you can redistribute it and/or              *
+ * modify it under the terms of the GNU General Public License                *
+ * as published by the Free Software Foundation; either version 2             *
+ * of the License, or (at your option) any later version.                     *
+ *                                                                            *
+ * This program is distributed in the hope that it will be useful,            *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
+ * GNU General Public License for more details.                               *
+ *                                                                            *
+ * You should have received a copy of the GNU General Public License          *
+ * along with this program; if not, write to the Free Software Foundation     *
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
+ ******************************************************************************/
+
+#include "remote/apilistener.h"
+#include "base/dynamictype.h"
+#include "base/logger_fwd.h"
+#include <boost/foreach.hpp>
+#include <fstream>
+
+using namespace icinga;
+
+bool ApiListener::IsConfigMaster(const Zone::Ptr& zone) const
+{
+       String path = Application::GetZonesDir() + "/" + zone->GetName();
+
+#ifndef _WIN32
+       struct stat statbuf;
+       return (lstat(path.CStr(), &statbuf) >= 0);
+#else /* _WIN32 */
+       struct _stat statbuf;
+       return (_stat(path.CStr(), &statbuf) >= 0);
+#endif /* _WIN32 */
+}
+
+void ApiListener::ConfigGlobHandler(const Dictionary::Ptr& config, const String& path, const String& file)
+{
+       CONTEXT("Creating config update for file '" + file + "'");
+
+       std::ifstream fp(file.CStr());
+       if (!fp)
+               return;
+
+       String content((std::istreambuf_iterator<char>(fp)), std::istreambuf_iterator<char>());
+       config->Set(file.SubStr(path.GetLength()), content);
+}
+
+void ApiListener::SyncZoneDir(const Zone::Ptr& zone) const
+{
+       Log(LogInformation, "remote", "Syncing zone: " + zone->GetName());
+
+       String dirNew = Application::GetZonesDir() + "/" + zone->GetName();
+       String dirOld = Application::GetLocalStateDir() + "/lib/icinga2/api/zones/" + zone->GetName();
+
+#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));
+       }
+
+       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, configNew) {
+               if (configOld->Get(kv.first) != kv.second) {
+                       String path = dirOld + "/" + kv.first;
+                       Log(LogInformation, "remote", "Updating configuration file: " + path);
+
+                       std::ofstream fp(path.CStr(), std::ofstream::out | std::ostream::trunc);
+                       fp << kv.second;
+                       fp.close();
+               }
+       }
+
+       BOOST_FOREACH(const Dictionary::Pair& kv, configOld) {
+               if (!configNew->Contains(kv.first)) {
+                       String path = dirOld + "/" + kv.first;
+                       (void) unlink(path.CStr());
+               }
+       }
+}
+
+void ApiListener::SyncZoneDirs(void) const
+{
+       BOOST_FOREACH(const Zone::Ptr& zone, DynamicType::GetObjects<Zone>()) {
+               if (!IsConfigMaster(zone))
+                       continue;
+
+               SyncZoneDir(zone);
+       }
+
+       bool configChange = false;
+
+       // TODO: remove configuration files for zones which don't exist anymore (i.e. don't have a Zone object)
+
+       if (configChange) {
+               Log(LogInformation, "remote", "Restarting after configuration change.");
+               Application::RequestRestart();
+       }
+}
index 095d941126f98eb6f8df6f684d75955eede7d8f9..9f659d82b87c9281a349122bdc611de3ff3e5d94 100644 (file)
@@ -53,6 +53,8 @@ void ApiListener::OnConfigLoaded(void)
 
        if (!Endpoint::GetByName(GetIdentity()))
                BOOST_THROW_EXCEPTION(std::runtime_error("Endpoint object for '" + GetIdentity() + "' is missing."));
+
+       SyncZoneDirs();
 }
 
 /**
index cf133a3352ac4744249d242208d0eaa799403b50..c7cdb7070caee01f1800822c4f4b337b873f1486 100644 (file)
@@ -100,6 +100,12 @@ private:
        void CloseLogFile(void);
        static void LogGlobHandler(std::vector<int>& files, const String& file);
        void ReplayLog(const ApiClient::Ptr& client);
+
+       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);
+
 };
 
 }
index d19d4b9923fac8cf434f5c5ca1b2486f57786576..381536c3344764930fcddf2d0c739f62c68373e0 100644 (file)
@@ -1,4 +1,5 @@
 #include "base/dynamicobject.h"
+#include "base/application.h"
 
 namespace icinga
 {
index 3f8da604206a7651b647b31b568f1281918df89c..bdf58db5fda58adaece21868950ba4aee832025e 100644 (file)
@@ -30,7 +30,7 @@
        %attribute %string "crl_path",
 
        %attribute %string "bind_host",
-       %attribute %string "bind_port"
+       %attribute %string "bind_port",
 }
 
 %type Endpoint {