1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2 
3 #include "methods/clusterzonechecktask.hpp"
4 #include "icinga/checkcommand.hpp"
5 #include "icinga/macroprocessor.hpp"
6 #include "remote/apilistener.hpp"
7 #include "remote/endpoint.hpp"
8 #include "remote/zone.hpp"
9 #include "base/function.hpp"
10 #include "base/utility.hpp"
11 #include "base/perfdatavalue.hpp"
12 
13 using namespace icinga;
14 
15 REGISTER_FUNCTION_NONCONST(Internal, ClusterZoneCheck, &ClusterZoneCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros");
16 
ScriptFunc(const Checkable::Ptr & checkable,const CheckResult::Ptr & cr,const Dictionary::Ptr & resolvedMacros,bool useResolvedMacros)17 void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
18 	const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros)
19 {
20 	REQUIRE_NOT_NULL(checkable);
21 	REQUIRE_NOT_NULL(cr);
22 
23 	ApiListener::Ptr listener = ApiListener::GetInstance();
24 	CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand();
25 	String commandName = command->GetName();
26 
27 	if (!listener) {
28 		String output = "No API listener is configured for this instance.";
29 		ServiceState state = ServiceUnknown;
30 
31 		if (Checkable::ExecuteCommandProcessFinishedHandler) {
32 			double now = Utility::GetTime();
33 			ProcessResult pr;
34 			pr.PID = -1;
35 			pr.Output = output;
36 			pr.ExecutionStart = now;
37 			pr.ExecutionEnd = now;
38 			pr.ExitStatus = state;
39 
40 			Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr);
41 		} else {
42 			cr->SetCommand(commandName);
43 			cr->SetOutput(output);
44 			cr->SetState(state);
45 			checkable->ProcessCheckResult(cr);
46 		}
47 
48 		return;
49 	}
50 
51 	Value raw_command = command->GetCommandLine();
52 
53 	Host::Ptr host;
54 	Service::Ptr service;
55 	tie(host, service) = GetHostService(checkable);
56 
57 	MacroProcessor::ResolverList resolvers;
58 
59 	if (MacroResolver::OverrideMacros)
60 		resolvers.emplace_back("override", MacroResolver::OverrideMacros);
61 
62 	if (service)
63 		resolvers.emplace_back("service", service);
64 	resolvers.emplace_back("host", host);
65 	resolvers.emplace_back("command", command);
66 	resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
67 
68 	String zoneName = MacroProcessor::ResolveMacros("$cluster_zone$", resolvers, checkable->GetLastCheckResult(),
69 		nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
70 
71 	String missingLagWarning;
72 	String missingLagCritical;
73 
74 	double lagWarning = MacroProcessor::ResolveMacros("$cluster_lag_warning$", resolvers, checkable->GetLastCheckResult(),
75 		&missingLagWarning, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
76 
77 	double lagCritical = MacroProcessor::ResolveMacros("$cluster_lag_critical$", resolvers, checkable->GetLastCheckResult(),
78 		&missingLagCritical, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
79 
80 	if (resolvedMacros && !useResolvedMacros)
81 		return;
82 
83 	if (zoneName.IsEmpty()) {
84 		String output = "Macro 'cluster_zone' must be set.";
85 		ServiceState state = ServiceUnknown;
86 
87 		if (Checkable::ExecuteCommandProcessFinishedHandler) {
88 			double now = Utility::GetTime();
89 			ProcessResult pr;
90 			pr.PID = -1;
91 			pr.Output = output;
92 			pr.ExecutionStart = now;
93 			pr.ExecutionEnd = now;
94 			pr.ExitStatus = state;
95 
96 			Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr);
97 		} else {
98 			cr->SetCommand(commandName);
99 			cr->SetOutput(output);
100 			cr->SetState(state);
101 			checkable->ProcessCheckResult(cr);
102 		}
103 
104 		return;
105 	}
106 
107 	Zone::Ptr zone = Zone::GetByName(zoneName);
108 
109 	if (!zone) {
110 		String output = "Zone '" + zoneName + "' does not exist.";
111 		ServiceState state = ServiceUnknown;
112 
113 		if (Checkable::ExecuteCommandProcessFinishedHandler) {
114 			double now = Utility::GetTime();
115 			ProcessResult pr;
116 			pr.PID = -1;
117 			pr.Output = output;
118 			pr.ExecutionStart = now;
119 			pr.ExecutionEnd = now;
120 			pr.ExitStatus = state;
121 
122 			Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr);
123 		} else {
124 			cr->SetCommand(commandName);
125 			cr->SetOutput(output);
126 			cr->SetState(state);
127 			checkable->ProcessCheckResult(cr);
128 		}
129 		return;
130 	}
131 
132 	bool connected = false;
133 	double zoneLag = 0;
134 
135 	double lastMessageSent = 0;
136 	double lastMessageReceived = 0;
137 	double messagesSentPerSecond = 0;
138 	double messagesReceivedPerSecond = 0;
139 	double bytesSentPerSecond = 0;
140 	double bytesReceivedPerSecond = 0;
141 
142 	for (const Endpoint::Ptr& endpoint : zone->GetEndpoints()) {
143 		if (endpoint->GetConnected())
144 			connected = true;
145 
146 		double eplag = ApiListener::CalculateZoneLag(endpoint);
147 
148 		if (eplag > 0 && eplag > zoneLag)
149 			zoneLag = eplag;
150 
151 		if (endpoint->GetLastMessageSent() > lastMessageSent)
152 			lastMessageSent = endpoint->GetLastMessageSent();
153 
154 		if (endpoint->GetLastMessageReceived() > lastMessageReceived)
155 			lastMessageReceived = endpoint->GetLastMessageReceived();
156 
157 		messagesSentPerSecond += endpoint->GetMessagesSentPerSecond();
158 		messagesReceivedPerSecond += endpoint->GetMessagesReceivedPerSecond();
159 		bytesSentPerSecond += endpoint->GetBytesSentPerSecond();
160 		bytesReceivedPerSecond += endpoint->GetBytesReceivedPerSecond();
161 	}
162 
163 	ServiceState state;
164 	String output;
165 
166 	if (connected) {
167 		state = ServiceOK;
168 		output = "Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag);
169 
170 		/* Check whether the thresholds have been resolved and compare them */
171 		if (missingLagCritical.IsEmpty() && zoneLag > lagCritical) {
172 			state = ServiceCritical;
173 			output = "Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag)
174 				+ " greater than critical threshold: " + Utility::FormatDuration(lagCritical);
175 		} else if (missingLagWarning.IsEmpty() && zoneLag > lagWarning) {
176 			state = ServiceWarning;
177 			output = "Zone '" + zoneName + "' is connected. Log lag: " + Utility::FormatDuration(zoneLag)
178 				+ " greater than warning threshold: " + Utility::FormatDuration(lagWarning);
179 		}
180 	} else {
181 		state = ServiceCritical;
182 		output = "Zone '" + zoneName + "' is not connected. Log lag: " + Utility::FormatDuration(zoneLag);
183 	}
184 
185 	if (Checkable::ExecuteCommandProcessFinishedHandler) {
186 		double now = Utility::GetTime();
187 		ProcessResult pr;
188 		pr.PID = -1;
189 		pr.Output = output;
190 		pr.ExecutionStart = now;
191 		pr.ExecutionEnd = now;
192 		pr.ExitStatus = state;
193 
194 		Checkable::ExecuteCommandProcessFinishedHandler(commandName, pr);
195 	} else {
196 		cr->SetCommand(commandName);
197 		cr->SetState(state);
198 		cr->SetOutput(output);
199 		cr->SetPerformanceData(new Array({
200 			new PerfdataValue("slave_lag", zoneLag, false, "s", lagWarning, lagCritical),
201 			new PerfdataValue("last_messages_sent", lastMessageSent),
202 			new PerfdataValue("last_messages_received", lastMessageReceived),
203 			new PerfdataValue("sum_messages_sent_per_second", messagesSentPerSecond),
204 			new PerfdataValue("sum_messages_received_per_second", messagesReceivedPerSecond),
205 			new PerfdataValue("sum_bytes_sent_per_second", bytesSentPerSecond),
206 			new PerfdataValue("sum_bytes_received_per_second", bytesReceivedPerSecond)
207 		}));
208 
209 		checkable->ProcessCheckResult(cr);
210 	}
211 }
212