Implement timeperiods.

This commit is contained in:
Gunnar Beutner 2013-03-13 16:04:53 +01:00
parent 6c9de84683
commit 442a2dbc7d
19 changed files with 408 additions and 16 deletions

View File

@ -98,11 +98,24 @@ void CheckerComponent::CheckThreadProc(void)
m_IdleServices.erase(service);
/* reschedule the service if checks are currently disabled
* for it and this is not a forced check */
if (!service->GetEnableActiveChecks() && !service->GetForceNextCheck()) {
Logger::Write(LogDebug, "checker", "Ignoring service check for disabled service: " + service->GetName());
bool check = true;
if (!service->GetForceNextCheck()) {
if (!service->GetEnableActiveChecks()) {
Logger::Write(LogDebug, "checker", "Skipping check for service '" + service->GetName() + "': active checks are disabled");
check = false;
}
TimePeriod::Ptr tp = service->GetCheckPeriod();
if (tp && !tp->IsInside(Utility::GetTime())) {
Logger::Write(LogDebug, "checker", "Skipping check for service '" + service->GetName() + "': not in check_period");
check = false;
}
}
/* reschedule the service if checks are disabled */
if (!check) {
service->UpdateNextCheck();
typedef nth_index<ServiceSet, 1>::type CheckTimeView;

View File

@ -95,7 +95,7 @@ shared_ptr<T> DynamicObjectFactory(const Dictionary::Ptr& serializedUpdate)
}
#define REGISTER_TYPE_ALIAS(type, alias) \
static icinga::RegisterTypeHelper g_RegisterDT_ ## type(alias, DynamicObjectFactory<type>)
static icinga::RegisterTypeHelper g_RegisterDT_ ## type(alias, DynamicObjectFactory<type>);
#define REGISTER_TYPE(type) \
REGISTER_TYPE_ALIAS(type, #type)

View File

@ -78,7 +78,9 @@ public:
};
#define REGISTER_SCRIPTFUNCTION(name, callback) \
static icinga::RegisterFunctionHelper g_RegisterSF_ ## type(name, callback)
static icinga::RegisterFunctionHelper g_RegisterSF_ ## name(#name, callback)
#undef MKSYMBOL
}

View File

@ -90,4 +90,6 @@ private:
# define ASSERT(expr)
#endif /* _DEBUG */
#define CONCAT(a, b) a ## b
#endif /* UTILITY_H */

View File

@ -50,6 +50,8 @@ libicinga_la_SOURCES = \
service.h \
servicegroup.cpp \
servicegroup.h \
timeperiod.cpp \
timeperiod.h \
user.cpp \
user.h \
usergroup.cpp \

View File

@ -21,7 +21,7 @@
using namespace icinga;
REGISTER_SCRIPTFUNCTION("GetAnswerToEverything", &API::GetAnswerToEverything);
REGISTER_SCRIPTFUNCTION(GetAnswerToEverything, &API::GetAnswerToEverything);
/**
* @threadsafety Always.

View File

@ -26,7 +26,7 @@ map<String, map<String, Service::WeakPtr> > Host::m_ServicesCache;
bool Host::m_ServicesCacheNeedsUpdate = false;
Timer::Ptr Host::m_ServicesCacheTimer;
REGISTER_SCRIPTFUNCTION("ValidateServiceDictionary", &Host::ValidateServiceDictionary);
REGISTER_SCRIPTFUNCTION(ValidateServiceDictionary, &Host::ValidateServiceDictionary);
REGISTER_TYPE(Host);
@ -191,6 +191,10 @@ static void CopyServiceAttributes(TDict serviceDesc, const ConfigItemBuilder::Pt
if (!notification_interval.IsEmpty())
builder->AddExpression("notification_interval", OperatorSet, notification_interval);
Value check_period = serviceDesc->Get("check_period");
if (!check_period.IsEmpty())
builder->AddExpression("check_period", OperatorSet, check_period);
if (copyServiceAttrs) {
Value servicedependencies = serviceDesc->Get("servicedependencies");
if (!servicedependencies.IsEmpty())

View File

@ -42,10 +42,10 @@ using boost::algorithm::is_any_of;
#include "externalcommandprocessor.h"
#include "endpoint.h"
#include "endpointmanager.h"
#include "icingaapplication.h"
#include "timeperiod.h"
#include "user.h"
#include "usergroup.h"

View File

@ -51,6 +51,7 @@ type Host {
%attribute string "*"
},
%attribute string "check_period",
%attribute number "check_interval",
%attribute number "retry_interval",
@ -99,6 +100,7 @@ type Host {
/* service attributes */
%attribute number "max_check_attempts",
%attribute string "check_period",
%attribute number "check_interval",
%attribute number "retry_interval",
%attribute number "notification_interval",
@ -144,6 +146,7 @@ type Service {
},
%attribute string "check_command",
%attribute number "max_check_attempts",
%attribute string "check_period",
%attribute number "check_interval",
%attribute number "retry_interval",
%attribute dictionary "hostdependencies" {
@ -248,3 +251,11 @@ type UserGroup {
%attribute string "action_url",
%attribute string "notes_url"
}
type TimePeriod {
%require "methods",
%attribute dictionary "methods" {
%require "update",
%attribute string "update"
},
}

View File

@ -50,6 +50,7 @@
<ClCompile Include="service-comment.cpp" />
<ClCompile Include="service-downtime.cpp" />
<ClCompile Include="servicegroup.cpp" />
<ClCompile Include="timeperiod.cpp" />
<ClCompile Include="user.cpp" />
<ClCompile Include="usergroup.cpp" />
</ItemGroup>
@ -70,6 +71,7 @@
<ClInclude Include="pluginnotificationtask.h" />
<ClInclude Include="service.h" />
<ClInclude Include="servicegroup.h" />
<ClInclude Include="timeperiod.h" />
<ClInclude Include="user.h" />
<ClInclude Include="usergroup.h" />
</ItemGroup>
@ -240,4 +242,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -213,8 +213,7 @@ void Notification::BeginExecuteNotificationHelper(const Dictionary::Ptr& notific
arguments.push_back(macros);
arguments.push_back(type);
ScriptTask::Ptr task;
task = MakeMethodTask("notify", arguments);
ScriptTask::Ptr task = MakeMethodTask("notify", arguments);
if (!task) {
Logger::Write(LogWarning, "icinga", "Notification object '" + GetName() + "' doesn't have a 'notify' method.");

View File

@ -21,7 +21,7 @@
using namespace icinga;
REGISTER_SCRIPTFUNCTION("NullCheck", &NullCheckTask::ScriptFunc);
REGISTER_SCRIPTFUNCTION(NullCheck, &NullCheckTask::ScriptFunc);
/**
* @threadsafety Always.

View File

@ -21,7 +21,7 @@
using namespace icinga;
REGISTER_SCRIPTFUNCTION("PluginCheck", &PluginCheckTask::ScriptFunc);
REGISTER_SCRIPTFUNCTION(PluginCheck, &PluginCheckTask::ScriptFunc);
PluginCheckTask::PluginCheckTask(const ScriptTask::Ptr& task, const Process::Ptr& process, const Value& command)
: m_Task(task), m_Process(process), m_Command(command)

View File

@ -21,7 +21,7 @@
using namespace icinga;
REGISTER_SCRIPTFUNCTION("PluginNotification", &PluginNotificationTask::ScriptFunc);
REGISTER_SCRIPTFUNCTION(PluginNotification, &PluginNotificationTask::ScriptFunc);
PluginNotificationTask::PluginNotificationTask(const ScriptTask::Ptr& task, const Process::Ptr& process,
const String& service, const String& command)

View File

@ -47,6 +47,14 @@ long Service::GetMaxCheckAttempts(void) const
return m_MaxCheckAttempts;
}
/**
* @threadsafety Always.
*/
TimePeriod::Ptr Service::GetCheckPeriod(void) const
{
return TimePeriod::GetByName(m_CheckPeriod);
}
/**
* @threadsafety Always.
*/

View File

@ -35,6 +35,7 @@ Service::Service(const Dictionary::Ptr& serializedObject)
RegisterAttribute("check_command", Attribute_Config, &m_CheckCommand);
RegisterAttribute("max_check_attempts", Attribute_Config, &m_MaxCheckAttempts);
RegisterAttribute("check_period", Attribute_Config, &m_CheckPeriod);
RegisterAttribute("check_interval", Attribute_Config, &m_CheckInterval);
RegisterAttribute("retry_interval", Attribute_Config, &m_RetryInterval);
RegisterAttribute("checkers", Attribute_Config, &m_Checkers);

View File

@ -110,6 +110,7 @@ public:
Dictionary::Ptr GetCheckers(void) const;
Value GetCheckCommand(void) const;
long GetMaxCheckAttempts(void) const;
TimePeriod::Ptr GetCheckPeriod(void) const;
double GetCheckInterval(void) const;
double GetRetryInterval(void) const;
@ -264,6 +265,7 @@ private:
/* Checks */
Attribute<Value> m_CheckCommand;
Attribute<long> m_MaxCheckAttempts;
Attribute<String> m_CheckPeriod;
Attribute<double> m_CheckInterval;
Attribute<double> m_RetryInterval;
Attribute<double> m_NextCheck;

280
lib/icinga/timeperiod.cpp Normal file
View File

@ -0,0 +1,280 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012 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 "i2-icinga.h"
using namespace icinga;
REGISTER_TYPE(TimePeriod);
REGISTER_SCRIPTFUNCTION(EmptyTimePeriod, &TimePeriod::EmptyTimePeriodUpdate);
REGISTER_SCRIPTFUNCTION(EvenMinutesTimePeriod, &TimePeriod::EvenMinutesTimePeriodUpdate);
Timer::Ptr TimePeriod::m_UpdateTimer;
TimePeriod::TimePeriod(const Dictionary::Ptr& serializedUpdate)
: DynamicObject(serializedUpdate)
{
RegisterAttribute("valid_begin", Attribute_Replicated, &m_ValidBegin);
RegisterAttribute("valid_end", Attribute_Replicated, &m_ValidEnd);
RegisterAttribute("segments", Attribute_Replicated, &m_Segments);
if (!m_UpdateTimer) {
m_UpdateTimer = boost::make_shared<Timer>();
m_UpdateTimer->SetInterval(300);
m_UpdateTimer->OnTimerExpired.connect(boost::bind(&TimePeriod::UpdateTimerHandler));
m_UpdateTimer->Start();
}
}
void TimePeriod::Start(void)
{
/* Pre-fill the time period for the next 24 hours. */
double now = Utility::GetTime();
UpdateRegion(now, now + 24 * 3600);
}
/**
* @threadsafety Always.
*/
TimePeriod::Ptr TimePeriod::GetByName(const String& name)
{
DynamicObject::Ptr configObject = DynamicObject::GetObject("TimePeriod", name);
return dynamic_pointer_cast<TimePeriod>(configObject);
}
void TimePeriod::AddSegment(double begin, double end)
{
ASSERT(OwnsLock());
if (m_ValidBegin.IsEmpty() || begin < m_ValidBegin)
m_ValidBegin = begin;
if (m_ValidEnd.IsEmpty() || end > m_ValidEnd)
m_ValidEnd = end;
Array::Ptr segments = m_Segments;
if (segments) {
/* Try to merge the new segment into an existing segment. */
ObjectLock dlock(segments);
BOOST_FOREACH(const Dictionary::Ptr& segment, segments) {
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. */
return;
}
if (segment->Get("begin") > begin && segment->Get("begin") < end) {
segment->Set("begin", begin); /* Extend an existing segment. */
return;
}
}
}
/* Create new segment if we weren't able to merge this into an existing segment. */
Dictionary::Ptr segment = boost::make_shared<Dictionary>();
segment->Set("begin", begin);
segment->Set("end", end);
if (!segments) {
segments = boost::make_shared<Array>();
m_Segments = segments;
}
segments->Add(segment);
Touch("segments");
}
void TimePeriod::RemoveSegment(double begin, double end)
{
ASSERT(OwnsLock());
if (m_ValidBegin.IsEmpty() || begin < m_ValidBegin)
m_ValidBegin = begin;
if (m_ValidEnd.IsEmpty() || end > m_ValidEnd)
m_ValidEnd = end;
Array::Ptr segments = m_Segments;
if (!segments)
return;
/* Try to split or adjust an existing segment. */
ObjectLock dlock(segments);
BOOST_FOREACH(const Dictionary::Ptr& segment, segments) {
BOOST_THROW_EXCEPTION(runtime_error("Not implemented."));
}
Touch("segments");
}
void TimePeriod::PurgeSegments(double end)
{
ASSERT(OwnsLock());
if (m_ValidBegin.IsEmpty() || end < m_ValidBegin)
return;
m_ValidBegin = end;
Array::Ptr segments = m_Segments;
if (!segments)
return;
Array::Ptr newSegments = boost::make_shared<Array>();
/* Remove old segments. */
ObjectLock dlock(segments);
BOOST_FOREACH(const Dictionary::Ptr& segment, segments) {
if (segment->Get("end") >= end)
newSegments->Add(segment);
}
m_Segments = newSegments;
Touch("segments");
}
void TimePeriod::UpdateRegion(double begin, double end)
{
if (begin < m_ValidEnd)
begin = m_ValidEnd;
if (end < m_ValidEnd)
return;
TimePeriod::Ptr self = GetSelf();
vector<Value> arguments;
arguments.push_back(self);
arguments.push_back(begin);
arguments.push_back(end);
ScriptTask::Ptr task = MakeMethodTask("update", arguments);
if (!task) {
Logger::Write(LogWarning, "icinga", "TimePeriod object '" + GetName() + "' doesn't have an 'update' method.");
return;
}
task->Start();
task->GetResult();
}
bool TimePeriod::IsInside(double ts) const
{
ObjectLock olock(this);
if (m_ValidBegin.IsEmpty() || ts < m_ValidBegin || m_ValidEnd.IsEmpty() || ts > m_ValidEnd)
return true; /* Assume that all invalid regions are "inside". */
Array::Ptr segments = m_Segments;
if (segments) {
ObjectLock dlock(segments);
BOOST_FOREACH(const Dictionary::Ptr& segment, segments) {
if (ts > segment->Get("begin") && ts < segment->Get("end"))
return true;
}
}
return false;
}
double TimePeriod::FindNextTransition(double begin)
{
ObjectLock olock(this);
Array::Ptr segments = m_Segments;
double closestTransition = -1;
if (segments) {
ObjectLock dlock(segments);
BOOST_FOREACH(const Dictionary::Ptr& segment, segments) {
if (segment->Get("begin") > begin && (segment->Get("begin") < closestTransition || closestTransition == -1))
closestTransition = segment->Get("begin");
if (segment->Get("end") > begin && (segment->Get("end") < closestTransition || closestTransition == -1))
closestTransition = segment->Get("end");
}
}
return closestTransition;
}
void TimePeriod::UpdateTimerHandler(void)
{
double now = Utility::GetTime();
BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("TimePeriod")) {
TimePeriod::Ptr tp = static_pointer_cast<TimePeriod>(object);
/* Only update time periods that have been defined on this node. */
if (!ConfigItem::GetObject("TimePeriod", tp->GetName()))
continue;
tp->PurgeSegments(now - 3600);
if (tp->m_ValidEnd < now + 3 * 3600)
tp->UpdateRegion(tp->m_ValidEnd, tp->m_ValidEnd + 24 * 3600);
}
}
void TimePeriod::EmptyTimePeriodUpdate(const ScriptTask::Ptr& task, const vector<Value>& arguments)
{
if (arguments.size() < 3)
BOOST_THROW_EXCEPTION(runtime_error("Expected 3 arguments."));
TimePeriod::Ptr tp = arguments[0];
double begin = arguments[1];
double end = arguments[2];
ObjectLock olock(tp);
tp->RemoveSegment(begin, end);
task->FinishResult(Empty);
}
void TimePeriod::EvenMinutesTimePeriodUpdate(const ScriptTask::Ptr& task, const vector<Value>& arguments)
{
if (arguments.size() < 3)
BOOST_THROW_EXCEPTION(runtime_error("Expected 3 arguments."));
TimePeriod::Ptr tp = arguments[0];
double begin = arguments[1];
double end = arguments[2];
{
ObjectLock olock(tp);
tp->RemoveSegment(begin, end);
for (long t = begin; t < end; t += 60) {
if ((t / 60) % 2 == 0)
tp->AddSegment(t, t + 60);
}
}
task->FinishResult(Empty);
}

66
lib/icinga/timeperiod.h Normal file
View File

@ -0,0 +1,66 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012 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 TIMEPERIOD_H
#define TIMEPERIOD_H
namespace icinga
{
/**
* A time period.
*
* @ingroup icinga
*/
class I2_ICINGA_API TimePeriod : public DynamicObject
{
public:
typedef shared_ptr<TimePeriod> Ptr;
typedef weak_ptr<TimePeriod> WeakPtr;
TimePeriod(const Dictionary::Ptr& serializedUpdate);
static TimePeriod::Ptr GetByName(const String& name);
virtual void Start(void);
void AddSegment(double s, double end);
void RemoveSegment(double begin, double end);
void PurgeSegments(double end);
void UpdateRegion(double begin, double end);
bool IsInside(double ts) const;
double FindNextTransition(double begin);
static void EmptyTimePeriodUpdate(const ScriptTask::Ptr& task, const vector<Value>& arguments);
static void EvenMinutesTimePeriodUpdate(const ScriptTask::Ptr& task, const vector<Value>& arguments);
private:
Attribute<double> m_ValidBegin;
Attribute<double> m_ValidEnd;
Attribute<Array::Ptr> m_Segments;
static Timer::Ptr m_UpdateTimer;
static void UpdateTimerHandler(void);
};
}
#endif /* TIMEPERIOD_H */