Implement scheduled downtimes.

Fixes #3584
This commit is contained in:
Gunnar Beutner 2013-11-13 14:56:31 +01:00
parent 99f2b0673e
commit 41ded2858c
25 changed files with 550 additions and 65 deletions

View File

@ -1272,7 +1272,8 @@ void ClusterListener::MessageHandler(const Endpoint::Ptr& sender, const Dictiona
service->AddDowntime(downtime->GetAuthor(), downtime->GetComment(), service->AddDowntime(downtime->GetAuthor(), downtime->GetComment(),
downtime->GetStartTime(), downtime->GetEndTime(), downtime->GetStartTime(), downtime->GetEndTime(),
downtime->GetFixed(), downtime->GetTriggeredBy(), downtime->GetFixed(), downtime->GetTriggeredBy(),
downtime->GetDuration(), downtime->GetId(), sender->GetName()); downtime->GetDuration(), downtime->GetScheduledBy(),
downtime->GetId(), sender->GetName());
AsyncRelayMessage(sender, message, true); AsyncRelayMessage(sender, message, true);
} else if (message->Get("method") == "cluster::RemoveDowntime") { } else if (message->Get("method") == "cluster::RemoveDowntime") {

View File

@ -311,6 +311,44 @@ Attributes:
The `/etc/icinga2/conf.d/timeperiods.conf` file is usually used to define The `/etc/icinga2/conf.d/timeperiods.conf` file is usually used to define
timeperiods including this one. 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 ### <a id="objecttype-consolelogger"></a> ConsoleLogger

View File

@ -20,6 +20,7 @@ include(InstallConfig)
configure_file(icinga/icinga-classic-apache.conf.cmake ${CMAKE_CURRENT_BINARY_DIR}/icinga/icinga-classic-apache.conf) 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/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-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-service.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)
install_if_not_exists(icinga2/conf.d/generic-user.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d) install_if_not_exists(icinga2/conf.d/generic-user.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/conf.d)

View File

@ -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"
}
}

View File

@ -39,7 +39,11 @@ object Host "localhost" inherits "linux-server" {
services["load"] = { services["load"] = {
templates = [ "generic-service" ], templates = [ "generic-service" ],
check_command = "load" check_command = "load",
scheduled_downtimes["backup"] = {
templates = [ "backup-downtime" ]
}
}, },
services["processes"] = { services["processes"] = {

View File

@ -278,7 +278,9 @@ void DynamicObject::RestoreObjects(const String& filename, int attributeTypes)
if (object) { if (object) {
ASSERT(!object->IsActive()); ASSERT(!object->IsActive());
#ifdef _DEBUG
Log(LogDebug, "base", "Restoring object '" + name + "' of type '" + type + "'."); Log(LogDebug, "base", "Restoring object '" + name + "' of type '" + type + "'.");
#endif /* _DEBUG */
Deserialize(object, update, attributeTypes); Deserialize(object, update, attributeTypes);
object->OnStateLoaded(); object->OnStateLoaded();
} }

View File

@ -744,3 +744,28 @@ int Utility::Random(void)
return rand(); 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 */
}

View File

@ -108,6 +108,8 @@ public:
static int Random(void); static int Random(void);
static tm LocalTime(time_t ts);
private: private:
Utility(void); Utility(void);

View File

@ -157,7 +157,9 @@ DynamicObject::Ptr ConfigItem::Commit(void)
{ {
ASSERT(!OwnsLock()); ASSERT(!OwnsLock());
#ifdef _DEBUG
Log(LogDebug, "base", "Commit called for ConfigItem Type=" + GetType() + ", Name=" + GetName()); Log(LogDebug, "base", "Commit called for ConfigItem Type=" + GetType() + ", Name=" + GetName());
#endif /* _DEBUG */
/* Make sure the type is valid. */ /* Make sure the type is valid. */
DynamicType::Ptr dtype = DynamicType::GetByName(GetType()); DynamicType::Ptr dtype = DynamicType::GetByName(GetType());
@ -284,7 +286,9 @@ bool ConfigItem::ActivateItems(bool validateOnly)
if (object->IsActive()) if (object->IsActive())
continue; continue;
#ifdef _DEBUG
Log(LogDebug, "config", "Activating object '" + object->GetName() + "' of type '" + object->GetType()->GetName() + "'"); Log(LogDebug, "config", "Activating object '" + object->GetName() + "' of type '" + object->GetType()->GetName() + "'");
#endif /* _DEBUG */
object->Start(); object->Start();
ASSERT(object->IsActive()); ASSERT(object->IsActive());

View File

@ -28,7 +28,7 @@ add_library(db_ido SHARED
) )
include_directories(${Boost_INCLUDE_DIRS}) 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 ( set_target_properties (
db_ido PROPERTIES db_ido PROPERTIES

View File

@ -21,7 +21,7 @@
#include "db_ido/dbtype.h" #include "db_ido/dbtype.h"
#include "db_ido/dbvalue.h" #include "db_ido/dbvalue.h"
#include "icinga/timeperiod.h" #include "icinga/timeperiod.h"
#include "methods/legacytimeperiod.h" #include "icinga/legacytimeperiod.h"
#include "base/utility.h" #include "base/utility.h"
#include "base/exception.h" #include "base/exception.h"
#include "base/objectlock.h" #include "base/objectlock.h"
@ -75,25 +75,7 @@ void TimePeriodDbObject::OnConfigUpdate(void)
if (wday == -1) if (wday == -1)
continue; continue;
tm reference; tm reference = Utility::LocalTime(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 */
Array::Ptr segments = make_shared<Array>(); Array::Ptr segments = make_shared<Array>();
LegacyTimePeriod::ProcessTimeRanges(kv.second, &reference, segments); LegacyTimePeriod::ProcessTimeRanges(kv.second, &reference, segments);

View File

@ -28,6 +28,7 @@ mkclass_target(icingaapplication.ti icingaapplication.th)
mkclass_target(notificationcommand.ti notificationcommand.th) mkclass_target(notificationcommand.ti notificationcommand.th)
mkclass_target(notification.ti notification.th) mkclass_target(notification.ti notification.th)
mkclass_target(perfdatavalue.ti perfdatavalue.th) mkclass_target(perfdatavalue.ti perfdatavalue.th)
mkclass_target(scheduleddowntime.ti scheduleddowntime.th)
mkclass_target(servicegroup.ti servicegroup.th) mkclass_target(servicegroup.ti servicegroup.th)
mkclass_target(service.ti service.th) mkclass_target(service.ti service.th)
mkclass_target(timeperiod.ti timeperiod.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 cib.cpp command.cpp command.th comment.cpp comment.th compatutility.cpp
domain.cpp domain.th downtime.cpp downtime.th eventcommand.cpp eventcommand.th domain.cpp domain.th downtime.cpp downtime.th eventcommand.cpp eventcommand.th
externalcommandprocessor.cpp host.cpp host.th hostgroup.cpp hostgroup.th externalcommandprocessor.cpp host.cpp host.th hostgroup.cpp hostgroup.th
icingaapplication.cpp icingaapplication.th macroprocessor.cpp macroresolver.cpp icingaapplication.cpp icingaapplication.th legacytimeperiod.cpp
notificationcommand.cpp notificationcommand.th notification.cpp notification.th macroprocessor.cpp macroresolver.cpp notificationcommand.cpp notificationcommand.th
perfdatavalue.cpp perfdatavalue.th pluginutility.cpp service-check.cpp 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-comment.cpp service.cpp service-downtime.cpp service-event.cpp
service-flapping.cpp service.th servicegroup.cpp servicegroup.th service-flapping.cpp service.th servicegroup.cpp servicegroup.th
service-notification.cpp timeperiod.cpp timeperiod.th user.cpp user.th service-notification.cpp timeperiod.cpp timeperiod.th user.cpp user.th

View File

@ -13,6 +13,7 @@ class Downtime
[state] bool fixed; [state] bool fixed;
[state] double duration; [state] double duration;
[state] String triggered_by; [state] String triggered_by;
[state] String scheduled_by;
[state] Dictionary::Ptr triggers { [state] Dictionary::Ptr triggers {
default {{{ return make_shared<Dictionary>(); }}} default {{{ return make_shared<Dictionary>(); }}}
}; };

View File

@ -116,6 +116,16 @@ type Service {
} }
}, },
%attribute dictionary "scheduled_downtimes" {
%attribute dictionary "*" {
%attribute array "templates" {
%attribute name(ScheduledDowntime) "*"
},
%attribute any "*"
}
},
%attribute any "templates" %attribute any "templates"
} }
@ -126,6 +136,8 @@ type ServiceGroup {
type Notification { type Notification {
%require "host", %require "host",
%attribute name(Host) "host", %attribute name(Host) "host",
%require "service",
%attribute string "service", %attribute string "service",
%attribute dictionary "macros" { %attribute dictionary "macros" {
@ -237,3 +249,26 @@ type Domain {
%attribute number "*" %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"
}

View File

@ -17,13 +17,14 @@
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * * 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/scriptfunction.h"
#include "base/convert.h" #include "base/convert.h"
#include "base/exception.h" #include "base/exception.h"
#include "base/objectlock.h" #include "base/objectlock.h"
#include "base/logger_fwd.h" #include "base/logger_fwd.h"
#include "base/debug.h" #include "base/debug.h"
#include "base/utility.h"
#include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/classification.hpp>
#include <boost/foreach.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] == '-') { if (timespec.GetLength() == 10 && timespec[4] == '-' && timespec[7] == '-') {
int year = Convert::ToLong(timespec.SubStr(0, 4)); int year = Convert::ToLong(timespec.SubStr(0, 4));
int month = Convert::ToLong(timespec.SubStr(5, 2)); 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) { if (begin) {
begin->tm_year = year - 1900; begin->tm_year = year - 1900;
begin->tm_mon = month; begin->tm_mon = month;
begin->tm_mday = day; begin->tm_mday = day + 1;
begin->tm_hour = 0; begin->tm_hour = 0;
begin->tm_min = 0; begin->tm_min = 0;
begin->tm_sec = 0; begin->tm_sec = 0;
@ -155,7 +156,7 @@ void LegacyTimePeriod::ParseTimeSpec(const String& timespec, tm *begin, tm *end,
if (end) { if (end) {
end->tm_year = year - 1900; end->tm_year = year - 1900;
end->tm_mon = month; end->tm_mon = month;
end->tm_mday = day; end->tm_mday = day + 1;
end->tm_hour = 24; end->tm_hour = 24;
end->tm_min = 0; end->tm_min = 0;
end->tm_sec = 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 LegacyTimePeriod::ScriptFunc(const TimePeriod::Ptr& tp, double begin, double end)
{ {
Array::Ptr segments = make_shared<Array>(); Array::Ptr segments = make_shared<Array>();
@ -389,36 +448,24 @@ Array::Ptr LegacyTimePeriod::ScriptFunc(const TimePeriod::Ptr& tp, double begin,
if (ranges) { if (ranges) {
for (int i = 0; i <= (end - begin) / (24 * 60 * 60); i++) { for (int i = 0; i <= (end - begin) / (24 * 60 * 60); i++) {
time_t refts = begin + i * 24 * 60 * 60; 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))); Log(LogDebug, "icinga", "Checking reference time " + Convert::ToString(static_cast<long>(refts)));
#endif /* _DEBUG */
#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 */
ObjectLock olock(ranges); ObjectLock olock(ranges);
BOOST_FOREACH(const Dictionary::Pair& kv, ranges) { BOOST_FOREACH(const Dictionary::Pair& kv, ranges) {
if (!IsInDayDefinition(kv.first, &reference)) { if (!IsInDayDefinition(kv.first, &reference)) {
#ifdef _DEBUG
Log(LogDebug, "icinga", "Not in day definition '" + kv.first + "'."); Log(LogDebug, "icinga", "Not in day definition '" + kv.first + "'.");
#endif /* _DEBUG */
continue; continue;
} }
#ifdef _DEBUG
Log(LogDebug, "icinga", "In day definition '" + kv.first + "'."); Log(LogDebug, "icinga", "In day definition '" + kv.first + "'.");
#endif /* _DEBUG */
ProcessTimeRanges(kv.second, &reference, segments); ProcessTimeRanges(kv.second, &reference, segments);
} }

View File

@ -20,7 +20,7 @@
#ifndef LEGACYTIMEPERIOD_H #ifndef LEGACYTIMEPERIOD_H
#define LEGACYTIMEPERIOD_H #define LEGACYTIMEPERIOD_H
#include "methods/i2-methods.h" #include "icinga/i2-icinga.h"
#include "icinga/service.h" #include "icinga/service.h"
#include "base/dictionary.h" #include "base/dictionary.h"
@ -32,7 +32,7 @@ namespace icinga
* *
* @ingroup icinga * @ingroup icinga
*/ */
class I2_METHODS_API LegacyTimePeriod class I2_ICINGA_API LegacyTimePeriod
{ {
public: public:
static Array::Ptr ScriptFunc(const TimePeriod::Ptr& tp, double start, double end); 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 void ProcessTimeRangeRaw(const String& timerange, tm *reference, tm *begin, tm *end);
static Dictionary::Ptr ProcessTimeRange(const String& timerange, tm *reference); static Dictionary::Ptr ProcessTimeRange(const String& timerange, tm *reference);
static void ProcessTimeRanges(const String& timeranges, tm *reference, const Array::Ptr& result); 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: private:
LegacyTimePeriod(void); LegacyTimePeriod(void);

View File

@ -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());
}

View File

@ -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 */

View File

@ -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;
};
}

View File

@ -163,7 +163,9 @@ Comment::Ptr Service::GetCommentByID(const String& id)
void Service::AddCommentsToCache(void) void Service::AddCommentsToCache(void)
{ {
#ifdef _DEBUG
Log(LogDebug, "icinga", "Updating Service comments cache."); Log(LogDebug, "icinga", "Updating Service comments cache.");
#endif /* _DEBUG */
Dictionary::Ptr comments = GetComments(); Dictionary::Ptr comments = GetComments();

View File

@ -18,6 +18,7 @@
******************************************************************************/ ******************************************************************************/
#include "icinga/service.h" #include "icinga/service.h"
#include "config/configitembuilder.h"
#include "base/dynamictype.h" #include "base/dynamictype.h"
#include "base/objectlock.h" #include "base/objectlock.h"
#include "base/logger_fwd.h" #include "base/logger_fwd.h"
@ -47,7 +48,8 @@ int Service::GetNextDowntimeID(void)
String Service::AddDowntime(const String& author, const String& comment, String Service::AddDowntime(const String& author, const String& comment,
double startTime, double endTime, bool fixed, 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; String uid;
@ -66,6 +68,7 @@ String Service::AddDowntime(const String& author, const String& comment,
downtime->SetFixed(fixed); downtime->SetFixed(fixed);
downtime->SetDuration(duration); downtime->SetDuration(duration);
downtime->SetTriggeredBy(triggeredBy); downtime->SetTriggeredBy(triggeredBy);
downtime->SetScheduledBy(scheduledBy);
int legacy_id; int legacy_id;
@ -82,11 +85,8 @@ String Service::AddDowntime(const String& author, const String& comment,
Downtime::Ptr otherDowntime = otherDowntimes->Get(triggeredBy); Downtime::Ptr otherDowntime = otherDowntimes->Get(triggeredBy);
Dictionary::Ptr triggers = otherDowntime->GetTriggers(); Dictionary::Ptr triggers = otherDowntime->GetTriggers();
{
ObjectLock olock(otherOwner);
triggers->Set(triggeredBy, triggeredBy); triggers->Set(triggeredBy, triggeredBy);
} }
}
GetDowntimes()->Set(uid, downtime); GetDowntimes()->Set(uid, downtime);
@ -96,7 +96,8 @@ String Service::AddDowntime(const String& author, const String& comment,
l_DowntimesCache[uid] = GetSelf(); 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); OnDowntimeAdded(GetSelf(), downtime, authority);
@ -129,7 +130,7 @@ void Service::RemoveDowntime(const String& id, bool cancelled, const String& aut
downtime->SetWasCancelled(cancelled); 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); OnDowntimeRemoved(owner, downtime, authority);
} }
@ -230,7 +231,9 @@ void Service::StartDowntimesExpiredTimer(void)
void Service::AddDowntimesToCache(void) void Service::AddDowntimesToCache(void)
{ {
#ifdef _DEBUG
Log(LogDebug, "icinga", "Updating Service downtimes cache."); Log(LogDebug, "icinga", "Updating Service downtimes cache.");
#endif /* _DEBUG */
Dictionary::Ptr downtimes = GetDowntimes(); Dictionary::Ptr downtimes = GetDowntimes();
@ -312,3 +315,65 @@ int Service::GetDowntimeDepth(void) const
return downtime_depth; 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();
}
}

View File

@ -46,9 +46,6 @@ void Service::Start(void)
{ {
VERIFY(GetHost()); VERIFY(GetHost());
AddDowntimesToCache();
AddCommentsToCache();
StartDowntimesExpiredTimer(); StartDowntimesExpiredTimer();
double now = Utility::GetTime(); double now = Utility::GetTime();
@ -80,10 +77,36 @@ void Service::OnConfigLoaded(void)
m_Host->AddService(GetSelf()); m_Host->AddService(GetSelf());
UpdateSlaveNotifications(); UpdateSlaveNotifications();
UpdateSlaveScheduledDowntimes();
SetSchedulingOffset(Utility::Random()); 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) Service::Ptr Service::GetByNamePair(const String& hostName, const String& serviceName)
{ {
if (!hostName.IsEmpty()) { if (!hostName.IsEmpty()) {

View File

@ -192,7 +192,8 @@ public:
String AddDowntime(const String& author, const String& comment, String AddDowntime(const String& author, const String& comment,
double startTime, double endTime, bool fixed, double startTime, double endTime, bool fixed,
const String& triggeredBy, double duration, 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()); static void RemoveDowntime(const String& id, bool cancelled, const String& = String());
@ -208,6 +209,8 @@ public:
bool IsInDowntime(void) const; bool IsInDowntime(void) const;
bool IsAcknowledged(void); bool IsAcknowledged(void);
void UpdateSlaveScheduledDowntimes(void);
/* Comments */ /* Comments */
static int GetNextCommentID(void); static int GetNextCommentID(void);
@ -265,6 +268,7 @@ protected:
virtual void Start(void); virtual void Start(void);
virtual void OnConfigLoaded(void); virtual void OnConfigLoaded(void);
virtual void OnStateLoaded(void);
private: private:
Host::Ptr m_Host; Host::Ptr m_Host;

View File

@ -59,6 +59,7 @@ class Service : DynamicObject
default {{{ return 30; }}} default {{{ return 30; }}}
}; };
[config] Dictionary::Ptr notifications (NotificationDescriptions); [config] Dictionary::Ptr notifications (NotificationDescriptions);
[config] Dictionary::Ptr scheduled_downtimes (ScheduledDowntimeDescriptions);
[config] bool enable_active_checks (EnableActiveChecksRaw) { [config] bool enable_active_checks (EnableActiveChecksRaw) {
default {{{ return true; }}} default {{{ return true; }}}
}; };

View File

@ -16,9 +16,9 @@
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
add_library(methods SHARED add_library(methods SHARED
icingachecktask.cpp legacytimeperiod.cpp nullchecktask.cpp icingachecktask.cpp nullchecktask.cpp nulleventtask.cpp
nulleventtask.cpp pluginchecktask.cpp plugineventtask.cpp pluginchecktask.cpp plugineventtask.cpp pluginnotificationtask.cpp
pluginnotificationtask.cpp randomchecktask.cpp timeperiodtask.cpp randomchecktask.cpp timeperiodtask.cpp
) )
target_link_libraries(methods ${Boost_LIBRARIES} base config icinga) target_link_libraries(methods ${Boost_LIBRARIES} base config icinga)