Skip to content

Commit

Permalink
Implement support for the zones.d config directory.
Browse files Browse the repository at this point in the history
Refs #6191
  • Loading branch information
gunnarbeutner committed May 13, 2014
1 parent cb2e38a commit 0d36cc8
Show file tree
Hide file tree
Showing 11 changed files with 181 additions and 4 deletions.
1 change: 1 addition & 0 deletions doc/5-configuring-icinga-2.md
Original file line number Diff line number Diff line change
Expand Up @@ -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".
Expand Down
1 change: 1 addition & 0 deletions etc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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\")")
Expand Down
28 changes: 28 additions & 0 deletions icinga-app/icinga.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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);
Expand Down Expand Up @@ -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");
Expand Down
20 changes: 20 additions & 0 deletions lib/base/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
3 changes: 3 additions & 0 deletions lib/base/application.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ class I2_BASE_API Application : public ObjectImpl<Application> {
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);

Expand Down
8 changes: 5 additions & 3 deletions lib/remote/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand All @@ -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\")")


113 changes: 113 additions & 0 deletions lib/remote/apilistener-sync.cpp
Original file line number Diff line number Diff line change
@@ -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();
}
}
2 changes: 2 additions & 0 deletions lib/remote/apilistener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

/**
Expand Down
6 changes: 6 additions & 0 deletions lib/remote/apilistener.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ class I2_REMOTE_API ApiListener : public ObjectImpl<ApiListener>
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);

};

}
Expand Down
1 change: 1 addition & 0 deletions lib/remote/apilistener.ti
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "base/dynamicobject.h"
#include "base/application.h"

namespace icinga
{
Expand Down
2 changes: 1 addition & 1 deletion lib/remote/remote-type.conf
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
%attribute %string "crl_path",

%attribute %string "bind_host",
%attribute %string "bind_port"
%attribute %string "bind_port",
}

%type Endpoint {
Expand Down

0 comments on commit 0d36cc8

Please sign in to comment.