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