Implement support for the zones.d config directory.

Refs #6191
This commit is contained in:
Gunnar Beutner 2014-05-13 13:18:27 +02:00
parent cb2e38ae0d
commit 0d36cc8d5f
11 changed files with 181 additions and 4 deletions

View 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". 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". 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". 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". 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". StatePath |**Read-write.** Contains the path of the Icinga 2 state file. Defaults to LocalStateDir + "/lib/icinga2/icinga2.state".

View 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/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-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/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_if_not_exists(logrotate.d/icinga2 ${CMAKE_INSTALL_SYSCONFDIR}/logrotate.d)
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/pki\")") install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/pki\")")

View File

@ -65,6 +65,27 @@ static String LoadAppType(const String& typeSpec)
return typeSpec.SubStr(index + 1); 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) static bool LoadConfigFiles(const String& appType)
{ {
ConfigCompilerContext::GetInstance()->Reset(); 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; String name, fragment;
BOOST_FOREACH(boost::tie(name, fragment), ConfigFragmentRegistry::GetInstance()->GetItems()) { BOOST_FOREACH(boost::tie(name, fragment), ConfigFragmentRegistry::GetInstance()->GetItems()) {
ConfigCompiler::CompileText(name, fragment); ConfigCompiler::CompileText(name, fragment);
@ -265,6 +292,7 @@ int Main(void)
} }
#endif /* _WIN32 */ #endif /* _WIN32 */
Application::DeclareZonesDir(Application::GetSysconfDir() + "/icinga2/zones.d");
Application::DeclareApplicationType("icinga/IcingaApplication"); Application::DeclareApplicationType("icinga/IcingaApplication");
po::options_description desc("Supported options"); po::options_description desc("Supported options");

View File

@ -790,6 +790,26 @@ String Application::GetLocalStateDir(void)
return ScriptVariable::Get("LocalStateDir"); 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. * Sets the path for the local state dir.
* *

View File

@ -79,6 +79,9 @@ public:
static String GetSysconfDir(void); static String GetSysconfDir(void);
static void DeclareSysconfDir(const String& path); static void DeclareSysconfDir(const String& path);
static String GetZonesDir(void);
static void DeclareZonesDir(const String& path);
static String GetLocalStateDir(void); static String GetLocalStateDir(void);
static void DeclareLocalStateDir(const String& path); static void DeclareLocalStateDir(const String& path);

View File

@ -22,9 +22,9 @@ mkclass_target(zone.ti zone.th)
mkembedconfig_target(remote-type.conf remote-type.cpp) mkembedconfig_target(remote-type.conf remote-type.cpp)
add_library(remote SHARED add_library(remote SHARED
apiclient.cpp apifunction.cpp apilistener.cpp apilistener.th authority.cpp apiclient.cpp apifunction.cpp apilistener.cpp apilistener-sync.cpp
endpoint.cpp endpoint.th jsonrpc.cpp messageorigin.cpp remote-type.cpp apilistener.th authority.cpp endpoint.cpp endpoint.th jsonrpc.cpp
zone.cpp zone.th messageorigin.cpp remote-type.cpp zone.cpp zone.th
) )
include_directories(${Boost_INCLUDE_DIRS}) 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\")")
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/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/repository\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/zones\")")

View File

@ -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();
}
}

View File

@ -53,6 +53,8 @@ void ApiListener::OnConfigLoaded(void)
if (!Endpoint::GetByName(GetIdentity())) if (!Endpoint::GetByName(GetIdentity()))
BOOST_THROW_EXCEPTION(std::runtime_error("Endpoint object for '" + GetIdentity() + "' is missing.")); BOOST_THROW_EXCEPTION(std::runtime_error("Endpoint object for '" + GetIdentity() + "' is missing."));
SyncZoneDirs();
} }
/** /**

View File

@ -100,6 +100,12 @@ private:
void CloseLogFile(void); void CloseLogFile(void);
static void LogGlobHandler(std::vector<int>& files, const String& file); static void LogGlobHandler(std::vector<int>& files, const String& file);
void ReplayLog(const ApiClient::Ptr& client); 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);
}; };
} }

View File

@ -1,4 +1,5 @@
#include "base/dynamicobject.h" #include "base/dynamicobject.h"
#include "base/application.h"
namespace icinga namespace icinga
{ {

View File

@ -30,7 +30,7 @@
%attribute %string "crl_path", %attribute %string "crl_path",
%attribute %string "bind_host", %attribute %string "bind_host",
%attribute %string "bind_port" %attribute %string "bind_port",
} }
%type Endpoint { %type Endpoint {