Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions lib/icinga/legacytimeperiod.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,56 @@ void LegacyTimePeriod::ProcessTimeRanges(const String& timeranges, tm *reference
}
}

Dictionary::Ptr LegacyTimePeriod::FindRunningSegment(const String& daydef, const String& timeranges, tm *reference)
{
tm begin, end, iter;
time_t tsend, tsiter, tsref;
int stride;

tsref = mktime(reference);

ParseTimeRange(daydef, &begin, &end, &stride, reference);

iter = begin;

tsend = mktime(&end);

do {
if (IsInTimeRange(&begin, &end, stride, &iter)) {
Array::Ptr segments = new Array();
ProcessTimeRanges(timeranges, &iter, segments);

Dictionary::Ptr bestSegment;
double bestEnd;

ObjectLock olock(segments);
for (const Dictionary::Ptr& segment : segments) {
double begin = segment->Get("begin");
double end = segment->Get("end");

if (begin >= tsref || end < tsref)
continue;

if (!bestSegment || end > bestEnd) {
bestSegment = segment;
bestEnd = end;
}
}

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

Dictionary::Ptr LegacyTimePeriod::FindNextSegment(const String& daydef, const String& timeranges, tm *reference)
{
tm begin, end, iter, ref;
Expand Down
1 change: 1 addition & 0 deletions lib/icinga/legacytimeperiod.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class LegacyTimePeriod
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);
static Dictionary::Ptr FindRunningSegment(const String& daydef, const String& timeranges, tm *reference);

private:
LegacyTimePeriod();
Expand Down
143 changes: 124 additions & 19 deletions lib/icinga/scheduleddowntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,14 @@ Checkable::Ptr ScheduledDowntime::GetCheckable() const
return host->GetServiceByShortName(GetServiceName());
}

std::pair<double, double> ScheduledDowntime::FindNextSegment()
std::pair<double, double> ScheduledDowntime::FindRunningSegment(double minEnd)
{
time_t refts = Utility::GetTime();
tm reference = Utility::LocalTime(refts);

Log(LogDebug, "ScheduledDowntime")
<< "Finding next scheduled downtime segment for time " << refts;
<< "Finding running scheduled downtime segment for time " << refts
<< " (minEnd " << (minEnd > 0 ? Utility::FormatDateTime("%c", minEnd) : "-") << ")";

Dictionary::Ptr ranges = GetRanges();

Expand All @@ -132,42 +133,151 @@ std::pair<double, double> ScheduledDowntime::FindNextSegment()
Array::Ptr segments = new Array();

Dictionary::Ptr bestSegment;
double bestBegin;
double bestBegin, bestEnd;
double now = Utility::GetTime();

ObjectLock olock(ranges);

/* Find the longest lasting (and longer than minEnd, if given) segment that's already running */
for (const Dictionary::Pair& kv : ranges) {
Log(LogDebug, "ScheduledDowntime")
<< "Evaluating segment: " << kv.first << ": " << kv.second << " at ";
<< "Evaluating (running?) segment: " << kv.first << ": " << kv.second;

Dictionary::Ptr segment = LegacyTimePeriod::FindNextSegment(kv.first, kv.second, &reference);
Dictionary::Ptr segment = LegacyTimePeriod::FindRunningSegment(kv.first, kv.second, &reference);

if (!segment)
continue;

double begin = segment->Get("begin");
double end = segment->Get("end");

Log(LogDebug, "ScheduledDowntime")
<< "Considering segment: " << Utility::FormatDateTime("%c", segment->Get("begin")) << " -> " << Utility::FormatDateTime("%c", segment->Get("end"));
<< "Considering (running?) segment: " << Utility::FormatDateTime("%c", begin) << " -> " << Utility::FormatDateTime("%c", end);

if (begin >= now || end < now) {
Log(LogDebug, "ScheduledDowntime") << "not running.";
continue;
}
if (minEnd && end <= minEnd) {
Log(LogDebug, "ScheduledDowntime") << "ending too early.";
continue;
}

if (!bestSegment || end > bestEnd) {
Log(LogDebug, "ScheduledDowntime") << "(best match yet)";
bestSegment = segment;
bestBegin = begin;
bestEnd = end;
}
}

if (bestSegment)
return std::make_pair(bestBegin, bestEnd);

return std::make_pair(0, 0);
}

std::pair<double, double> ScheduledDowntime::FindNextSegment(double minBegin)
{
time_t refts = Utility::GetTime();
tm reference = Utility::LocalTime(refts);

Log(LogDebug, "ScheduledDowntime")
<< "Finding next scheduled downtime segment for time " << refts
<< " (minBegin " << (minBegin > 0 ? Utility::FormatDateTime("%c", minBegin) : "-") << ")";

Dictionary::Ptr ranges = GetRanges();

if (!ranges)
return std::make_pair(0, 0);

Array::Ptr segments = new Array();

Dictionary::Ptr bestSegment;
double bestBegin, bestEnd;
double now = Utility::GetTime();

ObjectLock olock(ranges);

/* Find the segment starting earliest, but not earlier than minBegin */
for (const Dictionary::Pair& kv : ranges) {
Log(LogDebug, "ScheduledDowntime")
<< "Evaluating segment: " << kv.first << ": " << kv.second;

Dictionary::Ptr segment = LegacyTimePeriod::FindNextSegment(kv.first, kv.second, &reference);

if (!segment)
continue;

double begin = segment->Get("begin");
double end = segment->Get("end");

if (begin < now)
Log(LogDebug, "ScheduledDowntime")
<< "Considering segment: " << Utility::FormatDateTime("%c", begin) << " -> " << Utility::FormatDateTime("%c", end);

if (begin < now) {
Log(LogDebug, "ScheduledDowntime") << "already running.";
continue;
}
if (minBegin && begin < minBegin) {
Log(LogDebug, "ScheduledDowntime") << "beginning to early.";
continue;
}

if (!bestSegment || begin < bestBegin) {
Log(LogDebug, "ScheduledDowntime") << "(best match yet)";
bestSegment = segment;
bestBegin = begin;
bestEnd = end;
}
}

if (bestSegment)
return std::make_pair(bestSegment->Get("begin"), bestSegment->Get("end"));
else
return std::make_pair(0, 0);
return std::make_pair(bestBegin, bestEnd);

return std::make_pair(0, 0);
}

void ScheduledDowntime::CreateNextDowntime()
{
/* Try to merge the next segment into a running downtime */
Log(LogDebug, "ScheduledDowntime") << "Try merge";
for (const Downtime::Ptr& downtime : GetCheckable()->GetDowntimes()) {
double current_end;
if (downtime->GetScheduledBy() != GetName()) {
Log(LogDebug, "ScheduledDowntime") << "Not by us (" << downtime->GetScheduledBy() << " != " << GetName() << ")";
continue;
}
current_end = downtime->GetEndTime();
if (current_end > Utility::GetTime() + 12*60*60) {
Log(LogDebug, "ScheduledDowntime") << "By us, long-running (" << Utility::FormatDateTime("%c", current_end) << ")";
/* return anyway, don't queue a new downtime now */
return;
} else {
Log(LogDebug, "ScheduledDowntime") << "By us, ends soon (" << Utility::FormatDateTime("%c", current_end) << ")";
std::pair<double, double> segment = FindNextSegment(current_end);
/* Merge an immediately following segment */
if (segment.first == 0) {
Log(LogDebug, "ScheduledDowntime") << "No next Segment";
} else if (segment.first == current_end) {
Log(LogDebug, "ScheduledDowntime") << "Next Segment fits, extending end time " << Utility::FormatDateTime("%c", current_end) << " to " << Utility::FormatDateTime("%c", segment.second);
downtime->SetEndTime(segment.second, false);
return;
} else {
Log(LogDebug, "ScheduledDowntime") << "Next Segment doesn't fit: " << Utility::FormatDateTime("%c", segment.first) << " != " << Utility::FormatDateTime("%c", current_end);
continue;
}
}
}
Log(LogDebug, "ScheduledDowntime") << "No merge";

double minEnd = 0;

for (const Downtime::Ptr& downtime : GetCheckable()->GetDowntimes()) {
double end = downtime->GetEndTime();
if (end > minEnd)
minEnd = end;

if (downtime->GetScheduledBy() != GetName() ||
downtime->GetStartTime() < Utility::GetTime())
continue;
Expand All @@ -179,16 +289,11 @@ void ScheduledDowntime::CreateNextDowntime()
Log(LogDebug, "ScheduledDowntime")
<< "Creating new Downtime for ScheduledDowntime \"" << GetName() << "\"";

std::pair<double, double> segment = FindNextSegment();

std::pair<double, double> segment = FindRunningSegment(minEnd);
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;
segment = FindNextSegment();
if (segment.first == 0 && segment.second == 0)
return;
}

String downtimeName = Downtime::AddDowntime(GetCheckable(), GetAuthor(), GetComment(),
Expand Down
3 changes: 2 additions & 1 deletion lib/icinga/scheduleddowntime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ class ScheduledDowntime final : public ObjectImpl<ScheduledDowntime>
private:
static void TimerProc();

std::pair<double, double> FindNextSegment();
std::pair<double, double> FindRunningSegment(double minEnd = 0);
std::pair<double, double> FindNextSegment(double minBegin = 0);
void CreateNextDowntime();

static bool EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, const String& name, ScriptFrame& frame, const ApplyRule& rule);
Expand Down