]> granicus.if.org Git - icinga2/commitdiff
Implement scheduled downtimes.
authorGunnar Beutner <gunnar.beutner@netways.de>
Wed, 13 Nov 2013 13:56:31 +0000 (14:56 +0100)
committerGunnar Beutner <gunnar.beutner@netways.de>
Fri, 6 Dec 2013 12:45:18 +0000 (13:45 +0100)
Fixes #3584

25 files changed:
components/cluster/clusterlistener.cpp
doc/4.3-object-types.md
etc/CMakeLists.txt
etc/icinga2/conf.d/downtimes.conf [new file with mode: 0644]
etc/icinga2/conf.d/localhost.conf
lib/base/dynamicobject.cpp
lib/base/utility.cpp
lib/base/utility.h
lib/config/configitem.cpp
lib/db_ido/CMakeLists.txt
lib/db_ido/timeperioddbobject.cpp
lib/icinga/CMakeLists.txt
lib/icinga/downtime.ti
lib/icinga/icinga-type.conf
lib/icinga/legacytimeperiod.cpp [moved from lib/methods/legacytimeperiod.cpp with 87% similarity]
lib/icinga/legacytimeperiod.h [moved from lib/methods/legacytimeperiod.h with 93% similarity]
lib/icinga/scheduleddowntime.cpp [new file with mode: 0644]
lib/icinga/scheduleddowntime.h [new file with mode: 0644]
lib/icinga/scheduleddowntime.ti [new file with mode: 0644]
lib/icinga/service-comment.cpp
lib/icinga/service-downtime.cpp
lib/icinga/service.cpp
lib/icinga/service.h
lib/icinga/service.ti
lib/methods/CMakeLists.txt

index 5cf0b456ae44cb52344fe633d42b47d32b138800..a56ff286cb037998f88e35e23a4b371a192b4043 100644 (file)
@@ -1272,7 +1272,8 @@ void ClusterListener::MessageHandler(const Endpoint::Ptr& sender, const Dictiona
                service->AddDowntime(downtime->GetAuthor(), downtime->GetComment(),
                    downtime->GetStartTime(), downtime->GetEndTime(),
                    downtime->GetFixed(), downtime->GetTriggeredBy(),
-                   downtime->GetDuration(), downtime->GetId(), sender->GetName());
+                   downtime->GetDuration(), downtime->GetScheduledBy(),
+                   downtime->GetId(), sender->GetName());
 
                AsyncRelayMessage(sender, message, true);
        } else if (message->Get("method") == "cluster::RemoveDowntime") {
index cc9641d2dd110f2574554bb0f700f72e5b71bef9..9754735ace350d0873ce82756d70433849daa07c 100644 (file)
@@ -311,6 +311,44 @@ Attributes:
 The `/etc/icinga2/conf.d/timeperiods.conf` file is usually used to define
 timeperiods including this one.
 
+### <a id="objecttype-scheduleddowntime"></a> ScheduledDowntime
+
+ScheduledDowntime objects can be used to set up recurring downtimes for services.
+
+> **Best Practice**
+>
+> Rather than creating a `ScheduledDowntime` object for a specific service it is usually easier
+> to just create a `ScheduledDowntime` template and using the `scheduled_downtimes` attribute in the `Service`
+> object to associate these templates with a service.
+
+Example:
+
+    object ScheduledDowntime "some-downtime" {
+      host = "localhost",
+      service = "ping4",
+
+      author = "icingaadmin",
+      comment = "Some comment",
+
+      fixed = false,
+      duration = 30m,
+
+      ranges = {
+        "sunday" = "02:00-03:00"
+      }
+    }
+
+Attributes:
+
+  Name            |Description
+  ----------------|----------------
+  host            |**Required.** The name of the host this notification belongs to.
+  service         |**Required.** The short name of the service this notification belongs to.
+  author          |**Required.** The author of the downtime.
+  comment         |**Required.** A comment for the downtime.
+  fixed           |**Optional.** Whether this is a fixed downtime. Defaults to true.
+  duration        |**Optional.** How long the downtime lasts. Only has an effect for flexible (non-fixed) downtimes.
+  ranges          |**Required.** A dictionary containing information which days and durations apply to this timeperiod.
 
 ### <a id="objecttype-consolelogger"></a> ConsoleLogger
 
index 99a3bba6230d39a8408eac8d6feedc978744a7f1..11427fd246aeed4fa04b45803b1e439351f28240 100644 (file)
@@ -20,6 +20,7 @@ include(InstallConfig)
 configure_file(icinga/icinga-classic-apache.conf.cmake ${CMAKE_CURRENT_BINARY_DIR}/icinga/icinga-classic-apache.conf)
 
 install_if_not_exists(icinga2/icinga2.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2)
+install_if_not_exists(icinga2/conf.d/downtimes.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
 install_if_not_exists(icinga2/conf.d/generic-host.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
 install_if_not_exists(icinga2/conf.d/generic-service.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
 install_if_not_exists(icinga2/conf.d/generic-user.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
diff --git a/etc/icinga2/conf.d/downtimes.conf b/etc/icinga2/conf.d/downtimes.conf
new file mode 100644 (file)
index 0000000..f06abc4
--- /dev/null
@@ -0,0 +1,19 @@
+/**
+ * The example downtime templates.
+ */
+
+template ScheduledDowntime "backup-downtime" {
+  author = "icingaadmin",
+  comment = "Scheduled downtime for backup",
+
+  ranges = {
+    monday = "02:00-03:00",
+    tuesday = "02:00-03:00",
+    wednesday = "02:00-03:00",
+    thursday = "02:00-03:00",
+    friday = "02:00-03:00",
+    saturday = "02:00-03:00",
+    sunday = "02:00-03:00"
+  }
+}
+
index a031059571f6b7c8ff3cd1cd500bcf8c9f5db548..807011582471c957f4f18e1f23dc336e47916d1e 100644 (file)
@@ -39,7 +39,11 @@ object Host "localhost" inherits "linux-server" {
   services["load"] = {
     templates = [ "generic-service" ],
 
-    check_command = "load"
+    check_command = "load",
+
+    scheduled_downtimes["backup"] = {
+      templates = [ "backup-downtime" ]
+    }
   },
 
   services["processes"] = {
index 4982f92487f8de2886979042e0b0ed8a51e0e123..5f637ece9c2a2aa1a05cc44c7c65a89e74bd147a 100644 (file)
@@ -278,7 +278,9 @@ void DynamicObject::RestoreObjects(const String& filename, int attributeTypes)
 
                if (object) {
                        ASSERT(!object->IsActive());
+#ifdef _DEBUG
                        Log(LogDebug, "base", "Restoring object '" + name + "' of type '" + type + "'.");
+#endif /* _DEBUG */
                        Deserialize(object, update, attributeTypes);
                        object->OnStateLoaded();
                }
index 0c772c0d07de0d03a1100e66ef1c341daabdf5b7..6e8bdd1f3ffdac347003bfdabc79c1204ff74e50 100644 (file)
@@ -744,3 +744,28 @@ int Utility::Random(void)
 
        return rand();
 }
+
+tm Utility::LocalTime(time_t ts)
+{
+#ifdef _MSC_VER
+       tm *result = localtime(&ts);
+
+       if (temp == NULL) {
+               BOOST_THROW_EXCEPTION(posix_error()
+                   << boost::errinfo_api_function("localtime")
+                   << boost::errinfo_errno(errno));
+       }
+
+       return *result;
+#else /* _MSC_VER */
+       tm result;
+
+       if (localtime_r(&ts, &result) == NULL) {
+               BOOST_THROW_EXCEPTION(posix_error()
+                   << boost::errinfo_api_function("localtime_r")
+                   << boost::errinfo_errno(errno));
+       }
+
+       return result;
+#endif /* _MSC_VER */
+}
\ No newline at end of file
index ba070a6af425c3b6dbe7d8bb2de602bbfd56dba5..07e56edec06d0522167a00dccf7ecf268c28c8e8 100644 (file)
@@ -108,6 +108,8 @@ public:
 
        static int Random(void);
 
+       static tm LocalTime(time_t ts);
+
 private:
        Utility(void);
 
index b7287331b4447eb2d94243e8dbfc902f6bccaf83..26720749de88ce659b8536c0964ae67971d24efb 100644 (file)
@@ -157,7 +157,9 @@ DynamicObject::Ptr ConfigItem::Commit(void)
 {
        ASSERT(!OwnsLock());
 
+#ifdef _DEBUG
        Log(LogDebug, "base", "Commit called for ConfigItem Type=" + GetType() + ", Name=" + GetName());
+#endif /* _DEBUG */
 
        /* Make sure the type is valid. */
        DynamicType::Ptr dtype = DynamicType::GetByName(GetType());
@@ -284,7 +286,9 @@ bool ConfigItem::ActivateItems(bool validateOnly)
                        if (object->IsActive())
                                continue;
 
+#ifdef _DEBUG
                        Log(LogDebug, "config", "Activating object '" + object->GetName() + "' of type '" + object->GetType()->GetName() + "'");
+#endif /* _DEBUG */
                        object->Start();
 
                        ASSERT(object->IsActive());
index ae3620ff2ae85ee9f7219eeca706577bd1faeec5..1eb75719a17a3fde849ee9d60e8e3e8f9b12cb19 100644 (file)
@@ -28,7 +28,7 @@ add_library(db_ido SHARED
 )
 
 include_directories(${Boost_INCLUDE_DIRS})
-target_link_libraries(db_ido ${Boost_LIBRARIES} base config icinga methods)
+target_link_libraries(db_ido ${Boost_LIBRARIES} base config icinga)
 
 set_target_properties (
   db_ido PROPERTIES
index 6aab467753f1482d2fce1c0b070da68691a02185..2f1a93cc0b29001dbdfae9096709b17e18d81116 100644 (file)
@@ -21,7 +21,7 @@
 #include "db_ido/dbtype.h"
 #include "db_ido/dbvalue.h"
 #include "icinga/timeperiod.h"
-#include "methods/legacytimeperiod.h"
+#include "icinga/legacytimeperiod.h"
 #include "base/utility.h"
 #include "base/exception.h"
 #include "base/objectlock.h"
@@ -75,25 +75,7 @@ void TimePeriodDbObject::OnConfigUpdate(void)
                if (wday == -1)
                        continue;
 
-               tm reference;
-
-#ifdef _MSC_VER
-               tm *temp = localtime(&refts);
-
-               if (temp == NULL) {
-                       BOOST_THROW_EXCEPTION(posix_error()
-                           << boost::errinfo_api_function("localtime")
-                           << boost::errinfo_errno(errno));
-               }
-
-               reference = *temp;
-#else /* _MSC_VER */
-               if (localtime_r(&refts, &reference) == NULL) {
-                       BOOST_THROW_EXCEPTION(posix_error()
-                           << boost::errinfo_api_function("localtime_r")
-                           << boost::errinfo_errno(errno));
-               }
-#endif /* _MSC_VER */
+               tm reference = Utility::LocalTime(refts);
 
                Array::Ptr segments = make_shared<Array>();
                LegacyTimePeriod::ProcessTimeRanges(kv.second, &reference, segments);
index 853743d32fcaff120d4c1fc6b0d82eec3070ab43..eea9589f165585b7f17686ec953386ce0c905129 100644 (file)
@@ -28,6 +28,7 @@ mkclass_target(icingaapplication.ti icingaapplication.th)
 mkclass_target(notificationcommand.ti notificationcommand.th)
 mkclass_target(notification.ti notification.th)
 mkclass_target(perfdatavalue.ti perfdatavalue.th)
+mkclass_target(scheduleddowntime.ti scheduleddowntime.th)
 mkclass_target(servicegroup.ti servicegroup.th)
 mkclass_target(service.ti service.th)
 mkclass_target(timeperiod.ti timeperiod.th)
@@ -41,9 +42,10 @@ add_library(icinga SHARED
   cib.cpp command.cpp command.th comment.cpp comment.th compatutility.cpp
   domain.cpp domain.th downtime.cpp downtime.th eventcommand.cpp eventcommand.th
   externalcommandprocessor.cpp host.cpp host.th hostgroup.cpp hostgroup.th
-  icingaapplication.cpp icingaapplication.th macroprocessor.cpp macroresolver.cpp
-  notificationcommand.cpp notificationcommand.th notification.cpp notification.th
-  perfdatavalue.cpp perfdatavalue.th pluginutility.cpp service-check.cpp
+  icingaapplication.cpp icingaapplication.th legacytimeperiod.cpp
+  macroprocessor.cpp macroresolver.cpp notificationcommand.cpp notificationcommand.th
+  notification.cpp notification.th perfdatavalue.cpp perfdatavalue.th
+  pluginutility.cpp scheduleddowntime.cpp scheduleddowntime.th service-check.cpp
   service-comment.cpp service.cpp service-downtime.cpp service-event.cpp
   service-flapping.cpp service.th servicegroup.cpp servicegroup.th
   service-notification.cpp timeperiod.cpp timeperiod.th user.cpp user.th
index e26014a582382cc7427bc7a1eb49cea2d013a8b0..c68812aeac9e55d89c399f4568e9deee2a6b8863 100644 (file)
@@ -13,6 +13,7 @@ class Downtime
        [state] bool fixed;
        [state] double duration;
        [state] String triggered_by;
+       [state] String scheduled_by;
        [state] Dictionary::Ptr triggers {
                default {{{ return make_shared<Dictionary>(); }}}
        };
index 706b2d7cf131441adc87e71312fb8cbce32d8f57..fa8e2e460f9b6079463f483fae0f5a14f1b21353 100644 (file)
@@ -116,6 +116,16 @@ type Service {
                }
        },
 
+       %attribute dictionary "scheduled_downtimes" {
+               %attribute dictionary "*" {
+                       %attribute array "templates" {
+                               %attribute name(ScheduledDowntime) "*"
+                       },
+
+                       %attribute any "*"
+               }
+       },
+
        %attribute any "templates"
 }
 
@@ -126,6 +136,8 @@ type ServiceGroup {
 type Notification {
        %require "host",
        %attribute name(Host) "host",
+
+       %require "service",
        %attribute string "service",
 
        %attribute dictionary "macros" {
@@ -237,3 +249,26 @@ type Domain {
                %attribute number "*"
        }
 }
+
+type ScheduledDowntime {
+       %require "host",
+       %attribute name(Host) "host",
+       %require "service",
+       %attribute string "service",
+
+       %require "author",
+       %attribute string "author",
+
+       %require "comment",
+       %attribute string "comment",
+
+       %attribute number "duration",
+       %attribute number "fixed",
+
+       %require "ranges",
+       %attribute dictionary "ranges" {
+               %attribute string "*"
+       },
+
+       %attribute any "templates"
+}
similarity index 87%
rename from lib/methods/legacytimeperiod.cpp
rename to lib/icinga/legacytimeperiod.cpp
index fd56d31dc2d5bc6d6a0523f91281091dd978b386..d49008067ccde3aaac8c68bb79c237f85c82cfdb 100644 (file)
  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
  ******************************************************************************/
 
-#include "methods/legacytimeperiod.h"
+#include "icinga/legacytimeperiod.h"
 #include "base/scriptfunction.h"
 #include "base/convert.h"
 #include "base/exception.h"
 #include "base/objectlock.h"
 #include "base/logger_fwd.h"
 #include "base/debug.h"
+#include "base/utility.h"
 #include <boost/algorithm/string/split.hpp>
 #include <boost/algorithm/string/classification.hpp>
 #include <boost/foreach.hpp>
@@ -141,12 +142,12 @@ void LegacyTimePeriod::ParseTimeSpec(const String& timespec, tm *begin, tm *end,
        if (timespec.GetLength() == 10 && timespec[4] == '-' && timespec[7] == '-') {
                int year = Convert::ToLong(timespec.SubStr(0, 4));
                int month = Convert::ToLong(timespec.SubStr(5, 2));
-               int day = Convert::ToLong(timespec.SubStr(7, 2));
+               int day = Convert::ToLong(timespec.SubStr(8, 2));
 
                if (begin) {
                        begin->tm_year = year - 1900;
                        begin->tm_mon = month;
-                       begin->tm_mday = day;
+                       begin->tm_mday = day + 1;
                        begin->tm_hour = 0;
                        begin->tm_min = 0;
                        begin->tm_sec = 0;
@@ -155,7 +156,7 @@ void LegacyTimePeriod::ParseTimeSpec(const String& timespec, tm *begin, tm *end,
                if (end) {
                        end->tm_year = year - 1900;
                        end->tm_mon = month;
-                       end->tm_mday = day;
+                       end->tm_mday = day + 1;
                        end->tm_hour = 24;
                        end->tm_min = 0;
                        end->tm_sec = 0;
@@ -380,6 +381,64 @@ void LegacyTimePeriod::ProcessTimeRanges(const String& timeranges, tm *reference
        }
 }
 
+Dictionary::Ptr LegacyTimePeriod::FindNextSegment(const String& daydef, const String& timeranges, tm *reference)
+{
+       tm begin, end, iter, ref;
+       time_t tsend, tsiter, tsref;
+       int stride;
+
+       for (int pass = 1; pass <= 2; pass++) {
+               if (pass == 1) {
+                       ref = *reference;
+               } else {
+                       ref = end;
+                       ref.tm_mday++;
+               }
+
+               tsref = mktime(&ref);
+
+               ParseTimeRange(daydef, &begin, &end, &stride, &ref);
+
+               iter = begin;
+
+               tsend = mktime(&end);
+               tsiter = mktime(&iter);
+
+               do {
+                       if (IsInTimeRange(&begin, &end, stride, &iter)) {
+                               Array::Ptr segments = make_shared<Array>();
+                               ProcessTimeRanges(timeranges, &iter, segments);
+
+                               Dictionary::Ptr bestSegment;
+                               double bestBegin;
+
+                               BOOST_FOREACH(const Dictionary::Ptr& segment, segments) {
+                                       double begin = segment->Get("begin");
+
+                                       if (begin < tsref)
+                                               continue;
+
+                                       if (!bestSegment || begin < bestBegin) {
+                                               bestSegment = segment;
+                                               bestBegin = begin;
+                                       }
+                               }
+
+                               if (bestSegment)
+                                       return bestSegment;
+                       }
+
+                       iter.tm_mday++;
+                       iter.tm_hour = 0;
+                       iter.tm_min = 0;
+                       iter.tm_sec = 0;
+                       tsiter = mktime(&iter);
+               } while (tsiter < tsend);
+       }
+
+       return Dictionary::Ptr();
+}
+
 Array::Ptr LegacyTimePeriod::ScriptFunc(const TimePeriod::Ptr& tp, double begin, double end)
 {
        Array::Ptr segments = make_shared<Array>();
@@ -389,36 +448,24 @@ Array::Ptr LegacyTimePeriod::ScriptFunc(const TimePeriod::Ptr& tp, double begin,
        if (ranges) {
                for (int i = 0; i <= (end - begin) / (24 * 60 * 60); i++) {
                        time_t refts = begin + i * 24 * 60 * 60;
-                       tm reference;
+                       tm reference = Utility::LocalTime(refts);
 
+#ifdef _DEBUG
                        Log(LogDebug, "icinga", "Checking reference time " + Convert::ToString(static_cast<long>(refts)));
-
-#ifdef _MSC_VER
-                       tm *temp = localtime(&refts);
-
-                       if (temp == NULL) {
-                               BOOST_THROW_EXCEPTION(posix_error()
-                                   << boost::errinfo_api_function("localtime")
-                                   << boost::errinfo_errno(errno));
-                       }
-
-                       reference = *temp;
-#else /* _MSC_VER */
-                       if (localtime_r(&refts, &reference) == NULL) {
-                               BOOST_THROW_EXCEPTION(posix_error()
-                                   << boost::errinfo_api_function("localtime_r")
-                                   << boost::errinfo_errno(errno));
-                       }
-#endif /* _MSC_VER */
+#endif /* _DEBUG */
 
                        ObjectLock olock(ranges);
                        BOOST_FOREACH(const Dictionary::Pair& kv, ranges) {
                                if (!IsInDayDefinition(kv.first, &reference)) {
+#ifdef _DEBUG
                                        Log(LogDebug, "icinga", "Not in day definition '" + kv.first + "'.");
+#endif /* _DEBUG */
                                        continue;
                                }
 
+#ifdef _DEBUG
                                Log(LogDebug, "icinga", "In day definition '" + kv.first + "'.");
+#endif /* _DEBUG */
 
                                ProcessTimeRanges(kv.second, &reference, segments);
                        }
similarity index 93%
rename from lib/methods/legacytimeperiod.h
rename to lib/icinga/legacytimeperiod.h
index d2cb2ea06b316398e837209c9700f85f633fd42d..01852d27ea1b9e1beb0a8abeb7247ee2920831a3 100644 (file)
@@ -20,7 +20,7 @@
 #ifndef LEGACYTIMEPERIOD_H
 #define LEGACYTIMEPERIOD_H
 
-#include "methods/i2-methods.h"
+#include "icinga/i2-icinga.h"
 #include "icinga/service.h"
 #include "base/dictionary.h"
 
@@ -32,7 +32,7 @@ namespace icinga
  *
  * @ingroup icinga
  */
-class I2_METHODS_API LegacyTimePeriod
+class I2_ICINGA_API LegacyTimePeriod
 {
 public:
        static Array::Ptr ScriptFunc(const TimePeriod::Ptr& tp, double start, double end);
@@ -47,6 +47,7 @@ public:
        static void ProcessTimeRangeRaw(const String& timerange, tm *reference, tm *begin, tm *end);
        static Dictionary::Ptr ProcessTimeRange(const String& timerange, tm *reference);
        static void ProcessTimeRanges(const String& timeranges, tm *reference, const Array::Ptr& result);
+       static Dictionary::Ptr FindNextSegment(const String& daydef, const String& timeranges, tm *reference);
 
 private:
        LegacyTimePeriod(void);
diff --git a/lib/icinga/scheduleddowntime.cpp b/lib/icinga/scheduleddowntime.cpp
new file mode 100644 (file)
index 0000000..7aa4a1c
--- /dev/null
@@ -0,0 +1,146 @@
+/******************************************************************************
+ * Icinga 2                                                                   *
+ * Copyright (C) 2012-2013 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/scheduleddowntime.h"
+#include "icinga/legacytimeperiod.h"
+#include "base/timer.h"
+#include "base/dynamictype.h"
+#include "base/initialize.h"
+#include "base/utility.h"
+#include "base/objectlock.h"
+#include "base/convert.h"
+#include "base/logger_fwd.h"
+#include "base/exception.h"
+#include <boost/foreach.hpp>
+
+using namespace icinga;
+
+REGISTER_TYPE(ScheduledDowntime);
+
+INITIALIZE_ONCE(&ScheduledDowntime::StaticInitialize);
+
+static Timer::Ptr l_Timer;
+
+void ScheduledDowntime::StaticInitialize(void)
+{
+       l_Timer = make_shared<Timer>();
+       l_Timer->SetInterval(60);
+       l_Timer->OnTimerExpired.connect(boost::bind(&ScheduledDowntime::TimerProc));
+       l_Timer->Start();
+}
+
+void ScheduledDowntime::Start(void)
+{
+       DynamicObject::Start();
+
+       CreateNextDowntime();
+}
+
+void ScheduledDowntime::TimerProc(void)
+{
+       BOOST_FOREACH(const ScheduledDowntime::Ptr& sd, DynamicType::GetObjects<ScheduledDowntime>()) {
+               sd->CreateNextDowntime();
+       }
+}
+
+Service::Ptr ScheduledDowntime::GetService(void) const
+{
+       Host::Ptr host = Host::GetByName(GetHostRaw());
+
+       if (GetServiceRaw().IsEmpty())
+               return host->GetCheckService();
+       else
+               return host->GetServiceByShortName(GetServiceRaw());
+}
+
+std::pair<double, double> ScheduledDowntime::FindNextSegment(void)
+{
+       time_t refts = Utility::GetTime();
+       tm reference = Utility::LocalTime(refts);
+
+       Log(LogDebug, "icinga", "Finding next scheduled downtime segment for time " + Convert::ToString(static_cast<long>(refts)));
+
+       Dictionary::Ptr ranges = GetRanges();
+
+       Array::Ptr segments = make_shared<Array>();
+
+       Dictionary::Ptr bestSegment;
+       double bestBegin;
+       double now = Utility::GetTime();
+
+       ObjectLock olock(ranges);
+       BOOST_FOREACH(const Dictionary::Pair& kv, ranges) {
+               tm rangeref;
+
+               Dictionary::Ptr segment = LegacyTimePeriod::FindNextSegment(kv.first, kv.second, &reference);
+
+               if (!segment)
+                       continue;
+
+               double begin = segment->Get("begin");
+
+               if (begin < now)
+                       continue;
+
+               if (!bestSegment || begin < bestBegin) {
+                       bestSegment = segment;
+                       bestBegin = begin;
+               }
+       }
+
+       if (bestSegment)
+               return std::make_pair(bestSegment->Get("begin"), bestSegment->Get("end"));
+       else
+               return std::make_pair(0, 0);
+}
+
+void ScheduledDowntime::CreateNextDowntime(void)
+{
+       Dictionary::Ptr downtimes = GetService()->GetDowntimes();
+
+       {
+               ObjectLock dlock(downtimes);
+               BOOST_FOREACH(const Dictionary::Pair& kv, downtimes) {
+                       Downtime::Ptr downtime = kv.second;
+
+                       if (downtime->GetScheduledBy() != GetName() ||
+                           downtime->GetStartTime() < Utility::GetTime())
+                               continue;
+
+                       /* We've found a downtime that is owned by us and that hasn't started yet - we're done. */
+                       return;
+               }
+       }
+
+       std::pair<double, double> segment = FindNextSegment();
+
+       if (segment.first == 0 && segment.second == 0) {
+               tm reference = Utility::LocalTime(Utility::GetTime());
+               reference.tm_mday++;
+               reference.tm_hour = 0;
+               reference.tm_min = 0;
+               reference.tm_sec = 0;
+
+               return;
+       }
+
+       GetService()->AddDowntime(GetAuthor(), GetComment(),
+           segment.first, segment.second,
+           GetFixed(), String(), GetDuration(), GetName());
+}
diff --git a/lib/icinga/scheduleddowntime.h b/lib/icinga/scheduleddowntime.h
new file mode 100644 (file)
index 0000000..2e6b45b
--- /dev/null
@@ -0,0 +1,58 @@
+/******************************************************************************
+ * Icinga 2                                                                   *
+ * Copyright (C) 2012-2013 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 SCHEDULEDDOWNTIME_H
+#define SCHEDULEDDOWNTIME_H
+
+#include "icinga/i2-icinga.h"
+#include "icinga/scheduleddowntime.th"
+#include "icinga/service.h"
+#include <utility>
+
+namespace icinga
+{
+
+/**
+ * An Icinga scheduled downtime specification.
+ *
+ * @ingroup icinga
+ */
+class I2_ICINGA_API ScheduledDowntime : public ObjectImpl<ScheduledDowntime>
+{
+public:
+       DECLARE_PTR_TYPEDEFS(ScheduledDowntime);
+       DECLARE_TYPENAME(ScheduledDowntime);
+
+       static void StaticInitialize(void);
+
+       Service::Ptr GetService(void) const;
+
+protected:
+       virtual void Start(void);
+
+private:
+       static void TimerProc(void);
+
+       std::pair<double, double> FindNextSegment(void);
+       void CreateNextDowntime(void);
+};
+
+}
+
+#endif /* SCHEDULEDDOWNTIME_H */
diff --git a/lib/icinga/scheduleddowntime.ti b/lib/icinga/scheduleddowntime.ti
new file mode 100644 (file)
index 0000000..6b31a5f
--- /dev/null
@@ -0,0 +1,22 @@
+#include "base/dynamicobject.h"
+
+namespace icinga
+{
+
+class ScheduledDowntime : DynamicObject
+{
+       [config, protected] String host (HostRaw);
+       [config, protected] String service (ServiceRaw);
+
+       [config] String author;
+       [config] String comment;
+
+       [config] double duration;
+       [config] bool fixed {
+               default {{{ return true; }}}
+       };
+
+       [config] Dictionary::Ptr ranges;
+};
+
+}
index bfeae0a87d38f0d26ef864c362616f5a05f9139e..961a47eb7a3215b0b146b9eaa0ee6c6c32c65e58 100644 (file)
@@ -163,7 +163,9 @@ Comment::Ptr Service::GetCommentByID(const String& id)
 
 void Service::AddCommentsToCache(void)
 {
+#ifdef _DEBUG
        Log(LogDebug, "icinga", "Updating Service comments cache.");
+#endif /* _DEBUG */
 
        Dictionary::Ptr comments = GetComments();
 
index 3a397a827cb3172d803365aa4625d292768993c7..83b45fc434677c5a2c96e2f0489d11266733a7d8 100644 (file)
@@ -18,6 +18,7 @@
  ******************************************************************************/
 
 #include "icinga/service.h"
+#include "config/configitembuilder.h"
 #include "base/dynamictype.h"
 #include "base/objectlock.h"
 #include "base/logger_fwd.h"
@@ -47,7 +48,8 @@ int Service::GetNextDowntimeID(void)
 
 String Service::AddDowntime(const String& author, const String& comment,
     double startTime, double endTime, bool fixed,
-    const String& triggeredBy, double duration, const String& id, const String& authority)
+    const String& triggeredBy, double duration, const String& scheduledBy,
+    const String& id, const String& authority)
 {
        String uid;
 
@@ -66,6 +68,7 @@ String Service::AddDowntime(const String& author, const String& comment,
        downtime->SetFixed(fixed);
        downtime->SetDuration(duration);
        downtime->SetTriggeredBy(triggeredBy);
+       downtime->SetScheduledBy(scheduledBy);
 
        int legacy_id;
 
@@ -82,10 +85,7 @@ String Service::AddDowntime(const String& author, const String& comment,
                Downtime::Ptr otherDowntime = otherDowntimes->Get(triggeredBy);
                Dictionary::Ptr triggers = otherDowntime->GetTriggers();
 
-               {
-                       ObjectLock olock(otherOwner);
-                       triggers->Set(triggeredBy, triggeredBy);
-               }
+               triggers->Set(triggeredBy, triggeredBy);
        }
 
        GetDowntimes()->Set(uid, downtime);
@@ -96,7 +96,8 @@ String Service::AddDowntime(const String& author, const String& comment,
                l_DowntimesCache[uid] = GetSelf();
        }
 
-       Log(LogWarning, "icinga", "added downtime with ID '" + Convert::ToString(downtime->GetLegacyId()) + "'.");
+       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);
 
@@ -129,7 +130,7 @@ void Service::RemoveDowntime(const String& id, bool cancelled, const String& aut
 
        downtime->SetWasCancelled(cancelled);
 
-       Log(LogWarning, "icinga", "removed downtime with ID '" + Convert::ToString(downtime->GetLegacyId()) + "' from service '" + owner->GetName() + "'.");
+       Log(LogDebug, "icinga", "Removed downtime with ID '" + Convert::ToString(downtime->GetLegacyId()) + "' from service '" + owner->GetName() + "'.");
 
        OnDowntimeRemoved(owner, downtime, authority);
 }
@@ -230,7 +231,9 @@ void Service::StartDowntimesExpiredTimer(void)
 
 void Service::AddDowntimesToCache(void)
 {
+#ifdef _DEBUG
        Log(LogDebug, "icinga", "Updating Service downtimes cache.");
+#endif /* _DEBUG */
 
        Dictionary::Ptr downtimes = GetDowntimes();
 
@@ -312,3 +315,65 @@ int Service::GetDowntimeDepth(void) const
 
        return downtime_depth;
 }
+
+void Service::UpdateSlaveScheduledDowntimes(void)
+{
+       ConfigItem::Ptr item = ConfigItem::GetObject("Service", GetName());
+
+       /* Don't create slave scheduled downtimes unless we own this object */
+       if (!item)
+               return;
+
+       /* Service scheduled downtime descs */
+       Dictionary::Ptr descs = GetScheduledDowntimeDescriptions();
+
+       if (!descs)
+               return;
+
+       ObjectLock olock(descs);
+
+       BOOST_FOREACH(const Dictionary::Pair& kv, descs) {
+               std::ostringstream namebuf;
+               namebuf << GetName() << ":" << kv.first;
+               String name = namebuf.str();
+
+               std::vector<String> path;
+               path.push_back("scheduled_downtimes");
+               path.push_back(kv.first);
+
+               DebugInfo di;
+               item->GetLinkedExpressionList()->FindDebugInfoPath(path, di);
+
+               if (di.Path.IsEmpty())
+                       di = item->GetDebugInfo();
+
+               ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
+               builder->SetType("ScheduledDowntime");
+               builder->SetName(name);
+               builder->AddExpression("host", OperatorSet, GetHost()->GetName());
+               builder->AddExpression("service", OperatorSet, GetShortName());
+
+               Dictionary::Ptr scheduledDowntime = kv.second;
+
+               Array::Ptr templates = scheduledDowntime->Get("templates");
+
+               if (templates) {
+                       ObjectLock tlock(templates);
+
+                       BOOST_FOREACH(const Value& tmpl, templates) {
+                               builder->AddParent(tmpl);
+                       }
+               }
+
+               /* Clone attributes from the scheduled downtime expression list. */
+               ExpressionList::Ptr sd_exprl = make_shared<ExpressionList>();
+               item->GetLinkedExpressionList()->ExtractPath(path, sd_exprl);
+
+               builder->AddExpressionList(sd_exprl);
+
+               ConfigItem::Ptr scheduledDowntimeItem = builder->Compile();
+               scheduledDowntimeItem->Register();
+               DynamicObject::Ptr dobj = scheduledDowntimeItem->Commit();
+               dobj->OnConfigLoaded();
+       }
+}
index fcca38ed382325df3b3da05ff0b946eaac6a6f12..482892758920a08ca92a59b6fdf68f0452ac3779 100644 (file)
@@ -46,9 +46,6 @@ void Service::Start(void)
 {
        VERIFY(GetHost());
 
-       AddDowntimesToCache();
-       AddCommentsToCache();
-
        StartDowntimesExpiredTimer();
 
        double now = Utility::GetTime();
@@ -80,10 +77,36 @@ void Service::OnConfigLoaded(void)
                m_Host->AddService(GetSelf());
 
        UpdateSlaveNotifications();
+       UpdateSlaveScheduledDowntimes();
 
        SetSchedulingOffset(Utility::Random());
 }
 
+void Service::OnStateLoaded(void)
+{
+       AddDowntimesToCache();
+       AddCommentsToCache();
+
+       std::vector<String> ids;
+       Dictionary::Ptr downtimes = GetDowntimes();
+
+       {
+               ObjectLock dlock(downtimes);
+               BOOST_FOREACH(const Dictionary::Pair& kv, downtimes) {
+                       Downtime::Ptr downtime = kv.second;
+
+                       if (downtime->GetScheduledBy().IsEmpty())
+                               continue;
+
+                       ids.push_back(kv.first);
+               }
+       }
+
+       BOOST_FOREACH(const String& id, ids) {
+               RemoveDowntime(id, true);
+       }
+}
+
 Service::Ptr Service::GetByNamePair(const String& hostName, const String& serviceName)
 {
        if (!hostName.IsEmpty()) {
index 1680e2221e2283c3cd2aaebba5e5d5040c7c6315..c6cd6b8c9b7b88fd67fba5475b03aaf90956641f 100644 (file)
@@ -192,7 +192,8 @@ public:
        String AddDowntime(const String& author, const String& comment,
            double startTime, double endTime, bool fixed,
            const String& triggeredBy, double duration,
-           const String& id = String(), const String& authority = String());
+           const String& scheduledBy = String(), const String& id = String(),
+           const String& authority = String());
 
        static void RemoveDowntime(const String& id, bool cancelled, const String& = String());
 
@@ -208,6 +209,8 @@ public:
        bool IsInDowntime(void) const;
        bool IsAcknowledged(void);
 
+       void UpdateSlaveScheduledDowntimes(void);
+
        /* Comments */
        static int GetNextCommentID(void);
 
@@ -265,6 +268,7 @@ protected:
        virtual void Start(void);
 
        virtual void OnConfigLoaded(void);
+       virtual void OnStateLoaded(void);
 
 private:
        Host::Ptr m_Host;
index ee693c70606bd66a7a56604dfaae7574efab9105..0f91b1307dad2bcdce2f4be37c562ae7d8861b80 100644 (file)
@@ -59,6 +59,7 @@ class Service : DynamicObject
                default {{{ return 30; }}}
        };
        [config] Dictionary::Ptr notifications (NotificationDescriptions);
+       [config] Dictionary::Ptr scheduled_downtimes (ScheduledDowntimeDescriptions);
        [config] bool enable_active_checks (EnableActiveChecksRaw) {
                default {{{ return true; }}}
        };
index 64dde6cbe1141405c540636b43ddd280f2d83aea..745bb33b4acd3320a5ab02f69d39a55573c99e76 100644 (file)
@@ -16,9 +16,9 @@
 # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
 add_library(methods SHARED
-  icingachecktask.cpp legacytimeperiod.cpp nullchecktask.cpp
-  nulleventtask.cpp pluginchecktask.cpp plugineventtask.cpp
-  pluginnotificationtask.cpp randomchecktask.cpp timeperiodtask.cpp
+  icingachecktask.cpp nullchecktask.cpp nulleventtask.cpp
+  pluginchecktask.cpp plugineventtask.cpp pluginnotificationtask.cpp
+  randomchecktask.cpp timeperiodtask.cpp
 )
 
 target_link_libraries(methods ${Boost_LIBRARIES} base config icinga)