]> granicus.if.org Git - icinga2/blob - lib/icinga/host.cpp
add GetChildHosts() to host object
[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 "icinga/host.h"
21 #include "icinga/service.h"
22 #include "icinga/hostgroup.h"
23 #include "icinga/icingaapplication.h"
24 #include "base/dynamictype.h"
25 #include "base/objectlock.h"
26 #include "base/logger_fwd.h"
27 #include "base/timer.h"
28 #include "base/convert.h"
29 #include "base/scriptfunction.h"
30 #include "base/utility.h"
31 #include "config/configitembuilder.h"
32 #include "config/configcompilercontext.h"
33 #include <boost/tuple/tuple.hpp>
34 #include <boost/smart_ptr/make_shared.hpp>
35 #include <boost/foreach.hpp>
36
37 using namespace icinga;
38
39 static boost::mutex l_ServiceMutex;
40 static std::map<String, std::map<String, Service::WeakPtr> > l_ServicesCache;
41 static bool l_ServicesCacheNeedsUpdate = false;
42 static Timer::Ptr l_ServicesCacheTimer;
43
44 REGISTER_SCRIPTFUNCTION(ValidateServiceDictionary, &Host::ValidateServiceDictionary);
45
46 REGISTER_TYPE(Host);
47
48 Host::Host(const Dictionary::Ptr& serializedUpdate)
49         : DynamicObject(serializedUpdate)
50 {
51         RegisterAttribute("display_name", Attribute_Config, &m_DisplayName);
52         RegisterAttribute("hostgroups", Attribute_Config, &m_HostGroups);
53         RegisterAttribute("macros", Attribute_Config, &m_Macros);
54         RegisterAttribute("hostdependencies", Attribute_Config, &m_HostDependencies);
55         RegisterAttribute("servicedependencies", Attribute_Config, &m_ServiceDependencies);
56         RegisterAttribute("hostcheck", Attribute_Config, &m_HostCheck);
57
58 }
59
60 Host::~Host(void)
61 {
62         HostGroup::InvalidateMembersCache();
63
64         if (m_SlaveServices) {
65                 ConfigItem::Ptr service;
66                 BOOST_FOREACH(boost::tie(boost::tuples::ignore, service), m_SlaveServices) {
67                         service->Unregister();
68                 }
69         }
70 }
71
72 void Host::OnRegistrationCompleted(void)
73 {
74         ASSERT(!OwnsLock());
75
76         DynamicObject::OnRegistrationCompleted();
77
78         Host::InvalidateServicesCache();
79         UpdateSlaveServices();
80 }
81
82 String Host::GetDisplayName(void) const
83 {
84         if (!m_DisplayName.IsEmpty())
85                 return m_DisplayName;
86         else
87                 return GetName();
88 }
89
90 Host::Ptr Host::GetByName(const String& name)
91 {
92         DynamicObject::Ptr configObject = DynamicObject::GetObject("Host", name);
93
94         return dynamic_pointer_cast<Host>(configObject);
95 }
96
97 Array::Ptr Host::GetGroups(void) const
98 {
99         return m_HostGroups;
100 }
101
102 Dictionary::Ptr Host::GetMacros(void) const
103 {
104         return m_Macros;
105 }
106
107 Array::Ptr Host::GetHostDependencies(void) const
108 {
109         return m_HostDependencies;;
110 }
111
112 Array::Ptr Host::GetServiceDependencies(void) const
113 {
114         return m_ServiceDependencies;
115 }
116
117 String Host::GetHostCheck(void) const
118 {
119         return m_HostCheck;
120 }
121
122 bool Host::IsReachable(void) const
123 {
124         ASSERT(!OwnsLock());
125
126         std::set<Service::Ptr> parentServices = GetParentServices();
127
128         BOOST_FOREACH(const Service::Ptr& service, parentServices) {
129                 ObjectLock olock(service);
130
131                 /* ignore pending services */
132                 if (!service->GetLastCheckResult())
133                         continue;
134
135                 /* ignore soft states */
136                 if (service->GetStateType() == StateTypeSoft)
137                         continue;
138
139                 /* ignore services states OK and Warning */
140                 if (service->GetState() == StateOK ||
141                     service->GetState() == StateWarning)
142                         continue;
143
144                 return false;
145         }
146
147         std::set<Host::Ptr> parentHosts = GetParentHosts();
148
149         BOOST_FOREACH(const Host::Ptr& host, parentHosts) {
150                 Service::Ptr hc = host->GetHostCheckService();
151
152                 /* ignore hosts that don't have a hostcheck */
153                 if (!hc)
154                         continue;
155
156                 ObjectLock olock(hc);
157
158                 /* ignore soft states */
159                 if (hc->GetStateType() == StateTypeSoft)
160                         continue;
161
162                 /* ignore hosts that are up */
163                 if (hc->GetState() == StateOK)
164                         continue;
165
166                 return false;
167         }
168
169         return true;
170 }
171
172 void Host::UpdateSlaveServices(void)
173 {
174         ASSERT(!OwnsLock());
175
176         ConfigItem::Ptr item = ConfigItem::GetObject("Host", GetName());
177
178         /* Don't create slave services unless we own this object */
179         if (!item)
180                 return;
181
182         Dictionary::Ptr oldServices = m_SlaveServices;
183         Dictionary::Ptr serviceDescs = Get("services");
184
185         Dictionary::Ptr newServices = boost::make_shared<Dictionary>();
186
187         if (serviceDescs) {
188                 ObjectLock olock(serviceDescs);
189                 String svcname;
190                 Value svcdesc;
191                 BOOST_FOREACH(boost::tie(svcname, svcdesc), serviceDescs) {
192                         if (svcdesc.IsScalar())
193                                 svcname = svcdesc;
194
195                         std::ostringstream namebuf;
196                         namebuf << GetName() << ":" << svcname;
197                         String name = namebuf.str();
198
199                         ConfigItemBuilder::Ptr builder = boost::make_shared<ConfigItemBuilder>(item->GetDebugInfo());
200                         builder->SetType("Service");
201                         builder->SetName(name);
202                         builder->AddExpression("host_name", OperatorSet, GetName());
203                         builder->AddExpression("display_name", OperatorSet, svcname);
204                         builder->AddExpression("short_name", OperatorSet, svcname);
205
206                         if (!svcdesc.IsObjectType<Dictionary>())
207                                 BOOST_THROW_EXCEPTION(std::invalid_argument("Service description must be either a string or a dictionary."));
208
209                         Dictionary::Ptr service = svcdesc;
210
211                         Array::Ptr templates = service->Get("templates");
212
213                         if (templates) {
214                                 ObjectLock olock(templates);
215
216                                 BOOST_FOREACH(const Value& tmpl, templates) {
217                                         builder->AddParent(tmpl);
218                                 }
219                         }
220
221                         /* Clone attributes from the host object. */
222                         std::set<String, string_iless> keys;
223                         keys.insert("check_interval");
224                         keys.insert("retry_interval");
225                         keys.insert("servicegroups");
226                         keys.insert("checkers");
227                         keys.insert("notification_interval");
228                         keys.insert("notification_type_filter");
229                         keys.insert("notification_state_filter");
230                         keys.insert("check_period");
231                         keys.insert("servicedependencies");
232                         keys.insert("hostdependencies");
233                         keys.insert("export_macros");
234                         keys.insert("macros");
235                         keys.insert("custom");
236
237                         ExpressionList::Ptr host_exprl = boost::make_shared<ExpressionList>();
238                         item->GetLinkedExpressionList()->ExtractFiltered(keys, host_exprl);
239                         builder->AddExpressionList(host_exprl);
240
241                         /* Clone attributes from the service expression list. */
242                         std::vector<String> path;
243                         path.push_back("services");
244                         path.push_back(svcname);
245
246                         ExpressionList::Ptr svc_exprl = boost::make_shared<ExpressionList>();
247                         item->GetLinkedExpressionList()->ExtractPath(path, svc_exprl);
248                         builder->AddExpressionList(svc_exprl);
249
250                         ConfigItem::Ptr serviceItem = builder->Compile();
251                         DynamicObject::Ptr dobj = serviceItem->Commit();
252
253                         newServices->Set(name, serviceItem);
254                 }
255         }
256
257         if (oldServices) {
258                 ObjectLock olock(oldServices);
259
260                 ConfigItem::Ptr service;
261                 BOOST_FOREACH(boost::tie(boost::tuples::ignore, service), oldServices) {
262                         if (!service)
263                                 continue;
264
265                         if (!newServices->Contains(service->GetName()))
266                                 service->Unregister();
267                 }
268         }
269
270         newServices->Seal();
271
272         Set("slave_services", newServices);
273 }
274
275 void Host::OnAttributeChanged(const String& name)
276 {
277         ASSERT(!OwnsLock());
278
279         if (name == "hostgroups")
280                 HostGroup::InvalidateMembersCache();
281         else if (name == "services") {
282                 UpdateSlaveServices();
283         } else if (name == "notifications") {
284                 BOOST_FOREACH(const Service::Ptr& service, GetServices()) {
285                         service->UpdateSlaveNotifications();
286                 }
287         }
288 }
289
290 std::set<Service::Ptr> Host::GetServices(void) const
291 {
292         std::set<Service::Ptr> services;
293
294         boost::mutex::scoped_lock lock(l_ServiceMutex);
295
296         Service::WeakPtr wservice;
297         BOOST_FOREACH(boost::tie(boost::tuples::ignore, wservice), l_ServicesCache[GetName()]) {
298                 Service::Ptr service = wservice.lock();
299
300                 if (!service)
301                         continue;
302
303                 services.insert(service);
304         }
305
306         return services;
307 }
308
309 int Host::GetTotalServices(void) const
310 {
311         return GetServices().size();
312 }
313
314 void Host::InvalidateServicesCache(void)
315 {
316         {
317                 boost::mutex::scoped_lock lock(l_ServiceMutex);
318
319                 if (l_ServicesCacheNeedsUpdate)
320                         return; /* Someone else has already requested a refresh. */
321
322                 if (!l_ServicesCacheTimer) {
323                         l_ServicesCacheTimer = boost::make_shared<Timer>();
324                         l_ServicesCacheTimer->SetInterval(0.5);
325                         l_ServicesCacheTimer->OnTimerExpired.connect(boost::bind(&Host::RefreshServicesCache));
326                         l_ServicesCacheTimer->Start();
327                 }
328
329                 l_ServicesCacheNeedsUpdate = true;
330         }
331 }
332
333 void Host::RefreshServicesCache(void)
334 {
335         {
336                 boost::mutex::scoped_lock lock(l_ServiceMutex);
337
338                 if (!l_ServicesCacheNeedsUpdate)
339                         return;
340
341                 l_ServicesCacheNeedsUpdate = false;
342         }
343
344         Log(LogDebug, "icinga", "Updating Host services cache.");
345
346         std::map<String, std::map<String, Service::WeakPtr> > newServicesCache;
347
348         BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Service")) {
349                 const Service::Ptr& service = static_pointer_cast<Service>(object);
350
351                 Host::Ptr host = service->GetHost();
352
353                 if (!host)
354                         continue;
355
356                 // TODO: assert for duplicate short_names
357
358                 newServicesCache[host->GetName()][service->GetShortName()] = service;
359         }
360
361         boost::mutex::scoped_lock lock(l_ServiceMutex);
362         l_ServicesCache.swap(newServicesCache);
363 }
364
365 Value Host::ValidateServiceDictionary(const String& location, const Dictionary::Ptr& attrs)
366 {
367         ObjectLock olock(attrs);
368
369         String key;
370         Value value;
371         BOOST_FOREACH(boost::tie(key, value), attrs) {
372                 std::vector<String> templates;
373
374                 if (!value.IsObjectType<Dictionary>())
375                         BOOST_THROW_EXCEPTION(std::invalid_argument("Service description must be a dictionary."));
376
377                 Dictionary::Ptr serviceDesc = value;
378
379                 Array::Ptr templatesArray = serviceDesc->Get("templates");
380
381                 if (templatesArray) {
382                         ObjectLock tlock(templatesArray);
383
384                         BOOST_FOREACH(const Value& tmpl, templatesArray) {
385                                 templates.push_back(tmpl);
386                         }
387                 }
388
389                 BOOST_FOREACH(const String& name, templates) {
390                         ConfigItem::Ptr item;
391
392                         ConfigCompilerContext *context = ConfigCompilerContext::GetContext();
393
394                         if (context)
395                                 item = context->GetItem("Service", name);
396
397                         /* ignore already active objects while we're in the compiler
398                          * context and linking to existing items is disabled. */
399                         if (!item && (!context || (context->GetFlags() & CompilerLinkExisting)))
400                                 item = ConfigItem::GetObject("Service", name);
401
402                         if (!item) {
403                                 ConfigCompilerContext::GetContext()->AddError(false, "Validation failed for " +
404                                     location + ": Template '" + name + "' not found.");
405                         }
406                 }
407         }
408
409         return Empty;
410 }
411
412 Service::Ptr Host::GetServiceByShortName(const Value& name) const
413 {
414         if (name.IsScalar()) {
415                 {
416                         boost::mutex::scoped_lock lock(l_ServiceMutex);
417
418                         std::map<String, Service::WeakPtr>& services = l_ServicesCache[GetName()];
419                         std::map<String, Service::WeakPtr>::iterator it = services.find(name);
420
421                         if (it != services.end()) {
422                                 Service::Ptr service = it->second.lock();
423                                 ASSERT(service);
424                                 return service;
425                         }
426                 }
427
428                 return Service::Ptr();
429         } else if (name.IsObjectType<Dictionary>()) {
430                 Dictionary::Ptr dict = name;
431                 String short_name;
432
433                 return Service::GetByNamePair(dict->Get("host"), dict->Get("service"));
434         } else {
435                 BOOST_THROW_EXCEPTION(std::invalid_argument("Host/Service name pair is invalid: " + name.Serialize()));
436         }
437 }
438
439 std::set<Host::Ptr> Host::GetParentHosts(void) const
440 {
441         std::set<Host::Ptr> parents;
442
443         Array::Ptr dependencies = GetHostDependencies();
444
445         if (dependencies) {
446                 ObjectLock olock(dependencies);
447
448                 BOOST_FOREACH(const Value& value, dependencies) {
449                         if (value == GetName())
450                                 continue;
451
452                         Host::Ptr host = GetByName(value);
453
454                         if (!host)
455                                 continue;
456
457                         parents.insert(host);
458                 }
459         }
460
461         return parents;
462 }
463
464 std::set<Host::Ptr> Host::GetChildHosts(void) const
465 {
466         std::set<Host::Ptr> childs;
467
468         BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Host")) {
469                 const Host::Ptr& host = static_pointer_cast<Host>(object);
470
471                 Array::Ptr dependencies = host->GetHostDependencies();
472
473                 if (dependencies) {
474                         ObjectLock olock(dependencies);
475
476                         BOOST_FOREACH(const Value& value, dependencies) {
477                                 if (value == GetName())
478                                         childs.insert(host);
479                         }
480                 }
481         }
482
483         return childs;
484
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::CalculateState(ServiceState state, bool reachable)
515 {
516         if (!reachable)
517                 return HostUnreachable;
518
519         switch (state) {
520                 case StateOK:
521                 case StateWarning:
522                         return HostUp;
523                 default:
524                         return HostDown;
525         }
526 }
527
528 HostState Host::GetState(void) const
529 {
530         ASSERT(!OwnsLock());
531
532         if (!IsReachable())
533                 return HostUnreachable;
534
535         Service::Ptr hc = GetHostCheckService();
536
537         if (!hc)
538                 return HostUp;
539
540         switch (hc->GetState()) {
541                 case StateOK:
542                 case StateWarning:
543                         return HostUp;
544                 default:
545                         return HostDown;
546         }
547
548 }
549
550 HostState Host::GetLastState(void) const
551 {
552         ASSERT(!OwnsLock());
553
554         if (!IsReachable())
555                 return HostUnreachable;
556
557         Service::Ptr hc = GetHostCheckService();
558
559         if (!hc)
560                 return HostUp;
561
562         switch (hc->GetLastState()) {
563                 case StateOK:
564                 case StateWarning:
565                         return HostUp;
566                 default:
567                         return HostDown;
568         }
569 }
570
571 HostState Host::GetLastHardState(void) const
572 {
573         ASSERT(!OwnsLock());
574
575         if (!IsReachable())
576                 return HostUnreachable;
577
578         Service::Ptr hc = GetHostCheckService();
579
580         if (!hc)
581                 return HostUp;
582
583         switch (hc->GetLastHardState()) {
584                 case StateOK:
585                 case StateWarning:
586                         return HostUp;
587                 default:
588                         return HostDown;
589         }
590 }
591
592 double Host::GetLastStateUp(void) const
593 {
594         ASSERT(!OwnsLock());
595
596         Service::Ptr hc = GetHostCheckService();
597
598         if (!hc)
599                 return 0;
600
601         if (hc->GetLastStateOK() > hc->GetLastStateWarning())
602                 return hc->GetLastStateOK();
603         else
604                 return hc->GetLastStateWarning();
605 }
606
607 double Host::GetLastStateDown(void) const
608 {
609         ASSERT(!OwnsLock());
610
611         Service::Ptr hc = GetHostCheckService();
612
613         if (!hc)
614                 return 0;
615
616         return hc->GetLastStateCritical();
617 }
618
619 double Host::GetLastStateUnreachable(void) const
620 {
621         ASSERT(!OwnsLock());
622
623         Service::Ptr hc = GetHostCheckService();
624
625         if (!hc)
626                 return 0;
627
628         return hc->GetLastStateUnreachable();
629 }
630
631 double Host::GetLastStateChange(void) const
632 {
633         Service::Ptr hc = GetHostCheckService();
634
635         if (!hc)
636                 return IcingaApplication::GetInstance()->GetStartTime();
637
638         return hc->GetLastStateChange();
639 }
640
641
642 double Host::GetLastHardStateChange(void) const
643 {
644         Service::Ptr hc = GetHostCheckService();
645
646         if (!hc)
647                 return IcingaApplication::GetInstance()->GetStartTime();
648
649         return hc->GetLastHardStateChange();
650 }
651
652 StateType Host::GetLastStateType(void) const
653 {
654         Service::Ptr hc = GetHostCheckService();
655
656         if (!hc)
657                 return StateTypeHard;
658
659         return hc->GetLastStateType();
660 }
661
662 StateType Host::GetStateType(void) const
663 {
664         Service::Ptr hc = GetHostCheckService();
665
666         if (!hc)
667                 return StateTypeHard;
668
669         return hc->GetStateType();
670 }
671
672 String Host::StateToString(HostState state)
673 {
674         switch (state) {
675                 case HostUp:
676                         return "UP";
677                 case HostDown:
678                         return "DOWN";
679                 case HostUnreachable:
680                         return "UNREACHABLE";
681                 default:
682                         return "INVALID";
683         }
684 }
685
686 bool Host::ResolveMacro(const String& macro, const Dictionary::Ptr&, String *result) const
687 {
688         if (macro == "HOSTNAME") {
689                 *result = GetName();
690                 return true;
691         }
692         else if (macro == "HOSTDISPLAYNAME" || macro == "HOSTALIAS") {
693                 *result = GetDisplayName();
694                 return true;
695         }
696
697         Service::Ptr hc = GetHostCheckService();
698         Dictionary::Ptr hccr;
699
700         if (hc) {
701                 ServiceState state = hc->GetState();
702                 bool reachable = IsReachable();
703
704                 if (macro == "HOSTSTATE") {
705                         *result = Convert::ToString(CalculateState(state, reachable));
706                         return true;
707                 } else if (macro == "HOSTSTATEID") {
708                         *result = Convert::ToString(state);
709                         return true;
710                 } else if (macro == "HOSTSTATETYPE") {
711                         *result = Service::StateTypeToString(hc->GetStateType());
712                         return true;
713                 } else if (macro == "HOSTATTEMPT") {
714                         *result = Convert::ToString(hc->GetCurrentCheckAttempt());
715                         return true;
716                 } else if (macro == "MAXHOSTATTEMPT") {
717                         *result = Convert::ToString(hc->GetMaxCheckAttempts());
718                         return true;
719                 } else if (macro == "LASTHOSTSTATE") {
720                         *result = StateToString(GetLastState());
721                         return true;
722                 } else if (macro == "LASTHOSTSTATEID") {
723                         *result = Convert::ToString(GetLastState());
724                         return true;
725                 } else if (macro == "LASTHOSTSTATETYPE") {
726                         *result = Service::StateTypeToString(GetLastStateType());
727                         return true;
728                 } else if (macro == "LASTHOSTSTATECHANGE") {
729                         *result = Convert::ToString((long)hc->GetLastStateChange());
730                         return true;
731                 }
732
733                 hccr = hc->GetLastCheckResult();
734         }
735
736         if (hccr) {
737                 if (macro == "HOSTLATENCY") {
738                         *result = Convert::ToString(Service::CalculateLatency(hccr));
739                         return true;
740                 } else if (macro == "HOSTEXECUTIONTIME") {
741                         *result = Convert::ToString(Service::CalculateExecutionTime(hccr));
742                         return true;
743                 } else if (macro == "HOSTOUTPUT") {
744                         *result = hccr->Get("output");
745                         return true;
746                 } else if (macro == "HOSTPERFDATA") {
747                         *result = hccr->Get("performance_data_raw");
748                         return true;
749                 } else if (macro == "LASTHOSTCHECK") {
750                         *result = Convert::ToString((long)hccr->Get("schedule_start"));
751                         return true;
752                 }
753         }
754
755         Dictionary::Ptr macros = GetMacros();
756
757         String name = macro;
758
759         if (name == "HOSTADDRESS")
760                 name = "address";
761         else if (macro == "HOSTADDRESS6")
762                 name = "address6";
763
764         if (macros && macros->Contains(name)) {
765                 *result = macros->Get(name);
766                 return true;
767         }
768
769         if (macro == "HOSTADDRESS" || macro == "HOSTADDRESS6") {
770                 *result = GetName();
771                 return true;
772         }
773
774         return false;
775 }