diff --git a/itl/types.conf b/itl/types.conf index 343e79aec..194ec627f 100644 --- a/itl/types.conf +++ b/itl/types.conf @@ -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" +} diff --git a/lib/icinga/Makefile.am b/lib/icinga/Makefile.am index 4d7d8d015..d0abe8003 100644 --- a/lib/icinga/Makefile.am +++ b/lib/icinga/Makefile.am @@ -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 diff --git a/lib/icinga/host.cpp b/lib/icinga/host.cpp index f265283d6..8f5bc0899 100644 --- a/lib/icinga/host.cpp +++ b/lib/icinga/host.cpp @@ -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 Host::GetParentServices(void) const diff --git a/lib/icinga/i2-icinga.h b/lib/icinga/i2-icinga.h index 914217090..603b60fd2 100644 --- a/lib/icinga/i2-icinga.h +++ b/lib/icinga/i2-icinga.h @@ -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" diff --git a/lib/icinga/icinga.vcxproj b/lib/icinga/icinga.vcxproj index 1d625a71e..36f5028aa 100644 --- a/lib/icinga/icinga.vcxproj +++ b/lib/icinga/icinga.vcxproj @@ -32,8 +32,11 @@ + + + @@ -48,8 +51,10 @@ + + @@ -203,4 +208,4 @@ - + \ No newline at end of file diff --git a/lib/icinga/icinga.vcxproj.filters b/lib/icinga/icinga.vcxproj.filters index c1744f395..636ee0c21 100644 --- a/lib/icinga/icinga.vcxproj.filters +++ b/lib/icinga/icinga.vcxproj.filters @@ -43,6 +43,15 @@ Quelldateien + + Quelldateien + + + Quelldateien + + + Quelldateien + @@ -81,6 +90,12 @@ Headerdateien + + Headerdateien + + + Headerdateien + diff --git a/lib/icinga/notification.cpp b/lib/icinga/notification.cpp new file mode 100644 index 000000000..a52914a4e --- /dev/null +++ b/lib/icinga/notification.cpp @@ -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(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 arguments; + arguments.push_back(static_cast(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); + } +} diff --git a/lib/icinga/notification.h b/lib/icinga/notification.h new file mode 100644 index 000000000..aa97d3fcf --- /dev/null +++ b/lib/icinga/notification.h @@ -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 Ptr; + typedef weak_ptr WeakPtr; + + Notification(const Dictionary::Ptr& properties); + ~Notification(void); + + static bool Exists(const String& name); + static Notification::Ptr GetByName(const String& name); + + shared_ptr GetService(void) const; + String GetNotificationCommand(void) const; + Dictionary::Ptr GetMacros(void) const; + + void SendNotification(void); + +private: + set m_Tasks; + + void NotificationCompletedHandler(const ScriptTask::Ptr& task); +}; + +} + +#endif /* NOTIFICATION_H */ diff --git a/lib/icinga/pluginchecktask.cpp b/lib/icinga/pluginchecktask.cpp index 28a362ee4..bf9188195 100644 --- a/lib/icinga/pluginchecktask.cpp +++ b/lib/icinga/pluginchecktask.cpp @@ -33,10 +33,10 @@ void PluginCheckTask::ScriptFunc(const ScriptTask::Ptr& task, const vector()) - BOOST_THROW_EXCEPTION(invalid_argument("Argument must be a config object.")); + if (!vservice.IsObjectType()) + BOOST_THROW_EXCEPTION(invalid_argument("Argument must be a service.")); - Service::Ptr service = static_cast(vservice); + Service::Ptr service = vservice; String checkCommand = service->GetCheckCommand(); diff --git a/lib/icinga/pluginchecktask.h b/lib/icinga/pluginchecktask.h index 520df9eec..e282e27f3 100644 --- a/lib/icinga/pluginchecktask.h +++ b/lib/icinga/pluginchecktask.h @@ -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); diff --git a/lib/icinga/pluginnotificationtask.cpp b/lib/icinga/pluginnotificationtask.cpp new file mode 100644 index 000000000..1387e6696 --- /dev/null +++ b/lib/icinga/pluginnotificationtask.cpp @@ -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& arguments) +{ + if (arguments.size() < 1) + BOOST_THROW_EXCEPTION(invalid_argument("Missing argument: Notification target must be specified.")); + + if (!arguments[0].IsObjectType()) + BOOST_THROW_EXCEPTION(invalid_argument("Argument must be a service.")); + + Notification::Ptr notification = arguments[0]; + + String notificationCommand = notification->GetNotificationCommand(); + + vector 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(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; + } +} diff --git a/lib/icinga/pluginnotificationtask.h b/lib/icinga/pluginnotificationtask.h new file mode 100644 index 000000000..11b4284df --- /dev/null +++ b/lib/icinga/pluginnotificationtask.h @@ -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& 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 */ diff --git a/lib/icinga/service-notifications.cpp b/lib/icinga/service-notifications.cpp new file mode 100644 index 000000000..9cba9f905 --- /dev/null +++ b/lib/icinga/service-notifications.cpp @@ -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 > 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(object); + + m_NotificationsCache[notification->GetService()->GetName()].insert(notification); + } + + m_NotificationsCacheValid = true; +} + +set Service::GetNotifications(void) const +{ + set notifications; + + ValidateNotificationsCache(); + + BOOST_FOREACH(const Notification::WeakPtr& wservice, m_NotificationsCache[GetName()]) { + Notification::Ptr notification = wservice.lock(); + + if (!notification) + continue; + + notifications.insert(notification); + } + + return notifications; +} diff --git a/lib/icinga/service.cpp b/lib/icinga/service.cpp index 46f53286c..88f5e2df3 100644 --- a/lib/icinga/service.cpp +++ b/lib/icinga/service.cpp @@ -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& 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(); result->Set("state", StateUnknown); diff --git a/lib/icinga/service.h b/lib/icinga/service.h index fee3c75e7..0d3cf176f 100644 --- a/lib/icinga/service.h +++ b/lib/icinga/service.h @@ -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 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& 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& callback); + /* Notifications */ + static map > m_NotificationsCache; + static bool m_NotificationsCacheValid; }; }