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