]> granicus.if.org Git - icinga2/commitdiff
Refactor the agent and cluster components.
authorGunnar Beutner <gunnar@beutner.name>
Sat, 3 May 2014 18:02:22 +0000 (20:02 +0200)
committerGunnar Beutner <gunnar.beutner@netways.de>
Thu, 8 May 2014 07:13:04 +0000 (09:13 +0200)
Refs #6107

86 files changed:
components/CMakeLists.txt
components/agent/CMakeLists.txt [deleted file]
components/agent/agent-type.conf [deleted file]
components/agent/agentchecktask.cpp [deleted file]
components/agent/agentlistener.cpp [deleted file]
components/agent/agentlistener.ti [deleted file]
components/checker/CMakeLists.txt
components/checker/checkercomponent.cpp
components/cluster/CMakeLists.txt [deleted file]
components/cluster/cluster-type.conf [deleted file]
components/cluster/clusterlistener.cpp [deleted file]
components/cluster/clusterlistener.h [deleted file]
components/demo/CMakeLists.txt
components/demo/demo.cpp
components/demo/demo.h
etc/CMakeLists.txt
etc/icinga2/constants.conf
etc/icinga2/features-available/agent.conf [deleted file]
etc/icinga2/features-available/api.conf [new file with mode: 0644]
icinga-app/icinga.cpp
lib/base/application.cpp
lib/base/application.h
lib/base/dynamicobject.cpp
lib/base/dynamicobject.h
lib/base/dynamicobject.ti
lib/base/initialize.h
lib/base/serializer.cpp
lib/base/socket.h
lib/base/stream.h
lib/base/timer.cpp
lib/base/tlsstream.cpp
lib/base/tlsstream.h
lib/base/utility.cpp
lib/base/utility.h
lib/config/aexpression.cpp
lib/config/base-type.conf
lib/config/config_lexer.ll
lib/config/config_parser.yy
lib/config/configitem.cpp
lib/config/configitem.h
lib/config/configitembuilder.cpp
lib/config/configitembuilder.h
lib/db_ido/dbevents.cpp
lib/db_ido/dbevents.h
lib/db_ido/endpointdbobject.cpp
lib/db_ido/endpointdbobject.h
lib/icinga/CMakeLists.txt
lib/icinga/api.cpp
lib/icinga/api.h
lib/icinga/apievents.cpp [new file with mode: 0644]
lib/icinga/apievents.h [new file with mode: 0644]
lib/icinga/checkable-check.cpp
lib/icinga/checkable-comment.cpp
lib/icinga/checkable-downtime.cpp
lib/icinga/checkable-flapping.cpp
lib/icinga/checkable-notification.cpp
lib/icinga/checkable.cpp
lib/icinga/checkable.h
lib/icinga/domain.ti [deleted file]
lib/icinga/externalcommandprocessor.cpp
lib/icinga/externalcommandprocessor.h
lib/icinga/icinga-type.conf
lib/icinga/notification.cpp
lib/icinga/notification.h
lib/methods/CMakeLists.txt
lib/methods/clusterchecktask.cpp [moved from components/cluster/clusterchecktask.cpp with 73% similarity]
lib/methods/clusterchecktask.h [moved from components/cluster/clusterchecktask.h with 100% similarity]
lib/remote/CMakeLists.txt
lib/remote/apiclient.cpp [new file with mode: 0644]
lib/remote/apiclient.h [moved from components/agent/agentchecktask.h with 62% similarity]
lib/remote/apifunction.cpp [new file with mode: 0644]
lib/remote/apifunction.h [new file with mode: 0644]
lib/remote/apilistener.cpp [new file with mode: 0644]
lib/remote/apilistener.h [moved from components/agent/agentlistener.h with 57% similarity]
lib/remote/apilistener.ti [moved from components/cluster/clusterlistener.ti with 67% similarity]
lib/remote/endpoint.cpp
lib/remote/endpoint.h
lib/remote/endpoint.ti
lib/remote/messageorigin.cpp [moved from lib/icinga/domain.cpp with 89% similarity]
lib/remote/messageorigin.h [moved from lib/icinga/domain.h with 80% similarity]
lib/remote/remote-type.conf
lib/remote/zone.cpp [moved from components/cluster/clusterlink.cpp with 61% similarity]
lib/remote/zone.h [moved from components/cluster/clusterlink.h with 75% similarity]
lib/remote/zone.ti [new file with mode: 0644]
test/base-timer.cpp
test/test.cpp

index 527a938e9b11383d60627e88b72a6ba4d43121b7..737807c54714a7c74954574a71b2e104626b9867 100644 (file)
@@ -1,6 +1,4 @@
-add_subdirectory(agent)
 add_subdirectory(checker)
-add_subdirectory(cluster)
 add_subdirectory(compat)
 add_subdirectory(db_ido_mysql)
 add_subdirectory(db_ido_pgsql)
diff --git a/components/agent/CMakeLists.txt b/components/agent/CMakeLists.txt
deleted file mode 100644 (file)
index 67396c1..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-# 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.
-
-mkclass_target(agentlistener.ti agentlistener.th)
-
-mkembedconfig_target(agent-type.conf agent-type.cpp)
-
-add_library(agent SHARED
-  agentchecktask.cpp agentlistener.cpp agentlistener.th
-  agent-type.cpp
-)
-
-target_link_libraries(agent ${Boost_LIBRARIES} base config icinga remote)
-
-set_target_properties (
-  agent PROPERTIES
-  INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}/icinga2
-  FOLDER Components
-)
-
-install(TARGETS agent RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2)
-
-#install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/agent\")")
-install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/agent/inventory\")")
-
diff --git a/components/agent/agent-type.conf b/components/agent/agent-type.conf
deleted file mode 100644 (file)
index 58a88dc..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/******************************************************************************
- * 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.             *
- ******************************************************************************/
-
-%type AgentListener {
-       %attribute %string "cert_path",
-       %require "cert_path",
-
-       %attribute %string "key_path",
-       %require "key_path",
-
-       %attribute %string "ca_path",
-       %require "ca_path",
-
-       %attribute %string "crl_path",
-
-       %attribute %string "bind_host",
-       %attribute %string "bind_port",
-       
-       %attribute %string "upstream_name",
-       %attribute %string "upstream_host",
-       %attribute %string "upstream_port",
-       %attribute %number "upstream_interval"
-}
diff --git a/components/agent/agentchecktask.cpp b/components/agent/agentchecktask.cpp
deleted file mode 100644 (file)
index 674ea42..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/******************************************************************************
- * 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 "agent/agentchecktask.h"
-#include "agent/agentlistener.h"
-#include "icinga/service.h"
-#include "icinga/checkcommand.h"
-#include "icinga/macroprocessor.h"
-#include "icinga/icingaapplication.h"
-#include "base/application.h"
-#include "base/objectlock.h"
-#include "base/convert.h"
-#include "base/utility.h"
-#include "base/initialize.h"
-#include "base/scriptfunction.h"
-#include "base/dynamictype.h"
-
-using namespace icinga;
-
-boost::mutex l_Mutex;
-std::map<Checkable::Ptr, double> l_PendingChecks;
-Timer::Ptr l_AgentTimer;
-
-INITIALIZE_ONCE(&AgentCheckTask::StaticInitialize);
-REGISTER_SCRIPTFUNCTION(AgentCheck, &AgentCheckTask::ScriptFunc);
-
-void AgentCheckTask::StaticInitialize(void)
-{
-       l_AgentTimer = make_shared<Timer>();
-       l_AgentTimer->OnTimerExpired.connect(boost::bind(&AgentCheckTask::AgentTimerHandler));
-       l_AgentTimer->SetInterval(60);
-       l_AgentTimer->Start();
-}
-
-void AgentCheckTask::AgentTimerHandler(void)
-{
-       boost::mutex::scoped_lock lock(l_Mutex);
-
-       std::map<Checkable::Ptr, double> newmap;
-       std::pair<Checkable::Ptr, double> kv;
-       
-       double now = Utility::GetTime();
-
-       BOOST_FOREACH(kv, l_PendingChecks) {
-               if (kv.second < now - 60 && kv.first->IsCheckPending() && !SendResult(kv.first, false)) {
-                       CheckResult::Ptr cr = make_shared<CheckResult>();
-                       cr->SetOutput("Agent isn't responding.");
-                       cr->SetState(ServiceCritical);
-                       kv.first->ProcessCheckResult(cr);
-               } else {
-                       newmap.insert(kv);
-               }
-       }
-       
-       l_PendingChecks.swap(newmap);
-}
-
-bool AgentCheckTask::SendResult(const Checkable::Ptr& checkable, bool enqueue)
-{
-       Host::Ptr host;
-       Service::Ptr service;
-       tie(host, service) = GetHostService(checkable);
-
-       MacroProcessor::ResolverList resolvers;
-       if (service)
-               resolvers.push_back(std::make_pair("service", service));
-       resolvers.push_back(std::make_pair("host", host));
-       resolvers.push_back(std::make_pair("command", checkable->GetCheckCommand()));
-       resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
-
-       String agent_identity = MacroProcessor::ResolveMacros("$agent_identity$", resolvers, checkable->GetLastCheckResult());
-       String agent_host = MacroProcessor::ResolveMacros("$agent_host$", resolvers, checkable->GetLastCheckResult());
-       String agent_service = MacroProcessor::ResolveMacros("$agent_service$", resolvers, checkable->GetLastCheckResult());
-
-       if (agent_identity.IsEmpty() || agent_host.IsEmpty()) {
-               Log(LogWarning, "agent", "'agent_name' and 'agent_host' must be set for agent checks.");
-               return false;
-       }
-
-       String agent_peer_host = MacroProcessor::ResolveMacros("$agent_peer_host$", resolvers, checkable->GetLastCheckResult());
-       String agent_peer_port = MacroProcessor::ResolveMacros("$agent_peer_port$", resolvers, checkable->GetLastCheckResult());
-
-       double now = Utility::GetTime();
-
-       BOOST_FOREACH(const AgentListener::Ptr& al, DynamicType::GetObjects<AgentListener>()) {
-               double seen = al->GetAgentSeen(agent_identity);
-
-               if (seen < now - 300)
-                       continue;
-
-               CheckResult::Ptr cr = al->GetCheckResult(agent_identity, agent_host, agent_service);
-
-               if (cr) {
-                       checkable->ProcessCheckResult(cr);
-                       return true;
-               }
-       }
-
-       if (enqueue) {
-               {
-                       boost::mutex::scoped_lock lock(l_Mutex);
-                       l_PendingChecks[checkable] = now;
-               }
-
-               BOOST_FOREACH(const AgentListener::Ptr& al, DynamicType::GetObjects<AgentListener>()) {
-                       if (!agent_peer_host.IsEmpty() && !agent_peer_port.IsEmpty())
-                               al->AddConnection(agent_peer_host, agent_peer_port);
-               }
-       }
-
-       return false;
-}
-
-void AgentCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr)
-{
-       SendResult(checkable, true);
-}
diff --git a/components/agent/agentlistener.cpp b/components/agent/agentlistener.cpp
deleted file mode 100644 (file)
index dfc9919..0000000
+++ /dev/null
@@ -1,323 +0,0 @@
-/******************************************************************************
- * 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 "agent/agentlistener.h"
-#include "remote/jsonrpc.h"
-#include "icinga/icingaapplication.h"
-#include "base/netstring.h"
-#include "base/dynamictype.h"
-#include "base/logger_fwd.h"
-#include "base/objectlock.h"
-#include "base/networkstream.h"
-#include "base/application.h"
-#include "base/context.h"
-#include <fstream>
-
-using namespace icinga;
-
-REGISTER_TYPE(AgentListener);
-
-/**
- * Starts the component.
- */
-void AgentListener::Start(void)
-{
-       DynamicObject::Start();
-
-       m_Results = make_shared<Dictionary>();
-
-       /* set up SSL context */
-       shared_ptr<X509> cert = GetX509Certificate(GetCertPath());
-       SetIdentity(GetCertificateCN(cert));
-       Log(LogInformation, "agent", "My identity: " + GetIdentity());
-
-       m_SSLContext = MakeSSLContext(GetCertPath(), GetKeyPath(), GetCaPath());
-
-       if (!GetCrlPath().IsEmpty())
-               AddCRLToSSLContext(m_SSLContext, GetCrlPath());
-
-       /* create the primary JSON-RPC listener */
-       if (!GetBindPort().IsEmpty())
-               AddListener(GetBindPort());
-
-       m_AgentTimer = make_shared<Timer>();
-       m_AgentTimer->OnTimerExpired.connect(boost::bind(&AgentListener::AgentTimerHandler, this));
-       m_AgentTimer->SetInterval(GetUpstreamInterval());
-       m_AgentTimer->Start();
-       m_AgentTimer->Reschedule(0);
-}
-
-shared_ptr<SSL_CTX> AgentListener::GetSSLContext(void) const
-{
-       return m_SSLContext;
-}
-
-String AgentListener::GetInventoryDir(void)
-{
-       return Application::GetLocalStateDir() + "/lib/icinga2/agent/inventory/";
-}
-
-/**
- * Creates a new JSON-RPC listener on the specified port.
- *
- * @param service The port to listen on.
- */
-void AgentListener::AddListener(const String& service)
-{
-       ObjectLock olock(this);
-
-       shared_ptr<SSL_CTX> sslContext = m_SSLContext;
-
-       if (!sslContext)
-               BOOST_THROW_EXCEPTION(std::logic_error("SSL context is required for AddListener()"));
-
-       std::ostringstream s;
-       s << "Adding new listener: port " << service;
-       Log(LogInformation, "agent", s.str());
-
-       TcpSocket::Ptr server = make_shared<TcpSocket>();
-       server->Bind(service, AF_INET6);
-
-       boost::thread thread(boost::bind(&AgentListener::ListenerThreadProc, this, server));
-       thread.detach();
-
-       m_Servers.insert(server);
-}
-
-void AgentListener::ListenerThreadProc(const Socket::Ptr& server)
-{
-       Utility::SetThreadName("Cluster Listener");
-
-       server->Listen();
-
-       for (;;) {
-               Socket::Ptr client = server->Accept();
-
-               Utility::QueueAsyncCallback(boost::bind(&AgentListener::NewClientHandler, this, client, TlsRoleServer));
-       }
-}
-
-/**
- * Creates a new JSON-RPC client and connects to the specified host and port.
- *
- * @param node The remote host.
- * @param service The remote port.
- */
-void AgentListener::AddConnection(const String& node, const String& service) {
-       {
-               ObjectLock olock(this);
-
-               shared_ptr<SSL_CTX> sslContext = m_SSLContext;
-
-               if (!sslContext)
-                       BOOST_THROW_EXCEPTION(std::logic_error("SSL context is required for AddConnection()"));
-       }
-
-       TcpSocket::Ptr client = make_shared<TcpSocket>();
-
-       client->Connect(node, service);
-       Utility::QueueAsyncCallback(boost::bind(&AgentListener::NewClientHandler, this, client, TlsRoleClient));
-}
-
-/**
- * Processes a new client connection.
- *
- * @param client The new client.
- */
-void AgentListener::NewClientHandler(const Socket::Ptr& client, TlsRole role)
-{
-       CONTEXT("Handling new agent client connection");
-
-       TlsStream::Ptr tlsStream;
-
-       {
-               ObjectLock olock(this);
-               tlsStream = make_shared<TlsStream>(client, role, m_SSLContext);
-       }
-
-       tlsStream->Handshake();
-
-       shared_ptr<X509> cert = tlsStream->GetPeerCertificate();
-       String identity = GetCertificateCN(cert);
-
-       Log(LogInformation, "agent", "New client connection for identity '" + identity + "'");
-
-       if (identity != GetUpstreamName()) {
-               Dictionary::Ptr request = make_shared<Dictionary>();
-               request->Set("method", "get_crs");
-               JsonRpc::SendMessage(tlsStream, request);
-       }
-
-       try {
-               Dictionary::Ptr message = JsonRpc::ReadMessage(tlsStream);
-               MessageHandler(tlsStream, identity, message);
-       } catch (const std::exception& ex) {
-               Log(LogWarning, "agent", "Error while reading JSON-RPC message for agent '" + identity + "': " + DiagnosticInformation(ex));
-       }
-
-       tlsStream->Close();
-}
-
-void AgentListener::MessageHandler(const TlsStream::Ptr& sender, const String& identity, const Dictionary::Ptr& message)
-{
-       CONTEXT("Processing agent message of type '" + message->Get("method") + "'");
-
-       String method = message->Get("method");
-
-       if (identity == GetUpstreamName()) {
-               if (method == "get_crs") {
-                       Dictionary::Ptr hosts = make_shared<Dictionary>();
-
-                       BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjects<Host>()) {
-                               Dictionary::Ptr hostInfo = make_shared<Dictionary>();
-
-                               hostInfo->Set("cr", Serialize(host->GetLastCheckResult()));
-
-                               Dictionary::Ptr services = make_shared<Dictionary>();
-
-                               BOOST_FOREACH(const Service::Ptr& service, host->GetServices()) {
-                                       Dictionary::Ptr serviceInfo = make_shared<Dictionary>();
-                                       serviceInfo->Set("cr", Serialize(service->GetLastCheckResult()));
-                                       services->Set(service->GetShortName(), serviceInfo);
-                               }
-
-                               hostInfo->Set("services", services);
-
-                               hosts->Set(host->GetName(), hostInfo);
-                       }
-
-                       Dictionary::Ptr params = make_shared<Dictionary>();
-                       params->Set("hosts", hosts);
-
-                       Dictionary::Ptr request = make_shared<Dictionary>();
-                       request->Set("method", "push_crs");
-                       request->Set("params", params);
-
-                       JsonRpc::SendMessage(sender, request);
-               }
-       }
-
-       if (method == "push_crs") {
-               Value paramsv = message->Get("params");
-
-               if (paramsv.IsEmpty() || !paramsv.IsObjectType<Dictionary>())
-                       return;
-
-               Dictionary::Ptr params = paramsv;
-
-               params->Set("seen", Utility::GetTime());
-
-               Dictionary::Ptr inventoryDescr = make_shared<Dictionary>();
-               inventoryDescr->Set("identity", identity);
-               inventoryDescr->Set("params", params);
-
-               String inventoryFile = GetInventoryDir() + SHA256(identity);
-               String inventoryTempFile = inventoryFile + ".tmp";
-
-               std::ofstream fp(inventoryTempFile.CStr(), std::ofstream::out | std::ostream::trunc);
-               fp << JsonSerialize(inventoryDescr);
-               fp.close();
-
-#ifdef _WIN32
-               _unlink(inventoryFile.CStr());
-#endif /* _WIN32 */
-
-               if (rename(inventoryTempFile.CStr(), inventoryFile.CStr()) < 0) {
-                       BOOST_THROW_EXCEPTION(posix_error()
-                           << boost::errinfo_api_function("rename")
-                           << boost::errinfo_errno(errno)
-                           << boost::errinfo_file_name(inventoryTempFile));
-               }
-
-               m_Results->Set(identity, params);
-       }
-}
-
-double  AgentListener::GetAgentSeen(const String& agentIdentity)
-{
-       Dictionary::Ptr agentparams = m_Results->Get(agentIdentity);
-
-       if (!agentparams)
-               return 0;
-
-       return agentparams->Get("seen");
-}
-
-CheckResult::Ptr AgentListener::GetCheckResult(const String& agentIdentity, const String& hostName, const String& serviceName)
-{
-       Dictionary::Ptr agentparams = m_Results->Get(agentIdentity);
-
-       if (!agentparams)
-               return CheckResult::Ptr();
-
-       Value hostsv = agentparams->Get("hosts");
-
-       if (hostsv.IsEmpty() || !hostsv.IsObjectType<Dictionary>())
-               return CheckResult::Ptr();
-
-       Dictionary::Ptr hosts = hostsv;
-
-       Value hostv = hosts->Get(hostName);
-
-       if (hostv.IsEmpty() || !hostv.IsObjectType<Dictionary>())
-               return CheckResult::Ptr();
-
-       Dictionary::Ptr host = hostv;
-
-       if (serviceName.IsEmpty()) {
-               Value hostcrv = Deserialize(host->Get("cr"));
-
-               if (hostcrv.IsEmpty() || !hostcrv.IsObjectType<CheckResult>())
-                       return CheckResult::Ptr();
-
-               return hostcrv;
-       } else {
-               Value servicesv = host->Get("services");
-
-               if (servicesv.IsEmpty() || !servicesv.IsObjectType<Dictionary>())
-                       return CheckResult::Ptr();
-
-               Dictionary::Ptr services = servicesv;
-
-               Value servicev = services->Get(serviceName);
-
-               if (servicev.IsEmpty() || !servicev.IsObjectType<Dictionary>())
-                       return CheckResult::Ptr();
-
-               Dictionary::Ptr service = servicev;
-
-               Value servicecrv = Deserialize(service->Get("cr"));
-
-               if (servicecrv.IsEmpty() || !servicecrv.IsObjectType<CheckResult>())
-                       return CheckResult::Ptr();
-
-               return servicecrv;
-       }
-}
-
-void AgentListener::AgentTimerHandler(void)
-{
-       String host = GetUpstreamHost();
-       String port = GetUpstreamPort();
-
-       if (host.IsEmpty() || port.IsEmpty())
-               return;
-
-       AddConnection(host, port);
-}
diff --git a/components/agent/agentlistener.ti b/components/agent/agentlistener.ti
deleted file mode 100644 (file)
index e42c942..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#include "base/dynamicobject.h"
-#include "base/application.h"
-
-namespace icinga
-{
-
-class AgentListener : DynamicObject
-{
-       [config] String cert_path;
-       [config] String key_path;
-       [config] String ca_path;
-       [config] String crl_path;
-       [config] String bind_host;
-       [config] String bind_port;
-       [config] String upstream_host;
-       [config] String upstream_port;
-       [config] String upstream_name;
-       [config] int upstream_interval {
-               default {{{ return 60; }}}
-       };
-       String identity;
-};
-
-}
index 7fe18cf9b4f4fb1d946810b7cd47ea141b89f7b1..33761dc98b2227ffa5cfe14d3e9ef6bb325bf039 100644 (file)
@@ -21,7 +21,7 @@ mkembedconfig_target(checker-type.conf checker-type.cpp)
 
 add_library(checker SHARED checkercomponent.cpp checkercomponent.th checker-type.cpp)
 
-target_link_libraries(checker ${Boost_LIBRARIES} base config icinga)
+target_link_libraries(checker ${Boost_LIBRARIES} base config icinga remote)
 
 set_target_properties (
   checker PROPERTIES
index 808bf6d62a82ded40c1ecfc9ed5a35e867527dd0..2f53142dc6e3d1fbecf60c049827ba49cff3d033 100644 (file)
@@ -20,6 +20,7 @@
 #include "checker/checkercomponent.h"
 #include "icinga/icingaapplication.h"
 #include "icinga/cib.h"
+#include "remote/apilistener.h"
 #include "base/dynamictype.h"
 #include "base/objectlock.h"
 #include "base/utility.h"
@@ -63,7 +64,6 @@ void CheckerComponent::OnConfigLoaded(void)
 {
        DynamicObject::OnStarted.connect(bind(&CheckerComponent::ObjectHandler, this, _1));
        DynamicObject::OnStopped.connect(bind(&CheckerComponent::ObjectHandler, this, _1));
-       DynamicObject::OnAuthorityChanged.connect(bind(&CheckerComponent::ObjectHandler, this, _1));
 
        Checkable::OnNextCheckChanged.connect(bind(&CheckerComponent::NextCheckChangedHandler, this, _1));
 }
@@ -117,12 +117,6 @@ void CheckerComponent::CheckThreadProc(void)
                CheckTimeView::iterator it = idx.begin();
                Checkable::Ptr checkable = *it;
 
-               if (!checkable->HasAuthority("checker")) {
-                       m_IdleCheckables.erase(checkable);
-
-                       continue;
-               }
-
                double wait = checkable->GetNextCheck() - Utility::GetTime();
 
                if (wait > 0) {
@@ -227,7 +221,7 @@ void CheckerComponent::ExecuteCheckHelper(const Checkable::Ptr& checkable)
                if (it != m_PendingCheckables.end()) {
                        m_PendingCheckables.erase(it);
 
-                       if (checkable->IsActive() && checkable->HasAuthority("checker"))
+                       if (checkable->IsActive())
                                m_IdleCheckables.insert(checkable);
 
                        m_CV.notify_all();
@@ -257,10 +251,13 @@ void CheckerComponent::ObjectHandler(const DynamicObject::Ptr& object)
 
        Checkable::Ptr checkable = static_pointer_cast<Checkable>(object);
 
+       Zone::Ptr zone = Zone::GetByName(checkable->GetZone());
+       bool same_zone = (!zone || Zone::GetLocalZone() == zone);
+
        {
                boost::mutex::scoped_lock lock(m_Mutex);
 
-               if (object->IsActive() && object->HasAuthority("checker")) {
+               if (object->IsActive() && same_zone) {
                        if (m_PendingCheckables.find(checkable) != m_PendingCheckables.end())
                                return;
 
diff --git a/components/cluster/CMakeLists.txt b/components/cluster/CMakeLists.txt
deleted file mode 100644 (file)
index e592754..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-# 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.
-
-mkclass_target(clusterlistener.ti clusterlistener.th)
-
-mkembedconfig_target(cluster-type.conf cluster-type.cpp)
-
-add_library(cluster SHARED
-  clusterchecktask.cpp clusterlink.cpp clusterlistener.cpp clusterlistener.th
-  cluster-type.cpp
-)
-
-target_link_libraries(cluster ${Boost_LIBRARIES} base config icinga remote)
-
-set_target_properties (
-  cluster PROPERTIES
-  INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}/icinga2
-  FOLDER Components
-)
-
-install(TARGETS cluster RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2)
-
-#install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/cluster\")")
-install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/cluster/config\")")
-install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/cluster/log\")")
-
diff --git a/components/cluster/cluster-type.conf b/components/cluster/cluster-type.conf
deleted file mode 100644 (file)
index eea76f4..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/******************************************************************************
- * 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.             *
- ******************************************************************************/
-
-%type ClusterListener {
-       %attribute %string "cert_path",
-       %require "cert_path",
-
-       %attribute %string "key_path",
-       %require "key_path",
-
-       %attribute %string "ca_path",
-       %require "ca_path",
-
-       %attribute %string "crl_path",
-
-       %attribute %string "bind_host",
-       %attribute %string "bind_port",
-
-       %attribute %array "peers" {
-               %attribute %name(Endpoint) "*"
-       }
-}
diff --git a/components/cluster/clusterlistener.cpp b/components/cluster/clusterlistener.cpp
deleted file mode 100644 (file)
index 16501ff..0000000
+++ /dev/null
@@ -1,1872 +0,0 @@
-/******************************************************************************
- * 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 "cluster/clusterlistener.h"
-#include "remote/endpoint.h"
-#include "icinga/cib.h"
-#include "icinga/domain.h"
-#include "icinga/icingaapplication.h"
-#include "base/netstring.h"
-#include "base/dynamictype.h"
-#include "base/logger_fwd.h"
-#include "base/objectlock.h"
-#include "base/networkstream.h"
-#include "base/zlibstream.h"
-#include "base/application.h"
-#include "base/convert.h"
-#include "base/context.h"
-#include "base/statsfunction.h"
-#include <fstream>
-
-using namespace icinga;
-
-REGISTER_TYPE(ClusterListener);
-
-REGISTER_STATSFUNCTION(ClusterListenerStats, &ClusterListener::StatsFunc);
-
-Value ClusterListener::StatsFunc(Dictionary::Ptr& status, Dictionary::Ptr& perfdata)
-{
-       Dictionary::Ptr nodes = make_shared<Dictionary>();
-       std::pair<Dictionary::Ptr, Dictionary::Ptr> stats;
-
-       BOOST_FOREACH(const ClusterListener::Ptr& cluster_listener, DynamicType::GetObjects<ClusterListener>()) {
-               stats = cluster_listener->GetClusterStatus();
-               nodes->Set(cluster_listener->GetName(), stats.first);
-
-               String perfdata_prefix = "clusterlistener_" + cluster_listener->GetName() + "_";
-               BOOST_FOREACH(Dictionary::Pair const& kv, stats.second) {
-                       perfdata->Set(perfdata_prefix + kv.first, kv.second);
-               }
-       }
-
-       status->Set("clusterlistener", nodes);
-
-       return 0;
-}
-
-/**
- * Starts the component.
- */
-void ClusterListener::Start(void)
-{
-       DynamicObject::Start();
-
-       {
-               ObjectLock olock(this);
-               RotateLogFile();
-               OpenLogFile();
-       }
-
-       /* set up SSL context */
-       shared_ptr<X509> cert = GetX509Certificate(GetCertPath());
-       SetIdentity(GetCertificateCN(cert));
-       Log(LogInformation, "cluster", "My identity: " + GetIdentity());
-
-       Endpoint::Ptr self = Endpoint::GetByName(GetIdentity());
-
-       if (!self)
-               BOOST_THROW_EXCEPTION(std::invalid_argument("No configuration available for the local endpoint."));
-
-       m_SSLContext = MakeSSLContext(GetCertPath(), GetKeyPath(), GetCaPath());
-
-       if (!GetCrlPath().IsEmpty())
-               AddCRLToSSLContext(m_SSLContext, GetCrlPath());
-
-       /* create the primary JSON-RPC listener */
-       if (!GetBindPort().IsEmpty())
-               AddListener(GetBindPort());
-
-       m_ClusterTimer = make_shared<Timer>();
-       m_ClusterTimer->OnTimerExpired.connect(boost::bind(&ClusterListener::ClusterTimerHandler, this));
-       m_ClusterTimer->SetInterval(5);
-       m_ClusterTimer->Start();
-
-       m_MessageQueue.SetExceptionCallback(&ClusterListener::MessageExceptionHandler);
-
-       Checkable::OnNewCheckResult.connect(boost::bind(&ClusterListener::CheckResultHandler, this, _1, _2, _3));
-       Checkable::OnNextCheckChanged.connect(boost::bind(&ClusterListener::NextCheckChangedHandler, this, _1, _2, _3));
-       Notification::OnNextNotificationChanged.connect(boost::bind(&ClusterListener::NextNotificationChangedHandler, this, _1, _2, _3));
-       Checkable::OnForceNextCheckChanged.connect(boost::bind(&ClusterListener::ForceNextCheckChangedHandler, this, _1, _2, _3));
-       Checkable::OnForceNextNotificationChanged.connect(boost::bind(&ClusterListener::ForceNextNotificationChangedHandler, this, _1, _2, _3));
-       Checkable::OnEnableActiveChecksChanged.connect(boost::bind(&ClusterListener::EnableActiveChecksChangedHandler, this, _1, _2, _3));
-       Checkable::OnEnablePassiveChecksChanged.connect(boost::bind(&ClusterListener::EnablePassiveChecksChangedHandler, this, _1, _2, _3));
-       Checkable::OnEnableNotificationsChanged.connect(boost::bind(&ClusterListener::EnableNotificationsChangedHandler, this, _1, _2, _3));
-       Checkable::OnEnableFlappingChanged.connect(boost::bind(&ClusterListener::EnableFlappingChangedHandler, this, _1, _2, _3));
-       Checkable::OnCommentAdded.connect(boost::bind(&ClusterListener::CommentAddedHandler, this, _1, _2, _3));
-       Checkable::OnCommentRemoved.connect(boost::bind(&ClusterListener::CommentRemovedHandler, this, _1, _2, _3));
-       Checkable::OnDowntimeAdded.connect(boost::bind(&ClusterListener::DowntimeAddedHandler, this, _1, _2, _3));
-       Checkable::OnDowntimeRemoved.connect(boost::bind(&ClusterListener::DowntimeRemovedHandler, this, _1, _2, _3));
-       Checkable::OnAcknowledgementSet.connect(boost::bind(&ClusterListener::AcknowledgementSetHandler, this, _1, _2, _3, _4, _5, _6));
-       Checkable::OnAcknowledgementCleared.connect(boost::bind(&ClusterListener::AcknowledgementClearedHandler, this, _1, _2));
-
-       Endpoint::OnMessageReceived.connect(boost::bind(&ClusterListener::AsyncMessageHandler, this, _1, _2));
-
-       BOOST_FOREACH(const DynamicType::Ptr& type, DynamicType::GetTypes()) {
-               BOOST_FOREACH(const DynamicObject::Ptr& object, type->GetObjects()) {
-                       BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
-                               int privs = 0;
-
-                               Array::Ptr domains = object->GetDomains();
-
-                               if (domains) {
-                                       ObjectLock olock(domains);
-                                       BOOST_FOREACH(const String& domain, domains) {
-                                               Domain::Ptr domainObj = Domain::GetByName(domain);
-
-                                               if (!domainObj)
-                                                       BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid domain: " + domain));
-
-                                               privs |= domainObj->GetPrivileges(endpoint->GetName());
-                                       }
-                               } else {
-                                       privs = INT_MAX;
-                               }
-
-                               Log(LogDebug, "cluster", "Privileges for object '" + object->GetName() + "' of type '" + object->GetType()->GetName() + "' for instance '" + endpoint->GetName() + "' are '" + Convert::ToString(privs) + "'");
-                               object->SetPrivileges(endpoint->GetName(), privs);
-                       }
-               }
-       }
-}
-
-/**
- * Stops the component.
- */
-void ClusterListener::Stop(void)
-{
-       ObjectLock olock(this);
-       CloseLogFile();
-       RotateLogFile();
-}
-
-shared_ptr<SSL_CTX> ClusterListener::GetSSLContext(void) const
-{
-       return m_SSLContext;
-}
-
-/**
- * Creates a new JSON-RPC listener on the specified port.
- *
- * @param service The port to listen on.
- */
-void ClusterListener::AddListener(const String& service)
-{
-       ObjectLock olock(this);
-
-       shared_ptr<SSL_CTX> sslContext = m_SSLContext;
-
-       if (!sslContext)
-               BOOST_THROW_EXCEPTION(std::logic_error("SSL context is required for AddListener()"));
-
-       std::ostringstream s;
-       s << "Adding new listener: port " << service;
-       Log(LogInformation, "cluster", s.str());
-
-       TcpSocket::Ptr server = make_shared<TcpSocket>();
-       server->Bind(service, AF_INET6);
-
-       boost::thread thread(boost::bind(&ClusterListener::ListenerThreadProc, this, server));
-       thread.detach();
-
-       m_Servers.insert(server);
-}
-
-void ClusterListener::ListenerThreadProc(const Socket::Ptr& server)
-{
-       Utility::SetThreadName("Cluster Listener");
-
-       server->Listen();
-
-       for (;;) {
-               Socket::Ptr client = server->Accept();
-
-               Utility::QueueAsyncCallback(boost::bind(&ClusterListener::NewClientHandler, this, client, TlsRoleServer));
-       }
-}
-
-/**
- * Creates a new JSON-RPC client and connects to the specified host and port.
- *
- * @param node The remote host.
- * @param service The remote port.
- */
-void ClusterListener::AddConnection(const String& node, const String& service) {
-       {
-               ObjectLock olock(this);
-
-               shared_ptr<SSL_CTX> sslContext = m_SSLContext;
-
-               if (!sslContext)
-                       BOOST_THROW_EXCEPTION(std::logic_error("SSL context is required for AddConnection()"));
-       }
-
-       TcpSocket::Ptr client = make_shared<TcpSocket>();
-
-       client->Connect(node, service);
-       Utility::QueueAsyncCallback(boost::bind(&ClusterListener::NewClientHandler, this, client, TlsRoleClient));
-}
-
-void ClusterListener::AsyncRelayMessage(const Endpoint::Ptr& source, const Endpoint::Ptr& destination, const Dictionary::Ptr& message, bool persistent)
-{
-       m_RelayQueue.Enqueue(boost::bind(&ClusterListener::RelayMessage, this, source, destination, message, persistent));
-}
-
-void ClusterListener::PersistMessage(const Endpoint::Ptr& source, const Dictionary::Ptr& message)
-{
-       double ts = message->Get("ts");
-
-       ASSERT(ts != 0);
-
-       Dictionary::Ptr pmessage = make_shared<Dictionary>();
-       pmessage->Set("timestamp", ts);
-
-       if (source)
-               pmessage->Set("source", source->GetName());
-
-       pmessage->Set("message", JsonSerialize(message));
-       pmessage->Set("security", message->Get("security"));
-
-       ObjectLock olock(this);
-       if (m_LogFile) {
-               NetString::WriteStringToStream(m_LogFile, JsonSerialize(pmessage));
-               m_LogMessageCount++;
-               SetLogMessageTimestamp(ts);
-
-               if (m_LogMessageCount > 50000) {
-                       CloseLogFile();
-                       RotateLogFile();
-                       OpenLogFile();
-               }
-       }
-}
-
-void ClusterListener::RelayMessage(const Endpoint::Ptr& source, const Endpoint::Ptr& destination, const Dictionary::Ptr& message, bool persistent)
-{
-       double ts = Utility::GetTime();
-       message->Set("ts", ts);
-
-       if (persistent)
-               m_LogQueue.Enqueue(boost::bind(&ClusterListener::PersistMessage, this, source, message));
-
-       Dictionary::Ptr security = message->Get("security");
-       DynamicObject::Ptr secobj;
-       int privs = 0;
-
-       if (security) {
-               String type = security->Get("type");
-               DynamicType::Ptr dtype = DynamicType::GetByName(type);
-
-               if (!dtype) {
-                       Log(LogWarning, "cluster", "Invalid type in security attribute: " + type);
-                       return;
-               }
-
-               String name = security->Get("name");
-               secobj = dtype->GetObject(name);
-
-               if (!secobj) {
-                       Log(LogWarning, "cluster", "Invalid object name in security attribute: " + name + " (of type '" + type + "')");
-                       return;
-               }
-
-               privs = security->Get("privs");
-       }
-
-       double now = Utility::GetTime();
-
-       BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
-               if (!endpoint->IsConnected())
-                       continue;
-
-               if (destination && endpoint != destination)
-                       continue;
-
-               if (!destination && endpoint->GetBlockedUntil() > now)
-                       continue;
-
-               if (endpoint == source)
-                       continue;
-
-               if (endpoint->GetName() == GetIdentity())
-                       continue;
-
-               if (secobj && !secobj->HasPrivileges(endpoint->GetName(), privs)) {
-                       Log(LogDebug, "cluster", "Not sending message to endpoint '" + endpoint->GetName() + "': Insufficient privileges.");
-                       continue;
-               }
-
-               {
-                       ObjectLock olock(endpoint);
-
-                       if (!endpoint->GetSyncing())
-                               endpoint->SendMessage(message);
-               }
-       }
-}
-
-String ClusterListener::GetClusterDir(void) const
-{
-       return Application::GetLocalStateDir() + "/lib/icinga2/cluster/";
-}
-
-void ClusterListener::OpenLogFile(void)
-{
-       ASSERT(OwnsLock());
-
-       String path = GetClusterDir() + "log/current";
-
-       std::fstream *fp = new std::fstream(path.CStr(), std::fstream::out | std::ofstream::app);
-
-       if (!fp->good()) {
-               Log(LogWarning, "cluster", "Could not open spool file: " + path);
-               return;
-       }
-
-       StdioStream::Ptr logStream = make_shared<StdioStream>(fp, true);
-#ifdef HAVE_BIOZLIB
-       m_LogFile = make_shared<ZlibStream>(logStream);
-#else /* HAVE_BIOZLIB */
-       m_LogFile = logStream;
-#endif /* HAVE_BIOZLIB */
-       m_LogMessageCount = 0;
-       SetLogMessageTimestamp(0);
-}
-
-void ClusterListener::CloseLogFile(void)
-{
-       ASSERT(OwnsLock());
-
-       if (!m_LogFile)
-               return;
-
-       m_LogFile->Close();
-       m_LogFile.reset();
-
-}
-
-void ClusterListener::RotateLogFile(void)
-{
-       ASSERT(OwnsLock());
-
-       double ts = GetLogMessageTimestamp();
-
-       if (ts == 0)
-               ts = Utility::GetTime();
-
-       String oldpath = GetClusterDir() + "log/current";
-       String newpath = GetClusterDir() + "log/" + Convert::ToString(static_cast<int>(ts) + 1);
-       (void) rename(oldpath.CStr(), newpath.CStr());
-}
-
-void ClusterListener::LogGlobHandler(std::vector<int>& files, const String& file)
-{
-       String name = Utility::BaseName(file);
-
-       int ts;
-
-       try {
-               ts = Convert::ToLong(name);
-       } catch (const std::exception&) {
-               return;
-       }
-
-       files.push_back(ts);
-}
-
-void ClusterListener::ReplayLog(const Endpoint::Ptr& endpoint, const Stream::Ptr& stream)
-{
-       CONTEXT("Replaying log for Endpoint '" + endpoint->GetName() + "'");
-
-       int count = -1;
-       double peer_ts = endpoint->GetLocalLogPosition();
-       bool last_sync = false;
-
-       ASSERT(!OwnsLock());
-
-       for (;;) {
-               ObjectLock olock(this);
-
-               CloseLogFile();
-               RotateLogFile();
-
-               if (count == -1 || count > 50000) {
-                       OpenLogFile();
-                       olock.Unlock();
-               } else {
-                       last_sync = true;
-               }
-
-               count = 0;
-
-               std::vector<int> files;
-               Utility::Glob(GetClusterDir() + "log/*", boost::bind(&ClusterListener::LogGlobHandler, boost::ref(files), _1), GlobFile);
-               std::sort(files.begin(), files.end());
-
-               BOOST_FOREACH(int ts, files) {
-                       String path = GetClusterDir() + "log/" + Convert::ToString(ts);
-
-                       if (ts < peer_ts)
-                               continue;
-
-                       Log(LogInformation, "cluster", "Replaying log: " + path);
-
-                       std::fstream *fp = new std::fstream(path.CStr(), std::fstream::in);
-                       StdioStream::Ptr logStream = make_shared<StdioStream>(fp, true);
-#ifdef HAVE_BIOZLIB
-                       ZlibStream::Ptr lstream = make_shared<ZlibStream>(logStream);
-#else /* HAVE_BIOZLIB */
-                       Stream::Ptr lstream = logStream;
-#endif /* HAVE_BIOZLIB */
-
-                       String message;
-                       while (true) {
-                               Dictionary::Ptr pmessage;
-
-                               try {
-                                       if (!NetString::ReadStringFromStream(lstream, &message))
-                                               break;
-
-                                       pmessage = JsonDeserialize(message);
-                               } catch (std::exception&) {
-                                       Log(LogWarning, "cluster", "Unexpected end-of-file for cluster log: " + path);
-
-                                       /* Log files may be incomplete or corrupted. This is perfectly OK. */
-                                       break;
-                               }
-
-                               if (pmessage->Get("timestamp") < peer_ts)
-                                       continue;
-
-                               if (pmessage->Get("source") == endpoint->GetName())
-                                       continue;
-
-                               Dictionary::Ptr security = pmessage->Get("security");
-                               DynamicObject::Ptr secobj;
-                               int privs;
-
-                               if (security) {
-                                       String type = security->Get("type");
-                                       DynamicType::Ptr dtype = DynamicType::GetByName(type);
-
-                                       if (!dtype) {
-                                               Log(LogDebug, "cluster", "Invalid type in security attribute: " + type);
-                                               continue;
-                                       }
-
-                                       String name = security->Get("name");
-                                       secobj = dtype->GetObject(name);
-
-                                       if (!secobj) {
-                                               Log(LogDebug, "cluster", "Invalid object name in security attribute: " + name + " (of type '" + type + "')");
-                                               continue;
-                                       }
-
-                                       privs = security->Get("privs");
-                               }
-
-                               if (secobj && !secobj->HasPrivileges(endpoint->GetName(), privs)) {
-                                       Log(LogDebug, "cluster", "Not replaying message to endpoint '" + endpoint->GetName() + "': Insufficient privileges.");
-                                       continue;
-                               }
-
-                               NetString::WriteStringToStream(stream, pmessage->Get("message"));
-                               count++;
-
-                               peer_ts = pmessage->Get("timestamp");
-                       }
-
-                       lstream->Close();
-               }
-
-               Log(LogInformation, "cluster", "Replayed " + Convert::ToString(count) + " messages.");
-
-               if (last_sync) {
-                       {
-                               ObjectLock olock2(endpoint);
-                               endpoint->SetSyncing(false);
-                       }
-
-                       OpenLogFile();
-
-                       break;
-               }
-       }
-}
-
-void ClusterListener::ConfigGlobHandler(const Dictionary::Ptr& config, const String& file, bool basename)
-{
-       CONTEXT("Creating config update for file '" + file + "'");
-
-       Dictionary::Ptr elem = make_shared<Dictionary>();
-
-       std::ifstream fp(file.CStr());
-       if (!fp)
-               return;
-
-       String content((std::istreambuf_iterator<char>(fp)), std::istreambuf_iterator<char>());
-       elem->Set("content", content);
-
-       config->Set(basename ? Utility::BaseName(file) : file, elem);
-}
-
-/**
- * Processes a new client connection.
- *
- * @param client The new client.
- */
-void ClusterListener::NewClientHandler(const Socket::Ptr& client, TlsRole role)
-{
-       CONTEXT("Handling new cluster client connection");
-
-       TlsStream::Ptr tlsStream = make_shared<TlsStream>(client, role, m_SSLContext);
-       tlsStream->Handshake();
-
-       shared_ptr<X509> cert = tlsStream->GetPeerCertificate();
-       String identity = GetCertificateCN(cert);
-
-       Endpoint::Ptr endpoint = Endpoint::GetByName(identity);
-
-       if (!endpoint) {
-               Log(LogInformation, "cluster", "Closing endpoint '" + identity + "': No configuration available.");
-               tlsStream->Close();
-               return;
-       }
-
-       if (endpoint->GetClient()) {
-               tlsStream->Close();
-               return;
-       }
-
-       Log(LogInformation, "cluster", "New client connection for identity '" + identity + "'");
-
-       {
-               ObjectLock olock(endpoint);
-
-               endpoint->SetSyncing(true);
-               endpoint->SetSeen(Utility::GetTime());
-               endpoint->SetClient(tlsStream);
-       }
-
-       Dictionary::Ptr config = make_shared<Dictionary>();
-       Array::Ptr configFiles = endpoint->GetConfigFiles();
-
-       if (configFiles) {
-               ObjectLock olock(configFiles);
-               BOOST_FOREACH(const String& pattern, configFiles) {
-                       Utility::Glob(pattern, boost::bind(&ClusterListener::ConfigGlobHandler, boost::cref(config), _1, false), GlobFile);
-               }
-       }
-
-       Array::Ptr configFilesRecursive = endpoint->GetConfigFilesRecursive();
-
-       if (configFilesRecursive) {
-               ObjectLock olock(configFilesRecursive);
-               BOOST_FOREACH(const Value& configFile, configFilesRecursive) {
-                       if (configFile.IsObjectType<Dictionary>()) {
-                               Dictionary::Ptr configFileDict = configFile;
-                               String path = configFileDict->Get("path");
-                               String pattern = configFileDict->Get("pattern");
-                               Utility::GlobRecursive(path, pattern, boost::bind(&ClusterListener::ConfigGlobHandler, boost::cref(config), _1, false), GlobFile);
-                       } else {
-                               String configFilePath = configFile;
-                               Utility::GlobRecursive(configFilePath, "*.conf", boost::bind(&ClusterListener::ConfigGlobHandler, boost::cref(config), _1, false), GlobFile);
-                       }
-               }
-       }
-
-       Log(LogInformation, "cluster", "Sending " + Convert::ToString(static_cast<long>(config->GetLength())) + " config files to endpoint '" + endpoint->GetName() + "'.");
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("identity", GetIdentity());
-       params->Set("config_files", config);
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::Config");
-       message->Set("params", params);
-
-       NetString::WriteStringToStream(tlsStream, JsonSerialize(message));
-
-       ReplayLog(endpoint, tlsStream);
-}
-
-void ClusterListener::UpdateLinks(void)
-{
-       ObjectLock olock(this);
-       /* build a set of potential routes */
-       std::set<ClusterLink> links;
-       std::pair<String, EndpointPeerInfo> kv;
-       BOOST_FOREACH(kv, m_VisibleEndpoints) {
-               String endpoint = kv.first;
-               const EndpointPeerInfo& epi = kv.second;
-
-               if (GetIdentity() == endpoint)
-                       continue;
-
-               if (epi.Seen > Utility::GetTime() - 30)
-                       links.insert(ClusterLink(GetIdentity(), endpoint));
-
-               if (!epi.Peers)
-                       continue;
-
-               ObjectLock olock(epi.Peers);
-               BOOST_FOREACH(const String& peer, epi.Peers)
-                       links.insert(ClusterLink(endpoint, peer));
-       }
-       olock.Unlock();
-
-       /* sort the routes by metric */
-       std::vector<ClusterLink> sortedLinks;
-       std::copy(links.begin(), links.end(), std::back_inserter(sortedLinks));
-       std::sort(sortedLinks.begin(), sortedLinks.end(), ClusterLinkMetricLessComparer());
-
-       /* pick routes */
-       std::set<String> visitedEndpoints;
-       BOOST_FOREACH(const ClusterLink& link, sortedLinks) {
-               Endpoint::Ptr other;
-
-               if (link.From == GetIdentity())
-                       other = Endpoint::GetByName(link.To);
-               else if (link.To == GetIdentity())
-                       other = Endpoint::GetByName(link.From);
-
-               if (visitedEndpoints.find(link.From) != visitedEndpoints.end() &&
-                   visitedEndpoints.find(link.To) != visitedEndpoints.end()) {
-                       if (other) {
-                               Log(LogInformation, "cluster", "Blocking link to '" + other->GetName() + "'");
-
-                               Dictionary::Ptr message = make_shared<Dictionary>();
-                               message->Set("jsonrpc", "2.0");
-                               message->Set("method", "cluster::BlockLink");
-                               message->Set("params", make_shared<Dictionary>());
-
-                               AsyncRelayMessage(Endpoint::Ptr(), other, message, false);
-                       }
-
-                       continue;
-               }
-
-               visitedEndpoints.insert(link.From);
-               visitedEndpoints.insert(link.To);
-       }
-}
-
-void ClusterListener::ClusterTimerHandler(void)
-{
-       /* Update endpoint routes */
-       UpdateLinks();
-
-       /* Eww. */
-       Dictionary::Ptr features = make_shared<Dictionary>();
-       features->Set("checker", SupportsChecks());
-       features->Set("notification", SupportsNotifications());
-
-       /* broadcast a heartbeat message */
-       BOOST_FOREACH(const Endpoint::Ptr& destination, DynamicType::GetObjects<Endpoint>()) {
-               std::set<String> connected_endpoints;
-
-               BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
-                       if (endpoint->GetName() == GetIdentity())
-                               continue;
-
-                       if (!endpoint->IsConnected())
-                               continue;
-
-                       connected_endpoints.insert(endpoint->GetName());
-               }
-
-               Array::Ptr epnames = make_shared<Array>();
-               BOOST_FOREACH(const String& name, connected_endpoints)
-                       epnames->Add(name);
-
-               Dictionary::Ptr params = make_shared<Dictionary>();
-               params->Set("identity", GetIdentity());
-               params->Set("features", features);
-               params->Set("connected_endpoints", epnames);
-
-               Dictionary::Ptr message = make_shared<Dictionary>();
-               message->Set("jsonrpc", "2.0");
-               message->Set("method", "cluster::HeartBeat");
-               message->Set("params", params);
-
-               Endpoint::GetByName(GetIdentity())->SetFeatures(features);
-
-               AsyncRelayMessage(Endpoint::Ptr(), destination, message, false);
-       }
-
-       {
-               ObjectLock olock(this);
-               /* check if we've recently seen heartbeat messages from our peers */
-               BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
-                       if (endpoint->GetSeen() > Utility::GetTime() - 60)
-                               continue;
-
-                       m_VisibleEndpoints.erase(endpoint->GetName());
-
-                       Stream::Ptr client = endpoint->GetClient();
-
-                       if (client) {
-                               Log(LogWarning, "cluster", "Closing connection for endpoint '" + endpoint->GetName() + "' due to inactivity.");
-                               client->Close();
-                               endpoint->SetClient(Stream::Ptr());
-                       }
-               }
-       }
-
-       std::vector<int> files;
-       Utility::Glob(GetClusterDir() + "log/*", boost::bind(&ClusterListener::LogGlobHandler, boost::ref(files), _1), GlobFile);
-       std::sort(files.begin(), files.end());
-
-       BOOST_FOREACH(int ts, files) {
-               bool need = false;
-
-               BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
-                       if (endpoint->GetName() == GetIdentity())
-                               continue;
-
-                       double position = endpoint->GetLocalLogPosition();
-
-                       if (position != 0 && ts > position) {
-                               need = true;
-                               break;
-                       }
-               }
-
-               if (!need) {
-                       String path = GetClusterDir() + "log/" + Convert::ToString(ts);
-                       Log(LogInformation, "cluster", "Removing old log file: " + path);
-                       (void) unlink(path.CStr());
-               }
-       }
-
-       UpdateAuthority();
-
-       Array::Ptr peers = GetPeers();
-
-       if (peers) {
-               ObjectLock olock(peers);
-               BOOST_FOREACH(const String& peer, peers) {
-                       Endpoint::Ptr endpoint = Endpoint::GetByName(peer);
-
-                       if (!endpoint) {
-                               Log(LogWarning, "cluster", "Attempted to reconnect to endpoint '" + peer + "': No configuration found.");
-                               continue;
-                       }
-
-                       if (endpoint->IsConnected())
-                               continue;
-
-                       String host, port;
-                       host = endpoint->GetHost();
-                       port = endpoint->GetPort();
-
-                       if (host.IsEmpty() || port.IsEmpty()) {
-                               Log(LogWarning, "cluster", "Can't reconnect "
-                                   "to endpoint '" + endpoint->GetName() + "': No "
-                                   "host/port information.");
-                               continue;
-                       }
-
-                       try {
-                               Log(LogInformation, "cluster", "Attempting to reconnect to cluster endpoint '" + endpoint->GetName() + "' via '" + host + ":" + port + "'.");
-                               AddConnection(host, port);
-                       } catch (std::exception& ex) {
-                               std::ostringstream msgbuf;
-                               msgbuf << "Exception occured while reconnecting to endpoint '"
-                                      << endpoint->GetName() << "': " << DiagnosticInformation(ex);
-                               Log(LogWarning, "cluster", msgbuf.str());
-                       }
-               }
-       }
-}
-
-void ClusterListener::SetSecurityInfo(const Dictionary::Ptr& message, const DynamicObject::Ptr& object, int privs)
-{
-       ASSERT(object);
-
-       Dictionary::Ptr security = make_shared<Dictionary>();
-       security->Set("type", object->GetType()->GetName());
-       security->Set("name", object->GetName());
-       security->Set("privs", privs);
-
-       message->Set("security", security);
-}
-
-void ClusterListener::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const String& authority)
-{
-       if (!authority.IsEmpty() && authority != GetIdentity())
-               return;
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("type", checkable->GetReflectionType()->GetName());
-       params->Set("checkable", checkable->GetName());
-       params->Set("check_result", Serialize(cr));
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::CheckResult");
-       message->Set("params", params);
-
-       SetSecurityInfo(message, checkable, DomainPrivRead);
-
-       AsyncRelayMessage(Endpoint::Ptr(), Endpoint::Ptr(), message, true);
-}
-
-void ClusterListener::NextCheckChangedHandler(const Checkable::Ptr& checkable, double nextCheck, const String& authority)
-{
-       if (!authority.IsEmpty() && authority != GetIdentity())
-               return;
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("type", checkable->GetReflectionType()->GetName());
-       params->Set("checkable", checkable->GetName());
-       params->Set("next_check", nextCheck);
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::SetNextCheck");
-       message->Set("params", params);
-
-       SetSecurityInfo(message, checkable, DomainPrivRead);
-
-       AsyncRelayMessage(Endpoint::Ptr(), Endpoint::Ptr(), message, true);
-}
-
-void ClusterListener::NextNotificationChangedHandler(const Notification::Ptr& notification, double nextNotification, const String& authority)
-{
-       if (!authority.IsEmpty() && authority != GetIdentity())
-               return;
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("notification", notification->GetName());
-       params->Set("next_notification", nextNotification);
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::SetNextNotification");
-       message->Set("params", params);
-
-       SetSecurityInfo(message, notification->GetCheckable(), DomainPrivRead);
-
-       AsyncRelayMessage(Endpoint::Ptr(), Endpoint::Ptr(), message, true);
-}
-
-void ClusterListener::ForceNextCheckChangedHandler(const Checkable::Ptr& checkable, bool forced, const String& authority)
-{
-       if (!authority.IsEmpty() && authority != GetIdentity())
-               return;
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("type", checkable->GetReflectionType()->GetName());
-       params->Set("checkable", checkable->GetName());
-       params->Set("forced", forced);
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::SetForceNextCheck");
-       message->Set("params", params);
-
-       SetSecurityInfo(message, checkable, DomainPrivRead);
-
-       AsyncRelayMessage(Endpoint::Ptr(), Endpoint::Ptr(), message, true);
-}
-
-void ClusterListener::ForceNextNotificationChangedHandler(const Checkable::Ptr& checkable, bool forced, const String& authority)
-{
-       if (!authority.IsEmpty() && authority != GetIdentity())
-               return;
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("type", checkable->GetReflectionType()->GetName());
-       params->Set("checkable", checkable->GetName());
-       params->Set("forced", forced);
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::SetForceNextNotification");
-       message->Set("params", params);
-
-       SetSecurityInfo(message, checkable, DomainPrivRead);
-
-       AsyncRelayMessage(Endpoint::Ptr(), Endpoint::Ptr(), message, true);
-}
-
-void ClusterListener::EnableActiveChecksChangedHandler(const Checkable::Ptr& checkable, bool enabled, const String& authority)
-{
-       if (!authority.IsEmpty() && authority != GetIdentity())
-               return;
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("type", checkable->GetReflectionType()->GetName());
-       params->Set("checkable", checkable->GetName());
-       params->Set("enabled", enabled);
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::SetEnableActiveChecks");
-       message->Set("params", params);
-
-       SetSecurityInfo(message, checkable, DomainPrivRead);
-
-       AsyncRelayMessage(Endpoint::Ptr(), Endpoint::Ptr(), message, true);
-}
-
-void ClusterListener::EnablePassiveChecksChangedHandler(const Checkable::Ptr& checkable, bool enabled, const String& authority)
-{
-       if (!authority.IsEmpty() && authority != GetIdentity())
-               return;
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("type", checkable->GetReflectionType()->GetName());
-       params->Set("checkable", checkable->GetName());
-       params->Set("enabled", enabled);
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::SetEnablePassiveChecks");
-       message->Set("params", params);
-
-       SetSecurityInfo(message, checkable, DomainPrivRead);
-
-       AsyncRelayMessage(Endpoint::Ptr(), Endpoint::Ptr(), message, true);
-}
-
-void ClusterListener::EnableNotificationsChangedHandler(const Checkable::Ptr& checkable, bool enabled, const String& authority)
-{
-       if (!authority.IsEmpty() && authority != GetIdentity())
-               return;
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("type", checkable->GetReflectionType()->GetName());
-       params->Set("checkable", checkable->GetName());
-       params->Set("enabled", enabled);
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::SetEnableNotifications");
-       message->Set("params", params);
-
-       SetSecurityInfo(message, checkable, DomainPrivRead);
-
-       AsyncRelayMessage(Endpoint::Ptr(), Endpoint::Ptr(), message, true);
-}
-
-void ClusterListener::EnableFlappingChangedHandler(const Checkable::Ptr& checkable, bool enabled, const String& authority)
-{
-       if (!authority.IsEmpty() && authority != GetIdentity())
-               return;
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("type", checkable->GetReflectionType()->GetName());
-       params->Set("checkable", checkable->GetName());
-       params->Set("enabled", enabled);
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::SetEnableFlapping");
-       message->Set("params", params);
-
-       SetSecurityInfo(message, checkable, DomainPrivRead);
-
-       AsyncRelayMessage(Endpoint::Ptr(), Endpoint::Ptr(), message, true);
-}
-
-void ClusterListener::CommentAddedHandler(const Checkable::Ptr& checkable, const Comment::Ptr& comment, const String& authority)
-{
-       if (!authority.IsEmpty() && authority != GetIdentity())
-               return;
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("type", checkable->GetReflectionType()->GetName());
-       params->Set("checkable", checkable->GetName());
-       params->Set("comment", Serialize(comment));
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::AddComment");
-       message->Set("params", params);
-
-       SetSecurityInfo(message, checkable, DomainPrivRead);
-
-       AsyncRelayMessage(Endpoint::Ptr(), Endpoint::Ptr(), message, true);
-}
-
-void ClusterListener::CommentRemovedHandler(const Checkable::Ptr& checkable, const Comment::Ptr& comment, const String& authority)
-{
-       if (!authority.IsEmpty() && authority != GetIdentity())
-               return;
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("type", checkable->GetReflectionType()->GetName());
-       params->Set("checkable", checkable->GetName());
-       params->Set("id", comment->GetId());
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::RemoveComment");
-       message->Set("params", params);
-
-       SetSecurityInfo(message, checkable, DomainPrivRead);
-
-       AsyncRelayMessage(Endpoint::Ptr(), Endpoint::Ptr(), message, true);
-}
-
-void ClusterListener::DowntimeAddedHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, const String& authority)
-{
-       if (!authority.IsEmpty() && authority != GetIdentity())
-               return;
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("type", checkable->GetReflectionType()->GetName());
-       params->Set("checkable", checkable->GetName());
-       params->Set("downtime", Serialize(downtime));
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::AddDowntime");
-       message->Set("params", params);
-
-       SetSecurityInfo(message, checkable, DomainPrivRead);
-
-       AsyncRelayMessage(Endpoint::Ptr(), Endpoint::Ptr(), message, true);
-}
-
-void ClusterListener::DowntimeRemovedHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, const String& authority)
-{
-       if (!authority.IsEmpty() && authority != GetIdentity())
-               return;
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("type", checkable->GetReflectionType()->GetName());
-       params->Set("checkable", checkable->GetName());
-       params->Set("id", downtime->GetId());
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::RemoveDowntime");
-       message->Set("params", params);
-
-       SetSecurityInfo(message, checkable, DomainPrivRead);
-
-       AsyncRelayMessage(Endpoint::Ptr(), Endpoint::Ptr(), message, true);
-}
-
-void ClusterListener::AcknowledgementSetHandler(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, double expiry, const String& authority)
-{
-       if (!authority.IsEmpty() && authority != GetIdentity())
-               return;
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("type", checkable->GetReflectionType()->GetName());
-       params->Set("checkable", checkable->GetName());
-       params->Set("author", author);
-       params->Set("comment", comment);
-       params->Set("acktype", type);
-       params->Set("expiry", expiry);
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::SetAcknowledgement");
-       message->Set("params", params);
-
-       SetSecurityInfo(message, checkable, DomainPrivRead);
-
-       AsyncRelayMessage(Endpoint::Ptr(), Endpoint::Ptr(), message, true);
-}
-
-void ClusterListener::AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const String& authority)
-{
-       if (!authority.IsEmpty() && authority != GetIdentity())
-               return;
-
-       Dictionary::Ptr params = make_shared<Dictionary>();
-       params->Set("type", checkable->GetReflectionType()->GetName());
-       params->Set("checkable", checkable->GetName());
-
-       Dictionary::Ptr message = make_shared<Dictionary>();
-       message->Set("jsonrpc", "2.0");
-       message->Set("method", "cluster::ClearAcknowledgement");
-       message->Set("params", params);
-
-       SetSecurityInfo(message, checkable, DomainPrivRead);
-
-       AsyncRelayMessage(Endpoint::Ptr(), Endpoint::Ptr(), message, true);
-}
-
-void ClusterListener::AsyncMessageHandler(const Endpoint::Ptr& sender, const Dictionary::Ptr& message)
-{
-       m_MessageQueue.Enqueue(boost::bind(&ClusterListener::MessageHandler, this, sender, message));
-}
-
-void ClusterListener::MessageExceptionHandler(boost::exception_ptr exp)
-{
-       Log(LogCritical, "cluster", "Exception while processing cluster message: " + DiagnosticInformation(exp));
-}
-
-void ClusterListener::MessageHandler(const Endpoint::Ptr& sender, const Dictionary::Ptr& message)
-{
-       CONTEXT("Processing cluster message of type '" + message->Get("method") + "'");
-
-       sender->SetSeen(Utility::GetTime());
-
-       if (message->Contains("ts")) {
-               double ts = message->Get("ts");
-
-               /* ignore old messages */
-               if (ts < sender->GetRemoteLogPosition())
-                       return;
-
-               if (sender->GetRemoteLogPosition() + 10 < ts) {
-                       Dictionary::Ptr lparams = make_shared<Dictionary>();
-                       lparams->Set("log_position", message->Get("ts"));
-
-                       Dictionary::Ptr lmessage = make_shared<Dictionary>();
-                       lmessage->Set("jsonrpc", "2.0");
-                       lmessage->Set("method", "cluster::SetLogPosition");
-                       lmessage->Set("params", lparams);
-
-                       sender->SendMessage(lmessage);
-
-                       Log(LogInformation, "cluster", "Acknowledging log position for identity '" + sender->GetName() + "': " + Utility::FormatDateTime("%Y/%m/%d %H:%M:%S", message->Get("ts")));
-                       sender->SetRemoteLogPosition(message->Get("ts"));
-
-                       ObjectLock olock(this);
-                       const EndpointPeerInfo& epi = m_VisibleEndpoints[sender->GetName()];
-
-                       if (epi.Peers) {
-                               ObjectLock olock(epi.Peers);
-                               BOOST_FOREACH(const String& epname, epi.Peers) {
-                                       if (epname == GetIdentity())
-                                               continue;
-
-                                       Endpoint::Ptr peer_endpoint = Endpoint::GetByName(epname);
-
-                                       if (!peer_endpoint)
-                                               continue;
-
-                                       Log(LogInformation, "cluster", "Acknowledging log position for identity '" + peer_endpoint->GetName() + "' (via '" + sender->GetName() + "'): " + Utility::FormatDateTime("%Y/%m/%d %H:%M:%S", message->Get("ts")));
-                                       peer_endpoint->SetRemoteLogPosition(message->Get("ts"));
-                               }
-                       }
-               }
-       }
-
-       Dictionary::Ptr params = message->Get("params");
-
-       if (message->Get("method") == "cluster::HeartBeat") {
-               if (!params)
-                       return;
-
-               String identity = params->Get("identity");
-
-               {
-                       ObjectLock olock(this);
-                       EndpointPeerInfo epi;
-                       epi.Seen = Utility::GetTime();
-                       epi.Peers = params->Get("connected_endpoints");
-                       m_VisibleEndpoints[identity] = epi;
-               }
-
-               Endpoint::Ptr endpoint = Endpoint::GetByName(identity);
-
-               if (endpoint) {
-                       endpoint->SetSeen(Utility::GetTime());
-                       endpoint->SetFeatures(params->Get("features"));
-               }
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, false);
-       } else if (message->Get("method") == "cluster::BlockLink") {
-               Log(LogDebug, "cluster", "Got cluster::BlockLink message. Blocking direct link for '" + sender->GetName() + "'");
-               sender->SetBlockedUntil(Utility::GetTime() + 30);
-       } else if (message->Get("method") == "cluster::CheckResult") {
-               if (!params)
-                       return;
-
-               String type = params->Get("type");
-               String chk = params->Get("checkable");
-
-               Checkable::Ptr checkable;
-
-               if (type == "Host")
-                       checkable = Host::GetByName(chk);
-               else if (type == "Service")
-                       checkable = Service::GetByName(chk);
-               else
-                       return;
-
-               if (!checkable)
-                       return;
-
-               if (!checkable->HasPrivileges(sender->GetName(), DomainPrivCheckResult)) {
-                       Log(LogDebug, "cluster", "Not accepting cluster::CheckResult message from endpoint '" + sender->GetName() + "' for checkable '" + checkable->GetName() + "': Insufficient privileges.");
-                       return;
-               }
-
-               CheckResult::Ptr cr = Deserialize(params->Get("check_result"), true);
-
-               if (!cr)
-                       return;
-
-               checkable->ProcessCheckResult(cr, sender->GetName());
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       } else if (message->Get("method") == "cluster::SetNextCheck") {
-               if (!params)
-                       return;
-
-               String type = params->Get("type");
-               String chk = params->Get("checkable");
-
-               Checkable::Ptr checkable;
-
-               if (type == "Host")
-                       checkable = DynamicObject::GetObject<Host>(chk);
-               else if (type == "Service")
-                       checkable = DynamicObject::GetObject<Service>(chk);
-               else
-                       return;
-
-               if (!checkable)
-                       return;
-
-               if (!checkable->HasPrivileges(sender->GetName(), DomainPrivCommand)) {
-                       Log(LogDebug, "cluster", "Not accepting cluster::SetNextCheck message from endpoint '" + sender->GetName() + "' for checkable '" + checkable->GetName() + "': Insufficient privileges.");
-                       return;
-               }
-
-               double nextCheck = params->Get("next_check");
-
-               checkable->SetNextCheck(nextCheck, sender->GetName());
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       } else if (message->Get("method") == "cluster::SetForceNextCheck") {
-               if (!params)
-                       return;
-
-               String type = params->Get("type");
-               String chk = params->Get("checkable");
-
-               Checkable::Ptr checkable;
-
-               if (type == "Host")
-                       checkable = DynamicObject::GetObject<Host>(chk);
-               else if (type == "Service")
-                       checkable = DynamicObject::GetObject<Service>(chk);
-               else
-                       return;
-
-               if (!checkable)
-                       return;
-
-               if (!checkable->HasPrivileges(sender->GetName(), DomainPrivCommand)) {
-                       Log(LogDebug, "cluster", "Not accepting cluster::SetForceNextCheck message from endpoint '" + sender->GetName() + "' for checkable '" + checkable->GetName() + "': Insufficient privileges.");
-                       return;
-               }
-
-               bool forced = params->Get("forced");
-
-               checkable->SetForceNextCheck(forced, sender->GetName());
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       } else if (message->Get("method") == "cluster::SetForceNextNotification") {
-               if (!params)
-                       return;
-
-               String type = params->Get("type");
-               String chk = params->Get("checkable");
-
-               Checkable::Ptr checkable;
-
-               if (type == "Host")
-                       checkable = DynamicObject::GetObject<Host>(chk);
-               else if (type == "Service")
-                       checkable = DynamicObject::GetObject<Service>(chk);
-               else
-                       return;
-
-               if (!checkable)
-                       return;
-
-               if (!checkable->HasPrivileges(sender->GetName(), DomainPrivCommand)) {
-                       Log(LogDebug, "cluster", "Not accepting cluster::SetForceNextNotification message from endpoint '" + sender->GetName() + "' for checkable '" + checkable->GetName() + "': Insufficient privileges.");
-                       return;
-               }
-
-               bool forced = params->Get("forced");
-
-               checkable->SetForceNextNotification(forced, sender->GetName());
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       } else if (message->Get("method") == "cluster::SetEnableActiveChecks") {
-               if (!params)
-                       return;
-
-               String type = params->Get("type");
-               String chk = params->Get("checkable");
-
-               Checkable::Ptr checkable;
-
-               if (type == "Host")
-                       checkable = DynamicObject::GetObject<Host>(chk);
-               else if (type == "Service")
-                       checkable = DynamicObject::GetObject<Service>(chk);
-               else
-                       return;
-
-               if (!checkable)
-                       return;
-
-               if (!checkable->HasPrivileges(sender->GetName(), DomainPrivCommand)) {
-                       Log(LogDebug, "cluster", "Not accepting cluster::SetEnableActiveChecks message from endpoint '" + sender->GetName() + "' for checkable '" + checkable->GetName() + "': Insufficient privileges.");
-                       return;
-               }
-
-               bool enabled = params->Get("enabled");
-
-               checkable->SetEnableActiveChecks(enabled, sender->GetName());
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       } else if (message->Get("method") == "cluster::SetEnablePassiveChecks") {
-               if (!params)
-                       return;
-
-               String type = params->Get("type");
-               String chk = params->Get("checkable");
-
-               Checkable::Ptr checkable;
-
-               if (type == "Host")
-                       checkable = DynamicObject::GetObject<Host>(chk);
-               else if (type == "Service")
-                       checkable = DynamicObject::GetObject<Service>(chk);
-               else
-                       return;
-
-               if (!checkable)
-                       return;
-
-               if (!checkable->HasPrivileges(sender->GetName(), DomainPrivCommand)) {
-                       Log(LogDebug, "cluster", "Not accepting cluster::SetEnablePassiveChecks message from endpoint '" + sender->GetName() + "' for checkable '" + checkable->GetName() + "': Insufficient privileges.");
-                       return;
-               }
-
-               bool enabled = params->Get("enabled");
-
-               checkable->SetEnablePassiveChecks(enabled, sender->GetName());
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       } else if (message->Get("method") == "cluster::SetEnableNotifications") {
-               if (!params)
-                       return;
-
-               String type = params->Get("type");
-               String chk = params->Get("checkable");
-
-               Checkable::Ptr checkable;
-
-               if (type == "Host")
-                       checkable = DynamicObject::GetObject<Host>(chk);
-               else if (type == "Service")
-                       checkable = DynamicObject::GetObject<Service>(chk);
-               else
-                       return;
-
-               if (!checkable)
-                       return;
-
-               if (!checkable->HasPrivileges(sender->GetName(), DomainPrivCommand)) {
-                       Log(LogDebug, "cluster", "Not accepting cluster::SetEnableNotifications message from endpoint '" + sender->GetName() + "' for checkable '" + checkable->GetName() + "': Insufficient privileges.");
-                       return;
-               }
-
-               bool enabled = params->Get("enabled");
-
-               checkable->SetEnableNotifications(enabled, sender->GetName());
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       } else if (message->Get("method") == "cluster::SetEnableFlapping") {
-               if (!params)
-                       return;
-
-               String type = params->Get("type");
-               String chk = params->Get("checkable");
-
-               Checkable::Ptr checkable;
-
-               if (type == "Host")
-                       checkable = DynamicObject::GetObject<Host>(chk);
-               else if (type == "Service")
-                       checkable = DynamicObject::GetObject<Service>(chk);
-               else
-                       return;
-
-               if (!checkable)
-                       return;
-
-               if (!checkable->HasPrivileges(sender->GetName(), DomainPrivCommand)) {
-                       Log(LogDebug, "cluster", "Not accepting cluster::SetEnableFlapping message from endpoint '" + sender->GetName() + "' for checkable '" + checkable->GetName() + "': Insufficient privileges.");
-                       return;
-               }
-
-               bool enabled = params->Get("enabled");
-
-               checkable->SetEnableFlapping(enabled, sender->GetName());
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       } else if (message->Get("method") == "cluster::SetNextNotification") {
-               if (!params)
-                       return;
-
-               String nfc = params->Get("notification");
-
-               Notification::Ptr notification = Notification::GetByName(nfc);
-
-               if (!notification)
-                       return;
-
-               Checkable::Ptr service = notification->GetCheckable();
-
-               if (!service->HasPrivileges(sender->GetName(), DomainPrivCommand)) {
-                       Log(LogDebug, "cluster", "Not accepting cluster::SetNextNotification message from endpoint '" + sender->GetName() + "' for service '" + service->GetName() + "': Insufficient privileges.");
-                       return;
-               }
-
-               double nextNotification = params->Get("next_notification");
-
-               notification->SetNextNotification(nextNotification, sender->GetName());
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       } else if (message->Get("method") == "cluster::AddComment") {
-               if (!params)
-                       return;
-
-               String type = params->Get("type");
-               String chk = params->Get("checkable");
-
-               Checkable::Ptr checkable;
-
-               if (type == "Host")
-                       checkable = DynamicObject::GetObject<Host>(chk);
-               else if (type == "Service")
-                       checkable = DynamicObject::GetObject<Service>(chk);
-               else
-                       return;
-
-               if (!checkable)
-                       return;
-
-               if (!checkable->HasPrivileges(sender->GetName(), DomainPrivCommand)) {
-                       Log(LogDebug, "cluster", "Not accepting cluster::AddComment message from endpoint '" + sender->GetName() + "' for checkable '" + checkable->GetName() + "': Insufficient privileges.");
-                       return;
-               }
-
-               Comment::Ptr comment = Deserialize(params->Get("comment"), true);
-
-               checkable->AddComment(comment->GetEntryType(), comment->GetAuthor(),
-                   comment->GetText(), comment->GetExpireTime(), comment->GetId(), sender->GetName());
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       } else if (message->Get("method") == "cluster::RemoveComment") {
-               if (!params)
-                       return;
-
-               String type = params->Get("type");
-               String chk = params->Get("checkable");
-
-               Checkable::Ptr checkable;
-
-               if (type == "Host")
-                       checkable = DynamicObject::GetObject<Host>(chk);
-               else if (type == "Service")
-                       checkable = DynamicObject::GetObject<Service>(chk);
-               else
-                       return;
-
-               if (!checkable)
-                       return;
-
-               if (!checkable->HasPrivileges(sender->GetName(), DomainPrivCommand)) {
-                       Log(LogDebug, "cluster", "Not accepting cluster::RemoveComment message from endpoint '" + sender->GetName() + "' for checkable '" + checkable->GetName() + "': Insufficient privileges.");
-                       return;
-               }
-
-               String id = params->Get("id");
-
-               checkable->RemoveComment(id, sender->GetName());
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       } else if (message->Get("method") == "cluster::AddDowntime") {
-               if (!params)
-                       return;
-
-               String type = params->Get("type");
-               String chk = params->Get("checkable");
-
-               Checkable::Ptr checkable;
-
-               if (type == "Host")
-                       checkable = DynamicObject::GetObject<Host>(chk);
-               else if (type == "Service")
-                       checkable = DynamicObject::GetObject<Service>(chk);
-               else
-                       return;
-
-               if (!checkable)
-                       return;
-
-               if (!checkable->HasPrivileges(sender->GetName(), DomainPrivCommand)) {
-                       Log(LogDebug, "cluster", "Not accepting cluster::AddDowntime message from endpoint '" + sender->GetName() + "' for checkable '" + checkable->GetName() + "': Insufficient privileges.");
-                       return;
-               }
-
-               Downtime::Ptr downtime = Deserialize(params->Get("downtime"), true);
-
-               checkable->AddDowntime(downtime->GetAuthor(), downtime->GetComment(),
-                   downtime->GetStartTime(), downtime->GetEndTime(),
-                   downtime->GetFixed(), downtime->GetTriggeredBy(),
-                   downtime->GetDuration(), downtime->GetScheduledBy(),
-                   downtime->GetId(), sender->GetName());
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       } else if (message->Get("method") == "cluster::RemoveDowntime") {
-               if (!params)
-                       return;
-
-               String type = params->Get("type");
-               String chk = params->Get("checkable");
-
-               Checkable::Ptr checkable;
-
-               if (type == "Host")
-                       checkable = DynamicObject::GetObject<Host>(chk);
-               else if (type == "Service")
-                       checkable = DynamicObject::GetObject<Service>(chk);
-               else
-                       return;
-
-               if (!checkable)
-                       return;
-
-               if (!checkable->HasPrivileges(sender->GetName(), DomainPrivCommand)) {
-                       Log(LogDebug, "cluster", "Not accepting cluster::RemoveDowntime message from endpoint '" + sender->GetName() + "' for checkable '" + checkable->GetName() + "': Insufficient privileges.");
-                       return;
-               }
-
-               String id = params->Get("id");
-
-               checkable->RemoveDowntime(id, false, sender->GetName());
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       } else if (message->Get("method") == "cluster::SetAcknowledgement") {
-               if (!params)
-                       return;
-
-               String type = params->Get("type");
-               String chk = params->Get("checkable");
-
-               Checkable::Ptr checkable;
-
-               if (type == "Host")
-                       checkable = DynamicObject::GetObject<Host>(chk);
-               else if (type == "Service")
-                       checkable = DynamicObject::GetObject<Service>(chk);
-               else
-                       return;
-
-               if (!checkable)
-                       return;
-
-               if (!checkable->HasPrivileges(sender->GetName(), DomainPrivCommand)) {
-                       Log(LogDebug, "cluster", "Not accepting cluster::SetAcknowledgement message from endpoint '" + sender->GetName() + "' for checkable '" + checkable->GetName() + "': Insufficient privileges.");
-                       return;
-               }
-
-               String author = params->Get("author");
-               String comment = params->Get("comment");
-               int acktype = params->Get("acktype");
-               double expiry = params->Get("expiry");
-
-               checkable->AcknowledgeProblem(author, comment, static_cast<AcknowledgementType>(acktype), expiry, sender->GetName());
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       } else if (message->Get("method") == "cluster::ClearAcknowledgement") {
-               if (!params)
-                       return;
-
-               String type = params->Get("type");
-               String chk = params->Get("checkable");
-
-               Checkable::Ptr checkable;
-
-               if (type == "Host")
-                       checkable = DynamicObject::GetObject<Host>(chk);
-               else if (type == "Service")
-                       checkable = DynamicObject::GetObject<Service>(chk);
-               else
-                       return;
-
-               if (!checkable)
-                       return;
-
-               if (!checkable->HasPrivileges(sender->GetName(), DomainPrivCommand)) {
-                       Log(LogDebug, "cluster", "Not accepting cluster::ClearAcknowledgement message from endpoint '" + sender->GetName() + "' for checkable '" + checkable->GetName() + "': Insufficient privileges.");
-                       return;
-               }
-
-               {
-                       ObjectLock olock(checkable);
-                       checkable->ClearAcknowledgement(sender->GetName());
-               }
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       } else if (message->Get("method") == "cluster::SetLogPosition") {
-               if (!params)
-                       return;
-
-               sender->SetLocalLogPosition(params->Get("log_position"));
-       } else if (message->Get("method") == "cluster::Config") {
-               if (!params)
-                       return;
-
-               Dictionary::Ptr remoteConfig = params->Get("config_files");
-               
-               if (!remoteConfig)
-                       return;
-
-               Endpoint::Ptr self = Endpoint::GetByName(GetIdentity());
-
-               Array::Ptr acceptConfig = self->GetAcceptConfig();
-
-               bool accept = false;
-
-               if (acceptConfig) {
-                       ObjectLock olock(acceptConfig);
-                       BOOST_FOREACH(const String& pattern, acceptConfig) {
-                               if (pattern == sender->GetName()) {
-                                       accept = true;
-                                       break;
-                               }
-                       }
-               }
-
-               String identity = params->Get("identity");
-
-               if (!accept) {
-                       Log(LogWarning, "cluster", "Ignoring config update from endpoint '" + sender->GetName() + "' for identity '" + identity + "'.");
-                       return;
-               }
-
-               Log(LogInformation, "cluster", "Processing config update for identity '" + identity + "'.");
-
-               String dir = GetClusterDir() + "config/" + SHA256(identity);
-#ifndef _WIN32
-               if (mkdir(dir.CStr(), 0700) < 0 && errno != EEXIST) {
-#else /*_ WIN32 */
-               if (mkdir(dir.CStr()) < 0 && errno != EEXIST) {
-#endif /* _WIN32 */
-                       BOOST_THROW_EXCEPTION(posix_error()
-                               << boost::errinfo_api_function("localtime")
-                               << boost::errinfo_errno(errno));
-               }
-
-               Dictionary::Ptr localConfig = make_shared<Dictionary>();
-               Utility::Glob(dir + "/*", boost::bind(&ClusterListener::ConfigGlobHandler, boost::cref(localConfig), _1, true), GlobFile);
-
-               bool configChange = false;
-
-               /* figure out whether config files were removed */
-               if (localConfig->GetLength() != remoteConfig->GetLength())
-                       configChange = true;
-
-               ObjectLock olock(remoteConfig);
-               BOOST_FOREACH(const Dictionary::Pair& kv, remoteConfig) {
-                       Dictionary::Ptr remoteFile = kv.second;
-                       bool writeFile = false;
-                       String hash = SHA256(kv.first);
-                       String path = dir + "/" + hash + ".conf";
-                       
-                       if (!localConfig->Contains(hash))
-                               writeFile = true;
-                       else {
-                               Dictionary::Ptr localFile = localConfig->Get(hash);
-
-                               String localContent = localFile->Get("content");
-                               String remoteContent = remoteFile->Get("content");
-
-                               if (localContent != remoteContent)
-                                       writeFile = true;
-                       }
-
-                       if (writeFile) {
-                               configChange = true;
-
-                               Log(LogInformation, "cluster", "Updating configuration file: " + path);
-
-                               std::ofstream fp(path.CStr(), std::ofstream::out | std::ostream::trunc);
-                               fp << remoteFile->Get("content");
-                               fp.close();
-                       }
-
-                       localConfig->Remove(hash);
-               }
-               olock.Unlock();
-
-               ObjectLock olock2(localConfig);
-               BOOST_FOREACH(const Dictionary::Pair& kv, localConfig) {
-                       String path = dir + "/" + kv.first;
-                       Log(LogInformation, "cluster", "Removing obsolete config file: " + path);
-                       (void) unlink(path.CStr());
-                       configChange = true;
-               }
-               olock2.Unlock();
-
-               if (configChange) {
-                       Log(LogInformation, "cluster", "Restarting after configuration change.");
-                       Application::RequestRestart();
-               }
-
-               AsyncRelayMessage(sender, Endpoint::Ptr(), message, true);
-       }
-}
-
-bool ClusterListener::IsAuthority(const DynamicObject::Ptr& object, const String& type)
-{
-       double now = Utility::GetTime();
-
-       Array::Ptr authorities = object->GetAuthorities();
-       std::vector<String> endpoints;
-
-       BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
-               bool match = false;
-
-               if ((endpoint->GetSeen() < now - 30 && endpoint->GetName() != GetIdentity()) || !endpoint->HasFeature(type))
-                       continue;
-
-               if (authorities) {
-                       ObjectLock olock(authorities);
-                       BOOST_FOREACH(const String& authority, authorities) {
-                               if (authority == endpoint->GetName()) {
-                                       match = true;
-
-                                       break;
-                               }
-                       }
-               } else {
-                       match = true;
-               }
-
-               if (match)
-                       endpoints.push_back(endpoint->GetName());
-       }
-
-       if (endpoints.empty())
-               return false;
-
-       std::sort(endpoints.begin(), endpoints.end());
-
-       String key = object->GetType()->GetName() + "\t" + object->GetName();
-       unsigned long hash = Utility::SDBM(key);
-       unsigned long index = hash % endpoints.size();
-
-//     Log(LogDebug, "cluster", "Authority for object '" + object->GetName() + "' of type '" + object->GetType()->GetName() + "' is '" + endpoints[index] + "'.");
-
-       return (endpoints[index] == GetIdentity());
-}
-
-void ClusterListener::UpdateAuthority(void)
-{
-       Log(LogDebug, "cluster", "Updating authority for objects.");
-
-       int checker_count = 0, notifications_count = 0;
-
-       BOOST_FOREACH(const DynamicType::Ptr& type, DynamicType::GetTypes()) {
-               BOOST_FOREACH(const DynamicObject::Ptr& object, type->GetObjects()) {
-                       bool checkerAuthority = IsAuthority(object, "checker");
-
-                       if (checkerAuthority)
-                               checker_count++;
-
-                       object->SetAuthority("checker", checkerAuthority);
-
-                       bool notificationAuthority = IsAuthority(object, "notifications");
-
-                       if (notificationAuthority)
-                               notifications_count++;
-
-                       object->SetAuthority("notifications", notificationAuthority);
-               }
-       }
-
-       Log(LogDebug, "cluster", "Cluster authority: " + Convert::ToString(checker_count) + "x checker, " + Convert::ToString(notifications_count) + "x notifications");
-}
-
-bool ClusterListener::SupportsChecks(void)
-{
-       return SupportsFeature("CheckerComponent") && (IcingaApplication::GetInstance()->GetEnableHostChecks() || IcingaApplication::GetInstance()->GetEnableServiceChecks());
-}
-
-bool ClusterListener::SupportsNotifications(void)
-{
-       return SupportsFeature("NotificationComponent") && IcingaApplication::GetInstance()->GetEnableNotifications();
-}
-
-bool ClusterListener::SupportsFeature(const String& name)
-{
-       DynamicType::Ptr type = DynamicType::GetByName(name);
-
-       if (!type)
-               return false;
-
-       return std::distance(type->GetObjects().first, type->GetObjects().second) > 0;
-}
-
-std::pair<Dictionary::Ptr, Dictionary::Ptr> ClusterListener::GetClusterStatus(void)
-{
-       Dictionary::Ptr status = make_shared<Dictionary>();
-       Dictionary::Ptr perfdata = make_shared<Dictionary>();
-
-       /* cluster stats */
-       status->Set("node", IcingaApplication::GetInstance()->GetNodeName());
-       status->Set("identity", GetIdentity());
-
-       double count_endpoints = 0;
-       Array::Ptr not_connected_endpoints = make_shared<Array>();
-       Array::Ptr connected_endpoints = make_shared<Array>();
-
-       BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
-               count_endpoints++;
-
-               if(!endpoint->IsAvailable() && endpoint->GetName() != GetIdentity())
-                       not_connected_endpoints->Add(endpoint->GetName());
-               else if(endpoint->IsAvailable() && endpoint->GetName() != GetIdentity())
-                       connected_endpoints->Add(endpoint->GetName());
-       }
-
-       status->Set("num_endpoints", count_endpoints);
-       status->Set("num_conn_endpoints", connected_endpoints->GetLength());
-       status->Set("num_not_conn_endpoints", not_connected_endpoints->GetLength());
-       status->Set("conn_endpoints", connected_endpoints);
-       status->Set("not_conn_endpoints", not_connected_endpoints);
-
-       perfdata->Set("num_endpoints", count_endpoints);
-       perfdata->Set("num_conn_endpoints", Convert::ToDouble(connected_endpoints->GetLength()));
-       perfdata->Set("num_not_conn_endpoints", Convert::ToDouble(not_connected_endpoints->GetLength()));
-
-       return std::make_pair(status, perfdata);
-}
diff --git a/components/cluster/clusterlistener.h b/components/cluster/clusterlistener.h
deleted file mode 100644 (file)
index 51d3eaa..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/******************************************************************************
- * 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.             *
- ******************************************************************************/
-
-#ifndef CLUSTERLISTENER_H
-#define CLUSTERLISTENER_H
-
-#include "cluster/clusterlistener.th"
-#include "cluster/clusterlink.h"
-#include "base/dynamicobject.h"
-#include "base/timer.h"
-#include "base/array.h"
-#include "base/tcpsocket.h"
-#include "base/tlsstream.h"
-#include "base/utility.h"
-#include "base/tlsutility.h"
-#include "base/stdiostream.h"
-#include "base/workqueue.h"
-#include "icinga/service.h"
-#include "remote/endpoint.h"
-
-namespace icinga
-{
-
-/**
- * @ingroup cluster
- */
-struct EndpointPeerInfo
-{
-       double Seen;
-       Array::Ptr Peers;
-};
-
-/**
- * @ingroup cluster
- */
-class ClusterListener : public ObjectImpl<ClusterListener>
-{
-public:
-       DECLARE_PTR_TYPEDEFS(ClusterListener);
-       DECLARE_TYPENAME(ClusterListener);
-
-        static Value StatsFunc(Dictionary::Ptr& status, Dictionary::Ptr& perfdata);
-
-       virtual void Start(void);
-       virtual void Stop(void);
-
-       shared_ptr<SSL_CTX> GetSSLContext(void) const;
-       String GetClusterDir(void) const;
-
-        std::pair<Dictionary::Ptr, Dictionary::Ptr> GetClusterStatus(void);
-
-private:
-       shared_ptr<SSL_CTX> m_SSLContext;
-
-       WorkQueue m_RelayQueue;
-       WorkQueue m_MessageQueue;
-       WorkQueue m_LogQueue;
-
-       Timer::Ptr m_ClusterTimer;
-       void ClusterTimerHandler(void);
-
-       std::set<TcpSocket::Ptr> m_Servers;
-
-       void AddListener(const String& service);
-       void AddConnection(const String& node, const String& service);
-
-       static void ConfigGlobHandler(const Dictionary::Ptr& config, const String& file, bool basename);
-
-       void NewClientHandler(const Socket::Ptr& client, TlsRole role);
-       void ListenerThreadProc(const Socket::Ptr& server);
-
-       std::map<String, EndpointPeerInfo> m_VisibleEndpoints;
-
-       void UpdateLinks(void);
-
-       void AsyncRelayMessage(const Endpoint::Ptr& source, const Endpoint::Ptr& destination, const Dictionary::Ptr& message, bool persistent);
-       void RelayMessage(const Endpoint::Ptr& source, const Endpoint::Ptr& destination, const Dictionary::Ptr& message, bool persistent);
-
-       void OpenLogFile(void);
-       void RotateLogFile(void);
-       void CloseLogFile(void);
-       static void LogGlobHandler(std::vector<int>& files, const String& file);
-       void ReplayLog(const Endpoint::Ptr& endpoint, const Stream::Ptr& stream);
-
-       Stream::Ptr m_LogFile;
-       size_t m_LogMessageCount;
-
-       void CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const String& authority);
-       void NextCheckChangedHandler(const Checkable::Ptr& checkable, double nextCheck, const String& authority);
-       void NextNotificationChangedHandler(const Notification::Ptr& notification, double nextCheck, const String& authority);
-       void ForceNextCheckChangedHandler(const Checkable::Ptr& checkable, bool forced, const String& authority);
-       void ForceNextNotificationChangedHandler(const Checkable::Ptr& checkable, bool forced, const String& authority);
-       void EnableActiveChecksChangedHandler(const Checkable::Ptr& checkable, bool enabled, const String& authority);
-       void EnablePassiveChecksChangedHandler(const Checkable::Ptr& checkable, bool enabled, const String& authority);
-       void EnableNotificationsChangedHandler(const Checkable::Ptr& checkable, bool enabled, const String& authority);
-       void EnableFlappingChangedHandler(const Checkable::Ptr& checkable, bool enabled, const String& authority);
-       void CommentAddedHandler(const Checkable::Ptr& checkable, const Comment::Ptr& comment, const String& authority);
-       void CommentRemovedHandler(const Checkable::Ptr& checkable, const Comment::Ptr& comment, const String& authority);
-       void DowntimeAddedHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, const String& authority);
-       void DowntimeRemovedHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, const String& authority);
-       void AcknowledgementSetHandler(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, double expiry, const String& authority);
-       void AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const String& authority);
-
-       void AsyncMessageHandler(const Endpoint::Ptr& sender, const Dictionary::Ptr& message);
-       void MessageHandler(const Endpoint::Ptr& sender, const Dictionary::Ptr& message);
-
-       bool IsAuthority(const DynamicObject::Ptr& object, const String& type);
-       void UpdateAuthority(void);
-
-       static bool SupportsChecks(void);
-       static bool SupportsNotifications(void);
-        static bool SupportsFeature(const String& name);
-
-       void SetSecurityInfo(const Dictionary::Ptr& message, const DynamicObject::Ptr& object, int privs);
-
-       void PersistMessage(const Endpoint::Ptr& source, const Dictionary::Ptr& message);
-
-       static void MessageExceptionHandler(boost::exception_ptr exp);
-};
-
-}
-
-#endif /* CLUSTERLISTENER_H */
index 2b1d0f3a02c07036c8593ed9b8340f2f4cae38f4..d31377efaba019a0b654a25cc3e019ae8a0606ae 100644 (file)
@@ -21,7 +21,7 @@ mkembedconfig_target(demo-type.conf demo-type.cpp)
 
 add_library(demo SHARED demo.cpp demo.th demo-type.cpp)
 
-target_link_libraries(demo ${Boost_LIBRARIES} base config icinga)
+target_link_libraries(demo ${Boost_LIBRARIES} base config icinga remote)
 
 set_target_properties (
   demo PROPERTIES
index abb91c0da906b154202cfd2ffcab8bab8f9e93fb..c8dde99667c157e0a93c81a4f1211b20363d18a2 100644 (file)
@@ -18,6 +18,8 @@
  ******************************************************************************/
 
 #include "demo/demo.h"
+#include "remote/apilistener.h"
+#include "remote/apifunction.h"
 #include "base/dynamictype.h"
 #include "base/logger_fwd.h"
 
@@ -25,6 +27,8 @@ using namespace icinga;
 
 REGISTER_TYPE(Demo);
 
+REGISTER_APIFUNCTION(HelloWorld, demo, &Demo::DemoMessageHandler);
+
 /**
  * Starts the component.
  */
@@ -39,19 +43,23 @@ void Demo::Start(void)
 }
 
 /**
- * Stops the component.
+ * Periodically broadcasts an API message.
  */
-void Demo::Stop(void)
+void Demo::DemoTimerHandler(void)
 {
-       /* Nothing to do here. */
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("method", "demo::HelloWorld");
+
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+       if (listener) {
+               listener->RelayMessage(MessageOrigin(), DynamicObject::Ptr(), message, true);
+               Log(LogInformation, "demo", "Sent demo::HelloWorld message");
+       }
 }
 
-/**
- * Periodically sends a demo::HelloWorld message.
- *
- * @param - Event arguments for the timer.
- */
-void Demo::DemoTimerHandler(void)
+Value Demo::DemoMessageHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
 {
-       Log(LogInformation, "demo", "Hello World!");
+       Log(LogInformation, "demo", "Got demo message from '" + origin.FromClient->GetEndpoint()->GetName() + "'");
+
+       return Empty;
 }
index 299b59fafe1cb75f3643405f0eef9d29adb47054..f70e3f23ecf181ae7230f249c3d5d4f97beb07a2 100644 (file)
@@ -21,6 +21,7 @@
 #define DEMO_H
 
 #include "demo/demo.th"
+#include "remote/messageorigin.h"
 #include "base/timer.h"
 
 namespace icinga
@@ -36,7 +37,9 @@ public:
        DECLARE_TYPENAME(Demo);
 
        virtual void Start(void);
-       virtual void Stop(void);
+
+       static Value DemoMessageHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
 
 private:
        Timer::Ptr m_DemoTimer;
index 93f642d3d8f6a2a1ad6115c7d6380016cb5183a8..e2d869bc76f593632f568963d5b742c9bb1130fa 100644 (file)
@@ -40,7 +40,7 @@ install_if_not_exists(icinga2/conf.d/hosts/localhost/users.conf ${CMAKE_INSTALL_
 install_if_not_exists(icinga2/conf.d/notifications.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
 install_if_not_exists(icinga2/conf.d/timeperiods.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
 install_if_not_exists(icinga2/conf.d/users.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
-install_if_not_exists(icinga2/features-available/agent.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available)
+install_if_not_exists(icinga2/features-available/api.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available)
 install_if_not_exists(icinga2/features-available/checker.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available)
 install_if_not_exists(icinga2/features-available/command.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available)
 install_if_not_exists(icinga2/features-available/compatlog.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available)
@@ -59,6 +59,8 @@ install_if_not_exists(icinga2/scripts/mail-host-notification.sh ${CMAKE_INSTALL_
 install_if_not_exists(icinga2/scripts/mail-service-notification.sh ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/scripts)
 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\")")
+
 if(NOT WIN32)
   install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled\")")
   install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ../features-available/checker.conf \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled/checker.conf\")")
index 74539d2678dcc7cd8a72cdcd1de721f3939b5f10..7f9c9146f45e870afd8bbee2ecccda047df6f686 100644 (file)
@@ -1,7 +1,13 @@
 /**
  * This file defines global constants which can be used in
- * the other configuration files. At a minimum the
- * PluginDir constant should be defined.
+ * the other configuration files.
  */
 
+/* The directory which contains the plugins from the Monitoring Plugins project. */
 const PluginDir = "/usr/lib/nagios/plugins"
+
+/* Our local instance name. This should be the common name from the API certificate */
+const NodeName = "localhost"
+
+/* Our local zone name. */
+const ZoneName = "master"
diff --git a/etc/icinga2/features-available/agent.conf b/etc/icinga2/features-available/agent.conf
deleted file mode 100644 (file)
index 152e9fd..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * The agent listener accepts checks from agents.
- */
-
-library "agent"
-
-object AgentListener "agent" {
-  cert_path = SysconfDir + "/icinga2/pki/your-master.crt"
-  key_path = SysconfDir + "/icinga2/pki/your-master.key"
-  ca_path = SysconfDir + "/icinga2/pki/ca.crt"
-}
diff --git a/etc/icinga2/features-available/api.conf b/etc/icinga2/features-available/api.conf
new file mode 100644 (file)
index 0000000..802100a
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * The API listener is used for distributed monitoring setups.
+ */
+
+object ApiListener "api" {
+  cert_path = SysconfDir + "/icinga2/pki/" + NodeName + ".crt"
+  key_path = SysconfDir + "/icinga2/pki/" + NodeName + ".key"
+  ca_path = SysconfDir + "/icinga2/pki/ca.crt"
+}
+
+object Endpoint NodeName {
+  host = NodeName
+}
+
+object Zone ZoneName {
+  endpoints = [ NodeName ]
+}
+
+/*object Endpoint "satellite.example.org" {
+  host = "satellite.example.org"
+}
+
+object Zone "satellite" {
+  parent = "master"
+  endpoints = [ "satellite.example.org" ]
+}*/
+
index b4bd319da78d0ddb5d28c9c993b1b76f4d1a5cd3..3058376b54f299e48e667ee1a78a04b5c596850a 100644 (file)
@@ -652,6 +652,9 @@ VOID WINAPI ServiceMain(DWORD argc, LPSTR *argv)
 */
 int main(int argc, char **argv)
 {
+       /* must be called before using any other libbase functions */
+       Application::InitializeBase();
+
        /* Set command-line arguments. */
        Application::SetArgC(argc);
        Application::SetArgV(argv);
index 03a3cf64b1b5cd1e51e0022b174cde92f1840473..bf9f8d34283385509b1b3cd315ef6a4d4132483c 100644 (file)
@@ -103,6 +103,11 @@ Application::~Application(void)
        m_Instance = NULL;
 }
 
+void Application::InitializeBase(void)
+{
+       Utility::ExecuteDeferredInitializers();
+}
+
 /**
  * Retrieves a pointer to the application singleton object.
  *
index 9f5e5ebe1b5c0b3bd091b8b5e1a56906a31c1453..3be23547e304037b6333cd7874a2411056fbb02a 100644 (file)
@@ -26,9 +26,8 @@
 #include "base/dynamicobject.h"
 #include "base/process.h"
 
-namespace icinga {
-
-class Component;
+namespace icinga
+{
 
 /**
  * Abstract base class for applications.
@@ -41,6 +40,8 @@ public:
 
        ~Application(void);
 
+       static void InitializeBase(void);
+
        static Application::Ptr GetInstance(void);
 
        int Run(void);
index 334065dff1162608e0e9d4bc7f10d740a7246b1a..286c80b6cd6068eafe451f03f927817f576bccc4 100644 (file)
@@ -44,8 +44,7 @@ INITIALIZE_ONCE(&DynamicObject::StaticInitialize);
 
 boost::signals2::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnStarted;
 boost::signals2::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnStopped;
-boost::signals2::signal<void (const DynamicObject::Ptr&, const String&)> DynamicObject::OnStateChanged;
-boost::signals2::signal<void (const DynamicObject::Ptr&, const String&, bool)> DynamicObject::OnAuthorityChanged;
+boost::signals2::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnStateChanged;
 boost::signals2::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnVarsChanged;
 
 void DynamicObject::StaticInitialize(void)
@@ -71,56 +70,6 @@ bool DynamicObject::IsActive(void) const
        return GetActive();
 }
 
-void DynamicObject::SetAuthority(const String& type, bool value)
-{
-       ASSERT(!OwnsLock());
-
-       {
-               ObjectLock olock(this);
-
-               bool old_value = HasAuthority(type);
-
-               if (old_value == value)
-                       return;
-
-               if (GetAuthorityInfo() == NULL)
-                       SetAuthorityInfo(make_shared<Dictionary>());
-
-               GetAuthorityInfo()->Set(type, value);
-       }
-
-       OnAuthorityChanged(GetSelf(), type, value);
-}
-
-bool DynamicObject::HasAuthority(const String& type) const
-{
-       Dictionary::Ptr authorityInfo = GetAuthorityInfo();
-
-       if (!authorityInfo || !authorityInfo->Contains(type))
-               return true;
-
-       return authorityInfo->Get(type);
-}
-
-void DynamicObject::SetPrivileges(const String& instance, int privs)
-{
-       m_Privileges[instance] = privs;
-}
-
-bool DynamicObject::HasPrivileges(const String& instance, int privs) const
-{
-       if (privs == 0)
-               return true;
-
-       std::map<String, int>::const_iterator it;
-       it = m_Privileges.find(instance);
-
-       if (it == m_Privileges.end())
-               return false;
-
-       return (it->second & privs) == privs;
-}
-
 void DynamicObject::SetExtension(const String& key, const Object::Ptr& object)
 {
        Dictionary::Ptr extensions = GetExtensions();
index f729891d31d35d21c317cff3ae31134e70318d34..0dbf09f6b39e062434318116240e11579e57d659 100644 (file)
@@ -77,8 +77,7 @@ public:
 
        static boost::signals2::signal<void (const DynamicObject::Ptr&)> OnStarted;
        static boost::signals2::signal<void (const DynamicObject::Ptr&)> OnStopped;
-       static boost::signals2::signal<void (const DynamicObject::Ptr&, const String&)> OnStateChanged;
-       static boost::signals2::signal<void (const DynamicObject::Ptr&, const String&, bool)> OnAuthorityChanged;
+       static boost::signals2::signal<void (const DynamicObject::Ptr&)> OnStateChanged;
        static boost::signals2::signal<void (const DynamicObject::Ptr&)> OnVarsChanged;
 
        Value InvokeMethod(const String& method, const std::vector<Value>& arguments);
@@ -87,12 +86,6 @@ public:
 
        bool IsActive(void) const;
 
-       void SetAuthority(const String& type, bool value);
-       bool HasAuthority(const String& type) const;
-
-       void SetPrivileges(const String& instance, int privs);
-       bool HasPrivileges(const String& instance, int privs) const;
-
        void SetExtension(const String& key, const Object::Ptr& object);
        Object::Ptr GetExtension(const String& key);
        void ClearExtension(const String& key);
index 6adbcc5ef8e09df98396ed1fc8d9c45976f63fae..4f9fce254bb7f37586fc535e57686c3b64784bbe 100644 (file)
@@ -20,12 +20,10 @@ abstract class DynamicObject
                }}}
        };
        [config, get_protected] String type (TypeName);
-       [config] String package;
+       [config] String zone;
        [config, get_protected] Array::Ptr templates;
        [config] Dictionary::Ptr methods;
        [config] Dictionary::Ptr vars (VarsRaw);
-       [config] Array::Ptr domains;
-       [config] Array::Ptr authorities;
        [get_protected] bool active;
        [get_protected] bool start_called;
        [get_protected] bool stop_called;
index 43120c06819436fcfb74d66384f0722502a96333..b0dfff905aa01457722507eea13745d85cd58479 100644 (file)
@@ -30,11 +30,7 @@ typedef void (*InitializeFunc)(void);
 
 inline bool InitializeOnceHelper(InitializeFunc func)
 {
-       if (Utility::GetLoadingLibrary())
-               Utility::AddDeferredInitializer(func);
-       else
-               func();
-
+       Utility::AddDeferredInitializer(func);
        return true;
 }
 
index 88c2ad7ac669229edb12534d2a86eb0950cff853..cfea4069ff8166326df15fba984a377bb91072a7 100644 (file)
@@ -1,21 +1,21 @@
 /******************************************************************************
-* 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.             *
-******************************************************************************/
+ * 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 "base/serializer.h"
 #include "base/type.h"
index e8c008ac08e485f4d911f41a20094e991443aad1..f055ce52158a513fe6793f7448ed0b8f00d135b6 100644 (file)
@@ -27,7 +27,8 @@
 #include <boost/thread/condition_variable.hpp>
 #include <boost/signals2.hpp>
 
-namespace icinga {
+namespace icinga
+{
 
 /**
  * Base class for connection-oriented sockets.
index 78ea8acadb43d4de649c3eca292c4c873154f403..cf96750ed049ffc9fadc6009f99fbdafb106ffad 100644 (file)
 namespace icinga
 {
 
+enum ConnectionRole
+{
+       RoleClient,
+       RoleServer
+};
+
 struct ReadLineContext
 {
        ReadLineContext(void) : Buffer(NULL), Size(0), Eof(false), MustRead(true)
index 85b5cfa4f8710a67fa033329d517e10c038ad594..8afe259d742baf221a24835fde1f5a7329752bb5 100644 (file)
@@ -21,7 +21,6 @@
 #include "base/application.h"
 #include "base/debug.h"
 #include "base/utility.h"
-#include "base/logger_fwd.h"
 #include <boost/bind.hpp>
 #include <boost/foreach.hpp>
 #include <boost/thread/thread.hpp>
@@ -48,7 +47,7 @@ struct icinga::TimerNextExtractor
         * @param wtimer Weak pointer to the timer.
         * @returns The next timestamp
         */
-       double operator()(const weak_ptr<Timer>& wtimer)
+       double operator()(const weak_ptr<Timer>& wtimer) const
        {
                Timer::Ptr timer = wtimer.lock();
 
@@ -87,7 +86,7 @@ void Timer::Initialize(void)
 {
        boost::mutex::scoped_lock lock(l_Mutex);
        l_StopThread = false;
-       l_Thread = boost::thread(boost::bind(&Timer::TimerThreadProc));
+       l_Thread = boost::thread(&Timer::TimerThreadProc);
 }
 
 /**
@@ -296,7 +295,7 @@ void Timer::TimerThreadProc(void)
 
                double wait = timer->m_Next - Utility::GetTime();
 
-               if (wait > 0) {
+               if (wait > 0.01) {
                        /* Make sure the timer we just examined can be destroyed while we're waiting. */
                        timer.reset();
 
index b875a16cf0665c68db187ae28c3827d78408e7d7..7a9ab3901338414c6e44f5d9a3b28ee608bbfd8b 100644 (file)
@@ -36,7 +36,7 @@ bool I2_EXPORT TlsStream::m_SSLIndexInitialized = false;
  * @param role The role of the client.
  * @param sslContext The SSL context for the client.
  */
-TlsStream::TlsStream(const Socket::Ptr& socket, TlsRole role, shared_ptr<SSL_CTX> sslContext)
+TlsStream::TlsStream(const Socket::Ptr& socket, ConnectionRole role, shared_ptr<SSL_CTX> sslContext)
        : m_Socket(socket), m_Role(role)
 {
        m_SSL = shared_ptr<SSL>(SSL_new(sslContext.get()), SSL_free);
@@ -62,7 +62,7 @@ TlsStream::TlsStream(const Socket::Ptr& socket, TlsRole role, shared_ptr<SSL_CTX
        BIO_set_nbio(m_BIO, 1);
        SSL_set_bio(m_SSL.get(), m_BIO, m_BIO);
 
-       if (m_Role == TlsRoleServer)
+       if (m_Role == RoleServer)
                SSL_set_accept_state(m_SSL.get());
        else
                SSL_set_connect_state(m_SSL.get());
@@ -90,13 +90,11 @@ shared_ptr<X509> TlsStream::GetPeerCertificate(void) const
 
 void TlsStream::Handshake(void)
 {
-       ASSERT(!OwnsLock());
-
        for (;;) {
                int rc, err;
 
                {
-                       ObjectLock olock(this);
+                       boost::mutex::scoped_lock lock(m_SSLLock);
                        rc = SSL_do_handshake(m_SSL.get());
 
                        if (rc > 0)
@@ -128,15 +126,13 @@ void TlsStream::Handshake(void)
  */
 size_t TlsStream::Read(void *buffer, size_t count)
 {
-       ASSERT(!OwnsLock());
-
        size_t left = count;
 
        while (left > 0) {
                int rc, err;
 
                {
-                       ObjectLock olock(this);
+                       boost::mutex::scoped_lock lock(m_SSLLock);
                        rc = SSL_read(m_SSL.get(), ((char *)buffer) + (count - left), left);
 
                        if (rc <= 0)
@@ -169,15 +165,13 @@ size_t TlsStream::Read(void *buffer, size_t count)
 
 void TlsStream::Write(const void *buffer, size_t count)
 {
-       ASSERT(!OwnsLock());
-
        size_t left = count;
 
        while (left > 0) {
                int rc, err;
 
                {
-                       ObjectLock olock(this);
+                       boost::mutex::scoped_lock lock(m_SSLLock);
                        rc = SSL_write(m_SSL.get(), ((const char *)buffer) + (count - left), left);
 
                        if (rc <= 0)
@@ -211,13 +205,11 @@ void TlsStream::Write(const void *buffer, size_t count)
  */
 void TlsStream::Close(void)
 {
-       ASSERT(!OwnsLock());
-
        for (;;) {
                int rc, err;
 
                {
-                       ObjectLock olock(this);
+                       boost::mutex::scoped_lock lock(m_SSLLock);
 
                        do {
                                rc = SSL_shutdown(m_SSL.get());
index 55685c9b9845ba2f442f89c77e39dc6bba9f1e17..22e313fa24b31ebf9342f6378f4974a69c606aa0 100644 (file)
 namespace icinga
 {
 
-enum TlsRole
-{
-       TlsRoleClient,
-       TlsRoleServer
-};
-
 /**
  * A TLS stream.
  *
@@ -44,7 +38,7 @@ class I2_BASE_API TlsStream : public Stream
 public:
        DECLARE_PTR_TYPEDEFS(TlsStream);
 
-       TlsStream(const Socket::Ptr& socket, TlsRole role, shared_ptr<SSL_CTX> sslContext);
+       TlsStream(const Socket::Ptr& socket, ConnectionRole role, shared_ptr<SSL_CTX> sslContext);
 
        shared_ptr<X509> GetClientCertificate(void) const;
        shared_ptr<X509> GetPeerCertificate(void) const;
@@ -59,11 +53,12 @@ public:
        virtual bool IsEof(void) const;
 
 private:
+       boost::mutex m_SSLLock;
        shared_ptr<SSL> m_SSL;
        BIO *m_BIO;
 
        Socket::Ptr m_Socket;
-       TlsRole m_Role;
+       ConnectionRole m_Role;
 
        static int m_SSLIndex;
        static bool m_SSLIndexInitialized;
index 9901b61f599b17749399c098dbd5f0ca0e1f855f..d5d5de2ce9a0c1bd2e30935322ec9bbeef3fe628 100644 (file)
@@ -370,21 +370,9 @@ Utility::LoadExtensionLibrary(const String& library)
 
        Log(LogInformation, "base", "Loading library '" + path + "'");
 
-       m_DeferredInitializers.reset(new std::vector<boost::function<void(void)> >());
-
 #ifdef _WIN32
-       HMODULE hModule;
+       HMODULE hModule = LoadLibrary(path.CStr());
        
-       try {
-               SetLoadingLibrary(true);
-               hModule = LoadLibrary(path.CStr());
-       } catch (...) {
-               SetLoadingLibrary(false);
-               throw;
-       }
-
-       SetLoadingLibrary(false);
-
        if (hModule == NULL) {
                BOOST_THROW_EXCEPTION(win32_error()
                    << boost::errinfo_api_function("LoadLibrary")
@@ -392,44 +380,35 @@ Utility::LoadExtensionLibrary(const String& library)
                    << boost::errinfo_file_name(path));
        }
 #else /* _WIN32 */
-       void *hModule;
+       void *hModule = dlopen(path.CStr(), RTLD_NOW);
        
-       try {
-               hModule = dlopen(path.CStr(), RTLD_NOW);
-       } catch (...) {
-               SetLoadingLibrary(false);
-               throw;
-       }
-
-       SetLoadingLibrary(false);
-
        if (hModule == NULL) {
                BOOST_THROW_EXCEPTION(std::runtime_error("Could not load library '" + path + "': " + dlerror()));
        }
 #endif /* _WIN32 */
 
-       BOOST_FOREACH(const boost::function<void(void)>& callback, *m_DeferredInitializers.get())
-               callback();
+       ExecuteDeferredInitializers();
 
-       m_DeferredInitializers.reset();
 
        return hModule;
 }
 
-bool Utility::GetLoadingLibrary(void)
+void Utility::ExecuteDeferredInitializers(void)
 {
-       bool *loading = m_LoadingLibrary.get();
-       return loading && *loading;
-}
+       if (!m_DeferredInitializers.get())
+               return;
 
-void Utility::SetLoadingLibrary(bool loading)
-{
-       bool *ploading = new bool(loading);
-       m_LoadingLibrary.reset(ploading);
+       BOOST_FOREACH(const boost::function<void(void)>& callback, *m_DeferredInitializers.get())
+               callback();
+
+       m_DeferredInitializers.reset();
 }
 
 void Utility::AddDeferredInitializer(const boost::function<void(void)>& callback)
 {
+       if (!m_DeferredInitializers.get())
+               m_DeferredInitializers.reset(new std::vector<boost::function<void(void)> >());
+
        m_DeferredInitializers.get()->push_back(callback);
 }
 
@@ -536,9 +515,7 @@ bool Utility::Glob(const String& pathSpec, const boost::function<void (const Str
                struct stat statbuf;
 
                if (stat(*gp, &statbuf) < 0)
-                       BOOST_THROW_EXCEPTION(posix_error()
-                           << boost::errinfo_api_function("stat")
-                           << boost::errinfo_errno(errno));
+                       continue;
 
                if (!S_ISDIR(statbuf.st_mode) && !S_ISREG(statbuf.st_mode))
                        continue;
index c12253e61f547c85e5f60c1478e2c95e74364d5e..34c9514956ed060e98523b10ab94a9d25f6b50b5 100644 (file)
@@ -97,9 +97,8 @@ public:
 #endif /* _WIN32 */
        LoadExtensionLibrary(const String& library);
 
-       static bool GetLoadingLibrary(void);
-       static void SetLoadingLibrary(bool loading);
        static void AddDeferredInitializer(const boost::function<void(void)>& callback);
+       static void ExecuteDeferredInitializers(void);
 
 #ifndef _WIN32
        static void SetNonBlocking(int fd);
index 8d152188f27d648b8a491a004448edd9e546df64..523c039c579e6496ae2de64d4c45c8f03cec2e79 100644 (file)
@@ -523,7 +523,7 @@ Value AExpression::OpObject(const AExpression* expr, const Dictionary::Ptr& loca
        String type = left->Get(1);
        AExpression::Ptr aname = left->Get(2);
        AExpression::Ptr filter = left->Get(3);
-       String package = left->Get(4);
+       String zone = left->Get(4);
 
        String name = aname->Evaluate(locals);
 
@@ -561,7 +561,7 @@ Value AExpression::OpObject(const AExpression* expr, const Dictionary::Ptr& loca
        item->AddExpression(exprl);
        item->SetAbstract(abstract);
        item->SetScope(locals);
-       item->SetPackage(package);
+       item->SetZone(zone);
        item->Compile()->Register();
 
        ObjectRule::AddRule(type, name, exprl, filter, expr->m_DebugInfo, locals);
index 46ee8b606e8f8eec615b94afe67ff281ae43341f..29a91176bd5a528e71b21d707091d277efe0e24a 100644 (file)
@@ -24,7 +24,7 @@
        %require "type",
        %attribute %string "type",
 
-       %attribute %string "package",
+       %attribute %name(Zone) "zone",
 
        %attribute %array "templates" {
                %attribute %string "*"
        %attribute %dictionary "vars" {
                %attribute %string "*"
        },
-
-       %attribute %array "domains" {
-               %attribute %string "*"
-       }
 }
 
 %type Logger {
index c7564e46a0a248e84639d56e9a8b7120d2c74618..829cd9c2ee3814ab610fffd3cb30f16e7ae547c4 100644 (file)
@@ -231,7 +231,7 @@ ignore                              return T_IGNORE;
 function                       return T_FUNCTION;
 lambda                         return T_LAMBDA;
 return                         return T_RETURN;
-package                                return T_PACKAGE;
+zone                           return T_ZONE;
 \<\<                           { yylval->op = &AExpression::OpShiftLeft; return T_SHIFT_LEFT; }
 \>\>                           { yylval->op = &AExpression::OpShiftRight; return T_SHIFT_RIGHT; }
 \<=                            { yylval->op = &AExpression::OpLessThanOrEqual; return T_LESS_THAN_OR_EQUAL; }
index 4dd436dbb80a28313bdefac42aa43b62eff7007b..d7e4399889b5701eb2fb12b2a1d1c7224fbced6f 100644 (file)
@@ -160,7 +160,7 @@ static void MakeRBinaryOp(Value** result, AExpression::OpCallback& op, Value *le
 %token T_FUNCTION "function (T_FUNCTION)"
 %token T_LAMBDA "lambda (T_LAMBDA)"
 %token T_RETURN "return (T_RETURN)"
-%token T_PACKAGE "package (T_PACKAGE)"
+%token T_ZONE "zone (T_ZONE)"
 
 %type <text> identifier
 %type <array> rterm_items
@@ -214,7 +214,7 @@ static std::stack<TypeRuleList::Ptr> m_RuleLists;
 static ConfigType::Ptr m_Type;
 
 static Dictionary::Ptr m_ModuleScope;
-static String m_Package;
+static String m_Zone;
 static int m_StatementNum;
 
 static bool m_Apply;
@@ -227,7 +227,7 @@ void ConfigCompiler::Compile(void)
 {
        m_ModuleScope = make_shared<Dictionary>();
        
-       String parentPackage = m_Package;
+       String parentZone = m_Zone;
        int parentStatementNum = m_StatementNum;
        m_StatementNum = 0;
 
@@ -240,7 +240,7 @@ void ConfigCompiler::Compile(void)
                ConfigCompilerContext::GetInstance()->AddMessage(true, DiagnosticInformation(ex));
        }
 
-       m_Package = parentPackage;
+       m_Zone = parentZone;
        m_StatementNum = parentStatementNum;
 }
 
@@ -253,7 +253,7 @@ statements: /* empty */
        | statements statement
        ;
 
-statement: type | package | include | include_recursive | library | constant
+statement: type | zone | include | include_recursive | library | constant
        {
                m_StatementNum++;
        }
@@ -269,37 +269,39 @@ statement: type | package | include | include_recursive | library | constant
        }
        ;
 
-package: T_PACKAGE rterm sep
+zone: T_ZONE rterm sep
        {
                AExpression::Ptr aexpr = *$2;
                delete $2;
 
-               if (!m_Package.IsEmpty())
-                       BOOST_THROW_EXCEPTION(std::invalid_argument("Package name cannot be changed once it's been set."));
+               if (!m_Zone.IsEmpty())
+                       BOOST_THROW_EXCEPTION(std::invalid_argument("Zone name cannot be changed once it's been set."));
 
                if (m_StatementNum != 0)
-                       BOOST_THROW_EXCEPTION(std::invalid_argument("'package' directive must be the first statement in a file."));
+                       BOOST_THROW_EXCEPTION(std::invalid_argument("'zone' directive must be the first statement in a file."));
 
-               m_Package = aexpr->Evaluate(m_ModuleScope);
+               m_Zone = aexpr->Evaluate(m_ModuleScope);
        }
-       | T_PACKAGE rterm rterm_scope sep
+       | T_ZONE rterm
        {
                AExpression::Ptr aexpr = *$2;
                delete $2;
 
-               AExpression::Ptr ascope = *$3;
-               delete $3;
-
-               if (!m_Package.IsEmpty())
-                       BOOST_THROW_EXCEPTION(std::invalid_argument("Package name cannot be changed once it's been set."));
+               if (!m_Zone.IsEmpty())
+                       BOOST_THROW_EXCEPTION(std::invalid_argument("Zone name cannot be changed once it's been set."));
 
-               m_Package = aexpr->Evaluate(m_ModuleScope);
+               m_Zone = aexpr->Evaluate(m_ModuleScope);
+       }
+       rterm_scope sep
+       {
+               AExpression::Ptr ascope = *$4;
+               delete $4;
 
                try {
                        ascope->Evaluate(m_ModuleScope);
-                       m_Package = String();
+                       m_Zone = String();
                } catch (...) {
-                       m_Package = String();
+                       m_Zone = String();
                }
        }
        ;
@@ -511,7 +513,7 @@ object:
 
                args->Add(filter);
 
-               args->Add(m_Package);
+               args->Add(m_Zone);
 
                $$ = new Value(make_shared<AExpression>(&AExpression::OpObject, args, exprl, DebugInfoRange(@2, @5)));
 
index e405d8dff21f21de9a79d7c96addb1749ee3e6c7..d5964d1b5ace2ee178bb8a0d22c392e5640aec60 100644 (file)
@@ -50,10 +50,10 @@ ConfigItem::ItemMap ConfigItem::m_Items;
 ConfigItem::ConfigItem(const String& type, const String& name,
     bool abstract, const AExpression::Ptr& exprl,
     const DebugInfo& debuginfo, const Dictionary::Ptr& scope,
-    const String& package)
+    const String& zone)
        : m_Type(type), m_Name(name), m_Abstract(abstract), m_Validated(false),
          m_ExpressionList(exprl), m_DebugInfo(debuginfo),
-         m_Scope(scope), m_Package(package)
+         m_Scope(scope), m_Zone(zone)
 {
 }
 
@@ -119,6 +119,8 @@ Dictionary::Ptr ConfigItem::GetProperties(void)
        if (!m_Properties) {
                m_Properties = make_shared<Dictionary>();
                m_Properties->Set("type", m_Type);
+               if (!m_Zone.IsEmpty())
+                       m_Properties->Set("zone", m_Zone);
                m_Properties->Set("__parent", m_Scope);
                GetExpressionList()->Evaluate(m_Properties);
                m_Properties->Remove("__parent");
index 743d111c2a1174d39752855f668e2a0984372a92..8ce6fa1ff80d4ca2d490eaf765da4666674191c1 100644 (file)
@@ -39,7 +39,7 @@ public:
 
        ConfigItem(const String& type, const String& name, bool abstract,
            const AExpression::Ptr& exprl, const DebugInfo& debuginfo,
-           const Dictionary::Ptr& scope, const String& package);
+           const Dictionary::Ptr& scope, const String& zone);
 
        String GetType(void) const;
        String GetName(void) const;
@@ -57,7 +57,7 @@ public:
 
        Dictionary::Ptr GetScope(void) const;
 
-       String GetPackage(void) const;
+       String GetZone(void) const;
 
        static ConfigItem::Ptr GetObject(const String& type,
            const String& name);
@@ -81,7 +81,7 @@ private:
                                       items. */
        DebugInfo m_DebugInfo; /**< Debug information. */
        Dictionary::Ptr m_Scope; /**< variable scope. */
-       String m_Package; /**< The package. */
+       String m_Zone; /**< The zone. */
 
        DynamicObject::Ptr m_Object;
 
index 3d29016548d9ccf5186229a1f191db2ff6700ea2..e70b36dc2f66effdb499aa43fbd8bc045ef621e7 100644 (file)
@@ -60,9 +60,9 @@ void ConfigItemBuilder::SetScope(const Dictionary::Ptr& scope)
        m_Scope = scope;
 }
 
-void ConfigItemBuilder::SetPackage(const String& package)
+void ConfigItemBuilder::SetZone(const String& zone)
 {
-       m_Package = package;
+       m_Zone = zone;
 }
 
 void ConfigItemBuilder::AddExpression(const AExpression::Ptr& expr)
@@ -104,5 +104,5 @@ ConfigItem::Ptr ConfigItemBuilder::Compile(void)
        AExpression::Ptr exprl = make_shared<AExpression>(&AExpression::OpDict, exprs, true, m_DebugInfo);
 
        return make_shared<ConfigItem>(m_Type, m_Name, m_Abstract, exprl,
-           m_DebugInfo, m_Scope, m_Package);
+           m_DebugInfo, m_Scope, m_Zone);
 }
index c9609a9cd3aaf21b8adf6efe4dc519bcda57ee3d..afad84759c71fb410b5210e0a137f1333ddb6f8a 100644 (file)
@@ -46,7 +46,7 @@ public:
        void SetName(const String& name);
        void SetAbstract(bool abstract);
        void SetScope(const Dictionary::Ptr& scope);
-       void SetPackage(const String& name);
+       void SetZone(const String& zone);
 
        void AddExpression(const AExpression::Ptr& expr);
 
@@ -59,7 +59,7 @@ private:
        Array::Ptr m_Expressions; /**< Expressions for this item. */
        DebugInfo m_DebugInfo; /**< Debug information. */
        Dictionary::Ptr m_Scope; /**< variable scope. */
-       String m_Package; /**< The package. */
+       String m_Zone; /**< The zone. */
 };
 
 }
index 92b4ef3abf92f8248ddb07bcd8dee5eff3236471..ad7da223e568bb90890110a751ea536553abe685 100644 (file)
@@ -50,7 +50,7 @@ void DbEvents::StaticInitialize(void)
        Checkable::OnAcknowledgementSet.connect(boost::bind(&DbEvents::AddAcknowledgement, _1, _4));
        Checkable::OnAcknowledgementCleared.connect(boost::bind(&DbEvents::RemoveAcknowledgement, _1));
 
-       Checkable::OnNextCheckChanged.connect(bind(&DbEvents::NextCheckChangedHandler, _1, _2, _3));
+       Checkable::OnNextCheckChanged.connect(bind(&DbEvents::NextCheckChangedHandler, _1, _2));
        Checkable::OnFlappingChanged.connect(bind(&DbEvents::FlappingChangedHandler, _1, _2));
        Checkable::OnNotificationSentToAllUsers.connect(bind(&DbEvents::LastNotificationChangedHandler, _1, _2));
 
@@ -78,7 +78,7 @@ void DbEvents::StaticInitialize(void)
 }
 
 /* check events */
-void DbEvents::NextCheckChangedHandler(const Checkable::Ptr& checkable, double nextCheck, const String& authority)
+void DbEvents::NextCheckChangedHandler(const Checkable::Ptr& checkable, double nextCheck)
 {
        Host::Ptr host;
        Service::Ptr service;
index 05966f59454f1c573dcb9779ebc538bb9b537026..aec44f4ef2a3f1aa4d166cff2586bc266100d1a5 100644 (file)
@@ -72,7 +72,7 @@ public:
         static void AddLogHistory(const Checkable::Ptr& checkable, String buffer, LogEntryType type);
 
         /* Status */
-       static void NextCheckChangedHandler(const Checkable::Ptr& checkable, double nextCheck, const String& authority);
+       static void NextCheckChangedHandler(const Checkable::Ptr& checkable, double nextCheck);
        static void FlappingChangedHandler(const Checkable::Ptr& checkable, FlappingState state);
        static void LastNotificationChangedHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable);
 
index b95e1d177356ffba7323d2af7f8f2f504e290402..13615fbbd091782537cae21d59bd5ca311691798 100644 (file)
@@ -39,7 +39,7 @@ INITIALIZE_ONCE(&EndpointDbObject::StaticInitialize);
 void EndpointDbObject::StaticInitialize(void)
 {
        Endpoint::OnConnected.connect(boost::bind(&EndpointDbObject::UpdateConnectedStatus, _1));
-       Endpoint::OnDisconnected.connect(boost::bind(&EndpointDbObject::UpdateDisconnectedStatus, _1));
+       Endpoint::OnDisconnected.connect(boost::bind(&EndpointDbObject::UpdateConnectedStatus, _1));
 }
 
 EndpointDbObject::EndpointDbObject(const DbType::Ptr& type, const String& name1, const String& name2)
@@ -73,16 +73,8 @@ Dictionary::Ptr EndpointDbObject::GetStatusFields(void) const
 
 void EndpointDbObject::UpdateConnectedStatus(const Endpoint::Ptr& endpoint)
 {
-       UpdateConnectedStatusInternal(endpoint, true);
-}
+       bool connected = EndpointIsConnected(endpoint);
 
-void EndpointDbObject::UpdateDisconnectedStatus(const Endpoint::Ptr& endpoint)
-{
-       UpdateConnectedStatusInternal(endpoint, false);
-}
-
-void EndpointDbObject::UpdateConnectedStatusInternal(const Endpoint::Ptr& endpoint, bool connected)
-{
        Log(LogDebug, "db_ido", "update is_connected=" + Convert::ToString(connected ? 1 : 0) + " for endpoint '" + endpoint->GetName() + "'");
 
        DbQuery query1;
index 7a69c548b2994c70678a65c225fd01e5e0698069..e2cf45e4807018134fb972e3a429cd3105005557 100644 (file)
@@ -49,8 +49,6 @@ protected:
 
 private:
         static void UpdateConnectedStatus(const Endpoint::Ptr& endpoint);
-        static void UpdateDisconnectedStatus(const Endpoint::Ptr& endpoint);
-        static void UpdateConnectedStatusInternal(const Endpoint::Ptr& endpoint, bool connected);
         static int EndpointIsConnected(const Endpoint::Ptr& endpoint);
 };
 
index f18e9125725aa76a721ac0c6d0853bd59bfd8593..ba61fa49bc303807b5f72644b7972f642dc8f6f1 100644 (file)
@@ -21,7 +21,6 @@ mkclass_target(checkresult.ti checkresult.th)
 mkclass_target(command.ti command.th)
 mkclass_target(comment.ti comment.th)
 mkclass_target(dependency.ti dependency.th)
-mkclass_target(domain.ti domain.th)
 mkclass_target(downtime.ti downtime.th)
 mkclass_target(eventcommand.ti eventcommand.th)
 mkclass_target(hostgroup.ti hostgroup.th)
@@ -41,10 +40,10 @@ mkclass_target(user.ti user.th)
 mkembedconfig_target(icinga-type.conf icinga-type.cpp)
 
 add_library(icinga SHARED
-  api.cpp checkable.cpp checkable.th checkable-dependency.cpp checkable-downtime.cpp checkable-event.cpp
+  api.cpp apievents.cpp checkable.cpp checkable.th checkable-dependency.cpp checkable-downtime.cpp checkable-event.cpp
   checkable-flapping.cpp checkcommand.cpp checkcommand.th checkresult.cpp checkresult.th
   cib.cpp command.cpp command.th comment.cpp comment.th compatutility.cpp dependency.cpp dependency.th
-  dependency-apply.cpp domain.cpp domain.th downtime.cpp downtime.th eventcommand.cpp eventcommand.th
+  dependency-apply.cpp downtime.cpp downtime.th eventcommand.cpp eventcommand.th
   externalcommandprocessor.cpp host.cpp host.th hostgroup.cpp hostgroup.th icingaapplication.cpp
   icingaapplication.th icingastatuswriter.cpp icingastatuswriter.th legacytimeperiod.cpp macroprocessor.cpp
   notificationcommand.cpp notificationcommand.th notification.cpp notification.th notification-apply.cpp
@@ -54,7 +53,7 @@ add_library(icinga SHARED
   user.cpp user.th usergroup.cpp usergroup.th icinga-type.cpp
 )
 
-target_link_libraries(icinga ${Boost_LIBRARIES} base config)
+target_link_libraries(icinga ${Boost_LIBRARIES} base config remote)
 
 set_target_properties (
   icinga PROPERTIES
index 723e33a5f8df420a419083d1a7ea45976cddf237..57eb8656e94ee51345bd9f2ea6f2d0977d45827a 100644 (file)
  ******************************************************************************/
 
 #include "icinga/api.h"
-#include "base/scriptfunction.h"
+#include "remote/apifunction.h"
 #include "base/logger_fwd.h"
 
 using namespace icinga;
 
-REGISTER_SCRIPTFUNCTION(GetAnswerToEverything, &API::GetAnswerToEverything);
+REGISTER_APIFUNCTION(GetAnswerToEverything, uapi, boost::bind(&API::GetAnswerToEverything, _2));
 
-int API::GetAnswerToEverything(const String& text)
+Value API::GetAnswerToEverything(const Dictionary::Ptr& params)
 {
+       String text;
+
+       if (params)
+               text = params->Get("text");
+
        Log(LogInformation, "icinga", "Hello from the Icinga 2 API: " + text);
 
        return 42;
index b163f5502b08b00148b0eb349e64851c4021561a..e5c79e28d71e6fca7e027fd657065c0ec566879d 100644 (file)
@@ -21,6 +21,7 @@
 #define API_H
 
 #include "icinga/i2-icinga.h"
+#include "remote/apiclient.h"
 #include "base/value.h"
 #include <vector>
 
@@ -28,14 +29,12 @@ namespace icinga
 {
 
 /**
- * A state change message for a service.
- *
  * @ingroup icinga
  */
 class I2_ICINGA_API API
 {
 public:
-       static int GetAnswerToEverything(const String& text);
+       static Value GetAnswerToEverything(const Dictionary::Ptr& params);
 
 private:
        API(void);
diff --git a/lib/icinga/apievents.cpp b/lib/icinga/apievents.cpp
new file mode 100644 (file)
index 0000000..3063193
--- /dev/null
@@ -0,0 +1,967 @@
+/******************************************************************************
+ * 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 "icinga/apievents.h"
+#include "icinga/service.h"
+#include "remote/apilistener.h"
+#include "remote/apiclient.h"
+#include "remote/apifunction.h"
+#include "base/application.h"
+#include "base/dynamictype.h"
+#include "base/objectlock.h"
+#include "base/utility.h"
+#include "base/logger_fwd.h"
+#include "base/exception.h"
+#include "base/initialize.h"
+#include <fstream>
+
+using namespace icinga;
+
+INITIALIZE_ONCE(&ApiEvents::StaticInitialize);
+
+REGISTER_APIFUNCTION(CheckResult, event, &ApiEvents::CheckResultAPIHandler);
+REGISTER_APIFUNCTION(SetNextCheck, event, &ApiEvents::NextCheckChangedAPIHandler);
+REGISTER_APIFUNCTION(SetNextNotification, event, &ApiEvents::NextNotificationChangedAPIHandler);
+REGISTER_APIFUNCTION(SetForceNextCheck, event, &ApiEvents::ForceNextCheckChangedAPIHandler);
+REGISTER_APIFUNCTION(SetForceNextNotification, event, &ApiEvents::ForceNextNotificationChangedAPIHandler);
+REGISTER_APIFUNCTION(SetEnableActiveChecks, event, &ApiEvents::EnableActiveChecksChangedAPIHandler);
+REGISTER_APIFUNCTION(SetEnablePassiveChecks, event, &ApiEvents::EnablePassiveChecksChangedAPIHandler);
+REGISTER_APIFUNCTION(SetEnableNotifications, event, &ApiEvents::EnableNotificationsChangedAPIHandler);
+REGISTER_APIFUNCTION(SetEnableFlapping, event, &ApiEvents::EnableFlappingChangedAPIHandler);
+REGISTER_APIFUNCTION(AddComment, event, &ApiEvents::CommentAddedAPIHandler);
+REGISTER_APIFUNCTION(RemoveComment, event, &ApiEvents::CommentRemovedAPIHandler);
+REGISTER_APIFUNCTION(AddDowntime, event, &ApiEvents::DowntimeAddedAPIHandler);
+REGISTER_APIFUNCTION(RemoveDowntime, event, &ApiEvents::DowntimeRemovedAPIHandler);
+REGISTER_APIFUNCTION(SetAcknowledgement, event, &ApiEvents::AcknowledgementSetAPIHandler);
+REGISTER_APIFUNCTION(ClearAcknowledgement, event, &ApiEvents::AcknowledgementClearedAPIHandler);
+REGISTER_APIFUNCTION(UpdateRepository, event, &ApiEvents::UpdateRepositoryAPIHandler);
+
+static Timer::Ptr l_RepositoryTimer;
+
+void ApiEvents::StaticInitialize(void)
+{
+       Checkable::OnNewCheckResult.connect(&ApiEvents::CheckResultHandler);
+       Checkable::OnNextCheckChanged.connect(&ApiEvents::NextCheckChangedHandler);
+       Notification::OnNextNotificationChanged.connect(&ApiEvents::NextNotificationChangedHandler);
+       Checkable::OnForceNextCheckChanged.connect(&ApiEvents::ForceNextCheckChangedHandler);
+       Checkable::OnForceNextNotificationChanged.connect(&ApiEvents::ForceNextNotificationChangedHandler);
+       Checkable::OnEnableActiveChecksChanged.connect(&ApiEvents::EnableActiveChecksChangedHandler);
+       Checkable::OnEnablePassiveChecksChanged.connect(&ApiEvents::EnablePassiveChecksChangedHandler);
+       Checkable::OnEnableNotificationsChanged.connect(&ApiEvents::EnableNotificationsChangedHandler);
+       Checkable::OnEnableFlappingChanged.connect(&ApiEvents::EnableFlappingChangedHandler);
+       Checkable::OnCommentAdded.connect(&ApiEvents::CommentAddedHandler);
+       Checkable::OnCommentRemoved.connect(&ApiEvents::CommentRemovedHandler);
+       Checkable::OnDowntimeAdded.connect(&ApiEvents::DowntimeAddedHandler);
+       Checkable::OnDowntimeRemoved.connect(&ApiEvents::DowntimeRemovedHandler);
+       Checkable::OnAcknowledgementSet.connect(&ApiEvents::AcknowledgementSetHandler);
+       Checkable::OnAcknowledgementCleared.connect(&ApiEvents::AcknowledgementClearedHandler);
+
+       l_RepositoryTimer = make_shared<Timer>();
+       l_RepositoryTimer->SetInterval(60);
+       l_RepositoryTimer->OnTimerExpired.connect(boost::bind(&ApiEvents::RepositoryTimerHandler));
+       l_RepositoryTimer->Start();
+       l_RepositoryTimer->Reschedule(0);
+}
+
+void ApiEvents::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const MessageOrigin& origin)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::CheckResult");
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("host", host->GetName());
+       if (service)
+               params->Set("service", service->GetShortName());
+       params->Set("cr", Serialize(cr));
+
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, checkable, message, true);
+}
+
+Value ApiEvents::CheckResultAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Value crv = params->Get("cr");
+
+       CheckResult::Ptr cr = Deserialize(crv, true);
+
+       Host::Ptr host = Host::GetByName(params->Get("host"));
+
+       if (!host)
+               return Empty;
+
+       Checkable::Ptr checkable;
+
+       if (params->Contains("service"))
+               checkable = host->GetServiceByShortName(params->Get("service"));
+       else
+               checkable = host;
+
+       if (!checkable)
+               return Empty;
+
+       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+               return Empty;
+
+       checkable->ProcessCheckResult(cr, origin);
+
+       return Empty;
+}
+
+void ApiEvents::NextCheckChangedHandler(const Checkable::Ptr& checkable, double nextCheck, const MessageOrigin& origin)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("host", host->GetName());
+       if (service)
+               params->Set("service", service->GetShortName());
+       params->Set("next_check", nextCheck);
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::SetNextCheck");
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, checkable, message, true);
+}
+
+Value ApiEvents::NextCheckChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Host::Ptr host = Host::GetByName(params->Get("host"));
+
+       if (!host)
+               return Empty;
+
+       Checkable::Ptr checkable;
+
+       if (params->Contains("service"))
+               checkable = host->GetServiceByShortName(params->Get("service"));
+       else
+               checkable = host;
+
+       if (!checkable)
+               return Empty;
+
+       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+               return Empty;
+
+       checkable->SetNextCheck(params->Get("next_check"), origin);
+
+       return Empty;
+}
+
+void ApiEvents::NextNotificationChangedHandler(const Notification::Ptr& notification, double nextNotification, const MessageOrigin& origin)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("notification", notification->GetName());
+       params->Set("next_notification", nextNotification);
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::SetNextNotification");
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, notification, message, true);
+}
+
+Value ApiEvents::NextNotificationChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Notification::Ptr notification = Notification::GetByName(params->Get("notification"));
+
+       if (!notification)
+               return Empty;
+
+       if (origin.FromZone && !origin.FromZone->CanAccessObject(notification))
+               return Empty;
+
+       notification->SetNextNotification(params->Get("next_notification"), origin);
+
+       return Empty;
+}
+
+void ApiEvents::ForceNextCheckChangedHandler(const Checkable::Ptr& checkable, bool forced, const MessageOrigin& origin)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("host", host->GetName());
+       if (service)
+               params->Set("service", service->GetShortName());
+       params->Set("forced", forced);
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::SetForceNextCheck");
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, checkable, message, true);
+}
+
+Value ApiEvents::ForceNextCheckChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Host::Ptr host = Host::GetByName(params->Get("host"));
+
+       if (!host)
+               return Empty;
+
+       Checkable::Ptr checkable;
+
+       if (params->Contains("service"))
+               checkable = host->GetServiceByShortName(params->Get("service"));
+       else
+               checkable = host;
+
+       if (!checkable)
+               return Empty;
+
+       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+               return Empty;
+
+       checkable->SetForceNextCheck(params->Get("forced"), origin);
+
+       return Empty;
+}
+
+void ApiEvents::ForceNextNotificationChangedHandler(const Checkable::Ptr& checkable, bool forced, const MessageOrigin& origin)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("host", host->GetName());
+       if (service)
+               params->Set("service", service->GetShortName());
+       params->Set("forced", forced);
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::SetForceNextNotification");
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, checkable, message, true);
+}
+
+Value ApiEvents::ForceNextNotificationChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Host::Ptr host = Host::GetByName(params->Get("host"));
+
+       if (!host)
+               return Empty;
+
+       Checkable::Ptr checkable;
+
+       if (params->Contains("service"))
+               checkable = host->GetServiceByShortName(params->Get("service"));
+       else
+               checkable = host;
+
+       if (!checkable)
+               return Empty;
+
+       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+               return Empty;
+
+       checkable->SetForceNextNotification(params->Get("forced"), origin);
+
+       return Empty;
+}
+
+void ApiEvents::EnableActiveChecksChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("host", host->GetName());
+       if (service)
+               params->Set("service", service->GetShortName());
+       params->Set("enabled", enabled);
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::SetEnableActiveChecks");
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, checkable, message, true);
+}
+
+Value ApiEvents::EnableActiveChecksChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Host::Ptr host = Host::GetByName(params->Get("host"));
+
+       if (!host)
+               return Empty;
+
+       Checkable::Ptr checkable;
+
+       if (params->Contains("service"))
+               checkable = host->GetServiceByShortName(params->Get("service"));
+       else
+               checkable = host;
+
+       if (!checkable)
+               return Empty;
+
+       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+               return Empty;
+
+       checkable->SetEnableActiveChecks(params->Get("enabled"), origin);
+
+       return Empty;
+}
+
+void ApiEvents::EnablePassiveChecksChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("host", host->GetName());
+       if (service)
+               params->Set("service", service->GetShortName());
+       params->Set("enabled", enabled);
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::SetEnablePassiveChecks");
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, checkable, message, true);
+}
+
+Value ApiEvents::EnablePassiveChecksChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Host::Ptr host = Host::GetByName(params->Get("host"));
+
+       if (!host)
+               return Empty;
+
+       Checkable::Ptr checkable;
+
+       if (params->Contains("service"))
+               checkable = host->GetServiceByShortName(params->Get("service"));
+       else
+               checkable = host;
+
+       if (!checkable)
+               return Empty;
+
+       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+               return Empty;
+
+       checkable->SetEnablePassiveChecks(params->Get("enabled"), origin);
+
+       return Empty;
+}
+
+void ApiEvents::EnableNotificationsChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("host", host->GetName());
+       if (service)
+               params->Set("service", service->GetShortName());
+       params->Set("enabled", enabled);
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::SetEnableNotifications");
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, checkable, message, true);
+}
+
+Value ApiEvents::EnableNotificationsChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Host::Ptr host = Host::GetByName(params->Get("host"));
+
+       if (!host)
+               return Empty;
+
+       Checkable::Ptr checkable;
+
+       if (params->Contains("service"))
+               checkable = host->GetServiceByShortName(params->Get("service"));
+       else
+               checkable = host;
+
+       if (!checkable)
+               return Empty;
+
+       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+               return Empty;
+
+       checkable->SetEnableNotifications(params->Get("enabled"), origin);
+
+       return Empty;
+}
+
+void ApiEvents::EnableFlappingChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("host", host->GetName());
+       if (service)
+               params->Set("service", service->GetShortName());
+       params->Set("enabled", enabled);
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::SetEnableFlapping");
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, checkable, message, true);
+}
+
+Value ApiEvents::EnableFlappingChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Host::Ptr host = Host::GetByName(params->Get("host"));
+
+       if (!host)
+               return Empty;
+
+       Checkable::Ptr checkable;
+
+       if (params->Contains("service"))
+               checkable = host->GetServiceByShortName(params->Get("service"));
+       else
+               checkable = host;
+
+       if (!checkable)
+               return Empty;
+
+       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+               return Empty;
+
+       checkable->SetEnableFlapping(params->Get("enabled"), origin);
+
+       return Empty;
+}
+
+void ApiEvents::CommentAddedHandler(const Checkable::Ptr& checkable, const Comment::Ptr& comment, const MessageOrigin& origin)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("host", host->GetName());
+       if (service)
+               params->Set("service", service->GetShortName());
+       params->Set("comment", Serialize(comment));
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::AddComment");
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, checkable, message, true);
+}
+
+Value ApiEvents::CommentAddedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Host::Ptr host = Host::GetByName(params->Get("host"));
+
+       if (!host)
+               return Empty;
+
+       Checkable::Ptr checkable;
+
+       if (params->Contains("service"))
+               checkable = host->GetServiceByShortName(params->Get("service"));
+       else
+               checkable = host;
+
+       if (!checkable)
+               return Empty;
+
+       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+               return Empty;
+
+       Comment::Ptr comment = Deserialize(params->Get("comment"), true);
+
+       checkable->AddComment(comment->GetEntryType(), comment->GetAuthor(),
+           comment->GetText(), comment->GetExpireTime(), comment->GetId(), origin);
+
+       return Empty;
+}
+
+void ApiEvents::CommentRemovedHandler(const Checkable::Ptr& checkable, const Comment::Ptr& comment, const MessageOrigin& origin)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("host", host->GetName());
+       if (service)
+               params->Set("service", service->GetShortName());
+       params->Set("id", comment->GetId());
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::RemoveComment");
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, checkable, message, true);
+}
+
+Value ApiEvents::CommentRemovedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Host::Ptr host = Host::GetByName(params->Get("host"));
+
+       if (!host)
+               return Empty;
+
+       Checkable::Ptr checkable;
+
+       if (params->Contains("service"))
+               checkable = host->GetServiceByShortName(params->Get("service"));
+       else
+               checkable = host;
+
+       if (!checkable)
+               return Empty;
+
+       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+               return Empty;
+
+       checkable->RemoveComment(params->Get("id"), origin);
+
+       return Empty;
+}
+
+void ApiEvents::DowntimeAddedHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, const MessageOrigin& origin)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("host", host->GetName());
+       if (service)
+               params->Set("service", service->GetShortName());
+       params->Set("downtime", Serialize(downtime));
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::AddDowntime");
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, checkable, message, true);
+}
+
+Value ApiEvents::DowntimeAddedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Host::Ptr host = Host::GetByName(params->Get("host"));
+
+       if (!host)
+               return Empty;
+
+       Checkable::Ptr checkable;
+
+       if (params->Contains("service"))
+               checkable = host->GetServiceByShortName(params->Get("service"));
+       else
+               checkable = host;
+
+       if (!checkable)
+               return Empty;
+
+       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+               return Empty;
+
+       Downtime::Ptr downtime = Deserialize(params->Get("downtime"), true);
+
+       checkable->AddDowntime(downtime->GetAuthor(), downtime->GetComment(),
+           downtime->GetStartTime(), downtime->GetEndTime(),
+           downtime->GetFixed(), downtime->GetTriggeredBy(),
+           downtime->GetDuration(), downtime->GetScheduledBy(),
+           downtime->GetId(), origin);
+
+       return Empty;
+}
+
+void ApiEvents::DowntimeRemovedHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, const MessageOrigin& origin)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("host", host->GetName());
+       if (service)
+               params->Set("service", service->GetShortName());
+       params->Set("id", downtime->GetId());
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::RemoveDowntime");
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, checkable, message, true);
+}
+
+Value ApiEvents::DowntimeRemovedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Host::Ptr host = Host::GetByName(params->Get("host"));
+
+       if (!host)
+               return Empty;
+
+       Checkable::Ptr checkable;
+
+       if (params->Contains("service"))
+               checkable = host->GetServiceByShortName(params->Get("service"));
+       else
+               checkable = host;
+
+       if (!checkable)
+               return Empty;
+
+       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+               return Empty;
+
+       checkable->RemoveDowntime(params->Get("id"), false, origin);
+
+       return Empty;
+}
+
+void ApiEvents::AcknowledgementSetHandler(const Checkable::Ptr& checkable,
+    const String& author, const String& comment, AcknowledgementType type,
+    double expiry, const MessageOrigin& origin)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("host", host->GetName());
+       if (service)
+               params->Set("service", service->GetShortName());
+       params->Set("author", author);
+       params->Set("comment", comment);
+       params->Set("acktype", type);
+       params->Set("expiry", expiry);
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::SetAcknowledgement");
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, checkable, message, true);
+}
+
+Value ApiEvents::AcknowledgementSetAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Host::Ptr host = Host::GetByName(params->Get("host"));
+
+       if (!host)
+               return Empty;
+
+       Checkable::Ptr checkable;
+
+       if (params->Contains("service"))
+               checkable = host->GetServiceByShortName(params->Get("service"));
+       else
+               checkable = host;
+
+       if (!checkable)
+               return Empty;
+
+       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+               return Empty;
+
+       checkable->AcknowledgeProblem(params->Get("author"), params->Get("comment"),
+           static_cast<AcknowledgementType>(static_cast<int>(params->Get("acktype"))),
+           params->Get("expiry"), origin);
+
+       return Empty;
+}
+
+void ApiEvents::AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const MessageOrigin& origin)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("host", host->GetName());
+       if (service)
+               params->Set("service", service->GetShortName());
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::ClearAcknowledgement");
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, checkable, message, true);
+}
+
+Value ApiEvents::AcknowledgementClearedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Host::Ptr host = Host::GetByName(params->Get("host"));
+
+       if (!host)
+               return Empty;
+
+       Checkable::Ptr checkable;
+
+       if (params->Contains("service"))
+               checkable = host->GetServiceByShortName(params->Get("service"));
+       else
+               checkable = host;
+
+       if (!checkable)
+               return Empty;
+
+       if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
+               return Empty;
+
+       checkable->ClearAcknowledgement(origin);
+
+       return Empty;
+}
+
+void ApiEvents::RepositoryTimerHandler(void)
+{
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return;
+
+       Dictionary::Ptr repository = make_shared<Dictionary>();
+
+       BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjects<Host>()) {
+               Array::Ptr services = make_shared<Array>();
+
+               BOOST_FOREACH(const Service::Ptr& service, host->GetServices()) {
+                       services->Add(service->GetName());
+               }
+
+               repository->Set(host->GetName(), services);
+       }
+
+       Endpoint::Ptr my_endpoint = Endpoint::GetLocalEndpoint();
+       Zone::Ptr my_zone = my_endpoint->GetZone();
+
+       Dictionary::Ptr params = make_shared<Dictionary>();
+       params->Set("endpoint", my_endpoint->GetName());
+
+       Zone::Ptr parent_zone = my_zone->GetParent();
+       if (parent_zone)
+               params->Set("parent_zone", parent_zone->GetName());
+
+       params->Set("zone", my_zone->GetName());
+       params->Set("repository", repository);
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::UpdateRepository");
+       message->Set("params", params);
+
+       listener->RelayMessage(MessageOrigin(), my_zone, message, true);
+}
+
+String ApiEvents::GetRepositoryDir(void)
+{
+       return Application::GetLocalStateDir() + "/lib/icinga2/api/repository/";
+}
+
+Value ApiEvents::UpdateRepositoryAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       Zone::Ptr zone = Zone::GetByName(params->Get("zone"));
+
+       if (!zone || (origin.FromZone && !origin.FromZone->CanAccessObject(zone)))
+           return Empty;
+
+       String repositoryFile = GetRepositoryDir() + SHA256(params->Get("endpoint"));
+       String repositoryTempFile = repositoryFile + ".tmp";
+
+       std::ofstream fp(repositoryTempFile.CStr(), std::ofstream::out | std::ostream::trunc);
+       fp << JsonSerialize(params);
+       fp.close();
+
+#ifdef _WIN32
+       _unlink(inventoryFile.CStr());
+#endif /* _WIN32 */
+
+       if (rename(repositoryTempFile.CStr(), repositoryFile.CStr()) < 0) {
+               BOOST_THROW_EXCEPTION(posix_error()
+                   << boost::errinfo_api_function("rename")
+                   << boost::errinfo_errno(errno)
+                   << boost::errinfo_file_name(repositoryTempFile));
+       }
+
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return Empty;
+
+       Dictionary::Ptr message = make_shared<Dictionary>();
+       message->Set("jsonrpc", "2.0");
+       message->Set("method", "event::UpdateRepository");
+       message->Set("params", params);
+
+       listener->RelayMessage(origin, Zone::GetLocalZone(), message, true);
+
+       return Empty;
+}
diff --git a/lib/icinga/apievents.h b/lib/icinga/apievents.h
new file mode 100644 (file)
index 0000000..584cc30
--- /dev/null
@@ -0,0 +1,93 @@
+/******************************************************************************
+ * 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.             *
+ ******************************************************************************/
+
+#ifndef APIEVENTS_H
+#define APIEVENTS_H
+
+#include "icinga/checkable.h"
+#include "remote/apiclient.h"
+#include "base/stream.h"
+#include "base/timer.h"
+#include "base/array.h"
+#include <boost/signals2.hpp>
+
+namespace icinga
+{
+
+/**
+ * @ingroup icinga
+ */
+class I2_ICINGA_API ApiEvents
+{
+public:
+       static void StaticInitialize(void);
+
+       static void CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const MessageOrigin& origin);
+       static Value CheckResultAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
+       static void NextCheckChangedHandler(const Checkable::Ptr& checkable, double nextCheck, const MessageOrigin& origin);
+       static Value NextCheckChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
+       static void NextNotificationChangedHandler(const Notification::Ptr& notification, double nextCheck, const MessageOrigin& origin);
+       static Value NextNotificationChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
+       static void ForceNextCheckChangedHandler(const Checkable::Ptr& checkable, bool forced, const MessageOrigin& origin);
+       static Value ForceNextCheckChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
+       static void ForceNextNotificationChangedHandler(const Checkable::Ptr& checkable, bool forced, const MessageOrigin& origin);
+       static Value ForceNextNotificationChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
+       static void EnableActiveChecksChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin);
+       static Value EnableActiveChecksChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
+       static void EnablePassiveChecksChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin);
+       static Value EnablePassiveChecksChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
+       static void EnableNotificationsChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin);
+       static Value EnableNotificationsChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
+       static void EnableFlappingChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin);
+       static Value EnableFlappingChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
+       static void CommentAddedHandler(const Checkable::Ptr& checkable, const Comment::Ptr& comment, const MessageOrigin& origin);
+       static Value CommentAddedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
+       static void CommentRemovedHandler(const Checkable::Ptr& checkable, const Comment::Ptr& comment, const MessageOrigin& origin);
+       static Value CommentRemovedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
+       static void DowntimeAddedHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, const MessageOrigin& origin);
+       static Value DowntimeAddedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
+       static void DowntimeRemovedHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, const MessageOrigin& origin);
+       static Value DowntimeRemovedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
+       static void AcknowledgementSetHandler(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, double expiry, const MessageOrigin& origin);
+       static Value AcknowledgementSetAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
+       static void AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const MessageOrigin& origin);
+       static Value AcknowledgementClearedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+
+       static String GetRepositoryDir(void);
+       static void RepositoryTimerHandler(void);
+       static Value UpdateRepositoryAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+};
+
+}
+
+#endif /* APIEVENTS_H */
index 3194b3e563ac726f8c01f08a4567fad82cbc22f2..31e2ca1777bbe595bfc469bd0b3f5d61d631d662 100644 (file)
@@ -21,6 +21,7 @@
 #include "icinga/checkcommand.h"
 #include "icinga/icingaapplication.h"
 #include "icinga/cib.h"
+#include "remote/apilistener.h"
 #include "base/dynamictype.h"
 #include "base/objectlock.h"
 #include "base/logger_fwd.h"
 
 using namespace icinga;
 
-boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, const String&)> Checkable::OnNewCheckResult;
-boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, StateType, const String&)> Checkable::OnStateChange;
+boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, const MessageOrigin&)> Checkable::OnNewCheckResult;
+boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, StateType, const MessageOrigin&)> Checkable::OnStateChange;
 boost::signals2::signal<void (const Checkable::Ptr&, NotificationType, const CheckResult::Ptr&, const String&, const String&)> Checkable::OnNotificationsRequested;
-boost::signals2::signal<void (const Checkable::Ptr&, double, const String&)> Checkable::OnNextCheckChanged;
-boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> Checkable::OnForceNextCheckChanged;
-boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> Checkable::OnForceNextNotificationChanged;
-boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> Checkable::OnEnableActiveChecksChanged;
-boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> Checkable::OnEnablePassiveChecksChanged;
-boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> Checkable::OnEnableNotificationsChanged;
-boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> Checkable::OnEnableFlappingChanged;
+boost::signals2::signal<void (const Checkable::Ptr&, double, const MessageOrigin&)> Checkable::OnNextCheckChanged;
+boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> Checkable::OnForceNextCheckChanged;
+boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> Checkable::OnForceNextNotificationChanged;
+boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> Checkable::OnEnableActiveChecksChanged;
+boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> Checkable::OnEnablePassiveChecksChanged;
+boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> Checkable::OnEnableNotificationsChanged;
+boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> Checkable::OnEnableFlappingChanged;
 boost::signals2::signal<void (const Checkable::Ptr&, FlappingState)> Checkable::OnFlappingChanged;
 
 CheckCommand::Ptr Checkable::GetCheckCommand(void) const
@@ -115,11 +116,11 @@ long Checkable::GetSchedulingOffset(void)
        return m_SchedulingOffset;
 }
 
-void Checkable::SetNextCheck(double nextCheck, const String& authority)
+void Checkable::SetNextCheck(double nextCheck, const MessageOrigin& origin)
 {
        SetNextCheckRaw(nextCheck);
 
-       OnNextCheckChanged(GetSelf(), nextCheck, authority);
+       OnNextCheckChanged(GetSelf(), nextCheck, origin);
 }
 
 double Checkable::GetNextCheck(void)
@@ -171,11 +172,11 @@ bool Checkable::GetEnableActiveChecks(void) const
                return GetEnableActiveChecksRaw();
 }
 
-void Checkable::SetEnableActiveChecks(bool enabled, const String& authority)
+void Checkable::SetEnableActiveChecks(bool enabled, const MessageOrigin& origin)
 {
        SetOverrideEnableActiveChecks(enabled);
 
-       OnEnableActiveChecksChanged(GetSelf(), enabled, authority);
+       OnEnableActiveChecksChanged(GetSelf(), enabled, origin);
 }
 
 bool Checkable::GetEnablePassiveChecks(void) const
@@ -186,11 +187,11 @@ bool Checkable::GetEnablePassiveChecks(void) const
                return GetEnablePassiveChecksRaw();
 }
 
-void Checkable::SetEnablePassiveChecks(bool enabled, const String& authority)
+void Checkable::SetEnablePassiveChecks(bool enabled, const MessageOrigin& origin)
 {
        SetOverrideEnablePassiveChecks(enabled);
 
-       OnEnablePassiveChecksChanged(GetSelf(), enabled, authority);
+       OnEnablePassiveChecksChanged(GetSelf(), enabled, origin);
 }
 
 bool Checkable::GetForceNextCheck(void) const
@@ -198,11 +199,11 @@ bool Checkable::GetForceNextCheck(void) const
        return GetForceNextCheckRaw();
 }
 
-void Checkable::SetForceNextCheck(bool forced, const String& authority)
+void Checkable::SetForceNextCheck(bool forced, const MessageOrigin& origin)
 {
        SetForceNextCheckRaw(forced);
 
-       OnForceNextCheckChanged(GetSelf(), forced, authority);
+       OnForceNextCheckChanged(GetSelf(), forced, origin);
 }
 
 int Checkable::GetMaxCheckAttempts(void) const
@@ -218,7 +219,7 @@ void Checkable::SetMaxCheckAttempts(int attempts)
        SetOverrideMaxCheckAttempts(attempts);
 }
 
-void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const String& authority)
+void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrigin& origin)
 {
        {
                ObjectLock olock(this);
@@ -239,7 +240,7 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const String& aut
        if (cr->GetExecutionEnd() == 0)
                cr->SetExecutionEnd(now);
 
-       if (authority.IsEmpty())
+       if (origin.IsLocal())
                cr->SetCheckSource(IcingaApplication::GetInstance()->GetNodeName());
 
        bool reachable = IsReachable();
@@ -399,15 +400,15 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const String& aut
 //                     " threshold: " + Convert::ToString(GetFlappingThreshold()) +
 //                     "% current: " + Convert::ToString(GetFlappingCurrent()) + "%.");
 
-       OnNewCheckResult(GetSelf(), cr, authority);
+       OnNewCheckResult(GetSelf(), cr, origin);
 
        /* signal status updates to for example db_ido */
-       OnStateChanged(GetSelf(), authority);
+       OnStateChanged(GetSelf());
 
        if (hardChange)
-               OnStateChange(GetSelf(), cr, StateTypeHard, authority);
+               OnStateChange(GetSelf(), cr, StateTypeHard, origin);
        else if (stateChange)
-               OnStateChange(GetSelf(), cr, StateTypeSoft, authority);
+               OnStateChange(GetSelf(), cr, StateTypeSoft, origin);
 
        if (GetStateType() == StateTypeSoft || hardChange || recovery)
                ExecuteEventHandler();
index e6e28b81e7cd200e3b60f5eb1cdb2d97e04de397..7d4bb4fe3040237569a3cb353f9f943fa8a0a80f 100644 (file)
@@ -33,8 +33,8 @@ static std::map<int, String> l_LegacyCommentsCache;
 static std::map<String, Checkable::WeakPtr> l_CommentsCache;
 static Timer::Ptr l_CommentsExpireTimer;
 
-boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const String&)> Checkable::OnCommentAdded;
-boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const String&)> Checkable::OnCommentRemoved;
+boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const MessageOrigin&)> Checkable::OnCommentAdded;
+boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const MessageOrigin&)> Checkable::OnCommentRemoved;
 
 int Checkable::GetNextCommentID(void)
 {
@@ -44,7 +44,7 @@ int Checkable::GetNextCommentID(void)
 }
 
 String Checkable::AddComment(CommentType entryType, const String& author,
-    const String& text, double expireTime, const String& id, const String& authority)
+    const String& text, double expireTime, const String& id, const MessageOrigin& origin)
 {
        String uid;
 
@@ -78,7 +78,7 @@ String Checkable::AddComment(CommentType entryType, const String& author,
                l_CommentsCache[uid] = GetSelf();
        }
 
-       OnCommentAdded(GetSelf(), comment, authority);
+       OnCommentAdded(GetSelf(), comment, origin);
 
        return uid;
 }
@@ -100,7 +100,7 @@ void Checkable::RemoveAllComments(void)
        }
 }
 
-void Checkable::RemoveComment(const String& id, const String& authority)
+void Checkable::RemoveComment(const String& id, const MessageOrigin& origin)
 {
        Checkable::Ptr owner = GetOwnerByCommentID(id);
 
@@ -126,7 +126,7 @@ void Checkable::RemoveComment(const String& id, const String& authority)
                l_CommentsCache.erase(id);
        }
 
-       OnCommentRemoved(owner, comment, authority);
+       OnCommentRemoved(owner, comment, origin);
 }
 
 String Checkable::GetCommentIDFromLegacyID(int id)
index 2fea94069f2897da7bb597f2eb7096cc4950665f..62fc77d476c41bced14029a710652438a40c374c 100644 (file)
@@ -35,8 +35,8 @@ static std::map<int, String> l_LegacyDowntimesCache;
 static std::map<String, Checkable::WeakPtr> l_DowntimesCache;
 static Timer::Ptr l_DowntimesExpireTimer;
 
-boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const String&)> Checkable::OnDowntimeAdded;
-boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const String&)> Checkable::OnDowntimeRemoved;
+boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const MessageOrigin&)> Checkable::OnDowntimeAdded;
+boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const MessageOrigin&)> Checkable::OnDowntimeRemoved;
 boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&)> Checkable::OnDowntimeTriggered;
 
 int Checkable::GetNextDowntimeID(void)
@@ -49,7 +49,7 @@ int Checkable::GetNextDowntimeID(void)
 String Checkable::AddDowntime(const String& author, const String& comment,
     double startTime, double endTime, bool fixed,
     const String& triggeredBy, double duration, const String& scheduledBy,
-    const String& id, const String& authority)
+    const String& id, const MessageOrigin& origin)
 {
        String uid;
 
@@ -106,12 +106,12 @@ String Checkable::AddDowntime(const String& author, const String& comment,
        Log(LogDebug, "icinga", "Added downtime with ID '" + Convert::ToString(downtime->GetLegacyId()) +
            "' between '" + Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", startTime) + "' and '" + Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", endTime) + "'.");
 
-       OnDowntimeAdded(GetSelf(), downtime, authority);
+       OnDowntimeAdded(GetSelf(), downtime, origin);
 
        return uid;
 }
 
-void Checkable::RemoveDowntime(const String& id, bool cancelled, const String& authority)
+void Checkable::RemoveDowntime(const String& id, bool cancelled, const MessageOrigin& origin)
 {
        Checkable::Ptr owner = GetOwnerByDowntimeID(id);
 
@@ -146,7 +146,7 @@ void Checkable::RemoveDowntime(const String& id, bool cancelled, const String& a
 
        Log(LogDebug, "icinga", "Removed downtime with ID '" + Convert::ToString(downtime->GetLegacyId()) + "' from service '" + owner->GetName() + "'.");
 
-       OnDowntimeRemoved(owner, downtime, authority);
+       OnDowntimeRemoved(owner, downtime, origin);
 }
 
 void Checkable::TriggerDowntimes(void)
index bcc8fe6cf6929ca57ea31289cf8976ba267a3a75..5bc71fb82a101b6da456efe175fb97f4c15e404d 100644 (file)
@@ -47,12 +47,12 @@ bool Checkable::GetEnableFlapping(void) const
                return GetEnableFlappingRaw();
 }
 
-void Checkable::SetEnableFlapping(bool enabled, const String& authority)
+void Checkable::SetEnableFlapping(bool enabled, const MessageOrigin& origin)
 {
        SetOverrideEnableFlapping(enabled);
 
        OnFlappingChanged(GetSelf(), enabled ? FlappingEnabled : FlappingDisabled);
-       OnEnableFlappingChanged(GetSelf(), enabled, authority);
+       OnEnableFlappingChanged(GetSelf(), enabled, origin);
 }
 
 void Checkable::UpdateFlappingStatus(bool stateChange)
index 5b44e6d3b4dfb4694651ea818336c2f4cd5e22a8..ef3667bc338f2b0fc5aaf726b4d3bb9990f939a2 100644 (file)
@@ -108,11 +108,11 @@ bool Checkable::GetEnableNotifications(void) const
                return GetEnableNotificationsRaw();
 }
 
-void Checkable::SetEnableNotifications(bool enabled, const String& authority)
+void Checkable::SetEnableNotifications(bool enabled, const MessageOrigin& origin)
 {
        SetOverrideEnableNotifications(enabled);
 
-       OnEnableNotificationsChanged(GetSelf(), enabled, authority);
+       OnEnableNotificationsChanged(GetSelf(), enabled, origin);
 }
 
 bool Checkable::GetForceNextNotification(void) const
@@ -120,9 +120,9 @@ bool Checkable::GetForceNextNotification(void) const
        return GetForceNextNotificationRaw();
 }
 
-void Checkable::SetForceNextNotification(bool forced, const String& authority)
+void Checkable::SetForceNextNotification(bool forced, const MessageOrigin& origin)
 {
        SetForceNextNotificationRaw(forced);
 
-       OnForceNextNotificationChanged(GetSelf(), forced, authority);
+       OnForceNextNotificationChanged(GetSelf(), forced, origin);
 }
index c1d1877359e636c58983a7fe16ee498e8d7f8da2..9daf9c88f934ddcaad61fef2068799a7a1ba438c 100644 (file)
@@ -39,8 +39,8 @@ REGISTER_TYPE(Checkable);
 
 INITIALIZE_ONCE(&Checkable::StartDowntimesExpiredTimer);
 
-boost::signals2::signal<void (const Checkable::Ptr&, const String&, const String&, AcknowledgementType, double, const String&)> Checkable::OnAcknowledgementSet;
-boost::signals2::signal<void (const Checkable::Ptr&, const String&)> Checkable::OnAcknowledgementCleared;
+boost::signals2::signal<void (const Checkable::Ptr&, const String&, const String&, AcknowledgementType, double, const MessageOrigin&)> Checkable::OnAcknowledgementSet;
+boost::signals2::signal<void (const Checkable::Ptr&, const MessageOrigin&)> Checkable::OnAcknowledgementCleared;
 
 Checkable::Checkable(void)
        : m_CheckRunning(false)
@@ -129,7 +129,7 @@ bool Checkable::IsAcknowledged(void)
        return GetAcknowledgement() != AcknowledgementNone;
 }
 
-void Checkable::AcknowledgeProblem(const String& author, const String& comment, AcknowledgementType type, double expiry, const String& authority)
+void Checkable::AcknowledgeProblem(const String& author, const String& comment, AcknowledgementType type, double expiry, const MessageOrigin& origin)
 {
        {
                ObjectLock olock(this);
@@ -140,17 +140,17 @@ void Checkable::AcknowledgeProblem(const String& author, const String& comment,
 
        OnNotificationsRequested(GetSelf(), NotificationAcknowledgement, GetLastCheckResult(), author, comment);
 
-       OnAcknowledgementSet(GetSelf(), author, comment, type, expiry, authority);
+       OnAcknowledgementSet(GetSelf(), author, comment, type, expiry, origin);
 }
 
-void Checkable::ClearAcknowledgement(const String& authority)
+void Checkable::ClearAcknowledgement(const MessageOrigin& origin)
 {
        ASSERT(OwnsLock());
 
        SetAcknowledgementRaw(AcknowledgementNone);
        SetAcknowledgementExpiry(0);
 
-       OnAcknowledgementCleared(GetSelf(), authority);
+       OnAcknowledgementCleared(GetSelf(), origin);
 }
 
 bool Checkable::GetEnablePerfdata(void) const
@@ -161,7 +161,7 @@ bool Checkable::GetEnablePerfdata(void) const
                return GetEnablePerfdataRaw();
 }
 
-void Checkable::SetEnablePerfdata(bool enabled, const String& authority)
+void Checkable::SetEnablePerfdata(bool enabled, const MessageOrigin& origin)
 {
        SetOverrideEnablePerfdata(enabled);
 }
index e068d2477c04295de630eaa9f6b1267881f4429b..f999a71b1393603b3df7d3e1e8ffdcd529854b18 100644 (file)
@@ -27,6 +27,7 @@
 #include "icinga/notification.h"
 #include "icinga/comment.h"
 #include "icinga/downtime.h"
+#include "remote/messageorigin.h"
 #include "base/i2-base.h"
 #include "base/array.h"
 #include <boost/signals2.hpp>
@@ -97,8 +98,8 @@ public:
 
        AcknowledgementType GetAcknowledgement(void);
 
-       void AcknowledgeProblem(const String& author, const String& comment, AcknowledgementType type, double expiry = 0, const String& authority = String());
-       void ClearAcknowledgement(const String& authority = String());
+       void AcknowledgeProblem(const String& author, const String& comment, AcknowledgementType type, double expiry = 0, const MessageOrigin& origin = MessageOrigin());
+       void ClearAcknowledgement(const MessageOrigin& origin = MessageOrigin());
 
        /* Checks */
        shared_ptr<CheckCommand> GetCheckCommand(void) const;
@@ -119,7 +120,7 @@ public:
        long GetSchedulingOffset(void);
        void SetSchedulingOffset(long offset);
 
-       void SetNextCheck(double nextCheck, const String& authority = String());
+       void SetNextCheck(double nextCheck, const MessageOrigin& origin = MessageOrigin());
        double GetNextCheck(void);
        void UpdateNextCheck(void);
 
@@ -128,18 +129,18 @@ public:
        double GetLastCheck(void) const;
 
        bool GetEnableActiveChecks(void) const;
-       void SetEnableActiveChecks(bool enabled, const String& authority = String());
+       void SetEnableActiveChecks(bool enabled, const MessageOrigin& origin = MessageOrigin());
 
        bool GetEnablePassiveChecks(void) const;
-       void SetEnablePassiveChecks(bool enabled, const String& authority = String());
+       void SetEnablePassiveChecks(bool enabled, const MessageOrigin& origin = MessageOrigin());
 
        bool GetForceNextCheck(void) const;
-       void SetForceNextCheck(bool forced, const String& authority = String());
+       void SetForceNextCheck(bool forced, const MessageOrigin& origin = MessageOrigin());
 
        static void UpdateStatistics(const CheckResult::Ptr& cr);
 
        void ExecuteCheck(void);
-       void ProcessCheckResult(const CheckResult::Ptr& cr, const String& authority = String());
+       void ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrigin& origin = MessageOrigin());
 
        int GetModifiedAttributes(void) const;
        void SetModifiedAttributes(int flags);
@@ -149,15 +150,15 @@ public:
        static double CalculateExecutionTime(const CheckResult::Ptr& cr);
        static double CalculateLatency(const CheckResult::Ptr& cr);
 
-       static boost::signals2::signal<void (const Checkable::Ptr&, double, const String&)> OnNextCheckChanged;
-       static boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> OnForceNextCheckChanged;
-       static boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> OnForceNextNotificationChanged;
-       static boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> OnEnableActiveChecksChanged;
-       static boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> OnEnablePassiveChecksChanged;
-       static boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> OnEnableNotificationsChanged;
-       static boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> OnEnableFlappingChanged;
-       static boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, const String&)> OnNewCheckResult;
-       static boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, StateType, const String&)> OnStateChange;
+       static boost::signals2::signal<void (const Checkable::Ptr&, double, const MessageOrigin&)> OnNextCheckChanged;
+       static boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> OnForceNextCheckChanged;
+       static boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> OnForceNextNotificationChanged;
+       static boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> OnEnableActiveChecksChanged;
+       static boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> OnEnablePassiveChecksChanged;
+       static boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> OnEnableNotificationsChanged;
+       static boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> OnEnableFlappingChanged;
+       static boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, const MessageOrigin&)> OnNewCheckResult;
+       static boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, StateType, const MessageOrigin&)> OnStateChange;
        static boost::signals2::signal<void (const Checkable::Ptr&, NotificationType, const CheckResult::Ptr&,
            const String&, const String&)> OnNotificationsRequested;
        static boost::signals2::signal<void (const Notification::Ptr&, const Checkable::Ptr&, const std::set<User::Ptr>&,
@@ -169,15 +170,15 @@ public:
        static boost::signals2::signal<void (const Notification::Ptr&, const Checkable::Ptr&, const std::set<User::Ptr>&,
            const NotificationType&, const CheckResult::Ptr&, const String&,
            const String&)> OnNotificationSentToAllUsers;
-       static boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const String&)> OnCommentAdded;
-       static boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const String&)> OnCommentRemoved;
-       static boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const String&)> OnDowntimeAdded;
-       static boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const String&)> OnDowntimeRemoved;
+       static boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const MessageOrigin&)> OnCommentAdded;
+       static boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const MessageOrigin&)> OnCommentRemoved;
+       static boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const MessageOrigin&)> OnDowntimeAdded;
+       static boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const MessageOrigin&)> OnDowntimeRemoved;
        static boost::signals2::signal<void (const Checkable::Ptr&, FlappingState)> OnFlappingChanged;
        static boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&)> OnDowntimeTriggered;
        static boost::signals2::signal<void (const Checkable::Ptr&, const String&, const String&, AcknowledgementType,
-                                            double, const String&)> OnAcknowledgementSet;
-       static boost::signals2::signal<void (const Checkable::Ptr&, const String&)> OnAcknowledgementCleared;
+                                            double, const MessageOrigin&)> OnAcknowledgementSet;
+       static boost::signals2::signal<void (const Checkable::Ptr&, const MessageOrigin&)> OnAcknowledgementCleared;
        static boost::signals2::signal<void (const Checkable::Ptr&)> OnEventCommandExecuted;
 
        /* Downtimes */
@@ -189,9 +190,9 @@ public:
            double startTime, double endTime, bool fixed,
            const String& triggeredBy, double duration,
            const String& scheduledBy = String(), const String& id = String(),
-           const String& authority = String());
+           const MessageOrigin& origin = MessageOrigin());
 
-       static void RemoveDowntime(const String& id, bool cancelled, const String& = String());
+       static void RemoveDowntime(const String& id, bool cancelled, const MessageOrigin& origin = MessageOrigin());
 
         void TriggerDowntimes(void);
        static void TriggerDowntime(const String& id);
@@ -209,11 +210,11 @@ public:
        static int GetNextCommentID(void);
 
        String AddComment(CommentType entryType, const String& author,
-           const String& text, double expireTime, const String& id = String(), const String& authority = String());
+           const String& text, double expireTime, const String& id = String(), const MessageOrigin& origin = MessageOrigin());
 
        void RemoveAllComments(void);
        void RemoveCommentsByType(int type);
-       static void RemoveComment(const String& id, const String& authority = String());
+       static void RemoveComment(const String& id, const MessageOrigin& origin = MessageOrigin());
 
        static String GetCommentIDFromLegacyID(int id);
        static Checkable::Ptr GetOwnerByCommentID(const String& id);
@@ -221,7 +222,7 @@ public:
 
        /* Notifications */
        bool GetEnableNotifications(void) const;
-       void SetEnableNotifications(bool enabled, const String& authority = String());
+       void SetEnableNotifications(bool enabled, const MessageOrigin& origin = MessageOrigin());
 
        void SendNotifications(NotificationType type, const CheckResult::Ptr& cr, const String& author = "", const String& text = "");
 
@@ -229,7 +230,7 @@ public:
        void AddNotification(const Notification::Ptr& notification);
        void RemoveNotification(const Notification::Ptr& notification);
 
-       void SetForceNextNotification(bool force, const String& authority = String());
+       void SetForceNextNotification(bool force, const MessageOrigin& origin = MessageOrigin());
        bool GetForceNextNotification(void) const;
 
        void ResetNotificationNumbers(void);
@@ -247,14 +248,14 @@ public:
        double GetFlappingCurrent(void) const;
 
        bool GetEnableFlapping(void) const;
-       void SetEnableFlapping(bool enabled, const String& authority = String());
+       void SetEnableFlapping(bool enabled, const MessageOrigin& origin = MessageOrigin());
 
        bool IsFlapping(void) const;
        void UpdateFlappingStatus(bool stateChange);
 
        /* Performance data */
        bool GetEnablePerfdata(void) const;
-       void SetEnablePerfdata(bool enabled, const String& authority = String());
+       void SetEnablePerfdata(bool enabled, const MessageOrigin& origin = MessageOrigin());
 
        /* Dependencies */
        void AddDependency(const shared_ptr<Dependency>& dep);
diff --git a/lib/icinga/domain.ti b/lib/icinga/domain.ti
deleted file mode 100644 (file)
index 886f985..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "base/dynamicobject.h"
-
-namespace icinga
-{
-
-class Domain : DynamicObject
-{
-       [config] Dictionary::Ptr acl;
-};
-
-}
index 4b86dacf6867b13dc8c65c4b23bcb3d5b8731b60..09761437cafa2874d6ac9d1c606725a13568b2bf 100644 (file)
@@ -28,6 +28,7 @@
 #include "icinga/checkcommand.h"
 #include "icinga/eventcommand.h"
 #include "icinga/notificationcommand.h"
+#include "remote/apifunction.h"
 #include "base/convert.h"
 #include "base/logger_fwd.h"
 #include "base/objectlock.h"
@@ -41,8 +42,7 @@
 
 using namespace icinga;
 
-static boost::once_flag l_InitializeOnce = BOOST_ONCE_INIT;
-static boost::mutex l_Mutex;
+INITIALIZE_ONCE(&ExternalCommandProcessor::StaticInitialize);
 
 typedef boost::function<void (double time, const std::vector<String>& arguments)> ExternalCommandCallback;
 
@@ -53,18 +53,46 @@ struct ExternalCommandInfo
        size_t MaxArgs;
 };
 
-static std::map<String, ExternalCommandInfo> l_Commands;
+static boost::mutex& GetMutex(void)
+{
+       static boost::mutex mtx;
+       return mtx;
+}
+static std::map<String, ExternalCommandInfo>& GetCommands(void)
+{
+       static std::map<String, ExternalCommandInfo> commands;
+       return commands;
+}
 
 boost::signals2::signal<void (double, const String&, const std::vector<String>&)> ExternalCommandProcessor::OnNewExternalCommand;
 
+static Value ExternalCommandAPIWrapper(const String& command, const Dictionary::Ptr& params)
+{
+       std::vector<String> arguments;
+
+       if (params) {
+               int i = 0;
+               while (params->Contains("arg" + Convert::ToString(i))) {
+                       arguments.push_back(params->Get("arg" + Convert::ToString(i)));
+                       i++;
+               }
+       }
+
+       ExternalCommandProcessor::Execute(Utility::GetTime(), command, arguments);
+       return true;
+}
+
 static void RegisterCommand(const String& command, const ExternalCommandCallback& callback, size_t minArgs = 0, size_t maxArgs = -1)
 {
-       boost::mutex::scoped_lock lock(l_Mutex);
+       boost::mutex::scoped_lock lock(GetMutex());
        ExternalCommandInfo eci;
        eci.Callback = callback;
        eci.MinArgs = minArgs;
        eci.MaxArgs = (maxArgs == -1) ? minArgs : maxArgs;
-       l_Commands[command] = eci;
+       GetCommands()[command] = eci;
+
+       ApiFunction::Ptr afunc = make_shared<ApiFunction>(boost::bind(&ExternalCommandAPIWrapper, command, _2));
+       ApiFunction::Register("extcmd::" + command, afunc);
 }
 
 void ExternalCommandProcessor::Execute(const String& line)
@@ -100,17 +128,15 @@ void ExternalCommandProcessor::Execute(const String& line)
 
 void ExternalCommandProcessor::Execute(double time, const String& command, const std::vector<String>& arguments)
 {
-       boost::call_once(l_InitializeOnce, &ExternalCommandProcessor::Initialize);
-
        ExternalCommandInfo eci;
 
        {
-               boost::mutex::scoped_lock lock(l_Mutex);
+               boost::mutex::scoped_lock lock(GetMutex());
 
                std::map<String, ExternalCommandInfo>::iterator it;
-               it = l_Commands.find(command);
+               it = GetCommands().find(command);
 
-               if (it == l_Commands.end())
+               if (it == GetCommands().end())
                        BOOST_THROW_EXCEPTION(std::invalid_argument("The external command '" + command + "' does not exist."));
 
                eci = it->second;
@@ -143,7 +169,7 @@ void ExternalCommandProcessor::Execute(double time, const String& command, const
        eci.Callback(time, realArguments);
 }
 
-void ExternalCommandProcessor::Initialize(void)
+void ExternalCommandProcessor::StaticInitialize(void)
 {
        RegisterCommand("PROCESS_HOST_CHECK_RESULT", &ExternalCommandProcessor::ProcessHostCheckResult, 3);
        RegisterCommand("PROCESS_SERVICE_CHECK_RESULT", &ExternalCommandProcessor::ProcessServiceCheckResult, 4);
index 70f317cabfe2a241bbecb3567b4c3f8370bbe441..256cf1be6abf941a9f02318ff71de536da87466d 100644 (file)
@@ -37,13 +37,13 @@ public:
        static void Execute(const String& line);
        static void Execute(double time, const String& command, const std::vector<String>& arguments);
 
-        static boost::signals2::signal<void (double, const String&, const std::vector<String>&)> OnNewExternalCommand;
+       static void StaticInitialize(void);
+       
+       static boost::signals2::signal<void(double, const String&, const std::vector<String>&)> OnNewExternalCommand;
 
 private:
        ExternalCommandProcessor(void);
 
-       static void Initialize(void);
-
        static void ProcessHostCheckResult(double time, const std::vector<String>& arguments);
        static void ProcessServiceCheckResult(double time, const std::vector<String>& arguments);
        static void ScheduleHostCheck(double time, const std::vector<String>& arguments);
index 0f00ac1ccb9eb8fa21b729dea14cd0d42c0685f8..9ed16537a5c2b3ce4cde2445441edf7acc0136da 100644 (file)
 
 }
 
-%type Domain {
-       %attribute %dictionary "acl" {
-               %attribute %number "*"
-       }
-}
-
 %type ScheduledDowntime {
        %require "host_name",
        %attribute %name(Host) "host_name",
index a85f4a4c8c5238a55a94a16ccbe3d13264a52102..1e5d38b0891cf09937e20f37e987c9707cf88471 100644 (file)
@@ -39,7 +39,7 @@ REGISTER_TYPE(Notification);
 REGISTER_SCRIPTFUNCTION(ValidateNotificationFilters, &Notification::ValidateFilters);
 INITIALIZE_ONCE(&Notification::StaticInitialize);
 
-boost::signals2::signal<void (const Notification::Ptr&, double, const String&)> Notification::OnNextNotificationChanged;
+boost::signals2::signal<void (const Notification::Ptr&, double, const MessageOrigin&)> Notification::OnNextNotificationChanged;
 
 String NotificationNameComposer::MakeName(const String& shortName, const Dictionary::Ptr props) const
 {
@@ -180,11 +180,11 @@ double Notification::GetNextNotification(void) const
  * Sets the timestamp when the next periodical notification should be sent.
  * This does not affect notifications that are sent for state changes.
  */
-void Notification::SetNextNotification(double time, const String& authority)
+void Notification::SetNextNotification(double time, const MessageOrigin& origin)
 {
        SetNextNotificationRaw(time);
 
-       OnNextNotificationChanged(GetSelf(), time, authority);
+       OnNextNotificationChanged(GetSelf(), time, origin);
 }
 
 void Notification::UpdateNotificationNumber(void)
index 3cd25217575cedd8bba247c31cf6b4e7b63ae57f..b72e9954ea8af34ebdb9ce514dbb84d8d260a7d0 100644 (file)
@@ -25,6 +25,7 @@
 #include "icinga/user.h"
 #include "icinga/usergroup.h"
 #include "icinga/timeperiod.h"
+#include "remote/messageorigin.h"
 #include "config/applyrule.h"
 #include "base/array.h"
 
@@ -86,7 +87,7 @@ public:
        std::set<UserGroup::Ptr> GetUserGroups(void) const;
 
        double GetNextNotification(void) const;
-       void SetNextNotification(double time, const String& authority = String());
+       void SetNextNotification(double time, const MessageOrigin& origin = MessageOrigin());
 
        void UpdateNotificationNumber(void);
        void ResetNotificationNumber(void);
@@ -97,7 +98,7 @@ public:
 
        static String NotificationTypeToString(NotificationType type);
 
-       static boost::signals2::signal<void (const Notification::Ptr&, double, const String&)> OnNextNotificationChanged;
+       static boost::signals2::signal<void (const Notification::Ptr&, double, const MessageOrigin&)> OnNextNotificationChanged;
 
        static void RegisterApplyRuleHandler(void);
 
index 59bc28d3d946f99c6d1f78d489a005c3c74ed952..1ceacf793a2fba9f885b49bf78df3db217885d13 100644 (file)
@@ -22,7 +22,7 @@ else()
 endif()
 
 add_library(methods SHARED
-  castfuncs.cpp icingachecktask.cpp nullchecktask.cpp nulleventtask.cpp
+  castfuncs.cpp clusterchecktask.cpp icingachecktask.cpp nullchecktask.cpp nulleventtask.cpp
   pluginchecktask.cpp plugineventtask.cpp pluginnotificationtask.cpp
   randomchecktask.cpp timeperiodtask.cpp ${WindowsSources}
 )
similarity index 73%
rename from components/cluster/clusterchecktask.cpp
rename to lib/methods/clusterchecktask.cpp
index adb6f953a382cc44382dae684a30d0ac13ccaea1..e5dd39128280afe11a9bbaaee770f7ee9ea14529 100644 (file)
@@ -17,8 +17,8 @@
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
-#include "cluster/clusterchecktask.h"
-#include "cluster/clusterlistener.h"
+#include "methods/clusterchecktask.h"
+#include "remote/apilistener.h"
 #include "remote/endpoint.h"
 #include "icinga/cib.h"
 #include "icinga/service.h"
@@ -35,38 +35,39 @@ using namespace icinga;
 
 REGISTER_SCRIPTFUNCTION(ClusterCheck, &ClusterCheckTask::ScriptFunc);
 
-void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr)
+void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr)
 {
-       /* fetch specific cluster status */
-       std::pair<Dictionary::Ptr, Dictionary::Ptr> stats;
-       BOOST_FOREACH(const ClusterListener::Ptr& cluster_listener, DynamicType::GetObjects<ClusterListener>()) {
-               /* XXX there's only one cluster listener */
-               stats = cluster_listener->GetClusterStatus();
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener) {
+               cr->SetOutput("No API listener is configured for this instance.");
+               cr->SetState(ServiceUnknown);
+               checkable->ProcessCheckResult(cr);
+               return;
        }
 
+       std::pair<Dictionary::Ptr, Dictionary::Ptr> stats = listener->GetStatus();
+
        Dictionary::Ptr status = stats.first;
 
        /* use feature stats perfdata */
        std::pair<Dictionary::Ptr, Dictionary::Ptr> feature_stats = CIB::GetFeatureStats();
-       Dictionary::Ptr perfdata = feature_stats.second;
+       cr->SetPerformanceData(feature_stats.second);
 
        String connected_endpoints = FormatArray(status->Get("conn_endpoints"));
        String not_connected_endpoints = FormatArray(status->Get("not_conn_endpoints"));
 
-       ServiceState state = ServiceOK;
-       String output = "Icinga 2 Cluster is running: Connected Endpoints: "+ Convert::ToString(status->Get("num_conn_endpoints")) + " (" +
-           connected_endpoints + ").";
-
        if (status->Get("num_not_conn_endpoints") > 0) {
-               state = ServiceCritical;
-               output = "Icinga 2 Cluster Problem: " + Convert::ToString(status->Get("num_not_conn_endpoints")) +
-                   " Endpoints (" + not_connected_endpoints + ") not connected.";
+               cr->SetState(ServiceCritical);
+               cr->SetOutput("Icinga 2 Cluster is running: Connected Endpoints: "+ Convert::ToString(status->Get("num_conn_endpoints")) + " (" +
+           connected_endpoints + ").");
+       } else {
+               cr->SetState(ServiceOK);
+               cr->SetOutput("Icinga 2 Cluster Problem: " + Convert::ToString(status->Get("num_not_conn_endpoints")) +
+                   " Endpoints (" + not_connected_endpoints + ") not connected.");
        }
 
-       cr->SetOutput(output);
-       cr->SetPerformanceData(perfdata);
-       cr->SetState(state);
-       service->ProcessCheckResult(cr);
+       checkable->ProcessCheckResult(cr);
 }
 
 String ClusterCheckTask::FormatArray(const Array::Ptr& arr)
@@ -88,4 +89,3 @@ String ClusterCheckTask::FormatArray(const Array::Ptr& arr)
 
        return str;
 }
-
index 367fdfb92764a08d6cdc6da44ca2b14c4ad70085..9a8400deaf0fdd359da8ecccda75e397aea39624 100644 (file)
 # along with this program; if not, write to the Free Software Foundation
 # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
+mkclass_target(apilistener.ti apilistener.th)
 mkclass_target(endpoint.ti endpoint.th)
+mkclass_target(zone.ti zone.th)
 
 mkembedconfig_target(remote-type.conf remote-type.cpp)
 
 add_library(remote SHARED
-  endpoint.cpp endpoint.th jsonrpc.cpp remote-type.cpp
+  apiclient.cpp apifunction.cpp apilistener.cpp apilistener.th endpoint.cpp
+  endpoint.th jsonrpc.cpp messageorigin.cpp remote-type.cpp zone.cpp zone.th
 )
 
 include_directories(${Boost_INCLUDE_DIRS})
@@ -39,4 +42,7 @@ install(
   LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2
 )
 
+#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\")")
 
diff --git a/lib/remote/apiclient.cpp b/lib/remote/apiclient.cpp
new file mode 100644 (file)
index 0000000..cc4bb43
--- /dev/null
@@ -0,0 +1,200 @@
+/******************************************************************************
+ * 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/apiclient.h"
+#include "remote/apilistener.h"
+#include "remote/apifunction.h"
+#include "remote/jsonrpc.h"
+#include "base/application.h"
+#include "base/dynamictype.h"
+#include "base/objectlock.h"
+#include "base/utility.h"
+#include "base/logger_fwd.h"
+#include "base/exception.h"
+#include "base/initialize.h"
+#include "config/configitembuilder.h"
+
+using namespace icinga;
+
+Timer::Ptr ApiClient::m_KeepAliveTimer;
+
+INITIALIZE_ONCE(&ApiClient::StaticInitialize);
+
+static Value SetLogPositionHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
+REGISTER_APIFUNCTION(SetLogPosition, log, &SetLogPositionHandler);
+
+ApiClient::ApiClient(const Endpoint::Ptr& endpoint, const Stream::Ptr& stream, ConnectionRole role)
+       : m_Endpoint(endpoint), m_Stream(stream), m_Role(role), m_Seen(Utility::GetTime())
+{ }
+
+void ApiClient::StaticInitialize(void)
+{
+       m_KeepAliveTimer = make_shared<Timer>();
+       m_KeepAliveTimer->OnTimerExpired.connect(boost::bind(&ApiClient::KeepAliveTimerHandler));
+       m_KeepAliveTimer->SetInterval(5);
+       m_KeepAliveTimer->Start();
+}
+
+void ApiClient::Start(void)
+{
+       boost::thread thread(boost::bind(&ApiClient::MessageThreadProc, static_cast<ApiClient::Ptr>(GetSelf())));
+       thread.detach();
+}
+
+Endpoint::Ptr ApiClient::GetEndpoint(void) const
+{
+       return m_Endpoint;
+}
+
+Stream::Ptr ApiClient::GetStream(void) const
+{
+       return m_Stream;
+}
+
+ConnectionRole ApiClient::GetRole(void) const
+{
+       return m_Role;
+}
+
+void ApiClient::SendMessage(const Dictionary::Ptr& message)
+{
+       try {
+               ObjectLock olock(m_Stream);
+               JsonRpc::SendMessage(m_Stream, message);
+               if (message->Get("method") != "log::SetLogPosition")
+                       m_Seen = Utility::GetTime();
+       } catch (const std::exception& ex) {
+               std::ostringstream msgbuf;
+               msgbuf << "Error while sending JSON-RPC message for endpoint '" << m_Endpoint->GetName() << "': " << DiagnosticInformation(ex);
+               Log(LogWarning, "remote", msgbuf.str());
+
+               Disconnect();
+       }
+}
+
+void ApiClient::Disconnect(void)
+{
+       Log(LogWarning, "remote", "API client disconnected for endpoint '" + m_Endpoint->GetName() + "'");
+       m_Stream->Close();
+       m_Endpoint->RemoveClient(GetSelf());
+}
+
+bool ApiClient::ProcessMessage(void)
+{
+       Dictionary::Ptr message = JsonRpc::ReadMessage(m_Stream);
+
+       if (!message)
+               return false;
+
+       if (message->Get("method") != "log::SetLogPosition")
+               m_Seen = Utility::GetTime();
+
+       if (message->Contains("ts")) {
+               double ts = message->Get("ts");
+
+               /* ignore old messages */
+               if (ts < m_Endpoint->GetRemoteLogPosition())
+                       return true;
+
+               m_Endpoint->SetRemoteLogPosition(ts);
+       }
+
+       MessageOrigin origin;
+       origin.FromClient = GetSelf();
+
+       if (m_Endpoint->GetZone() != Zone::GetLocalZone())
+               origin.FromZone = m_Endpoint->GetZone();
+       else
+               origin.FromZone = Zone::GetByName(message->Get("originZone"));
+
+       String method = message->Get("method");
+
+       Log(LogDebug, "remote", "Received '" + method + "' message from '" + m_Endpoint->GetName() + "'");
+
+       Dictionary::Ptr resultMessage = make_shared<Dictionary>();
+
+       try {
+               ApiFunction::Ptr afunc = ApiFunction::GetByName(method);
+
+               if (!afunc)
+                       BOOST_THROW_EXCEPTION(std::invalid_argument("Function '" + method + "' does not exist."));
+
+               resultMessage->Set("result", afunc->Invoke(origin, message->Get("params")));
+       } catch (std::exception& ex) {
+               resultMessage->Set("error", DiagnosticInformation(ex));
+       }
+
+       if (message->Contains("id")) {
+               resultMessage->Set("jsonrpc", "2.0");
+               resultMessage->Set("id", message->Get("id"));
+               JsonRpc::SendMessage(m_Stream, resultMessage);
+       }
+
+       return true;
+}
+
+void ApiClient::MessageThreadProc(void)
+{
+       Utility::SetThreadName("API Client");
+
+       try {
+               while (ProcessMessage())
+                       ; /* empty loop body */
+
+               Disconnect();
+       } catch (const std::exception& ex) {
+               Log(LogWarning, "remote", "Error while reading JSON-RPC message for endpoint '" + m_Endpoint->GetName() + "': " + DiagnosticInformation(ex));
+       }
+}
+
+void ApiClient::KeepAliveTimerHandler(void)
+{
+       double now = Utility::GetTime();
+
+       BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
+               if (endpoint->GetZone() == Zone::GetLocalZone())
+                       continue;
+
+               if (endpoint->GetSyncing() || endpoint->GetKeepAlive() <= 0)
+                       continue;
+
+               double timeout = now - endpoint->GetKeepAlive();
+
+               BOOST_FOREACH(const ApiClient::Ptr& client, endpoint->GetClients()) {
+                       if (client->m_Seen < timeout) {
+                               Log(LogInformation, "remote", "Closing connection with inactive endpoint '" + endpoint->GetName() + "'");
+                               client->Disconnect();
+                       }
+               }
+       }
+}
+
+Value SetLogPositionHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
+{
+       if (!params)
+               return Empty;
+
+       double log_position = params->Get("log_position");
+       Endpoint::Ptr endpoint = origin.FromClient->GetEndpoint();
+
+       if (log_position > endpoint->GetLocalLogPosition())
+               endpoint->SetLocalLogPosition(log_position);
+
+       return Empty;
+}
similarity index 62%
rename from components/agent/agentchecktask.h
rename to lib/remote/apiclient.h
index e35a34e5cd690c4d7a3e796e1ce9c5346a86fde3..a9e13210e56ebc5711a17ba7425bad611b2d25a4 100644 (file)
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
-#ifndef AGENTCHECKTASK_H
-#define AGENTCHECKTASK_H
+#ifndef APICLIENT_H
+#define APICLIENT_H
 
-#include "icinga/service.h"
+#include "remote/endpoint.h"
+#include "base/stream.h"
 #include "base/timer.h"
+#include "base/array.h"
+#include "remote/i2-remote.h"
+#include <boost/signals2.hpp>
 
 namespace icinga
 {
 
+enum ClientRole
+{
+       ClientInbound,
+       ClientOutbound
+};
+
 /**
- * Agent check type.
+ * An API client connection.
  *
- * @ingroup methods
+ * @ingroup remote
  */
-class AgentCheckTask
+class I2_REMOTE_API ApiClient : public Object
 {
 public:
+       DECLARE_PTR_TYPEDEFS(ApiClient);
+
+       ApiClient(const Endpoint::Ptr& endpoint, const Stream::Ptr& stream, ConnectionRole role);
+
        static void StaticInitialize(void);
-       static void ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr);
+
+       void Start(void);
+
+       Endpoint::Ptr GetEndpoint(void) const;
+       Stream::Ptr GetStream(void) const;
+       ConnectionRole GetRole(void) const;
+
+       void Disconnect(void);
+
+       void SendMessage(const Dictionary::Ptr& request);
 
 private:
-       AgentCheckTask(void);
-       
-       static void AgentTimerHandler(void);
+       Endpoint::Ptr m_Endpoint;
+       Stream::Ptr m_Stream;
+       ConnectionRole m_Role;
+       double m_Seen;
+       bool m_Syncing;
+
+       bool ProcessMessage(void);
+       void MessageThreadProc(void);
 
-       static bool SendResult(const Checkable::Ptr& checkable, bool enqueue);
+       static Timer::Ptr m_KeepAliveTimer;
+       static void KeepAliveTimerHandler(void);
 };
 
 }
 
-#endif /* AGENTCHECKTASK_H */
+#endif /* APICLIENT_H */
diff --git a/lib/remote/apifunction.cpp b/lib/remote/apifunction.cpp
new file mode 100644 (file)
index 0000000..f14bde0
--- /dev/null
@@ -0,0 +1,59 @@
+/******************************************************************************
+ * 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/apifunction.h"
+#include "base/registry.h"
+#include "base/singleton.h"
+
+using namespace icinga;
+
+ApiFunction::ApiFunction(const Callback& function)
+: m_Callback(function)
+{ }
+
+Value ApiFunction::Invoke(const MessageOrigin& origin, const Dictionary::Ptr& arguments)
+{
+       return m_Callback(origin, arguments);
+}
+
+RegisterApiFunctionHelper::RegisterApiFunctionHelper(const String& name, const ApiFunction::Callback& function)
+{
+       ApiFunction::Ptr func = make_shared<ApiFunction>(function);
+       ApiFunctionRegistry::GetInstance()->Register(name, func);
+}
+
+ApiFunction::Ptr ApiFunction::GetByName(const String& name)
+{
+       return ApiFunctionRegistry::GetInstance()->GetItem(name);
+}
+
+void ApiFunction::Register(const String& name, const ApiFunction::Ptr& function)
+{
+       ApiFunctionRegistry::GetInstance()->Register(name, function);
+}
+
+void ApiFunction::Unregister(const String& name)
+{
+       ApiFunctionRegistry::GetInstance()->Unregister(name);
+}
+
+ApiFunctionRegistry *ApiFunctionRegistry::GetInstance(void)
+{
+       return Singleton<ApiFunctionRegistry>::GetInstance();
+}
diff --git a/lib/remote/apifunction.h b/lib/remote/apifunction.h
new file mode 100644 (file)
index 0000000..4112292
--- /dev/null
@@ -0,0 +1,87 @@
+/******************************************************************************
+ * 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.             *
+ ******************************************************************************/
+
+#ifndef APIFUNCTION_H
+#define APIFUNCTION_H
+
+#include "remote/i2-remote.h"
+#include "remote/apiclient.h"
+#include "remote/messageorigin.h"
+#include "base/registry.h"
+#include "base/singleton.h"
+#include "base/value.h"
+#include "base/dictionary.h"
+#include <vector>
+#include <boost/function.hpp>
+
+namespace icinga
+{
+
+/**
+ * An API function.
+ *
+ * @ingroup base
+ */
+class I2_REMOTE_API ApiFunction : public Object
+{
+public:
+       DECLARE_PTR_TYPEDEFS(ApiFunction);
+
+       typedef boost::function<Value(const MessageOrigin& origin, const Dictionary::Ptr&)> Callback;
+
+       ApiFunction(const Callback& function);
+
+       Value Invoke(const MessageOrigin& origin, const Dictionary::Ptr& arguments);
+
+       static ApiFunction::Ptr GetByName(const String& name);
+       static void Register(const String& name, const ApiFunction::Ptr& function);
+       static void Unregister(const String& name);
+
+private:
+       Callback m_Callback;
+};
+
+/**
+ * A registry for API functions.
+ *
+ * @ingroup base
+ */
+class I2_REMOTE_API ApiFunctionRegistry : public Registry<ApiFunctionRegistry, ApiFunction::Ptr>
+{
+public:
+       static ApiFunctionRegistry *GetInstance(void);
+};
+
+/**
+ * Helper class for registering ApiFunction implementation classes.
+ *
+ * @ingroup base
+ */
+class I2_REMOTE_API RegisterApiFunctionHelper
+{
+public:
+       RegisterApiFunctionHelper(const String& name, const ApiFunction::Callback& function);
+};
+
+#define REGISTER_APIFUNCTION(name, ns, callback) \
+       I2_EXPORT icinga::RegisterApiFunctionHelper g_RegisterAF_ ## name(#ns "::" #name, callback)
+
+}
+
+#endif /* APIFUNCTION_H */
diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp
new file mode 100644 (file)
index 0000000..b783a9a
--- /dev/null
@@ -0,0 +1,633 @@
+/******************************************************************************
+ * 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 "remote/apiclient.h"
+#include "remote/endpoint.h"
+#include "remote/jsonrpc.h"
+#include "base/convert.h"
+#include "base/netstring.h"
+#include "base/dynamictype.h"
+#include "base/logger_fwd.h"
+#include "base/objectlock.h"
+#include "base/stdiostream.h"
+#include "base/networkstream.h"
+#include "base/application.h"
+#include "base/context.h"
+#include "base/statsfunction.h"
+#include <fstream>
+
+using namespace icinga;
+
+REGISTER_TYPE(ApiListener);
+
+boost::signals2::signal<void(bool)> ApiListener::OnMasterChanged;
+
+REGISTER_STATSFUNCTION(ApiListenerStats, &ApiListener::StatsFunc);
+
+void ApiListener::OnConfigLoaded(void)
+{
+       /* set up SSL context */
+       shared_ptr<X509> cert = GetX509Certificate(GetCertPath());
+       SetIdentity(GetCertificateCN(cert));
+       Log(LogInformation, "remote", "My API identity: " + GetIdentity());
+
+       m_SSLContext = MakeSSLContext(GetCertPath(), GetKeyPath(), GetCaPath());
+
+       if (!GetCrlPath().IsEmpty())
+               AddCRLToSSLContext(m_SSLContext, GetCrlPath());
+
+       if (!Endpoint::GetByName(GetIdentity()))
+               BOOST_THROW_EXCEPTION(std::runtime_error("Endpoint object for '" + GetIdentity() + "' is missing."));
+}
+
+/**
+ * Starts the component.
+ */
+void ApiListener::Start(void)
+{
+       if (std::distance(DynamicType::GetObjects<ApiListener>().first, DynamicType::GetObjects<ApiListener>().second) > 1)
+               BOOST_THROW_EXCEPTION(std::runtime_error("Only one ApiListener object is allowed."));
+
+       DynamicObject::Start();
+
+       {
+               boost::mutex::scoped_lock(m_LogLock);
+               RotateLogFile();
+               OpenLogFile();
+       }
+
+       /* create the primary JSON-RPC listener */
+       AddListener(GetBindPort());
+
+       m_Timer = make_shared<Timer>();
+       m_Timer->OnTimerExpired.connect(boost::bind(&ApiListener::ApiTimerHandler, this));
+       m_Timer->SetInterval(5);
+       m_Timer->Start();
+       m_Timer->Reschedule(0);
+
+       OnMasterChanged(true);
+}
+
+ApiListener::Ptr ApiListener::GetInstance(void)
+{
+       BOOST_FOREACH(const ApiListener::Ptr& listener, DynamicType::GetObjects<ApiListener>())
+               return listener;
+
+       return ApiListener::Ptr();
+}
+
+shared_ptr<SSL_CTX> ApiListener::GetSSLContext(void) const
+{
+       return m_SSLContext;
+}
+
+Endpoint::Ptr ApiListener::GetMaster(void) const
+{
+       Zone::Ptr zone = Zone::GetLocalZone();
+       std::vector<String> names;
+
+       BOOST_FOREACH(const Endpoint::Ptr& endpoint, zone->GetEndpoints())
+               if (endpoint->IsConnected() || endpoint->GetName() == GetIdentity())
+                       names.push_back(endpoint->GetName());
+
+       std::sort(names.begin(), names.end());
+
+       return Endpoint::GetByName(*names.begin());
+}
+
+bool ApiListener::IsMaster(void) const
+{
+       return GetMaster()->GetName() == GetIdentity();
+}
+
+/**
+ * Creates a new JSON-RPC listener on the specified port.
+ *
+ * @param service The port to listen on.
+ */
+void ApiListener::AddListener(const String& service)
+{
+       ObjectLock olock(this);
+
+       shared_ptr<SSL_CTX> sslContext = m_SSLContext;
+
+       if (!sslContext)
+               BOOST_THROW_EXCEPTION(std::logic_error("SSL context is required for AddListener()"));
+
+       std::ostringstream s;
+       s << "Adding new listener: port " << service;
+       Log(LogInformation, "agent", s.str());
+
+       TcpSocket::Ptr server = make_shared<TcpSocket>();
+       server->Bind(service, AF_INET6);
+
+       boost::thread thread(boost::bind(&ApiListener::ListenerThreadProc, this, server));
+       thread.detach();
+
+       m_Servers.insert(server);
+}
+
+void ApiListener::ListenerThreadProc(const Socket::Ptr& server)
+{
+       Utility::SetThreadName("API Listener");
+
+       server->Listen();
+
+       for (;;) {
+               Socket::Ptr client = server->Accept();
+
+               Utility::QueueAsyncCallback(boost::bind(&ApiListener::NewClientHandler, this, client, RoleServer));
+       }
+}
+
+/**
+ * Creates a new JSON-RPC client and connects to the specified host and port.
+ *
+ * @param node The remote host.
+ * @param service The remote port.
+ */
+void ApiListener::AddConnection(const String& node, const String& service) {
+       {
+               ObjectLock olock(this);
+
+               shared_ptr<SSL_CTX> sslContext = m_SSLContext;
+
+               if (!sslContext)
+                       BOOST_THROW_EXCEPTION(std::logic_error("SSL context is required for AddConnection()"));
+       }
+
+       TcpSocket::Ptr client = make_shared<TcpSocket>();
+
+       client->Connect(node, service);
+       Utility::QueueAsyncCallback(boost::bind(&ApiListener::NewClientHandler, this, client, RoleClient));
+}
+
+/**
+ * Processes a new client connection.
+ *
+ * @param client The new client.
+ */
+void ApiListener::NewClientHandler(const Socket::Ptr& client, ConnectionRole role)
+{
+       CONTEXT("Handling new API client connection");
+
+       TlsStream::Ptr tlsStream;
+
+       {
+               ObjectLock olock(this);
+               tlsStream = make_shared<TlsStream>(client, role, m_SSLContext);
+       }
+
+       tlsStream->Handshake();
+
+       shared_ptr<X509> cert = tlsStream->GetPeerCertificate();
+       String identity = GetCertificateCN(cert);
+
+       Endpoint::Ptr endpoint = Endpoint::GetByName(identity);
+
+       if (!endpoint) {
+               Log(LogInformation, "remote", "New client for unknown endpoint '" + identity + "'");
+               return;
+       }
+
+       Log(LogInformation, "remote", "New client connection for identity '" + identity + "'");
+
+       bool need_sync = !endpoint->IsConnected();
+
+       ApiClient::Ptr aclient = make_shared<ApiClient>(endpoint, tlsStream, role);
+       aclient->Start();
+
+       if (need_sync) {
+               {
+                       ObjectLock olock(endpoint);
+
+                       endpoint->SetSyncing(true);
+               }
+
+               ReplayLog(aclient);
+       }
+
+       endpoint->AddClient(aclient);
+}
+
+void ApiListener::ApiTimerHandler(void)
+{
+       double now = Utility::GetTime();
+
+       std::vector<int> files;
+       Utility::Glob(GetApiDir() + "log/*", boost::bind(&ApiListener::LogGlobHandler, boost::ref(files), _1), GlobFile);
+       std::sort(files.begin(), files.end());
+
+       BOOST_FOREACH(int ts, files) {
+               bool need = false;
+
+               BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
+                       if (endpoint->GetName() == GetIdentity())
+                               continue;
+
+                       if (endpoint->GetLogDuration() >= 0 && ts < now - endpoint->GetLogDuration())
+                               continue;
+
+                       if (ts > endpoint->GetLocalLogPosition()) {
+                               need = true;
+                               break;
+                       }
+               }
+
+               if (!need) {
+                       String path = GetApiDir() + "log/" + Convert::ToString(ts);
+                       Log(LogInformation, "remote", "Removing old log file: " + path);
+                       (void)unlink(path.CStr());
+               }
+       }
+
+       if (IsMaster()) {
+               Zone::Ptr my_zone = Zone::GetLocalZone();
+
+               BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
+                       if (endpoint->IsConnected() || endpoint->GetName() == GetIdentity())
+                               continue;
+
+                       if (endpoint->GetHost().IsEmpty() || endpoint->GetPort().IsEmpty())
+                               continue;
+
+                       Zone::Ptr their_zone = endpoint->GetZone();
+
+                       if (my_zone != their_zone && my_zone != their_zone->GetParent() && their_zone != my_zone->GetParent())
+                               continue;
+
+                       AddConnection(endpoint->GetHost(), endpoint->GetPort());
+               }
+       }
+
+       BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
+               if (!endpoint->IsConnected())
+                       continue;
+
+               double ts = endpoint->GetRemoteLogPosition();
+
+               if (ts == 0)
+                       continue;
+
+               Dictionary::Ptr lparams = make_shared<Dictionary>();
+               lparams->Set("log_position", ts);
+
+               Dictionary::Ptr lmessage = make_shared<Dictionary>();
+               lmessage->Set("jsonrpc", "2.0");
+               lmessage->Set("method", "log::SetLogPosition");
+               lmessage->Set("params", lparams);
+
+               BOOST_FOREACH(const ApiClient::Ptr& client, endpoint->GetClients())
+                       client->SendMessage(lmessage);
+
+               Log(LogInformation, "remote", "Setting log position for identity '" + endpoint->GetName() + "': " +
+                       Utility::FormatDateTime("%Y/%m/%d %H:%M:%S", ts));
+       }
+
+       Log(LogInformation, "remote", "Current master: " + GetMaster()->GetName());
+
+       std::vector<String> names;
+       BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>())
+               if (endpoint->IsConnected())
+                       names.push_back(endpoint->GetName() + " (" + Convert::ToString(endpoint->GetClients().size()) + ")");
+
+       Log(LogInformation, "remote", "Connected endpoints: " + Utility::NaturalJoin(names));
+}
+
+void ApiListener::RelayMessage(const MessageOrigin& origin, const DynamicObject::Ptr& secobj, const Dictionary::Ptr& message, bool log)
+{
+       m_RelayQueue.Enqueue(boost::bind(&ApiListener::SyncRelayMessage, this, origin, secobj, message, log));
+}
+
+void ApiListener::PersistMessage(const Dictionary::Ptr& message)
+{
+       double ts = message->Get("ts");
+
+       ASSERT(ts != 0);
+
+       Dictionary::Ptr pmessage = make_shared<Dictionary>();
+       pmessage->Set("timestamp", ts);
+
+       pmessage->Set("message", JsonSerialize(message));
+
+       boost::mutex::scoped_lock lock(m_LogLock);
+       if (m_LogFile) {
+               NetString::WriteStringToStream(m_LogFile, JsonSerialize(pmessage));
+               m_LogMessageCount++;
+               SetLogMessageTimestamp(ts);
+
+               if (m_LogMessageCount > 50000) {
+                       CloseLogFile();
+                       RotateLogFile();
+                       OpenLogFile();
+               }
+       }
+}
+
+void ApiListener::SyncRelayMessage(const MessageOrigin& origin, const DynamicObject::Ptr& secobj, const Dictionary::Ptr& message, bool log)
+{
+       double ts = Utility::GetTime();
+       message->Set("ts", ts);
+
+       Log(LogDebug, "remote", "Relaying '" + message->Get("method") + "' message");
+
+       if (log)
+               m_LogQueue.Enqueue(boost::bind(&ApiListener::PersistMessage, this, message));
+
+       if (origin.FromZone)
+               message->Set("originZone", origin.FromZone->GetName());
+
+       bool is_master = IsMaster();
+       Endpoint::Ptr master = GetMaster();
+       Zone::Ptr my_zone = Zone::GetLocalZone();
+
+       std::vector<Endpoint::Ptr> skippedEndpoints;
+       std::set<Zone::Ptr> finishedZones;
+
+       BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
+               /* don't relay messages to ourselves or disconnected endpoints */
+               if (endpoint->GetName() == GetIdentity() || !endpoint->IsConnected())
+                       continue;
+
+               Zone::Ptr target_zone = endpoint->GetZone();
+
+               /* don't relay the message to the zone through more than one endpoint */
+               if (finishedZones.find(target_zone) != finishedZones.end()) {
+                       skippedEndpoints.push_back(endpoint);
+                       continue;
+               }
+
+               /* don't relay messages back to the endpoint which we got the message from */
+               if (origin.FromClient && endpoint == origin.FromClient->GetEndpoint()) {
+                       skippedEndpoints.push_back(endpoint);
+                       continue;
+               }
+
+               /* don't relay messages back to the zone which we got the message from */
+               if (origin.FromZone && target_zone == origin.FromZone) {
+                       skippedEndpoints.push_back(endpoint);
+                       continue;
+               }
+
+               /* only relay message to the master if we're not currently the master */
+               if (!is_master && master != endpoint) {
+                       skippedEndpoints.push_back(endpoint);
+                       continue;
+               }
+
+               /* only relay the message to a) the same zone, b) the parent zone and c) direct child zones */
+               if (target_zone != my_zone && target_zone != my_zone->GetParent() &&
+                   secobj->GetZone() != target_zone->GetName()) {
+                       skippedEndpoints.push_back(endpoint);
+                       continue;
+               }
+
+               /* only relay messages to zones which have access to the object */
+               if (!target_zone->CanAccessObject(secobj))
+                       continue;
+
+               finishedZones.insert(target_zone);
+
+               {
+                       ObjectLock olock(endpoint);
+
+                       if (!endpoint->GetSyncing()) {
+                               Log(LogDebug, "remote", "Sending message to '" + endpoint->GetName() + "'");
+
+                               BOOST_FOREACH(const ApiClient::Ptr& client, endpoint->GetClients())
+                                       client->SendMessage(message);
+                       }
+               }
+       }
+
+       BOOST_FOREACH(const Endpoint::Ptr& endpoint, skippedEndpoints)
+               endpoint->SetLocalLogPosition(ts);
+}
+
+String ApiListener::GetApiDir(void)
+{
+       return Application::GetLocalStateDir() + "/lib/icinga2/api/";
+}
+
+/* must hold m_LogLock */
+void ApiListener::OpenLogFile(void)
+{
+       String path = GetApiDir() + "log/current";
+
+       std::fstream *fp = new std::fstream(path.CStr(), std::fstream::out | std::ofstream::app);
+
+       if (!fp->good()) {
+               Log(LogWarning, "cluster", "Could not open spool file: " + path);
+               return;
+       }
+
+       StdioStream::Ptr logStream = make_shared<StdioStream>(fp, true);
+#ifdef HAVE_BIOZLIB
+       m_LogFile = make_shared<ZlibStream>(logStream);
+#else /* HAVE_BIOZLIB */
+       m_LogFile = logStream;
+#endif /* HAVE_BIOZLIB */
+       m_LogMessageCount = 0;
+       SetLogMessageTimestamp(Utility::GetTime());
+}
+
+/* must hold m_LogLock */
+void ApiListener::CloseLogFile(void)
+{
+       if (!m_LogFile)
+               return;
+
+       m_LogFile->Close();
+       m_LogFile.reset();
+}
+
+/* must hold m_LogLock */
+void ApiListener::RotateLogFile(void)
+{
+       double ts = GetLogMessageTimestamp();
+
+       if (ts == 0)
+               ts = Utility::GetTime();
+
+       String oldpath = GetApiDir() + "log/current";
+       String newpath = GetApiDir() + "log/" + Convert::ToString(static_cast<int>(ts)+1);
+       (void) rename(oldpath.CStr(), newpath.CStr());
+}
+
+void ApiListener::LogGlobHandler(std::vector<int>& files, const String& file)
+{
+       String name = Utility::BaseName(file);
+
+       int ts;
+
+       try {
+               ts = Convert::ToLong(name);
+       }
+       catch (const std::exception&) {
+               return;
+       }
+
+       files.push_back(ts);
+}
+
+void ApiListener::ReplayLog(const ApiClient::Ptr& client)
+{
+       Endpoint::Ptr endpoint = client->GetEndpoint();
+
+       CONTEXT("Replaying log for Endpoint '" + endpoint->GetName() + "'");
+
+       int count = -1;
+       double peer_ts = endpoint->GetLocalLogPosition();
+       bool last_sync = false;
+
+       for (;;) {
+               boost::mutex::scoped_lock lock(m_LogLock);
+
+               CloseLogFile();
+               RotateLogFile();
+
+               if (count == -1 || count > 50000) {
+                       OpenLogFile();
+                       lock.unlock();
+               } else {
+                       last_sync = true;
+               }
+
+               count = 0;
+
+               std::vector<int> files;
+               Utility::Glob(GetApiDir() + "log/*", boost::bind(&ApiListener::LogGlobHandler, boost::ref(files), _1), GlobFile);
+               std::sort(files.begin(), files.end());
+
+               BOOST_FOREACH(int ts, files) {
+                       String path = GetApiDir() + "log/" + Convert::ToString(ts);
+
+                       if (ts < peer_ts)
+                               continue;
+
+                       Log(LogInformation, "cluster", "Replaying log: " + path);
+
+                       std::fstream *fp = new std::fstream(path.CStr(), std::fstream::in);
+                       StdioStream::Ptr logStream = make_shared<StdioStream>(fp, true);
+#ifdef HAVE_BIOZLIB
+                       ZlibStream::Ptr lstream = make_shared<ZlibStream>(logStream);
+#else /* HAVE_BIOZLIB */
+                       Stream::Ptr lstream = logStream;
+#endif /* HAVE_BIOZLIB */
+
+                       String message;
+                       while (true) {
+                               Dictionary::Ptr pmessage;
+
+                               try {
+                                       if (!NetString::ReadStringFromStream(lstream, &message))
+                                               break;
+
+                                       pmessage = JsonDeserialize(message);
+                               } catch (const std::exception&) {
+                                       Log(LogWarning, "cluster", "Unexpected end-of-file for cluster log: " + path);
+
+                                       /* Log files may be incomplete or corrupted. This is perfectly OK. */
+                                       break;
+                               }
+
+                               if (pmessage->Get("timestamp") <= peer_ts)
+                                       continue;
+
+                               NetString::WriteStringToStream(client->GetStream(), pmessage->Get("message"));
+                               count++;
+
+                               peer_ts = pmessage->Get("timestamp");
+                       }
+
+                       lstream->Close();
+               }
+
+               Log(LogInformation, "cluster", "Replayed " + Convert::ToString(count) + " messages.");
+
+               if (last_sync) {
+                       {
+                               ObjectLock olock2(endpoint);
+                               endpoint->SetSyncing(false);
+                       }
+
+                       OpenLogFile();
+
+                       break;
+               }
+       }
+}
+
+Value ApiListener::StatsFunc(Dictionary::Ptr& status, Dictionary::Ptr& perfdata)
+{
+       Dictionary::Ptr nodes = make_shared<Dictionary>();
+       std::pair<Dictionary::Ptr, Dictionary::Ptr> stats;
+
+       ApiListener::Ptr listener = ApiListener::GetInstance();
+
+       if (!listener)
+               return 0;
+
+       stats = listener->GetStatus();
+
+       BOOST_FOREACH(Dictionary::Pair const& kv, stats.second)
+               perfdata->Set("api_" + kv.first, kv.second);
+
+       status->Set("api", stats.first);
+
+       return 0;
+}
+
+std::pair<Dictionary::Ptr, Dictionary::Ptr> ApiListener::GetStatus(void)
+{
+       Dictionary::Ptr status = make_shared<Dictionary>();
+       Dictionary::Ptr perfdata = make_shared<Dictionary>();
+
+       /* cluster stats */
+       status->Set("identity", GetIdentity());
+
+       double count_endpoints = 0;
+       Array::Ptr not_connected_endpoints = make_shared<Array>();
+       Array::Ptr connected_endpoints = make_shared<Array>();
+
+       BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
+               if (endpoint->GetName() == GetIdentity())
+                       continue;
+
+               count_endpoints++;
+
+               if (!endpoint->IsConnected())
+                       not_connected_endpoints->Add(endpoint->GetName());
+               else
+                       connected_endpoints->Add(endpoint->GetName());
+       }
+
+       status->Set("num_endpoints", count_endpoints);
+       status->Set("num_conn_endpoints", connected_endpoints->GetLength());
+       status->Set("num_not_conn_endpoints", not_connected_endpoints->GetLength());
+       status->Set("conn_endpoints", connected_endpoints);
+       status->Set("not_conn_endpoints", not_connected_endpoints);
+
+       perfdata->Set("num_endpoints", count_endpoints);
+       perfdata->Set("num_conn_endpoints", Convert::ToDouble(connected_endpoints->GetLength()));
+       perfdata->Set("num_not_conn_endpoints", Convert::ToDouble(not_connected_endpoints->GetLength()));
+
+       return std::make_pair(status, perfdata);
+}
similarity index 57%
rename from components/agent/agentlistener.h
rename to lib/remote/apilistener.h
index 6967e3ae8c827e202c9c243368104afd27ba3030..8fea8dc7a233b68019f773512893a43f5ec4477f 100644 (file)
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
-#ifndef AGENTLISTENER_H
-#define AGENTLISTENER_H
-
-#include "agent/agentlistener.th"
+#ifndef APILISTENER_H
+#define APILISTENER_H
+
+#include "remote/apilistener.th"
+#include "remote/apiclient.h"
+#include "remote/endpoint.h"
+#include "remote/zone.h"
+#include "remote/messageorigin.h"
 #include "base/dynamicobject.h"
 #include "base/timer.h"
 #include "base/array.h"
+#include "base/workqueue.h"
 #include "base/tcpsocket.h"
 #include "base/tlsstream.h"
 #include "base/utility.h"
 #include "base/tlsutility.h"
-#include "icinga/service.h"
 
 namespace icinga
 {
 
+class ApiClient;
+
 /**
- * @ingroup agent
- */
-class AgentListener : public ObjectImpl<AgentListener>
+* @ingroup remote
+*/
+class I2_REMOTE_API ApiListener : public ObjectImpl<ApiListener>
 {
 public:
-       DECLARE_PTR_TYPEDEFS(AgentListener);
-       DECLARE_TYPENAME(AgentListener);
+       DECLARE_PTR_TYPEDEFS(ApiListener);
+       DECLARE_TYPENAME(ApiListener);
 
-       virtual void Start(void);
+       static boost::signals2::signal<void(bool)> OnMasterChanged;
+
+       static ApiListener::Ptr GetInstance(void);
 
        shared_ptr<SSL_CTX> GetSSLContext(void) const;
 
-       double GetAgentSeen(const String& agentIdentity);
-       CheckResult::Ptr GetCheckResult(const String& agentIdentity, const String& hostName, const String& serviceName);
+       Endpoint::Ptr GetMaster(void) const;
+       bool IsMaster(void) const;
+
+       static String GetApiDir(void);
+
+       void RelayMessage(const MessageOrigin& origin, const DynamicObject::Ptr& secobj, const Dictionary::Ptr& message, bool log);
+
+       static Value StatsFunc(Dictionary::Ptr& status, Dictionary::Ptr& perfdata);
+       std::pair<Dictionary::Ptr, Dictionary::Ptr> GetStatus(void);
+
+protected:
+       virtual void OnConfigLoaded(void);
+       virtual void Start(void);
 
 private:
        shared_ptr<SSL_CTX> m_SSLContext;
        std::set<TcpSocket::Ptr> m_Servers;
        Timer::Ptr m_Timer;
 
-       Dictionary::Ptr m_Results;
-
-       Timer::Ptr m_AgentTimer;
-       void AgentTimerHandler(void);
+       void ApiTimerHandler(void);
 
        void AddListener(const String& service);
        void AddConnection(const String& node, const String& service);
 
-       void NewClientHandler(const Socket::Ptr& client, TlsRole role);
+       void NewClientHandler(const Socket::Ptr& client, ConnectionRole role);
        void ListenerThreadProc(const Socket::Ptr& server);
 
        void MessageHandler(const TlsStream::Ptr& sender, const String& identity, const Dictionary::Ptr& message);
 
-       static String GetInventoryDir(void);
+       WorkQueue m_RelayQueue;
+       WorkQueue m_LogQueue;
+
+       boost::mutex m_LogLock;
+       Stream::Ptr m_LogFile;
+       size_t m_LogMessageCount;
+
+       void SyncRelayMessage(const MessageOrigin& origin, const DynamicObject::Ptr& secobj, const Dictionary::Ptr& message, bool log);
+       void PersistMessage(const Dictionary::Ptr& message);
 
-       friend class AgentCheckTask;
+       void OpenLogFile(void);
+       void RotateLogFile(void);
+       void CloseLogFile(void);
+       static void LogGlobHandler(std::vector<int>& files, const String& file);
+       void ReplayLog(const ApiClient::Ptr& client);
 };
 
 }
 
-#endif /* AGENTLISTENER_H */
+#endif /* APILISTENER_H */
similarity index 67%
rename from components/cluster/clusterlistener.ti
rename to lib/remote/apilistener.ti
index 6138ae4173f6ae8aa2de92aff6bb112cdad1fd52..d19d4b9923fac8cf434f5c5ca1b2486f57786576 100644 (file)
@@ -1,19 +1,22 @@
 #include "base/dynamicobject.h"
-#include "base/application.h"
 
 namespace icinga
 {
 
-class ClusterListener : DynamicObject
+class ApiListener : DynamicObject
 {
        [config] String cert_path;
        [config] String key_path;
        [config] String ca_path;
        [config] String crl_path;
+
        [config] String bind_host;
-       [config] String bind_port;
-       [config] Array::Ptr peers;
+       [config] String bind_port {
+               default {{{ return "5665"; }}}
+       };
+
        [state] double log_message_timestamp;
+
        String identity;
 };
 
index d44d9bd8f4d610520fb281fe21deb19b6ae1128a..fb01cae8878758181b918f434e0a22621703d0d4 100644 (file)
  ******************************************************************************/
 
 #include "remote/endpoint.h"
+#include "remote/apilistener.h"
+#include "remote/apiclient.h"
 #include "remote/jsonrpc.h"
+#include "remote/zone.h"
 #include "base/application.h"
 #include "base/dynamictype.h"
 #include "base/objectlock.h"
 #include "base/utility.h"
 #include "base/logger_fwd.h"
 #include "base/exception.h"
-#include "config/configitembuilder.h"
 
 using namespace icinga;
 
 REGISTER_TYPE(Endpoint);
 
-boost::signals2::signal<void (const Endpoint::Ptr&)> Endpoint::OnConnected;
-boost::signals2::signal<void (const Endpoint::Ptr&)> Endpoint::OnDisconnected;
-boost::signals2::signal<void (const Endpoint::Ptr&, const Dictionary::Ptr&)> Endpoint::OnMessageReceived;
+boost::signals2::signal<void(const Endpoint::Ptr&, const ApiClient::Ptr&)> Endpoint::OnConnected;
+boost::signals2::signal<void(const Endpoint::Ptr&, const ApiClient::Ptr&)> Endpoint::OnDisconnected;
 
-/**
- * Checks whether this endpoint is connected.
- *
- * @returns true if the endpoint is connected, false otherwise.
- */
-bool Endpoint::IsConnected(void) const
+void Endpoint::OnConfigLoaded(void)
 {
-       return GetClient() != NULL;
-}
+       DynamicObject::OnConfigLoaded();
 
-bool Endpoint::IsAvailable(void) const
-{
-       return GetSeen() > Utility::GetTime() - 30;
-}
+       BOOST_FOREACH(const Zone::Ptr& zone, DynamicType::GetObjects<Zone>()) {
+               const std::set<Endpoint::Ptr> members = zone->GetEndpoints();
 
-Stream::Ptr Endpoint::GetClient(void) const
-{
-       return m_Client;
+               if (members.find(GetSelf()) != members.end()) {
+                       if (m_Zone)
+                               BOOST_THROW_EXCEPTION(std::runtime_error("Endpoint '" + GetName() + "' is in more than one zone."));
+
+                       m_Zone = zone;
+               }
+       }
+
+       if (!m_Zone)
+               BOOST_THROW_EXCEPTION(std::runtime_error("Endpoint '" + GetName() + "' does not belong to a zone."));
 }
 
-void Endpoint::SetClient(const Stream::Ptr& client)
+void Endpoint::AddClient(const ApiClient::Ptr& client)
 {
-       SetBlockedUntil(Utility::GetTime() + 15);
+       bool was_master = ApiListener::GetInstance()->IsMaster();
 
-       if (m_Client)
-               m_Client->Close();
+       {
+               boost::mutex::scoped_lock lock(m_ClientsLock);
+               m_Clients.insert(client);
+       }
 
-       m_Client = client;
+       bool is_master = ApiListener::GetInstance()->IsMaster();
 
-       if (client) {
-               boost::thread thread(boost::bind(&Endpoint::MessageThreadProc, this, client));
-               thread.detach();
+       if (was_master != is_master)
+               ApiListener::OnMasterChanged(is_master);
 
-               OnConnected(GetSelf());
-               Log(LogInformation, "remote", "Endpoint connected: " + GetName());
-       } else {
-               OnDisconnected(GetSelf());
-               Log(LogInformation, "remote", "Endpoint disconnected: " + GetName());
-       }
+       OnConnected(GetSelf(), client);
 }
 
-void Endpoint::SendMessage(const Dictionary::Ptr& message)
+void Endpoint::RemoveClient(const ApiClient::Ptr& client)
 {
-       Stream::Ptr client = GetClient();
+       bool was_master = ApiListener::GetInstance()->IsMaster();
 
-       if (!client)
-               return;
+       {
+               boost::mutex::scoped_lock lock(m_ClientsLock);
+               m_Clients.erase(client);
+       }
 
-       try {
-               JsonRpc::SendMessage(client, message);
-       } catch (const std::exception& ex) {
-               std::ostringstream msgbuf;
-               msgbuf << "Error while sending JSON-RPC message for endpoint '" << GetName() << "': " << DiagnosticInformation(ex);
-               Log(LogWarning, "remote", msgbuf.str());
+       bool is_master = ApiListener::GetInstance()->IsMaster();
 
-               m_Client.reset();
+       if (was_master != is_master)
+               ApiListener::OnMasterChanged(is_master);
 
-               OnDisconnected(GetSelf());
-               Log(LogWarning, "remote", "Endpoint disconnected: " + GetName());
-       }
+       OnDisconnected(GetSelf(), client);
 }
 
-void Endpoint::MessageThreadProc(const Stream::Ptr& stream)
+std::set<ApiClient::Ptr> Endpoint::GetClients(void) const
 {
-       Utility::SetThreadName("EndpointMsg");
-
-       for (;;) {
-               Dictionary::Ptr message;
-
-               try {
-                       message = JsonRpc::ReadMessage(stream);
-               } catch (const std::exception& ex) {
-                       Log(LogWarning, "remote", "Error while reading JSON-RPC message for endpoint '" + GetName() + "': " + DiagnosticInformation(ex));
-
-                       m_Client.reset();
-
-                       OnDisconnected(GetSelf());
-                       Log(LogWarning, "remote", "Endpoint disconnected: " + GetName());
+       boost::mutex::scoped_lock lock(m_ClientsLock);
+       return m_Clients;
+}
 
-                       return;
-               }
+Zone::Ptr Endpoint::GetZone(void) const
+{
+       return m_Zone;
+}
 
-               OnMessageReceived(GetSelf(), message);
-       }
+bool Endpoint::IsConnected(void) const
+{
+       boost::mutex::scoped_lock lock(m_ClientsLock);
+       return !m_Clients.empty();
 }
 
-bool Endpoint::HasFeature(const String& type) const
+Endpoint::Ptr Endpoint::GetLocalEndpoint(void)
 {
-       Dictionary::Ptr features = GetFeatures();
+       ApiListener::Ptr listener = ApiListener::GetInstance();
 
-       if (!features)
-               return false;
+       if (!listener)
+               return Endpoint::Ptr();
 
-       return features->Get(type);
+       return Endpoint::GetByName(listener->GetIdentity());
 }
-
index f7ae173478c1f6667cc48007307b7859541c0049..9a8bec3c2582c3a716c81f25ea33ba8aa9406a8b 100644 (file)
 namespace icinga
 {
 
-class EndpointManager;
+class ApiClient;
+class Zone;
 
 /**
  * An endpoint that can be used to send and receive messages.
  *
- * @ingroup cluster
+ * @ingroup remote
  */
 class I2_REMOTE_API Endpoint : public ObjectImpl<Endpoint>
 {
@@ -42,26 +43,26 @@ public:
        DECLARE_PTR_TYPEDEFS(Endpoint);
        DECLARE_TYPENAME(Endpoint);
 
-       static boost::signals2::signal<void (const Endpoint::Ptr&)> OnConnected;
-        static boost::signals2::signal<void (const Endpoint::Ptr&)> OnDisconnected;
-       static boost::signals2::signal<void (const Endpoint::Ptr&, const Dictionary::Ptr&)> OnMessageReceived;
+       static boost::signals2::signal<void(const Endpoint::Ptr&, const shared_ptr<ApiClient>&)> OnConnected;
+       static boost::signals2::signal<void(const Endpoint::Ptr&, const shared_ptr<ApiClient>&)> OnDisconnected;
 
-       Stream::Ptr GetClient(void) const;
-       void SetClient(const Stream::Ptr& client);
+       void AddClient(const shared_ptr<ApiClient>& client);
+       void RemoveClient(const shared_ptr<ApiClient>& client);
+       std::set<shared_ptr<ApiClient> > GetClients(void) const;
+
+       shared_ptr<Zone> GetZone(void) const;
 
        bool IsConnected(void) const;
-       bool IsAvailable(void) const;
 
-       void SendMessage(const Dictionary::Ptr& request);
+       static Endpoint::Ptr GetLocalEndpoint(void);
 
-       bool HasFeature(const String& type) const;
+protected:
+       virtual void OnConfigLoaded(void);
 
 private:
-       Stream::Ptr m_Client;
-       boost::thread m_Thread;
-       Array::Ptr m_ConnectedEndpoints;
-
-       void MessageThreadProc(const Stream::Ptr& stream);
+       mutable boost::mutex m_ClientsLock;
+       std::set<shared_ptr<ApiClient> > m_Clients;
+       shared_ptr<Zone> m_Zone;
 };
 
 }
index 36d52a4d2f094d9044836d7ed7bf3d520473c69c..6e1a521d89686a5df2221ae4f4fbdf407961d88c 100644 (file)
@@ -6,19 +6,20 @@ namespace icinga
 class Endpoint : DynamicObject
 {
        [config] String host;
-       [config] String port;
-       [config] Array::Ptr config_files;
-       [config] Array::Ptr config_files_recursive;
-       [config] Array::Ptr accept_config;
-       [config] int metric;
+       [config] String port {
+               default {{{ return "5665"; }}}
+       };
+       [config] double keep_alive {
+               default {{{ return 300; }}}
+       };
+       [config] double log_duration {
+               default {{{ return 86400; }}}
+       };
 
-       [state] double seen;
        [state] double local_log_position;
        [state] double remote_log_position;
-       [state] Dictionary::Ptr features;
 
        bool syncing;
-       double blocked_until;
 };
 
 }
similarity index 89%
rename from lib/icinga/domain.cpp
rename to lib/remote/messageorigin.cpp
index f0a52448c09db576551ca5cfedd7dd2411433ce9..8369e868c4da3d3ac405ec30af2122839c73713d 100644 (file)
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
-#include "icinga/domain.h"
-#include "base/dynamictype.h"
+#include "remote/messageorigin.h"
 
 using namespace icinga;
 
-REGISTER_TYPE(Domain);
+bool MessageOrigin::IsLocal(void) const
+{
+       return !FromClient;
+}
 
-int Domain::GetPrivileges(const String& instance) const
+bool MessageOrigin::IsSameZone(void) const
 {
-       return GetAcl()->Get(instance);
+       return !FromZone;
 }
similarity index 80%
rename from lib/icinga/domain.h
rename to lib/remote/messageorigin.h
index 24e97cb551f3d58adcec82e832e32f6dd9f92eae..d96e64fee99342963df2f419d91e78571853b111 100644 (file)
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
-#ifndef DOMAIN_H
-#define DOMAIN_H
+#ifndef MESSAGEORIGIN_H
+#define MESSAGEORIGIN_H
 
-#include "icinga/i2-icinga.h"
-#include "icinga/domain.th"
-#include "base/dictionary.h"
+#include "remote/zone.h"
+#include "remote/apiclient.h"
 
 namespace icinga
 {
 
 /**
- * A domain.
- *
- * @ingroup icinga
+ * @ingroup remote
  */
-class I2_ICINGA_API Domain : public ObjectImpl<Domain>
+struct I2_REMOTE_API MessageOrigin
 {
-public:
-       DECLARE_PTR_TYPEDEFS(Domain);
-       DECLARE_TYPENAME(Domain);
+       Zone::Ptr FromZone;
+       ApiClient::Ptr FromClient;
 
-       int GetPrivileges(const String& instance) const;
+       bool IsLocal(void) const;
+       bool IsSameZone(void) const;
 };
 
 }
 
-#endif /* DOMAIN_H */
+#endif /* MESSAGEORIGIN_H */
index e75f017fcdbe01758d773d912c16edb82b327e20..3f8da604206a7651b647b31b568f1281918df89c 100644 (file)
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
+ %type ApiListener {
+       %require "cert_path",
+       %attribute %string "cert_path",
+
+       %require "key_path",
+       %attribute %string "key_path",
+
+       %require "ca_path",
+       %attribute %string "ca_path",
+
+       %attribute %string "crl_path",
+
+       %attribute %string "bind_host",
+       %attribute %string "bind_port"
+}
+
 %type Endpoint {
-       %require "host",
        %attribute %string "host",
-
-       %require "port",
        %attribute %string "port",
 
-       %attribute %number "metric",
-
-       %attribute %array "config_files" {
-               %attribute %string "*"
-       },
+       %attribute %number "keep_alive",
+       %attribute %number "log_duration"
+}
 
-       %attribute %array "config_files_recursive" {
-               %attribute %string "*",
-               %attribute %dictionary "*" {
-                       %attribute %string "path",
-                       %attribute %string "pattern"
-               }
-       },
+%type Zone {
+       %attribute %name(Zone) "parent",
 
-       %attribute %array "accept_config" {
+       %attribute %array "endpoints" {
                %attribute %name(Endpoint) "*"
        }
 }
similarity index 61%
rename from components/cluster/clusterlink.cpp
rename to lib/remote/zone.cpp
index b52ad8b40cd4fc6177260239836103843b0131b2..0c7d09ee2867501f309adf09588137dba982b9a6 100644 (file)
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
-#include "cluster/clusterlink.h"
+#include "remote/zone.h"
+#include "base/application.h"
+#include "base/dynamictype.h"
+#include "base/objectlock.h"
+#include "base/utility.h"
+#include "base/logger_fwd.h"
+#include "base/exception.h"
 
 using namespace icinga;
 
-ClusterLink::ClusterLink(const String& from, const String& to)
+REGISTER_TYPE(Zone);
+
+Zone::Ptr Zone::GetParent(void) const
 {
-       if (from < to) {
-               From = from;
-               To = to;
-       } else {
-               From = to;
-               To = from;
-       }
+       return Zone::GetByName(GetParentRaw());
 }
 
-int ClusterLink::GetMetric(void) const
+std::set<Endpoint::Ptr> Zone::GetEndpoints(void) const
 {
-       int metric = 0;
+       std::set<Endpoint::Ptr> result;
 
-       Endpoint::Ptr fromEp = Endpoint::GetByName(From);
-       if (fromEp)
-               metric += fromEp->GetMetric();
+       BOOST_FOREACH(const String& endpoint, GetEndpointsRaw())
+               result.insert(Endpoint::GetByName(endpoint));
 
-       Endpoint::Ptr toEp = Endpoint::GetByName(To);
-       if (toEp)
-               metric += toEp->GetMetric();
-
-       return metric;
+       return result;
 }
 
-bool ClusterLink::operator<(const ClusterLink& other) const
+bool Zone::CanAccessObject(const DynamicObject::Ptr& object) const
 {
-       if (From < other.From)
-               return true;
+       Zone::Ptr object_zone;
+
+       if (dynamic_pointer_cast<Zone>(object))
+               object_zone = static_pointer_cast<Zone>(object);
        else
-               return To < other.To;
+               Zone::GetByName(object->GetZone());
+
+       if (!object_zone)
+               return false;
+
+       while (object_zone) {
+               if (object_zone.get() == this)
+                       return true;
+
+               object_zone = object_zone->GetParent();
+       }
+
+       return false;
 }
 
-bool ClusterLinkMetricLessComparer::operator()(const ClusterLink& a, const ClusterLink& b) const
+Zone::Ptr Zone::GetLocalZone(void)
 {
-       int metricA = a.GetMetric();
-       int metricB = b.GetMetric();
-
-       if (metricA < metricB)
-               return true;
-       else if (metricB > metricA)
-               return false;
-       else
-               return a < b;
+       return Endpoint::GetLocalEndpoint()->GetZone();
 }
similarity index 75%
rename from components/cluster/clusterlink.h
rename to lib/remote/zone.h
index 9c9e744c818b503b4407e3e362329d62ab2f90c4..f17b4ac1100f92701f63b5726e0281dae78650a3 100644 (file)
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
-#ifndef CLUSTERLINK_H
-#define CLUSTERLINK_H
+#ifndef ZONE_H
+#define ZONE_H
 
+#include "remote/zone.th"
 #include "remote/endpoint.h"
+#include "base/array.h"
+#include "remote/i2-remote.h"
+#include <boost/signals2.hpp>
 
 namespace icinga
 {
 
 /**
- * @ingroup cluster
+ * @ingroup remote
  */
-struct ClusterLink
+class I2_REMOTE_API Zone : public ObjectImpl<Zone>
 {
-       String From;
-       String To;
+public:
+       DECLARE_PTR_TYPEDEFS(Zone);
+       DECLARE_TYPENAME(Zone);
 
-       ClusterLink(const String& from, const String& to);
+       Zone::Ptr GetParent(void) const;
+       std::set<Endpoint::Ptr> GetEndpoints(void) const;
 
-       int GetMetric(void) const;
-       bool operator<(const ClusterLink& other) const;
-};
+       bool CanAccessObject(const DynamicObject::Ptr& object) const;
 
-struct ClusterLinkMetricLessComparer
-{
-       bool operator()(const ClusterLink& a, const ClusterLink& b) const;
+       static Zone::Ptr GetLocalZone(void);
 };
 
 }
 
-#endif /* CLUSTERLINK_H */
+#endif /* ZONE_H */
diff --git a/lib/remote/zone.ti b/lib/remote/zone.ti
new file mode 100644 (file)
index 0000000..7c1b152
--- /dev/null
@@ -0,0 +1,12 @@
+#include "base/dynamicobject.h"
+
+namespace icinga
+{
+
+class Zone : DynamicObject
+{
+       [config] String parent (ParentRaw);
+       [config] Array::Ptr endpoints (EndpointsRaw);
+};
+
+}
index 1dfaa19f4b420fe89d512cee8ee35c523fa8063b..e6aa1fb3127596501b7b200a929bfe2ff284cf08 100644 (file)
@@ -60,6 +60,8 @@ static void Callback(int *counter)
 
 BOOST_AUTO_TEST_CASE(invoke)
 {
+       Utility::Sleep(5);
+
        int counter;
        Timer::Ptr timer = make_shared<Timer>();
        timer->OnTimerExpired.connect(boost::bind(&Callback, &counter));
index d83b3ce5e22f57ba3b950f6457d301e0436ad5d5..f4520e7dc3ad947b764179e681ef31d52e810e55 100644 (file)
 #define BOOST_TEST_MAIN
 #define BOOST_TEST_MODULE icinga2_test
 
+#include "base/application.h"
 #include <BoostTestTargetConfig.h>
 
+using namespace icinga;
+
+struct InitLibBase
+{
+       InitLibBase(void)
+       {
+               Application::InitializeBase();
+       }
+};
+
+BOOST_GLOBAL_FIXTURE(InitLibBase);