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(),
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") {

View 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

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)
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)

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"] = {
templates = [ "generic-service" ],
check_command = "load"
check_command = "load",
scheduled_downtimes["backup"] = {
templates = [ "backup-downtime" ]
}
},
services["processes"] = {

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

View 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 */
}

View File

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

View 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());

View 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

View 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);

View 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

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

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

View File

@ -17,13 +17,14 @@
* 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);
}

View 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);

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)
{
#ifdef _DEBUG
Log(LogDebug, "icinga", "Updating Service comments cache.");
#endif /* _DEBUG */
Dictionary::Ptr comments = GetComments();

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

View 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()) {

View 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;

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

View 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)