Skip to content

Commit

Permalink
Implement exclude and include ability for TimePeriod objects
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Reamer authored and Michael Friedrich committed May 21, 2016
1 parent d49b63d commit 54e1c8a
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 8 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -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>
Expand Down
66 changes: 64 additions & 2 deletions doc/5-advanced-topics.md
Expand Up @@ -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.
Expand Down Expand Up @@ -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

Expand Down
3 changes: 3 additions & 0 deletions doc/6-object-types.md
Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion lib/checker/checkercomponent.cpp
Expand Up @@ -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;
}
}
Expand Down
6 changes: 4 additions & 2 deletions lib/icinga/notification.cpp
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand Down
78 changes: 75 additions & 3 deletions lib/icinga/timeperiod.cpp
Expand Up @@ -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;
}

}
}

Expand Down Expand Up @@ -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);
Expand All @@ -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());
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions lib/icinga/timeperiod.hpp
Expand Up @@ -54,8 +54,11 @@ class I2_ICINGA_API TimePeriod : public ObjectImpl<TimePeriod>
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);
Expand Down
9 changes: 9 additions & 0 deletions lib/icinga/timeperiod.ti
Expand Up @@ -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;
Expand Down

0 comments on commit 54e1c8a

Please sign in to comment.