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