1 /******************************************************************************
3 * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) *
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. *
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. *
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 ******************************************************************************/
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>
33 using namespace icinga;
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;
40 REGISTER_SCRIPTFUNCTION(ValidateServiceDictionary, &Host::ValidateServiceDictionary);
44 Host::Host(const Dictionary::Ptr& serializedUpdate)
45 : DynamicObject(serializedUpdate)
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);
58 HostGroup::InvalidateMembersCache();
60 if (m_SlaveServices) {
61 ConfigItem::Ptr service;
62 BOOST_FOREACH(boost::tie(boost::tuples::ignore, service), m_SlaveServices) {
63 service->Unregister();
68 void Host::OnRegistrationCompleted(void)
72 DynamicObject::OnRegistrationCompleted();
74 Host::InvalidateServicesCache();
75 UpdateSlaveServices();
78 String Host::GetDisplayName(void) const
80 if (!m_DisplayName.IsEmpty())
86 Host::Ptr Host::GetByName(const String& name)
88 DynamicObject::Ptr configObject = DynamicObject::GetObject("Host", name);
90 return dynamic_pointer_cast<Host>(configObject);
93 Array::Ptr Host::GetGroups(void) const
98 Dictionary::Ptr Host::GetMacros(void) const
103 Array::Ptr Host::GetHostDependencies(void) const
105 return m_HostDependencies;;
108 Array::Ptr Host::GetServiceDependencies(void) const
110 return m_ServiceDependencies;
113 String Host::GetHostCheck(void) const
118 bool Host::IsReachable(void) const
122 std::set<Service::Ptr> parentServices = GetParentServices();
124 BOOST_FOREACH(const Service::Ptr& service, parentServices) {
125 ObjectLock olock(service);
127 /* ignore pending services */
128 if (!service->GetLastCheckResult())
131 /* ignore soft states */
132 if (service->GetStateType() == StateTypeSoft)
135 /* ignore services states OK and Warning */
136 if (service->GetState() == StateOK ||
137 service->GetState() == StateWarning)
143 std::set<Host::Ptr> parentHosts = GetParentHosts();
145 BOOST_FOREACH(const Host::Ptr& host, parentHosts) {
146 Service::Ptr hc = host->GetHostCheckService();
148 /* ignore hosts that don't have a hostcheck */
152 ObjectLock olock(hc);
154 /* ignore soft states */
155 if (hc->GetStateType() == StateTypeSoft)
158 /* ignore hosts that are up */
159 if (hc->GetState() == StateOK)
168 void Host::UpdateSlaveServices(void)
172 ConfigItem::Ptr item = ConfigItem::GetObject("Host", GetName());
174 /* Don't create slave services unless we own this object */
178 Dictionary::Ptr oldServices = m_SlaveServices;
179 Dictionary::Ptr serviceDescs = Get("services");
181 Dictionary::Ptr newServices = boost::make_shared<Dictionary>();
184 ObjectLock olock(serviceDescs);
187 BOOST_FOREACH(boost::tie(svcname, svcdesc), serviceDescs) {
188 if (svcdesc.IsScalar())
191 std::ostringstream namebuf;
192 namebuf << GetName() << "-" << svcname;
193 String name = namebuf.str();
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);
202 if (!svcdesc.IsObjectType<Dictionary>())
203 BOOST_THROW_EXCEPTION(std::invalid_argument("Service description must be either a string or a dictionary."));
205 Dictionary::Ptr service = svcdesc;
207 Array::Ptr templates = service->Get("templates");
210 ObjectLock olock(templates);
212 BOOST_FOREACH(const Value& tmpl, templates) {
213 builder->AddParent(tmpl);
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");
228 ExpressionList::Ptr host_exprl = boost::make_shared<ExpressionList>();
229 item->GetLinkedExpressionList()->ExtractFiltered(keys, host_exprl);
230 builder->AddExpressionList(host_exprl);
232 /* Clone attributes from the service expression list. */
233 std::vector<String> path;
234 path.push_back("services");
235 path.push_back(svcname);
237 ExpressionList::Ptr svc_exprl = boost::make_shared<ExpressionList>();
238 item->GetLinkedExpressionList()->ExtractPath(path, svc_exprl);
239 builder->AddExpressionList(svc_exprl);
241 ConfigItem::Ptr serviceItem = builder->Compile();
242 DynamicObject::Ptr dobj = serviceItem->Commit();
244 newServices->Set(name, serviceItem);
249 ObjectLock olock(oldServices);
251 ConfigItem::Ptr service;
252 BOOST_FOREACH(boost::tie(boost::tuples::ignore, service), oldServices) {
256 if (!newServices->Contains(service->GetName()))
257 service->Unregister();
263 Set("slave_services", newServices);
266 void Host::OnAttributeChanged(const String& name)
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();
281 std::set<Service::Ptr> Host::GetServices(void) const
283 std::set<Service::Ptr> services;
285 boost::mutex::scoped_lock lock(l_ServiceMutex);
287 Service::WeakPtr wservice;
288 BOOST_FOREACH(boost::tie(boost::tuples::ignore, wservice), l_ServicesCache[GetName()]) {
289 Service::Ptr service = wservice.lock();
294 services.insert(service);
300 void Host::InvalidateServicesCache(void)
303 boost::mutex::scoped_lock lock(l_ServiceMutex);
305 if (l_ServicesCacheNeedsUpdate)
306 return; /* Someone else has already requested a refresh. */
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();
315 l_ServicesCacheNeedsUpdate = true;
319 void Host::RefreshServicesCache(void)
322 boost::mutex::scoped_lock lock(l_ServiceMutex);
324 if (!l_ServicesCacheNeedsUpdate)
327 l_ServicesCacheNeedsUpdate = false;
330 Log(LogDebug, "icinga", "Updating Host services cache.");
332 std::map<String, std::map<String, Service::WeakPtr> > newServicesCache;
334 BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Service")) {
335 const Service::Ptr& service = static_pointer_cast<Service>(object);
337 Host::Ptr host = service->GetHost();
342 // TODO: assert for duplicate short_names
344 newServicesCache[host->GetName()][service->GetShortName()] = service;
347 boost::mutex::scoped_lock lock(l_ServiceMutex);
348 l_ServicesCache.swap(newServicesCache);
351 void Host::ValidateServiceDictionary(const ScriptTask::Ptr& task, const std::vector<Value>& arguments)
353 if (arguments.size() < 1)
354 BOOST_THROW_EXCEPTION(std::invalid_argument("Missing argument: Location must be specified."));
356 if (arguments.size() < 2)
357 BOOST_THROW_EXCEPTION(std::invalid_argument("Missing argument: Attribute dictionary must be specified."));
359 String location = arguments[0];
360 Dictionary::Ptr attrs = arguments[1];
361 ObjectLock olock(attrs);
365 BOOST_FOREACH(boost::tie(key, value), attrs) {
366 std::vector<String> templates;
368 if (!value.IsObjectType<Dictionary>())
369 BOOST_THROW_EXCEPTION(std::invalid_argument("Service description must be a dictionary."));
371 Dictionary::Ptr serviceDesc = value;
373 Array::Ptr templatesArray = serviceDesc->Get("templates");
375 if (templatesArray) {
376 ObjectLock tlock(templatesArray);
378 BOOST_FOREACH(const Value& tmpl, templatesArray) {
379 templates.push_back(tmpl);
383 BOOST_FOREACH(const String& name, templates) {
384 ConfigItem::Ptr item;
386 ConfigCompilerContext *context = ConfigCompilerContext::GetContext();
389 item = context->GetItem("Service", name);
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);
397 ConfigCompilerContext::GetContext()->AddError(false, "Validation failed for " +
398 location + ": Template '" + name + "' not found.");
403 task->FinishResult(Empty);
406 Service::Ptr Host::GetServiceByShortName(const Value& name) const
408 if (name.IsScalar()) {
410 boost::mutex::scoped_lock lock(l_ServiceMutex);
412 std::map<String, Service::WeakPtr>& services = l_ServicesCache[GetName()];
413 std::map<String, Service::WeakPtr>::iterator it = services.find(name);
415 if (it != services.end()) {
416 Service::Ptr service = it->second.lock();
422 return Service::Ptr();
423 } else if (name.IsObjectType<Dictionary>()) {
424 Dictionary::Ptr dict = name;
427 ASSERT(dict->IsSealed());
429 return Service::GetByNamePair(dict->Get("host"), dict->Get("service"));
431 BOOST_THROW_EXCEPTION(std::invalid_argument("Host/Service name pair is invalid."));
435 std::set<Host::Ptr> Host::GetParentHosts(void) const
437 std::set<Host::Ptr> parents;
439 Array::Ptr dependencies = GetHostDependencies();
442 ObjectLock olock(dependencies);
444 BOOST_FOREACH(const Value& value, dependencies) {
445 if (value == GetName())
448 Host::Ptr host = GetByName(value);
453 parents.insert(host);
460 Service::Ptr Host::GetHostCheckService(void) const
462 String host_check = GetHostCheck();
464 if (host_check.IsEmpty())
465 return Service::Ptr();
467 return GetServiceByShortName(host_check);
470 std::set<Service::Ptr> Host::GetParentServices(void) const
472 std::set<Service::Ptr> parents;
474 Array::Ptr dependencies = GetServiceDependencies();
477 ObjectLock olock(dependencies);
479 BOOST_FOREACH(const Value& value, dependencies) {
480 parents.insert(GetServiceByShortName(value));
487 HostState Host::CalculateState(ServiceState state, bool reachable)
490 return HostUnreachable;
501 HostState Host::GetLastState(void) const
506 return HostUnreachable;
508 Service::Ptr hc = GetHostCheckService();
513 switch (hc->GetLastState()) {
522 StateType Host::GetLastStateType(void) const
524 Service::Ptr hc = GetHostCheckService();
527 return StateTypeHard;
529 return hc->GetLastStateType();
532 String Host::StateToString(HostState state)
539 case HostUnreachable:
540 return "UNREACHABLE";
546 Dictionary::Ptr Host::CalculateDynamicMacros(void) const
550 Dictionary::Ptr macros = boost::make_shared<Dictionary>();
553 ObjectLock olock(this);
555 macros->Set("HOSTNAME", GetName());
556 macros->Set("HOSTDISPLAYNAME", GetDisplayName());
557 macros->Set("HOSTALIAS", GetName());
562 Service::Ptr hc = GetHostCheckService();
565 ObjectLock olock(hc);
567 ServiceState state = hc->GetState();
568 bool reachable = IsReachable();
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());
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());
581 cr = hc->GetLastCheckResult();
585 macros->Set("HOSTLATENCY", Service::CalculateLatency(cr));
586 macros->Set("HOSTEXECUTIONTIME", Service::CalculateExecutionTime(cr));
588 macros->Set("HOSTOUTPUT", cr->Get("output"));
589 macros->Set("HOSTPERFDATA", cr->Get("performance_data_raw"));
591 macros->Set("LASTHOSTCHECK", (long)cr->Get("schedule_start"));