]> granicus.if.org Git - icinga2/blob - lib/icinga/host.cpp
Use Expression::Extract* for notifications.
[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 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
231                         ExpressionList::Ptr host_exprl = boost::make_shared<ExpressionList>();
232                         item->GetLinkedExpressionList()->ExtractFiltered(keys, host_exprl);
233                         builder->AddExpressionList(host_exprl);
234
235                         /* Clone attributes from the service expression list. */
236                         std::vector<String> path;
237                         path.push_back("services");
238                         path.push_back(svcname);
239
240                         ExpressionList::Ptr svc_exprl = boost::make_shared<ExpressionList>();
241                         item->GetLinkedExpressionList()->ExtractPath(path, svc_exprl);
242                         builder->AddExpressionList(svc_exprl);
243
244                         ConfigItem::Ptr serviceItem = builder->Compile();
245                         DynamicObject::Ptr dobj = serviceItem->Commit();
246
247                         newServices->Set(name, serviceItem);
248                 }
249         }
250
251         if (oldServices) {
252                 ObjectLock olock(oldServices);
253
254                 ConfigItem::Ptr service;
255                 BOOST_FOREACH(boost::tie(boost::tuples::ignore, service), oldServices) {
256                         if (!service)
257                                 continue;
258
259                         if (!newServices->Contains(service->GetName()))
260                                 service->Unregister();
261                 }
262         }
263
264         newServices->Seal();
265
266         Set("slave_services", newServices);
267 }
268
269 void Host::OnAttributeChanged(const String& name)
270 {
271         ASSERT(!OwnsLock());
272
273         if (name == "hostgroups")
274                 HostGroup::InvalidateMembersCache();
275         else if (name == "services") {
276                 UpdateSlaveServices();
277         } else if (name == "notifications") {
278                 BOOST_FOREACH(const Service::Ptr& service, GetServices()) {
279                         service->UpdateSlaveNotifications();
280                 }
281         }
282 }
283
284 std::set<Service::Ptr> Host::GetServices(void) const
285 {
286         std::set<Service::Ptr> services;
287
288         boost::mutex::scoped_lock lock(l_ServiceMutex);
289
290         Service::WeakPtr wservice;
291         BOOST_FOREACH(boost::tie(boost::tuples::ignore, wservice), l_ServicesCache[GetName()]) {
292                 Service::Ptr service = wservice.lock();
293
294                 if (!service)
295                         continue;
296
297                 services.insert(service);
298         }
299
300         return services;
301 }
302
303 void Host::InvalidateServicesCache(void)
304 {
305         {
306                 boost::mutex::scoped_lock lock(l_ServiceMutex);
307
308                 if (l_ServicesCacheNeedsUpdate)
309                         return; /* Someone else has already requested a refresh. */
310
311                 if (!l_ServicesCacheTimer) {
312                         l_ServicesCacheTimer = boost::make_shared<Timer>();
313                         l_ServicesCacheTimer->SetInterval(0.5);
314                         l_ServicesCacheTimer->OnTimerExpired.connect(boost::bind(&Host::RefreshServicesCache));
315                         l_ServicesCacheTimer->Start();
316                 }
317
318                 l_ServicesCacheNeedsUpdate = true;
319         }
320 }
321
322 void Host::RefreshServicesCache(void)
323 {
324         {
325                 boost::mutex::scoped_lock lock(l_ServiceMutex);
326
327                 if (!l_ServicesCacheNeedsUpdate)
328                         return;
329
330                 l_ServicesCacheNeedsUpdate = false;
331         }
332
333         Log(LogDebug, "icinga", "Updating Host services cache.");
334
335         std::map<String, std::map<String, Service::WeakPtr> > newServicesCache;
336
337         BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Service")) {
338                 const Service::Ptr& service = static_pointer_cast<Service>(object);
339
340                 Host::Ptr host = service->GetHost();
341
342                 if (!host)
343                         continue;
344
345                 // TODO: assert for duplicate short_names
346
347                 newServicesCache[host->GetName()][service->GetShortName()] = service;
348         }
349
350         boost::mutex::scoped_lock lock(l_ServiceMutex);
351         l_ServicesCache.swap(newServicesCache);
352 }
353
354 void Host::ValidateServiceDictionary(const ScriptTask::Ptr& task, const std::vector<Value>& arguments)
355 {
356         if (arguments.size() < 1)
357                 BOOST_THROW_EXCEPTION(std::invalid_argument("Missing argument: Location must be specified."));
358
359         if (arguments.size() < 2)
360                 BOOST_THROW_EXCEPTION(std::invalid_argument("Missing argument: Attribute dictionary must be specified."));
361
362         String location = arguments[0];
363         Dictionary::Ptr attrs = arguments[1];
364         ObjectLock olock(attrs);
365
366         String key;
367         Value value;
368         BOOST_FOREACH(boost::tie(key, value), attrs) {
369                 std::vector<String> templates;
370
371                 if (!value.IsObjectType<Dictionary>())
372                         BOOST_THROW_EXCEPTION(std::invalid_argument("Service description must be a dictionary."));
373
374                 Dictionary::Ptr serviceDesc = value;
375
376                 Array::Ptr templatesArray = serviceDesc->Get("templates");
377
378                 if (templatesArray) {
379                         ObjectLock tlock(templatesArray);
380
381                         BOOST_FOREACH(const Value& tmpl, templatesArray) {
382                                 templates.push_back(tmpl);
383                         }
384                 }
385
386                 BOOST_FOREACH(const String& name, templates) {
387                         ConfigItem::Ptr item;
388
389                         ConfigCompilerContext *context = ConfigCompilerContext::GetContext();
390
391                         if (context)
392                                 item = context->GetItem("Service", name);
393
394                         /* ignore already active objects while we're in the compiler
395                          * context and linking to existing items is disabled. */
396                         if (!item && (!context || (context->GetFlags() & CompilerLinkExisting)))
397                                 item = ConfigItem::GetObject("Service", name);
398
399                         if (!item) {
400                                 ConfigCompilerContext::GetContext()->AddError(false, "Validation failed for " +
401                                     location + ": Template '" + name + "' not found.");
402                         }
403                 }
404         }
405
406         task->FinishResult(Empty);
407 }
408
409 Service::Ptr Host::GetServiceByShortName(const Value& name) const
410 {
411         if (name.IsScalar()) {
412                 {
413                         boost::mutex::scoped_lock lock(l_ServiceMutex);
414
415                         std::map<String, Service::WeakPtr>& services = l_ServicesCache[GetName()];
416                         std::map<String, Service::WeakPtr>::iterator it = services.find(name);
417
418                         if (it != services.end()) {
419                                 Service::Ptr service = it->second.lock();
420                                 ASSERT(service);
421                                 return service;
422                         }
423                 }
424
425                 return Service::Ptr();
426         } else if (name.IsObjectType<Dictionary>()) {
427                 Dictionary::Ptr dict = name;
428                 String short_name;
429
430                 ASSERT(dict->IsSealed());
431
432                 return Service::GetByNamePair(dict->Get("host"), dict->Get("service"));
433         } else {
434                 BOOST_THROW_EXCEPTION(std::invalid_argument("Host/Service name pair is invalid."));
435         }
436 }
437
438 std::set<Host::Ptr> Host::GetParentHosts(void) const
439 {
440         std::set<Host::Ptr> parents;
441
442         Array::Ptr dependencies = GetHostDependencies();
443
444         if (dependencies) {
445                 ObjectLock olock(dependencies);
446
447                 BOOST_FOREACH(const Value& value, dependencies) {
448                         if (value == GetName())
449                                 continue;
450
451                         Host::Ptr host = GetByName(value);
452
453                         if (!host)
454                                 continue;
455
456                         parents.insert(host);
457                 }
458         }
459
460         return parents;
461 }
462
463 Service::Ptr Host::GetHostCheckService(void) const
464 {
465         String host_check = GetHostCheck();
466
467         if (host_check.IsEmpty())
468                 return Service::Ptr();
469
470         return GetServiceByShortName(host_check);
471 }
472
473 std::set<Service::Ptr> Host::GetParentServices(void) const
474 {
475         std::set<Service::Ptr> parents;
476
477         Array::Ptr dependencies = GetServiceDependencies();
478
479         if (dependencies) {
480                 ObjectLock olock(dependencies);
481
482                 BOOST_FOREACH(const Value& value, dependencies) {
483                         parents.insert(GetServiceByShortName(value));
484                 }
485         }
486
487         return parents;
488 }
489
490 HostState Host::CalculateState(ServiceState state, bool reachable)
491 {
492         if (!reachable)
493                 return HostUnreachable;
494
495         switch (state) {
496                 case StateOK:
497                 case StateWarning:
498                         return HostUp;
499                 default:
500                         return HostDown;
501         }
502 }
503
504 HostState Host::GetLastState(void) const
505 {
506         ASSERT(!OwnsLock());
507
508         if (!IsReachable())
509                 return HostUnreachable;
510
511         Service::Ptr hc = GetHostCheckService();
512
513         if (!hc)
514                 return HostUp;
515
516         switch (hc->GetLastState()) {
517                 case StateOK:
518                 case StateWarning:
519                         return HostUp;
520                 default:
521                         return HostDown;
522         }
523 }
524
525 StateType Host::GetLastStateType(void) const
526 {
527         Service::Ptr hc = GetHostCheckService();
528
529         if (!hc)
530                 return StateTypeHard;
531
532         return hc->GetLastStateType();
533 }
534
535 String Host::StateToString(HostState state)
536 {
537         switch (state) {
538                 case HostUp:
539                         return "UP";
540                 case HostDown:
541                         return "DOWN";
542                 case HostUnreachable:
543                         return "UNREACHABLE";
544                 default:
545                         return "INVALID";
546         }
547 }
548
549 Dictionary::Ptr Host::CalculateDynamicMacros(void) const
550 {
551         ASSERT(!OwnsLock());
552
553         Dictionary::Ptr macros = boost::make_shared<Dictionary>();
554
555         {
556                 ObjectLock olock(this);
557
558                 macros->Set("HOSTNAME", GetName());
559                 macros->Set("HOSTDISPLAYNAME", GetDisplayName());
560                 macros->Set("HOSTALIAS", GetName());
561         }
562
563         Dictionary::Ptr cr;
564
565         Service::Ptr hc = GetHostCheckService();
566
567         if (hc) {
568                 ObjectLock olock(hc);
569
570                 ServiceState state = hc->GetState();
571                 bool reachable = IsReachable();
572
573                 macros->Set("HOSTSTATE", CalculateState(state, reachable));
574                 macros->Set("HOSTSTATEID", state);
575                 macros->Set("HOSTSTATETYPE", Service::StateTypeToString(hc->GetStateType()));
576                 macros->Set("HOSTATTEMPT", hc->GetCurrentCheckAttempt());
577                 macros->Set("MAXHOSTATTEMPT", hc->GetMaxCheckAttempts());
578
579                 macros->Set("LASTHOSTSTATE", StateToString(GetLastState()));
580                 macros->Set("LASTHOSTSTATEID", GetLastState());
581                 macros->Set("LASTHOSTSTATETYPE", Service::StateTypeToString(GetLastStateType()));
582                 macros->Set("LASTHOSTSTATECHANGE", (long)hc->GetLastStateChange());
583
584                 cr = hc->GetLastCheckResult();
585         }
586
587         if (cr) {
588                 macros->Set("HOSTLATENCY", Service::CalculateLatency(cr));
589                 macros->Set("HOSTEXECUTIONTIME", Service::CalculateExecutionTime(cr));
590
591                 macros->Set("HOSTOUTPUT", cr->Get("output"));
592                 macros->Set("HOSTPERFDATA", cr->Get("performance_data_raw"));
593
594                 macros->Set("LASTHOSTCHECK", (long)cr->Get("schedule_start"));
595         }
596
597         macros->Seal();
598
599         return macros;
600 }