1 /******************************************************************************
3 * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License *
7 * as published by the Free Software Foundation; either version 2 *
8 * of the License, or (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software Foundation *
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ******************************************************************************/
20 #include "i2-icinga.h"
22 using namespace icinga;
24 const int Service::DefaultMaxCheckAttempts = 3;
25 const int Service::DefaultCheckInterval = 5 * 60;
26 const int Service::CheckIntervalDivisor = 5;
28 signals2::signal<void (const Service::Ptr&, const String&)> Service::OnCheckerChanged;
29 signals2::signal<void (const Service::Ptr&, const Value&)> Service::OnNextCheckChanged;
31 Value Service::GetCheckCommand(void) const
33 return m_CheckCommand;
36 long Service::GetMaxCheckAttempts(void) const
38 if (m_MaxCheckAttempts.IsEmpty())
39 return DefaultMaxCheckAttempts;
41 return m_MaxCheckAttempts;
44 double Service::GetCheckInterval(void) const
46 if (m_CheckInterval.IsEmpty())
47 return DefaultCheckInterval;
49 return m_CheckInterval;
52 double Service::GetRetryInterval(void) const
54 if (m_RetryInterval.IsEmpty())
55 return GetCheckInterval() / CheckIntervalDivisor;
57 return m_RetryInterval;
60 Dictionary::Ptr Service::GetCheckers(void) const
65 void Service::SetSchedulingOffset(long offset)
67 m_SchedulingOffset = offset;
70 long Service::GetSchedulingOffset(void)
72 return m_SchedulingOffset;
75 void Service::SetNextCheck(double nextCheck)
77 m_NextCheck = nextCheck;
81 double Service::GetNextCheck(void)
83 if (m_NextCheck.IsEmpty()) {
86 if (m_NextCheck.IsEmpty())
87 BOOST_THROW_EXCEPTION(runtime_error("Failed to schedule next check."));
93 void Service::UpdateNextCheck(void)
97 if (GetStateType() == StateTypeSoft)
98 interval = GetRetryInterval();
100 interval = GetCheckInterval();
102 double now = Utility::GetTime();
106 adj = fmod(now + GetSchedulingOffset(), interval);
108 SetNextCheck(now - adj + interval);
111 void Service::SetCurrentChecker(const String& checker)
113 m_CurrentChecker = checker;
114 Touch("current_checker");
117 String Service::GetCurrentChecker(void) const
119 return m_CurrentChecker;
122 void Service::SetCurrentCheckAttempt(long attempt)
124 m_CheckAttempt = attempt;
125 Touch("check_attempt");
128 long Service::GetCurrentCheckAttempt(void) const
130 if (m_CheckAttempt.IsEmpty())
133 return m_CheckAttempt;
136 void Service::SetState(ServiceState state)
138 m_State = static_cast<long>(state);
142 ServiceState Service::GetState(void) const
144 if (m_State.IsEmpty())
147 int ivalue = static_cast<int>(m_State);
148 return static_cast<ServiceState>(ivalue);
151 void Service::SetStateType(ServiceStateType type)
153 m_StateType = static_cast<long>(type);
157 ServiceStateType Service::GetStateType(void) const
159 if (m_StateType.IsEmpty())
160 return StateTypeSoft;
162 int ivalue = static_cast<int>(m_StateType);
163 return static_cast<ServiceStateType>(ivalue);
166 void Service::SetLastCheckResult(const Dictionary::Ptr& result)
168 m_LastResult = result;
169 Touch("last_result");
172 Dictionary::Ptr Service::GetLastCheckResult(void) const
177 void Service::SetLastStateChange(double ts)
179 m_LastStateChange = ts;
180 Touch("last_state_change");
183 double Service::GetLastStateChange(void) const
185 if (m_LastStateChange.IsEmpty())
186 return IcingaApplication::GetInstance()->GetStartTime();
188 return m_LastStateChange;
191 void Service::SetLastHardStateChange(double ts)
193 m_LastHardStateChange = ts;
194 Touch("last_hard_state_change");
197 double Service::GetLastHardStateChange(void) const
199 if (m_LastHardStateChange.IsEmpty())
200 return IcingaApplication::GetInstance()->GetStartTime();
202 return m_LastHardStateChange;
205 bool Service::GetEnableActiveChecks(void) const
207 if (m_EnableActiveChecks.IsEmpty())
210 return m_EnableActiveChecks;
213 void Service::SetEnableActiveChecks(bool enabled)
215 m_EnableActiveChecks = enabled ? 1 : 0;
216 Touch("enable_active_checks");
219 bool Service::GetEnablePassiveChecks(void) const
221 if (m_EnablePassiveChecks.IsEmpty())
224 return m_EnablePassiveChecks;
227 void Service::SetEnablePassiveChecks(bool enabled)
229 m_EnablePassiveChecks = enabled ? 1 : 0;
230 Touch("enable_passive_checks");
233 bool Service::GetForceNextCheck(void) const
235 if (m_ForceNextCheck.IsEmpty())
238 return static_cast<bool>(m_ForceNextCheck);
241 void Service::SetForceNextCheck(bool forced)
243 m_ForceNextCheck = forced ? 1 : 0;
244 Touch("force_next_check");
247 void Service::ApplyCheckResult(const Dictionary::Ptr& cr)
249 ServiceState old_state = GetState();
250 ServiceStateType old_stateType = GetStateType();
251 bool hardChange = false;
254 long attempt = GetCurrentCheckAttempt();
256 if (cr->Get("state") == StateOK) {
257 if (old_state != StateOK && old_stateType == StateTypeHard)
258 hardChange = true; // hard recovery
260 if (old_state == StateOK)
261 SetStateType(StateTypeHard);
266 if (attempt >= GetMaxCheckAttempts()) {
267 SetStateType(StateTypeHard);
270 } else if (GetStateType() == StateTypeSoft || GetState() == StateOK) {
271 SetStateType(StateTypeSoft);
278 SetCurrentCheckAttempt(attempt);
280 int state = cr->Get("state");
281 SetState(static_cast<ServiceState>(state));
283 SetLastCheckResult(cr);
285 double now = Utility::GetTime();
287 if (old_state != GetState()) {
288 SetLastStateChange(now);
290 /* remove acknowledgements */
291 if (GetAcknowledgement() == AcknowledgementNormal ||
292 (GetAcknowledgement() == AcknowledgementSticky && GetStateType() == StateTypeHard && GetState() == StateOK)) {
293 SetAcknowledgement(AcknowledgementNone);
294 SetAcknowledgementExpiry(0);
297 /* reschedule service dependencies */
298 BOOST_FOREACH(const Service::Ptr& parent, GetParentServices()) {
299 parent->SetNextCheck(Utility::GetTime());
302 /* reschedule host dependencies */
303 BOOST_FOREACH(const Host::Ptr& parent, GetParentHosts()) {
304 Service::Ptr service = parent->GetHostCheckService();
307 service->SetNextCheck(Utility::GetTime());
311 if (GetState() != StateOK)
315 SetLastHardStateChange(now);
317 /* Make sure the notification component sees the updated
318 * state/state_type attributes. */
321 if (IsReachable(GetSelf()) && !IsInDowntime() && !IsAcknowledged())
322 RequestNotifications(recovery ? NotificationRecovery : NotificationProblem);
326 ServiceState Service::StateFromString(const String& state)
330 else if (state == "WARNING")
332 else if (state == "CRITICAL")
333 return StateCritical;
334 else if (state == "UNCHECKABLE")
335 return StateUncheckable;
340 String Service::StateToString(ServiceState state)
349 case StateUncheckable:
350 return "UNCHECKABLE";
357 ServiceStateType Service::StateTypeFromString(const String& type)
360 return StateTypeSoft;
362 return StateTypeHard;
365 String Service::StateTypeToString(ServiceStateType type)
367 if (type == StateTypeSoft)
373 bool Service::IsAllowedChecker(const String& checker) const
375 Dictionary::Ptr checkers = GetCheckers();
381 BOOST_FOREACH(tie(tuples::ignore, pattern), checkers) {
382 if (Utility::Match(pattern, checker))
389 void Service::BeginExecuteCheck(const Service::Ptr& self, const function<void (void)>& callback)
391 ObjectLock slock(self);
393 /* don't run another check if there is one pending */
394 if (self->m_CurrentTask) {
397 /* we need to call the callback anyway */
403 /* keep track of scheduling info in case the check type doesn't provide its own information */
404 Dictionary::Ptr checkInfo = boost::make_shared<Dictionary>();
405 checkInfo->Set("schedule_start", self->GetNextCheck());
406 checkInfo->Set("execution_start", Utility::GetTime());
408 vector<Dictionary::Ptr> macroDicts;
409 macroDicts.push_back(self->GetMacros());
410 macroDicts.push_back(Service::CalculateDynamicMacros(self));
412 Value raw_command = self->GetCheckCommand();
414 Host::Ptr host = self->GetHost();
419 ObjectLock olock(host);
420 macroDicts.push_back(host->GetMacros());
423 macroDicts.push_back(Host::CalculateDynamicMacros(host));
425 IcingaApplication::Ptr app = IcingaApplication::GetInstance();
428 ObjectLock olock(app);
429 macroDicts.push_back(app->GetMacros());
432 macroDicts.push_back(IcingaApplication::CalculateDynamicMacros(app));
434 Dictionary::Ptr macros = MacroProcessor::MergeMacroDicts(macroDicts);
436 checkInfo->Set("macros", macros);
438 vector<Value> arguments;
439 arguments.push_back(self);
440 arguments.push_back(macros);
442 ScriptTask::Ptr task;
445 ObjectLock olock(self);
446 task = self->MakeMethodTask("check", arguments);
447 self->m_CurrentTask = task;
450 task->Start(boost::bind(&Service::CheckCompletedHandler, self, checkInfo, _1, callback));
453 void Service::CheckCompletedHandler(const Dictionary::Ptr& checkInfo,
454 const ScriptTask::Ptr& task, const function<void (void)>& callback)
456 checkInfo->Set("execution_end", Utility::GetTime());
457 checkInfo->Set("schedule_end", Utility::GetTime());
459 Dictionary::Ptr result;
465 ObjectLock tlock(task);
466 vresult = task->GetResult();
469 if (vresult.IsObjectType<Dictionary>())
471 } catch (const exception& ex) {
473 msgbuf << "Exception occured during check for service '"
474 << GetName() << "': " << diagnostic_information(ex);
475 String message = msgbuf.str();
477 Logger::Write(LogWarning, "icinga", message);
479 result = boost::make_shared<Dictionary>();
480 result->Set("state", StateUnknown);
481 result->Set("output", message);
485 if (!result->Contains("schedule_start"))
486 result->Set("schedule_start", checkInfo->Get("schedule_start"));
488 if (!result->Contains("schedule_end"))
489 result->Set("schedule_end", checkInfo->Get("schedule_end"));
491 if (!result->Contains("execution_start"))
492 result->Set("execution_start", checkInfo->Get("execution_start"));
494 if (!result->Contains("execution_end"))
495 result->Set("execution_end", checkInfo->Get("execution_end"));
497 if (!result->Contains("macros"))
498 result->Set("macros", checkInfo->Get("macros"));
500 if (!result->Contains("active"))
501 result->Set("active", 1);
503 if (!result->Contains("current_checker")) {
504 EndpointManager::Ptr em = EndpointManager::GetInstance();
505 ObjectLock olock(em);
507 result->Set("current_checker", em->GetIdentity());
512 ObjectLock olock(this);
514 ProcessCheckResult(result);
516 m_CurrentTask.reset();
518 /* figure out when the next check is for this service; the call to
519 * ApplyCheckResult() should've already done this but lets do it again
520 * just in case there was no check result. */
527 void Service::ProcessCheckResult(const Dictionary::Ptr& cr)
529 ApplyCheckResult(cr);
531 Service::UpdateStatistics(cr);
533 /* Flush the object so other instances see the service's
534 * new state when they receive the CheckResult message */
538 rm.SetMethod("checker::CheckResult");
540 /* TODO: add _old_ state to message */
541 CheckResultMessage params;
542 params.SetService(GetName());
543 params.SetCheckResult(cr);
545 rm.SetParams(params);
547 EndpointManager::Ptr em = EndpointManager::GetInstance();
548 ObjectLock olock(em);
549 em->SendMulticastMessage(rm);
552 void Service::UpdateStatistics(const Dictionary::Ptr& cr)
555 Value schedule_end = cr->Get("schedule_end");
556 if (!schedule_end.IsEmpty())
557 ts = static_cast<time_t>(schedule_end);
559 ts = static_cast<time_t>(Utility::GetTime());
561 Value active = cr->Get("active");
562 if (active.IsEmpty() || static_cast<long>(active))
563 CIB::UpdateActiveChecksStatistics(ts, 1);
565 CIB::UpdatePassiveChecksStatistics(ts, 1);
568 double Service::CalculateExecutionTime(const Dictionary::Ptr& cr)
570 ObjectLock olock(cr);
572 double execution_start = 0, execution_end = 0;
575 ObjectLock olock(cr);
577 if (!cr->Contains("execution_start") || !cr->Contains("execution_end"))
580 execution_start = cr->Get("execution_start");
581 execution_end = cr->Get("execution_end");
584 return (execution_end - execution_start);
587 double Service::CalculateLatency(const Dictionary::Ptr& cr)
589 double schedule_start = 0, schedule_end = 0;
592 ObjectLock olock(cr);
594 if (!cr->Contains("schedule_start") || !cr->Contains("schedule_end"))
597 schedule_start = cr->Get("schedule_start");
598 schedule_end = cr->Get("schedule_end");
601 return (schedule_end - schedule_start) - CalculateExecutionTime(cr);