Refactor the agent and cluster components.

Refs #6107
This commit is contained in:
Gunnar Beutner 2014-05-03 20:02:22 +02:00 committed by Gunnar Beutner
parent 0571d8a464
commit 45270f1bb8
86 changed files with 2708 additions and 3279 deletions

View File

@ -1,6 +1,4 @@
add_subdirectory(agent)
add_subdirectory(checker)
add_subdirectory(cluster)
add_subdirectory(compat)
add_subdirectory(db_ido_mysql)
add_subdirectory(db_ido_pgsql)

View File

@ -1,39 +0,0 @@
# Icinga 2
# Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
mkclass_target(agentlistener.ti agentlistener.th)
mkembedconfig_target(agent-type.conf agent-type.cpp)
add_library(agent SHARED
agentchecktask.cpp agentlistener.cpp agentlistener.th
agent-type.cpp
)
target_link_libraries(agent ${Boost_LIBRARIES} base config icinga remote)
set_target_properties (
agent PROPERTIES
INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}/icinga2
FOLDER Components
)
install(TARGETS agent RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2)
#install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/agent\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/agent/inventory\")")

View File

@ -1,39 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
%type AgentListener {
%attribute %string "cert_path",
%require "cert_path",
%attribute %string "key_path",
%require "key_path",
%attribute %string "ca_path",
%require "ca_path",
%attribute %string "crl_path",
%attribute %string "bind_host",
%attribute %string "bind_port",
%attribute %string "upstream_name",
%attribute %string "upstream_host",
%attribute %string "upstream_port",
%attribute %number "upstream_interval"
}

View File

@ -1,133 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "agent/agentchecktask.h"
#include "agent/agentlistener.h"
#include "icinga/service.h"
#include "icinga/checkcommand.h"
#include "icinga/macroprocessor.h"
#include "icinga/icingaapplication.h"
#include "base/application.h"
#include "base/objectlock.h"
#include "base/convert.h"
#include "base/utility.h"
#include "base/initialize.h"
#include "base/scriptfunction.h"
#include "base/dynamictype.h"
using namespace icinga;
boost::mutex l_Mutex;
std::map<Checkable::Ptr, double> l_PendingChecks;
Timer::Ptr l_AgentTimer;
INITIALIZE_ONCE(&AgentCheckTask::StaticInitialize);
REGISTER_SCRIPTFUNCTION(AgentCheck, &AgentCheckTask::ScriptFunc);
void AgentCheckTask::StaticInitialize(void)
{
l_AgentTimer = make_shared<Timer>();
l_AgentTimer->OnTimerExpired.connect(boost::bind(&AgentCheckTask::AgentTimerHandler));
l_AgentTimer->SetInterval(60);
l_AgentTimer->Start();
}
void AgentCheckTask::AgentTimerHandler(void)
{
boost::mutex::scoped_lock lock(l_Mutex);
std::map<Checkable::Ptr, double> newmap;
std::pair<Checkable::Ptr, double> kv;
double now = Utility::GetTime();
BOOST_FOREACH(kv, l_PendingChecks) {
if (kv.second < now - 60 && kv.first->IsCheckPending() && !SendResult(kv.first, false)) {
CheckResult::Ptr cr = make_shared<CheckResult>();
cr->SetOutput("Agent isn't responding.");
cr->SetState(ServiceCritical);
kv.first->ProcessCheckResult(cr);
} else {
newmap.insert(kv);
}
}
l_PendingChecks.swap(newmap);
}
bool AgentCheckTask::SendResult(const Checkable::Ptr& checkable, bool enqueue)
{
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
MacroProcessor::ResolverList resolvers;
if (service)
resolvers.push_back(std::make_pair("service", service));
resolvers.push_back(std::make_pair("host", host));
resolvers.push_back(std::make_pair("command", checkable->GetCheckCommand()));
resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
String agent_identity = MacroProcessor::ResolveMacros("$agent_identity$", resolvers, checkable->GetLastCheckResult());
String agent_host = MacroProcessor::ResolveMacros("$agent_host$", resolvers, checkable->GetLastCheckResult());
String agent_service = MacroProcessor::ResolveMacros("$agent_service$", resolvers, checkable->GetLastCheckResult());
if (agent_identity.IsEmpty() || agent_host.IsEmpty()) {
Log(LogWarning, "agent", "'agent_name' and 'agent_host' must be set for agent checks.");
return false;
}
String agent_peer_host = MacroProcessor::ResolveMacros("$agent_peer_host$", resolvers, checkable->GetLastCheckResult());
String agent_peer_port = MacroProcessor::ResolveMacros("$agent_peer_port$", resolvers, checkable->GetLastCheckResult());
double now = Utility::GetTime();
BOOST_FOREACH(const AgentListener::Ptr& al, DynamicType::GetObjects<AgentListener>()) {
double seen = al->GetAgentSeen(agent_identity);
if (seen < now - 300)
continue;
CheckResult::Ptr cr = al->GetCheckResult(agent_identity, agent_host, agent_service);
if (cr) {
checkable->ProcessCheckResult(cr);
return true;
}
}
if (enqueue) {
{
boost::mutex::scoped_lock lock(l_Mutex);
l_PendingChecks[checkable] = now;
}
BOOST_FOREACH(const AgentListener::Ptr& al, DynamicType::GetObjects<AgentListener>()) {
if (!agent_peer_host.IsEmpty() && !agent_peer_port.IsEmpty())
al->AddConnection(agent_peer_host, agent_peer_port);
}
}
return false;
}
void AgentCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr)
{
SendResult(checkable, true);
}

View File

@ -1,323 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "agent/agentlistener.h"
#include "remote/jsonrpc.h"
#include "icinga/icingaapplication.h"
#include "base/netstring.h"
#include "base/dynamictype.h"
#include "base/logger_fwd.h"
#include "base/objectlock.h"
#include "base/networkstream.h"
#include "base/application.h"
#include "base/context.h"
#include <fstream>
using namespace icinga;
REGISTER_TYPE(AgentListener);
/**
* Starts the component.
*/
void AgentListener::Start(void)
{
DynamicObject::Start();
m_Results = make_shared<Dictionary>();
/* set up SSL context */
shared_ptr<X509> cert = GetX509Certificate(GetCertPath());
SetIdentity(GetCertificateCN(cert));
Log(LogInformation, "agent", "My identity: " + GetIdentity());
m_SSLContext = MakeSSLContext(GetCertPath(), GetKeyPath(), GetCaPath());
if (!GetCrlPath().IsEmpty())
AddCRLToSSLContext(m_SSLContext, GetCrlPath());
/* create the primary JSON-RPC listener */
if (!GetBindPort().IsEmpty())
AddListener(GetBindPort());
m_AgentTimer = make_shared<Timer>();
m_AgentTimer->OnTimerExpired.connect(boost::bind(&AgentListener::AgentTimerHandler, this));
m_AgentTimer->SetInterval(GetUpstreamInterval());
m_AgentTimer->Start();
m_AgentTimer->Reschedule(0);
}
shared_ptr<SSL_CTX> AgentListener::GetSSLContext(void) const
{
return m_SSLContext;
}
String AgentListener::GetInventoryDir(void)
{
return Application::GetLocalStateDir() + "/lib/icinga2/agent/inventory/";
}
/**
* Creates a new JSON-RPC listener on the specified port.
*
* @param service The port to listen on.
*/
void AgentListener::AddListener(const String& service)
{
ObjectLock olock(this);
shared_ptr<SSL_CTX> sslContext = m_SSLContext;
if (!sslContext)
BOOST_THROW_EXCEPTION(std::logic_error("SSL context is required for AddListener()"));
std::ostringstream s;
s << "Adding new listener: port " << service;
Log(LogInformation, "agent", s.str());
TcpSocket::Ptr server = make_shared<TcpSocket>();
server->Bind(service, AF_INET6);
boost::thread thread(boost::bind(&AgentListener::ListenerThreadProc, this, server));
thread.detach();
m_Servers.insert(server);
}
void AgentListener::ListenerThreadProc(const Socket::Ptr& server)
{
Utility::SetThreadName("Cluster Listener");
server->Listen();
for (;;) {
Socket::Ptr client = server->Accept();
Utility::QueueAsyncCallback(boost::bind(&AgentListener::NewClientHandler, this, client, TlsRoleServer));
}
}
/**
* Creates a new JSON-RPC client and connects to the specified host and port.
*
* @param node The remote host.
* @param service The remote port.
*/
void AgentListener::AddConnection(const String& node, const String& service) {
{
ObjectLock olock(this);
shared_ptr<SSL_CTX> sslContext = m_SSLContext;
if (!sslContext)
BOOST_THROW_EXCEPTION(std::logic_error("SSL context is required for AddConnection()"));
}
TcpSocket::Ptr client = make_shared<TcpSocket>();
client->Connect(node, service);
Utility::QueueAsyncCallback(boost::bind(&AgentListener::NewClientHandler, this, client, TlsRoleClient));
}
/**
* Processes a new client connection.
*
* @param client The new client.
*/
void AgentListener::NewClientHandler(const Socket::Ptr& client, TlsRole role)
{
CONTEXT("Handling new agent client connection");
TlsStream::Ptr tlsStream;
{
ObjectLock olock(this);
tlsStream = make_shared<TlsStream>(client, role, m_SSLContext);
}
tlsStream->Handshake();
shared_ptr<X509> cert = tlsStream->GetPeerCertificate();
String identity = GetCertificateCN(cert);
Log(LogInformation, "agent", "New client connection for identity '" + identity + "'");
if (identity != GetUpstreamName()) {
Dictionary::Ptr request = make_shared<Dictionary>();
request->Set("method", "get_crs");
JsonRpc::SendMessage(tlsStream, request);
}
try {
Dictionary::Ptr message = JsonRpc::ReadMessage(tlsStream);
MessageHandler(tlsStream, identity, message);
} catch (const std::exception& ex) {
Log(LogWarning, "agent", "Error while reading JSON-RPC message for agent '" + identity + "': " + DiagnosticInformation(ex));
}
tlsStream->Close();
}
void AgentListener::MessageHandler(const TlsStream::Ptr& sender, const String& identity, const Dictionary::Ptr& message)
{
CONTEXT("Processing agent message of type '" + message->Get("method") + "'");
String method = message->Get("method");
if (identity == GetUpstreamName()) {
if (method == "get_crs") {
Dictionary::Ptr hosts = make_shared<Dictionary>();
BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjects<Host>()) {
Dictionary::Ptr hostInfo = make_shared<Dictionary>();
hostInfo->Set("cr", Serialize(host->GetLastCheckResult()));
Dictionary::Ptr services = make_shared<Dictionary>();
BOOST_FOREACH(const Service::Ptr& service, host->GetServices()) {
Dictionary::Ptr serviceInfo = make_shared<Dictionary>();
serviceInfo->Set("cr", Serialize(service->GetLastCheckResult()));
services->Set(service->GetShortName(), serviceInfo);
}
hostInfo->Set("services", services);
hosts->Set(host->GetName(), hostInfo);
}
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("hosts", hosts);
Dictionary::Ptr request = make_shared<Dictionary>();
request->Set("method", "push_crs");
request->Set("params", params);
JsonRpc::SendMessage(sender, request);
}
}
if (method == "push_crs") {
Value paramsv = message->Get("params");
if (paramsv.IsEmpty() || !paramsv.IsObjectType<Dictionary>())
return;
Dictionary::Ptr params = paramsv;
params->Set("seen", Utility::GetTime());
Dictionary::Ptr inventoryDescr = make_shared<Dictionary>();
inventoryDescr->Set("identity", identity);
inventoryDescr->Set("params", params);
String inventoryFile = GetInventoryDir() + SHA256(identity);
String inventoryTempFile = inventoryFile + ".tmp";
std::ofstream fp(inventoryTempFile.CStr(), std::ofstream::out | std::ostream::trunc);
fp << JsonSerialize(inventoryDescr);
fp.close();
#ifdef _WIN32
_unlink(inventoryFile.CStr());
#endif /* _WIN32 */
if (rename(inventoryTempFile.CStr(), inventoryFile.CStr()) < 0) {
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("rename")
<< boost::errinfo_errno(errno)
<< boost::errinfo_file_name(inventoryTempFile));
}
m_Results->Set(identity, params);
}
}
double AgentListener::GetAgentSeen(const String& agentIdentity)
{
Dictionary::Ptr agentparams = m_Results->Get(agentIdentity);
if (!agentparams)
return 0;
return agentparams->Get("seen");
}
CheckResult::Ptr AgentListener::GetCheckResult(const String& agentIdentity, const String& hostName, const String& serviceName)
{
Dictionary::Ptr agentparams = m_Results->Get(agentIdentity);
if (!agentparams)
return CheckResult::Ptr();
Value hostsv = agentparams->Get("hosts");
if (hostsv.IsEmpty() || !hostsv.IsObjectType<Dictionary>())
return CheckResult::Ptr();
Dictionary::Ptr hosts = hostsv;
Value hostv = hosts->Get(hostName);
if (hostv.IsEmpty() || !hostv.IsObjectType<Dictionary>())
return CheckResult::Ptr();
Dictionary::Ptr host = hostv;
if (serviceName.IsEmpty()) {
Value hostcrv = Deserialize(host->Get("cr"));
if (hostcrv.IsEmpty() || !hostcrv.IsObjectType<CheckResult>())
return CheckResult::Ptr();
return hostcrv;
} else {
Value servicesv = host->Get("services");
if (servicesv.IsEmpty() || !servicesv.IsObjectType<Dictionary>())
return CheckResult::Ptr();
Dictionary::Ptr services = servicesv;
Value servicev = services->Get(serviceName);
if (servicev.IsEmpty() || !servicev.IsObjectType<Dictionary>())
return CheckResult::Ptr();
Dictionary::Ptr service = servicev;
Value servicecrv = Deserialize(service->Get("cr"));
if (servicecrv.IsEmpty() || !servicecrv.IsObjectType<CheckResult>())
return CheckResult::Ptr();
return servicecrv;
}
}
void AgentListener::AgentTimerHandler(void)
{
String host = GetUpstreamHost();
String port = GetUpstreamPort();
if (host.IsEmpty() || port.IsEmpty())
return;
AddConnection(host, port);
}

View File

@ -1,24 +0,0 @@
#include "base/dynamicobject.h"
#include "base/application.h"
namespace icinga
{
class AgentListener : DynamicObject
{
[config] String cert_path;
[config] String key_path;
[config] String ca_path;
[config] String crl_path;
[config] String bind_host;
[config] String bind_port;
[config] String upstream_host;
[config] String upstream_port;
[config] String upstream_name;
[config] int upstream_interval {
default {{{ return 60; }}}
};
String identity;
};
}

View File

@ -21,7 +21,7 @@ mkembedconfig_target(checker-type.conf checker-type.cpp)
add_library(checker SHARED checkercomponent.cpp checkercomponent.th checker-type.cpp)
target_link_libraries(checker ${Boost_LIBRARIES} base config icinga)
target_link_libraries(checker ${Boost_LIBRARIES} base config icinga remote)
set_target_properties (
checker PROPERTIES

View File

@ -20,6 +20,7 @@
#include "checker/checkercomponent.h"
#include "icinga/icingaapplication.h"
#include "icinga/cib.h"
#include "remote/apilistener.h"
#include "base/dynamictype.h"
#include "base/objectlock.h"
#include "base/utility.h"
@ -63,7 +64,6 @@ void CheckerComponent::OnConfigLoaded(void)
{
DynamicObject::OnStarted.connect(bind(&CheckerComponent::ObjectHandler, this, _1));
DynamicObject::OnStopped.connect(bind(&CheckerComponent::ObjectHandler, this, _1));
DynamicObject::OnAuthorityChanged.connect(bind(&CheckerComponent::ObjectHandler, this, _1));
Checkable::OnNextCheckChanged.connect(bind(&CheckerComponent::NextCheckChangedHandler, this, _1));
}
@ -117,12 +117,6 @@ void CheckerComponent::CheckThreadProc(void)
CheckTimeView::iterator it = idx.begin();
Checkable::Ptr checkable = *it;
if (!checkable->HasAuthority("checker")) {
m_IdleCheckables.erase(checkable);
continue;
}
double wait = checkable->GetNextCheck() - Utility::GetTime();
if (wait > 0) {
@ -227,7 +221,7 @@ void CheckerComponent::ExecuteCheckHelper(const Checkable::Ptr& checkable)
if (it != m_PendingCheckables.end()) {
m_PendingCheckables.erase(it);
if (checkable->IsActive() && checkable->HasAuthority("checker"))
if (checkable->IsActive())
m_IdleCheckables.insert(checkable);
m_CV.notify_all();
@ -257,10 +251,13 @@ void CheckerComponent::ObjectHandler(const DynamicObject::Ptr& object)
Checkable::Ptr checkable = static_pointer_cast<Checkable>(object);
Zone::Ptr zone = Zone::GetByName(checkable->GetZone());
bool same_zone = (!zone || Zone::GetLocalZone() == zone);
{
boost::mutex::scoped_lock lock(m_Mutex);
if (object->IsActive() && object->HasAuthority("checker")) {
if (object->IsActive() && same_zone) {
if (m_PendingCheckables.find(checkable) != m_PendingCheckables.end())
return;

View File

@ -1,40 +0,0 @@
# Icinga 2
# Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
mkclass_target(clusterlistener.ti clusterlistener.th)
mkembedconfig_target(cluster-type.conf cluster-type.cpp)
add_library(cluster SHARED
clusterchecktask.cpp clusterlink.cpp clusterlistener.cpp clusterlistener.th
cluster-type.cpp
)
target_link_libraries(cluster ${Boost_LIBRARIES} base config icinga remote)
set_target_properties (
cluster PROPERTIES
INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR}/icinga2
FOLDER Components
)
install(TARGETS cluster RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2)
#install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/cluster\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/cluster/config\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/cluster/log\")")

View File

@ -1,38 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
%type ClusterListener {
%attribute %string "cert_path",
%require "cert_path",
%attribute %string "key_path",
%require "key_path",
%attribute %string "ca_path",
%require "ca_path",
%attribute %string "crl_path",
%attribute %string "bind_host",
%attribute %string "bind_port",
%attribute %array "peers" {
%attribute %name(Endpoint) "*"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,139 +0,0 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef CLUSTERLISTENER_H
#define CLUSTERLISTENER_H
#include "cluster/clusterlistener.th"
#include "cluster/clusterlink.h"
#include "base/dynamicobject.h"
#include "base/timer.h"
#include "base/array.h"
#include "base/tcpsocket.h"
#include "base/tlsstream.h"
#include "base/utility.h"
#include "base/tlsutility.h"
#include "base/stdiostream.h"
#include "base/workqueue.h"
#include "icinga/service.h"
#include "remote/endpoint.h"
namespace icinga
{
/**
* @ingroup cluster
*/
struct EndpointPeerInfo
{
double Seen;
Array::Ptr Peers;
};
/**
* @ingroup cluster
*/
class ClusterListener : public ObjectImpl<ClusterListener>
{
public:
DECLARE_PTR_TYPEDEFS(ClusterListener);
DECLARE_TYPENAME(ClusterListener);
static Value StatsFunc(Dictionary::Ptr& status, Dictionary::Ptr& perfdata);
virtual void Start(void);
virtual void Stop(void);
shared_ptr<SSL_CTX> GetSSLContext(void) const;
String GetClusterDir(void) const;
std::pair<Dictionary::Ptr, Dictionary::Ptr> GetClusterStatus(void);
private:
shared_ptr<SSL_CTX> m_SSLContext;
WorkQueue m_RelayQueue;
WorkQueue m_MessageQueue;
WorkQueue m_LogQueue;
Timer::Ptr m_ClusterTimer;
void ClusterTimerHandler(void);
std::set<TcpSocket::Ptr> m_Servers;
void AddListener(const String& service);
void AddConnection(const String& node, const String& service);
static void ConfigGlobHandler(const Dictionary::Ptr& config, const String& file, bool basename);
void NewClientHandler(const Socket::Ptr& client, TlsRole role);
void ListenerThreadProc(const Socket::Ptr& server);
std::map<String, EndpointPeerInfo> m_VisibleEndpoints;
void UpdateLinks(void);
void AsyncRelayMessage(const Endpoint::Ptr& source, const Endpoint::Ptr& destination, const Dictionary::Ptr& message, bool persistent);
void RelayMessage(const Endpoint::Ptr& source, const Endpoint::Ptr& destination, const Dictionary::Ptr& message, bool persistent);
void OpenLogFile(void);
void RotateLogFile(void);
void CloseLogFile(void);
static void LogGlobHandler(std::vector<int>& files, const String& file);
void ReplayLog(const Endpoint::Ptr& endpoint, const Stream::Ptr& stream);
Stream::Ptr m_LogFile;
size_t m_LogMessageCount;
void CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const String& authority);
void NextCheckChangedHandler(const Checkable::Ptr& checkable, double nextCheck, const String& authority);
void NextNotificationChangedHandler(const Notification::Ptr& notification, double nextCheck, const String& authority);
void ForceNextCheckChangedHandler(const Checkable::Ptr& checkable, bool forced, const String& authority);
void ForceNextNotificationChangedHandler(const Checkable::Ptr& checkable, bool forced, const String& authority);
void EnableActiveChecksChangedHandler(const Checkable::Ptr& checkable, bool enabled, const String& authority);
void EnablePassiveChecksChangedHandler(const Checkable::Ptr& checkable, bool enabled, const String& authority);
void EnableNotificationsChangedHandler(const Checkable::Ptr& checkable, bool enabled, const String& authority);
void EnableFlappingChangedHandler(const Checkable::Ptr& checkable, bool enabled, const String& authority);
void CommentAddedHandler(const Checkable::Ptr& checkable, const Comment::Ptr& comment, const String& authority);
void CommentRemovedHandler(const Checkable::Ptr& checkable, const Comment::Ptr& comment, const String& authority);
void DowntimeAddedHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, const String& authority);
void DowntimeRemovedHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, const String& authority);
void AcknowledgementSetHandler(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, double expiry, const String& authority);
void AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const String& authority);
void AsyncMessageHandler(const Endpoint::Ptr& sender, const Dictionary::Ptr& message);
void MessageHandler(const Endpoint::Ptr& sender, const Dictionary::Ptr& message);
bool IsAuthority(const DynamicObject::Ptr& object, const String& type);
void UpdateAuthority(void);
static bool SupportsChecks(void);
static bool SupportsNotifications(void);
static bool SupportsFeature(const String& name);
void SetSecurityInfo(const Dictionary::Ptr& message, const DynamicObject::Ptr& object, int privs);
void PersistMessage(const Endpoint::Ptr& source, const Dictionary::Ptr& message);
static void MessageExceptionHandler(boost::exception_ptr exp);
};
}
#endif /* CLUSTERLISTENER_H */

View File

@ -21,7 +21,7 @@ mkembedconfig_target(demo-type.conf demo-type.cpp)
add_library(demo SHARED demo.cpp demo.th demo-type.cpp)
target_link_libraries(demo ${Boost_LIBRARIES} base config icinga)
target_link_libraries(demo ${Boost_LIBRARIES} base config icinga remote)
set_target_properties (
demo PROPERTIES

View File

@ -18,6 +18,8 @@
******************************************************************************/
#include "demo/demo.h"
#include "remote/apilistener.h"
#include "remote/apifunction.h"
#include "base/dynamictype.h"
#include "base/logger_fwd.h"
@ -25,6 +27,8 @@ using namespace icinga;
REGISTER_TYPE(Demo);
REGISTER_APIFUNCTION(HelloWorld, demo, &Demo::DemoMessageHandler);
/**
* Starts the component.
*/
@ -39,19 +43,23 @@ void Demo::Start(void)
}
/**
* Stops the component.
*/
void Demo::Stop(void)
{
/* Nothing to do here. */
}
/**
* Periodically sends a demo::HelloWorld message.
*
* @param - Event arguments for the timer.
* Periodically broadcasts an API message.
*/
void Demo::DemoTimerHandler(void)
{
Log(LogInformation, "demo", "Hello World!");
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");
}
}
Value Demo::DemoMessageHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
Log(LogInformation, "demo", "Got demo message from '" + origin.FromClient->GetEndpoint()->GetName() + "'");
return Empty;
}

View File

@ -21,6 +21,7 @@
#define DEMO_H
#include "demo/demo.th"
#include "remote/messageorigin.h"
#include "base/timer.h"
namespace icinga
@ -36,7 +37,9 @@ public:
DECLARE_TYPENAME(Demo);
virtual void Start(void);
virtual void Stop(void);
static Value DemoMessageHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
private:
Timer::Ptr m_DemoTimer;

View File

@ -40,7 +40,7 @@ install_if_not_exists(icinga2/conf.d/hosts/localhost/users.conf ${CMAKE_INSTALL_
install_if_not_exists(icinga2/conf.d/notifications.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
install_if_not_exists(icinga2/conf.d/timeperiods.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
install_if_not_exists(icinga2/conf.d/users.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
install_if_not_exists(icinga2/features-available/agent.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available)
install_if_not_exists(icinga2/features-available/api.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available)
install_if_not_exists(icinga2/features-available/checker.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available)
install_if_not_exists(icinga2/features-available/command.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available)
install_if_not_exists(icinga2/features-available/compatlog.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-available)
@ -59,6 +59,8 @@ install_if_not_exists(icinga2/scripts/mail-host-notification.sh ${CMAKE_INSTALL_
install_if_not_exists(icinga2/scripts/mail-service-notification.sh ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/scripts)
install_if_not_exists(logrotate.d/icinga2 ${CMAKE_INSTALL_SYSCONFDIR}/logrotate.d)
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/pki\")")
if(NOT WIN32)
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled\")")
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ../features-available/checker.conf \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_SYSCONFDIR}/icinga2/features-enabled/checker.conf\")")

View File

@ -1,7 +1,13 @@
/**
* This file defines global constants which can be used in
* the other configuration files. At a minimum the
* PluginDir constant should be defined.
* the other configuration files.
*/
/* The directory which contains the plugins from the Monitoring Plugins project. */
const PluginDir = "/usr/lib/nagios/plugins"
/* Our local instance name. This should be the common name from the API certificate */
const NodeName = "localhost"
/* Our local zone name. */
const ZoneName = "master"

View File

@ -1,11 +0,0 @@
/**
* The agent listener accepts checks from agents.
*/
library "agent"
object AgentListener "agent" {
cert_path = SysconfDir + "/icinga2/pki/your-master.crt"
key_path = SysconfDir + "/icinga2/pki/your-master.key"
ca_path = SysconfDir + "/icinga2/pki/ca.crt"
}

View File

@ -0,0 +1,27 @@
/**
* The API listener is used for distributed monitoring setups.
*/
object ApiListener "api" {
cert_path = SysconfDir + "/icinga2/pki/" + NodeName + ".crt"
key_path = SysconfDir + "/icinga2/pki/" + NodeName + ".key"
ca_path = SysconfDir + "/icinga2/pki/ca.crt"
}
object Endpoint NodeName {
host = NodeName
}
object Zone ZoneName {
endpoints = [ NodeName ]
}
/*object Endpoint "satellite.example.org" {
host = "satellite.example.org"
}
object Zone "satellite" {
parent = "master"
endpoints = [ "satellite.example.org" ]
}*/

View File

@ -652,6 +652,9 @@ VOID WINAPI ServiceMain(DWORD argc, LPSTR *argv)
*/
int main(int argc, char **argv)
{
/* must be called before using any other libbase functions */
Application::InitializeBase();
/* Set command-line arguments. */
Application::SetArgC(argc);
Application::SetArgV(argv);

View File

@ -103,6 +103,11 @@ Application::~Application(void)
m_Instance = NULL;
}
void Application::InitializeBase(void)
{
Utility::ExecuteDeferredInitializers();
}
/**
* Retrieves a pointer to the application singleton object.
*

View File

@ -26,9 +26,8 @@
#include "base/dynamicobject.h"
#include "base/process.h"
namespace icinga {
class Component;
namespace icinga
{
/**
* Abstract base class for applications.
@ -41,6 +40,8 @@ public:
~Application(void);
static void InitializeBase(void);
static Application::Ptr GetInstance(void);
int Run(void);

View File

@ -44,8 +44,7 @@ INITIALIZE_ONCE(&DynamicObject::StaticInitialize);
boost::signals2::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnStarted;
boost::signals2::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnStopped;
boost::signals2::signal<void (const DynamicObject::Ptr&, const String&)> DynamicObject::OnStateChanged;
boost::signals2::signal<void (const DynamicObject::Ptr&, const String&, bool)> DynamicObject::OnAuthorityChanged;
boost::signals2::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnStateChanged;
boost::signals2::signal<void (const DynamicObject::Ptr&)> DynamicObject::OnVarsChanged;
void DynamicObject::StaticInitialize(void)
@ -71,56 +70,6 @@ bool DynamicObject::IsActive(void) const
return GetActive();
}
void DynamicObject::SetAuthority(const String& type, bool value)
{
ASSERT(!OwnsLock());
{
ObjectLock olock(this);
bool old_value = HasAuthority(type);
if (old_value == value)
return;
if (GetAuthorityInfo() == NULL)
SetAuthorityInfo(make_shared<Dictionary>());
GetAuthorityInfo()->Set(type, value);
}
OnAuthorityChanged(GetSelf(), type, value);
}
bool DynamicObject::HasAuthority(const String& type) const
{
Dictionary::Ptr authorityInfo = GetAuthorityInfo();
if (!authorityInfo || !authorityInfo->Contains(type))
return true;
return authorityInfo->Get(type);
}
void DynamicObject::SetPrivileges(const String& instance, int privs)
{
m_Privileges[instance] = privs;
}
bool DynamicObject::HasPrivileges(const String& instance, int privs) const
{
if (privs == 0)
return true;
std::map<String, int>::const_iterator it;
it = m_Privileges.find(instance);
if (it == m_Privileges.end())
return false;
return (it->second & privs) == privs;
}
void DynamicObject::SetExtension(const String& key, const Object::Ptr& object)
{
Dictionary::Ptr extensions = GetExtensions();

View File

@ -77,8 +77,7 @@ public:
static boost::signals2::signal<void (const DynamicObject::Ptr&)> OnStarted;
static boost::signals2::signal<void (const DynamicObject::Ptr&)> OnStopped;
static boost::signals2::signal<void (const DynamicObject::Ptr&, const String&)> OnStateChanged;
static boost::signals2::signal<void (const DynamicObject::Ptr&, const String&, bool)> OnAuthorityChanged;
static boost::signals2::signal<void (const DynamicObject::Ptr&)> OnStateChanged;
static boost::signals2::signal<void (const DynamicObject::Ptr&)> OnVarsChanged;
Value InvokeMethod(const String& method, const std::vector<Value>& arguments);
@ -87,12 +86,6 @@ public:
bool IsActive(void) const;
void SetAuthority(const String& type, bool value);
bool HasAuthority(const String& type) const;
void SetPrivileges(const String& instance, int privs);
bool HasPrivileges(const String& instance, int privs) const;
void SetExtension(const String& key, const Object::Ptr& object);
Object::Ptr GetExtension(const String& key);
void ClearExtension(const String& key);

View File

@ -20,12 +20,10 @@ abstract class DynamicObject
}}}
};
[config, get_protected] String type (TypeName);
[config] String package;
[config] String zone;
[config, get_protected] Array::Ptr templates;
[config] Dictionary::Ptr methods;
[config] Dictionary::Ptr vars (VarsRaw);
[config] Array::Ptr domains;
[config] Array::Ptr authorities;
[get_protected] bool active;
[get_protected] bool start_called;
[get_protected] bool stop_called;

View File

@ -30,11 +30,7 @@ typedef void (*InitializeFunc)(void);
inline bool InitializeOnceHelper(InitializeFunc func)
{
if (Utility::GetLoadingLibrary())
Utility::AddDeferredInitializer(func);
else
func();
Utility::AddDeferredInitializer(func);
return true;
}

View File

@ -1,21 +1,21 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "base/serializer.h"
#include "base/type.h"

View File

@ -27,7 +27,8 @@
#include <boost/thread/condition_variable.hpp>
#include <boost/signals2.hpp>
namespace icinga {
namespace icinga
{
/**
* Base class for connection-oriented sockets.

View File

@ -29,6 +29,12 @@
namespace icinga
{
enum ConnectionRole
{
RoleClient,
RoleServer
};
struct ReadLineContext
{
ReadLineContext(void) : Buffer(NULL), Size(0), Eof(false), MustRead(true)

View File

@ -21,7 +21,6 @@
#include "base/application.h"
#include "base/debug.h"
#include "base/utility.h"
#include "base/logger_fwd.h"
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/thread/thread.hpp>
@ -48,7 +47,7 @@ struct icinga::TimerNextExtractor
* @param wtimer Weak pointer to the timer.
* @returns The next timestamp
*/
double operator()(const weak_ptr<Timer>& wtimer)
double operator()(const weak_ptr<Timer>& wtimer) const
{
Timer::Ptr timer = wtimer.lock();
@ -87,7 +86,7 @@ void Timer::Initialize(void)
{
boost::mutex::scoped_lock lock(l_Mutex);
l_StopThread = false;
l_Thread = boost::thread(boost::bind(&Timer::TimerThreadProc));
l_Thread = boost::thread(&Timer::TimerThreadProc);
}
/**
@ -296,7 +295,7 @@ void Timer::TimerThreadProc(void)
double wait = timer->m_Next - Utility::GetTime();
if (wait > 0) {
if (wait > 0.01) {
/* Make sure the timer we just examined can be destroyed while we're waiting. */
timer.reset();

View File

@ -36,7 +36,7 @@ bool I2_EXPORT TlsStream::m_SSLIndexInitialized = false;
* @param role The role of the client.
* @param sslContext The SSL context for the client.
*/
TlsStream::TlsStream(const Socket::Ptr& socket, TlsRole role, shared_ptr<SSL_CTX> sslContext)
TlsStream::TlsStream(const Socket::Ptr& socket, ConnectionRole role, shared_ptr<SSL_CTX> sslContext)
: m_Socket(socket), m_Role(role)
{
m_SSL = shared_ptr<SSL>(SSL_new(sslContext.get()), SSL_free);
@ -62,7 +62,7 @@ TlsStream::TlsStream(const Socket::Ptr& socket, TlsRole role, shared_ptr<SSL_CTX
BIO_set_nbio(m_BIO, 1);
SSL_set_bio(m_SSL.get(), m_BIO, m_BIO);
if (m_Role == TlsRoleServer)
if (m_Role == RoleServer)
SSL_set_accept_state(m_SSL.get());
else
SSL_set_connect_state(m_SSL.get());
@ -90,13 +90,11 @@ shared_ptr<X509> TlsStream::GetPeerCertificate(void) const
void TlsStream::Handshake(void)
{
ASSERT(!OwnsLock());
for (;;) {
int rc, err;
{
ObjectLock olock(this);
boost::mutex::scoped_lock lock(m_SSLLock);
rc = SSL_do_handshake(m_SSL.get());
if (rc > 0)
@ -128,15 +126,13 @@ void TlsStream::Handshake(void)
*/
size_t TlsStream::Read(void *buffer, size_t count)
{
ASSERT(!OwnsLock());
size_t left = count;
while (left > 0) {
int rc, err;
{
ObjectLock olock(this);
boost::mutex::scoped_lock lock(m_SSLLock);
rc = SSL_read(m_SSL.get(), ((char *)buffer) + (count - left), left);
if (rc <= 0)
@ -169,15 +165,13 @@ size_t TlsStream::Read(void *buffer, size_t count)
void TlsStream::Write(const void *buffer, size_t count)
{
ASSERT(!OwnsLock());
size_t left = count;
while (left > 0) {
int rc, err;
{
ObjectLock olock(this);
boost::mutex::scoped_lock lock(m_SSLLock);
rc = SSL_write(m_SSL.get(), ((const char *)buffer) + (count - left), left);
if (rc <= 0)
@ -211,13 +205,11 @@ void TlsStream::Write(const void *buffer, size_t count)
*/
void TlsStream::Close(void)
{
ASSERT(!OwnsLock());
for (;;) {
int rc, err;
{
ObjectLock olock(this);
boost::mutex::scoped_lock lock(m_SSLLock);
do {
rc = SSL_shutdown(m_SSL.get());

View File

@ -28,12 +28,6 @@
namespace icinga
{
enum TlsRole
{
TlsRoleClient,
TlsRoleServer
};
/**
* A TLS stream.
*
@ -44,7 +38,7 @@ class I2_BASE_API TlsStream : public Stream
public:
DECLARE_PTR_TYPEDEFS(TlsStream);
TlsStream(const Socket::Ptr& socket, TlsRole role, shared_ptr<SSL_CTX> sslContext);
TlsStream(const Socket::Ptr& socket, ConnectionRole role, shared_ptr<SSL_CTX> sslContext);
shared_ptr<X509> GetClientCertificate(void) const;
shared_ptr<X509> GetPeerCertificate(void) const;
@ -59,11 +53,12 @@ public:
virtual bool IsEof(void) const;
private:
boost::mutex m_SSLLock;
shared_ptr<SSL> m_SSL;
BIO *m_BIO;
Socket::Ptr m_Socket;
TlsRole m_Role;
ConnectionRole m_Role;
static int m_SSLIndex;
static bool m_SSLIndexInitialized;

View File

@ -370,21 +370,9 @@ Utility::LoadExtensionLibrary(const String& library)
Log(LogInformation, "base", "Loading library '" + path + "'");
m_DeferredInitializers.reset(new std::vector<boost::function<void(void)> >());
#ifdef _WIN32
HMODULE hModule;
HMODULE hModule = LoadLibrary(path.CStr());
try {
SetLoadingLibrary(true);
hModule = LoadLibrary(path.CStr());
} catch (...) {
SetLoadingLibrary(false);
throw;
}
SetLoadingLibrary(false);
if (hModule == NULL) {
BOOST_THROW_EXCEPTION(win32_error()
<< boost::errinfo_api_function("LoadLibrary")
@ -392,44 +380,35 @@ Utility::LoadExtensionLibrary(const String& library)
<< boost::errinfo_file_name(path));
}
#else /* _WIN32 */
void *hModule;
void *hModule = dlopen(path.CStr(), RTLD_NOW);
try {
hModule = dlopen(path.CStr(), RTLD_NOW);
} catch (...) {
SetLoadingLibrary(false);
throw;
}
SetLoadingLibrary(false);
if (hModule == NULL) {
BOOST_THROW_EXCEPTION(std::runtime_error("Could not load library '" + path + "': " + dlerror()));
}
#endif /* _WIN32 */
BOOST_FOREACH(const boost::function<void(void)>& callback, *m_DeferredInitializers.get())
callback();
ExecuteDeferredInitializers();
m_DeferredInitializers.reset();
return hModule;
}
bool Utility::GetLoadingLibrary(void)
void Utility::ExecuteDeferredInitializers(void)
{
bool *loading = m_LoadingLibrary.get();
return loading && *loading;
}
if (!m_DeferredInitializers.get())
return;
void Utility::SetLoadingLibrary(bool loading)
{
bool *ploading = new bool(loading);
m_LoadingLibrary.reset(ploading);
BOOST_FOREACH(const boost::function<void(void)>& callback, *m_DeferredInitializers.get())
callback();
m_DeferredInitializers.reset();
}
void Utility::AddDeferredInitializer(const boost::function<void(void)>& callback)
{
if (!m_DeferredInitializers.get())
m_DeferredInitializers.reset(new std::vector<boost::function<void(void)> >());
m_DeferredInitializers.get()->push_back(callback);
}
@ -536,9 +515,7 @@ bool Utility::Glob(const String& pathSpec, const boost::function<void (const Str
struct stat statbuf;
if (stat(*gp, &statbuf) < 0)
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("stat")
<< boost::errinfo_errno(errno));
continue;
if (!S_ISDIR(statbuf.st_mode) && !S_ISREG(statbuf.st_mode))
continue;

View File

@ -97,9 +97,8 @@ public:
#endif /* _WIN32 */
LoadExtensionLibrary(const String& library);
static bool GetLoadingLibrary(void);
static void SetLoadingLibrary(bool loading);
static void AddDeferredInitializer(const boost::function<void(void)>& callback);
static void ExecuteDeferredInitializers(void);
#ifndef _WIN32
static void SetNonBlocking(int fd);

View File

@ -523,7 +523,7 @@ Value AExpression::OpObject(const AExpression* expr, const Dictionary::Ptr& loca
String type = left->Get(1);
AExpression::Ptr aname = left->Get(2);
AExpression::Ptr filter = left->Get(3);
String package = left->Get(4);
String zone = left->Get(4);
String name = aname->Evaluate(locals);
@ -561,7 +561,7 @@ Value AExpression::OpObject(const AExpression* expr, const Dictionary::Ptr& loca
item->AddExpression(exprl);
item->SetAbstract(abstract);
item->SetScope(locals);
item->SetPackage(package);
item->SetZone(zone);
item->Compile()->Register();
ObjectRule::AddRule(type, name, exprl, filter, expr->m_DebugInfo, locals);

View File

@ -24,7 +24,7 @@
%require "type",
%attribute %string "type",
%attribute %string "package",
%attribute %name(Zone) "zone",
%attribute %array "templates" {
%attribute %string "*"
@ -35,10 +35,6 @@
%attribute %dictionary "vars" {
%attribute %string "*"
},
%attribute %array "domains" {
%attribute %string "*"
}
}
%type Logger {

View File

@ -231,7 +231,7 @@ ignore return T_IGNORE;
function return T_FUNCTION;
lambda return T_LAMBDA;
return return T_RETURN;
package return T_PACKAGE;
zone return T_ZONE;
\<\< { yylval->op = &AExpression::OpShiftLeft; return T_SHIFT_LEFT; }
\>\> { yylval->op = &AExpression::OpShiftRight; return T_SHIFT_RIGHT; }
\<= { yylval->op = &AExpression::OpLessThanOrEqual; return T_LESS_THAN_OR_EQUAL; }

View File

@ -160,7 +160,7 @@ static void MakeRBinaryOp(Value** result, AExpression::OpCallback& op, Value *le
%token T_FUNCTION "function (T_FUNCTION)"
%token T_LAMBDA "lambda (T_LAMBDA)"
%token T_RETURN "return (T_RETURN)"
%token T_PACKAGE "package (T_PACKAGE)"
%token T_ZONE "zone (T_ZONE)"
%type <text> identifier
%type <array> rterm_items
@ -214,7 +214,7 @@ static std::stack<TypeRuleList::Ptr> m_RuleLists;
static ConfigType::Ptr m_Type;
static Dictionary::Ptr m_ModuleScope;
static String m_Package;
static String m_Zone;
static int m_StatementNum;
static bool m_Apply;
@ -227,7 +227,7 @@ void ConfigCompiler::Compile(void)
{
m_ModuleScope = make_shared<Dictionary>();
String parentPackage = m_Package;
String parentZone = m_Zone;
int parentStatementNum = m_StatementNum;
m_StatementNum = 0;
@ -240,7 +240,7 @@ void ConfigCompiler::Compile(void)
ConfigCompilerContext::GetInstance()->AddMessage(true, DiagnosticInformation(ex));
}
m_Package = parentPackage;
m_Zone = parentZone;
m_StatementNum = parentStatementNum;
}
@ -253,7 +253,7 @@ statements: /* empty */
| statements statement
;
statement: type | package | include | include_recursive | library | constant
statement: type | zone | include | include_recursive | library | constant
{
m_StatementNum++;
}
@ -269,37 +269,39 @@ statement: type | package | include | include_recursive | library | constant
}
;
package: T_PACKAGE rterm sep
zone: T_ZONE rterm sep
{
AExpression::Ptr aexpr = *$2;
delete $2;
if (!m_Package.IsEmpty())
BOOST_THROW_EXCEPTION(std::invalid_argument("Package name cannot be changed once it's been set."));
if (!m_Zone.IsEmpty())
BOOST_THROW_EXCEPTION(std::invalid_argument("Zone name cannot be changed once it's been set."));
if (m_StatementNum != 0)
BOOST_THROW_EXCEPTION(std::invalid_argument("'package' directive must be the first statement in a file."));
BOOST_THROW_EXCEPTION(std::invalid_argument("'zone' directive must be the first statement in a file."));
m_Package = aexpr->Evaluate(m_ModuleScope);
m_Zone = aexpr->Evaluate(m_ModuleScope);
}
| T_PACKAGE rterm rterm_scope sep
| T_ZONE rterm
{
AExpression::Ptr aexpr = *$2;
delete $2;
AExpression::Ptr ascope = *$3;
delete $3;
if (!m_Zone.IsEmpty())
BOOST_THROW_EXCEPTION(std::invalid_argument("Zone name cannot be changed once it's been set."));
if (!m_Package.IsEmpty())
BOOST_THROW_EXCEPTION(std::invalid_argument("Package name cannot be changed once it's been set."));
m_Package = aexpr->Evaluate(m_ModuleScope);
m_Zone = aexpr->Evaluate(m_ModuleScope);
}
rterm_scope sep
{
AExpression::Ptr ascope = *$4;
delete $4;
try {
ascope->Evaluate(m_ModuleScope);
m_Package = String();
m_Zone = String();
} catch (...) {
m_Package = String();
m_Zone = String();
}
}
;
@ -511,7 +513,7 @@ object:
args->Add(filter);
args->Add(m_Package);
args->Add(m_Zone);
$$ = new Value(make_shared<AExpression>(&AExpression::OpObject, args, exprl, DebugInfoRange(@2, @5)));

View File

@ -50,10 +50,10 @@ ConfigItem::ItemMap ConfigItem::m_Items;
ConfigItem::ConfigItem(const String& type, const String& name,
bool abstract, const AExpression::Ptr& exprl,
const DebugInfo& debuginfo, const Dictionary::Ptr& scope,
const String& package)
const String& zone)
: m_Type(type), m_Name(name), m_Abstract(abstract), m_Validated(false),
m_ExpressionList(exprl), m_DebugInfo(debuginfo),
m_Scope(scope), m_Package(package)
m_Scope(scope), m_Zone(zone)
{
}
@ -119,6 +119,8 @@ Dictionary::Ptr ConfigItem::GetProperties(void)
if (!m_Properties) {
m_Properties = make_shared<Dictionary>();
m_Properties->Set("type", m_Type);
if (!m_Zone.IsEmpty())
m_Properties->Set("zone", m_Zone);
m_Properties->Set("__parent", m_Scope);
GetExpressionList()->Evaluate(m_Properties);
m_Properties->Remove("__parent");

View File

@ -39,7 +39,7 @@ public:
ConfigItem(const String& type, const String& name, bool abstract,
const AExpression::Ptr& exprl, const DebugInfo& debuginfo,
const Dictionary::Ptr& scope, const String& package);
const Dictionary::Ptr& scope, const String& zone);
String GetType(void) const;
String GetName(void) const;
@ -57,7 +57,7 @@ public:
Dictionary::Ptr GetScope(void) const;
String GetPackage(void) const;
String GetZone(void) const;
static ConfigItem::Ptr GetObject(const String& type,
const String& name);
@ -81,7 +81,7 @@ private:
items. */
DebugInfo m_DebugInfo; /**< Debug information. */
Dictionary::Ptr m_Scope; /**< variable scope. */
String m_Package; /**< The package. */
String m_Zone; /**< The zone. */
DynamicObject::Ptr m_Object;

View File

@ -60,9 +60,9 @@ void ConfigItemBuilder::SetScope(const Dictionary::Ptr& scope)
m_Scope = scope;
}
void ConfigItemBuilder::SetPackage(const String& package)
void ConfigItemBuilder::SetZone(const String& zone)
{
m_Package = package;
m_Zone = zone;
}
void ConfigItemBuilder::AddExpression(const AExpression::Ptr& expr)
@ -104,5 +104,5 @@ ConfigItem::Ptr ConfigItemBuilder::Compile(void)
AExpression::Ptr exprl = make_shared<AExpression>(&AExpression::OpDict, exprs, true, m_DebugInfo);
return make_shared<ConfigItem>(m_Type, m_Name, m_Abstract, exprl,
m_DebugInfo, m_Scope, m_Package);
m_DebugInfo, m_Scope, m_Zone);
}

View File

@ -46,7 +46,7 @@ public:
void SetName(const String& name);
void SetAbstract(bool abstract);
void SetScope(const Dictionary::Ptr& scope);
void SetPackage(const String& name);
void SetZone(const String& zone);
void AddExpression(const AExpression::Ptr& expr);
@ -59,7 +59,7 @@ private:
Array::Ptr m_Expressions; /**< Expressions for this item. */
DebugInfo m_DebugInfo; /**< Debug information. */
Dictionary::Ptr m_Scope; /**< variable scope. */
String m_Package; /**< The package. */
String m_Zone; /**< The zone. */
};
}

View File

@ -50,7 +50,7 @@ void DbEvents::StaticInitialize(void)
Checkable::OnAcknowledgementSet.connect(boost::bind(&DbEvents::AddAcknowledgement, _1, _4));
Checkable::OnAcknowledgementCleared.connect(boost::bind(&DbEvents::RemoveAcknowledgement, _1));
Checkable::OnNextCheckChanged.connect(bind(&DbEvents::NextCheckChangedHandler, _1, _2, _3));
Checkable::OnNextCheckChanged.connect(bind(&DbEvents::NextCheckChangedHandler, _1, _2));
Checkable::OnFlappingChanged.connect(bind(&DbEvents::FlappingChangedHandler, _1, _2));
Checkable::OnNotificationSentToAllUsers.connect(bind(&DbEvents::LastNotificationChangedHandler, _1, _2));
@ -78,7 +78,7 @@ void DbEvents::StaticInitialize(void)
}
/* check events */
void DbEvents::NextCheckChangedHandler(const Checkable::Ptr& checkable, double nextCheck, const String& authority)
void DbEvents::NextCheckChangedHandler(const Checkable::Ptr& checkable, double nextCheck)
{
Host::Ptr host;
Service::Ptr service;

View File

@ -72,7 +72,7 @@ public:
static void AddLogHistory(const Checkable::Ptr& checkable, String buffer, LogEntryType type);
/* Status */
static void NextCheckChangedHandler(const Checkable::Ptr& checkable, double nextCheck, const String& authority);
static void NextCheckChangedHandler(const Checkable::Ptr& checkable, double nextCheck);
static void FlappingChangedHandler(const Checkable::Ptr& checkable, FlappingState state);
static void LastNotificationChangedHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable);

View File

@ -39,7 +39,7 @@ INITIALIZE_ONCE(&EndpointDbObject::StaticInitialize);
void EndpointDbObject::StaticInitialize(void)
{
Endpoint::OnConnected.connect(boost::bind(&EndpointDbObject::UpdateConnectedStatus, _1));
Endpoint::OnDisconnected.connect(boost::bind(&EndpointDbObject::UpdateDisconnectedStatus, _1));
Endpoint::OnDisconnected.connect(boost::bind(&EndpointDbObject::UpdateConnectedStatus, _1));
}
EndpointDbObject::EndpointDbObject(const DbType::Ptr& type, const String& name1, const String& name2)
@ -73,16 +73,8 @@ Dictionary::Ptr EndpointDbObject::GetStatusFields(void) const
void EndpointDbObject::UpdateConnectedStatus(const Endpoint::Ptr& endpoint)
{
UpdateConnectedStatusInternal(endpoint, true);
}
bool connected = EndpointIsConnected(endpoint);
void EndpointDbObject::UpdateDisconnectedStatus(const Endpoint::Ptr& endpoint)
{
UpdateConnectedStatusInternal(endpoint, false);
}
void EndpointDbObject::UpdateConnectedStatusInternal(const Endpoint::Ptr& endpoint, bool connected)
{
Log(LogDebug, "db_ido", "update is_connected=" + Convert::ToString(connected ? 1 : 0) + " for endpoint '" + endpoint->GetName() + "'");
DbQuery query1;

View File

@ -49,8 +49,6 @@ protected:
private:
static void UpdateConnectedStatus(const Endpoint::Ptr& endpoint);
static void UpdateDisconnectedStatus(const Endpoint::Ptr& endpoint);
static void UpdateConnectedStatusInternal(const Endpoint::Ptr& endpoint, bool connected);
static int EndpointIsConnected(const Endpoint::Ptr& endpoint);
};

View File

@ -21,7 +21,6 @@ mkclass_target(checkresult.ti checkresult.th)
mkclass_target(command.ti command.th)
mkclass_target(comment.ti comment.th)
mkclass_target(dependency.ti dependency.th)
mkclass_target(domain.ti domain.th)
mkclass_target(downtime.ti downtime.th)
mkclass_target(eventcommand.ti eventcommand.th)
mkclass_target(hostgroup.ti hostgroup.th)
@ -41,10 +40,10 @@ mkclass_target(user.ti user.th)
mkembedconfig_target(icinga-type.conf icinga-type.cpp)
add_library(icinga SHARED
api.cpp checkable.cpp checkable.th checkable-dependency.cpp checkable-downtime.cpp checkable-event.cpp
api.cpp apievents.cpp checkable.cpp checkable.th checkable-dependency.cpp checkable-downtime.cpp checkable-event.cpp
checkable-flapping.cpp checkcommand.cpp checkcommand.th checkresult.cpp checkresult.th
cib.cpp command.cpp command.th comment.cpp comment.th compatutility.cpp dependency.cpp dependency.th
dependency-apply.cpp domain.cpp domain.th downtime.cpp downtime.th eventcommand.cpp eventcommand.th
dependency-apply.cpp downtime.cpp downtime.th eventcommand.cpp eventcommand.th
externalcommandprocessor.cpp host.cpp host.th hostgroup.cpp hostgroup.th icingaapplication.cpp
icingaapplication.th icingastatuswriter.cpp icingastatuswriter.th legacytimeperiod.cpp macroprocessor.cpp
notificationcommand.cpp notificationcommand.th notification.cpp notification.th notification-apply.cpp
@ -54,7 +53,7 @@ add_library(icinga SHARED
user.cpp user.th usergroup.cpp usergroup.th icinga-type.cpp
)
target_link_libraries(icinga ${Boost_LIBRARIES} base config)
target_link_libraries(icinga ${Boost_LIBRARIES} base config remote)
set_target_properties (
icinga PROPERTIES

View File

@ -18,15 +18,20 @@
******************************************************************************/
#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;

View File

@ -21,6 +21,7 @@
#define API_H
#include "icinga/i2-icinga.h"
#include "remote/apiclient.h"
#include "base/value.h"
#include <vector>
@ -28,14 +29,12 @@ namespace icinga
{
/**
* A state change message for a service.
*
* @ingroup icinga
*/
class I2_ICINGA_API API
{
public:
static int GetAnswerToEverything(const String& text);
static Value GetAnswerToEverything(const Dictionary::Ptr& params);
private:
API(void);

967
lib/icinga/apievents.cpp Normal file
View File

@ -0,0 +1,967 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "icinga/apievents.h"
#include "icinga/service.h"
#include "remote/apilistener.h"
#include "remote/apiclient.h"
#include "remote/apifunction.h"
#include "base/application.h"
#include "base/dynamictype.h"
#include "base/objectlock.h"
#include "base/utility.h"
#include "base/logger_fwd.h"
#include "base/exception.h"
#include "base/initialize.h"
#include <fstream>
using namespace icinga;
INITIALIZE_ONCE(&ApiEvents::StaticInitialize);
REGISTER_APIFUNCTION(CheckResult, event, &ApiEvents::CheckResultAPIHandler);
REGISTER_APIFUNCTION(SetNextCheck, event, &ApiEvents::NextCheckChangedAPIHandler);
REGISTER_APIFUNCTION(SetNextNotification, event, &ApiEvents::NextNotificationChangedAPIHandler);
REGISTER_APIFUNCTION(SetForceNextCheck, event, &ApiEvents::ForceNextCheckChangedAPIHandler);
REGISTER_APIFUNCTION(SetForceNextNotification, event, &ApiEvents::ForceNextNotificationChangedAPIHandler);
REGISTER_APIFUNCTION(SetEnableActiveChecks, event, &ApiEvents::EnableActiveChecksChangedAPIHandler);
REGISTER_APIFUNCTION(SetEnablePassiveChecks, event, &ApiEvents::EnablePassiveChecksChangedAPIHandler);
REGISTER_APIFUNCTION(SetEnableNotifications, event, &ApiEvents::EnableNotificationsChangedAPIHandler);
REGISTER_APIFUNCTION(SetEnableFlapping, event, &ApiEvents::EnableFlappingChangedAPIHandler);
REGISTER_APIFUNCTION(AddComment, event, &ApiEvents::CommentAddedAPIHandler);
REGISTER_APIFUNCTION(RemoveComment, event, &ApiEvents::CommentRemovedAPIHandler);
REGISTER_APIFUNCTION(AddDowntime, event, &ApiEvents::DowntimeAddedAPIHandler);
REGISTER_APIFUNCTION(RemoveDowntime, event, &ApiEvents::DowntimeRemovedAPIHandler);
REGISTER_APIFUNCTION(SetAcknowledgement, event, &ApiEvents::AcknowledgementSetAPIHandler);
REGISTER_APIFUNCTION(ClearAcknowledgement, event, &ApiEvents::AcknowledgementClearedAPIHandler);
REGISTER_APIFUNCTION(UpdateRepository, event, &ApiEvents::UpdateRepositoryAPIHandler);
static Timer::Ptr l_RepositoryTimer;
void ApiEvents::StaticInitialize(void)
{
Checkable::OnNewCheckResult.connect(&ApiEvents::CheckResultHandler);
Checkable::OnNextCheckChanged.connect(&ApiEvents::NextCheckChangedHandler);
Notification::OnNextNotificationChanged.connect(&ApiEvents::NextNotificationChangedHandler);
Checkable::OnForceNextCheckChanged.connect(&ApiEvents::ForceNextCheckChangedHandler);
Checkable::OnForceNextNotificationChanged.connect(&ApiEvents::ForceNextNotificationChangedHandler);
Checkable::OnEnableActiveChecksChanged.connect(&ApiEvents::EnableActiveChecksChangedHandler);
Checkable::OnEnablePassiveChecksChanged.connect(&ApiEvents::EnablePassiveChecksChangedHandler);
Checkable::OnEnableNotificationsChanged.connect(&ApiEvents::EnableNotificationsChangedHandler);
Checkable::OnEnableFlappingChanged.connect(&ApiEvents::EnableFlappingChangedHandler);
Checkable::OnCommentAdded.connect(&ApiEvents::CommentAddedHandler);
Checkable::OnCommentRemoved.connect(&ApiEvents::CommentRemovedHandler);
Checkable::OnDowntimeAdded.connect(&ApiEvents::DowntimeAddedHandler);
Checkable::OnDowntimeRemoved.connect(&ApiEvents::DowntimeRemovedHandler);
Checkable::OnAcknowledgementSet.connect(&ApiEvents::AcknowledgementSetHandler);
Checkable::OnAcknowledgementCleared.connect(&ApiEvents::AcknowledgementClearedHandler);
l_RepositoryTimer = make_shared<Timer>();
l_RepositoryTimer->SetInterval(60);
l_RepositoryTimer->OnTimerExpired.connect(boost::bind(&ApiEvents::RepositoryTimerHandler));
l_RepositoryTimer->Start();
l_RepositoryTimer->Reschedule(0);
}
void ApiEvents::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const MessageOrigin& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::CheckResult");
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("host", host->GetName());
if (service)
params->Set("service", service->GetShortName());
params->Set("cr", Serialize(cr));
message->Set("params", params);
listener->RelayMessage(origin, checkable, message, true);
}
Value ApiEvents::CheckResultAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Value crv = params->Get("cr");
CheckResult::Ptr cr = Deserialize(crv, true);
Host::Ptr host = Host::GetByName(params->Get("host"));
if (!host)
return Empty;
Checkable::Ptr checkable;
if (params->Contains("service"))
checkable = host->GetServiceByShortName(params->Get("service"));
else
checkable = host;
if (!checkable)
return Empty;
if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
return Empty;
checkable->ProcessCheckResult(cr, origin);
return Empty;
}
void ApiEvents::NextCheckChangedHandler(const Checkable::Ptr& checkable, double nextCheck, const MessageOrigin& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("host", host->GetName());
if (service)
params->Set("service", service->GetShortName());
params->Set("next_check", nextCheck);
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::SetNextCheck");
message->Set("params", params);
listener->RelayMessage(origin, checkable, message, true);
}
Value ApiEvents::NextCheckChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Host::Ptr host = Host::GetByName(params->Get("host"));
if (!host)
return Empty;
Checkable::Ptr checkable;
if (params->Contains("service"))
checkable = host->GetServiceByShortName(params->Get("service"));
else
checkable = host;
if (!checkable)
return Empty;
if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
return Empty;
checkable->SetNextCheck(params->Get("next_check"), origin);
return Empty;
}
void ApiEvents::NextNotificationChangedHandler(const Notification::Ptr& notification, double nextNotification, const MessageOrigin& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("notification", notification->GetName());
params->Set("next_notification", nextNotification);
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::SetNextNotification");
message->Set("params", params);
listener->RelayMessage(origin, notification, message, true);
}
Value ApiEvents::NextNotificationChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Notification::Ptr notification = Notification::GetByName(params->Get("notification"));
if (!notification)
return Empty;
if (origin.FromZone && !origin.FromZone->CanAccessObject(notification))
return Empty;
notification->SetNextNotification(params->Get("next_notification"), origin);
return Empty;
}
void ApiEvents::ForceNextCheckChangedHandler(const Checkable::Ptr& checkable, bool forced, const MessageOrigin& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("host", host->GetName());
if (service)
params->Set("service", service->GetShortName());
params->Set("forced", forced);
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::SetForceNextCheck");
message->Set("params", params);
listener->RelayMessage(origin, checkable, message, true);
}
Value ApiEvents::ForceNextCheckChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Host::Ptr host = Host::GetByName(params->Get("host"));
if (!host)
return Empty;
Checkable::Ptr checkable;
if (params->Contains("service"))
checkable = host->GetServiceByShortName(params->Get("service"));
else
checkable = host;
if (!checkable)
return Empty;
if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
return Empty;
checkable->SetForceNextCheck(params->Get("forced"), origin);
return Empty;
}
void ApiEvents::ForceNextNotificationChangedHandler(const Checkable::Ptr& checkable, bool forced, const MessageOrigin& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("host", host->GetName());
if (service)
params->Set("service", service->GetShortName());
params->Set("forced", forced);
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::SetForceNextNotification");
message->Set("params", params);
listener->RelayMessage(origin, checkable, message, true);
}
Value ApiEvents::ForceNextNotificationChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Host::Ptr host = Host::GetByName(params->Get("host"));
if (!host)
return Empty;
Checkable::Ptr checkable;
if (params->Contains("service"))
checkable = host->GetServiceByShortName(params->Get("service"));
else
checkable = host;
if (!checkable)
return Empty;
if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
return Empty;
checkable->SetForceNextNotification(params->Get("forced"), origin);
return Empty;
}
void ApiEvents::EnableActiveChecksChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("host", host->GetName());
if (service)
params->Set("service", service->GetShortName());
params->Set("enabled", enabled);
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::SetEnableActiveChecks");
message->Set("params", params);
listener->RelayMessage(origin, checkable, message, true);
}
Value ApiEvents::EnableActiveChecksChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Host::Ptr host = Host::GetByName(params->Get("host"));
if (!host)
return Empty;
Checkable::Ptr checkable;
if (params->Contains("service"))
checkable = host->GetServiceByShortName(params->Get("service"));
else
checkable = host;
if (!checkable)
return Empty;
if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
return Empty;
checkable->SetEnableActiveChecks(params->Get("enabled"), origin);
return Empty;
}
void ApiEvents::EnablePassiveChecksChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("host", host->GetName());
if (service)
params->Set("service", service->GetShortName());
params->Set("enabled", enabled);
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::SetEnablePassiveChecks");
message->Set("params", params);
listener->RelayMessage(origin, checkable, message, true);
}
Value ApiEvents::EnablePassiveChecksChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Host::Ptr host = Host::GetByName(params->Get("host"));
if (!host)
return Empty;
Checkable::Ptr checkable;
if (params->Contains("service"))
checkable = host->GetServiceByShortName(params->Get("service"));
else
checkable = host;
if (!checkable)
return Empty;
if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
return Empty;
checkable->SetEnablePassiveChecks(params->Get("enabled"), origin);
return Empty;
}
void ApiEvents::EnableNotificationsChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("host", host->GetName());
if (service)
params->Set("service", service->GetShortName());
params->Set("enabled", enabled);
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::SetEnableNotifications");
message->Set("params", params);
listener->RelayMessage(origin, checkable, message, true);
}
Value ApiEvents::EnableNotificationsChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Host::Ptr host = Host::GetByName(params->Get("host"));
if (!host)
return Empty;
Checkable::Ptr checkable;
if (params->Contains("service"))
checkable = host->GetServiceByShortName(params->Get("service"));
else
checkable = host;
if (!checkable)
return Empty;
if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
return Empty;
checkable->SetEnableNotifications(params->Get("enabled"), origin);
return Empty;
}
void ApiEvents::EnableFlappingChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("host", host->GetName());
if (service)
params->Set("service", service->GetShortName());
params->Set("enabled", enabled);
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::SetEnableFlapping");
message->Set("params", params);
listener->RelayMessage(origin, checkable, message, true);
}
Value ApiEvents::EnableFlappingChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Host::Ptr host = Host::GetByName(params->Get("host"));
if (!host)
return Empty;
Checkable::Ptr checkable;
if (params->Contains("service"))
checkable = host->GetServiceByShortName(params->Get("service"));
else
checkable = host;
if (!checkable)
return Empty;
if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
return Empty;
checkable->SetEnableFlapping(params->Get("enabled"), origin);
return Empty;
}
void ApiEvents::CommentAddedHandler(const Checkable::Ptr& checkable, const Comment::Ptr& comment, const MessageOrigin& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("host", host->GetName());
if (service)
params->Set("service", service->GetShortName());
params->Set("comment", Serialize(comment));
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::AddComment");
message->Set("params", params);
listener->RelayMessage(origin, checkable, message, true);
}
Value ApiEvents::CommentAddedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Host::Ptr host = Host::GetByName(params->Get("host"));
if (!host)
return Empty;
Checkable::Ptr checkable;
if (params->Contains("service"))
checkable = host->GetServiceByShortName(params->Get("service"));
else
checkable = host;
if (!checkable)
return Empty;
if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
return Empty;
Comment::Ptr comment = Deserialize(params->Get("comment"), true);
checkable->AddComment(comment->GetEntryType(), comment->GetAuthor(),
comment->GetText(), comment->GetExpireTime(), comment->GetId(), origin);
return Empty;
}
void ApiEvents::CommentRemovedHandler(const Checkable::Ptr& checkable, const Comment::Ptr& comment, const MessageOrigin& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("host", host->GetName());
if (service)
params->Set("service", service->GetShortName());
params->Set("id", comment->GetId());
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::RemoveComment");
message->Set("params", params);
listener->RelayMessage(origin, checkable, message, true);
}
Value ApiEvents::CommentRemovedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Host::Ptr host = Host::GetByName(params->Get("host"));
if (!host)
return Empty;
Checkable::Ptr checkable;
if (params->Contains("service"))
checkable = host->GetServiceByShortName(params->Get("service"));
else
checkable = host;
if (!checkable)
return Empty;
if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
return Empty;
checkable->RemoveComment(params->Get("id"), origin);
return Empty;
}
void ApiEvents::DowntimeAddedHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, const MessageOrigin& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("host", host->GetName());
if (service)
params->Set("service", service->GetShortName());
params->Set("downtime", Serialize(downtime));
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::AddDowntime");
message->Set("params", params);
listener->RelayMessage(origin, checkable, message, true);
}
Value ApiEvents::DowntimeAddedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Host::Ptr host = Host::GetByName(params->Get("host"));
if (!host)
return Empty;
Checkable::Ptr checkable;
if (params->Contains("service"))
checkable = host->GetServiceByShortName(params->Get("service"));
else
checkable = host;
if (!checkable)
return Empty;
if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
return Empty;
Downtime::Ptr downtime = Deserialize(params->Get("downtime"), true);
checkable->AddDowntime(downtime->GetAuthor(), downtime->GetComment(),
downtime->GetStartTime(), downtime->GetEndTime(),
downtime->GetFixed(), downtime->GetTriggeredBy(),
downtime->GetDuration(), downtime->GetScheduledBy(),
downtime->GetId(), origin);
return Empty;
}
void ApiEvents::DowntimeRemovedHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, const MessageOrigin& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("host", host->GetName());
if (service)
params->Set("service", service->GetShortName());
params->Set("id", downtime->GetId());
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::RemoveDowntime");
message->Set("params", params);
listener->RelayMessage(origin, checkable, message, true);
}
Value ApiEvents::DowntimeRemovedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Host::Ptr host = Host::GetByName(params->Get("host"));
if (!host)
return Empty;
Checkable::Ptr checkable;
if (params->Contains("service"))
checkable = host->GetServiceByShortName(params->Get("service"));
else
checkable = host;
if (!checkable)
return Empty;
if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
return Empty;
checkable->RemoveDowntime(params->Get("id"), false, origin);
return Empty;
}
void ApiEvents::AcknowledgementSetHandler(const Checkable::Ptr& checkable,
const String& author, const String& comment, AcknowledgementType type,
double expiry, const MessageOrigin& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("host", host->GetName());
if (service)
params->Set("service", service->GetShortName());
params->Set("author", author);
params->Set("comment", comment);
params->Set("acktype", type);
params->Set("expiry", expiry);
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::SetAcknowledgement");
message->Set("params", params);
listener->RelayMessage(origin, checkable, message, true);
}
Value ApiEvents::AcknowledgementSetAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Host::Ptr host = Host::GetByName(params->Get("host"));
if (!host)
return Empty;
Checkable::Ptr checkable;
if (params->Contains("service"))
checkable = host->GetServiceByShortName(params->Get("service"));
else
checkable = host;
if (!checkable)
return Empty;
if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
return Empty;
checkable->AcknowledgeProblem(params->Get("author"), params->Get("comment"),
static_cast<AcknowledgementType>(static_cast<int>(params->Get("acktype"))),
params->Get("expiry"), origin);
return Empty;
}
void ApiEvents::AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const MessageOrigin& origin)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("host", host->GetName());
if (service)
params->Set("service", service->GetShortName());
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::ClearAcknowledgement");
message->Set("params", params);
listener->RelayMessage(origin, checkable, message, true);
}
Value ApiEvents::AcknowledgementClearedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Host::Ptr host = Host::GetByName(params->Get("host"));
if (!host)
return Empty;
Checkable::Ptr checkable;
if (params->Contains("service"))
checkable = host->GetServiceByShortName(params->Get("service"));
else
checkable = host;
if (!checkable)
return Empty;
if (origin.FromZone && !origin.FromZone->CanAccessObject(checkable))
return Empty;
checkable->ClearAcknowledgement(origin);
return Empty;
}
void ApiEvents::RepositoryTimerHandler(void)
{
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return;
Dictionary::Ptr repository = make_shared<Dictionary>();
BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjects<Host>()) {
Array::Ptr services = make_shared<Array>();
BOOST_FOREACH(const Service::Ptr& service, host->GetServices()) {
services->Add(service->GetName());
}
repository->Set(host->GetName(), services);
}
Endpoint::Ptr my_endpoint = Endpoint::GetLocalEndpoint();
Zone::Ptr my_zone = my_endpoint->GetZone();
Dictionary::Ptr params = make_shared<Dictionary>();
params->Set("endpoint", my_endpoint->GetName());
Zone::Ptr parent_zone = my_zone->GetParent();
if (parent_zone)
params->Set("parent_zone", parent_zone->GetName());
params->Set("zone", my_zone->GetName());
params->Set("repository", repository);
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::UpdateRepository");
message->Set("params", params);
listener->RelayMessage(MessageOrigin(), my_zone, message, true);
}
String ApiEvents::GetRepositoryDir(void)
{
return Application::GetLocalStateDir() + "/lib/icinga2/api/repository/";
}
Value ApiEvents::UpdateRepositoryAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
Zone::Ptr zone = Zone::GetByName(params->Get("zone"));
if (!zone || (origin.FromZone && !origin.FromZone->CanAccessObject(zone)))
return Empty;
String repositoryFile = GetRepositoryDir() + SHA256(params->Get("endpoint"));
String repositoryTempFile = repositoryFile + ".tmp";
std::ofstream fp(repositoryTempFile.CStr(), std::ofstream::out | std::ostream::trunc);
fp << JsonSerialize(params);
fp.close();
#ifdef _WIN32
_unlink(inventoryFile.CStr());
#endif /* _WIN32 */
if (rename(repositoryTempFile.CStr(), repositoryFile.CStr()) < 0) {
BOOST_THROW_EXCEPTION(posix_error()
<< boost::errinfo_api_function("rename")
<< boost::errinfo_errno(errno)
<< boost::errinfo_file_name(repositoryTempFile));
}
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return Empty;
Dictionary::Ptr message = make_shared<Dictionary>();
message->Set("jsonrpc", "2.0");
message->Set("method", "event::UpdateRepository");
message->Set("params", params);
listener->RelayMessage(origin, Zone::GetLocalZone(), message, true);
return Empty;
}

93
lib/icinga/apievents.h Normal file
View File

@ -0,0 +1,93 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef APIEVENTS_H
#define APIEVENTS_H
#include "icinga/checkable.h"
#include "remote/apiclient.h"
#include "base/stream.h"
#include "base/timer.h"
#include "base/array.h"
#include <boost/signals2.hpp>
namespace icinga
{
/**
* @ingroup icinga
*/
class I2_ICINGA_API ApiEvents
{
public:
static void StaticInitialize(void);
static void CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const MessageOrigin& origin);
static Value CheckResultAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
static void NextCheckChangedHandler(const Checkable::Ptr& checkable, double nextCheck, const MessageOrigin& origin);
static Value NextCheckChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
static void NextNotificationChangedHandler(const Notification::Ptr& notification, double nextCheck, const MessageOrigin& origin);
static Value NextNotificationChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
static void ForceNextCheckChangedHandler(const Checkable::Ptr& checkable, bool forced, const MessageOrigin& origin);
static Value ForceNextCheckChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
static void ForceNextNotificationChangedHandler(const Checkable::Ptr& checkable, bool forced, const MessageOrigin& origin);
static Value ForceNextNotificationChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
static void EnableActiveChecksChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin);
static Value EnableActiveChecksChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
static void EnablePassiveChecksChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin);
static Value EnablePassiveChecksChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
static void EnableNotificationsChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin);
static Value EnableNotificationsChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
static void EnableFlappingChangedHandler(const Checkable::Ptr& checkable, bool enabled, const MessageOrigin& origin);
static Value EnableFlappingChangedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
static void CommentAddedHandler(const Checkable::Ptr& checkable, const Comment::Ptr& comment, const MessageOrigin& origin);
static Value CommentAddedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
static void CommentRemovedHandler(const Checkable::Ptr& checkable, const Comment::Ptr& comment, const MessageOrigin& origin);
static Value CommentRemovedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
static void DowntimeAddedHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, const MessageOrigin& origin);
static Value DowntimeAddedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
static void DowntimeRemovedHandler(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, const MessageOrigin& origin);
static Value DowntimeRemovedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
static void AcknowledgementSetHandler(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, double expiry, const MessageOrigin& origin);
static Value AcknowledgementSetAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
static void AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const MessageOrigin& origin);
static Value AcknowledgementClearedAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
static String GetRepositoryDir(void);
static void RepositoryTimerHandler(void);
static Value UpdateRepositoryAPIHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
};
}
#endif /* APIEVENTS_H */

View File

@ -21,6 +21,7 @@
#include "icinga/checkcommand.h"
#include "icinga/icingaapplication.h"
#include "icinga/cib.h"
#include "remote/apilistener.h"
#include "base/dynamictype.h"
#include "base/objectlock.h"
#include "base/logger_fwd.h"
@ -33,16 +34,16 @@
using namespace icinga;
boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, const String&)> Checkable::OnNewCheckResult;
boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, StateType, const String&)> Checkable::OnStateChange;
boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, const MessageOrigin&)> Checkable::OnNewCheckResult;
boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, StateType, const MessageOrigin&)> Checkable::OnStateChange;
boost::signals2::signal<void (const Checkable::Ptr&, NotificationType, const CheckResult::Ptr&, const String&, const String&)> Checkable::OnNotificationsRequested;
boost::signals2::signal<void (const Checkable::Ptr&, double, const String&)> Checkable::OnNextCheckChanged;
boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> Checkable::OnForceNextCheckChanged;
boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> Checkable::OnForceNextNotificationChanged;
boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> Checkable::OnEnableActiveChecksChanged;
boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> Checkable::OnEnablePassiveChecksChanged;
boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> Checkable::OnEnableNotificationsChanged;
boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> Checkable::OnEnableFlappingChanged;
boost::signals2::signal<void (const Checkable::Ptr&, double, const MessageOrigin&)> Checkable::OnNextCheckChanged;
boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> Checkable::OnForceNextCheckChanged;
boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> Checkable::OnForceNextNotificationChanged;
boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> Checkable::OnEnableActiveChecksChanged;
boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> Checkable::OnEnablePassiveChecksChanged;
boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> Checkable::OnEnableNotificationsChanged;
boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> Checkable::OnEnableFlappingChanged;
boost::signals2::signal<void (const Checkable::Ptr&, FlappingState)> Checkable::OnFlappingChanged;
CheckCommand::Ptr Checkable::GetCheckCommand(void) const
@ -115,11 +116,11 @@ long Checkable::GetSchedulingOffset(void)
return m_SchedulingOffset;
}
void Checkable::SetNextCheck(double nextCheck, const String& authority)
void Checkable::SetNextCheck(double nextCheck, const MessageOrigin& origin)
{
SetNextCheckRaw(nextCheck);
OnNextCheckChanged(GetSelf(), nextCheck, authority);
OnNextCheckChanged(GetSelf(), nextCheck, origin);
}
double Checkable::GetNextCheck(void)
@ -171,11 +172,11 @@ bool Checkable::GetEnableActiveChecks(void) const
return GetEnableActiveChecksRaw();
}
void Checkable::SetEnableActiveChecks(bool enabled, const String& authority)
void Checkable::SetEnableActiveChecks(bool enabled, const MessageOrigin& origin)
{
SetOverrideEnableActiveChecks(enabled);
OnEnableActiveChecksChanged(GetSelf(), enabled, authority);
OnEnableActiveChecksChanged(GetSelf(), enabled, origin);
}
bool Checkable::GetEnablePassiveChecks(void) const
@ -186,11 +187,11 @@ bool Checkable::GetEnablePassiveChecks(void) const
return GetEnablePassiveChecksRaw();
}
void Checkable::SetEnablePassiveChecks(bool enabled, const String& authority)
void Checkable::SetEnablePassiveChecks(bool enabled, const MessageOrigin& origin)
{
SetOverrideEnablePassiveChecks(enabled);
OnEnablePassiveChecksChanged(GetSelf(), enabled, authority);
OnEnablePassiveChecksChanged(GetSelf(), enabled, origin);
}
bool Checkable::GetForceNextCheck(void) const
@ -198,11 +199,11 @@ bool Checkable::GetForceNextCheck(void) const
return GetForceNextCheckRaw();
}
void Checkable::SetForceNextCheck(bool forced, const String& authority)
void Checkable::SetForceNextCheck(bool forced, const MessageOrigin& origin)
{
SetForceNextCheckRaw(forced);
OnForceNextCheckChanged(GetSelf(), forced, authority);
OnForceNextCheckChanged(GetSelf(), forced, origin);
}
int Checkable::GetMaxCheckAttempts(void) const
@ -218,7 +219,7 @@ void Checkable::SetMaxCheckAttempts(int attempts)
SetOverrideMaxCheckAttempts(attempts);
}
void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const String& authority)
void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrigin& origin)
{
{
ObjectLock olock(this);
@ -239,7 +240,7 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const String& aut
if (cr->GetExecutionEnd() == 0)
cr->SetExecutionEnd(now);
if (authority.IsEmpty())
if (origin.IsLocal())
cr->SetCheckSource(IcingaApplication::GetInstance()->GetNodeName());
bool reachable = IsReachable();
@ -399,15 +400,15 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const String& aut
// " threshold: " + Convert::ToString(GetFlappingThreshold()) +
// "% current: " + Convert::ToString(GetFlappingCurrent()) + "%.");
OnNewCheckResult(GetSelf(), cr, authority);
OnNewCheckResult(GetSelf(), cr, origin);
/* signal status updates to for example db_ido */
OnStateChanged(GetSelf(), authority);
OnStateChanged(GetSelf());
if (hardChange)
OnStateChange(GetSelf(), cr, StateTypeHard, authority);
OnStateChange(GetSelf(), cr, StateTypeHard, origin);
else if (stateChange)
OnStateChange(GetSelf(), cr, StateTypeSoft, authority);
OnStateChange(GetSelf(), cr, StateTypeSoft, origin);
if (GetStateType() == StateTypeSoft || hardChange || recovery)
ExecuteEventHandler();

View File

@ -33,8 +33,8 @@ static std::map<int, String> l_LegacyCommentsCache;
static std::map<String, Checkable::WeakPtr> l_CommentsCache;
static Timer::Ptr l_CommentsExpireTimer;
boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const String&)> Checkable::OnCommentAdded;
boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const String&)> Checkable::OnCommentRemoved;
boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const MessageOrigin&)> Checkable::OnCommentAdded;
boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const MessageOrigin&)> Checkable::OnCommentRemoved;
int Checkable::GetNextCommentID(void)
{
@ -44,7 +44,7 @@ int Checkable::GetNextCommentID(void)
}
String Checkable::AddComment(CommentType entryType, const String& author,
const String& text, double expireTime, const String& id, const String& authority)
const String& text, double expireTime, const String& id, const MessageOrigin& origin)
{
String uid;
@ -78,7 +78,7 @@ String Checkable::AddComment(CommentType entryType, const String& author,
l_CommentsCache[uid] = GetSelf();
}
OnCommentAdded(GetSelf(), comment, authority);
OnCommentAdded(GetSelf(), comment, origin);
return uid;
}
@ -100,7 +100,7 @@ void Checkable::RemoveAllComments(void)
}
}
void Checkable::RemoveComment(const String& id, const String& authority)
void Checkable::RemoveComment(const String& id, const MessageOrigin& origin)
{
Checkable::Ptr owner = GetOwnerByCommentID(id);
@ -126,7 +126,7 @@ void Checkable::RemoveComment(const String& id, const String& authority)
l_CommentsCache.erase(id);
}
OnCommentRemoved(owner, comment, authority);
OnCommentRemoved(owner, comment, origin);
}
String Checkable::GetCommentIDFromLegacyID(int id)

View File

@ -35,8 +35,8 @@ static std::map<int, String> l_LegacyDowntimesCache;
static std::map<String, Checkable::WeakPtr> l_DowntimesCache;
static Timer::Ptr l_DowntimesExpireTimer;
boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const String&)> Checkable::OnDowntimeAdded;
boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const String&)> Checkable::OnDowntimeRemoved;
boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const MessageOrigin&)> Checkable::OnDowntimeAdded;
boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const MessageOrigin&)> Checkable::OnDowntimeRemoved;
boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&)> Checkable::OnDowntimeTriggered;
int Checkable::GetNextDowntimeID(void)
@ -49,7 +49,7 @@ int Checkable::GetNextDowntimeID(void)
String Checkable::AddDowntime(const String& author, const String& comment,
double startTime, double endTime, bool fixed,
const String& triggeredBy, double duration, const String& scheduledBy,
const String& id, const String& authority)
const String& id, const MessageOrigin& origin)
{
String uid;
@ -106,12 +106,12 @@ String Checkable::AddDowntime(const String& author, const String& comment,
Log(LogDebug, "icinga", "Added downtime with ID '" + Convert::ToString(downtime->GetLegacyId()) +
"' between '" + Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", startTime) + "' and '" + Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", endTime) + "'.");
OnDowntimeAdded(GetSelf(), downtime, authority);
OnDowntimeAdded(GetSelf(), downtime, origin);
return uid;
}
void Checkable::RemoveDowntime(const String& id, bool cancelled, const String& authority)
void Checkable::RemoveDowntime(const String& id, bool cancelled, const MessageOrigin& origin)
{
Checkable::Ptr owner = GetOwnerByDowntimeID(id);
@ -146,7 +146,7 @@ void Checkable::RemoveDowntime(const String& id, bool cancelled, const String& a
Log(LogDebug, "icinga", "Removed downtime with ID '" + Convert::ToString(downtime->GetLegacyId()) + "' from service '" + owner->GetName() + "'.");
OnDowntimeRemoved(owner, downtime, authority);
OnDowntimeRemoved(owner, downtime, origin);
}
void Checkable::TriggerDowntimes(void)

View File

@ -47,12 +47,12 @@ bool Checkable::GetEnableFlapping(void) const
return GetEnableFlappingRaw();
}
void Checkable::SetEnableFlapping(bool enabled, const String& authority)
void Checkable::SetEnableFlapping(bool enabled, const MessageOrigin& origin)
{
SetOverrideEnableFlapping(enabled);
OnFlappingChanged(GetSelf(), enabled ? FlappingEnabled : FlappingDisabled);
OnEnableFlappingChanged(GetSelf(), enabled, authority);
OnEnableFlappingChanged(GetSelf(), enabled, origin);
}
void Checkable::UpdateFlappingStatus(bool stateChange)

View File

@ -108,11 +108,11 @@ bool Checkable::GetEnableNotifications(void) const
return GetEnableNotificationsRaw();
}
void Checkable::SetEnableNotifications(bool enabled, const String& authority)
void Checkable::SetEnableNotifications(bool enabled, const MessageOrigin& origin)
{
SetOverrideEnableNotifications(enabled);
OnEnableNotificationsChanged(GetSelf(), enabled, authority);
OnEnableNotificationsChanged(GetSelf(), enabled, origin);
}
bool Checkable::GetForceNextNotification(void) const
@ -120,9 +120,9 @@ bool Checkable::GetForceNextNotification(void) const
return GetForceNextNotificationRaw();
}
void Checkable::SetForceNextNotification(bool forced, const String& authority)
void Checkable::SetForceNextNotification(bool forced, const MessageOrigin& origin)
{
SetForceNextNotificationRaw(forced);
OnForceNextNotificationChanged(GetSelf(), forced, authority);
OnForceNextNotificationChanged(GetSelf(), forced, origin);
}

View File

@ -39,8 +39,8 @@ REGISTER_TYPE(Checkable);
INITIALIZE_ONCE(&Checkable::StartDowntimesExpiredTimer);
boost::signals2::signal<void (const Checkable::Ptr&, const String&, const String&, AcknowledgementType, double, const String&)> Checkable::OnAcknowledgementSet;
boost::signals2::signal<void (const Checkable::Ptr&, const String&)> Checkable::OnAcknowledgementCleared;
boost::signals2::signal<void (const Checkable::Ptr&, const String&, const String&, AcknowledgementType, double, const MessageOrigin&)> Checkable::OnAcknowledgementSet;
boost::signals2::signal<void (const Checkable::Ptr&, const MessageOrigin&)> Checkable::OnAcknowledgementCleared;
Checkable::Checkable(void)
: m_CheckRunning(false)
@ -129,7 +129,7 @@ bool Checkable::IsAcknowledged(void)
return GetAcknowledgement() != AcknowledgementNone;
}
void Checkable::AcknowledgeProblem(const String& author, const String& comment, AcknowledgementType type, double expiry, const String& authority)
void Checkable::AcknowledgeProblem(const String& author, const String& comment, AcknowledgementType type, double expiry, const MessageOrigin& origin)
{
{
ObjectLock olock(this);
@ -140,17 +140,17 @@ void Checkable::AcknowledgeProblem(const String& author, const String& comment,
OnNotificationsRequested(GetSelf(), NotificationAcknowledgement, GetLastCheckResult(), author, comment);
OnAcknowledgementSet(GetSelf(), author, comment, type, expiry, authority);
OnAcknowledgementSet(GetSelf(), author, comment, type, expiry, origin);
}
void Checkable::ClearAcknowledgement(const String& authority)
void Checkable::ClearAcknowledgement(const MessageOrigin& origin)
{
ASSERT(OwnsLock());
SetAcknowledgementRaw(AcknowledgementNone);
SetAcknowledgementExpiry(0);
OnAcknowledgementCleared(GetSelf(), authority);
OnAcknowledgementCleared(GetSelf(), origin);
}
bool Checkable::GetEnablePerfdata(void) const
@ -161,7 +161,7 @@ bool Checkable::GetEnablePerfdata(void) const
return GetEnablePerfdataRaw();
}
void Checkable::SetEnablePerfdata(bool enabled, const String& authority)
void Checkable::SetEnablePerfdata(bool enabled, const MessageOrigin& origin)
{
SetOverrideEnablePerfdata(enabled);
}

View File

@ -27,6 +27,7 @@
#include "icinga/notification.h"
#include "icinga/comment.h"
#include "icinga/downtime.h"
#include "remote/messageorigin.h"
#include "base/i2-base.h"
#include "base/array.h"
#include <boost/signals2.hpp>
@ -97,8 +98,8 @@ public:
AcknowledgementType GetAcknowledgement(void);
void AcknowledgeProblem(const String& author, const String& comment, AcknowledgementType type, double expiry = 0, const String& authority = String());
void ClearAcknowledgement(const String& authority = String());
void AcknowledgeProblem(const String& author, const String& comment, AcknowledgementType type, double expiry = 0, const MessageOrigin& origin = MessageOrigin());
void ClearAcknowledgement(const MessageOrigin& origin = MessageOrigin());
/* Checks */
shared_ptr<CheckCommand> GetCheckCommand(void) const;
@ -119,7 +120,7 @@ public:
long GetSchedulingOffset(void);
void SetSchedulingOffset(long offset);
void SetNextCheck(double nextCheck, const String& authority = String());
void SetNextCheck(double nextCheck, const MessageOrigin& origin = MessageOrigin());
double GetNextCheck(void);
void UpdateNextCheck(void);
@ -128,18 +129,18 @@ public:
double GetLastCheck(void) const;
bool GetEnableActiveChecks(void) const;
void SetEnableActiveChecks(bool enabled, const String& authority = String());
void SetEnableActiveChecks(bool enabled, const MessageOrigin& origin = MessageOrigin());
bool GetEnablePassiveChecks(void) const;
void SetEnablePassiveChecks(bool enabled, const String& authority = String());
void SetEnablePassiveChecks(bool enabled, const MessageOrigin& origin = MessageOrigin());
bool GetForceNextCheck(void) const;
void SetForceNextCheck(bool forced, const String& authority = String());
void SetForceNextCheck(bool forced, const MessageOrigin& origin = MessageOrigin());
static void UpdateStatistics(const CheckResult::Ptr& cr);
void ExecuteCheck(void);
void ProcessCheckResult(const CheckResult::Ptr& cr, const String& authority = String());
void ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrigin& origin = MessageOrigin());
int GetModifiedAttributes(void) const;
void SetModifiedAttributes(int flags);
@ -149,15 +150,15 @@ public:
static double CalculateExecutionTime(const CheckResult::Ptr& cr);
static double CalculateLatency(const CheckResult::Ptr& cr);
static boost::signals2::signal<void (const Checkable::Ptr&, double, const String&)> OnNextCheckChanged;
static boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> OnForceNextCheckChanged;
static boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> OnForceNextNotificationChanged;
static boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> OnEnableActiveChecksChanged;
static boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> OnEnablePassiveChecksChanged;
static boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> OnEnableNotificationsChanged;
static boost::signals2::signal<void (const Checkable::Ptr&, bool, const String&)> OnEnableFlappingChanged;
static boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, const String&)> OnNewCheckResult;
static boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, StateType, const String&)> OnStateChange;
static boost::signals2::signal<void (const Checkable::Ptr&, double, const MessageOrigin&)> OnNextCheckChanged;
static boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> OnForceNextCheckChanged;
static boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> OnForceNextNotificationChanged;
static boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> OnEnableActiveChecksChanged;
static boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> OnEnablePassiveChecksChanged;
static boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> OnEnableNotificationsChanged;
static boost::signals2::signal<void (const Checkable::Ptr&, bool, const MessageOrigin&)> OnEnableFlappingChanged;
static boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, const MessageOrigin&)> OnNewCheckResult;
static boost::signals2::signal<void (const Checkable::Ptr&, const CheckResult::Ptr&, StateType, const MessageOrigin&)> OnStateChange;
static boost::signals2::signal<void (const Checkable::Ptr&, NotificationType, const CheckResult::Ptr&,
const String&, const String&)> OnNotificationsRequested;
static boost::signals2::signal<void (const Notification::Ptr&, const Checkable::Ptr&, const std::set<User::Ptr>&,
@ -169,15 +170,15 @@ public:
static boost::signals2::signal<void (const Notification::Ptr&, const Checkable::Ptr&, const std::set<User::Ptr>&,
const NotificationType&, const CheckResult::Ptr&, const String&,
const String&)> OnNotificationSentToAllUsers;
static boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const String&)> OnCommentAdded;
static boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const String&)> OnCommentRemoved;
static boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const String&)> OnDowntimeAdded;
static boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const String&)> OnDowntimeRemoved;
static boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const MessageOrigin&)> OnCommentAdded;
static boost::signals2::signal<void (const Checkable::Ptr&, const Comment::Ptr&, const MessageOrigin&)> OnCommentRemoved;
static boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const MessageOrigin&)> OnDowntimeAdded;
static boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&, const MessageOrigin&)> OnDowntimeRemoved;
static boost::signals2::signal<void (const Checkable::Ptr&, FlappingState)> OnFlappingChanged;
static boost::signals2::signal<void (const Checkable::Ptr&, const Downtime::Ptr&)> OnDowntimeTriggered;
static boost::signals2::signal<void (const Checkable::Ptr&, const String&, const String&, AcknowledgementType,
double, const String&)> OnAcknowledgementSet;
static boost::signals2::signal<void (const Checkable::Ptr&, const String&)> OnAcknowledgementCleared;
double, const MessageOrigin&)> OnAcknowledgementSet;
static boost::signals2::signal<void (const Checkable::Ptr&, const MessageOrigin&)> OnAcknowledgementCleared;
static boost::signals2::signal<void (const Checkable::Ptr&)> OnEventCommandExecuted;
/* Downtimes */
@ -189,9 +190,9 @@ public:
double startTime, double endTime, bool fixed,
const String& triggeredBy, double duration,
const String& scheduledBy = String(), const String& id = String(),
const String& authority = String());
const MessageOrigin& origin = MessageOrigin());
static void RemoveDowntime(const String& id, bool cancelled, const String& = String());
static void RemoveDowntime(const String& id, bool cancelled, const MessageOrigin& origin = MessageOrigin());
void TriggerDowntimes(void);
static void TriggerDowntime(const String& id);
@ -209,11 +210,11 @@ public:
static int GetNextCommentID(void);
String AddComment(CommentType entryType, const String& author,
const String& text, double expireTime, const String& id = String(), const String& authority = String());
const String& text, double expireTime, const String& id = String(), const MessageOrigin& origin = MessageOrigin());
void RemoveAllComments(void);
void RemoveCommentsByType(int type);
static void RemoveComment(const String& id, const String& authority = String());
static void RemoveComment(const String& id, const MessageOrigin& origin = MessageOrigin());
static String GetCommentIDFromLegacyID(int id);
static Checkable::Ptr GetOwnerByCommentID(const String& id);
@ -221,7 +222,7 @@ public:
/* Notifications */
bool GetEnableNotifications(void) const;
void SetEnableNotifications(bool enabled, const String& authority = String());
void SetEnableNotifications(bool enabled, const MessageOrigin& origin = MessageOrigin());
void SendNotifications(NotificationType type, const CheckResult::Ptr& cr, const String& author = "", const String& text = "");
@ -229,7 +230,7 @@ public:
void AddNotification(const Notification::Ptr& notification);
void RemoveNotification(const Notification::Ptr& notification);
void SetForceNextNotification(bool force, const String& authority = String());
void SetForceNextNotification(bool force, const MessageOrigin& origin = MessageOrigin());
bool GetForceNextNotification(void) const;
void ResetNotificationNumbers(void);
@ -247,14 +248,14 @@ public:
double GetFlappingCurrent(void) const;
bool GetEnableFlapping(void) const;
void SetEnableFlapping(bool enabled, const String& authority = String());
void SetEnableFlapping(bool enabled, const MessageOrigin& origin = MessageOrigin());
bool IsFlapping(void) const;
void UpdateFlappingStatus(bool stateChange);
/* Performance data */
bool GetEnablePerfdata(void) const;
void SetEnablePerfdata(bool enabled, const String& authority = String());
void SetEnablePerfdata(bool enabled, const MessageOrigin& origin = MessageOrigin());
/* Dependencies */
void AddDependency(const shared_ptr<Dependency>& dep);

View File

@ -1,11 +0,0 @@
#include "base/dynamicobject.h"
namespace icinga
{
class Domain : DynamicObject
{
[config] Dictionary::Ptr acl;
};
}

View File

@ -28,6 +28,7 @@
#include "icinga/checkcommand.h"
#include "icinga/eventcommand.h"
#include "icinga/notificationcommand.h"
#include "remote/apifunction.h"
#include "base/convert.h"
#include "base/logger_fwd.h"
#include "base/objectlock.h"
@ -41,8 +42,7 @@
using namespace icinga;
static boost::once_flag l_InitializeOnce = BOOST_ONCE_INIT;
static boost::mutex l_Mutex;
INITIALIZE_ONCE(&ExternalCommandProcessor::StaticInitialize);
typedef boost::function<void (double time, const std::vector<String>& arguments)> ExternalCommandCallback;
@ -53,18 +53,46 @@ struct ExternalCommandInfo
size_t MaxArgs;
};
static std::map<String, ExternalCommandInfo> l_Commands;
static boost::mutex& GetMutex(void)
{
static boost::mutex mtx;
return mtx;
}
static std::map<String, ExternalCommandInfo>& GetCommands(void)
{
static std::map<String, ExternalCommandInfo> commands;
return commands;
}
boost::signals2::signal<void (double, const String&, const std::vector<String>&)> ExternalCommandProcessor::OnNewExternalCommand;
static Value ExternalCommandAPIWrapper(const String& command, const Dictionary::Ptr& params)
{
std::vector<String> arguments;
if (params) {
int i = 0;
while (params->Contains("arg" + Convert::ToString(i))) {
arguments.push_back(params->Get("arg" + Convert::ToString(i)));
i++;
}
}
ExternalCommandProcessor::Execute(Utility::GetTime(), command, arguments);
return true;
}
static void RegisterCommand(const String& command, const ExternalCommandCallback& callback, size_t minArgs = 0, size_t maxArgs = -1)
{
boost::mutex::scoped_lock lock(l_Mutex);
boost::mutex::scoped_lock lock(GetMutex());
ExternalCommandInfo eci;
eci.Callback = callback;
eci.MinArgs = minArgs;
eci.MaxArgs = (maxArgs == -1) ? minArgs : maxArgs;
l_Commands[command] = eci;
GetCommands()[command] = eci;
ApiFunction::Ptr afunc = make_shared<ApiFunction>(boost::bind(&ExternalCommandAPIWrapper, command, _2));
ApiFunction::Register("extcmd::" + command, afunc);
}
void ExternalCommandProcessor::Execute(const String& line)
@ -100,17 +128,15 @@ void ExternalCommandProcessor::Execute(const String& line)
void ExternalCommandProcessor::Execute(double time, const String& command, const std::vector<String>& arguments)
{
boost::call_once(l_InitializeOnce, &ExternalCommandProcessor::Initialize);
ExternalCommandInfo eci;
{
boost::mutex::scoped_lock lock(l_Mutex);
boost::mutex::scoped_lock lock(GetMutex());
std::map<String, ExternalCommandInfo>::iterator it;
it = l_Commands.find(command);
it = GetCommands().find(command);
if (it == l_Commands.end())
if (it == GetCommands().end())
BOOST_THROW_EXCEPTION(std::invalid_argument("The external command '" + command + "' does not exist."));
eci = it->second;
@ -143,7 +169,7 @@ void ExternalCommandProcessor::Execute(double time, const String& command, const
eci.Callback(time, realArguments);
}
void ExternalCommandProcessor::Initialize(void)
void ExternalCommandProcessor::StaticInitialize(void)
{
RegisterCommand("PROCESS_HOST_CHECK_RESULT", &ExternalCommandProcessor::ProcessHostCheckResult, 3);
RegisterCommand("PROCESS_SERVICE_CHECK_RESULT", &ExternalCommandProcessor::ProcessServiceCheckResult, 4);

View File

@ -37,13 +37,13 @@ public:
static void Execute(const String& line);
static void Execute(double time, const String& command, const std::vector<String>& arguments);
static boost::signals2::signal<void (double, const String&, const std::vector<String>&)> OnNewExternalCommand;
static void StaticInitialize(void);
static boost::signals2::signal<void(double, const String&, const std::vector<String>&)> OnNewExternalCommand;
private:
ExternalCommandProcessor(void);
static void Initialize(void);
static void ProcessHostCheckResult(double time, const std::vector<String>& arguments);
static void ProcessServiceCheckResult(double time, const std::vector<String>& arguments);
static void ScheduleHostCheck(double time, const std::vector<String>& arguments);

View File

@ -232,12 +232,6 @@
}
%type Domain {
%attribute %dictionary "acl" {
%attribute %number "*"
}
}
%type ScheduledDowntime {
%require "host_name",
%attribute %name(Host) "host_name",

View File

@ -39,7 +39,7 @@ REGISTER_TYPE(Notification);
REGISTER_SCRIPTFUNCTION(ValidateNotificationFilters, &Notification::ValidateFilters);
INITIALIZE_ONCE(&Notification::StaticInitialize);
boost::signals2::signal<void (const Notification::Ptr&, double, const String&)> Notification::OnNextNotificationChanged;
boost::signals2::signal<void (const Notification::Ptr&, double, const MessageOrigin&)> Notification::OnNextNotificationChanged;
String NotificationNameComposer::MakeName(const String& shortName, const Dictionary::Ptr props) const
{
@ -180,11 +180,11 @@ double Notification::GetNextNotification(void) const
* Sets the timestamp when the next periodical notification should be sent.
* This does not affect notifications that are sent for state changes.
*/
void Notification::SetNextNotification(double time, const String& authority)
void Notification::SetNextNotification(double time, const MessageOrigin& origin)
{
SetNextNotificationRaw(time);
OnNextNotificationChanged(GetSelf(), time, authority);
OnNextNotificationChanged(GetSelf(), time, origin);
}
void Notification::UpdateNotificationNumber(void)

View File

@ -25,6 +25,7 @@
#include "icinga/user.h"
#include "icinga/usergroup.h"
#include "icinga/timeperiod.h"
#include "remote/messageorigin.h"
#include "config/applyrule.h"
#include "base/array.h"
@ -86,7 +87,7 @@ public:
std::set<UserGroup::Ptr> GetUserGroups(void) const;
double GetNextNotification(void) const;
void SetNextNotification(double time, const String& authority = String());
void SetNextNotification(double time, const MessageOrigin& origin = MessageOrigin());
void UpdateNotificationNumber(void);
void ResetNotificationNumber(void);
@ -97,7 +98,7 @@ public:
static String NotificationTypeToString(NotificationType type);
static boost::signals2::signal<void (const Notification::Ptr&, double, const String&)> OnNextNotificationChanged;
static boost::signals2::signal<void (const Notification::Ptr&, double, const MessageOrigin&)> OnNextNotificationChanged;
static void RegisterApplyRuleHandler(void);

View File

@ -22,7 +22,7 @@ else()
endif()
add_library(methods SHARED
castfuncs.cpp icingachecktask.cpp nullchecktask.cpp nulleventtask.cpp
castfuncs.cpp clusterchecktask.cpp icingachecktask.cpp nullchecktask.cpp nulleventtask.cpp
pluginchecktask.cpp plugineventtask.cpp pluginnotificationtask.cpp
randomchecktask.cpp timeperiodtask.cpp ${WindowsSources}
)

View File

@ -17,8 +17,8 @@
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "cluster/clusterchecktask.h"
#include "cluster/clusterlistener.h"
#include "methods/clusterchecktask.h"
#include "remote/apilistener.h"
#include "remote/endpoint.h"
#include "icinga/cib.h"
#include "icinga/service.h"
@ -35,38 +35,39 @@ using namespace icinga;
REGISTER_SCRIPTFUNCTION(ClusterCheck, &ClusterCheckTask::ScriptFunc);
void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr)
void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr)
{
/* fetch specific cluster status */
std::pair<Dictionary::Ptr, Dictionary::Ptr> stats;
BOOST_FOREACH(const ClusterListener::Ptr& cluster_listener, DynamicType::GetObjects<ClusterListener>()) {
/* XXX there's only one cluster listener */
stats = cluster_listener->GetClusterStatus();
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener) {
cr->SetOutput("No API listener is configured for this instance.");
cr->SetState(ServiceUnknown);
checkable->ProcessCheckResult(cr);
return;
}
std::pair<Dictionary::Ptr, Dictionary::Ptr> stats = listener->GetStatus();
Dictionary::Ptr status = stats.first;
/* use feature stats perfdata */
std::pair<Dictionary::Ptr, Dictionary::Ptr> feature_stats = CIB::GetFeatureStats();
Dictionary::Ptr perfdata = feature_stats.second;
cr->SetPerformanceData(feature_stats.second);
String connected_endpoints = FormatArray(status->Get("conn_endpoints"));
String not_connected_endpoints = FormatArray(status->Get("not_conn_endpoints"));
ServiceState state = ServiceOK;
String output = "Icinga 2 Cluster is running: Connected Endpoints: "+ Convert::ToString(status->Get("num_conn_endpoints")) + " (" +
connected_endpoints + ").";
if (status->Get("num_not_conn_endpoints") > 0) {
state = ServiceCritical;
output = "Icinga 2 Cluster Problem: " + Convert::ToString(status->Get("num_not_conn_endpoints")) +
" Endpoints (" + not_connected_endpoints + ") not connected.";
cr->SetState(ServiceCritical);
cr->SetOutput("Icinga 2 Cluster is running: Connected Endpoints: "+ Convert::ToString(status->Get("num_conn_endpoints")) + " (" +
connected_endpoints + ").");
} else {
cr->SetState(ServiceOK);
cr->SetOutput("Icinga 2 Cluster Problem: " + Convert::ToString(status->Get("num_not_conn_endpoints")) +
" Endpoints (" + not_connected_endpoints + ") not connected.");
}
cr->SetOutput(output);
cr->SetPerformanceData(perfdata);
cr->SetState(state);
service->ProcessCheckResult(cr);
checkable->ProcessCheckResult(cr);
}
String ClusterCheckTask::FormatArray(const Array::Ptr& arr)
@ -88,4 +89,3 @@ String ClusterCheckTask::FormatArray(const Array::Ptr& arr)
return str;
}

View File

@ -15,12 +15,15 @@
# along with this program; if not, write to the Free Software Foundation
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
mkclass_target(apilistener.ti apilistener.th)
mkclass_target(endpoint.ti endpoint.th)
mkclass_target(zone.ti zone.th)
mkembedconfig_target(remote-type.conf remote-type.cpp)
add_library(remote SHARED
endpoint.cpp endpoint.th jsonrpc.cpp remote-type.cpp
apiclient.cpp apifunction.cpp apilistener.cpp apilistener.th endpoint.cpp
endpoint.th jsonrpc.cpp messageorigin.cpp remote-type.cpp zone.cpp zone.th
)
include_directories(${Boost_INCLUDE_DIRS})
@ -39,4 +42,7 @@ install(
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/icinga2
)
#install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/log\")")
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/icinga2/api/repository\")")

200
lib/remote/apiclient.cpp Normal file
View File

@ -0,0 +1,200 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "remote/apiclient.h"
#include "remote/apilistener.h"
#include "remote/apifunction.h"
#include "remote/jsonrpc.h"
#include "base/application.h"
#include "base/dynamictype.h"
#include "base/objectlock.h"
#include "base/utility.h"
#include "base/logger_fwd.h"
#include "base/exception.h"
#include "base/initialize.h"
#include "config/configitembuilder.h"
using namespace icinga;
Timer::Ptr ApiClient::m_KeepAliveTimer;
INITIALIZE_ONCE(&ApiClient::StaticInitialize);
static Value SetLogPositionHandler(const MessageOrigin& origin, const Dictionary::Ptr& params);
REGISTER_APIFUNCTION(SetLogPosition, log, &SetLogPositionHandler);
ApiClient::ApiClient(const Endpoint::Ptr& endpoint, const Stream::Ptr& stream, ConnectionRole role)
: m_Endpoint(endpoint), m_Stream(stream), m_Role(role), m_Seen(Utility::GetTime())
{ }
void ApiClient::StaticInitialize(void)
{
m_KeepAliveTimer = make_shared<Timer>();
m_KeepAliveTimer->OnTimerExpired.connect(boost::bind(&ApiClient::KeepAliveTimerHandler));
m_KeepAliveTimer->SetInterval(5);
m_KeepAliveTimer->Start();
}
void ApiClient::Start(void)
{
boost::thread thread(boost::bind(&ApiClient::MessageThreadProc, static_cast<ApiClient::Ptr>(GetSelf())));
thread.detach();
}
Endpoint::Ptr ApiClient::GetEndpoint(void) const
{
return m_Endpoint;
}
Stream::Ptr ApiClient::GetStream(void) const
{
return m_Stream;
}
ConnectionRole ApiClient::GetRole(void) const
{
return m_Role;
}
void ApiClient::SendMessage(const Dictionary::Ptr& message)
{
try {
ObjectLock olock(m_Stream);
JsonRpc::SendMessage(m_Stream, message);
if (message->Get("method") != "log::SetLogPosition")
m_Seen = Utility::GetTime();
} catch (const std::exception& ex) {
std::ostringstream msgbuf;
msgbuf << "Error while sending JSON-RPC message for endpoint '" << m_Endpoint->GetName() << "': " << DiagnosticInformation(ex);
Log(LogWarning, "remote", msgbuf.str());
Disconnect();
}
}
void ApiClient::Disconnect(void)
{
Log(LogWarning, "remote", "API client disconnected for endpoint '" + m_Endpoint->GetName() + "'");
m_Stream->Close();
m_Endpoint->RemoveClient(GetSelf());
}
bool ApiClient::ProcessMessage(void)
{
Dictionary::Ptr message = JsonRpc::ReadMessage(m_Stream);
if (!message)
return false;
if (message->Get("method") != "log::SetLogPosition")
m_Seen = Utility::GetTime();
if (message->Contains("ts")) {
double ts = message->Get("ts");
/* ignore old messages */
if (ts < m_Endpoint->GetRemoteLogPosition())
return true;
m_Endpoint->SetRemoteLogPosition(ts);
}
MessageOrigin origin;
origin.FromClient = GetSelf();
if (m_Endpoint->GetZone() != Zone::GetLocalZone())
origin.FromZone = m_Endpoint->GetZone();
else
origin.FromZone = Zone::GetByName(message->Get("originZone"));
String method = message->Get("method");
Log(LogDebug, "remote", "Received '" + method + "' message from '" + m_Endpoint->GetName() + "'");
Dictionary::Ptr resultMessage = make_shared<Dictionary>();
try {
ApiFunction::Ptr afunc = ApiFunction::GetByName(method);
if (!afunc)
BOOST_THROW_EXCEPTION(std::invalid_argument("Function '" + method + "' does not exist."));
resultMessage->Set("result", afunc->Invoke(origin, message->Get("params")));
} catch (std::exception& ex) {
resultMessage->Set("error", DiagnosticInformation(ex));
}
if (message->Contains("id")) {
resultMessage->Set("jsonrpc", "2.0");
resultMessage->Set("id", message->Get("id"));
JsonRpc::SendMessage(m_Stream, resultMessage);
}
return true;
}
void ApiClient::MessageThreadProc(void)
{
Utility::SetThreadName("API Client");
try {
while (ProcessMessage())
; /* empty loop body */
Disconnect();
} catch (const std::exception& ex) {
Log(LogWarning, "remote", "Error while reading JSON-RPC message for endpoint '" + m_Endpoint->GetName() + "': " + DiagnosticInformation(ex));
}
}
void ApiClient::KeepAliveTimerHandler(void)
{
double now = Utility::GetTime();
BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
if (endpoint->GetZone() == Zone::GetLocalZone())
continue;
if (endpoint->GetSyncing() || endpoint->GetKeepAlive() <= 0)
continue;
double timeout = now - endpoint->GetKeepAlive();
BOOST_FOREACH(const ApiClient::Ptr& client, endpoint->GetClients()) {
if (client->m_Seen < timeout) {
Log(LogInformation, "remote", "Closing connection with inactive endpoint '" + endpoint->GetName() + "'");
client->Disconnect();
}
}
}
}
Value SetLogPositionHandler(const MessageOrigin& origin, const Dictionary::Ptr& params)
{
if (!params)
return Empty;
double log_position = params->Get("log_position");
Endpoint::Ptr endpoint = origin.FromClient->GetEndpoint();
if (log_position > endpoint->GetLocalLogPosition())
endpoint->SetLocalLogPosition(log_position);
return Empty;
}

View File

@ -17,34 +17,63 @@
* 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;
static bool SendResult(const Checkable::Ptr& checkable, bool enqueue);
bool ProcessMessage(void);
void MessageThreadProc(void);
static Timer::Ptr m_KeepAliveTimer;
static void KeepAliveTimerHandler(void);
};
}
#endif /* AGENTCHECKTASK_H */
#endif /* APICLIENT_H */

View File

@ -0,0 +1,59 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "remote/apifunction.h"
#include "base/registry.h"
#include "base/singleton.h"
using namespace icinga;
ApiFunction::ApiFunction(const Callback& function)
: m_Callback(function)
{ }
Value ApiFunction::Invoke(const MessageOrigin& origin, const Dictionary::Ptr& arguments)
{
return m_Callback(origin, arguments);
}
RegisterApiFunctionHelper::RegisterApiFunctionHelper(const String& name, const ApiFunction::Callback& function)
{
ApiFunction::Ptr func = make_shared<ApiFunction>(function);
ApiFunctionRegistry::GetInstance()->Register(name, func);
}
ApiFunction::Ptr ApiFunction::GetByName(const String& name)
{
return ApiFunctionRegistry::GetInstance()->GetItem(name);
}
void ApiFunction::Register(const String& name, const ApiFunction::Ptr& function)
{
ApiFunctionRegistry::GetInstance()->Register(name, function);
}
void ApiFunction::Unregister(const String& name)
{
ApiFunctionRegistry::GetInstance()->Unregister(name);
}
ApiFunctionRegistry *ApiFunctionRegistry::GetInstance(void)
{
return Singleton<ApiFunctionRegistry>::GetInstance();
}

87
lib/remote/apifunction.h Normal file
View File

@ -0,0 +1,87 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef APIFUNCTION_H
#define APIFUNCTION_H
#include "remote/i2-remote.h"
#include "remote/apiclient.h"
#include "remote/messageorigin.h"
#include "base/registry.h"
#include "base/singleton.h"
#include "base/value.h"
#include "base/dictionary.h"
#include <vector>
#include <boost/function.hpp>
namespace icinga
{
/**
* An API function.
*
* @ingroup base
*/
class I2_REMOTE_API ApiFunction : public Object
{
public:
DECLARE_PTR_TYPEDEFS(ApiFunction);
typedef boost::function<Value(const MessageOrigin& origin, const Dictionary::Ptr&)> Callback;
ApiFunction(const Callback& function);
Value Invoke(const MessageOrigin& origin, const Dictionary::Ptr& arguments);
static ApiFunction::Ptr GetByName(const String& name);
static void Register(const String& name, const ApiFunction::Ptr& function);
static void Unregister(const String& name);
private:
Callback m_Callback;
};
/**
* A registry for API functions.
*
* @ingroup base
*/
class I2_REMOTE_API ApiFunctionRegistry : public Registry<ApiFunctionRegistry, ApiFunction::Ptr>
{
public:
static ApiFunctionRegistry *GetInstance(void);
};
/**
* Helper class for registering ApiFunction implementation classes.
*
* @ingroup base
*/
class I2_REMOTE_API RegisterApiFunctionHelper
{
public:
RegisterApiFunctionHelper(const String& name, const ApiFunction::Callback& function);
};
#define REGISTER_APIFUNCTION(name, ns, callback) \
I2_EXPORT icinga::RegisterApiFunctionHelper g_RegisterAF_ ## name(#ns "::" #name, callback)
}
#endif /* APIFUNCTION_H */

633
lib/remote/apilistener.cpp Normal file
View File

@ -0,0 +1,633 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "remote/apilistener.h"
#include "remote/apiclient.h"
#include "remote/endpoint.h"
#include "remote/jsonrpc.h"
#include "base/convert.h"
#include "base/netstring.h"
#include "base/dynamictype.h"
#include "base/logger_fwd.h"
#include "base/objectlock.h"
#include "base/stdiostream.h"
#include "base/networkstream.h"
#include "base/application.h"
#include "base/context.h"
#include "base/statsfunction.h"
#include <fstream>
using namespace icinga;
REGISTER_TYPE(ApiListener);
boost::signals2::signal<void(bool)> ApiListener::OnMasterChanged;
REGISTER_STATSFUNCTION(ApiListenerStats, &ApiListener::StatsFunc);
void ApiListener::OnConfigLoaded(void)
{
/* set up SSL context */
shared_ptr<X509> cert = GetX509Certificate(GetCertPath());
SetIdentity(GetCertificateCN(cert));
Log(LogInformation, "remote", "My API identity: " + GetIdentity());
m_SSLContext = MakeSSLContext(GetCertPath(), GetKeyPath(), GetCaPath());
if (!GetCrlPath().IsEmpty())
AddCRLToSSLContext(m_SSLContext, GetCrlPath());
if (!Endpoint::GetByName(GetIdentity()))
BOOST_THROW_EXCEPTION(std::runtime_error("Endpoint object for '" + GetIdentity() + "' is missing."));
}
/**
* Starts the component.
*/
void ApiListener::Start(void)
{
if (std::distance(DynamicType::GetObjects<ApiListener>().first, DynamicType::GetObjects<ApiListener>().second) > 1)
BOOST_THROW_EXCEPTION(std::runtime_error("Only one ApiListener object is allowed."));
DynamicObject::Start();
{
boost::mutex::scoped_lock(m_LogLock);
RotateLogFile();
OpenLogFile();
}
/* create the primary JSON-RPC listener */
AddListener(GetBindPort());
m_Timer = make_shared<Timer>();
m_Timer->OnTimerExpired.connect(boost::bind(&ApiListener::ApiTimerHandler, this));
m_Timer->SetInterval(5);
m_Timer->Start();
m_Timer->Reschedule(0);
OnMasterChanged(true);
}
ApiListener::Ptr ApiListener::GetInstance(void)
{
BOOST_FOREACH(const ApiListener::Ptr& listener, DynamicType::GetObjects<ApiListener>())
return listener;
return ApiListener::Ptr();
}
shared_ptr<SSL_CTX> ApiListener::GetSSLContext(void) const
{
return m_SSLContext;
}
Endpoint::Ptr ApiListener::GetMaster(void) const
{
Zone::Ptr zone = Zone::GetLocalZone();
std::vector<String> names;
BOOST_FOREACH(const Endpoint::Ptr& endpoint, zone->GetEndpoints())
if (endpoint->IsConnected() || endpoint->GetName() == GetIdentity())
names.push_back(endpoint->GetName());
std::sort(names.begin(), names.end());
return Endpoint::GetByName(*names.begin());
}
bool ApiListener::IsMaster(void) const
{
return GetMaster()->GetName() == GetIdentity();
}
/**
* Creates a new JSON-RPC listener on the specified port.
*
* @param service The port to listen on.
*/
void ApiListener::AddListener(const String& service)
{
ObjectLock olock(this);
shared_ptr<SSL_CTX> sslContext = m_SSLContext;
if (!sslContext)
BOOST_THROW_EXCEPTION(std::logic_error("SSL context is required for AddListener()"));
std::ostringstream s;
s << "Adding new listener: port " << service;
Log(LogInformation, "agent", s.str());
TcpSocket::Ptr server = make_shared<TcpSocket>();
server->Bind(service, AF_INET6);
boost::thread thread(boost::bind(&ApiListener::ListenerThreadProc, this, server));
thread.detach();
m_Servers.insert(server);
}
void ApiListener::ListenerThreadProc(const Socket::Ptr& server)
{
Utility::SetThreadName("API Listener");
server->Listen();
for (;;) {
Socket::Ptr client = server->Accept();
Utility::QueueAsyncCallback(boost::bind(&ApiListener::NewClientHandler, this, client, RoleServer));
}
}
/**
* Creates a new JSON-RPC client and connects to the specified host and port.
*
* @param node The remote host.
* @param service The remote port.
*/
void ApiListener::AddConnection(const String& node, const String& service) {
{
ObjectLock olock(this);
shared_ptr<SSL_CTX> sslContext = m_SSLContext;
if (!sslContext)
BOOST_THROW_EXCEPTION(std::logic_error("SSL context is required for AddConnection()"));
}
TcpSocket::Ptr client = make_shared<TcpSocket>();
client->Connect(node, service);
Utility::QueueAsyncCallback(boost::bind(&ApiListener::NewClientHandler, this, client, RoleClient));
}
/**
* Processes a new client connection.
*
* @param client The new client.
*/
void ApiListener::NewClientHandler(const Socket::Ptr& client, ConnectionRole role)
{
CONTEXT("Handling new API client connection");
TlsStream::Ptr tlsStream;
{
ObjectLock olock(this);
tlsStream = make_shared<TlsStream>(client, role, m_SSLContext);
}
tlsStream->Handshake();
shared_ptr<X509> cert = tlsStream->GetPeerCertificate();
String identity = GetCertificateCN(cert);
Endpoint::Ptr endpoint = Endpoint::GetByName(identity);
if (!endpoint) {
Log(LogInformation, "remote", "New client for unknown endpoint '" + identity + "'");
return;
}
Log(LogInformation, "remote", "New client connection for identity '" + identity + "'");
bool need_sync = !endpoint->IsConnected();
ApiClient::Ptr aclient = make_shared<ApiClient>(endpoint, tlsStream, role);
aclient->Start();
if (need_sync) {
{
ObjectLock olock(endpoint);
endpoint->SetSyncing(true);
}
ReplayLog(aclient);
}
endpoint->AddClient(aclient);
}
void ApiListener::ApiTimerHandler(void)
{
double now = Utility::GetTime();
std::vector<int> files;
Utility::Glob(GetApiDir() + "log/*", boost::bind(&ApiListener::LogGlobHandler, boost::ref(files), _1), GlobFile);
std::sort(files.begin(), files.end());
BOOST_FOREACH(int ts, files) {
bool need = false;
BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
if (endpoint->GetName() == GetIdentity())
continue;
if (endpoint->GetLogDuration() >= 0 && ts < now - endpoint->GetLogDuration())
continue;
if (ts > endpoint->GetLocalLogPosition()) {
need = true;
break;
}
}
if (!need) {
String path = GetApiDir() + "log/" + Convert::ToString(ts);
Log(LogInformation, "remote", "Removing old log file: " + path);
(void)unlink(path.CStr());
}
}
if (IsMaster()) {
Zone::Ptr my_zone = Zone::GetLocalZone();
BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
if (endpoint->IsConnected() || endpoint->GetName() == GetIdentity())
continue;
if (endpoint->GetHost().IsEmpty() || endpoint->GetPort().IsEmpty())
continue;
Zone::Ptr their_zone = endpoint->GetZone();
if (my_zone != their_zone && my_zone != their_zone->GetParent() && their_zone != my_zone->GetParent())
continue;
AddConnection(endpoint->GetHost(), endpoint->GetPort());
}
}
BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
if (!endpoint->IsConnected())
continue;
double ts = endpoint->GetRemoteLogPosition();
if (ts == 0)
continue;
Dictionary::Ptr lparams = make_shared<Dictionary>();
lparams->Set("log_position", ts);
Dictionary::Ptr lmessage = make_shared<Dictionary>();
lmessage->Set("jsonrpc", "2.0");
lmessage->Set("method", "log::SetLogPosition");
lmessage->Set("params", lparams);
BOOST_FOREACH(const ApiClient::Ptr& client, endpoint->GetClients())
client->SendMessage(lmessage);
Log(LogInformation, "remote", "Setting log position for identity '" + endpoint->GetName() + "': " +
Utility::FormatDateTime("%Y/%m/%d %H:%M:%S", ts));
}
Log(LogInformation, "remote", "Current master: " + GetMaster()->GetName());
std::vector<String> names;
BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>())
if (endpoint->IsConnected())
names.push_back(endpoint->GetName() + " (" + Convert::ToString(endpoint->GetClients().size()) + ")");
Log(LogInformation, "remote", "Connected endpoints: " + Utility::NaturalJoin(names));
}
void ApiListener::RelayMessage(const MessageOrigin& origin, const DynamicObject::Ptr& secobj, const Dictionary::Ptr& message, bool log)
{
m_RelayQueue.Enqueue(boost::bind(&ApiListener::SyncRelayMessage, this, origin, secobj, message, log));
}
void ApiListener::PersistMessage(const Dictionary::Ptr& message)
{
double ts = message->Get("ts");
ASSERT(ts != 0);
Dictionary::Ptr pmessage = make_shared<Dictionary>();
pmessage->Set("timestamp", ts);
pmessage->Set("message", JsonSerialize(message));
boost::mutex::scoped_lock lock(m_LogLock);
if (m_LogFile) {
NetString::WriteStringToStream(m_LogFile, JsonSerialize(pmessage));
m_LogMessageCount++;
SetLogMessageTimestamp(ts);
if (m_LogMessageCount > 50000) {
CloseLogFile();
RotateLogFile();
OpenLogFile();
}
}
}
void ApiListener::SyncRelayMessage(const MessageOrigin& origin, const DynamicObject::Ptr& secobj, const Dictionary::Ptr& message, bool log)
{
double ts = Utility::GetTime();
message->Set("ts", ts);
Log(LogDebug, "remote", "Relaying '" + message->Get("method") + "' message");
if (log)
m_LogQueue.Enqueue(boost::bind(&ApiListener::PersistMessage, this, message));
if (origin.FromZone)
message->Set("originZone", origin.FromZone->GetName());
bool is_master = IsMaster();
Endpoint::Ptr master = GetMaster();
Zone::Ptr my_zone = Zone::GetLocalZone();
std::vector<Endpoint::Ptr> skippedEndpoints;
std::set<Zone::Ptr> finishedZones;
BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
/* don't relay messages to ourselves or disconnected endpoints */
if (endpoint->GetName() == GetIdentity() || !endpoint->IsConnected())
continue;
Zone::Ptr target_zone = endpoint->GetZone();
/* don't relay the message to the zone through more than one endpoint */
if (finishedZones.find(target_zone) != finishedZones.end()) {
skippedEndpoints.push_back(endpoint);
continue;
}
/* don't relay messages back to the endpoint which we got the message from */
if (origin.FromClient && endpoint == origin.FromClient->GetEndpoint()) {
skippedEndpoints.push_back(endpoint);
continue;
}
/* don't relay messages back to the zone which we got the message from */
if (origin.FromZone && target_zone == origin.FromZone) {
skippedEndpoints.push_back(endpoint);
continue;
}
/* only relay message to the master if we're not currently the master */
if (!is_master && master != endpoint) {
skippedEndpoints.push_back(endpoint);
continue;
}
/* only relay the message to a) the same zone, b) the parent zone and c) direct child zones */
if (target_zone != my_zone && target_zone != my_zone->GetParent() &&
secobj->GetZone() != target_zone->GetName()) {
skippedEndpoints.push_back(endpoint);
continue;
}
/* only relay messages to zones which have access to the object */
if (!target_zone->CanAccessObject(secobj))
continue;
finishedZones.insert(target_zone);
{
ObjectLock olock(endpoint);
if (!endpoint->GetSyncing()) {
Log(LogDebug, "remote", "Sending message to '" + endpoint->GetName() + "'");
BOOST_FOREACH(const ApiClient::Ptr& client, endpoint->GetClients())
client->SendMessage(message);
}
}
}
BOOST_FOREACH(const Endpoint::Ptr& endpoint, skippedEndpoints)
endpoint->SetLocalLogPosition(ts);
}
String ApiListener::GetApiDir(void)
{
return Application::GetLocalStateDir() + "/lib/icinga2/api/";
}
/* must hold m_LogLock */
void ApiListener::OpenLogFile(void)
{
String path = GetApiDir() + "log/current";
std::fstream *fp = new std::fstream(path.CStr(), std::fstream::out | std::ofstream::app);
if (!fp->good()) {
Log(LogWarning, "cluster", "Could not open spool file: " + path);
return;
}
StdioStream::Ptr logStream = make_shared<StdioStream>(fp, true);
#ifdef HAVE_BIOZLIB
m_LogFile = make_shared<ZlibStream>(logStream);
#else /* HAVE_BIOZLIB */
m_LogFile = logStream;
#endif /* HAVE_BIOZLIB */
m_LogMessageCount = 0;
SetLogMessageTimestamp(Utility::GetTime());
}
/* must hold m_LogLock */
void ApiListener::CloseLogFile(void)
{
if (!m_LogFile)
return;
m_LogFile->Close();
m_LogFile.reset();
}
/* must hold m_LogLock */
void ApiListener::RotateLogFile(void)
{
double ts = GetLogMessageTimestamp();
if (ts == 0)
ts = Utility::GetTime();
String oldpath = GetApiDir() + "log/current";
String newpath = GetApiDir() + "log/" + Convert::ToString(static_cast<int>(ts)+1);
(void) rename(oldpath.CStr(), newpath.CStr());
}
void ApiListener::LogGlobHandler(std::vector<int>& files, const String& file)
{
String name = Utility::BaseName(file);
int ts;
try {
ts = Convert::ToLong(name);
}
catch (const std::exception&) {
return;
}
files.push_back(ts);
}
void ApiListener::ReplayLog(const ApiClient::Ptr& client)
{
Endpoint::Ptr endpoint = client->GetEndpoint();
CONTEXT("Replaying log for Endpoint '" + endpoint->GetName() + "'");
int count = -1;
double peer_ts = endpoint->GetLocalLogPosition();
bool last_sync = false;
for (;;) {
boost::mutex::scoped_lock lock(m_LogLock);
CloseLogFile();
RotateLogFile();
if (count == -1 || count > 50000) {
OpenLogFile();
lock.unlock();
} else {
last_sync = true;
}
count = 0;
std::vector<int> files;
Utility::Glob(GetApiDir() + "log/*", boost::bind(&ApiListener::LogGlobHandler, boost::ref(files), _1), GlobFile);
std::sort(files.begin(), files.end());
BOOST_FOREACH(int ts, files) {
String path = GetApiDir() + "log/" + Convert::ToString(ts);
if (ts < peer_ts)
continue;
Log(LogInformation, "cluster", "Replaying log: " + path);
std::fstream *fp = new std::fstream(path.CStr(), std::fstream::in);
StdioStream::Ptr logStream = make_shared<StdioStream>(fp, true);
#ifdef HAVE_BIOZLIB
ZlibStream::Ptr lstream = make_shared<ZlibStream>(logStream);
#else /* HAVE_BIOZLIB */
Stream::Ptr lstream = logStream;
#endif /* HAVE_BIOZLIB */
String message;
while (true) {
Dictionary::Ptr pmessage;
try {
if (!NetString::ReadStringFromStream(lstream, &message))
break;
pmessage = JsonDeserialize(message);
} catch (const std::exception&) {
Log(LogWarning, "cluster", "Unexpected end-of-file for cluster log: " + path);
/* Log files may be incomplete or corrupted. This is perfectly OK. */
break;
}
if (pmessage->Get("timestamp") <= peer_ts)
continue;
NetString::WriteStringToStream(client->GetStream(), pmessage->Get("message"));
count++;
peer_ts = pmessage->Get("timestamp");
}
lstream->Close();
}
Log(LogInformation, "cluster", "Replayed " + Convert::ToString(count) + " messages.");
if (last_sync) {
{
ObjectLock olock2(endpoint);
endpoint->SetSyncing(false);
}
OpenLogFile();
break;
}
}
}
Value ApiListener::StatsFunc(Dictionary::Ptr& status, Dictionary::Ptr& perfdata)
{
Dictionary::Ptr nodes = make_shared<Dictionary>();
std::pair<Dictionary::Ptr, Dictionary::Ptr> stats;
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return 0;
stats = listener->GetStatus();
BOOST_FOREACH(Dictionary::Pair const& kv, stats.second)
perfdata->Set("api_" + kv.first, kv.second);
status->Set("api", stats.first);
return 0;
}
std::pair<Dictionary::Ptr, Dictionary::Ptr> ApiListener::GetStatus(void)
{
Dictionary::Ptr status = make_shared<Dictionary>();
Dictionary::Ptr perfdata = make_shared<Dictionary>();
/* cluster stats */
status->Set("identity", GetIdentity());
double count_endpoints = 0;
Array::Ptr not_connected_endpoints = make_shared<Array>();
Array::Ptr connected_endpoints = make_shared<Array>();
BOOST_FOREACH(const Endpoint::Ptr& endpoint, DynamicType::GetObjects<Endpoint>()) {
if (endpoint->GetName() == GetIdentity())
continue;
count_endpoints++;
if (!endpoint->IsConnected())
not_connected_endpoints->Add(endpoint->GetName());
else
connected_endpoints->Add(endpoint->GetName());
}
status->Set("num_endpoints", count_endpoints);
status->Set("num_conn_endpoints", connected_endpoints->GetLength());
status->Set("num_not_conn_endpoints", not_connected_endpoints->GetLength());
status->Set("conn_endpoints", connected_endpoints);
status->Set("not_conn_endpoints", not_connected_endpoints);
perfdata->Set("num_endpoints", count_endpoints);
perfdata->Set("num_conn_endpoints", Convert::ToDouble(connected_endpoints->GetLength()));
perfdata->Set("num_not_conn_endpoints", Convert::ToDouble(not_connected_endpoints->GetLength()));
return std::make_pair(status, perfdata);
}

View File

@ -17,61 +17,89 @@
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef AGENTLISTENER_H
#define AGENTLISTENER_H
#ifndef APILISTENER_H
#define APILISTENER_H
#include "agent/agentlistener.th"
#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;
friend class AgentCheckTask;
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);
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 */

View File

@ -1,19 +1,22 @@
#include "base/dynamicobject.h"
#include "base/application.h"
namespace icinga
{
class ClusterListener : DynamicObject
class ApiListener : DynamicObject
{
[config] String cert_path;
[config] String key_path;
[config] String ca_path;
[config] String crl_path;
[config] String bind_host;
[config] String bind_port;
[config] Array::Ptr peers;
[config] String bind_port {
default {{{ return "5665"; }}}
};
[state] double log_message_timestamp;
String identity;
};

View File

@ -18,116 +18,100 @@
******************************************************************************/
#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;
void Endpoint::OnConfigLoaded(void)
{
DynamicObject::OnConfigLoaded();
BOOST_FOREACH(const Zone::Ptr& zone, DynamicType::GetObjects<Zone>()) {
const std::set<Endpoint::Ptr> members = zone->GetEndpoints();
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::AddClient(const ApiClient::Ptr& client)
{
bool was_master = ApiListener::GetInstance()->IsMaster();
{
boost::mutex::scoped_lock lock(m_ClientsLock);
m_Clients.insert(client);
}
bool is_master = ApiListener::GetInstance()->IsMaster();
if (was_master != is_master)
ApiListener::OnMasterChanged(is_master);
OnConnected(GetSelf(), client);
}
void Endpoint::RemoveClient(const ApiClient::Ptr& client)
{
bool was_master = ApiListener::GetInstance()->IsMaster();
{
boost::mutex::scoped_lock lock(m_ClientsLock);
m_Clients.erase(client);
}
bool is_master = ApiListener::GetInstance()->IsMaster();
if (was_master != is_master)
ApiListener::OnMasterChanged(is_master);
OnDisconnected(GetSelf(), client);
}
std::set<ApiClient::Ptr> Endpoint::GetClients(void) const
{
boost::mutex::scoped_lock lock(m_ClientsLock);
return m_Clients;
}
Zone::Ptr Endpoint::GetZone(void) const
{
return m_Zone;
}
/**
* Checks whether this endpoint is connected.
*
* @returns true if the endpoint is connected, false otherwise.
*/
bool Endpoint::IsConnected(void) const
{
return GetClient() != NULL;
boost::mutex::scoped_lock lock(m_ClientsLock);
return !m_Clients.empty();
}
bool Endpoint::IsAvailable(void) const
Endpoint::Ptr Endpoint::GetLocalEndpoint(void)
{
return GetSeen() > Utility::GetTime() - 30;
ApiListener::Ptr listener = ApiListener::GetInstance();
if (!listener)
return Endpoint::Ptr();
return Endpoint::GetByName(listener->GetIdentity());
}
Stream::Ptr Endpoint::GetClient(void) const
{
return m_Client;
}
void Endpoint::SetClient(const Stream::Ptr& client)
{
SetBlockedUntil(Utility::GetTime() + 15);
if (m_Client)
m_Client->Close();
m_Client = client;
if (client) {
boost::thread thread(boost::bind(&Endpoint::MessageThreadProc, this, client));
thread.detach();
OnConnected(GetSelf());
Log(LogInformation, "remote", "Endpoint connected: " + GetName());
} else {
OnDisconnected(GetSelf());
Log(LogInformation, "remote", "Endpoint disconnected: " + GetName());
}
}
void Endpoint::SendMessage(const Dictionary::Ptr& message)
{
Stream::Ptr client = GetClient();
if (!client)
return;
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());
m_Client.reset();
OnDisconnected(GetSelf());
Log(LogWarning, "remote", "Endpoint disconnected: " + GetName());
}
}
void Endpoint::MessageThreadProc(const Stream::Ptr& stream)
{
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());
return;
}
OnMessageReceived(GetSelf(), message);
}
}
bool Endpoint::HasFeature(const String& type) const
{
Dictionary::Ptr features = GetFeatures();
if (!features)
return false;
return features->Get(type);
}

View File

@ -29,12 +29,13 @@
namespace icinga
{
class EndpointManager;
class ApiClient;
class Zone;
/**
* An endpoint that can be used to send and receive messages.
*
* @ingroup cluster
* @ingroup remote
*/
class I2_REMOTE_API Endpoint : public ObjectImpl<Endpoint>
{
@ -42,26 +43,26 @@ public:
DECLARE_PTR_TYPEDEFS(Endpoint);
DECLARE_TYPENAME(Endpoint);
static boost::signals2::signal<void (const Endpoint::Ptr&)> OnConnected;
static boost::signals2::signal<void (const Endpoint::Ptr&)> OnDisconnected;
static boost::signals2::signal<void (const Endpoint::Ptr&, const Dictionary::Ptr&)> OnMessageReceived;
static boost::signals2::signal<void(const Endpoint::Ptr&, const shared_ptr<ApiClient>&)> OnConnected;
static boost::signals2::signal<void(const Endpoint::Ptr&, const shared_ptr<ApiClient>&)> OnDisconnected;
Stream::Ptr GetClient(void) const;
void SetClient(const Stream::Ptr& client);
void AddClient(const shared_ptr<ApiClient>& client);
void RemoveClient(const shared_ptr<ApiClient>& client);
std::set<shared_ptr<ApiClient> > GetClients(void) const;
shared_ptr<Zone> GetZone(void) const;
bool IsConnected(void) const;
bool IsAvailable(void) const;
void SendMessage(const Dictionary::Ptr& request);
static Endpoint::Ptr GetLocalEndpoint(void);
bool HasFeature(const String& type) const;
protected:
virtual void OnConfigLoaded(void);
private:
Stream::Ptr m_Client;
boost::thread m_Thread;
Array::Ptr m_ConnectedEndpoints;
void MessageThreadProc(const Stream::Ptr& stream);
mutable boost::mutex m_ClientsLock;
std::set<shared_ptr<ApiClient> > m_Clients;
shared_ptr<Zone> m_Zone;
};
}

View File

@ -6,19 +6,20 @@ namespace icinga
class Endpoint : DynamicObject
{
[config] String host;
[config] String port;
[config] Array::Ptr config_files;
[config] Array::Ptr config_files_recursive;
[config] Array::Ptr accept_config;
[config] int metric;
[config] String port {
default {{{ return "5665"; }}}
};
[config] double keep_alive {
default {{{ return 300; }}}
};
[config] double log_duration {
default {{{ return 86400; }}}
};
[state] double seen;
[state] double local_log_position;
[state] double remote_log_position;
[state] Dictionary::Ptr features;
bool syncing;
double blocked_until;
};
}

View File

@ -17,14 +17,16 @@
* 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);
int Domain::GetPrivileges(const String& instance) const
bool MessageOrigin::IsLocal(void) const
{
return GetAcl()->Get(instance);
return !FromClient;
}
bool MessageOrigin::IsSameZone(void) const
{
return !FromZone;
}

View File

@ -17,30 +17,27 @@
* 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 */

View File

@ -17,28 +17,34 @@
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
%type Endpoint {
%require "host",
%attribute %string "host",
%type ApiListener {
%require "cert_path",
%attribute %string "cert_path",
%require "port",
%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 {
%attribute %string "host",
%attribute %string "port",
%attribute %number "metric",
%attribute %number "keep_alive",
%attribute %number "log_duration"
}
%attribute %array "config_files" {
%attribute %string "*"
},
%type Zone {
%attribute %name(Zone) "parent",
%attribute %array "config_files_recursive" {
%attribute %string "*",
%attribute %dictionary "*" {
%attribute %string "path",
%attribute %string "pattern"
}
},
%attribute %array "accept_config" {
%attribute %array "endpoints" {
%attribute %name(Endpoint) "*"
}
}

View File

@ -17,53 +17,56 @@
* 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());
bool ClusterLinkMetricLessComparer::operator()(const ClusterLink& a, const ClusterLink& b) const
{
int metricA = a.GetMetric();
int metricB = b.GetMetric();
if (metricA < metricB)
return true;
else if (metricB > metricA)
if (!object_zone)
return false;
else
return a < b;
while (object_zone) {
if (object_zone.get() == this)
return true;
object_zone = object_zone->GetParent();
}
return false;
}
Zone::Ptr Zone::GetLocalZone(void)
{
return Endpoint::GetLocalEndpoint()->GetZone();
}

View File

@ -17,33 +17,35 @@
* 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 */

12
lib/remote/zone.ti Normal file
View File

@ -0,0 +1,12 @@
#include "base/dynamicobject.h"
namespace icinga
{
class Zone : DynamicObject
{
[config] String parent (ParentRaw);
[config] Array::Ptr endpoints (EndpointsRaw);
};
}

View File

@ -60,6 +60,8 @@ static void Callback(int *counter)
BOOST_AUTO_TEST_CASE(invoke)
{
Utility::Sleep(5);
int counter;
Timer::Ptr timer = make_shared<Timer>();
timer->OnTimerExpired.connect(boost::bind(&Callback, &counter));

View File

@ -20,5 +20,17 @@
#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);