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