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