Implement a new object type for service dependencies.

Fixes #2799
This commit is contained in:
Gunnar Beutner 2014-02-27 11:05:55 +01:00
parent 05810f053d
commit 22d53cf3b5
16 changed files with 653 additions and 253 deletions

View File

@ -132,6 +132,11 @@ void CheckerComponent::CheckThreadProc(void)
bool check = true;
if (!forced) {
if (!service->IsReachable(DependencyCheckExecution)) {
Log(LogDebug, "icinga", "Skipping check for service '" + service->GetName() + "': Dependency failed.");
check = false;
}
if (!service->GetEnableActiveChecks() || !IcingaApplication::GetInstance()->GetEnableChecks()) {
Log(LogDebug, "checker", "Skipping check for service '" + service->GetName() + "': active checks are disabled");
check = false;

View File

@ -27,6 +27,7 @@
#include "icinga/timeperiod.h"
#include "icinga/notificationcommand.h"
#include "icinga/compatutility.h"
#include "icinga/dependency.h"
#include "base/dynamictype.h"
#include "base/objectlock.h"
#include "base/convert.h"
@ -508,25 +509,6 @@ void StatusDataWriter::DumpServiceObject(std::ostream& fp, const Service::Ptr& s
fp << "\t" "}" "\n"
"\n";
BOOST_FOREACH(const Service::Ptr& parent, service->GetParentServices()) {
Host::Ptr host = service->GetHost();
Host::Ptr parent_host = parent->GetHost();
if (!parent_host)
continue;
fp << "define servicedependency {" "\n"
"\t" "dependent_host_name" "\t" << host->GetName() << "\n"
"\t" "dependent_service_description" "\t" << service->GetShortName() << "\n"
"\t" "host_name" "\t" << parent_host->GetName() << "\n"
"\t" "service_description" "\t" << parent->GetShortName() << "\n"
"\t" "execution_failure_criteria" "\t" "n" "\n"
"\t" "notification_failure_criteria" "\t" "w,u,c" "\n"
"\t" "}" "\n"
"\n";
}
}
void StatusDataWriter::DumpCustomAttributes(std::ostream& fp, const DynamicObject::Ptr& object)
@ -673,6 +655,28 @@ void StatusDataWriter::UpdateObjectsCache(void)
DumpTimePeriod(objectfp, tp);
}
BOOST_FOREACH(const Dependency::Ptr& dep, DynamicType::GetObjects<Dependency>()) {
Service::Ptr parent_service = dep->GetParentService();
if (!parent_service)
continue;
Service::Ptr child_service = dep->GetChildService();
if (!child_service)
continue;
objectfp << "define servicedependency {" "\n"
"\t" "dependent_host_name" "\t" << child_service->GetHost()->GetName() << "\n"
"\t" "dependent_service_description" "\t" << child_service->GetShortName() << "\n"
"\t" "host_name" "\t" << parent_service->GetHost()->GetName() << "\n"
"\t" "service_description" "\t" << parent_service->GetShortName() << "\n"
"\t" "execution_failure_criteria" "\t" "n" "\n"
"\t" "notification_failure_criteria" "\t" "w,u,c" "\n"
"\t" "}" "\n"
"\n";
}
objectfp.close();
#ifdef _WIN32

View File

@ -17,12 +17,6 @@ Example:
groups = [ "all-hosts" ],
host_dependencies = [ "router" ],
service_dependencies = [
{ host = "db-server", service = "mysql" }
],
services["ping"] = {
templates = [ "ping" ]
},
@ -46,9 +40,8 @@ Attributes:
display_name |**Optional.** A short description of the host.
check |**Optional.** A service that is used to determine whether the host is up or down. This must be a service short name of a service that belongs to the host.
groups |**Optional.** A list of host groups this host belongs to.
host_dependencies|**Optional.** A list of host names which this host depends on. These dependencies are used to determine whether the host is unreachable.
service_dependencies|**Optional.** A list of services which this host depends on. Each array element must be a dictionary containing the keys "host" and "service". These dependencies are used to determine whether the host is unreachable.
services |**Optional.** Inline definition of services. Each dictionary item specifies a service.<br /><br />The `templates` attribute can be used to specify an array of templates that should be inherited by the service.<br /><br />The new service's name is "hostname!service" - where "service" is the dictionary key in the services dictionary.<br /><br />The dictionary key is used as the service's short name.
dependencies |**Optional.** Inline definition of dependencies. Each dictionary item specifies a dependency.<br /><br />The `templates` attribute can be used to specify an array of templates that should be inherited by the dependency object.<br /><br />The new dependency object's name is "hostname:service:dependency" - where "dependency" is the dictionary key in the dependencies dictionary.
macros |**Optional.** A dictionary containing macros that are specific to this host.
### <a id="objecttype-hostgroup"></a> HostGroup
@ -121,10 +114,9 @@ Attributes:
event\_command |**Optional.** The name of an event command that should be executed every time the service's state changes.
flapping\_threshold|**Optional.** The flapping threshold in percent when a service is considered to be flapping.
volatile |**Optional.** The volatile setting enables always `HARD` state types if `NOT-OK` state changes occur.
host_dependencies|**Optional.** A list of host names which this host depends on. These dependencies are used to determine whether the host is unreachable.
service_dependencies|**Optional.** A list of services which this host depends on. Each array element must be a dictionary containing the keys "host" and "service". These dependencies are used to determine whether the host is unreachable.
groups |**Optional.** The service groups this service belongs to.
notifications |**Optional.** Inline definition of notifications. Each dictionary item specifies a notification.<br /><br />The `templates` attribute can be used to specify an array of templates that should be inherited by the notification object.<br /><br />The new notification object's name is "hostname:service:notification" - where "notification" is the dictionary key in the notifications dictionary.
dependencies |**Optional.** Inline definition of dependencies. Each dictionary item specifies a dependency.<br /><br />The `templates` attribute can be used to specify an array of templates that should be inherited by the dependency object.<br /><br />The new dependency object's name is "hostname:service:dependency" - where "dependency" is the dictionary key in the dependencies dictionary.
authorities |**Optional.** A list of Endpoints on which this service check will be executed in a cluster scenario.
### <a id="objecttype-servicegroup"></a> ServiceGroup
@ -202,6 +194,50 @@ Available notification type and state filters:
>
> In order to notify on problem states, you will need the type filter `NotificationFilterProblem`.
### <a id="objecttype-dependency"></a> Dependency
Dependency objects are used to specify dependencies between hosts and services.
> **Best Practice**
>
> Rather than creating a `Dependency` object for a specific service it is usually easier
> to just create a `Dependency` template and using the `dependencies` attribute in the `Service`
> object to associate these templates with a service.
Example:
object Dependency "webserver-internet" {
child_host = "webserver",
child_service = "ping4",
parent_host = "internet",
parent_service = "ping4",
state_filter = (StateFilterOK),
disable_checks = true
}
Attributes:
Name |Description
----------------|----------------
parent_host |**Required.** The parent host.
parent_service |**Optional.** The parent service. When not specified the host's check service is used.
child_host |**Required.** The child host.
child_service |**Optional.** The child service. When not specified the host's check service is used.
disable_checks |**Optional.** Whether to disable checks when this dependency fails. Defaults to false.
disable_notifications|**Optional.** Whether to disable notifications when this dependency fails. Defaults to true.
period |**Optional.** Time period during which this dependency is enabled.
state_filter |**Optional.** A set of type filters when this dependency should be OK. Defaults to (StateFilterOK | StateFilterWarning).
Available state filters:
StateFilterOK
StateFilterWarning
StateFilterCritical
StateFilterUnknown
### <a id="objecttype-user"></a> User
A user.

View File

@ -19,6 +19,7 @@ mkclass_target(checkcommand.ti checkcommand.th)
mkclass_target(checkresult.ti checkresult.th)
mkclass_target(command.ti command.th)
mkclass_target(comment.ti comment.th)
mkclass_target(dependency.ti dependency.th)
mkclass_target(domain.ti domain.th)
mkclass_target(downtime.ti downtime.th)
mkclass_target(eventcommand.ti eventcommand.th)
@ -40,7 +41,7 @@ mkembedconfig_target(icinga-type.conf icinga-type.cpp)
add_library(icinga SHARED
api.cpp api.h checkcommand.cpp checkcommand.th checkresult.cpp checkresult.th
cib.cpp command.cpp command.th comment.cpp comment.th compatutility.cpp
cib.cpp command.cpp command.th comment.cpp comment.th compatutility.cpp dependency.cpp dependency.th
domain.cpp domain.th downtime.cpp downtime.th eventcommand.cpp eventcommand.th
externalcommandprocessor.cpp host.cpp host.th hostgroup.cpp hostgroup.th
icingaapplication.cpp icingaapplication.th icingastatuswriter.cpp
@ -48,7 +49,7 @@ add_library(icinga SHARED
macroprocessor.cpp macroresolver.cpp notificationcommand.cpp notificationcommand.th
notification.cpp notification.th perfdatavalue.cpp perfdatavalue.th
pluginutility.cpp scheduleddowntime.cpp scheduleddowntime.th service-check.cpp
service-comment.cpp service.cpp service-downtime.cpp service-event.cpp
service-comment.cpp service.cpp service-dependency.cpp service-downtime.cpp service-event.cpp
service-flapping.cpp service.th servicegroup.cpp servicegroup.th
service-notification.cpp timeperiod.cpp timeperiod.th user.cpp user.th
usergroup.cpp usergroup.th icinga-type.cpp

137
lib/icinga/dependency.cpp Normal file
View File

@ -0,0 +1,137 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-present 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 "icinga/dependency.h"
#include "base/dynamictype.h"
#include "base/objectlock.h"
#include "base/logger_fwd.h"
#include <boost/foreach.hpp>
using namespace icinga;
REGISTER_TYPE(Dependency);
void Dependency::OnStateLoaded(void)
{
DynamicObject::Start();
ASSERT(!OwnsLock());
if (!GetChildService())
Log(LogWarning, "icinga", "Dependency '" + GetName() + "' references an invalid child service and will be ignored.");
else
GetChildService()->AddDependency(GetSelf());
if (!GetParentService())
Log(LogWarning, "icinga", "Dependency '" + GetName() + "' references an invalid parent service and will always fail.");
else
GetParentService()->AddReverseDependency(GetSelf());
}
void Dependency::Stop(void)
{
DynamicObject::Stop();
if (GetChildService())
GetChildService()->RemoveDependency(GetSelf());
if (GetParentService())
GetParentService()->RemoveReverseDependency(GetSelf());
}
bool Dependency::IsAvailable(DependencyType dt) const
{
Service::Ptr service = GetParentService();
if (!service)
return false;
/* ignore if it's the same service */
if (service == GetChildService()) {
Log(LogDebug, "icinga", "Dependency '" + GetName() + "' passed: Parent and child service are identical.");
return true;
}
/* ignore pending services */
if (!service->GetLastCheckResult()) {
Log(LogDebug, "icinga", "Dependency '" + GetName() + "' passed: Service hasn't been checked yet.");
return true;
}
/* ignore soft states */
if (service->GetStateType() == StateTypeSoft) {
Log(LogDebug, "icinga", "Dependency '" + GetName() + "' passed: Service is in a soft state.");
return true;
}
/* check state */
if ((1 << static_cast<int>(service->GetState())) & GetStateFilter()) {
Log(LogDebug, "icinga", "Dependency '" + GetName() + "' passed: Service matches state filter.");
return true;
}
/* ignore if not in time period */
TimePeriod::Ptr tp = GetPeriod();
if (tp && !tp->IsInside(Utility::GetTime())) {
Log(LogDebug, "icinga", "Dependency '" + GetName() + "' passed: Outside time period.");
return true;
}
if (dt == DependencyCheckExecution && !GetDisableChecks()) {
Log(LogDebug, "icinga", "Dependency '" + GetName() + "' passed: Checks are not disabled.");
return true;
} else if (dt == DependencyNotification && !GetDisableNotifications()) {
Log(LogDebug, "icinga", "Dependency '" + GetName() + "' passed: Notifications are not disabled");
return true;
}
Log(LogDebug, "icinga", "Dependency '" + GetName() + "' failed.");
return false;
}
Service::Ptr Dependency::GetChildService(void) const
{
Host::Ptr host = Host::GetByName(GetChildHostRaw());
if (!host)
return Service::Ptr();
if (GetChildServiceRaw().IsEmpty())
return host->GetCheckService();
return host->GetServiceByShortName(GetChildServiceRaw());
}
Service::Ptr Dependency::GetParentService(void) const
{
Host::Ptr host = Host::GetByName(GetParentHostRaw());
if (!host)
return Service::Ptr();
if (GetParentServiceRaw().IsEmpty())
return host->GetCheckService();
return host->GetServiceByShortName(GetParentServiceRaw());
}
TimePeriod::Ptr Dependency::GetPeriod(void) const
{
return TimePeriod::GetByName(GetPeriodRaw());
}

57
lib/icinga/dependency.h Normal file
View File

@ -0,0 +1,57 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-present 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 DEPENDENCY_H
#define DEPENDENCY_H
#include "icinga/i2-icinga.h"
#include "icinga/dependency.th"
#include "icinga/service.h"
#include "base/array.h"
#include "base/dictionary.h"
namespace icinga
{
/**
* A service dependency..
*
* @ingroup icinga
*/
class I2_ICINGA_API Dependency : public ObjectImpl<Dependency>
{
public:
DECLARE_PTR_TYPEDEFS(Dependency);
DECLARE_TYPENAME(Dependency);
Service::Ptr GetParentService(void) const;
Service::Ptr GetChildService(void) const;
TimePeriod::Ptr GetPeriod(void) const;
bool IsAvailable(DependencyType dt) const;
protected:
virtual void OnStateLoaded(void);
virtual void Stop(void);
};
}
#endif /* DEPENDENCY_H */

25
lib/icinga/dependency.ti Normal file
View File

@ -0,0 +1,25 @@
#include "base/dynamicobject.h"
#include "icinga/service.h"
namespace icinga
{
class Dependency : DynamicObject
{
[config] String child_host (ChildHostRaw);
[config] String child_service (ChildServiceRaw);
[config] String parent_host (ParentHostRaw);
[config] String parent_service (ParentServiceRaw);
[config] String period (PeriodRaw);
[config] int state_filter {
default {{{ return (1 << StateOK) | (1 << StateWarning); }}}
};
[config] bool disable_checks;
[config] bool disable_notifications;
};
}

View File

@ -84,54 +84,15 @@ void Host::Stop(void)
// TODO: unregister slave services/notifications?
}
bool Host::IsReachable(void) const
bool Host::IsReachable(DependencyType dt, shared_ptr<Dependency> *failedDependency) const
{
ASSERT(!OwnsLock());
std::set<Service::Ptr> parentServices = GetParentServices();
Service::Ptr hc = GetCheckService();
if (!hc)
return true;
BOOST_FOREACH(const Service::Ptr& service, parentServices) {
ObjectLock olock(service);
/* ignore pending services */
if (!service->GetLastCheckResult())
continue;
/* ignore soft states */
if (service->GetStateType() == StateTypeSoft)
continue;
/* ignore services states OK and Warning */
if (service->GetState() == StateOK ||
service->GetState() == StateWarning)
continue;
return false;
}
std::set<Host::Ptr> parentHosts = GetParentHosts();
BOOST_FOREACH(const Host::Ptr& host, parentHosts) {
Service::Ptr hc = host->GetCheckService();
/* ignore hosts that don't have a check */
if (!hc)
continue;
ObjectLock olock(hc);
/* ignore soft states */
if (hc->GetStateType() == StateTypeSoft)
continue;
/* ignore hosts that are up */
if (hc->GetState() == StateOK)
continue;
return false;
}
return true;
return hc->IsReachable(dt, failedDependency);
}
void Host::UpdateSlaveServices(void)
@ -238,7 +199,14 @@ int Host::GetTotalServices(void) const
Service::Ptr Host::GetServiceByShortName(const Value& name) const
{
if (name.IsScalar()) {
if (name.IsEmpty()) {
Service::Ptr hc = GetCheckService();
if (!hc)
BOOST_THROW_EXCEPTION(std::invalid_argument("Host does not have a host check service: " + GetName()));
return hc;
} else if (name.IsScalar()) {
{
boost::mutex::scoped_lock lock(m_ServicesMutex);
@ -259,48 +227,6 @@ Service::Ptr Host::GetServiceByShortName(const Value& name) const
}
}
std::set<Host::Ptr> Host::GetParentHosts(void) const
{
std::set<Host::Ptr> parents;
Array::Ptr dependencies = GetHostDependencies();
if (dependencies) {
ObjectLock olock(dependencies);
BOOST_FOREACH(const Value& value, dependencies) {
if (value == GetName())
continue;
Host::Ptr host = GetByName(value);
parents.insert(host);
}
}
return parents;
}
std::set<Host::Ptr> Host::GetChildHosts(void) const
{
std::set<Host::Ptr> children;
BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjects<Host>()) {
Array::Ptr dependencies = host->GetHostDependencies();
if (dependencies) {
ObjectLock olock(dependencies);
BOOST_FOREACH(const Value& value, dependencies) {
if (value == GetName())
children.insert(host);
}
}
}
return children;
}
Service::Ptr Host::GetCheckService(void) const
{
String host_check = GetCheck();
@ -311,21 +237,48 @@ Service::Ptr Host::GetCheckService(void) const
return GetServiceByShortName(host_check);
}
std::set<Host::Ptr> Host::GetParentHosts(void) const
{
std::set<Host::Ptr> result;
Service::Ptr hc = GetCheckService();
if (hc)
result = hc->GetParentHosts();
return result;
}
std::set<Host::Ptr> Host::GetChildHosts(void) const
{
std::set<Host::Ptr> result;
Service::Ptr hc = GetCheckService();
if (hc)
result = hc->GetChildHosts();
return result;
}
std::set<Service::Ptr> Host::GetParentServices(void) const
{
std::set<Service::Ptr> parents;
std::set<Service::Ptr> result;
Service::Ptr hc = GetCheckService();
Array::Ptr dependencies = GetServiceDependencies();
if (hc)
result = hc->GetParentServices();
if (dependencies) {
ObjectLock olock(dependencies);
return result;
}
BOOST_FOREACH(const Value& value, dependencies) {
parents.insert(GetServiceByShortName(value));
}
}
std::set<Service::Ptr> Host::GetChildServices(void) const
{
std::set<Service::Ptr> result;
Service::Ptr hc = GetCheckService();
return parents;
if (hc)
result = hc->GetChildServices();
return result;
}
HostState Host::CalculateState(ServiceState state, bool reachable)

View File

@ -31,6 +31,7 @@ namespace icinga
{
class Service;
class Dependency;
/**
* The state of a host.
@ -44,6 +45,13 @@ enum HostState
HostUnreachable = 2
};
enum DependencyType
{
DependencyState,
DependencyCheckExecution,
DependencyNotification
};
/**
* An Icinga host.
*
@ -56,11 +64,13 @@ public:
DECLARE_TYPENAME(Host);
shared_ptr<Service> GetCheckService(void) const;
std::set<Host::Ptr> GetParentHosts(void) const;
std::set<Host::Ptr> GetChildHosts(void) const;
std::set<shared_ptr<Service> > GetParentServices(void) const;
std::set<shared_ptr<Service> > GetChildServices(void) const;
bool IsReachable() const;
bool IsReachable(DependencyType dt = DependencyState, shared_ptr<Dependency> *failedDependency = NULL) const;
shared_ptr<Service> GetServiceByShortName(const Value& name) const;

View File

@ -15,10 +15,9 @@ class Host : DynamicObject
};
[config] Array::Ptr groups;
[config] Dictionary::Ptr macros;
[config] Array::Ptr host_dependencies;
[config] Array::Ptr service_dependencies;
[config] String check;
[config, protected] Dictionary::Ptr services (ServiceDescriptions);
[config] Dictionary::Ptr dependencies (DependencyDescriptions);
};
}

View File

@ -23,18 +23,6 @@ type Host {
%attribute array "groups" {
%attribute name(HostGroup) "*"
},
%attribute array "host_dependencies" {
%attribute name(Host) "*"
},
%attribute array "service_dependencies" {
%attribute dictionary "*" {
%require "host",
%attribute name(Host) "host",
%require "service",
%attribute string "service"
}
},
%attribute dictionary "services" {
%attribute dictionary "*" {
%attribute array "templates" {
@ -45,6 +33,16 @@ type Host {
}
},
%attribute dictionary "dependencies" {
%attribute dictionary "*" {
%attribute array "templates" {
%attribute name(Dependency) "*"
},
%attribute any "*"
}
},
%attribute dictionary "macros" {
%attribute string "*"
}
@ -95,18 +93,16 @@ type Service {
%attribute number "volatile",
%attribute array "host_dependencies" {
%attribute name(Host) "*"
},
%attribute array "service_dependencies" {
%attribute dictionary "dependencies" {
%attribute dictionary "*" {
%require "host",
%attribute name(Host) "host",
%attribute array "templates" {
%attribute name(Dependency) "*"
},
%require "service",
%attribute string "service"
%attribute any "*"
}
},
%attribute array "groups" {
%attribute name(ServiceGroup) "*"
},
@ -285,3 +281,19 @@ type ScheduledDowntime {
%attribute any "templates"
}
type Dependency {
%require "parent_host",
%attribute name(Host) "parent_host",
%attribute string "parent_service",
%require "child_host",
%attribute name(Host) "child_host",
%attribute string "child_service",
%attribute name(TimePeriod) "period",
%attribute number "state_filter",
%attribute number "disable_checks",
%attribute number "disable_notifications"
}

View File

@ -240,6 +240,7 @@ void Service::ProcessCheckResult(const CheckResult::Ptr& cr, const String& autho
cr->SetCheckSource(authority);
bool reachable = IsReachable();
bool notification_reachable = IsReachable(DependencyNotification);
bool host_reachable = GetHost()->IsReachable();
@ -358,7 +359,7 @@ void Service::ProcessCheckResult(const CheckResult::Ptr& cr, const String& autho
Service::UpdateStatistics(cr);
bool in_downtime = IsInDowntime();
bool send_notification = hardChange && reachable && !in_downtime && !IsAcknowledged();
bool send_notification = hardChange && notification_reachable && !in_downtime && !IsAcknowledged();
if (old_state == StateOK && old_stateType == StateTypeSoft)
send_notification = false; /* Don't send notifications for SOFT-OK -> HARD-OK. */

View File

@ -0,0 +1,240 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-present 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 "icinga/service.h"
#include "icinga/dependency.h"
#include "config/configitembuilder.h"
#include "base/dynamictype.h"
#include "base/objectlock.h"
#include "base/logger_fwd.h"
#include "base/timer.h"
#include "base/utility.h"
#include "base/convert.h"
#include <boost/foreach.hpp>
using namespace icinga;
void Service::AddDependency(const Dependency::Ptr& dep)
{
boost::mutex::scoped_lock lock(m_DependencyMutex);
m_Dependencies.insert(dep);
}
void Service::RemoveDependency(const Dependency::Ptr& dep)
{
boost::mutex::scoped_lock lock(m_DependencyMutex);
m_Dependencies.erase(dep);
}
std::set<Dependency::Ptr> Service::GetDependencies(void) const
{
boost::mutex::scoped_lock lock(m_DependencyMutex);
return m_Dependencies;
}
void Service::AddReverseDependency(const Dependency::Ptr& dep)
{
boost::mutex::scoped_lock lock(m_DependencyMutex);
m_ReverseDependencies.insert(dep);
}
void Service::RemoveReverseDependency(const Dependency::Ptr& dep)
{
boost::mutex::scoped_lock lock(m_DependencyMutex);
m_ReverseDependencies.erase(dep);
}
std::set<Dependency::Ptr> Service::GetReverseDependencies(void) const
{
boost::mutex::scoped_lock lock(m_DependencyMutex);
return m_ReverseDependencies;
}
bool Service::IsReachable(DependencyType dt, Dependency::Ptr *failedDependency, int rstack) const
{
if (rstack > 20) {
Log(LogWarning, "icinga", "Too many nested dependencies for service '" + GetName() + "': Dependency failed.");
return false;
}
BOOST_FOREACH(const Service::Ptr& service, GetParentServices()) {
if (!service->IsReachable(dt, failedDependency, rstack + 1))
return false;
}
/* implicit dependency on host's check service */
if (dt == DependencyState || dt == DependencyNotification) {
Service::Ptr hc = GetHost()->GetCheckService();
if (hc && hc->GetState() == StateCritical && hc->GetStateType() == StateTypeHard) {
if (failedDependency)
*failedDependency = Dependency::Ptr();
return false;
}
}
BOOST_FOREACH(const Dependency::Ptr& dep, GetDependencies()) {
if (!dep->IsAvailable(dt)) {
if (failedDependency)
*failedDependency = dep;
return false;
}
}
if (failedDependency)
*failedDependency = Dependency::Ptr();
return true;
}
std::set<Host::Ptr> Service::GetParentHosts(void) const
{
std::set<Host::Ptr> result;
BOOST_FOREACH(const Service::Ptr& svc, GetParentServices())
result.insert(svc->GetHost());
return result;
}
std::set<Host::Ptr> Service::GetChildHosts(void) const
{
std::set<Host::Ptr> result;
BOOST_FOREACH(const Service::Ptr& svc, GetChildServices())
result.insert(svc->GetHost());
return result;
}
std::set<Service::Ptr> Service::GetParentServices(void) const
{
std::set<Service::Ptr> parents;
BOOST_FOREACH(const Dependency::Ptr& dep, GetDependencies()) {
Service::Ptr service = dep->GetParentService();
if (service)
parents.insert(service);
}
return parents;
}
std::set<Service::Ptr> Service::GetChildServices(void) const
{
std::set<Service::Ptr> parents;
BOOST_FOREACH(const Dependency::Ptr& dep, GetReverseDependencies()) {
Service::Ptr service = dep->GetChildService();
if (service)
parents.insert(service);
}
return parents;
}
void Service::UpdateSlaveDependencies(void)
{
/*
* pass == 0 -> steal host's dependency definitions
* pass == 1 -> service's dependencies
*/
for (int pass = 0; pass < 2; pass++) {
/* Service dependency descs */
Dictionary::Ptr descs;
if (pass == 0 && !IsHostCheck())
continue;
if (pass == 0)
descs = GetHost()->GetDependencyDescriptions();
else
GetDependencyDescriptions();
if (!descs || descs->GetLength() == 0)
continue;
ConfigItem::Ptr item;
if (pass == 0)
item = ConfigItem::GetObject("Host", GetHost()->GetName());
else
ConfigItem::GetObject("Service", GetName());
ObjectLock olock(descs);
BOOST_FOREACH(const Dictionary::Pair& kv, descs) {
std::ostringstream namebuf;
namebuf << GetName() << "!" << kv.first;
String name = namebuf.str();
std::vector<String> path;
path.push_back("dependencies");
path.push_back(kv.first);
ExpressionList::Ptr exprl;
{
ObjectLock ilock(item);
exprl = item->GetLinkedExpressionList();
}
DebugInfo di;
exprl->FindDebugInfoPath(path, di);
if (di.Path.IsEmpty())
di = item->GetDebugInfo();
ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
builder->SetType("Dependency");
builder->SetName(name);
builder->AddExpression("child_host", OperatorSet, GetHost()->GetName());
builder->AddExpression("child_service", OperatorSet, GetShortName());
Dictionary::Ptr dependency = kv.second;
Array::Ptr templates = dependency->Get("templates");
if (templates) {
ObjectLock tlock(templates);
BOOST_FOREACH(const Value& tmpl, templates) {
builder->AddParent(tmpl);
}
}
/* Clone attributes from the scheduled downtime expression list. */
ExpressionList::Ptr sd_exprl = make_shared<ExpressionList>();
exprl->ExtractPath(path, sd_exprl);
builder->AddExpressionList(sd_exprl);
ConfigItem::Ptr dependencyItem = builder->Compile();
dependencyItem->Register();
DynamicObject::Ptr dobj = dependencyItem->Commit();
dobj->OnConfigLoaded();
}
}
}

View File

@ -23,6 +23,7 @@
#include "icinga/icingaapplication.h"
#include "icinga/macroprocessor.h"
#include "icinga/pluginutility.h"
#include "icinga/dependency.h"
#include "config/configitembuilder.h"
#include "base/dynamictype.h"
#include "base/objectlock.h"
@ -77,6 +78,7 @@ void Service::OnConfigLoaded(void)
UpdateSlaveNotifications();
UpdateSlaveScheduledDowntimes();
UpdateSlaveDependencies();
SetSchedulingOffset(Utility::Random());
}
@ -138,60 +140,6 @@ bool Service::IsHostCheck(void) const
}
bool Service::IsReachable(void) const
{
ASSERT(!OwnsLock());
BOOST_FOREACH(const Service::Ptr& service, GetParentServices()) {
/* ignore ourselves */
if (service->GetName() == GetName())
continue;
ObjectLock olock(service);
/* ignore pending services */
if (!service->GetLastCheckResult())
continue;
/* ignore soft states */
if (service->GetStateType() == StateTypeSoft)
continue;
/* ignore services states OK and Warning */
if (service->GetState() == StateOK ||
service->GetState() == StateWarning)
continue;
return false;
}
BOOST_FOREACH(const Host::Ptr& host, GetParentHosts()) {
Service::Ptr hc = host->GetCheckService();
/* ignore hosts that don't have a hostcheck */
if (!hc)
continue;
/* ignore ourselves */
if (hc->GetName() == GetName())
continue;
ObjectLock olock(hc);
/* ignore soft states */
if (hc->GetStateType() == StateTypeSoft)
continue;
/* ignore hosts that are up */
if (hc->GetState() == StateOK)
continue;
return false;
}
return true;
}
AcknowledgementType Service::GetAcknowledgement(void)
{
ASSERT(OwnsLock());
@ -239,51 +187,6 @@ void Service::ClearAcknowledgement(const String& authority)
OnAcknowledgementCleared(GetSelf(), authority);
}
std::set<Host::Ptr> Service::GetParentHosts(void) const
{
std::set<Host::Ptr> parents;
Host::Ptr host = GetHost();
/* The service's host is implicitly a parent. */
parents.insert(host);
Array::Ptr dependencies = GetHostDependencies();
if (dependencies) {
ObjectLock olock(dependencies);
BOOST_FOREACH(const String& dependency, dependencies) {
parents.insert(Host::GetByName(dependency));
}
}
return parents;
}
std::set<Service::Ptr> Service::GetParentServices(void) const
{
std::set<Service::Ptr> parents;
Host::Ptr host = GetHost();
Array::Ptr dependencies = GetServiceDependencies();
if (host && dependencies) {
ObjectLock olock(dependencies);
BOOST_FOREACH(const Value& dependency, dependencies) {
Service::Ptr service = host->GetServiceByShortName(dependency);
if (!service || service->GetName() == GetName())
continue;
parents.insert(service);
}
}
return parents;
}
bool Service::GetEnablePerfdata(void) const
{
if (!GetOverrideEnablePerfdata().IsEmpty())

View File

@ -96,11 +96,13 @@ public:
Host::Ptr GetHost(void) const;
std::set<Host::Ptr> GetParentHosts(void) const;
std::set<Host::Ptr> GetChildHosts(void) const;
std::set<Service::Ptr> GetParentServices(void) const;
std::set<Service::Ptr> GetChildServices(void) const;
bool IsHostCheck(void) const;
bool IsReachable(void) const;
bool IsReachable(DependencyType dt = DependencyState, shared_ptr<Dependency> *failedDependency = NULL, int rstack = 0) const;
AcknowledgementType GetAcknowledgement(void);
@ -273,6 +275,17 @@ public:
bool GetEnablePerfdata(void) const;
void SetEnablePerfdata(bool enabled, const String& authority = String());
/* Dependencies */
void AddDependency(const shared_ptr<Dependency>& dep);
void RemoveDependency(const shared_ptr<Dependency>& dep);
std::set<shared_ptr<Dependency> > GetDependencies(void) const;
void AddReverseDependency(const shared_ptr<Dependency>& dep);
void RemoveReverseDependency(const shared_ptr<Dependency>& dep);
std::set<shared_ptr<Dependency> > GetReverseDependencies(void) const;
void UpdateSlaveDependencies(void);
protected:
virtual void Start(void);
@ -297,6 +310,11 @@ private:
/* Notifications */
std::set<Notification::Ptr> m_Notifications;
/* Dependencies */
mutable boost::mutex m_DependencyMutex;
std::set<shared_ptr<Dependency> > m_Dependencies;
std::set<shared_ptr<Dependency> > m_ReverseDependencies;
};
}

View File

@ -30,8 +30,6 @@ class Service : DynamicObject
}}}
};
[config] Dictionary::Ptr macros;
[config] Array::Ptr host_dependencies;
[config] Array::Ptr service_dependencies;
[config] Array::Ptr groups;
[config, protected] String check_command (CheckCommandRaw);
[config] int max_check_attempts (MaxCheckAttemptsRaw) {
@ -60,6 +58,7 @@ class Service : DynamicObject
};
[config] Dictionary::Ptr notifications (NotificationDescriptions);
[config] Dictionary::Ptr scheduled_downtimes (ScheduledDowntimeDescriptions);
[config] Dictionary::Ptr dependencies (DependencyDescriptions);
[config] bool enable_active_checks (EnableActiveChecksRaw) {
default {{{ return true; }}}
};