]> granicus.if.org Git - icinga2/commitdiff
Implemented notification request messages, notifications dictionary for hosts/service...
authorGunnar Beutner <gunnar.beutner@netways.de>
Sat, 9 Feb 2013 14:20:10 +0000 (15:20 +0100)
committerGunnar Beutner <gunnar.beutner@netways.de>
Sat, 9 Feb 2013 14:20:10 +0000 (15:20 +0100)
20 files changed:
components/notification/notificationcomponent.cpp
components/notification/notificationcomponent.h
itl/types.conf
lib/config/configitembuilder.cpp
lib/icinga/Makefile.am
lib/icinga/externalcommandprocessor.cpp
lib/icinga/externalcommandprocessor.h
lib/icinga/host.cpp
lib/icinga/i2-icinga.h
lib/icinga/icinga.vcxproj
lib/icinga/notification.cpp
lib/icinga/notification.h
lib/icinga/notificationrequestmessage.cpp [moved from lib/icinga/service-notifications.cpp with 55% similarity]
lib/icinga/notificationrequestmessage.h [new file with mode: 0644]
lib/icinga/pluginnotificationtask.cpp
lib/icinga/pluginnotificationtask.h
lib/icinga/service-notification.cpp [new file with mode: 0644]
lib/icinga/service.cpp
lib/icinga/service.h
lib/remoting/endpointmanager.cpp

index 7dac914c1077438a1ea8ec0d8b0628137d0306bb..d4de1111fd9aa42abd2b7b5b9de498b546301ef8 100644 (file)
@@ -28,6 +28,11 @@ EXPORT_COMPONENT(notification, NotificationComponent);
  */
 void NotificationComponent::Start(void)
 {
+       m_Endpoint = Endpoint::MakeEndpoint("notification", false);
+       m_Endpoint->RegisterTopicHandler("icinga::SendNotifications",
+           boost::bind(&NotificationComponent::SendNotificationsRequestHandler, this, _2,
+           _3));
+
        m_NotificationTimer = boost::make_shared<Timer>();
        m_NotificationTimer->SetInterval(5);
        m_NotificationTimer->OnTimerExpired.connect(boost::bind(&NotificationComponent::NotificationTimerHandler, this));
@@ -50,3 +55,26 @@ void NotificationComponent::NotificationTimerHandler(void)
 {
        // TODO: implement
 }
+
+
+/**
+ * Processes icinga::SendNotifications messages.
+ */
+void NotificationComponent::SendNotificationsRequestHandler(const Endpoint::Ptr& sender,
+    const RequestMessage& request)
+{
+       MessagePart params;
+       if (!request.GetParams(&params))
+               return;
+
+       String svc;
+       if (!params.Get("service", &svc))
+               return;
+
+       int type;
+       if (!params.Get("type", &type))
+               return;
+
+       Service::Ptr service = Service::GetByName(svc);
+       service->SendNotifications(static_cast<NotificationType>(type));
+}
index dbb2d449c4855d12bfb86a992578412561246f0a..c40cd8ac10a3912518d7bb837cb176fc70a17b37 100644 (file)
@@ -34,8 +34,10 @@ public:
 
 private:
        Timer::Ptr m_NotificationTimer;
+       Endpoint::Ptr m_Endpoint;
 
        void NotificationTimerHandler(void);
+       void SendNotificationsRequestHandler(const Endpoint::Ptr& sender, const RequestMessage& request);
 };
 
 }
index 194ec627fa491caf332c14916e54cda903854710..fb7ad5f56b245d4ecdcbd89f3116a3d2f566c13c 100644 (file)
@@ -103,6 +103,18 @@ type Host {
                }
        },
 
+       %attribute dictionary "notifications" {
+               %attribute string "*",
+               %attribute dictionary "*" {
+                       %require "notification",
+                       %attribute string "notification",
+
+                       %attribute dictionary "macros" {
+                               %attribute string "*"
+                       }
+               }
+       },
+
        /* service attributes */
        %attribute number "max_check_attempts",
        %attribute number "check_interval",
@@ -172,6 +184,18 @@ type Service {
        %attribute dictionary "methods" {
                %require "check",
                %attribute string "check"
+       },
+
+       %attribute dictionary "notifications" {
+               %attribute string "*",
+               %attribute dictionary "*" {
+                       %require "notification",
+                       %attribute string "notification",
+
+                       %attribute dictionary "macros" {
+                               %attribute string "*"
+                       }
+               }
        }
 }
 
index dde8c8d94635541eef6777031700e0df06e93e98..c680a611630e69a2714dfe867edea5810838cbef 100644 (file)
@@ -105,6 +105,16 @@ ConfigItem::Ptr ConfigItemBuilder::Compile(void)
                BOOST_THROW_EXCEPTION(invalid_argument(msgbuf.str()));
        }
 
+       BOOST_FOREACH(const String& parent, m_Parents) {
+               ConfigItem::Ptr item = ConfigItem::GetObject(m_Type, parent);
+
+               if (!item) {
+                       stringstream msgbuf;
+                       msgbuf << "The parent config item '" + parent + "' does not exist: " << m_DebugInfo;
+                       BOOST_THROW_EXCEPTION(invalid_argument(msgbuf.str()));
+               }
+       }
+
        ExpressionList::Ptr exprl = boost::make_shared<ExpressionList>();
 
        Expression execExpr("", OperatorExecute, m_ExpressionList, m_DebugInfo);
index d0abe80035661c5c9a49821f3f1a32bcf76b91f9..31b3a51daae733e26aef63b3d41b6c11822711a4 100644 (file)
@@ -22,6 +22,8 @@ libicinga_la_SOURCES =  \
        macroprocessor.h \
        notification.cpp \
        notification.h \
+       notificationrequestmessage.cpp \
+       notificationrequestmessage.h \
        nullchecktask.cpp \
        nullchecktask.h \
        pluginchecktask.cpp \
index bf6d52a447d6f8ae5a02995ebb87636d88fa0b26..69c4a0b91606f90be9721efd346d9724f76df043 100644 (file)
@@ -99,6 +99,8 @@ void ExternalCommandProcessor::Execute(double time, const String& command, const
                RegisterCommand("DEL_SVC_COMMENT", &ExternalCommandProcessor::DelSvcComment);
                RegisterCommand("DEL_ALL_HOST_COMMENTS", &ExternalCommandProcessor::DelAllHostComments);
                RegisterCommand("DEL_ALL_SVC_COMMENTS", &ExternalCommandProcessor::DelAllSvcComments);
+               RegisterCommand("SEND_CUSTOM_HOST_NOTIFICATION", &ExternalCommandProcessor::SendCustomHostNotification);
+               RegisterCommand("SEND_CUSTOM_SVC_NOTIFICATION", &ExternalCommandProcessor::SendCustomSvcNotification);
 
                m_Initialized = true;
        }
@@ -799,3 +801,28 @@ void ExternalCommandProcessor::DelAllSvcComments(double, const vector<String>& a
        Logger::Write(LogInformation, "icinga", "Removing all comments for service " + service->GetName());
        service->RemoveAllComments();
 }
+
+void ExternalCommandProcessor::SendCustomHostNotification(double time, const vector<String>& arguments)
+{
+       if (arguments.size() < 4)
+               BOOST_THROW_EXCEPTION(invalid_argument("Expected 4 arguments."));
+
+       Host::Ptr host = Host::GetByName(arguments[0]);
+
+       Logger::Write(LogInformation, "icinga", "Sending custom notification for host " + host->GetName());
+       Service::Ptr service = host->GetHostCheckService();
+       if (service) {
+               service->RequestNotifications(NotificationCustom);
+       }
+}
+
+void ExternalCommandProcessor::SendCustomSvcNotification(double time, const vector<String>& arguments)
+{
+       if (arguments.size() < 5)
+               BOOST_THROW_EXCEPTION(invalid_argument("Expected 5 arguments."));
+
+       Service::Ptr service = Service::GetByNamePair(arguments[0], arguments[1]);
+
+       Logger::Write(LogInformation, "icinga", "Sending custom notification for service " + service->GetName());
+       service->RequestNotifications(NotificationCustom);
+}
index 49a3e5da9c9f5e3ccccce9a323e39f5a668d5221..755c36b27a9cc4be7e34ba897849fcb6e0c863f5 100644 (file)
@@ -22,7 +22,7 @@
 
 namespace icinga
 {
-       
+
 class I2_ICINGA_API ExternalCommandProcessor {
 public:
        static void Execute(const String& line);
@@ -70,6 +70,8 @@ public:
        static void DelSvcComment(double time, const vector<String>& arguments);
        static void DelAllHostComments(double time, const vector<String>& arguments);
        static void DelAllSvcComments(double time, const vector<String>& arguments);
+       static void SendCustomHostNotification(double time, const vector<String>& arguments);
+       static void SendCustomSvcNotification(double time, const vector<String>& arguments);
 
 private:
        typedef function<void (double time, const vector<String>& arguments)> Callback;
index 8f5bc0899d65a599a74a6c2bc733f354ff74015a..48a14a829179b3453c771423f09517f9bf0dc94f 100644 (file)
@@ -152,7 +152,7 @@ template<bool copyServiceAttrs, typename TDict>
 static void CopyServiceAttributes(TDict serviceDesc, const ConfigItemBuilder::Ptr& builder)
 {
        /* TODO: we only need to copy macros if this is an inline definition,
-        * i.e. host->GetProperties() != service, however for now we just
+        * i.e. "typeid(serviceDesc)" != Service, however for now we just
         * copy them anyway. */
        Value macros = serviceDesc->Get("macros");
        if (!macros.IsEmpty())
@@ -194,7 +194,7 @@ void Host::UpdateSlaveServices(void)
        if (!item || IsAbstract())
                return;
 
-       Dictionary::Ptr oldServices = Get("convenience_services");
+       Dictionary::Ptr oldServices = Get("slave_services");
 
        Dictionary::Ptr newServices;
        newServices = boost::make_shared<Dictionary>();
@@ -212,7 +212,7 @@ void Host::UpdateSlaveServices(void)
                        ConfigItemBuilder::Ptr builder = boost::make_shared<ConfigItemBuilder>(item->GetDebugInfo());
                        builder->SetType("Service");
                        builder->SetName(name);
-                       builder->AddExpression("host_name", OperatorSet, item->GetName());
+                       builder->AddExpression("host_name", OperatorSet, GetName());
                        builder->AddExpression("alias", OperatorSet, svcname);
                        builder->AddExpression("short_name", OperatorSet, svcname);
 
@@ -252,7 +252,7 @@ void Host::UpdateSlaveServices(void)
                }
        }
 
-       Set("convenience_services", newServices);
+       Set("slave_services", newServices);
 }
 
 void Host::OnAttributeChanged(const String& name, const Value&)
@@ -261,6 +261,11 @@ void Host::OnAttributeChanged(const String& name, const Value&)
                HostGroup::InvalidateMembersCache();
        else if (name == "services")
                UpdateSlaveServices();
+       else if (name == "notifications") {
+               BOOST_FOREACH(const Service::Ptr& service, GetServices()) {
+                       service->UpdateSlaveNotifications();
+               }
+       }
 }
 
 set<Service::Ptr> Host::GetServices(void) const
index 603b60fd2887908c099fff86d3b66460454c9c9d..5ca4070f62665fdb66d915d94d069a7000278c93 100644 (file)
@@ -47,6 +47,7 @@ using boost::algorithm::is_any_of;
 #include "icingaapplication.h"
 
 #include "notification.h"
+#include "notificationrequestmessage.h"
 
 #include "host.h"
 #include "hostgroup.h"
index 36f5028aa6b04792502c8515a51c23f351d0a7f2..dbb7a8fbf8e67cc30fe7fce2b8573446ce2ec835 100644 (file)
     <ClCompile Include="icingaapplication.cpp" />
     <ClCompile Include="macroprocessor.cpp" />
     <ClCompile Include="notification.cpp" />
+    <ClCompile Include="notificationrequestmessage.cpp" />
     <ClCompile Include="pluginchecktask.cpp" />
     <ClCompile Include="nullchecktask.cpp" />
     <ClCompile Include="pluginnotificationtask.cpp" />
-    <ClCompile Include="service-notifications.cpp" />
+    <ClCompile Include="service-notification.cpp" />
     <ClCompile Include="service.cpp" />
     <ClCompile Include="service-comment.cpp" />
     <ClCompile Include="service-downtime.cpp" />
@@ -52,6 +53,7 @@
     <ClInclude Include="icingaapplication.h" />
     <ClInclude Include="macroprocessor.h" />
     <ClInclude Include="notification.h" />
+    <ClInclude Include="notificationrequestmessage.h" />
     <ClInclude Include="pluginchecktask.h" />
     <ClInclude Include="nullchecktask.h" />
     <ClInclude Include="pluginnotificationtask.h" />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
index a52914a4ed1176763da812f6bce875da5f203d90..905e85723e49320363c76409b45f05e12457816d 100644 (file)
@@ -25,10 +25,14 @@ REGISTER_TYPE(Notification, NULL);
 
 Notification::Notification(const Dictionary::Ptr& properties)
        : DynamicObject(properties)
-{ }
+{
+       Service::InvalidateNotificationsCache();
+}
 
 Notification::~Notification(void)
-{ }
+{
+       Service::InvalidateNotificationsCache();
+}
 
 bool Notification::Exists(const String& name)
 {
@@ -66,13 +70,20 @@ Dictionary::Ptr Notification::GetMacros(void) const
        return Get("macros");
 }
 
-void Notification::SendNotification(void)
+void Notification::SendNotification(NotificationType type)
 {
        vector<Value> arguments;
        arguments.push_back(static_cast<Notification::Ptr>(GetSelf()));
+       arguments.push_back(type);
        ScriptTask::Ptr task;
        task = InvokeMethod("notify", arguments, boost::bind(&Notification::NotificationCompletedHandler, this, _1));
 
+       if (!task) {
+               Logger::Write(LogWarning, "icinga", "Notification object '" + GetName() + "' doesn't have a 'notify' method.");
+
+               return;
+       }
+
        if (!task->IsFinished()) {
                /* We need to keep the task object alive until the completion handler is called. */
 
index aa97d3fcffb82d0a7d455c4c8dae75767e578f43..f91ca946c819dbbfe77ecff906f246d64afb2d74 100644 (file)
@@ -30,8 +30,11 @@ namespace icinga
  */
 enum NotificationType
 {
-       NotificationHost,
-       NotificationService
+       NotificationDowntimeStart,
+       NotificationDowntimeEnd,
+       NotificationDowntimeRemoved,
+       NotificationCustom,
+       NotificationStateChange
 };
 
 class Service;
@@ -57,7 +60,7 @@ public:
        String GetNotificationCommand(void) const;
        Dictionary::Ptr GetMacros(void) const;
 
-       void SendNotification(void);
+       void SendNotification(NotificationType type);
 
 private:
        set<ScriptTask::Ptr> m_Tasks;
similarity index 55%
rename from lib/icinga/service-notifications.cpp
rename to lib/icinga/notificationrequestmessage.cpp
index 9cba9f905730bc7e27fe9fd47678c80fc324b415..dc530e3a939cf99e7b10e6c58c94a501b7b2f4cd 100644 (file)
 
 using namespace icinga;
 
-map<String, set<Notification::WeakPtr> > Service::m_NotificationsCache;
-bool Service::m_NotificationsCacheValid;
-
-void Service::SendNotifications(void) const
+String NotificationRequestMessage::GetService(void) const
 {
-       BOOST_FOREACH(const Notification::Ptr& notification, GetNotifications()) {
-               notification->SendNotification();
-       }
+       String service;
+       Get("service", &service);
+       return service;
 }
 
-void Service::InvalidateNotificationsCache(void)
+void NotificationRequestMessage::SetService(const String& service)
 {
-       m_NotificationsCacheValid = false;
-       m_NotificationsCache.clear();
+       Set("service", service);
 }
 
-void Service::ValidateNotificationsCache(void)
+NotificationType NotificationRequestMessage::GetType(void) const
 {
-       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;
+       int type;
+       Get("type", &type);
+       return static_cast<NotificationType>(type);
 }
 
-set<Notification::Ptr> Service::GetNotifications(void) const
+void NotificationRequestMessage::SetType(NotificationType type)
 {
-       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;
+       Set("type", type);
 }
diff --git a/lib/icinga/notificationrequestmessage.h b/lib/icinga/notificationrequestmessage.h
new file mode 100644 (file)
index 0000000..be4889a
--- /dev/null
@@ -0,0 +1,46 @@
+/******************************************************************************
+ * 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 NOTIFICATIONREQUESTMESSAGE_H
+#define NOTIFICATIONREQUESTMESSAGE_H
+
+namespace icinga
+{
+
+/**
+ * An API request for sending notifications.
+ *
+ * @ingroup icinga
+ */
+class I2_ICINGA_API NotificationRequestMessage : public MessagePart
+{
+public:
+       NotificationRequestMessage(void) : MessagePart() { }
+       NotificationRequestMessage(const MessagePart& message) : MessagePart(message) { }
+
+       String GetService(void) const;
+       void SetService(const String& service);
+
+       NotificationType GetType(void) const;
+       void SetType(NotificationType type);
+};
+
+}
+
+#endif /* NOTIFICATIONREQUESTMESSAGE_H */
index 1387e6696ccee0aaf785969122f761ab56d76430..8bec5e77cc3147cdd23f255de36bd83bff794fe1 100644 (file)
@@ -23,8 +23,9 @@ 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)
+PluginNotificationTask::PluginNotificationTask(const ScriptTask::Ptr& task, const Process::Ptr& process,
+    const String& service, const String& command)
+       : m_Task(task), m_Process(process), m_ServiceName(service), m_Command(command)
 { }
 
 void PluginNotificationTask::ScriptFunc(const ScriptTask::Ptr& task, const vector<Value>& arguments)
@@ -32,10 +33,14 @@ void PluginNotificationTask::ScriptFunc(const ScriptTask::Ptr& task, const vecto
        if (arguments.size() < 1)
                BOOST_THROW_EXCEPTION(invalid_argument("Missing argument: Notification target must be specified."));
 
+       if (arguments.size() < 2)
+               BOOST_THROW_EXCEPTION(invalid_argument("Missing argument: Notification type must be specified."));
+
        if (!arguments[0].IsObjectType<Notification>())
                BOOST_THROW_EXCEPTION(invalid_argument("Argument must be a service."));
 
        Notification::Ptr notification = arguments[0];
+       NotificationType type = static_cast<NotificationType>(static_cast<int>(arguments[1]));
 
        String notificationCommand = notification->GetNotificationCommand();
 
@@ -48,7 +53,7 @@ void PluginNotificationTask::ScriptFunc(const ScriptTask::Ptr& task, const vecto
 
        Process::Ptr process = boost::make_shared<Process>(command);
 
-       PluginNotificationTask ct(task, process);
+       PluginNotificationTask ct(task, process, notification->GetService()->GetName(), command);
 
        process->Start(boost::bind(&PluginNotificationTask::ProcessFinishedHandler, ct));
 }
@@ -60,8 +65,13 @@ void PluginNotificationTask::ProcessFinishedHandler(PluginNotificationTask ct)
        try {
                pr = ct.m_Process->GetResult();
 
-               if (pr.ExitStatus != 0)
-                       Logger::Write(LogWarning, "icinga", "Notification command failed; output: " + pr.Output);
+               if (pr.ExitStatus != 0) {
+                       stringstream msgbuf;
+                       msgbuf << "Notification command '" << ct.m_Command << "' for service '"
+                              << ct.m_ServiceName << "' failed; exit status: "
+                              << pr.ExitStatus << ", output: " << pr.Output;
+                       Logger::Write(LogWarning, "icinga", msgbuf.str());
+               }
 
                ct.m_Task->FinishResult(Empty);
        } catch (...) {
index 11b4284dfe8889fb172a14febe93b367cf725f2c..bf39f12c7e8979754ac2cb4c69f107351aee093f 100644 (file)
@@ -37,10 +37,14 @@ private:
 
        static void ProcessFinishedHandler(PluginNotificationTask ct);
 
-       PluginNotificationTask(const ScriptTask::Ptr& task, const Process::Ptr& process);
+       PluginNotificationTask(const ScriptTask::Ptr& task, const Process::Ptr& process,
+           const String& service, const String& command);
 
        ScriptTask::Ptr m_Task;
        Process::Ptr m_Process;
+
+       String m_ServiceName;
+       String m_Command;
 };
 
 }
diff --git a/lib/icinga/service-notification.cpp b/lib/icinga/service-notification.cpp
new file mode 100644 (file)
index 0000000..fb7ab4f
--- /dev/null
@@ -0,0 +1,184 @@
+/******************************************************************************
+ * 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::RequestNotifications(NotificationType type) const
+{
+       RequestMessage msg;
+       msg.SetMethod("icinga::SendNotifications");
+
+       NotificationRequestMessage params;
+       msg.SetParams(params);
+
+       params.SetService(GetName());
+       params.SetType(type);
+
+       Logger::Write(LogInformation, "icinga", "Sending notification anycast request for service '" + GetName() + "'");
+       EndpointManager::GetInstance()->SendAnycastMessage(Endpoint::Ptr(), msg);
+}
+
+void Service::SendNotifications(NotificationType type) const
+{
+       Logger::Write(LogInformation, "icinga", "Sending notifications for service '" + GetName() + "'");
+
+       set<Notification::Ptr> notifications = GetNotifications();
+
+       if (notifications.size() == 0)
+               Logger::Write(LogInformation, "icinga", "Service '" + GetName() + "' does not have any notifications.");
+
+       BOOST_FOREACH(const Notification::Ptr& notification, notifications) {
+               notification->SendNotification(type);
+       }
+}
+
+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;
+}
+
+template<typename TDict>
+static void CopyNotificationAttributes(TDict notificationDesc, const ConfigItemBuilder::Ptr& builder)
+{
+       /* TODO: we only need to copy macros if this is an inline definition,
+        * i.e. "typeid(notificationDesc)" != Notification, however for now we just
+        * copy them anyway. */
+       Value macros = notificationDesc->Get("macros");
+       if (!macros.IsEmpty())
+               builder->AddExpression("macros", OperatorPlus, macros);
+
+       /*Value notificationInterval = notificationDesc->Get("notification_interval");
+       if (!notificationInterval.IsEmpty())
+               builder->AddExpression("notification_interval", OperatorSet, notificationInterval);*/
+}
+
+void Service::UpdateSlaveNotifications(void)
+{
+       ConfigItem::Ptr item = ConfigItem::GetObject("Service", GetName());
+
+       /* Don't create slave notifications unless we own this object
+        * and it's not a template. */
+       if (!item || IsAbstract())
+               return;
+
+       Dictionary::Ptr oldNotifications = Get("slave_notifications");
+
+       Dictionary::Ptr newNotifications;
+       newNotifications = boost::make_shared<Dictionary>();
+
+       vector<Dictionary::Ptr> notificationDescsList;
+       notificationDescsList.push_back(GetHost()->Get("notifications"));
+       notificationDescsList.push_back(Get("notifications"));
+
+       BOOST_FOREACH(const Dictionary::Ptr& notificationDescs, notificationDescsList) {
+               if (!notificationDescs)
+                       continue;
+
+               String nfcname;
+               Value nfcdesc;
+               BOOST_FOREACH(tie(nfcname, nfcdesc), notificationDescs) {
+                       stringstream namebuf;
+                       namebuf << GetName() << "-" << nfcname;
+                       String name = namebuf.str();
+
+                       ConfigItemBuilder::Ptr builder = boost::make_shared<ConfigItemBuilder>(item->GetDebugInfo());
+                       builder->SetType("Notification");
+                       builder->SetName(name);
+                       builder->AddExpression("host_name", OperatorSet, GetHost()->GetName());
+                       builder->AddExpression("service", OperatorSet, GetName());
+
+                       CopyNotificationAttributes(this, builder);
+
+                       if (nfcdesc.IsScalar()) {
+                               builder->AddParent(nfcdesc);
+                       } else if (nfcdesc.IsObjectType<Dictionary>()) {
+                               Dictionary::Ptr notification = nfcdesc;
+
+                               String parent = notification->Get("notification");
+                               if (parent.IsEmpty())
+                                       parent = nfcname;
+
+                               builder->AddParent(parent);
+
+                               CopyNotificationAttributes(notification, builder);
+                       } else {
+                               BOOST_THROW_EXCEPTION(invalid_argument("Notification description must be either a string or a dictionary."));
+                       }
+
+                       ConfigItem::Ptr notificationItem = builder->Compile();
+                       notificationItem->Commit();
+
+                       newNotifications->Set(name, notificationItem);
+               }
+       }
+
+       if (oldNotifications) {
+               ConfigItem::Ptr notification;
+               BOOST_FOREACH(tie(tuples::ignore, notification), oldNotifications) {
+                       if (!notification)
+                               continue;
+
+                       if (!newNotifications->Contains(notification->GetName()))
+                               notification->Unregister();
+               }
+       }
+
+       Set("slave_notifications", newNotifications);
+}
index 88f5e2df3aa858ee3efd9ac3944f401fd39e32bb..a97a18442acc77e4da2febea8c1045cda6c6ea7e 100644 (file)
@@ -529,17 +529,11 @@ void Service::ApplyCheckResult(const Dictionary::Ptr& cr)
 
        SetLastCheckResult(cr);
 
-       // TODO(debug): remove this
-       SendNotifications();
+       double now = Utility::GetTime();
 
        if (old_state != GetState()) {
-               double now = Utility::GetTime();
-
                SetLastStateChange(now);
 
-               if (old_stateType != GetStateType())
-                       SetLastHardStateChange(now);
-
                /* remove acknowledgements */
                if (GetAcknowledgement() == AcknowledgementNormal ||
                    (GetAcknowledgement() == AcknowledgementSticky && GetStateType() == StateTypeHard && GetState() == StateOK)) {
@@ -560,11 +554,19 @@ void Service::ApplyCheckResult(const Dictionary::Ptr& cr)
                                service->SetNextCheck(Utility::GetTime());
                }
 
-               // TODO: notify our child services/hosts that our state has changed
+               if (GetState() != StateOK)
+                       TriggerDowntimes();
        }
 
-       if (GetState() != StateOK)
-               TriggerDowntimes();
+       if (GetStateType() == StateTypeHard && (old_state != GetState() || old_stateType == StateTypeSoft)) {
+               SetLastHardStateChange(now);
+
+               /* Make sure the notification component sees the updated
+                * state/state_type attributes. */
+               DynamicObject::FlushTx();
+
+               RequestNotifications(NotificationStateChange);
+       }
 }
 
 ServiceState Service::StateFromString(const String& state)
@@ -645,7 +647,7 @@ void Service::OnAttributeChanged(const String& name, const Value& oldValue)
        else if (name == "comments")
                Service::InvalidateCommentCache();
        else if (name == "notifications")
-               Service::InvalidateNotificationsCache();
+               UpdateSlaveNotifications();
 }
 
 void Service::BeginExecuteCheck(const function<void (void)>& callback)
index 0d3cf176fc4f673ba62a679e245ac04bd544fa36..e136a26a14318f6d05a4c41614891f5d97ac24c2 100644 (file)
@@ -223,13 +223,16 @@ public:
        static void ValidateCommentCache(void);
 
        /* Notifications */
-       void SendNotifications(void) const;
+       void RequestNotifications(NotificationType type) const;
+       void SendNotifications(NotificationType type) const;
 
        static void InvalidateNotificationsCache(void);
        static void ValidateNotificationsCache(void);
 
        set<Notification::Ptr> GetNotifications(void) const;
 
+       void UpdateSlaveNotifications(void);
+
 protected:
        virtual void OnAttributeChanged(const String& name, const Value& oldValue);
 
index ebb90c795914528d690c5fc62330d849e89ccda8..9654ccbd1e8378e6622c5f2d4a8efd8269f5607b 100644 (file)
@@ -236,7 +236,7 @@ void EndpointManager::SendAnycastMessage(const Endpoint::Ptr& sender,
        BOOST_FOREACH(tie(tuples::ignore, object), DynamicType::GetByName("Endpoint")->GetObjects()) {
                Endpoint::Ptr endpoint = dynamic_pointer_cast<Endpoint>(object);
                /* don't forward messages between non-local endpoints */
-               if (!sender->IsLocal() && !endpoint->IsLocal())
+               if ((sender && !sender->IsLocal()) && !endpoint->IsLocal())
                        continue;
 
                if (endpoint->HasSubscription(method))