From: Gunnar Beutner Date: Tue, 3 Jul 2012 12:18:46 +0000 (+0200) Subject: Implemented service dependencies. X-Git-Tag: v0.0.1~317 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=734ec2f5fb7efbd6940f1b154cfd633107c5cd3f;p=icinga2 Implemented service dependencies. --- diff --git a/cib/cib.cpp b/cib/cib.cpp index 4065a5dbf..fa2ac41b6 100644 --- a/cib/cib.cpp +++ b/cib/cib.cpp @@ -4,6 +4,7 @@ using namespace icinga; int CIB::m_Types; Ringbuffer CIB::m_TaskStatistics(15 * 60); +boost::signal CIB::OnServiceStatusUpdate; void CIB::RequireInformation(InformationType types) { diff --git a/cib/cib.h b/cib/cib.h index fb7d92723..0077cec08 100644 --- a/cib/cib.h +++ b/cib/cib.h @@ -20,6 +20,8 @@ public: static void UpdateTaskStatistics(long tv, int num); static int GetTaskStatistics(long timespan); + static boost::signal OnServiceStatusUpdate; + private: static int m_Types; diff --git a/cib/i2-cib.h b/cib/i2-cib.h index cc5901ca8..5811b441a 100644 --- a/cib/i2-cib.h +++ b/cib/i2-cib.h @@ -22,8 +22,6 @@ #include "service.h" #include "servicegroup.h" -#include "cib.h" - #include "macroprocessor.h" #include "checkresult.h" #include "checktask.h" @@ -31,4 +29,6 @@ #include "servicestatusmessage.h" +#include "cib.h" + #endif /* I2CIB_H */ diff --git a/cib/nagioschecktask.cpp b/cib/nagioschecktask.cpp index f9a0cbe8a..b2b271d15 100644 --- a/cib/nagioschecktask.cpp +++ b/cib/nagioschecktask.cpp @@ -214,6 +214,12 @@ bool NagiosCheckTask::RunTask(void) exitcode = WEXITSTATUS(status); #else /* _MSC_VER */ exitcode = status; + + /* cmd.exe returns error code 1 (warning) when the plugin + * could not be executed - change the exit status to "unknown" + * when we have no plugin output. */ + if (output.empty()) + exitcode = 128; #endif /* _MSC_VER */ ServiceState state; diff --git a/cib/service.cpp b/cib/service.cpp index fe34e74dc..7ca10d6e3 100644 --- a/cib/service.cpp +++ b/cib/service.cpp @@ -2,6 +2,8 @@ using namespace icinga; +bool Service::m_DependencyCacheValid = false; + string Service::GetAlias(void) const { string value; @@ -105,6 +107,142 @@ Dictionary::Ptr Service::GetCheckers(void) const return value; } +vector Service::GetParents(void) const +{ + vector parents; + + Dictionary::Ptr dependencies = GetDependencies(); + if (dependencies) { + Dictionary::Iterator it; + for (it = dependencies->Begin(); it != dependencies->End(); it++) + parents.push_back(Service::GetByName(it->second)); + } + return parents; +} + +vector Service::GetChildren(void) const +{ + vector children; + + UpdateDependencyCache(); + + Dictionary::Ptr childrenCache; + GetConfigObject()->GetTag("dependency_children", &childrenCache); + + if (childrenCache) { + Dictionary::Iterator it; + for (it = childrenCache->Begin(); it != childrenCache->End(); it++) + children.push_back(Service::GetByName(it->second)); + } + + return children; +} + +void Service::UpdateDependencyCache(void) +{ + static long cacheTx = 0; + + if (m_DependencyCacheValid) + return; + + cacheTx++; + + ConfigObject::TMap::Range range = ConfigObject::GetObjects("service"); + ConfigObject::TMap::Iterator it; + for (it = range.first; it != range.second; it++) { + Service child = it->second; + + vector parents = child.GetParents(); + + vector::iterator st; + for (st = parents.begin(); st != parents.end(); st++) { + Service parent = *st; + + long tx = 0; + parent.GetConfigObject()->GetTag("dependency_cache_tx", &tx); + + Dictionary::Ptr children; + + /* rather than resetting the dependency dictionary in a separate loop we use the cache_tx + * tag to check if the dictionary is from this cache update run. */ + if (tx != cacheTx) { + children = boost::make_shared(); + parent.GetConfigObject()->SetTag("dependency_children", children); + parent.GetConfigObject()->SetTag("dependency_cache_tx", cacheTx); + } else { + parent.GetConfigObject()->GetTag("dependency_children", &children); + assert(children); + } + + children->AddUnnamedProperty(child.GetName()); + } + } + + m_DependencyCacheValid = true; +} + +void Service::InvalidateDependencyCache(void) +{ + m_DependencyCacheValid = false; +} + +ServiceStatusMessage Service::CalculateCombinedStatus(ServiceStatusMessage *input, const vector& parents) +{ + vector failedServices; + + time_t nextCheck = -1; + time_t lastChange = -1; + + vector::const_iterator it; + for (it = parents.begin(); it != parents.end(); it++) { + Service parent = *it; + + if (parent.GetState() != StateOK && parent.GetState() != StateWarning) + failedServices.push_back(parent); + + if (lastChange == -1 || parent.GetLastStateChange() > lastChange) + lastChange = parent.GetLastStateChange(); + + if (nextCheck == -1 || parent.GetNextCheck() < nextCheck) + nextCheck = parent.GetNextCheck(); + } + + string message; + ServiceState state; + + if (failedServices.empty()) { + if (input) + return *input; + + state = StateOK; + message = "Dependant services are available."; + } else { + state = StateUnreachable; + message = "One or more dependant services have failed."; + } + + ServiceStatusMessage result; + result.SetState(state); + result.SetStateType(StateTypeHard); + result.SetCurrentCheckAttempt(1); + result.SetNextCheck(nextCheck); + + time_t now; + time(&now); + + CheckResult cr; + cr.SetScheduleStart(now); + cr.SetScheduleEnd(now); + cr.SetExecutionStart(now); + cr.SetExecutionEnd(now); + cr.SetOutput(message); + cr.SetState(state); + + result.SetCheckResult(cr); + + return result; +} + void Service::SetNextCheck(time_t nextCheck) { GetConfigObject()->SetTag("next_check", (long)nextCheck); @@ -179,16 +317,23 @@ ServiceStateType Service::GetStateType(void) const return static_cast(value); } -void Service::SetLastCheckResult(const Dictionary::Ptr& result) +void Service::SetLastCheckResult(const CheckResult& result) { - GetConfigObject()->SetTag("last_result", result); + GetConfigObject()->SetTag("last_result", result.GetDictionary()); } -Dictionary::Ptr Service::GetLastCheckResult(void) const +bool Service::HasLastCheckResult(void) const { Dictionary::Ptr value; - GetConfigObject()->GetTag("last_result", &value); - return value; + return GetConfigObject()->GetTag("last_result", &value) && value; +} + +CheckResult Service::GetLastCheckResult(void) const +{ + Dictionary::Ptr value; + if (!GetConfigObject()->GetTag("last_result", &value)) + throw invalid_argument("Service has no last check result."); + return CheckResult(value); } void Service::SetLastStateChange(time_t ts) @@ -315,4 +460,3 @@ bool Service::IsAllowedChecker(const string& checker) const return false; } - diff --git a/cib/service.h b/cib/service.h index 320041b1f..95796b8e0 100644 --- a/cib/service.h +++ b/cib/service.h @@ -21,6 +21,7 @@ enum ServiceStateType }; class CheckResult; +class ServiceStatusMessage; class I2_CIB_API Service : public ConfigObjectAdapter { @@ -44,6 +45,13 @@ public: Dictionary::Ptr GetGroups(void) const; Dictionary::Ptr GetCheckers(void) const; + vector GetParents(void) const; + vector GetChildren(void) const; + static void UpdateDependencyCache(void); + static void InvalidateDependencyCache(void); + + static ServiceStatusMessage CalculateCombinedStatus(ServiceStatusMessage *input, const vector& parents); + void SetNextCheck(time_t nextCheck); time_t GetNextCheck(void); void UpdateNextCheck(void); @@ -62,8 +70,9 @@ public: void SetStateType(ServiceStateType type); ServiceStateType GetStateType(void) const; - void SetLastCheckResult(const Dictionary::Ptr& result); - Dictionary::Ptr GetLastCheckResult(void) const; + bool HasLastCheckResult(void) const; + void SetLastCheckResult(const CheckResult& result); + CheckResult GetLastCheckResult(void) const; void SetLastStateChange(time_t ts); time_t GetLastStateChange(void) const; @@ -78,6 +87,9 @@ public: static ServiceStateType StateTypeFromString(const string& state); static string StateTypeToString(ServiceStateType state); + +private: + static bool m_DependencyCacheValid; }; } diff --git a/components/cibsync/cibsynccomponent.cpp b/components/cibsync/cibsynccomponent.cpp index 3468bc641..abda6c134 100644 --- a/components/cibsync/cibsynccomponent.cpp +++ b/components/cibsync/cibsynccomponent.cpp @@ -79,25 +79,27 @@ void CIBSyncComponent::Stop(void) void CIBSyncComponent::ServiceStatusRequestHandler(const Endpoint::Ptr& sender, const RequestMessage& request) { - MessagePart params; + ServiceStatusMessage params; if (!request.GetParams(¶ms)) return; + CIB::OnServiceStatusUpdate(params); + string svcname; - if (!params.GetProperty("service", &svcname)) + if (!params.GetService(&svcname)) return; Service service = Service::GetByName(svcname); - long nextCheck; - if (params.GetProperty("next_check", &nextCheck)) + time_t nextCheck; + if (params.GetNextCheck(&nextCheck)) service.SetNextCheck(nextCheck); - long state, stateType; - if (params.GetProperty("state", &state) && params.GetProperty("state_type", &stateType)) { - long old_state, old_stateType; - old_state = service.GetState(); - old_stateType = service.GetStateType(); + ServiceState state; + ServiceStateType stateType; + if (params.GetState(&state) && params.GetStateType(&stateType)) { + ServiceState old_state = service.GetState(); + ServiceStateType old_stateType = service.GetStateType(); if (state != old_state) { time_t now; @@ -109,16 +111,16 @@ void CIBSyncComponent::ServiceStatusRequestHandler(const Endpoint::Ptr& sender, service.SetLastHardStateChange(now); } - service.SetState(static_cast(state)); - service.SetStateType(static_cast(stateType)); + service.SetState(state); + service.SetStateType(stateType); } long attempt; - if (params.GetProperty("current_attempt", &attempt)) + if (params.GetCurrentCheckAttempt(&attempt)) service.SetCurrentCheckAttempt(attempt); - Dictionary::Ptr cr; - if (params.GetProperty("result", &cr)) + CheckResult cr; + if (params.GetCheckResult(&cr)) service.SetLastCheckResult(cr); time_t now; diff --git a/components/compat/compatcomponent.cpp b/components/compat/compatcomponent.cpp index a5bda76c9..9809bf81b 100644 --- a/components/compat/compatcomponent.cpp +++ b/components/compat/compatcomponent.cpp @@ -94,24 +94,22 @@ void CompatComponent::DumpHostObject(ofstream& fp, Host host) void CompatComponent::DumpServiceStatus(ofstream& fp, Service service) { - Dictionary::Ptr cr; - cr = service.GetLastCheckResult(); - string output; string perfdata; - long schedule_start = -1, schedule_end = -1; - long execution_start = -1, execution_end = -1; - if (cr) { - cr->GetProperty("output", &output); - cr->GetProperty("schedule_start", &schedule_start); - cr->GetProperty("schedule_end", &schedule_end); - cr->GetProperty("execution_start", &execution_start); - cr->GetProperty("execution_end", &execution_end); - cr->GetProperty("performance_data_raw", &perfdata); + time_t schedule_start = -1, schedule_end = -1; + time_t execution_start = -1, execution_end = -1; + if (service.HasLastCheckResult()) { + CheckResult cr = service.GetLastCheckResult(); + output = cr.GetOutput(); + schedule_start = cr.GetScheduleStart(); + schedule_end = cr.GetScheduleEnd(); + execution_start = cr.GetExecutionStart(); + execution_end = cr.GetExecutionEnd(); + perfdata = cr.GetPerformanceDataRaw(); } - long execution_time = (execution_end - execution_start); - long latency = (schedule_end - schedule_start) - execution_time; + time_t execution_time = (execution_end - execution_start); + time_t latency = (schedule_end - schedule_start) - execution_time; int state = service.GetState(); @@ -123,7 +121,7 @@ void CompatComponent::DumpServiceStatus(ofstream& fp, Service service) << "\t" << "service_description=" << service.GetAlias() << "\n" << "\t" << "check_interval=" << service.GetCheckInterval() / 60.0 << "\n" << "\t" << "retry_interval=" << service.GetRetryInterval() / 60.0 << "\n" - << "\t" << "has_been_checked=" << (cr ? 1 : 0) << "\n" + << "\t" << "has_been_checked=" << (service.HasLastCheckResult() ? 1 : 0) << "\n" << "\t" << "should_be_scheduled=1" << "\n" << "\t" << "check_execution_time=" << execution_time << "\n" << "\t" << "check_latency=" << latency << "\n" diff --git a/components/delegation/delegationcomponent.cpp b/components/delegation/delegationcomponent.cpp index 886380993..ec9f6f616 100644 --- a/components/delegation/delegationcomponent.cpp +++ b/components/delegation/delegationcomponent.cpp @@ -297,7 +297,7 @@ void DelegationComponent::CheckResultRequestHandler(const Endpoint::Ptr& sender, return; string svcname; - if (params.GetService(&svcname)) + if (!params.GetService(&svcname)) return; Service service = Service::GetByName(svcname); @@ -306,12 +306,39 @@ void DelegationComponent::CheckResultRequestHandler(const Endpoint::Ptr& sender, if (!service.IsAllowedChecker(sender->GetIdentity())) return; - /* TODO: send state update for dependant services */ + vector children = service.GetChildren(); + + vector::iterator it; + for (it = children.begin(); it != children.end(); it++) { + Service child = *it; + + vector affectedServices = child.GetParents(); + affectedServices.push_back(child); + + ServiceStatusMessage statusmsg = Service::CalculateCombinedStatus(NULL, affectedServices); + statusmsg.SetService(child.GetName()); + + ServiceState state = StateUnreachable; + statusmsg.GetState(&state); + + if (child.GetState() == StateUnreachable || state == StateUnreachable) { + RequestMessage rm; + rm.SetMethod("delegation::ServiceStatus"); + rm.SetParams(statusmsg); + + EndpointManager::GetInstance()->SendMulticastMessage(m_Endpoint, rm); + } + } /* send state update */ RequestMessage rm; rm.SetMethod("delegation::ServiceStatus"); - rm.SetParams(params); + + vector parents = service.GetParents(); + ServiceStatusMessage statusmsg = Service::CalculateCombinedStatus(¶ms, parents); + statusmsg.SetService(service.GetName()); + + rm.SetParams(statusmsg); EndpointManager::GetInstance()->SendMulticastMessage(m_Endpoint, rm); } diff --git a/icinga-app/icinga-standalone.conf b/icinga-app/icinga-standalone.conf index aca4966a8..c82a790fa 100644 --- a/icinga-app/icinga-standalone.conf +++ b/icinga-app/icinga-standalone.conf @@ -1,4 +1,12 @@ local object application "icinga" { + cert = "icinga-c1.pem", + ca = "ca.crt", + + node = "192.168.2.235", + service = 7777 +} + +local object component "discovery" { } @@ -10,6 +18,23 @@ local object component "delegation" { } +local object endpoint "icinga-c2" { + roles = { "all" } +} + +local object endpoint "icinga-c3" { + roles = { "all" } +} + +local object endpoint "icinga-c4" { + roles = { "all" } +} + +local object role "all" { + publications = { "*" }, + subscriptions = { "*" } +} + object host "localhost" { } @@ -41,5 +66,7 @@ object service "localhost-ping2" inherits "ping" { macros += { address = "localhost" - } + }, + + dependencies = { "localhost-ping1" } } diff --git a/icinga.sln b/icinga.sln index b9d25a4eb..5d25c5e3e 100644 --- a/icinga.sln +++ b/icinga.sln @@ -41,6 +41,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "demo", "components\demo\dem EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "discovery", "components\discovery\discovery.vcxproj", "{EAD41628-BB96-4F99-9070-8A9676801295}" ProjectSection(ProjectDependencies) = postProject + {538D7F53-A6A0-459A-AE4F-70DB135BC9AF} = {538D7F53-A6A0-459A-AE4F-70DB135BC9AF} {C1FC77E1-04A4-481B-A78B-2F7AF489C2F8} = {C1FC77E1-04A4-481B-A78B-2F7AF489C2F8} EndProjectSection EndProject @@ -78,6 +79,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cibsync", "components\cibsy {C1FC77E1-04A4-481B-A78B-2F7AF489C2F8} = {C1FC77E1-04A4-481B-A78B-2F7AF489C2F8} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "compat", "components\compat\compat.vcxproj", "{2BD1C70C-43DB-4F44-B66B-67CF5C7044AA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -140,6 +143,10 @@ Global {704DDD8E-9E6D-4C22-80BD-6DE10F3A5E1C}.Debug|Win32.Build.0 = Debug|Win32 {704DDD8E-9E6D-4C22-80BD-6DE10F3A5E1C}.Release|Win32.ActiveCfg = Release|Win32 {704DDD8E-9E6D-4C22-80BD-6DE10F3A5E1C}.Release|Win32.Build.0 = Release|Win32 + {2BD1C70C-43DB-4F44-B66B-67CF5C7044AA}.Debug|Win32.ActiveCfg = Debug|Win32 + {2BD1C70C-43DB-4F44-B66B-67CF5C7044AA}.Debug|Win32.Build.0 = Debug|Win32 + {2BD1C70C-43DB-4F44-B66B-67CF5C7044AA}.Release|Win32.ActiveCfg = Release|Win32 + {2BD1C70C-43DB-4F44-B66B-67CF5C7044AA}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE