]> granicus.if.org Git - icinga2/commitdiff
Implement exclude and include ability for TimePeriod objects
authorPhilipp Dallig <philipp.dallig@gmail.com>
Fri, 25 Mar 2016 11:55:11 +0000 (12:55 +0100)
committerMichael Friedrich <michael.friedrich@netways.de>
Sat, 21 May 2016 18:33:09 +0000 (20:33 +0200)
This feature allows to exclude and include specific time period
objects and their time ranges from an existing time period object.

This comes in handy when e.g. excluding holidays.

fixes #7355

Signed-off-by: Michael Friedrich <michael.friedrich@netways.de>
AUTHORS
doc/5-advanced-topics.md
doc/6-object-types.md
lib/checker/checkercomponent.cpp
lib/icinga/notification.cpp
lib/icinga/timeperiod.cpp
lib/icinga/timeperiod.hpp
lib/icinga/timeperiod.ti

diff --git a/AUTHORS b/AUTHORS
index 1ab12b828acb1a971cac78f025935a30dc96c5d5..804b94bf544e2989fdb76e9eed1d9765df32efb2 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -71,6 +71,7 @@ Paul Richards <paul@minimoo.org>
 Per von Zweigbergk <pvz@itassistans.se>
 Petr Ruzicka <petr.ruzicka@gmail.com>
 Phil Hutchinson <phil@volumedia.co.uk>
+Philipp Dallig <philipp.dallig@gmail.com>
 Ralph Breier <ralph.breier@roedl.com>
 Reto Zeder <reto.zeder@arcade.ch>
 Ricardo Bartels <ricardo@bitchbrothers.com>
index de13ca4df27b287bf82871db1f3ee4002c24c54c..ebd72552be28cc5643de20871c436630b24b15de 100644 (file)
@@ -132,8 +132,9 @@ re-notify if the problem persists.
 
 ## <a id="timeperiods"></a> Time Periods
 
-Time Periods define time ranges in Icinga where event actions are
-triggered, for example whether a service check is executed or not within
+[Time Periods](6-object-types.md#objecttype-timeperiod) define
+time ranges in Icinga where event actions are triggered, for
+example whether a service check is executed or not within
 the `check_period` attribute. Or a notification should be sent to
 users or not, filtered by the `period` and `notification_period`
 configuration attributes for `Notification` and `User` objects.
@@ -208,6 +209,67 @@ Use the `period` attribute to assign time periods to
       period = "workhours"
     }
 
+### <a id="timeperiods-includes-excludes"></a> Time Periods Inclusion and Exclusion
+
+Sometimes it is necessary to exclude certain time ranges from
+your default time period definitions. For example if you don't
+want to send out any notification during the holiday season,
+or if you only want to allow small time windows for executed checks.
+
+The [TimePeriod object](6-object-types.md#objecttype-timeperiod)
+provides the `includes` and `excludes` attributes to solve this issue.
+`prefer_includes` defines whether included or excluded time periods are
+preferred.
+
+The following example defines a time period called `holidays` where
+notifications should be supressed:
+
+    object TimePeriod "holidays" {
+      import "legacy-timeperiod"
+    
+      ranges = {
+        "january 1" = "00:00-24:00"                 //new year's day
+        "july 4" = "00:00-24:00"                    //independence day
+        "december 25" = "00:00-24:00"               //christmas
+        "december 31" = "18:00-24:00"               //new year's eve (6pm+)
+        "2017-04-16" = "00:00-24:00"                //easter 2017
+        "monday -1 may" = "00:00-24:00"             //memorial day (last monday in may)
+        "monday 1 september" = "00:00-24:00"        //labor day (1st monday in september)
+        "thursday 4 november" = "00:00-24:00"       //thanksgiving (4th thursday in november)
+      }
+    }
+
+In addition to that the time period `weekends` defines an additional
+time window which should be excluded from notifications:
+
+    object TimePeriod "weekends-excluded" {
+      import "legacy-timeperiod"
+    
+      ranges = {
+        "saturday"  = "00:00-09:00,18:00-24:00"
+        "sunday"    = "00:00-09:00,18:00-24:00"
+      }
+    }
+
+The time period `prod-notification` defines the default time ranges
+and adds the excluded time period names as an array.
+
+    object TimePeriod "prod-notification" {
+      import "legacy-timeperiod"
+    
+      excludes = [ "holidays", "weekends-excluded" ]
+    
+      ranges = {
+        "monday"    = "00:00-24:00"
+        "tuesday"   = "00:00-24:00"
+        "wednesday" = "00:00-24:00"
+        "thursday"  = "00:00-24:00"
+        "friday"    = "00:00-24:00"
+        "saturday"  = "00:00-24:00"
+        "sunday"    = "00:00-24:00"
+      }
+    }
+
 
 ## <a id="use-functions-object-config"></a> Use Functions in Object Configuration
 
index 1518a62de285da1060bab36cc902e0187ea7e889..a07a1a146a37fd8936dddbc82ad0739d6b2f2e19 100644 (file)
@@ -1380,6 +1380,9 @@ Configuration Attributes:
   display_name    |**Optional.** A short description of the time period.
   update          |**Required.** The "update" script method takes care of updating the internal representation of the time period. In virtually all cases you should import the "legacy-timeperiod" template to take care of this setting.
   ranges          |**Required.** A dictionary containing information which days and durations apply to this timeperiod.
+  prefer_includes |**Optional.** Boolean whether to prefer timeperiods `includes` or `excludes`. Default to true.
+  excludes        |**Optional.** An array of timeperiods, which should exclude from your timerange.
+  includes        |**Optional.** An array of timeperiods, which should include into your timerange
 
 The `/etc/icinga2/conf.d/timeperiods.conf` file is usually used to define
 timeperiods including this one.
index bfb997fb6b2f8188ba684949cfecb274106c8805..f335a91475a5aceb96042da3780d36e0911ff95d 100644 (file)
@@ -162,7 +162,8 @@ void CheckerComponent::CheckThreadProc(void)
 
                        if (tp && !tp->IsInside(Utility::GetTime())) {
                                Log(LogNotice, "CheckerComponent")
-                                   << "Skipping check for object '" << checkable->GetName() << "': not in check_period";
+                                   << "Skipping check for object '" << checkable->GetName()
+                                   << "': not in check period '" << tp->GetName() << "'";
                                check = false;
                        }
                }
index 1cfefa0f98884408b7e037e028873499e41f287f..7d1f7247d22363ae3ccb6e938601e8fc8c473ef2 100644 (file)
@@ -250,7 +250,8 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe
 
                if (tp && !tp->IsInside(Utility::GetTime())) {
                        Log(LogNotice, "Notification")
-                           << "Not sending notifications for notification object '" << GetName() << "': not in timeperiod";
+                           << "Not sending notifications for notification object '" << GetName()
+                           << "': not in timeperiod '" << tp->GetName() << "'";
                        return;
                }
 
@@ -402,7 +403,8 @@ bool Notification::CheckNotificationUserFilters(NotificationType type, const Use
                if (tp && !tp->IsInside(Utility::GetTime())) {
                        Log(LogNotice, "Notification")
                            << "Not sending notifications for notification object '"
-                           << GetName() << " and user '" << user->GetName() << "': user not in timeperiod";
+                           << GetName() << " and user '" << user->GetName()
+                           << "': user period not in timeperiod '" << tp->GetName() << "'";
                        return false;
                }
 
index be2a6fd2ac3846555cf43d4c02741e9e9ece9fda..9899f6d34086cb26e3f1b8f0b6227086d3a7f925 100644 (file)
@@ -77,15 +77,22 @@ void TimePeriod::AddSegment(double begin, double end)
                        if (segment->Get("begin") <= begin && segment->Get("end") >= end)
                                return; /* New segment is fully contained in this segment. */
 
-                       if (segment->Get("begin") <= begin && segment->Get("end") >= begin) {
-                               segment->Set("end", end); /* Extend an existing segment. */
+                       if (segment->Get("begin") >= begin && segment->Get("end") <= end) {
+                               segment->Set("begin", begin);
+                               segment->Set("end", end); /* Extend an existing segment to both sides */
+                               return;
+                       }
+
+                       if (segment->Get("end") >= begin && segment->Get("end") <= end) {
+                               segment->Set("end", end); /* Extend an existing segment to right. */
                                return;
                        }
 
                        if (segment->Get("begin") >= begin && segment->Get("begin") <= end) {
-                               segment->Set("begin", begin); /* Extend an existing segment. */
+                               segment->Set("begin", begin); /* Extend an existing segment to left. */
                                return;
                        }
+
                }
        }
 
@@ -142,6 +149,21 @@ void TimePeriod::RemoveSegment(double begin, double end)
                        continue;
                }
 
+               /* Cut between */
+               if (segment->Get("begin") < begin && segment->Get("end") > end) {
+                       Dictionary::Ptr firstsegment = new Dictionary();
+                       firstsegment->Set("begin", segment->Get("begin"));
+                       firstsegment->Set("end", begin);
+
+                       Dictionary::Ptr secondsegment = new Dictionary();
+                       secondsegment->Set("begin", end);
+                       secondsegment->Set("end", segment->Get("end"));
+
+                       newSegments->Add(firstsegment);
+                       newSegments->Add(secondsegment);
+                       continue;
+               }
+
                /* Adjust the begin/end timestamps so as to not overlap with the specified range. */
                if (segment->Get("begin") > begin && segment->Get("begin") < end)
                        segment->Set("begin", end);
@@ -157,6 +179,11 @@ void TimePeriod::RemoveSegment(double begin, double end)
        Dump();
 }
 
+void TimePeriod::RemoveSegment(const Dictionary::Ptr& segment)
+{
+       RemoveSegment(segment->Get("begin"), segment->Get("end"));
+}
+
 void TimePeriod::PurgeSegments(double end)
 {
        ASSERT(OwnsLock());
@@ -187,6 +214,23 @@ void TimePeriod::PurgeSegments(double end)
        SetSegments(newSegments);
 }
 
+void TimePeriod::Merge(const TimePeriod::Ptr& timeperiod, bool include)
+{
+       Log(LogDebug, "TimePeriod")
+           << "Merge TimePeriod '" << GetName() << "' with '" << timeperiod->GetName() << "' "
+           << "Method: " << (include ? "include" : "exclude");
+
+       Array::Ptr segments = timeperiod->GetSegments();
+
+       if (segments) {
+               ObjectLock dlock(segments);
+               ObjectLock ilock(this);
+               BOOST_FOREACH(const Dictionary::Ptr& segment, segments) {
+                       include ? AddSegment(segment) : RemoveSegment(segment);
+               }
+       }
+}
+
 void TimePeriod::UpdateRegion(double begin, double end, bool clearExisting)
 {
        if (!clearExisting) {
@@ -215,6 +259,34 @@ void TimePeriod::UpdateRegion(double begin, double end, bool clearExisting)
                        }
                }
        }
+
+       bool preferInclude = GetPreferIncludes();
+
+       /* First handle the non preferred timeranges */
+       Array::Ptr timeranges = preferInclude ? GetExcludes() : GetIncludes();
+
+       if (timeranges) {
+               ObjectLock olock(timeranges);
+               BOOST_FOREACH(const String& name, timeranges) {
+                       const TimePeriod::Ptr timeperiod = TimePeriod::GetByName(name);
+
+                       if (timeperiod)
+                               Merge(timeperiod, !preferInclude);
+               }
+       }
+
+       /* Preferred timeranges must be handled at the end */
+       timeranges = preferInclude ? GetIncludes() : GetExcludes();
+
+       if (timeranges) {
+               ObjectLock olock(timeranges);
+               BOOST_FOREACH(const String& name, timeranges) {
+                       const TimePeriod::Ptr timeperiod = TimePeriod::GetByName(name);
+
+                       if (timeperiod)
+                               Merge(timeperiod, preferInclude);
+               }
+       }
 }
 
 bool TimePeriod::GetIsInside(void) const
index 51100f575292491d6e415266c08d604db0e3e9f0..9b6c881018e482294e44bc53c24401a58879e7df 100644 (file)
@@ -54,8 +54,11 @@ private:
        void AddSegment(double s, double end);
        void AddSegment(const Dictionary::Ptr& segment);
        void RemoveSegment(double begin, double end);
+       void RemoveSegment(const Dictionary::Ptr& segment);
        void PurgeSegments(double end);
 
+       void Merge(const TimePeriod::Ptr& timeperiod, bool include = true);
+
        void Dump(void);
 
        static void UpdateTimerHandler(void);
index 2b78b516eabd115075d398be75b9e05db4165c8a..50b3a771b9e75e429b49461c7e256223f6f44d44 100644 (file)
@@ -37,6 +37,15 @@ class TimePeriod : CustomVarObject
        };
        [config] Dictionary::Ptr ranges;
        [config, required] Function::Ptr update;
+       [config] bool prefer_includes {
+               default {{{ return true; }}}
+       };
+       [config] array(name(TimePeriod)) excludes {
+               default {{{ return new Array(); }}}
+       };
+       [config] array(name(TimePeriod)) includes {
+               default {{{ return new Array(); }}}
+       };
        [state, no_user_modify] Value valid_begin;
        [state, no_user_modify] Value valid_end;
        [state, no_user_modify] Array::Ptr segments;