]> granicus.if.org Git - icinga2/blob - lib/icinga/host.cpp
Refactor #includes (Part 2).
[icinga2] / lib / icinga / host.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/)        *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "i2-icinga.h"
21 #include "base/dynamictype.h"
22 #include "base/objectlock.h"
23 #include "base/logger_fwd.h"
24 #include <boost/tuple/tuple.hpp>
25 #include <boost/smart_ptr/make_shared.hpp>
26 #include <boost/foreach.hpp>
27
28 using namespace icinga;
29
30 boost::mutex Host::m_ServiceMutex;
31 std::map<String, std::map<String, Service::WeakPtr> > Host::m_ServicesCache;
32 bool Host::m_ServicesCacheNeedsUpdate = false;
33 Timer::Ptr Host::m_ServicesCacheTimer;
34
35 REGISTER_SCRIPTFUNCTION(ValidateServiceDictionary, &Host::ValidateServiceDictionary);
36
37 REGISTER_TYPE(Host);
38
39 Host::Host(const Dictionary::Ptr& serializedUpdate)
40         : DynamicObject(serializedUpdate)
41 {
42         RegisterAttribute("display_name", Attribute_Config, &m_DisplayName);
43         RegisterAttribute("hostgroups", Attribute_Config, &m_HostGroups);
44         RegisterAttribute("macros", Attribute_Config, &m_Macros);
45         RegisterAttribute("hostdependencies", Attribute_Config, &m_HostDependencies);
46         RegisterAttribute("servicedependencies", Attribute_Config, &m_ServiceDependencies);
47         RegisterAttribute("hostcheck", Attribute_Config, &m_HostCheck);
48
49 }
50
51 Host::~Host(void)
52 {
53         HostGroup::InvalidateMembersCache();
54
55         if (m_SlaveServices) {
56                 ConfigItem::Ptr service;
57                 BOOST_FOREACH(boost::tie(boost::tuples::ignore, service), m_SlaveServices) {
58                         service->Unregister();
59                 }
60         }
61 }
62
63 void Host::OnRegistrationCompleted(void)
64 {
65         ASSERT(!OwnsLock());
66
67         DynamicObject::OnRegistrationCompleted();
68
69         Host::InvalidateServicesCache();
70         UpdateSlaveServices();
71 }
72
73 String Host::GetDisplayName(void) const
74 {
75         if (!m_DisplayName.IsEmpty())
76                 return m_DisplayName;
77         else
78                 return GetName();
79 }
80
81 /**
82  * @threadsafety Always.
83  */
84 Host::Ptr Host::GetByName(const String& name)
85 {
86         DynamicObject::Ptr configObject = DynamicObject::GetObject("Host", name);
87
88         return dynamic_pointer_cast<Host>(configObject);
89 }
90
91 Array::Ptr Host::GetGroups(void) const
92 {
93         return m_HostGroups;;
94 }
95
96 Dictionary::Ptr Host::GetMacros(void) const
97 {
98         return m_Macros;
99 }
100
101 Array::Ptr Host::GetHostDependencies(void) const
102 {
103         return m_HostDependencies;;
104 }
105
106 Array::Ptr Host::GetServiceDependencies(void) const
107 {
108         return m_ServiceDependencies;
109 }
110
111 String Host::GetHostCheck(void) const
112 {
113         return m_HostCheck;
114 }
115
116 bool Host::IsReachable(void) const
117 {
118         ASSERT(!OwnsLock());
119
120         std::set<Service::Ptr> parentServices = GetParentServices();
121
122         BOOST_FOREACH(const Service::Ptr& service, parentServices) {
123                 ObjectLock olock(service);
124
125                 /* ignore pending services */
126                 if (!service->GetLastCheckResult())
127                         continue;
128
129                 /* ignore soft states */
130                 if (service->GetStateType() == StateTypeSoft)
131                         continue;
132
133                 /* ignore services states OK and Warning */
134                 if (service->GetState() == StateOK ||
135                     service->GetState() == StateWarning)
136                         continue;
137
138                 return false;
139         }
140
141         std::set<Host::Ptr> parentHosts = GetParentHosts();
142
143         BOOST_FOREACH(const Host::Ptr& host, parentHosts) {
144                 Service::Ptr hc = host->GetHostCheckService();
145
146                 /* ignore hosts that don't have a hostcheck */
147                 if (!hc)
148                         continue;
149
150                 ObjectLock olock(hc);
151
152                 /* ignore soft states */
153                 if (hc->GetStateType() == StateTypeSoft)
154                         continue;
155
156                 /* ignore hosts that are up */
157                 if (hc->GetState() == StateOK)
158                         continue;
159
160                 return false;
161         }
162
163         return true;
164 }
165
166 template<bool copyServiceAttrs, typename TDict>
167 static void CopyServiceAttributes(TDict serviceDesc, const ConfigItemBuilder::Ptr& builder)
168 {
169         /* TODO: we only need to copy macros if this is an inline definition,
170          * i.e. "typeid(serviceDesc)" != Service, however for now we just
171          * copy them anyway. */
172         Value macros = serviceDesc->Get("macros");
173         if (!macros.IsEmpty())
174                 builder->AddExpression("macros", OperatorPlus, macros);
175
176         Value checkInterval = serviceDesc->Get("check_interval");
177         if (!checkInterval.IsEmpty())
178                 builder->AddExpression("check_interval", OperatorSet, checkInterval);
179
180         Value retryInterval = serviceDesc->Get("retry_interval");
181         if (!retryInterval.IsEmpty())
182                 builder->AddExpression("retry_interval", OperatorSet, retryInterval);
183
184         Value sgroups = serviceDesc->Get("servicegroups");
185         if (!sgroups.IsEmpty())
186                 builder->AddExpression("servicegroups", OperatorPlus, sgroups);
187
188         Value checkers = serviceDesc->Get("checkers");
189         if (!checkers.IsEmpty())
190                 builder->AddExpression("checkers", OperatorSet, checkers);
191
192         Value short_name = serviceDesc->Get("short_name");
193         if (!short_name.IsEmpty())
194                 builder->AddExpression("short_name", OperatorSet, short_name);
195
196         Value notification_interval = serviceDesc->Get("notification_interval");
197         if (!notification_interval.IsEmpty())
198                 builder->AddExpression("notification_interval", OperatorSet, notification_interval);
199
200         Value check_period = serviceDesc->Get("check_period");
201         if (!check_period.IsEmpty())
202                 builder->AddExpression("check_period", OperatorSet, check_period);
203
204         if (copyServiceAttrs) {
205                 Value servicedependencies = serviceDesc->Get("servicedependencies");
206                 if (!servicedependencies.IsEmpty())
207                         builder->AddExpression("servicedependencies", OperatorPlus, servicedependencies);
208
209                 Value hostdependencies = serviceDesc->Get("hostdependencies");
210                 if (!hostdependencies.IsEmpty())
211                         builder->AddExpression("hostdependencies", OperatorPlus, hostdependencies);
212         }
213 }
214
215 void Host::UpdateSlaveServices(void)
216 {
217         ASSERT(!OwnsLock());
218
219         ConfigItem::Ptr item = ConfigItem::GetObject("Host", GetName());
220
221         /* Don't create slave services unless we own this object */
222         if (!item)
223                 return;
224
225         Dictionary::Ptr oldServices = m_SlaveServices;
226         Dictionary::Ptr serviceDescs = Get("services");
227
228         Dictionary::Ptr newServices = boost::make_shared<Dictionary>();
229
230         if (serviceDescs) {
231                 ObjectLock olock(serviceDescs);
232                 String svcname;
233                 Value svcdesc;
234                 BOOST_FOREACH(boost::tie(svcname, svcdesc), serviceDescs) {
235                         if (svcdesc.IsScalar())
236                                 svcname = svcdesc;
237
238                         std::ostringstream namebuf;
239                         namebuf << GetName() << "-" << svcname;
240                         String name = namebuf.str();
241
242                         ConfigItemBuilder::Ptr builder = boost::make_shared<ConfigItemBuilder>(item->GetDebugInfo());
243                         builder->SetType("Service");
244                         builder->SetName(name);
245                         builder->AddExpression("host_name", OperatorSet, GetName());
246                         builder->AddExpression("display_name", OperatorSet, svcname);
247                         builder->AddExpression("short_name", OperatorSet, svcname);
248
249                         CopyServiceAttributes<false>(this, builder);
250
251                         if (!svcdesc.IsObjectType<Dictionary>())
252                                 BOOST_THROW_EXCEPTION(std::invalid_argument("Service description must be either a string or a dictionary."));
253
254                         Dictionary::Ptr service = svcdesc;
255
256                         Array::Ptr templates = service->Get("templates");
257
258                         if (templates) {
259                                 ObjectLock olock(templates);
260
261                                 BOOST_FOREACH(const Value& tmpl, templates) {
262                                         builder->AddParent(tmpl);
263                                 }
264                         }
265
266                         CopyServiceAttributes<true>(service, builder);
267
268                         ConfigItem::Ptr serviceItem = builder->Compile();
269                         DynamicObject::Ptr dobj = serviceItem->Commit();
270
271                         newServices->Set(name, serviceItem);
272                 }
273         }
274
275         if (oldServices) {
276                 ObjectLock olock(oldServices);
277
278                 ConfigItem::Ptr service;
279                 BOOST_FOREACH(boost::tie(boost::tuples::ignore, service), oldServices) {
280                         if (!service)
281                                 continue;
282
283                         if (!newServices->Contains(service->GetName()))
284                                 service->Unregister();
285                 }
286         }
287
288         newServices->Seal();
289
290         Set("slave_services", newServices);
291 }
292
293 void Host::OnAttributeChanged(const String& name)
294 {
295         ASSERT(!OwnsLock());
296
297         if (name == "hostgroups")
298                 HostGroup::InvalidateMembersCache();
299         else if (name == "services") {
300                 UpdateSlaveServices();
301         } else if (name == "notifications") {
302                 BOOST_FOREACH(const Service::Ptr& service, GetServices()) {
303                         service->UpdateSlaveNotifications();
304                 }
305         }
306 }
307
308 std::set<Service::Ptr> Host::GetServices(void) const
309 {
310         std::set<Service::Ptr> services;
311
312         boost::mutex::scoped_lock lock(m_ServiceMutex);
313
314         Service::WeakPtr wservice;
315         BOOST_FOREACH(boost::tie(boost::tuples::ignore, wservice), m_ServicesCache[GetName()]) {
316                 Service::Ptr service = wservice.lock();
317
318                 if (!service)
319                         continue;
320
321                 services.insert(service);
322         }
323
324         return services;
325 }
326
327 void Host::InvalidateServicesCache(void)
328 {
329         {
330                 boost::mutex::scoped_lock lock(m_ServiceMutex);
331
332                 if (m_ServicesCacheNeedsUpdate)
333                         return; /* Someone else has already requested a refresh. */
334
335                 if (!m_ServicesCacheTimer) {
336                         m_ServicesCacheTimer = boost::make_shared<Timer>();
337                         m_ServicesCacheTimer->SetInterval(0.5);
338                         m_ServicesCacheTimer->OnTimerExpired.connect(boost::bind(&Host::RefreshServicesCache));
339                         m_ServicesCacheTimer->Start();
340                 }
341
342                 m_ServicesCacheNeedsUpdate = true;
343         }
344 }
345
346 void Host::RefreshServicesCache(void)
347 {
348         {
349                 boost::mutex::scoped_lock lock(m_ServiceMutex);
350
351                 if (!m_ServicesCacheNeedsUpdate)
352                         return;
353
354                 m_ServicesCacheNeedsUpdate = false;
355         }
356
357         Log(LogDebug, "icinga", "Updating Host services cache.");
358
359         std::map<String, std::map<String, Service::WeakPtr> > newServicesCache;
360
361         BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Service")) {
362                 const Service::Ptr& service = static_pointer_cast<Service>(object);
363
364                 Host::Ptr host = service->GetHost();
365
366                 if (!host)
367                         continue;
368
369                 // TODO: assert for duplicate short_names
370
371                 newServicesCache[host->GetName()][service->GetShortName()] = service;
372         }
373
374         boost::mutex::scoped_lock lock(m_ServiceMutex);
375         m_ServicesCache.swap(newServicesCache);
376 }
377
378 void Host::ValidateServiceDictionary(const ScriptTask::Ptr& task, const std::vector<Value>& arguments)
379 {
380         if (arguments.size() < 1)
381                 BOOST_THROW_EXCEPTION(std::invalid_argument("Missing argument: Location must be specified."));
382
383         if (arguments.size() < 2)
384                 BOOST_THROW_EXCEPTION(std::invalid_argument("Missing argument: Attribute dictionary must be specified."));
385
386         String location = arguments[0];
387         Dictionary::Ptr attrs = arguments[1];
388         ObjectLock olock(attrs);
389
390         String key;
391         Value value;
392         BOOST_FOREACH(boost::tie(key, value), attrs) {
393                 std::vector<String> templates;
394
395                 if (!value.IsObjectType<Dictionary>())
396                         BOOST_THROW_EXCEPTION(std::invalid_argument("Service description must be a dictionary."));
397
398                 Dictionary::Ptr serviceDesc = value;
399
400                 Array::Ptr templatesArray = serviceDesc->Get("templates");
401
402                 if (templatesArray) {
403                         ObjectLock tlock(templatesArray);
404
405                         BOOST_FOREACH(const Value& tmpl, templatesArray) {
406                                 templates.push_back(tmpl);
407                         }
408                 }
409
410                 BOOST_FOREACH(const String& name, templates) {
411                         ConfigItem::Ptr item;
412
413                         ConfigCompilerContext *context = ConfigCompilerContext::GetContext();
414
415                         if (context)
416                                 item = context->GetItem("Service", name);
417
418                         /* ignore already active objects while we're in the compiler
419                          * context and linking to existing items is disabled. */
420                         if (!item && (!context || (context->GetFlags() & CompilerLinkExisting)))
421                                 item = ConfigItem::GetObject("Service", name);
422
423                         if (!item) {
424                                 ConfigCompilerContext::GetContext()->AddError(false, "Validation failed for " +
425                                     location + ": Template '" + name + "' not found.");
426                         }
427                 }
428         }
429
430         task->FinishResult(Empty);
431 }
432
433 Service::Ptr Host::GetServiceByShortName(const Value& name) const
434 {
435         if (name.IsScalar()) {
436                 {
437                         boost::mutex::scoped_lock lock(m_ServiceMutex);
438
439                         std::map<String, Service::WeakPtr>& services = m_ServicesCache[GetName()];
440                         std::map<String, Service::WeakPtr>::iterator it = services.find(name);
441
442                         if (it != services.end()) {
443                                 Service::Ptr service = it->second.lock();
444                                 ASSERT(service);
445                                 return service;
446                         }
447                 }
448
449                 return Service::Ptr();
450         } else if (name.IsObjectType<Dictionary>()) {
451                 Dictionary::Ptr dict = name;
452                 String short_name;
453
454                 ASSERT(dict->IsSealed());
455
456                 return Service::GetByNamePair(dict->Get("host"), dict->Get("service"));
457         } else {
458                 BOOST_THROW_EXCEPTION(std::invalid_argument("Host/Service name pair is invalid."));
459         }
460 }
461
462 std::set<Host::Ptr> Host::GetParentHosts(void) const
463 {
464         std::set<Host::Ptr> parents;
465
466         Array::Ptr dependencies = GetHostDependencies();
467
468         if (dependencies) {
469                 ObjectLock olock(dependencies);
470
471                 BOOST_FOREACH(const Value& value, dependencies) {
472                         if (value == GetName())
473                                 continue;
474
475                         Host::Ptr host = GetByName(value);
476
477                         if (!host)
478                                 continue;
479
480                         parents.insert(host);
481                 }
482         }
483
484         return parents;
485 }
486
487 Service::Ptr Host::GetHostCheckService(void) const
488 {
489         String host_check = GetHostCheck();
490
491         if (host_check.IsEmpty())
492                 return Service::Ptr();
493
494         return GetServiceByShortName(host_check);
495 }
496
497 std::set<Service::Ptr> Host::GetParentServices(void) const
498 {
499         std::set<Service::Ptr> parents;
500
501         Array::Ptr dependencies = GetServiceDependencies();
502
503         if (dependencies) {
504                 ObjectLock olock(dependencies);
505
506                 BOOST_FOREACH(const Value& value, dependencies) {
507                         parents.insert(GetServiceByShortName(value));
508                 }
509         }
510
511         return parents;
512 }
513
514 HostState Host::GetState(void) const
515 {
516         if (!IsReachable())
517                 return HostUnreachable;
518
519         Service::Ptr hc = GetHostCheckService();
520
521         if (!hc)
522                 return HostUp;
523
524         switch (hc->GetState()) {
525                 case StateOK:
526                 case StateWarning:
527                         return HostUp;
528                 default:
529                         return HostDown;
530         }
531 }
532
533 StateType Host::GetStateType(void) const
534 {
535         Service::Ptr hc = GetHostCheckService();
536
537         if (!hc)
538                 return StateTypeHard;
539
540         return hc->GetStateType();
541 }
542
543 HostState Host::GetLastState(void) const
544 {
545         ASSERT(!OwnsLock());
546
547         if (!IsReachable())
548                 return HostUnreachable;
549
550         Service::Ptr hc = GetHostCheckService();
551
552         if (!hc)
553                 return HostUp;
554
555         switch (hc->GetLastState()) {
556                 case StateOK:
557                 case StateWarning:
558                         return HostUp;
559                 default:
560                         return HostDown;
561         }
562 }
563
564 StateType Host::GetLastStateType(void) const
565 {
566         Service::Ptr hc = GetHostCheckService();
567
568         if (!hc)
569                 return StateTypeHard;
570
571         return hc->GetLastStateType();
572 }
573
574 String Host::HostStateToString(HostState state)
575 {
576         switch (state) {
577                 case HostUp:
578                         return "UP";
579                 case HostDown:
580                         return "DOWN";
581                 case HostUnreachable:
582                         return "UNREACHABLE";
583                 default:
584                         return "INVALID";
585         }
586 }
587
588 Dictionary::Ptr Host::CalculateDynamicMacros(void) const
589 {
590         ASSERT(!OwnsLock());
591
592         Dictionary::Ptr macros = boost::make_shared<Dictionary>();
593
594         {
595                 ObjectLock olock(this);
596
597                 macros->Set("HOSTNAME", GetName());
598                 macros->Set("HOSTDISPLAYNAME", GetDisplayName());
599                 macros->Set("HOSTALIAS", GetName());
600         }
601
602         Dictionary::Ptr cr;
603
604         Service::Ptr hc = GetHostCheckService();
605
606         if (hc) {
607                 ObjectLock olock(hc);
608
609                 macros->Set("HOSTSTATE", HostStateToString(GetState()));
610                 macros->Set("HOSTSTATEID", GetState());
611                 macros->Set("HOSTSTATETYPE", Service::StateTypeToString(hc->GetStateType()));
612                 macros->Set("HOSTATTEMPT", hc->GetCurrentCheckAttempt());
613                 macros->Set("MAXHOSTATTEMPT", hc->GetMaxCheckAttempts());
614
615                 macros->Set("LASTHOSTSTATE", HostStateToString(GetLastState()));
616                 macros->Set("LASTHOSTSTATEID", GetLastState());
617                 macros->Set("LASTHOSTSTATETYPE", Service::StateTypeToString(GetLastStateType()));
618                 macros->Set("LASTHOSTSTATECHANGE", (long)hc->GetLastStateChange());
619
620                 cr = hc->GetLastCheckResult();
621         }
622
623         if (cr) {
624                 macros->Set("HOSTLATENCY", Service::CalculateLatency(cr));
625                 macros->Set("HOSTEXECUTIONTIME", Service::CalculateExecutionTime(cr));
626
627                 macros->Set("HOSTOUTPUT", cr->Get("output"));
628                 macros->Set("HOSTPERFDATA", cr->Get("performance_data_raw"));
629
630                 macros->Set("LASTHOSTCHECK", (long)cr->Get("schedule_start"));
631         }
632
633         macros->Seal();
634
635         return macros;
636 }