icinga2/lib/icinga/scheduleddowntime.cpp

258 lines
8.2 KiB
C++
Raw Normal View History

/******************************************************************************
* Icinga 2 *
2018-01-02 12:06:00 +01:00
* Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) *
* *
* 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. *
******************************************************************************/
2014-05-25 16:23:35 +02:00
#include "icinga/scheduleddowntime.hpp"
2018-01-18 13:50:38 +01:00
#include "icinga/scheduleddowntime-ti.cpp"
2014-05-25 16:23:35 +02:00
#include "icinga/legacytimeperiod.hpp"
#include "icinga/downtime.hpp"
#include "icinga/service.hpp"
#include "base/timer.hpp"
#include "base/configtype.hpp"
2014-05-25 16:23:35 +02:00
#include "base/utility.hpp"
#include "base/objectlock.hpp"
#include "base/convert.hpp"
2014-10-19 14:21:12 +02:00
#include "base/logger.hpp"
2014-05-25 16:23:35 +02:00
#include "base/exception.hpp"
#include <boost/thread/once.hpp>
using namespace icinga;
REGISTER_TYPE(ScheduledDowntime);
static Timer::Ptr l_Timer;
2014-11-06 19:35:47 +01:00
String ScheduledDowntimeNameComposer::MakeName(const String& shortName, const Object::Ptr& context) const
{
2014-11-06 19:35:47 +01:00
ScheduledDowntime::Ptr downtime = dynamic_pointer_cast<ScheduledDowntime>(context);
if (!downtime)
return "";
2014-11-06 19:35:47 +01:00
String name = downtime->GetHostName();
2014-11-06 19:35:47 +01:00
if (!downtime->GetServiceName().IsEmpty())
name += "!" + downtime->GetServiceName();
name += "!" + shortName;
return name;
}
Dictionary::Ptr ScheduledDowntimeNameComposer::ParseName(const String& name) const
{
2018-01-04 18:24:45 +01:00
std::vector<String> tokens = name.Split("!");
if (tokens.size() < 2)
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid ScheduledDowntime name."));
Dictionary::Ptr result = new Dictionary();
result->Set("host_name", tokens[0]);
if (tokens.size() > 2) {
result->Set("service_name", tokens[1]);
result->Set("name", tokens[2]);
} else {
result->Set("name", tokens[1]);
}
return result;
}
void ScheduledDowntime::OnAllConfigLoaded()
{
ObjectImpl<ScheduledDowntime>::OnAllConfigLoaded();
if (!GetCheckable())
BOOST_THROW_EXCEPTION(ScriptError("ScheduledDowntime '" + GetName() + "' references a host/service which doesn't exist.", GetDebugInfo()));
}
void ScheduledDowntime::Start(bool runtimeCreated)
{
ObjectImpl<ScheduledDowntime>::Start(runtimeCreated);
static boost::once_flag once = BOOST_ONCE_INIT;
2017-12-19 15:58:58 +01:00
boost::call_once(once, [this]() {
l_Timer = new Timer();
l_Timer->SetInterval(60);
l_Timer->OnTimerExpired.connect(std::bind(&ScheduledDowntime::TimerProc));
l_Timer->Start();
});
Utility::QueueAsyncCallback(std::bind(&ScheduledDowntime::CreateNextDowntime, this));
}
void ScheduledDowntime::TimerProc()
{
for (const ScheduledDowntime::Ptr& sd : ConfigType::GetObjectsByType<ScheduledDowntime>()) {
if (sd->IsActive())
sd->CreateNextDowntime();
}
}
Checkable::Ptr ScheduledDowntime::GetCheckable() const
{
Host::Ptr host = Host::GetByName(GetHostName());
if (GetServiceName().IsEmpty())
2014-04-03 15:36:13 +02:00
return host;
else
return host->GetServiceByShortName(GetServiceName());
}
std::pair<double, double> ScheduledDowntime::FindNextSegment()
{
time_t refts = Utility::GetTime();
tm reference = Utility::LocalTime(refts);
2014-10-19 17:52:17 +02:00
Log(LogDebug, "ScheduledDowntime")
<< "Finding next scheduled downtime segment for time " << refts;
Dictionary::Ptr ranges = GetRanges();
if (!ranges)
return std::make_pair(0, 0);
Array::Ptr segments = new Array();
Dictionary::Ptr bestSegment;
double bestBegin;
double now = Utility::GetTime();
ObjectLock olock(ranges);
for (const Dictionary::Pair& kv : ranges) {
Log(LogDebug, "ScheduledDowntime")
<< "Evaluating segment: " << kv.first << ": " << kv.second << " at ";
Dictionary::Ptr segment = LegacyTimePeriod::FindNextSegment(kv.first, kv.second, &reference);
if (!segment)
continue;
Log(LogDebug, "ScheduledDowntime")
<< "Considering segment: " << Utility::FormatDateTime("%c", segment->Get("begin")) << " -> " << Utility::FormatDateTime("%c", segment->Get("end"));
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()
{
for (const Downtime::Ptr& downtime : GetCheckable()->GetDowntimes()) {
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;
}
Log(LogDebug, "ScheduledDowntime")
<< "Creating new Downtime for ScheduledDowntime \"" << GetName() << "\"";
std::pair<double, double> segment = FindNextSegment();
if (segment.first == 0 && segment.second == 0)
return;
String downtimeName = Downtime::AddDowntime(GetCheckable(), GetAuthor(), GetComment(),
segment.first, segment.second,
GetFixed(), String(), GetDuration(), GetName(), GetName());
Downtime::Ptr downtime = Downtime::GetByName(downtimeName);
int childOptions = Downtime::ChildOptionsFromValue(GetChildOptions());
if (childOptions > 0) {
/* 'DowntimeTriggeredChildren' schedules child downtimes triggered by the parent downtime.
* 'DowntimeNonTriggeredChildren' schedules non-triggered downtimes for all children.
*/
String triggerName;
if (childOptions == 1)
triggerName = downtimeName;
Log(LogNotice, "ScheduledDowntime")
<< "Processing child options " << childOptions << " for downtime " << downtimeName;
for (const Checkable::Ptr& child : GetCheckable()->GetAllChildren()) {
Log(LogNotice, "ScheduledDowntime")
<< "Scheduling downtime for child object " << child->GetName();
String childDowntimeName = Downtime::AddDowntime(child, GetAuthor(), GetComment(),
segment.first, segment.second, GetFixed(), triggerName, GetDuration(), GetName(), GetName());
Log(LogNotice, "ScheduledDowntime")
<< "Add child downtime '" << childDowntimeName << "'.";
}
}
}
void ScheduledDowntime::ValidateRanges(const Lazy<Dictionary::Ptr>& lvalue, const ValidationUtils& utils)
{
ObjectImpl<ScheduledDowntime>::ValidateRanges(lvalue, utils);
if (!lvalue())
return;
/* create a fake time environment to validate the definitions */
time_t refts = Utility::GetTime();
tm reference = Utility::LocalTime(refts);
Array::Ptr segments = new Array();
ObjectLock olock(lvalue());
for (const Dictionary::Pair& kv : lvalue()) {
try {
tm begin_tm, end_tm;
int stride;
LegacyTimePeriod::ParseTimeRange(kv.first, &begin_tm, &end_tm, &stride, &reference);
} catch (const std::exception& ex) {
BOOST_THROW_EXCEPTION(ValidationError(this, { "ranges" }, "Invalid time specification '" + kv.first + "': " + ex.what()));
}
try {
LegacyTimePeriod::ProcessTimeRanges(kv.second, &reference, segments);
} catch (const std::exception& ex) {
BOOST_THROW_EXCEPTION(ValidationError(this, { "ranges" }, "Invalid time range definition '" + kv.second + "': " + ex.what()));
}
}
}
void ScheduledDowntime::ValidateChildOptions(const Lazy<Value>& lvalue, const ValidationUtils& utils)
{
ObjectImpl<ScheduledDowntime>::ValidateChildOptions(lvalue, utils);
try {
Downtime::ChildOptionsFromValue(lvalue());
} catch (const std::exception&) {
BOOST_THROW_EXCEPTION(ValidationError(this, { "child_options" }, "Invalid child_options specified"));
}
}