1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2 
3 #include "db_ido/hostdbobject.hpp"
4 #include "db_ido/hostgroupdbobject.hpp"
5 #include "db_ido/dbtype.hpp"
6 #include "db_ido/dbvalue.hpp"
7 #include "db_ido/dbevents.hpp"
8 #include "icinga/host.hpp"
9 #include "icinga/service.hpp"
10 #include "icinga/notification.hpp"
11 #include "icinga/dependency.hpp"
12 #include "icinga/checkcommand.hpp"
13 #include "icinga/eventcommand.hpp"
14 #include "icinga/compatutility.hpp"
15 #include "icinga/pluginutility.hpp"
16 #include "base/convert.hpp"
17 #include "base/objectlock.hpp"
18 #include "base/logger.hpp"
19 #include "base/json.hpp"
20 
21 using namespace icinga;
22 
23 REGISTER_DBTYPE(Host, "host", DbObjectTypeHost, "host_object_id", HostDbObject);
24 
HostDbObject(const DbType::Ptr & type,const String & name1,const String & name2)25 HostDbObject::HostDbObject(const DbType::Ptr& type, const String& name1, const String& name2)
26 	: DbObject(type, name1, name2)
27 { }
28 
GetConfigFields() const29 Dictionary::Ptr HostDbObject::GetConfigFields() const
30 {
31 	Dictionary::Ptr fields = new Dictionary();
32 	Host::Ptr host = static_pointer_cast<Host>(GetObject());
33 
34 	/* Compatibility fallback. */
35 	String displayName = host->GetDisplayName();
36 
37 	unsigned long notificationStateFilter = CompatUtility::GetCheckableNotificationTypeFilter(host);
38 	unsigned long notificationTypeFilter = CompatUtility::GetCheckableNotificationTypeFilter(host);
39 
40 	return new Dictionary({
41 		{ "alias", !displayName.IsEmpty() ? displayName : host->GetName() },
42 		{ "display_name", displayName },
43 		{ "address", host->GetAddress() },
44 		{ "address6", host->GetAddress6() },
45 		{ "check_command_object_id", host->GetCheckCommand() },
46 		{ "eventhandler_command_object_id", host->GetEventCommand() },
47 		{ "check_timeperiod_object_id", host->GetCheckPeriod() },
48 		{ "check_interval", host->GetCheckInterval() / 60.0 },
49 		{ "retry_interval", host->GetRetryInterval() / 60.0 },
50 		{ "max_check_attempts", host->GetMaxCheckAttempts() },
51 		{ "flap_detection_enabled", host->GetEnableFlapping() },
52 		{ "low_flap_threshold", host->GetFlappingThresholdLow() },
53 		{ "high_flap_threshold", host->GetFlappingThresholdLow() },
54 		{ "process_performance_data", host->GetEnablePerfdata() },
55 		{ "freshness_checks_enabled", 1 },
56 		{ "freshness_threshold", Convert::ToLong(host->GetCheckInterval()) },
57 		{ "event_handler_enabled", host->GetEnableEventHandler() },
58 		{ "passive_checks_enabled", host->GetEnablePassiveChecks() },
59 		{ "active_checks_enabled", host->GetEnableActiveChecks() },
60 		{ "notifications_enabled", host->GetEnableNotifications() },
61 		{ "notes", host->GetNotes() },
62 		{ "notes_url", host->GetNotesUrl() },
63 		{ "action_url", host->GetActionUrl() },
64 		{ "icon_image", host->GetIconImage() },
65 		{ "icon_image_alt", host->GetIconImageAlt() },
66 		{ "notification_interval", CompatUtility::GetCheckableNotificationNotificationInterval(host) },
67 		{ "notify_on_down", (notificationStateFilter & (ServiceWarning | ServiceCritical)) ? 1 : 0 },
68 		{ "notify_on_unreachable", 1 }, /* We don't have this filter and state, and as such we don't filter such notifications. */
69 		{ "notify_on_recovery", (notificationTypeFilter & NotificationRecovery) ? 1 : 0 },
70 		{ "notify_on_flapping", (notificationTypeFilter & (NotificationFlappingStart | NotificationFlappingEnd)) ? 1 : 0 },
71 		{ "notify_on_downtime", (notificationTypeFilter & (NotificationDowntimeStart | NotificationDowntimeEnd | NotificationDowntimeRemoved)) ? 1 : 0 }
72 	});
73 }
74 
GetStatusFields() const75 Dictionary::Ptr HostDbObject::GetStatusFields() const
76 {
77 	Dictionary::Ptr fields = new Dictionary();
78 	Host::Ptr host = static_pointer_cast<Host>(GetObject());
79 
80 	CheckResult::Ptr cr = host->GetLastCheckResult();
81 
82 	if (cr) {
83 		fields->Set("output", CompatUtility::GetCheckResultOutput(cr));
84 		fields->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr));
85 		fields->Set("perfdata", PluginUtility::FormatPerfdata(cr->GetPerformanceData()));
86 		fields->Set("check_source", cr->GetCheckSource());
87 		fields->Set("latency", cr->CalculateLatency());
88 		fields->Set("execution_time", cr->CalculateExecutionTime());
89 	}
90 
91 	int currentState = host->GetState();
92 
93 	if (currentState != HostUp && !host->GetLastReachable())
94 		currentState = 2; /* hardcoded compat state */
95 
96 	fields->Set("current_state", currentState);
97 	fields->Set("has_been_checked", host->HasBeenChecked());
98 	fields->Set("should_be_scheduled", host->GetEnableActiveChecks());
99 	fields->Set("current_check_attempt", host->GetCheckAttempt());
100 	fields->Set("max_check_attempts", host->GetMaxCheckAttempts());
101 	fields->Set("last_check", DbValue::FromTimestamp(host->GetLastCheck()));
102 	fields->Set("next_check", DbValue::FromTimestamp(host->GetNextCheck()));
103 	fields->Set("check_type", !host->GetEnableActiveChecks()); /* 0 .. active, 1 .. passive */
104 	fields->Set("last_state_change", DbValue::FromTimestamp(host->GetLastStateChange()));
105 	fields->Set("last_hard_state_change", DbValue::FromTimestamp(host->GetLastHardStateChange()));
106 	fields->Set("last_hard_state", host->GetLastHardState());
107 	fields->Set("last_time_up", DbValue::FromTimestamp(host->GetLastStateUp()));
108 	fields->Set("last_time_down", DbValue::FromTimestamp(host->GetLastStateDown()));
109 	fields->Set("last_time_unreachable", DbValue::FromTimestamp(host->GetLastStateUnreachable()));
110 	fields->Set("state_type", host->GetStateType());
111 	fields->Set("notifications_enabled", host->GetEnableNotifications());
112 	fields->Set("problem_has_been_acknowledged", host->GetAcknowledgement() != AcknowledgementNone);
113 	fields->Set("acknowledgement_type", host->GetAcknowledgement());
114 	fields->Set("passive_checks_enabled", host->GetEnablePassiveChecks());
115 	fields->Set("active_checks_enabled", host->GetEnableActiveChecks());
116 	fields->Set("event_handler_enabled", host->GetEnableEventHandler());
117 	fields->Set("flap_detection_enabled", host->GetEnableFlapping());
118 	fields->Set("is_flapping", host->IsFlapping());
119 	fields->Set("percent_state_change", host->GetFlappingCurrent());
120 	fields->Set("scheduled_downtime_depth", host->GetDowntimeDepth());
121 	fields->Set("process_performance_data", host->GetEnablePerfdata());
122 	fields->Set("normal_check_interval", host->GetCheckInterval() / 60.0);
123 	fields->Set("retry_check_interval", host->GetRetryInterval() / 60.0);
124 	fields->Set("check_timeperiod_object_id", host->GetCheckPeriod());
125 	fields->Set("is_reachable", host->GetLastReachable());
126 	fields->Set("original_attributes", JsonEncode(host->GetOriginalAttributes()));
127 
128 	fields->Set("current_notification_number", CompatUtility::GetCheckableNotificationNotificationNumber(host));
129 	fields->Set("last_notification", DbValue::FromTimestamp(CompatUtility::GetCheckableNotificationLastNotification(host)));
130 	fields->Set("next_notification", DbValue::FromTimestamp(CompatUtility::GetCheckableNotificationNextNotification(host)));
131 
132 	EventCommand::Ptr eventCommand = host->GetEventCommand();
133 
134 	if (eventCommand)
135 		fields->Set("event_handler", eventCommand->GetName());
136 
137 	CheckCommand::Ptr checkCommand = host->GetCheckCommand();
138 
139 	if (checkCommand)
140 		fields->Set("check_command", checkCommand->GetName());
141 
142 	return fields;
143 }
144 
OnConfigUpdateHeavy()145 void HostDbObject::OnConfigUpdateHeavy()
146 {
147 	Host::Ptr host = static_pointer_cast<Host>(GetObject());
148 
149 	/* groups */
150 	Array::Ptr groups = host->GetGroups();
151 
152 	std::vector<DbQuery> queries;
153 
154 	DbQuery query1;
155 	query1.Table = DbType::GetByName("HostGroup")->GetTable() + "_members";
156 	query1.Type = DbQueryDelete;
157 	query1.Category = DbCatConfig;
158 	query1.WhereCriteria = new Dictionary();
159 	query1.WhereCriteria->Set("host_object_id", host);
160 	queries.emplace_back(std::move(query1));
161 
162 	if (groups) {
163 		ObjectLock olock(groups);
164 		for (const String& groupName : groups) {
165 			HostGroup::Ptr group = HostGroup::GetByName(groupName);
166 
167 			DbQuery query2;
168 			query2.Table = DbType::GetByName("HostGroup")->GetTable() + "_members";
169 			query2.Type = DbQueryInsert;
170 			query2.Category = DbCatConfig;
171 			query2.Fields = new Dictionary({
172 				{ "instance_id", 0 }, /* DbConnection class fills in real ID */
173 				{ "hostgroup_id", DbValue::FromObjectInsertID(group) },
174 				{ "host_object_id", host }
175 			});
176 			query2.WhereCriteria = new Dictionary({
177 				{ "instance_id", 0 }, /* DbConnection class fills in real ID */
178 				{ "hostgroup_id", DbValue::FromObjectInsertID(group) },
179 				{ "host_object_id", host }
180 			});
181 			queries.emplace_back(std::move(query2));
182 		}
183 	}
184 
185 	DbObject::OnMultipleQueries(queries);
186 
187 	queries.clear();
188 
189 	DbQuery query2;
190 	query2.Table = GetType()->GetTable() + "_parenthosts";
191 	query2.Type = DbQueryDelete;
192 	query2.Category = DbCatConfig;
193 	query2.WhereCriteria = new Dictionary({
194 		{ GetType()->GetTable() + "_id", DbValue::FromObjectInsertID(GetObject()) }
195 	});
196 	queries.emplace_back(std::move(query2));
197 
198 	/* parents */
199 	for (const Checkable::Ptr& checkable : host->GetParents()) {
200 		Host::Ptr parent = dynamic_pointer_cast<Host>(checkable);
201 
202 		if (!parent)
203 			continue;
204 
205 		Log(LogDebug, "HostDbObject")
206 			<< "host parents: " << parent->GetName();
207 
208 		/* parents: host_id, parent_host_object_id */
209 		DbQuery query1;
210 		query1.Table = GetType()->GetTable() + "_parenthosts";
211 		query1.Type = DbQueryInsert;
212 		query1.Category = DbCatConfig;
213 		query1.Fields = new Dictionary({
214 			{ GetType()->GetTable() + "_id", DbValue::FromObjectInsertID(GetObject()) },
215 			{ "parent_host_object_id", parent },
216 			{ "instance_id", 0 } /* DbConnection class fills in real ID */
217 		});
218 		queries.emplace_back(std::move(query1));
219 	}
220 
221 	DbObject::OnMultipleQueries(queries);
222 
223 	/* host dependencies */
224 	Log(LogDebug, "HostDbObject")
225 		<< "host dependencies for '" << host->GetName() << "'";
226 
227 	queries.clear();
228 
229 	DbQuery query3;
230 	query3.Table = GetType()->GetTable() + "dependencies";
231 	query3.Type = DbQueryDelete;
232 	query3.Category = DbCatConfig;
233 	query3.WhereCriteria = new Dictionary({
234 		{ "dependent_host_object_id", host }
235 	});
236 	queries.emplace_back(std::move(query3));
237 
238 	for (const Dependency::Ptr& dep : host->GetDependencies()) {
239 		Checkable::Ptr parent = dep->GetParent();
240 
241 		if (!parent) {
242 			Log(LogDebug, "HostDbObject")
243 				<< "Missing parent for dependency '" << dep->GetName() << "'.";
244 			continue;
245 		}
246 
247 		int stateFilter = dep->GetStateFilter();
248 
249 		Log(LogDebug, "HostDbObject")
250 			<< "parent host: " << parent->GetName();
251 
252 		DbQuery query2;
253 		query2.Table = GetType()->GetTable() + "dependencies";
254 		query2.Type = DbQueryInsert;
255 		query2.Category = DbCatConfig;
256 		query2.Fields = new Dictionary({
257 			{ "host_object_id", parent },
258 			{ "dependent_host_object_id", host },
259 			{ "inherits_parent", 1 },
260 			{ "timeperiod_object_id", dep->GetPeriod() },
261 			{ "fail_on_up", (stateFilter & StateFilterUp) ? 1 : 0 },
262 			{ "fail_on_down", (stateFilter & StateFilterDown) ? 1 : 0 },
263 			{ "instance_id", 0 } /* DbConnection class fills in real ID */
264 		});
265 		queries.emplace_back(std::move(query2));
266 	}
267 
268 	DbObject::OnMultipleQueries(queries);
269 
270 	Log(LogDebug, "HostDbObject")
271 		<< "host contacts: " << host->GetName();
272 
273 	queries.clear();
274 
275 	DbQuery query4;
276 	query4.Table = GetType()->GetTable() + "_contacts";
277 	query4.Type = DbQueryDelete;
278 	query4.Category = DbCatConfig;
279 	query4.WhereCriteria = new Dictionary({
280 		{ "host_id", DbValue::FromObjectInsertID(host) }
281 	});
282 	queries.emplace_back(std::move(query4));
283 
284 	for (const User::Ptr& user : CompatUtility::GetCheckableNotificationUsers(host)) {
285 		Log(LogDebug, "HostDbObject")
286 			<< "host contacts: " << user->GetName();
287 
288 		DbQuery query_contact;
289 		query_contact.Table = GetType()->GetTable() + "_contacts";
290 		query_contact.Type = DbQueryInsert;
291 		query_contact.Category = DbCatConfig;
292 		query_contact.Fields = new Dictionary({
293 			{ "host_id", DbValue::FromObjectInsertID(host) },
294 			{ "contact_object_id", user },
295 			{ "instance_id", 0 } /* DbConnection class fills in real ID */
296 		});
297 		queries.emplace_back(std::move(query_contact));
298 	}
299 
300 	DbObject::OnMultipleQueries(queries);
301 
302 	Log(LogDebug, "HostDbObject")
303 		<< "host contactgroups: " << host->GetName();
304 
305 	queries.clear();
306 
307 	DbQuery query5;
308 	query5.Table = GetType()->GetTable() + "_contactgroups";
309 	query5.Type = DbQueryDelete;
310 	query5.Category = DbCatConfig;
311 	query5.WhereCriteria = new Dictionary({
312 		{ "host_id", DbValue::FromObjectInsertID(host) }
313 	});
314 	queries.emplace_back(std::move(query5));
315 
316 	for (const UserGroup::Ptr& usergroup : CompatUtility::GetCheckableNotificationUserGroups(host)) {
317 		Log(LogDebug, "HostDbObject")
318 			<< "host contactgroups: " << usergroup->GetName();
319 
320 		DbQuery query_contact;
321 		query_contact.Table = GetType()->GetTable() + "_contactgroups";
322 		query_contact.Type = DbQueryInsert;
323 		query_contact.Category = DbCatConfig;
324 		query_contact.Fields = new Dictionary({
325 			{ "host_id", DbValue::FromObjectInsertID(host) },
326 			{ "contactgroup_object_id", usergroup },
327 			{ "instance_id", 0 } /* DbConnection class fills in real ID */
328 		});
329 		queries.emplace_back(std::move(query_contact));
330 	}
331 
332 	DbObject::OnMultipleQueries(queries);
333 
334 	DoCommonConfigUpdate();
335 }
336 
OnConfigUpdateLight()337 void HostDbObject::OnConfigUpdateLight()
338 {
339 	DoCommonConfigUpdate();
340 }
341 
DoCommonConfigUpdate()342 void HostDbObject::DoCommonConfigUpdate()
343 {
344 	Host::Ptr host = static_pointer_cast<Host>(GetObject());
345 
346 	/* update comments and downtimes on config change */
347 	DbEvents::AddComments(host);
348 	DbEvents::AddDowntimes(host);
349 }
350 
CalculateConfigHash(const Dictionary::Ptr & configFields) const351 String HostDbObject::CalculateConfigHash(const Dictionary::Ptr& configFields) const
352 {
353 	String hashData = DbObject::CalculateConfigHash(configFields);
354 
355 	Host::Ptr host = static_pointer_cast<Host>(GetObject());
356 
357 	Array::Ptr groups = host->GetGroups();
358 
359 	if (groups) {
360 		groups = groups->ShallowClone();
361 		ObjectLock oLock (groups);
362 		std::sort(groups->Begin(), groups->End());
363 		hashData += DbObject::HashValue(groups);
364 	}
365 
366 	ArrayData parents;
367 
368 	/* parents */
369 	for (const Checkable::Ptr& checkable : host->GetParents()) {
370 		Host::Ptr parent = dynamic_pointer_cast<Host>(checkable);
371 
372 		if (!parent)
373 			continue;
374 
375 		parents.push_back(parent->GetName());
376 	}
377 
378 	std::sort(parents.begin(), parents.end());
379 
380 	hashData += DbObject::HashValue(new Array(std::move(parents)));
381 
382 	ArrayData dependencies;
383 
384 	/* dependencies */
385 	for (const Dependency::Ptr& dep : host->GetDependencies()) {
386 		Checkable::Ptr parent = dep->GetParent();
387 
388 		if (!parent)
389 			continue;
390 
391 		dependencies.push_back(new Array({
392 			parent->GetName(),
393 			dep->GetStateFilter(),
394 			dep->GetPeriodRaw()
395 		}));
396 	}
397 
398 	std::sort(dependencies.begin(), dependencies.end());
399 
400 	hashData += DbObject::HashValue(new Array(std::move(dependencies)));
401 
402 	ArrayData users;
403 
404 	for (const User::Ptr& user : CompatUtility::GetCheckableNotificationUsers(host)) {
405 		users.push_back(user->GetName());
406 	}
407 
408 	std::sort(users.begin(), users.end());
409 
410 	hashData += DbObject::HashValue(new Array(std::move(users)));
411 
412 	ArrayData userGroups;
413 
414 	for (const UserGroup::Ptr& usergroup : CompatUtility::GetCheckableNotificationUserGroups(host)) {
415 		userGroups.push_back(usergroup->GetName());
416 	}
417 
418 	std::sort(userGroups.begin(), userGroups.end());
419 
420 	hashData += DbObject::HashValue(new Array(std::move(userGroups)));
421 
422 	return SHA256(hashData);
423 }
424