diff --git a/doc/09-object-types.md b/doc/09-object-types.md index cdb6db5fb..af8075772 100644 --- a/doc/09-object-types.md +++ b/doc/09-object-types.md @@ -201,6 +201,7 @@ Configuration Attributes: parent\_service\_name | Object name | **Optional.** The parent service. If omitted, this dependency object is treated as host dependency. child\_host\_name | Object name | **Required.** The child host. child\_service\_name | Object name | **Optional.** The child service. If omitted, this dependency object is treated as host dependency. + redundancy\_group | String | **Optional.** Puts the dependency into a group of mutually redundant ones. See discussion below. disable\_checks | Boolean | **Optional.** Whether to disable checks (i.e., don't schedule active checks and drop passive results) when this dependency fails. Defaults to false. disable\_notifications | Boolean | **Optional.** Whether to disable notifications when this dependency fails. Defaults to true. ignore\_soft\_states | Boolean | **Optional.** Whether to ignore soft states for the reachability calculation. Defaults to true. @@ -218,6 +219,16 @@ Up Down ``` +Redundancy groups: + +Sometimes, you want a dependencies to accumulate (e.g., the parent considered reachable only if no dependency is violated), sometimes you want them to be regarded as redundant (e.g., the parent considered unreachable only if no dependency is fulfilled) or even a mixture of both. Think of a host connected to both a network and a storage switch vs. a host connected to redundant routers or a service like SSH depeding on both LDAP and DNS to function, while operating redundant LDAP servers as well as redundant DNS resolvers. + +Behaviour prior to 2.12.0 was to regard all dependecies as cumulative; 2.12.0 made all dependencies regareded redundant. +This may lead to unrelated services inadvertantly regarded to be redundant to each other. + +Specifying a `redundancy_group` causes a dependency to be regarded as redundant only inside that redundancy group. +Dependencies lacking a `redundancy_group` attribute are regarded as essential for the parent. + When using [apply rules](03-monitoring-basics.md#using-apply) for dependencies, you can leave out certain attributes which will be automatically determined by Icinga 2. diff --git a/lib/icinga/checkable-dependency.cpp b/lib/icinga/checkable-dependency.cpp index 5ce92886c..736553a48 100644 --- a/lib/icinga/checkable-dependency.cpp +++ b/lib/icinga/checkable-dependency.cpp @@ -74,25 +74,43 @@ bool Checkable::IsReachable(DependencyType dt, Dependency::Ptr *failedDependency auto deps = GetDependencies(); - int countDeps = deps.size(); - int countFailed = 0; + std::unordered_map violated; // key: redundancy group, value: nullptr if satisfied, violating dependency otherwise for (const Dependency::Ptr& dep : deps) { - if (!dep->IsAvailable(dt)) { - countFailed++; + std::string redundancy_group = dep->GetRedundancyGroup(); - if (failedDependency) - *failedDependency = dep; + if (!dep->IsAvailable(dt)) { + if (redundancy_group.empty()) { + Log(LogDebug, "Checkable") + << "Non-redundant dependency '" << dep->GetName() << "' failed for checkable '" << GetName() << "': Marking as unreachable."; + + if (failedDependency) + *failedDependency = dep; + + return false; + } + + // tentatively mark this dependency group as failed unless it is already marked; + // so it either passed before (don't overwrite) or already failed (so don't care) + if (violated.find(redundancy_group) == violated.end()) + violated.insert(std::make_pair(redundancy_group, dep)); + } else if (!redundancy_group.empty()) { + // definitely mark this dependency group as passed + violated.insert(std::make_pair(redundancy_group, nullptr)); } } - /* If there are dependencies, and all of them failed, mark as unreachable. */ - if (countDeps > 0 && countFailed == countDeps) { + auto violator = std::find_if(violated.begin(), violated.end(), [](const std::pair&v) { return v.second != nullptr; }); + if (violator != violated.end()) { Log(LogDebug, "Checkable") - << "All dependencies have failed for checkable '" << GetName() << "': Marking as unreachable."; + << "All dependencies in redundancy group '" << violator->first << "' have failed for checkable '" << GetName() << "': Marking as unreachable."; + + if (failedDependency) + *failedDependency = violator->second; return false; } + if (failedDependency) *failedDependency = nullptr; diff --git a/lib/icinga/dependency.ti b/lib/icinga/dependency.ti index 3fc832522..41de7ba23 100644 --- a/lib/icinga/dependency.ti +++ b/lib/icinga/dependency.ti @@ -77,6 +77,8 @@ class Dependency : CustomVarObject < DependencyNameComposer }}} }; + [config] String redundancy_group; + [config, navigation] name(TimePeriod) period (PeriodRaw) { navigate {{{ return TimePeriod::GetByName(GetPeriodRaw());