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