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