Implemented rudimentary notifications.

This commit is contained in:
Gunnar Beutner 2013-02-09 11:42:22 +01:00
parent 3ab071fbcb
commit 3e7376576e
15 changed files with 438 additions and 9 deletions

View File

@ -189,3 +189,21 @@ type Endpoint {
type TimePeriod {
}
type Notification {
%require "methods",
%attribute dictionary "methods" {
%require "notify",
%attribute string "notify"
},
%require "host_name",
%attribute string "host_name",
%attribute string "service",
%attribute dictionary "macros" {
%attribute string "*"
},
%attribute string "notification_command"
}

View File

@ -20,13 +20,18 @@ libicinga_la_SOURCES = \
icingaapplication.h \
macroprocessor.cpp \
macroprocessor.h \
notification.cpp \
notification.h \
nullchecktask.cpp \
nullchecktask.h \
pluginchecktask.cpp \
pluginchecktask.h \
pluginnotificationtask.cpp \
pluginnotificationtask.h \
service.cpp \
service-comment.cpp \
service-downtime.cpp \
service-notification.cpp \
service.h \
servicegroup.cpp \
servicegroup.h

View File

@ -395,7 +395,12 @@ Service::Ptr Host::GetHostCheckService(void) const
if (hostcheck.IsEmpty())
return Service::Ptr();
return GetServiceByShortName(hostcheck);
Service::Ptr service = GetServiceByShortName(hostcheck);
if (service->GetHost()->GetName() != GetName())
BOOST_THROW_EXCEPTION(runtime_error("Hostcheck service refers to another host's service."));
return service;
}
set<Service::Ptr> Host::GetParentServices(void) const

View File

@ -46,6 +46,8 @@ using boost::algorithm::is_any_of;
#include "endpointmanager.h"
#include "icingaapplication.h"
#include "notification.h"
#include "host.h"
#include "hostgroup.h"
#include "service.h"
@ -55,6 +57,8 @@ using boost::algorithm::is_any_of;
#include "pluginchecktask.h"
#include "nullchecktask.h"
#include "pluginnotificationtask.h"
#include "checkresultmessage.h"
#include "cib.h"

View File

@ -32,8 +32,11 @@
</ClCompile>
<ClCompile Include="icingaapplication.cpp" />
<ClCompile Include="macroprocessor.cpp" />
<ClCompile Include="notification.cpp" />
<ClCompile Include="pluginchecktask.cpp" />
<ClCompile Include="nullchecktask.cpp" />
<ClCompile Include="pluginnotificationtask.cpp" />
<ClCompile Include="service-notifications.cpp" />
<ClCompile Include="service.cpp" />
<ClCompile Include="service-comment.cpp" />
<ClCompile Include="service-downtime.cpp" />
@ -48,8 +51,10 @@
<ClInclude Include="i2-icinga.h" />
<ClInclude Include="icingaapplication.h" />
<ClInclude Include="macroprocessor.h" />
<ClInclude Include="notification.h" />
<ClInclude Include="pluginchecktask.h" />
<ClInclude Include="nullchecktask.h" />
<ClInclude Include="pluginnotificationtask.h" />
<ClInclude Include="service.h" />
<ClInclude Include="servicegroup.h" />
</ItemGroup>
@ -203,4 +208,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -43,6 +43,15 @@
<ClCompile Include="service-downtime.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="pluginnotificationtask.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="notification.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="service-notifications.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="i2-icinga.h">
@ -81,6 +90,12 @@
<ClInclude Include="checkresultmessage.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="pluginnotificationtask.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="notification.h">
<Filter>Headerdateien</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Headerdateien">

View File

@ -0,0 +1,99 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012 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 "i2-icinga.h"
using namespace icinga;
REGISTER_TYPE(Notification, NULL);
Notification::Notification(const Dictionary::Ptr& properties)
: DynamicObject(properties)
{ }
Notification::~Notification(void)
{ }
bool Notification::Exists(const String& name)
{
return (DynamicObject::GetObject("Notification", name));
}
Notification::Ptr Notification::GetByName(const String& name)
{
DynamicObject::Ptr configObject = DynamicObject::GetObject("Notification", name);
if (!configObject)
BOOST_THROW_EXCEPTION(invalid_argument("Notification '" + name + "' does not exist."));
return dynamic_pointer_cast<Notification>(configObject);
}
Service::Ptr Notification::GetService(void) const
{
Host::Ptr host = Host::GetByName(Get("host_name"));
String service = Get("service");
if (service.IsEmpty())
return host->GetHostCheckService();
else
return host->GetServiceByShortName(service);
}
String Notification::GetNotificationCommand(void) const
{
return Get("notification_command");
}
Dictionary::Ptr Notification::GetMacros(void) const
{
return Get("macros");
}
void Notification::SendNotification(void)
{
vector<Value> arguments;
arguments.push_back(static_cast<Notification::Ptr>(GetSelf()));
ScriptTask::Ptr task;
task = InvokeMethod("notify", arguments, boost::bind(&Notification::NotificationCompletedHandler, this, _1));
if (!task->IsFinished()) {
/* We need to keep the task object alive until the completion handler is called. */
m_Tasks.insert(task);
}
}
void Notification::NotificationCompletedHandler(const ScriptTask::Ptr& task)
{
m_Tasks.erase(task);
try {
(void) task->GetResult();
Logger::Write(LogInformation, "icinga", "Completed sending notification for service '" + GetService()->GetName() + "'");
} catch (const exception& ex) {
stringstream msgbuf;
msgbuf << "Exception occured during notification for service '"
<< GetService()->GetName() << "': " << diagnostic_information(ex);
String message = msgbuf.str();
Logger::Write(LogWarning, "icinga", message);
}
}

70
lib/icinga/notification.h Normal file
View File

@ -0,0 +1,70 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012 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 NOTIFICATION_H
#define NOTIFICATION_H
namespace icinga
{
/**
* The notification type.
*
* @ingroup icinga
*/
enum NotificationType
{
NotificationHost,
NotificationService
};
class Service;
/**
* An Icinga notification specification.
*
* @ingroup icinga
*/
class I2_ICINGA_API Notification : public DynamicObject
{
public:
typedef shared_ptr<Notification> Ptr;
typedef weak_ptr<Notification> WeakPtr;
Notification(const Dictionary::Ptr& properties);
~Notification(void);
static bool Exists(const String& name);
static Notification::Ptr GetByName(const String& name);
shared_ptr<Service> GetService(void) const;
String GetNotificationCommand(void) const;
Dictionary::Ptr GetMacros(void) const;
void SendNotification(void);
private:
set<ScriptTask::Ptr> m_Tasks;
void NotificationCompletedHandler(const ScriptTask::Ptr& task);
};
}
#endif /* NOTIFICATION_H */

View File

@ -33,10 +33,10 @@ void PluginCheckTask::ScriptFunc(const ScriptTask::Ptr& task, const vector<Value
BOOST_THROW_EXCEPTION(invalid_argument("Missing argument: Service must be specified."));
Value vservice = arguments[0];
if (!vservice.IsObjectType<DynamicObject>())
BOOST_THROW_EXCEPTION(invalid_argument("Argument must be a config object."));
if (!vservice.IsObjectType<Service>())
BOOST_THROW_EXCEPTION(invalid_argument("Argument must be a service."));
Service::Ptr service = static_cast<Service::Ptr>(vservice);
Service::Ptr service = vservice;
String checkCommand = service->GetCheckCommand();

View File

@ -37,7 +37,6 @@ public:
static Dictionary::Ptr ParseCheckOutput(const String& output);
private:
static void ProcessFinishedHandler(PluginCheckTask ct);
PluginCheckTask(const ScriptTask::Ptr& task, const Process::Ptr& process);

View File

@ -0,0 +1,71 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012 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 "i2-icinga.h"
using namespace icinga;
REGISTER_SCRIPTFUNCTION("native::PluginNotification", &PluginNotificationTask::ScriptFunc);
PluginNotificationTask::PluginNotificationTask(const ScriptTask::Ptr& task, const Process::Ptr& process)
: m_Task(task), m_Process(process)
{ }
void PluginNotificationTask::ScriptFunc(const ScriptTask::Ptr& task, const vector<Value>& arguments)
{
if (arguments.size() < 1)
BOOST_THROW_EXCEPTION(invalid_argument("Missing argument: Notification target must be specified."));
if (!arguments[0].IsObjectType<Notification>())
BOOST_THROW_EXCEPTION(invalid_argument("Argument must be a service."));
Notification::Ptr notification = arguments[0];
String notificationCommand = notification->GetNotificationCommand();
vector<Dictionary::Ptr> macroDicts;
macroDicts.push_back(notification->GetMacros());
macroDicts.push_back(notification->GetService()->GetMacros());
macroDicts.push_back(notification->GetService()->GetHost()->GetMacros());
macroDicts.push_back(IcingaApplication::GetInstance()->GetMacros());
String command = MacroProcessor::ResolveMacros(notificationCommand, macroDicts);
Process::Ptr process = boost::make_shared<Process>(command);
PluginNotificationTask ct(task, process);
process->Start(boost::bind(&PluginNotificationTask::ProcessFinishedHandler, ct));
}
void PluginNotificationTask::ProcessFinishedHandler(PluginNotificationTask ct)
{
ProcessResult pr;
try {
pr = ct.m_Process->GetResult();
if (pr.ExitStatus != 0)
Logger::Write(LogWarning, "icinga", "Notification command failed; output: " + pr.Output);
ct.m_Task->FinishResult(Empty);
} catch (...) {
ct.m_Task->FinishException(boost::current_exception());
return;
}
}

View File

@ -0,0 +1,48 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012 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 PLUGINNOTIFICATIONTASK_H
#define PLUGINNOTIFICATIONTASK_H
namespace icinga
{
/**
* Implements sending notifications based on external plugins.
*
* @ingroup icinga
*/
class I2_ICINGA_API PluginNotificationTask
{
public:
static void ScriptFunc(const ScriptTask::Ptr& task, const vector<Value>& arguments);
private:
static void ProcessFinishedHandler(PluginNotificationTask ct);
PluginNotificationTask(const ScriptTask::Ptr& task, const Process::Ptr& process);
ScriptTask::Ptr m_Task;
Process::Ptr m_Process;
};
}
#endif /* PLUGINNOTIFICATIONTASK_H */

View File

@ -0,0 +1,73 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012 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 "i2-icinga.h"
using namespace icinga;
map<String, set<Notification::WeakPtr> > Service::m_NotificationsCache;
bool Service::m_NotificationsCacheValid;
void Service::SendNotifications(void) const
{
BOOST_FOREACH(const Notification::Ptr& notification, GetNotifications()) {
notification->SendNotification();
}
}
void Service::InvalidateNotificationsCache(void)
{
m_NotificationsCacheValid = false;
m_NotificationsCache.clear();
}
void Service::ValidateNotificationsCache(void)
{
if (m_NotificationsCacheValid)
return;
m_NotificationsCache.clear();
DynamicObject::Ptr object;
BOOST_FOREACH(tie(tuples::ignore, object), DynamicType::GetByName("Notification")->GetObjects()) {
const Notification::Ptr& notification = static_pointer_cast<Notification>(object);
m_NotificationsCache[notification->GetService()->GetName()].insert(notification);
}
m_NotificationsCacheValid = true;
}
set<Notification::Ptr> Service::GetNotifications(void) const
{
set<Notification::Ptr> notifications;
ValidateNotificationsCache();
BOOST_FOREACH(const Notification::WeakPtr& wservice, m_NotificationsCache[GetName()]) {
Notification::Ptr notification = wservice.lock();
if (!notification)
continue;
notifications.insert(notification);
}
return notifications;
}

View File

@ -529,6 +529,9 @@ void Service::ApplyCheckResult(const Dictionary::Ptr& cr)
SetLastCheckResult(cr);
// TODO(debug): remove this
SendNotifications();
if (old_state != GetState()) {
double now = Utility::GetTime();
@ -641,6 +644,8 @@ void Service::OnAttributeChanged(const String& name, const Value& oldValue)
Service::InvalidateDowntimeCache();
else if (name == "comments")
Service::InvalidateCommentCache();
else if (name == "notifications")
Service::InvalidateNotificationsCache();
}
void Service::BeginExecuteCheck(const function<void (void)>& callback)
@ -699,7 +704,7 @@ void Service::CheckCompletedHandler(const Dictionary::Ptr& scheduleInfo,
<< GetName() << "': " << diagnostic_information(ex);
String message = msgbuf.str();
Logger::Write(LogWarning, "checker", message);
Logger::Write(LogWarning, "icinga", message);
result = boost::make_shared<Dictionary>();
result->Set("state", StateUnknown);

View File

@ -222,10 +222,21 @@ public:
static void InvalidateCommentCache(void);
static void ValidateCommentCache(void);
/* Notifications */
void SendNotifications(void) const;
static void InvalidateNotificationsCache(void);
static void ValidateNotificationsCache(void);
set<Notification::Ptr> GetNotifications(void) const;
protected:
virtual void OnAttributeChanged(const String& name, const Value& oldValue);
private:
void CheckCompletedHandler(const Dictionary::Ptr& scheduleInfo,
const ScriptTask::Ptr& task, const function<void (void)>& callback);
/* Downtimes */
static int m_NextDowntimeID;
@ -252,8 +263,9 @@ private:
void AddCommentsToCache(void);
void RemoveExpiredComments(void);
void CheckCompletedHandler(const Dictionary::Ptr& scheduleInfo,
const ScriptTask::Ptr& task, const function<void (void)>& callback);
/* Notifications */
static map<String, set<Notification::WeakPtr> > m_NotificationsCache;
static bool m_NotificationsCacheValid;
};
}