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