/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/checkable.hpp" #include "icinga/checkable-ti.cpp" #include "icinga/host.hpp" #include "icinga/service.hpp" #include "base/objectlock.hpp" #include "base/utility.hpp" #include "base/exception.hpp" #include "base/timer.hpp" #include using namespace icinga; REGISTER_TYPE_WITH_PROTOTYPE(Checkable, Checkable::GetPrototype()); INITIALIZE_ONCE(&Checkable::StaticInitialize); const std::map Checkable::m_FlappingStateFilterMap ({ {"OK", FlappingStateFilterOk}, {"Warning", FlappingStateFilterWarning}, {"Critical", FlappingStateFilterCritical}, {"Unknown", FlappingStateFilterUnknown}, {"Up", FlappingStateFilterOk}, {"Down", FlappingStateFilterCritical}, }); boost::signals2::signal Checkable::OnAcknowledgementSet; boost::signals2::signal Checkable::OnAcknowledgementCleared; boost::signals2::signal Checkable::OnFlappingChange; static Timer::Ptr l_CheckablesFireSuppressedNotifications; static Timer::Ptr l_CleanDeadlinedExecutions; thread_local std::function Checkable::ExecuteCommandProcessFinishedHandler; void Checkable::StaticInitialize() { /* fixed downtime start */ Downtime::OnDowntimeStarted.connect([](const Downtime::Ptr& downtime) { Checkable::NotifyFixedDowntimeStart(downtime); }); /* flexible downtime start */ Downtime::OnDowntimeTriggered.connect([](const Downtime::Ptr& downtime) { Checkable::NotifyFlexibleDowntimeStart(downtime); }); /* fixed/flexible downtime end */ Downtime::OnDowntimeRemoved.connect([](const Downtime::Ptr& downtime) { Checkable::NotifyDowntimeEnd(downtime); }); } Checkable::Checkable() { SetSchedulingOffset(Utility::Random()); } void Checkable::OnConfigLoaded() { ObjectImpl::OnConfigLoaded(); SetFlappingIgnoreStatesFilter(FilterArrayToInt(GetFlappingIgnoreStates(), m_FlappingStateFilterMap, ~0)); } void Checkable::OnAllConfigLoaded() { ObjectImpl::OnAllConfigLoaded(); Endpoint::Ptr endpoint = GetCommandEndpoint(); if (endpoint) { Zone::Ptr checkableZone = static_pointer_cast(GetZone()); if (checkableZone) { Zone::Ptr cmdZone = endpoint->GetZone(); if (cmdZone != checkableZone && cmdZone->GetParent() != checkableZone) { BOOST_THROW_EXCEPTION(ValidationError(this, { "command_endpoint" }, "Command endpoint must be in zone '" + checkableZone->GetName() + "' or in a direct child zone thereof.")); } } else { BOOST_THROW_EXCEPTION(ValidationError(this, { "command_endpoint" }, "Checkable with command endpoint requires a zone. Please check the troubleshooting documentation.")); } } } void Checkable::Start(bool runtimeCreated) { double now = Utility::GetTime(); { auto cr (GetLastCheckResult()); if (GetLastCheckStarted() > (cr ? cr->GetExecutionEnd() : 0.0)) { SetNextCheck(GetLastCheckStarted()); } } if (GetNextCheck() < now + 60) { double delta = std::min(GetCheckInterval(), 60.0); delta *= (double)std::rand() / RAND_MAX; SetNextCheck(now + delta); } ObjectImpl::Start(runtimeCreated); static boost::once_flag once = BOOST_ONCE_INIT; boost::call_once(once, []() { l_CheckablesFireSuppressedNotifications = new Timer(); l_CheckablesFireSuppressedNotifications->SetInterval(5); l_CheckablesFireSuppressedNotifications->OnTimerExpired.connect(&Checkable::FireSuppressedNotificationsTimer); l_CheckablesFireSuppressedNotifications->Start(); l_CleanDeadlinedExecutions = new Timer(); l_CleanDeadlinedExecutions->SetInterval(300); l_CleanDeadlinedExecutions->OnTimerExpired.connect(&Checkable::CleanDeadlinedExecutions); l_CleanDeadlinedExecutions->Start(); }); } void Checkable::AddGroup(const String& name) { std::unique_lock lock(m_CheckableMutex); Array::Ptr groups; auto *host = dynamic_cast(this); if (host) groups = host->GetGroups(); else groups = static_cast(this)->GetGroups(); if (groups && groups->Contains(name)) return; if (!groups) groups = new Array(); groups->Add(name); } AcknowledgementType Checkable::GetAcknowledgement() { auto avalue = static_cast(GetAcknowledgementRaw()); if (avalue != AcknowledgementNone) { double expiry = GetAcknowledgementExpiry(); if (expiry != 0 && expiry < Utility::GetTime()) { avalue = AcknowledgementNone; ClearAcknowledgement(""); } } return avalue; } bool Checkable::IsAcknowledged() const { return const_cast(this)->GetAcknowledgement() != AcknowledgementNone; } void Checkable::AcknowledgeProblem(const String& author, const String& comment, AcknowledgementType type, bool notify, bool persistent, double changeTime, double expiry, const MessageOrigin::Ptr& origin) { SetAcknowledgementRaw(type); SetAcknowledgementExpiry(expiry); if (notify && !IsPaused()) OnNotificationsRequested(this, NotificationAcknowledgement, GetLastCheckResult(), author, comment, nullptr); Log(LogInformation, "Checkable") << "Acknowledgement set for checkable '" << GetName() << "'."; OnAcknowledgementSet(this, author, comment, type, notify, persistent, changeTime, expiry, origin); SetAcknowledgementLastChange(changeTime); } void Checkable::ClearAcknowledgement(const String& removedBy, double changeTime, const MessageOrigin::Ptr& origin) { ObjectLock oLock (this); bool wasAcked = GetAcknowledgementRaw() != AcknowledgementNone; SetAcknowledgementRaw(AcknowledgementNone); SetAcknowledgementExpiry(0); Log(LogInformation, "Checkable") << "Acknowledgement cleared for checkable '" << GetName() << "'."; if (wasAcked) { OnAcknowledgementCleared(this, removedBy, changeTime, origin); SetAcknowledgementLastChange(changeTime); } } Endpoint::Ptr Checkable::GetCommandEndpoint() const { return Endpoint::GetByName(GetCommandEndpointRaw()); } int Checkable::GetSeverity() const { /* overridden in Host/Service class. */ return 0; } bool Checkable::GetProblem() const { auto cr (GetLastCheckResult()); return cr && !IsStateOK(cr->GetState()); } bool Checkable::GetHandled() const { return GetProblem() && (IsInDowntime() || IsAcknowledged()); } Timestamp Checkable::GetNextUpdate() const { auto cr (GetLastCheckResult()); double interval, latency; // TODO: Document this behavior. if (cr) { interval = GetEnableActiveChecks() && GetProblem() && GetStateType() == StateTypeSoft ? GetRetryInterval() : GetCheckInterval(); latency = cr->GetExecutionEnd() - cr->GetScheduleStart(); } else { interval = GetCheckInterval(); latency = 0.0; } return (GetEnableActiveChecks() ? GetNextCheck() : (cr ? cr->GetExecutionEnd() : Application::GetStartTime()) + interval) + interval + 2 * latency; } void Checkable::NotifyFixedDowntimeStart(const Downtime::Ptr& downtime) { if (!downtime->GetFixed()) return; NotifyDowntimeInternal(downtime); } void Checkable::NotifyFlexibleDowntimeStart(const Downtime::Ptr& downtime) { if (downtime->GetFixed()) return; NotifyDowntimeInternal(downtime); } void Checkable::NotifyDowntimeInternal(const Downtime::Ptr& downtime) { Checkable::Ptr checkable = downtime->GetCheckable(); if (!checkable->IsPaused()) OnNotificationsRequested(checkable, NotificationDowntimeStart, checkable->GetLastCheckResult(), downtime->GetAuthor(), downtime->GetComment(), nullptr); } void Checkable::NotifyDowntimeEnd(const Downtime::Ptr& downtime) { /* don't send notifications for downtimes which never triggered */ if (!downtime->IsTriggered()) return; Checkable::Ptr checkable = downtime->GetCheckable(); if (!checkable->IsPaused()) OnNotificationsRequested(checkable, NotificationDowntimeEnd, checkable->GetLastCheckResult(), downtime->GetAuthor(), downtime->GetComment(), nullptr); } void Checkable::ValidateCheckInterval(const Lazy& lvalue, const ValidationUtils& utils) { ObjectImpl::ValidateCheckInterval(lvalue, utils); if (lvalue() <= 0) BOOST_THROW_EXCEPTION(ValidationError(this, { "check_interval" }, "Interval must be greater than 0.")); } void Checkable::ValidateRetryInterval(const Lazy& lvalue, const ValidationUtils& utils) { ObjectImpl::ValidateRetryInterval(lvalue, utils); if (lvalue() <= 0) BOOST_THROW_EXCEPTION(ValidationError(this, { "retry_interval" }, "Interval must be greater than 0.")); } void Checkable::ValidateMaxCheckAttempts(const Lazy& lvalue, const ValidationUtils& utils) { ObjectImpl::ValidateMaxCheckAttempts(lvalue, utils); if (lvalue() <= 0) BOOST_THROW_EXCEPTION(ValidationError(this, { "max_check_attempts" }, "Value must be greater than 0.")); } void Checkable::CleanDeadlinedExecutions(const Timer * const&) { double now = Utility::GetTime(); Dictionary::Ptr executions; Dictionary::Ptr execution; for (auto& host : ConfigType::GetObjectsByType()) { executions = host->GetExecutions(); if (executions) { for (const String& key : executions->GetKeys()) { execution = executions->Get(key); if (execution->Contains("deadline") && now > execution->Get("deadline")) { executions->Remove(key); } } } } for (auto& service : ConfigType::GetObjectsByType()) { executions = service->GetExecutions(); if (executions) { for (const String& key : executions->GetKeys()) { execution = executions->Get(key); if (execution->Contains("deadline") && now > execution->Get("deadline")) { executions->Remove(key); } } } } }