int CIB::m_Types;
Ringbuffer CIB::m_TaskStatistics(15 * 60);
+boost::signal<void (const ServiceStatusMessage&)> CIB::OnServiceStatusUpdate;
void CIB::RequireInformation(InformationType types)
{
static void UpdateTaskStatistics(long tv, int num);
static int GetTaskStatistics(long timespan);
+ static boost::signal<void (const ServiceStatusMessage&)> OnServiceStatusUpdate;
+
private:
static int m_Types;
#include "service.h"
#include "servicegroup.h"
-#include "cib.h"
-
#include "macroprocessor.h"
#include "checkresult.h"
#include "checktask.h"
#include "servicestatusmessage.h"
+#include "cib.h"
+
#endif /* I2CIB_H */
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;
using namespace icinga;
+bool Service::m_DependencyCacheValid = false;
+
string Service::GetAlias(void) const
{
string value;
return value;
}
+vector<Service> Service::GetParents(void) const
+{
+ vector<Service> 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> Service::GetChildren(void) const
+{
+ vector<Service> 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<Service> parents = child.GetParents();
+
+ vector<Service>::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<Dictionary>();
+ 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<Service>& parents)
+{
+ vector<Service> failedServices;
+
+ time_t nextCheck = -1;
+ time_t lastChange = -1;
+
+ vector<Service>::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);
return static_cast<ServiceStateType>(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)
return false;
}
-
};
class CheckResult;
+class ServiceStatusMessage;
class I2_CIB_API Service : public ConfigObjectAdapter
{
Dictionary::Ptr GetGroups(void) const;
Dictionary::Ptr GetCheckers(void) const;
+ vector<Service> GetParents(void) const;
+ vector<Service> GetChildren(void) const;
+ static void UpdateDependencyCache(void);
+ static void InvalidateDependencyCache(void);
+
+ static ServiceStatusMessage CalculateCombinedStatus(ServiceStatusMessage *input, const vector<Service>& parents);
+
void SetNextCheck(time_t nextCheck);
time_t GetNextCheck(void);
void UpdateNextCheck(void);
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;
static ServiceStateType StateTypeFromString(const string& state);
static string StateTypeToString(ServiceStateType state);
+
+private:
+ static bool m_DependencyCacheValid;
};
}
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;
service.SetLastHardStateChange(now);
}
- service.SetState(static_cast<ServiceState>(state));
- service.SetStateType(static_cast<ServiceStateType>(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;
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();
<< "\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"
return;
string svcname;
- if (params.GetService(&svcname))
+ if (!params.GetService(&svcname))
return;
Service service = Service::GetByName(svcname);
if (!service.IsAllowedChecker(sender->GetIdentity()))
return;
- /* TODO: send state update for dependant services */
+ vector<Service> children = service.GetChildren();
+
+ vector<Service>::iterator it;
+ for (it = children.begin(); it != children.end(); it++) {
+ Service child = *it;
+
+ vector<Service> 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<Service> parents = service.GetParents();
+ ServiceStatusMessage statusmsg = Service::CalculateCombinedStatus(¶ms, parents);
+ statusmsg.SetService(service.GetName());
+
+ rm.SetParams(statusmsg);
EndpointManager::GetInstance()->SendMulticastMessage(m_Endpoint, rm);
}
local object application "icinga" {
+ cert = "icinga-c1.pem",
+ ca = "ca.crt",
+
+ node = "192.168.2.235",
+ service = 7777
+}
+
+local object component "discovery" {
}
}
+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" {
}
macros += {
address = "localhost"
- }
+ },
+
+ dependencies = { "localhost-ping1" }
}
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
{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
{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