]> granicus.if.org Git - icinga2/blob - lib/icinga/host.cpp
custom attr: move into DynamicObject using GetCustom()
[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
235                         ExpressionList::Ptr host_exprl = boost::make_shared<ExpressionList>();
236                         item->GetLinkedExpressionList()->ExtractFiltered(keys, host_exprl);
237                         builder->AddExpressionList(host_exprl);
238
239                         /* Clone attributes from the service expression list. */
240                         std::vector<String> path;
241                         path.push_back("services");
242                         path.push_back(svcname);
243
244                         ExpressionList::Ptr svc_exprl = boost::make_shared<ExpressionList>();
245                         item->GetLinkedExpressionList()->ExtractPath(path, svc_exprl);
246                         builder->AddExpressionList(svc_exprl);
247
248                         ConfigItem::Ptr serviceItem = builder->Compile();
249                         DynamicObject::Ptr dobj = serviceItem->Commit();
250
251                         newServices->Set(name, serviceItem);
252                 }
253         }
254
255         if (oldServices) {
256                 ObjectLock olock(oldServices);
257
258                 ConfigItem::Ptr service;
259                 BOOST_FOREACH(boost::tie(boost::tuples::ignore, service), oldServices) {
260                         if (!service)
261                                 continue;
262
263                         if (!newServices->Contains(service->GetName()))
264                                 service->Unregister();
265                 }
266         }
267
268         newServices->Seal();
269
270         Set("slave_services", newServices);
271 }
272
273 void Host::OnAttributeChanged(const String& name)
274 {
275         ASSERT(!OwnsLock());
276
277         if (name == "hostgroups")
278                 HostGroup::InvalidateMembersCache();
279         else if (name == "services") {
280                 UpdateSlaveServices();
281         } else if (name == "notifications") {
282                 BOOST_FOREACH(const Service::Ptr& service, GetServices()) {
283                         service->UpdateSlaveNotifications();
284                 }
285         }
286 }
287
288 std::set<Service::Ptr> Host::GetServices(void) const
289 {
290         std::set<Service::Ptr> services;
291
292         boost::mutex::scoped_lock lock(l_ServiceMutex);
293
294         Service::WeakPtr wservice;
295         BOOST_FOREACH(boost::tie(boost::tuples::ignore, wservice), l_ServicesCache[GetName()]) {
296                 Service::Ptr service = wservice.lock();
297
298                 if (!service)
299                         continue;
300
301                 services.insert(service);
302         }
303
304         return services;
305 }
306
307 int Host::GetTotalServices(void) const
308 {
309         return GetServices().size();
310 }
311
312 void Host::InvalidateServicesCache(void)
313 {
314         {
315                 boost::mutex::scoped_lock lock(l_ServiceMutex);
316
317                 if (l_ServicesCacheNeedsUpdate)
318                         return; /* Someone else has already requested a refresh. */
319
320                 if (!l_ServicesCacheTimer) {
321                         l_ServicesCacheTimer = boost::make_shared<Timer>();
322                         l_ServicesCacheTimer->SetInterval(0.5);
323                         l_ServicesCacheTimer->OnTimerExpired.connect(boost::bind(&Host::RefreshServicesCache));
324                         l_ServicesCacheTimer->Start();
325                 }
326
327                 l_ServicesCacheNeedsUpdate = true;
328         }
329 }
330
331 void Host::RefreshServicesCache(void)
332 {
333         {
334                 boost::mutex::scoped_lock lock(l_ServiceMutex);
335
336                 if (!l_ServicesCacheNeedsUpdate)
337                         return;
338
339                 l_ServicesCacheNeedsUpdate = false;
340         }
341
342         Log(LogDebug, "icinga", "Updating Host services cache.");
343
344         std::map<String, std::map<String, Service::WeakPtr> > newServicesCache;
345
346         BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Service")) {
347                 const Service::Ptr& service = static_pointer_cast<Service>(object);
348
349                 Host::Ptr host = service->GetHost();
350
351                 if (!host)
352                         continue;
353
354                 // TODO: assert for duplicate short_names
355
356                 newServicesCache[host->GetName()][service->GetShortName()] = service;
357         }
358
359         boost::mutex::scoped_lock lock(l_ServiceMutex);
360         l_ServicesCache.swap(newServicesCache);
361 }
362
363 Value Host::ValidateServiceDictionary(const String& location, const Dictionary::Ptr& attrs)
364 {
365         ObjectLock olock(attrs);
366
367         String key;
368         Value value;
369         BOOST_FOREACH(boost::tie(key, value), attrs) {
370                 std::vector<String> templates;
371
372                 if (!value.IsObjectType<Dictionary>())
373                         BOOST_THROW_EXCEPTION(std::invalid_argument("Service description must be a dictionary."));
374
375                 Dictionary::Ptr serviceDesc = value;
376
377                 Array::Ptr templatesArray = serviceDesc->Get("templates");
378
379                 if (templatesArray) {
380                         ObjectLock tlock(templatesArray);
381
382                         BOOST_FOREACH(const Value& tmpl, templatesArray) {
383                                 templates.push_back(tmpl);
384                         }
385                 }
386
387                 BOOST_FOREACH(const String& name, templates) {
388                         ConfigItem::Ptr item;
389
390                         ConfigCompilerContext *context = ConfigCompilerContext::GetContext();
391
392                         if (context)
393                                 item = context->GetItem("Service", name);
394
395                         /* ignore already active objects while we're in the compiler
396                          * context and linking to existing items is disabled. */
397                         if (!item && (!context || (context->GetFlags() & CompilerLinkExisting)))
398                                 item = ConfigItem::GetObject("Service", name);
399
400                         if (!item) {
401                                 ConfigCompilerContext::GetContext()->AddError(false, "Validation failed for " +
402                                     location + ": Template '" + name + "' not found.");
403                         }
404                 }
405         }
406
407         return Empty;
408 }
409
410 Service::Ptr Host::GetServiceByShortName(const Value& name) const
411 {
412         if (name.IsScalar()) {
413                 {
414                         boost::mutex::scoped_lock lock(l_ServiceMutex);
415
416                         std::map<String, Service::WeakPtr>& services = l_ServicesCache[GetName()];
417                         std::map<String, Service::WeakPtr>::iterator it = services.find(name);
418
419                         if (it != services.end()) {
420                                 Service::Ptr service = it->second.lock();
421                                 ASSERT(service);
422                                 return service;
423                         }
424                 }
425
426                 return Service::Ptr();
427         } else if (name.IsObjectType<Dictionary>()) {
428                 Dictionary::Ptr dict = name;
429                 String short_name;
430
431                 return Service::GetByNamePair(dict->Get("host"), dict->Get("service"));
432         } else {
433                 BOOST_THROW_EXCEPTION(std::invalid_argument("Host/Service name pair is invalid: " + name.Serialize()));
434         }
435 }
436
437 std::set<Host::Ptr> Host::GetParentHosts(void) const
438 {
439         std::set<Host::Ptr> parents;
440
441         Array::Ptr dependencies = GetHostDependencies();
442
443         if (dependencies) {
444                 ObjectLock olock(dependencies);
445
446                 BOOST_FOREACH(const Value& value, dependencies) {
447                         if (value == GetName())
448                                 continue;
449
450                         Host::Ptr host = GetByName(value);
451
452                         if (!host)
453                                 continue;
454
455                         parents.insert(host);
456                 }
457         }
458
459         return parents;
460 }
461
462 Service::Ptr Host::GetHostCheckService(void) const
463 {
464         String host_check = GetHostCheck();
465
466         if (host_check.IsEmpty())
467                 return Service::Ptr();
468
469         return GetServiceByShortName(host_check);
470 }
471
472 std::set<Service::Ptr> Host::GetParentServices(void) const
473 {
474         std::set<Service::Ptr> parents;
475
476         Array::Ptr dependencies = GetServiceDependencies();
477
478         if (dependencies) {
479                 ObjectLock olock(dependencies);
480
481                 BOOST_FOREACH(const Value& value, dependencies) {
482                         parents.insert(GetServiceByShortName(value));
483                 }
484         }
485
486         return parents;
487 }
488
489 HostState Host::CalculateState(ServiceState state, bool reachable)
490 {
491         if (!reachable)
492                 return HostUnreachable;
493
494         switch (state) {
495                 case StateOK:
496                 case StateWarning:
497                         return HostUp;
498                 default:
499                         return HostDown;
500         }
501 }
502
503 HostState Host::GetState(void) const
504 {
505         ASSERT(!OwnsLock());
506
507         if (!IsReachable())
508                 return HostUnreachable;
509
510         Service::Ptr hc = GetHostCheckService();
511
512         if (!hc)
513                 return HostUp;
514
515         switch (hc->GetState()) {
516                 case StateOK:
517                 case StateWarning:
518                         return HostUp;
519                 default:
520                         return HostDown;
521         }
522
523 }
524
525 HostState Host::GetLastState(void) const
526 {
527         ASSERT(!OwnsLock());
528
529         if (!IsReachable())
530                 return HostUnreachable;
531
532         Service::Ptr hc = GetHostCheckService();
533
534         if (!hc)
535                 return HostUp;
536
537         switch (hc->GetLastState()) {
538                 case StateOK:
539                 case StateWarning:
540                         return HostUp;
541                 default:
542                         return HostDown;
543         }
544 }
545
546 HostState Host::GetLastHardState(void) const
547 {
548         ASSERT(!OwnsLock());
549
550         if (!IsReachable())
551                 return HostUnreachable;
552
553         Service::Ptr hc = GetHostCheckService();
554
555         if (!hc)
556                 return HostUp;
557
558         switch (hc->GetLastHardState()) {
559                 case StateOK:
560                 case StateWarning:
561                         return HostUp;
562                 default:
563                         return HostDown;
564         }
565 }
566
567 double Host::GetLastStateChange(void) const
568 {
569         Service::Ptr hc = GetHostCheckService();
570
571         if (!hc)
572                 return IcingaApplication::GetInstance()->GetStartTime();
573
574         return hc->GetLastStateChange();
575 }
576
577
578 double Host::GetLastHardStateChange(void) const
579 {
580         Service::Ptr hc = GetHostCheckService();
581
582         if (!hc)
583                 return IcingaApplication::GetInstance()->GetStartTime();
584
585         return hc->GetLastHardStateChange();
586 }
587
588 StateType Host::GetLastStateType(void) const
589 {
590         Service::Ptr hc = GetHostCheckService();
591
592         if (!hc)
593                 return StateTypeHard;
594
595         return hc->GetLastStateType();
596 }
597
598 StateType Host::GetStateType(void) const
599 {
600         Service::Ptr hc = GetHostCheckService();
601
602         if (!hc)
603                 return StateTypeHard;
604
605         return hc->GetStateType();
606 }
607
608 String Host::StateToString(HostState state)
609 {
610         switch (state) {
611                 case HostUp:
612                         return "UP";
613                 case HostDown:
614                         return "DOWN";
615                 case HostUnreachable:
616                         return "UNREACHABLE";
617                 default:
618                         return "INVALID";
619         }
620 }
621
622 bool Host::ResolveMacro(const String& macro, const Dictionary::Ptr&, String *result) const
623 {
624         if (macro == "HOSTNAME") {
625                 *result = GetName();
626                 return true;
627         }
628         else if (macro == "HOSTDISPLAYNAME" || macro == "HOSTALIAS") {
629                 *result = GetDisplayName();
630                 return true;
631         }
632
633         Service::Ptr hc = GetHostCheckService();
634         Dictionary::Ptr hccr;
635
636         if (hc) {
637                 ServiceState state = hc->GetState();
638                 bool reachable = IsReachable();
639
640                 if (macro == "HOSTSTATE") {
641                         *result = Convert::ToString(CalculateState(state, reachable));
642                         return true;
643                 } else if (macro == "HOSTSTATEID") {
644                         *result = Convert::ToString(state);
645                         return true;
646                 } else if (macro == "HOSTSTATETYPE") {
647                         *result = Service::StateTypeToString(hc->GetStateType());
648                         return true;
649                 } else if (macro == "HOSTATTEMPT") {
650                         *result = Convert::ToString(hc->GetCurrentCheckAttempt());
651                         return true;
652                 } else if (macro == "MAXHOSTATTEMPT") {
653                         *result = Convert::ToString(hc->GetMaxCheckAttempts());
654                         return true;
655                 } else if (macro == "LASTHOSTSTATE") {
656                         *result = StateToString(GetLastState());
657                         return true;
658                 } else if (macro == "LASTHOSTSTATEID") {
659                         *result = Convert::ToString(GetLastState());
660                         return true;
661                 } else if (macro == "LASTHOSTSTATETYPE") {
662                         *result = Service::StateTypeToString(GetLastStateType());
663                         return true;
664                 } else if (macro == "LASTHOSTSTATECHANGE") {
665                         *result = Convert::ToString((long)hc->GetLastStateChange());
666                         return true;
667                 }
668
669                 hccr = hc->GetLastCheckResult();
670         }
671
672         if (hccr) {
673                 if (macro == "HOSTLATENCY") {
674                         *result = Convert::ToString(Service::CalculateLatency(hccr));
675                         return true;
676                 } else if (macro == "HOSTEXECUTIONTIME") {
677                         *result = Convert::ToString(Service::CalculateExecutionTime(hccr));
678                         return true;
679                 } else if (macro == "HOSTOUTPUT") {
680                         *result = hccr->Get("output");
681                         return true;
682                 } else if (macro == "HOSTPERFDATA") {
683                         *result = hccr->Get("performance_data_raw");
684                         return true;
685                 } else if (macro == "LASTHOSTCHECK") {
686                         *result = Convert::ToString((long)hccr->Get("schedule_start"));
687                         return true;
688                 }
689         }
690
691         Dictionary::Ptr macros = GetMacros();
692
693         String name = macro;
694
695         if (name == "HOSTADDRESS")
696                 name = "address";
697         else if (macro == "HOSTADDRESS6")
698                 name = "address6";
699
700         if (macros && macros->Contains(name)) {
701                 *result = macros->Get(name);
702                 return true;
703         }
704
705         if (macro == "HOSTADDRESS" || macro == "HOSTADDRESS6") {
706                 *result = GetName();
707                 return true;
708         }
709
710         return false;
711 }