if (!checkable)
return Empty;
- if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+ if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
return Empty;
checkable->ProcessCheckResult(cr, origin);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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"),
if (!checkable)
return Empty;
- if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+ if (!origin.FromZone || !origin.FromZone->CanAccessObject(checkable))
return Empty;
checkable->ClearAcknowledgement(origin);
******************************************************************************/
#include "remote/apilistener.h"
+#include "remote/apifunction.h"
#include "base/dynamictype.h"
#include "base/logger_fwd.h"
#include <boost/foreach.hpp>
using namespace icinga;
+REGISTER_APIFUNCTION(Update, config, &ApiListener::ConfigUpdateHandler);
+
bool ApiListener::IsConfigMaster(const Zone::Ptr& zone) const
{
String path = Application::GetZonesDir() + "/" + zone->GetName();
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);
}
}
- 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
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;
}