-add_subdirectory(agent)
add_subdirectory(checker)
-add_subdirectory(cluster)
add_subdirectory(compat)
add_subdirectory(db_ido_mysql)
add_subdirectory(db_ido_pgsql)
+++ /dev/null
-# 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\")")
-
+++ /dev/null
-/******************************************************************************
- * 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"
-}
+++ /dev/null
-/******************************************************************************
- * 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);
-}
+++ /dev/null
-/******************************************************************************
- * 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);
-}
+++ /dev/null
-#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;
-};
-
-}
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
#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"
{
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));
}
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) {
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();
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;
+++ /dev/null
-# 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\")")
-
+++ /dev/null
-/******************************************************************************
- * 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) "*"
- }
-}
+++ /dev/null
-/******************************************************************************
- * 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);
-}
+++ /dev/null
-/******************************************************************************
- * 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 */
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
******************************************************************************/
#include "demo/demo.h"
+#include "remote/apilistener.h"
+#include "remote/apifunction.h"
#include "base/dynamictype.h"
#include "base/logger_fwd.h"
REGISTER_TYPE(Demo);
+REGISTER_APIFUNCTION(HelloWorld, demo, &Demo::DemoMessageHandler);
+
/**
* Starts the component.
*/
}
/**
- * 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;
}
#define DEMO_H
#include "demo/demo.th"
+#include "remote/messageorigin.h"
#include "base/timer.h"
namespace icinga
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;
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)
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\")")
/**
* 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"
+++ /dev/null
-/**
- * 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"
-}
--- /dev/null
+/**
+ * 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" ]
+}*/
+
*/
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);
m_Instance = NULL;
}
+void Application::InitializeBase(void)
+{
+ Utility::ExecuteDeferredInitializers();
+}
+
/**
* Retrieves a pointer to the application singleton object.
*
#include "base/dynamicobject.h"
#include "base/process.h"
-namespace icinga {
-
-class Component;
+namespace icinga
+{
/**
* Abstract base class for applications.
~Application(void);
+ static void InitializeBase(void);
+
static Application::Ptr GetInstance(void);
int Run(void);
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)
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();
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);
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);
}}}
};
[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;
inline bool InitializeOnceHelper(InitializeFunc func)
{
- if (Utility::GetLoadingLibrary())
- Utility::AddDeferredInitializer(func);
- else
- func();
-
+ Utility::AddDeferredInitializer(func);
return true;
}
/******************************************************************************
-* 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"
#include <boost/thread/condition_variable.hpp>
#include <boost/signals2.hpp>
-namespace icinga {
+namespace icinga
+{
/**
* Base class for connection-oriented sockets.
namespace icinga
{
+enum ConnectionRole
+{
+ RoleClient,
+ RoleServer
+};
+
struct ReadLineContext
{
ReadLineContext(void) : Buffer(NULL), Size(0), Eof(false), MustRead(true)
#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>
* @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();
{
boost::mutex::scoped_lock lock(l_Mutex);
l_StopThread = false;
- l_Thread = boost::thread(boost::bind(&Timer::TimerThreadProc));
+ l_Thread = boost::thread(&Timer::TimerThreadProc);
}
/**
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();
* @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);
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());
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)
*/
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)
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)
*/
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());
namespace icinga
{
-enum TlsRole
-{
- TlsRoleClient,
- TlsRoleServer
-};
-
/**
* A TLS 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;
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;
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")
<< 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);
}
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;
#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);
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);
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);
%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 {
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; }
%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
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;
{
m_ModuleScope = make_shared<Dictionary>();
- String parentPackage = m_Package;
+ String parentZone = m_Zone;
int parentStatementNum = m_StatementNum;
m_StatementNum = 0;
ConfigCompilerContext::GetInstance()->AddMessage(true, DiagnosticInformation(ex));
}
- m_Package = parentPackage;
+ m_Zone = parentZone;
m_StatementNum = parentStatementNum;
}
| statements statement
;
-statement: type | package | include | include_recursive | library | constant
+statement: type | zone | include | include_recursive | library | constant
{
m_StatementNum++;
}
}
;
-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();
}
}
;
args->Add(filter);
- args->Add(m_Package);
+ args->Add(m_Zone);
$$ = new Value(make_shared<AExpression>(&AExpression::OpObject, args, exprl, DebugInfoRange(@2, @5)));
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)
{
}
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");
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;
Dictionary::Ptr GetScope(void) const;
- String GetPackage(void) const;
+ String GetZone(void) const;
static ConfigItem::Ptr GetObject(const String& type,
const String& name);
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;
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)
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);
}
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);
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. */
};
}
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));
}
/* 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;
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);
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)
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;
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);
};
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)
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
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
******************************************************************************/
#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;
#define API_H
#include "icinga/i2-icinga.h"
+#include "remote/apiclient.h"
#include "base/value.h"
#include <vector>
{
/**
- * 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);
--- /dev/null
+/******************************************************************************
+ * 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;
+}
--- /dev/null
+/******************************************************************************
+ * 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 */
#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
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)
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
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
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
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);
if (cr->GetExecutionEnd() == 0)
cr->SetExecutionEnd(now);
- if (authority.IsEmpty())
+ if (origin.IsLocal())
cr->SetCheckSource(IcingaApplication::GetInstance()->GetNodeName());
bool reachable = IsReachable();
// " 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();
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)
{
}
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;
l_CommentsCache[uid] = GetSelf();
}
- OnCommentAdded(GetSelf(), comment, authority);
+ OnCommentAdded(GetSelf(), comment, origin);
return uid;
}
}
}
-void Checkable::RemoveComment(const String& id, const String& authority)
+void Checkable::RemoveComment(const String& id, const MessageOrigin& origin)
{
Checkable::Ptr owner = GetOwnerByCommentID(id);
l_CommentsCache.erase(id);
}
- OnCommentRemoved(owner, comment, authority);
+ OnCommentRemoved(owner, comment, origin);
}
String Checkable::GetCommentIDFromLegacyID(int id)
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)
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;
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);
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)
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)
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
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);
}
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)
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);
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
return GetEnablePerfdataRaw();
}
-void Checkable::SetEnablePerfdata(bool enabled, const String& authority)
+void Checkable::SetEnablePerfdata(bool enabled, const MessageOrigin& origin)
{
SetOverrideEnablePerfdata(enabled);
}
#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>
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;
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);
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);
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>&,
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 */
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);
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);
/* 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 = "");
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);
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);
+++ /dev/null
-#include "base/dynamicobject.h"
-
-namespace icinga
-{
-
-class Domain : DynamicObject
-{
- [config] Dictionary::Ptr acl;
-};
-
-}
#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"
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;
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)
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;
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);
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);
}
-%type Domain {
- %attribute %dictionary "acl" {
- %attribute %number "*"
- }
-}
-
%type ScheduledDowntime {
%require "host_name",
%attribute %name(Host) "host_name",
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
{
* 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)
#include "icinga/user.h"
#include "icinga/usergroup.h"
#include "icinga/timeperiod.h"
+#include "remote/messageorigin.h"
#include "config/applyrule.h"
#include "base/array.h"
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);
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);
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}
)
* 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"
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)
return str;
}
-
# 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})
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\")")
--- /dev/null
+/******************************************************************************
+ * 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;
+}
* 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 */
--- /dev/null
+/******************************************************************************
+ * 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();
+}
--- /dev/null
+/******************************************************************************
+ * 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 */
--- /dev/null
+/******************************************************************************
+ * 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);
+}
* 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 */
#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;
};
******************************************************************************/
#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());
}
-
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>
{
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;
};
}
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;
};
}
* 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;
}
* 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 */
* 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) "*"
}
}
* 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();
}
* 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 */
--- /dev/null
+#include "base/dynamicobject.h"
+
+namespace icinga
+{
+
+class Zone : DynamicObject
+{
+ [config] String parent (ParentRaw);
+ [config] Array::Ptr endpoints (EndpointsRaw);
+};
+
+}
BOOST_AUTO_TEST_CASE(invoke)
{
+ Utility::Sleep(5);
+
int counter;
Timer::Ptr timer = make_shared<Timer>();
timer->OnTimerExpired.connect(boost::bind(&Callback, &counter));
#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);