diff --git a/components/checker/checkercomponent.cpp b/components/checker/checkercomponent.cpp index 69dcfd7f6..41f20d7da 100644 --- a/components/checker/checkercomponent.cpp +++ b/components/checker/checkercomponent.cpp @@ -129,7 +129,7 @@ void CheckerComponent::CheckThreadProc(void) try { olock.Unlock(); - Service::BeginExecuteCheck(service, boost::bind(&CheckerComponent::CheckCompletedHandler, this, service)); + Service::BeginExecuteCheck(service, boost::bind(&CheckerComponent::CheckCompletedHandler, static_cast(GetSelf()), service)); } catch (const exception& ex) { olock.Lock(); Logger::Write(LogCritical, "checker", "Exception occured while checking service '" + service->GetName() + "': " + diagnostic_information(ex)); diff --git a/components/compat/compatcomponent.cpp b/components/compat/compatcomponent.cpp index 93a0794ec..2135ce856 100644 --- a/components/compat/compatcomponent.cpp +++ b/components/compat/compatcomponent.cpp @@ -257,11 +257,8 @@ void CompatComponent::DumpDowntimes(ostream& fp, const Service::Ptr& owner, Comp void CompatComponent::DumpHostStatus(ostream& fp, const Host::Ptr& host) { - Service::Ptr hc; - { ObjectLock olock(host); - hc = host->GetHostCheckService(); fp << "hoststatus {" << "\n" << "\t" << "host_name=" << host->GetName() << "\n"; @@ -269,6 +266,7 @@ void CompatComponent::DumpHostStatus(ostream& fp, const Host::Ptr& host) ServiceState hcState = StateOK; + Service::Ptr hc = Host::GetHostCheckService(host); if (hc) { ObjectLock olock(hc); hcState = hc->GetState(); @@ -296,26 +294,23 @@ void CompatComponent::DumpHostStatus(ostream& fp, const Host::Ptr& host) void CompatComponent::DumpHostObject(ostream& fp, const Host::Ptr& host) { - Service::Ptr hc; - { ObjectLock olock(host); fp << "define host {" << "\n" << "\t" << "host_name" << "\t" << host->GetName() << "\n" << "\t" << "display_name" << "\t" << host->GetDisplayName() << "\n"; - - set parents = host->GetParentHosts(); - - if (!parents.empty()) { - fp << "\t" << "parents" << "\t"; - DumpNameList(fp, parents); - fp << "\n"; - } - - hc = host->GetHostCheckService(); } + set parents = Host::GetParentHosts(host); + + if (!parents.empty()) { + fp << "\t" << "parents" << "\t"; + DumpNameList(fp, parents); + fp << "\n"; + } + + Service::Ptr hc = Host::GetHostCheckService(host); if (hc) { ObjectLock olock(hc); @@ -326,7 +321,7 @@ void CompatComponent::DumpHostObject(ostream& fp, const Host::Ptr& host) << "\t" << "passive_checks_enabled" << "\t" << (hc->GetEnablePassiveChecks() ? 1 : 0) << "\n" << "\t" << "notifications_enabled" << "\t" << (hc->GetEnableNotifications() ? 1 : 0) << "\n" << "\t" << "notification_options" << "\t" << "d,u,r" << "\n" - << "\t" << "notification_interval" << "\t" << "60" << "\n" + << "\t" << "notification_interval" << "\t" << hc->GetNotificationInterval() << "\n" << "\t" << "notification_period" << "\t" << "24x7" << "\n"; } @@ -434,13 +429,11 @@ void CompatComponent::DumpServiceStatus(ostream& fp, const Service::Ptr& service void CompatComponent::DumpServiceObject(ostream& fp, const Service::Ptr& service) { - set parentServices; Host::Ptr host; String host_name, short_name; { ObjectLock olock(service); - parentServices = service->GetParentServices(); host = service->GetHost(); short_name = service->GetShortName(); } @@ -465,13 +458,13 @@ void CompatComponent::DumpServiceObject(ostream& fp, const Service::Ptr& service << "\t" << "passive_checks_enabled" << "\t" << (service->GetEnablePassiveChecks() ? 1 : 0) << "\n" << "\t" << "notifications_enabled" << "\t" << (service->GetEnableNotifications() ? 1 : 0) << "\n" << "\t" << "notification_options" << "\t" << "u,w,c,r" << "\n" - << "\t" << "notification_interval" << "\t" << "60" << "\n" + << "\t" << "notification_interval" << "\t" << service->GetNotificationInterval() << "\n" << "\t" << "notification_period" << "\t" << "24x7" << "\n" << "\t" << "}" << "\n" << "\n"; } - BOOST_FOREACH(const Service::Ptr& parent, parentServices) { + BOOST_FOREACH(const Service::Ptr& parent, Service::GetParentServices(service)) { ObjectLock plock(parent); fp << "define servicedependency {" << "\n" diff --git a/components/compatido/compatidocomponent.cpp b/components/compatido/compatidocomponent.cpp index 69bf055ad..4c5e23e08 100644 --- a/components/compatido/compatidocomponent.cpp +++ b/components/compatido/compatidocomponent.cpp @@ -440,7 +440,7 @@ void CompatIdoComponent::DumpHostStatus(const Host::Ptr& host) int state; if (!Host::IsReachable(host)) state = 2; /* unreachable */ - else if (host->GetHostCheckService()->GetState() != StateOK) + else if (Host::GetHostCheckService(host)->GetState() != StateOK) state = 1; /* down */ else state = 0; /* up */ diff --git a/components/notification/notificationcomponent.cpp b/components/notification/notificationcomponent.cpp index ae5178b52..1384b44d1 100644 --- a/components/notification/notificationcomponent.cpp +++ b/components/notification/notificationcomponent.cpp @@ -55,9 +55,24 @@ void NotificationComponent::Stop(void) */ void NotificationComponent::NotificationTimerHandler(void) { - // TODO: implement -} + double now = Utility::GetTime(); + BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Service")) { + Service::Ptr service = dynamic_pointer_cast(object); + ObjectLock olock(service); + + if (service->GetStateType() == StateTypeSoft) + continue; + + if (service->GetState() == StateOK) + continue; + + if (service->GetLastNotification() > now - service->GetNotificationInterval()) + continue; + + service->RequestNotifications(NotificationProblem); + } +} /** * Processes icinga::SendNotifications messages. @@ -79,6 +94,5 @@ void NotificationComponent::SendNotificationsRequestHandler(const Endpoint::Ptr& Service::Ptr service = Service::GetByName(svc); - ObjectLock olock(service); - service->SendNotifications(static_cast(type)); + Service::SendNotifications(service, static_cast(type)); } diff --git a/components/replication/replicationcomponent.cpp b/components/replication/replicationcomponent.cpp index 414827b73..70dd403e4 100644 --- a/components/replication/replicationcomponent.cpp +++ b/components/replication/replicationcomponent.cpp @@ -32,8 +32,8 @@ void ReplicationComponent::Start(void) DynamicObject::OnRegistered.connect(boost::bind(&ReplicationComponent::LocalObjectRegisteredHandler, this, _1)); DynamicObject::OnUnregistered.connect(boost::bind(&ReplicationComponent::LocalObjectUnregisteredHandler, this, _1)); - DynamicObject::OnTransactionClosing.connect(boost::bind(&ReplicationComponent::TransactionClosingHandler, this, _2)); - DynamicObject::OnFlushObject.connect(boost::bind(&ReplicationComponent::FlushObjectHandler, this, _1)); + DynamicObject::OnTransactionClosing.connect(boost::bind(&ReplicationComponent::TransactionClosingHandler, this, _1, _2)); + DynamicObject::OnFlushObject.connect(boost::bind(&ReplicationComponent::FlushObjectHandler, this, _1, _2)); Endpoint::OnConnected.connect(boost::bind(&ReplicationComponent::EndpointConnectedHandler, this, _1)); @@ -161,7 +161,7 @@ void ReplicationComponent::LocalObjectUnregisteredHandler(const DynamicObject::P MakeObjectMessage(object, "config::ObjectRemoved", 0, false)); } -void ReplicationComponent::TransactionClosingHandler(const set& modifiedObjects) +void ReplicationComponent::TransactionClosingHandler(double tx, const set& modifiedObjects) { if (modifiedObjects.empty()) return; @@ -176,16 +176,16 @@ void ReplicationComponent::TransactionClosingHandler(const set& modifiedObjects); - void FlushObjectHandler(const DynamicObject::Ptr& object); + void TransactionClosingHandler(double tx, const set& modifiedObjects); + void FlushObjectHandler(double tx, const DynamicObject::Ptr& object); void RemoteObjectUpdateHandler(const RequestMessage& request); void RemoteObjectRemovedHandler(const RequestMessage& request); diff --git a/itl/types.conf b/itl/types.conf index 0344b3ecd..d7e763991 100644 --- a/itl/types.conf +++ b/itl/types.conf @@ -210,7 +210,9 @@ type Service { %attribute string "*" } } - } + }, + + %attribute number "notification_interval" } type ServiceGroup { diff --git a/lib/base/dynamicobject.cpp b/lib/base/dynamicobject.cpp index d0f363885..e7ae7bd21 100644 --- a/lib/base/dynamicobject.cpp +++ b/lib/base/dynamicobject.cpp @@ -30,7 +30,7 @@ Timer::Ptr DynamicObject::m_TransactionTimer; signals2::signal DynamicObject::OnRegistered; signals2::signal DynamicObject::OnUnregistered; signals2::signal&)> DynamicObject::OnTransactionClosing; -signals2::signal DynamicObject::OnFlushObject; +signals2::signal DynamicObject::OnFlushObject; DynamicObject::DynamicObject(const Dictionary::Ptr& serializedObject) : m_EventSafe(false), m_ConfigTx(0), m_Registered(false) @@ -75,26 +75,6 @@ void DynamicObject::Initialize(void) m_TransactionTimer->Start(); } -/** - * @threadsafety Always. - */ -void DynamicObject::SendLocalUpdateEvents(void) -{ - map attrs; - - { - ObjectLock olock(this); - attrs.swap(m_ModifiedAttributes); - } - - /* Check if it's safe to send events. */ - if (GetEventSafe()) { - map::iterator it; - for (it = attrs.begin(); it != attrs.end(); it++) - OnAttributeChanged(it->first, it->second); - } -} - Dictionary::Ptr DynamicObject::BuildUpdate(double sinceTx, int attributeTypes) const { DynamicObject::AttributeConstIterator it; @@ -244,7 +224,7 @@ void DynamicObject::InternalSetAttribute(const String& name, const Value& data, if (GetEventSafe()) { /* We can't call GetSelf() in the constructor or destructor. - * The OnConstructionCompleted() function will take care of adding this + * The Register() function will take care of adding this * object to the list of modified objects later on if we can't * do it here. */ @@ -327,6 +307,17 @@ String DynamicObject::GetSource(void) const void DynamicObject::Register(void) { + /* It's now safe to send us attribute events. */ + SetEventSafe(true); + + /* Add this new object to the list of modified objects. + * We're doing this here because we can't construct + * a while WeakPtr from within the object's constructor. */ + { + boost::mutex::scoped_lock lock(m_TransactionMutex); + m_ModifiedObjects.insert(GetSelf()); + } + { DynamicType::Ptr dtype = GetType(); ObjectLock olock(dtype); @@ -339,10 +330,24 @@ void DynamicObject::Register(void) if (!dobj) dtype->RegisterObject(self); } +} - OnRegistered(GetSelf()); +void DynamicObject::OnRegistrationCompleted(void) +{ + DynamicObject::Ptr object; - Start(); + { + ObjectLock olock(this); + m_Registered = true; + + Start(); + + Flush(); + + object = GetSelf(); + } + + OnRegistered(object); } void DynamicObject::Start(void) @@ -532,8 +537,7 @@ double DynamicObject::GetCurrentTx(void) void DynamicObject::Flush(void) { - SendLocalUpdateEvents(); - OnFlushObject(GetSelf()); + OnFlushObject(GetCurrentTx(), GetSelf()); } /* @@ -558,30 +562,24 @@ void DynamicObject::NewTx(void) if (!object) continue; - object->SendLocalUpdateEvents(); + map attrs; + + { + ObjectLock olock(object); + attrs.swap(object->m_ModifiedAttributes); + } + + /* Check if it's safe to send events. */ + if (object->GetEventSafe()) { + map::iterator it; + for (it = attrs.begin(); it != attrs.end(); it++) + object->OnAttributeChanged(it->first, it->second); + } } OnTransactionClosing(tx, objects); } -void DynamicObject::OnConstructionCompleted(void) -{ - /* It's now safe to send us attribute events. */ - SetEventSafe(true); - - /* Add this new object to the list of modified objects. - * We're doing this here because we can't construct - * a while WeakPtr from within the object's constructor. */ - boost::mutex::scoped_lock lock(m_TransactionMutex); - m_ModifiedObjects.insert(GetSelf()); -} - -void DynamicObject::OnRegistrationCompleted(void) -{ - ObjectLock olock(this); - m_Registered = true; -} - void DynamicObject::OnAttributeChanged(const String&, const Value&) { } diff --git a/lib/base/dynamicobject.h b/lib/base/dynamicobject.h index 1c638981c..ddfce211a 100644 --- a/lib/base/dynamicobject.h +++ b/lib/base/dynamicobject.h @@ -228,7 +228,7 @@ public: static signals2::signal OnRegistered; static signals2::signal OnUnregistered; static signals2::signal&)> OnTransactionClosing; - static signals2::signal OnFlushObject; + static signals2::signal OnFlushObject; ScriptTask::Ptr MakeMethodTask(const String& method, const vector& arguments); @@ -243,9 +243,6 @@ public: void SetSource(const String& value); String GetSource(void) const; - void SetTx(double tx); - double GetTx(void) const; - void Flush(void); void Register(void); @@ -267,7 +264,6 @@ public: static double GetCurrentTx(void); protected: - virtual void OnConstructionCompleted(void); virtual void OnRegistrationCompleted(void); virtual void OnAttributeChanged(const String& name, const Value& oldValue); @@ -301,7 +297,7 @@ private: static boost::once_flag m_TransactionOnce; static Timer::Ptr m_TransactionTimer; - friend class DynamicType; /* for OnInitCompleted. */ + friend class DynamicType; /* for OnRegistrationCompleted. */ }; } diff --git a/lib/base/dynamictype.cpp b/lib/base/dynamictype.cpp index 4e753b0b5..8d3a23158 100644 --- a/lib/base/dynamictype.cpp +++ b/lib/base/dynamictype.cpp @@ -99,14 +99,7 @@ void DynamicType::RegisterObject(const DynamicObject::Ptr& object) m_ObjectMap[name] = object; m_ObjectSet.insert(object); - /* notify the object that it's been registered */ object->OnRegistrationCompleted(); - - { - ObjectLock olock(object); - object->Flush(); - } - } void DynamicType::UnregisterObject(const DynamicObject::Ptr& object) @@ -162,8 +155,6 @@ DynamicObject::Ptr DynamicType::CreateObject(const Dictionary::Ptr& serializedUp object->ApplyUpdate(serializedUpdate, Attribute_All & ~Attribute_Config); } - object->OnConstructionCompleted(); - return object; } diff --git a/lib/base/script.cpp b/lib/base/script.cpp index 12050aea5..740028dac 100644 --- a/lib/base/script.cpp +++ b/lib/base/script.cpp @@ -35,10 +35,8 @@ Script::Script(const Dictionary::Ptr& properties) RegisterAttribute("code", Attribute_Config, &m_Code); } -void Script::OnRegistrationCompleted(void) +void Script::Start(void) { - DynamicObject::OnRegistrationCompleted(); - SpawnInterpreter(); } diff --git a/lib/base/script.h b/lib/base/script.h index 34e748eab..a599239f3 100644 --- a/lib/base/script.h +++ b/lib/base/script.h @@ -38,11 +38,12 @@ public: Script(const Dictionary::Ptr& properties); + virtual void Start(void); + String GetLanguage(void) const; String GetCode(void) const; protected: - virtual void OnRegistrationCompleted(void); virtual void OnAttributeUpdate(const String& name, const Value& oldValue); private: diff --git a/lib/icinga/externalcommandprocessor.cpp b/lib/icinga/externalcommandprocessor.cpp index dcbf24606..d303a7a15 100644 --- a/lib/icinga/externalcommandprocessor.cpp +++ b/lib/icinga/externalcommandprocessor.cpp @@ -162,7 +162,7 @@ void ExternalCommandProcessor::ProcessHostCheckResult(double time, const vector< Host::Ptr host = Host::GetByName(arguments[0]); - Service::Ptr hc = host->GetHostCheckService(); + Service::Ptr hc = Host::GetHostCheckService(host); if (!hc->GetEnablePassiveChecks()) BOOST_THROW_EXCEPTION(invalid_argument("Got passive check result for host '" + arguments[0] + "' which has passive checks disabled.")); @@ -222,7 +222,7 @@ void ExternalCommandProcessor::ScheduleHostCheck(double, const vector& a Host::Ptr host = Host::GetByName(arguments[0]); - Service::Ptr hc = host->GetHostCheckService(); + Service::Ptr hc = Host::GetHostCheckService(host); double planned_check = Convert::ToDouble(arguments[1]); @@ -243,7 +243,7 @@ void ExternalCommandProcessor::ScheduleForcedHostCheck(double, const vectorGetHostCheckService(); + Service::Ptr hc = Host::GetHostCheckService(host); Logger::Write(LogInformation, "icinga", "Rescheduling next check for host '" + arguments[0] + "'"); hc->SetForceNextCheck(true); @@ -289,7 +289,7 @@ void ExternalCommandProcessor::EnableHostCheck(double, const vector& arg Host::Ptr host = Host::GetByName(arguments[0]); Logger::Write(LogInformation, "icinga", "Enabling active checks for host '" + arguments[0] + "'"); - Service::Ptr hc = host->GetHostCheckService(); + Service::Ptr hc = Host::GetHostCheckService(host); if (hc) hc->SetEnableActiveChecks(true); @@ -303,7 +303,7 @@ void ExternalCommandProcessor::DisableHostCheck(double, const vector& ar Host::Ptr host = Host::GetByName(arguments[0]); Logger::Write(LogInformation, "icinga", "Disabling active checks for host '" + arguments[0] + "'"); - Service::Ptr hc = host->GetHostCheckService(); + Service::Ptr hc = Host::GetHostCheckService(host); if (hc) hc->SetEnableActiveChecks(false); @@ -454,7 +454,7 @@ void ExternalCommandProcessor::AcknowledgeHostProblem(double, const vectorGetName() + "'"); - Service::Ptr service = host->GetHostCheckService(); + Service::Ptr service = Host::GetHostCheckService(host); if (service) { if (service->GetState() == StateOK) BOOST_THROW_EXCEPTION(invalid_argument("The host '" + arguments[0] + "' is OK.")); @@ -474,7 +474,7 @@ void ExternalCommandProcessor::AcknowledgeHostProblemExpire(double, const vector Host::Ptr host = Host::GetByName(arguments[0]); Logger::Write(LogInformation, "icinga", "Setting timed acknowledgement for host '" + host->GetName() + "'"); - Service::Ptr service = host->GetHostCheckService(); + Service::Ptr service = Host::GetHostCheckService(host); if (service) { if (service->GetState() == StateOK) BOOST_THROW_EXCEPTION(invalid_argument("The host '" + arguments[0] + "' is OK.")); @@ -491,7 +491,7 @@ void ExternalCommandProcessor::RemoveHostAcknowledgement(double, const vectorGetName() + "'"); - Service::Ptr service = host->GetHostCheckService(); + Service::Ptr service = Host::GetHostCheckService(host); if (service) { service->ClearAcknowledgement(); } @@ -561,7 +561,7 @@ void ExternalCommandProcessor::EnablePassiveHostChecks(double, const vectorGetHostCheckService(); + Service::Ptr hc = Host::GetHostCheckService(host); if (hc) hc->SetEnablePassiveChecks(true); @@ -575,7 +575,7 @@ void ExternalCommandProcessor::DisablePassiveHostChecks(double, const vectorGetHostCheckService(); + Service::Ptr hc = Host::GetHostCheckService(host); if (hc) hc->SetEnablePassiveChecks(false); @@ -735,7 +735,7 @@ void ExternalCommandProcessor::ScheduleHostDowntime(double, const vector triggeredBy = Service::GetDowntimeIDFromLegacyID(triggeredByLegacy); Logger::Write(LogInformation, "icinga", "Creating downtime for host " + host->GetName()); - Service::Ptr service = host->GetHostCheckService(); + Service::Ptr service = Host::GetHostCheckService(host); if (service) { (void) service->AddDowntime(arguments[6], arguments[7], Convert::ToDouble(arguments[1]), Convert::ToDouble(arguments[2]), @@ -788,7 +788,7 @@ void ExternalCommandProcessor::ScheduleHostgroupHostDowntime(double, const vecto BOOST_FOREACH(const Host::Ptr& host, HostGroup::GetMembers(hg)) { Logger::Write(LogInformation, "icinga", "Creating downtime for host " + host->GetName()); - Service::Ptr service = host->GetHostCheckService(); + Service::Ptr service = Host::GetHostCheckService(host); if (service) { (void) service->AddDowntime(arguments[6], arguments[7], Convert::ToDouble(arguments[1]), Convert::ToDouble(arguments[2]), @@ -849,7 +849,7 @@ void ExternalCommandProcessor::ScheduleServicegroupHostDowntime(double, const ve BOOST_FOREACH(const Service::Ptr& service, ServiceGroup::GetMembers(sg)) { Host::Ptr host = service->GetHost(); - Service::Ptr hcService = host->GetHostCheckService(); + Service::Ptr hcService = Host::GetHostCheckService(host); if (hcService) services.insert(hcService); } @@ -890,7 +890,7 @@ void ExternalCommandProcessor::AddHostComment(double, const vector& argu Host::Ptr host = Host::GetByName(arguments[0]); Logger::Write(LogInformation, "icinga", "Creating comment for host " + host->GetName()); - Service::Ptr service = host->GetHostCheckService(); + Service::Ptr service = Host::GetHostCheckService(host); if (service) (void) service->AddComment(CommentUser, arguments[2], arguments[3], 0); } @@ -937,7 +937,7 @@ void ExternalCommandProcessor::DelAllHostComments(double, const vector& Host::Ptr host = Host::GetByName(arguments[0]); Logger::Write(LogInformation, "icinga", "Removing all comments for host " + host->GetName()); - Service::Ptr service = host->GetHostCheckService(); + Service::Ptr service = Host::GetHostCheckService(host); if (service) service->RemoveAllComments(); } @@ -961,7 +961,7 @@ void ExternalCommandProcessor::SendCustomHostNotification(double time, const vec Host::Ptr host = Host::GetByName(arguments[0]); Logger::Write(LogInformation, "icinga", "Sending custom notification for host " + host->GetName()); - Service::Ptr service = host->GetHostCheckService(); + Service::Ptr service = Host::GetHostCheckService(host); if (service) { service->RequestNotifications(NotificationCustom); } @@ -986,7 +986,7 @@ void ExternalCommandProcessor::DelayHostNotification(double time, const vectorGetName()); - Service::Ptr service = host->GetHostCheckService(); + Service::Ptr service = Host::GetHostCheckService(host); if (service) { service->SetLastNotification(Convert::ToDouble(arguments[1])); } @@ -1011,7 +1011,7 @@ void ExternalCommandProcessor::EnableHostNotifications(double, const vectorGetHostCheckService(); + Service::Ptr hc = Host::GetHostCheckService(host); if (hc) hc->SetEnableNotifications(true); @@ -1025,7 +1025,7 @@ void ExternalCommandProcessor::DisableHostNotifications(double, const vectorGetHostCheckService(); + Service::Ptr hc = Host::GetHostCheckService(host); if (hc) hc->SetEnableNotifications(false); diff --git a/lib/icinga/host.cpp b/lib/icinga/host.cpp index 56f8a6022..4008ae0ab 100644 --- a/lib/icinga/host.cpp +++ b/lib/icinga/host.cpp @@ -21,9 +21,8 @@ using namespace icinga; -boost::mutex Host::m_Mutex; +boost::mutex Host::m_ServiceMutex; map > > Host::m_ServicesCache; -bool Host::m_ServicesCacheValid = true; REGISTER_SCRIPTFUNCTION("ValidateServiceDictionary", &Host::ValidateServiceDictionary); @@ -41,17 +40,9 @@ Host::Host(const Dictionary::Ptr& properties) } -void Host::OnRegistrationCompleted(void) -{ - DynamicObject::OnRegistrationCompleted(); - - HostGroup::InvalidateMembersCache(); - Host::UpdateSlaveServices(GetSelf()); -} - Host::~Host(void) { - HostGroup::InvalidateMembersCache(); + HostGroup::RefreshMembersCache(); if (m_SlaveServices) { ConfigItem::Ptr service; @@ -61,6 +52,14 @@ Host::~Host(void) } } +void Host::OnRegistrationCompleted(void) +{ + DynamicObject::OnRegistrationCompleted(); + + HostGroup::RefreshMembersCache(); + Host::UpdateSlaveServices(GetSelf()); +} + String Host::GetDisplayName(void) const { if (!m_DisplayName.IsEmpty()) @@ -117,12 +116,7 @@ String Host::GetHostCheck(void) const bool Host::IsReachable(const Host::Ptr& self) { - set parentServices; - - { - ObjectLock olock(self); - parentServices = self->GetParentServices(); - } + set parentServices = Host::GetParentServices(self); BOOST_FOREACH(const Service::Ptr& service, parentServices) { ObjectLock olock(service); @@ -143,21 +137,10 @@ bool Host::IsReachable(const Host::Ptr& self) return false; } - set parentHosts; - - { - ObjectLock olock(self); - parentHosts = self->GetParentHosts(); - } + set parentHosts = Host::GetParentHosts(self); BOOST_FOREACH(const Host::Ptr& host, parentHosts) { - Service::Ptr hc; - - { - ObjectLock olock(host); - hc = host->GetHostCheckService(); - } - + Service::Ptr hc = Host::GetHostCheckService(host); ObjectLock olock(hc); /* ignore hosts that are up */ @@ -309,7 +292,7 @@ void Host::UpdateSlaveServices(const Host::Ptr& self) void Host::OnAttributeChanged(const String& name, const Value&) { if (name == "hostgroups") - HostGroup::InvalidateMembersCache(); + HostGroup::RefreshMembersCache(); else if (name == "services") { UpdateSlaveServices(GetSelf()); } else if (name == "notifications") { @@ -323,9 +306,6 @@ void Host::OnAttributeChanged(const String& name, const Value&) BOOST_FOREACH(const Service::Ptr& service, services) { Service::UpdateSlaveNotifications(service); } - } else if (name == "hostcheck") { - ObjectLock olock(this); - m_HostCheckService = GetServiceByShortName(GetHostCheck()); } } @@ -333,9 +313,7 @@ set Host::GetServices(void) const { set services; - boost::mutex::scoped_lock lock(m_Mutex); - - ValidateServicesCache(); + boost::mutex::scoped_lock lock(m_ServiceMutex); Service::WeakPtr wservice; BOOST_FOREACH(tie(tuples::ignore, wservice), m_ServicesCache[GetName()]) { @@ -350,22 +328,9 @@ set Host::GetServices(void) const return services; } -void Host::InvalidateServicesCache(void) +void Host::RefreshServicesCache(void) { - boost::mutex::scoped_lock lock(m_Mutex); - m_ServicesCacheValid = false; - m_ServicesCache.clear(); -} - -/** - * @threadsafety Caller must hold m_Mutex. - */ -void Host::ValidateServicesCache(void) -{ - if (m_ServicesCacheValid) - return; - - m_ServicesCache.clear(); + map > > newServicesCache; BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Service")) { const Service::Ptr& service = static_pointer_cast(object); @@ -388,10 +353,11 @@ void Host::ValidateServicesCache(void) // TODO: assert for duplicate short_names - m_ServicesCache[host_name][short_name] = service; + newServicesCache[host_name][short_name] = service; } - m_ServicesCacheValid = true; + boost::mutex::scoped_lock lock(m_ServiceMutex); + m_ServicesCache.swap(newServicesCache); } void Host::ValidateServiceDictionary(const ScriptTask::Ptr& task, const vector& arguments) @@ -444,16 +410,20 @@ void Host::ValidateServiceDictionary(const ScriptTask::Ptr& task, const vectorFinishResult(Empty); } -Service::Ptr Host::GetServiceByShortName(const Value& name) const +Service::Ptr Host::GetServiceByShortName(const Host::Ptr& self, const Value& name) { + String host_name; + + { + ObjectLock olock(self); + host_name = self->GetName(); + } + if (name.IsScalar()) { - { - boost::mutex::scoped_lock lock(m_Mutex); + boost::mutex::scoped_lock lock(m_ServiceMutex); - ValidateServicesCache(); - - map >& services = m_ServicesCache[GetName()]; + map >& services = m_ServicesCache[host_name]; map >::iterator it = services.find(name); if (it != services.end()) { @@ -466,22 +436,29 @@ Service::Ptr Host::GetServiceByShortName(const Value& name) const return Service::GetByName(name); } else if (name.IsObjectType()) { Dictionary::Ptr dict = name; - return GetByName(dict->Get("host"))->GetServiceByShortName(dict->Get("service")); + return GetServiceByShortName(GetByName(dict->Get("host")), dict->Get("service")); } else { BOOST_THROW_EXCEPTION(invalid_argument("Host/Service name pair is invalid.")); } } -set Host::GetParentHosts(void) const +set Host::GetParentHosts(const Host::Ptr& self) { set parents; - Dictionary::Ptr dependencies = GetHostDependencies(); + Dictionary::Ptr dependencies; + String host_name; + + { + ObjectLock olock(self); + dependencies = self->GetHostDependencies(); + host_name = self->GetName(); + } if (dependencies) { Value value; BOOST_FOREACH(tie(tuples::ignore, value), dependencies) { - if (value == GetName()) + if (value == host_name) continue; parents.insert(Host::GetByName(value)); @@ -491,21 +468,33 @@ set Host::GetParentHosts(void) const return parents; } -Service::Ptr Host::GetHostCheckService(void) const +Service::Ptr Host::GetHostCheckService(const Host::Ptr& self) { - return m_HostCheckService.lock(); + String host_check; + + { + ObjectLock olock(self); + host_check = self->GetHostCheck(); + } + + return GetServiceByShortName(self, host_check); } -set Host::GetParentServices(void) const +set Host::GetParentServices(const Host::Ptr& self) { set parents; - Dictionary::Ptr dependencies = GetServiceDependencies(); + Dictionary::Ptr dependencies; + + { + ObjectLock olock(self); + dependencies = self->GetServiceDependencies(); + } if (dependencies) { Value value; BOOST_FOREACH(tie(tuples::ignore, value), dependencies) { - parents.insert(GetServiceByShortName(value)); + parents.insert(GetServiceByShortName(self, value)); } } @@ -516,22 +505,20 @@ Dictionary::Ptr Host::CalculateDynamicMacros(const Host::Ptr& self) { Dictionary::Ptr macros = boost::make_shared(); - Service::Ptr hc; - { ObjectLock olock(self); macros->Set("HOSTNAME", self->GetName()); macros->Set("HOSTDISPLAYNAME", self->GetDisplayName()); macros->Set("HOSTALIAS", self->GetName()); - - hc = self->GetHostCheckService(); } bool reachable = Host::IsReachable(self); Dictionary::Ptr cr; + Service::Ptr hc = Host::GetHostCheckService(self); + if (hc) { ObjectLock olock(hc); diff --git a/lib/icinga/host.h b/lib/icinga/host.h index 47cf6cfd1..d0314f2b1 100644 --- a/lib/icinga/host.h +++ b/lib/icinga/host.h @@ -52,23 +52,23 @@ public: static Dictionary::Ptr CalculateDynamicMacros(const Host::Ptr& self); - shared_ptr GetHostCheckService(void) const; - set GetParentHosts(void) const; - set > GetParentServices(void) const; + static shared_ptr GetHostCheckService(const Host::Ptr& self); + static set GetParentHosts(const Host::Ptr& self); + static set > GetParentServices(const Host::Ptr& self); static bool IsReachable(const Host::Ptr& self); - shared_ptr GetServiceByShortName(const Value& name) const; + static shared_ptr GetServiceByShortName(const Host::Ptr& self, const Value& name); set > GetServices(void) const; - static void InvalidateServicesCache(void); + static void RefreshServicesCache(void); static void ValidateServiceDictionary(const ScriptTask::Ptr& task, const std::vector& arguments); protected: - void OnRegistrationCompleted(void); - void OnAttributeChanged(const String& name, const Value& oldValue); + virtual void OnRegistrationCompleted(void); + virtual void OnAttributeChanged(const String& name, const Value& oldValue); private: Attribute m_DisplayName; @@ -79,15 +79,12 @@ private: Attribute m_HostCheck; Dictionary::Ptr m_SlaveServices; - static boost::mutex m_Mutex; + static boost::mutex m_ServiceMutex; static map > > m_ServicesCache; - static bool m_ServicesCacheValid; static void UpdateSlaveServices(const Host::Ptr& self); static void ValidateServicesCache(void); - - weak_ptr m_HostCheckService; }; } diff --git a/lib/icinga/hostgroup.cpp b/lib/icinga/hostgroup.cpp index 9928a4e3f..694e1b0f3 100644 --- a/lib/icinga/hostgroup.cpp +++ b/lib/icinga/hostgroup.cpp @@ -23,7 +23,6 @@ using namespace icinga; boost::mutex HostGroup::m_Mutex; map > HostGroup::m_MembersCache; -bool HostGroup::m_MembersCacheValid = true; REGISTER_TYPE(HostGroup, NULL); @@ -35,6 +34,16 @@ HostGroup::HostGroup(const Dictionary::Ptr& properties) RegisterAttribute("action_url", Attribute_Config, &m_ActionUrl); } +HostGroup::~HostGroup(void) +{ + RefreshMembersCache(); +} + +void HostGroup::OnRegistrationCompleted(void) +{ + RefreshMembersCache(); +} + String HostGroup::GetDisplayName(void) const { if (!m_DisplayName.IsEmpty()) @@ -88,10 +97,8 @@ set HostGroup::GetMembers(const HostGroup::Ptr& self) { boost::mutex::scoped_lock lock(m_Mutex); - ValidateMembersCache(); - - BOOST_FOREACH(const Host::WeakPtr& hst, m_MembersCache[name]) { - Host::Ptr host = hst.lock(); + BOOST_FOREACH(const Host::WeakPtr& whost, m_MembersCache[name]) { + Host::Ptr host = whost.lock(); if (!host) continue; @@ -103,22 +110,9 @@ set HostGroup::GetMembers(const HostGroup::Ptr& self) return hosts; } -void HostGroup::InvalidateMembersCache(void) +void HostGroup::RefreshMembersCache(void) { - boost::mutex::scoped_lock lock(m_Mutex); - m_MembersCacheValid = false; - m_MembersCache.clear(); -} - -/** - * @threadsafety Caller must hold m_Mutex. - */ -void HostGroup::ValidateMembersCache(void) -{ - if (m_MembersCacheValid) - return; - - m_MembersCache.clear(); + map > > newMembersCache; BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Host")) { const Host::Ptr& host = static_pointer_cast(object); @@ -132,12 +126,13 @@ void HostGroup::ValidateMembersCache(void) Value hostgroup; BOOST_FOREACH(tie(tuples::ignore, hostgroup), dict) { if (!HostGroup::Exists(hostgroup)) - Logger::Write(LogWarning, "icinga", "Host group '" + static_cast(hostgroup) + "' used but not defined."); + continue; - m_MembersCache[hostgroup].push_back(host); + newMembersCache[hostgroup].push_back(host); } } } - m_MembersCacheValid = true; + boost::mutex::scoped_lock lock(m_Mutex); + m_MembersCache.swap(newMembersCache); } diff --git a/lib/icinga/hostgroup.h b/lib/icinga/hostgroup.h index 8074510a7..35d39cae9 100644 --- a/lib/icinga/hostgroup.h +++ b/lib/icinga/hostgroup.h @@ -34,7 +34,8 @@ public: typedef shared_ptr Ptr; typedef weak_ptr WeakPtr; - HostGroup(const Dictionary::Ptr& properties);; + HostGroup(const Dictionary::Ptr& properties); + ~HostGroup(void); static bool Exists(const String& name); static HostGroup::Ptr GetByName(const String& name); @@ -44,7 +45,11 @@ public: String GetActionUrl(void) const; static set GetMembers(const HostGroup::Ptr& self); - static void InvalidateMembersCache(void); + + static void RefreshMembersCache(void); + +protected: + virtual void OnRegistrationCompleted(void); private: Attribute m_DisplayName; @@ -53,9 +58,6 @@ private: static boost::mutex m_Mutex; static map > > m_MembersCache; - static bool m_MembersCacheValid; - - static void ValidateMembersCache(void); }; } diff --git a/lib/icinga/notification.cpp b/lib/icinga/notification.cpp index 6b1a1b628..e7810d971 100644 --- a/lib/icinga/notification.cpp +++ b/lib/icinga/notification.cpp @@ -32,12 +32,12 @@ Notification::Notification(const Dictionary::Ptr& properties) RegisterAttribute("host_name", Attribute_Config, &m_HostName); RegisterAttribute("service", Attribute_Config, &m_Service); - Service::InvalidateNotificationsCache(); + Service::RefreshNotificationsCache(); } Notification::~Notification(void) { - Service::InvalidateNotificationsCache(); + Service::RefreshNotificationsCache(); } bool Notification::Exists(const String& name) @@ -60,9 +60,9 @@ Service::Ptr Notification::GetService(void) const Host::Ptr host = Host::GetByName(m_HostName); if (m_Service.IsEmpty()) - return host->GetHostCheckService(); + return Host::GetHostCheckService(host); else - return host->GetServiceByShortName(m_Service); + return Host::GetServiceByShortName(host, m_Service); } Value Notification::GetNotificationCommand(void) const @@ -239,5 +239,5 @@ void Notification::NotificationCompletedHandler(const ScriptTask::Ptr& task) void Notification::OnAttributeChanged(const String& name, const Value& oldValue) { if (name == "host_name" || name == "service") - Service::InvalidateNotificationsCache(); + Service::RefreshNotificationsCache(); } diff --git a/lib/icinga/service-check.cpp b/lib/icinga/service-check.cpp index 5bc4e2bc5..72a344071 100644 --- a/lib/icinga/service-check.cpp +++ b/lib/icinga/service-check.cpp @@ -254,10 +254,11 @@ void Service::ApplyCheckResult(const Dictionary::Ptr& cr) long attempt = GetCurrentCheckAttempt(); if (cr->Get("state") == StateOK) { - if (old_state == StateOK && old_stateType == StateTypeSoft) { + if (old_state == StateOK && old_stateType == StateTypeSoft) hardChange = true; // hard recovery + + if (old_state == StateOK || old_stateType == StateTypeSoft) SetStateType(StateTypeHard); - } attempt = 1; recovery = true; @@ -294,16 +295,18 @@ void Service::ApplyCheckResult(const Dictionary::Ptr& cr) } /* reschedule service dependencies */ - BOOST_FOREACH(const Service::Ptr& parent, GetParentServices()) { + BOOST_FOREACH(const Service::Ptr& parent, Service::GetParentServices(GetSelf())) { parent->SetNextCheck(Utility::GetTime()); } /* reschedule host dependencies */ - BOOST_FOREACH(const Host::Ptr& parent, GetParentHosts()) { - Service::Ptr service = parent->GetHostCheckService(); + BOOST_FOREACH(const Host::Ptr& parent, Service::GetParentHosts(GetSelf())) { + Service::Ptr service = Host::GetHostCheckService(parent); - if (service) + if (service) { + ObjectLock olock(service); service->SetNextCheck(Utility::GetTime()); + } } } diff --git a/lib/icinga/service-comment.cpp b/lib/icinga/service-comment.cpp index 6e18c84ab..ad82c22a1 100644 --- a/lib/icinga/service-comment.cpp +++ b/lib/icinga/service-comment.cpp @@ -22,9 +22,9 @@ using namespace icinga; int Service::m_NextCommentID = 1; +boost::mutex Service::m_CommentMutex; map Service::m_LegacyCommentsCache; map Service::m_CommentsCache; -bool Service::m_CommentsCacheValid; Timer::Ptr Service::m_CommentsExpireTimer; int Service::GetNextCommentID(void) @@ -46,7 +46,15 @@ String Service::AddComment(CommentType entryType, const String& author, comment->Set("author", author); comment->Set("text", text); comment->Set("expire_time", expireTime); - comment->Set("legacy_id", m_NextCommentID++); + + int legacy_id; + + { + boost::mutex::scoped_lock lock(m_CommentMutex); + legacy_id = m_NextCommentID++; + } + + comment->Set("legacy_id", legacy_id); Dictionary::Ptr comments = m_Comments; @@ -64,7 +72,8 @@ String Service::AddComment(CommentType entryType, const String& author, void Service::RemoveAllComments(void) { - Set("comments", Empty); + m_Comments = Empty; + Touch("comments"); } void Service::RemoveComment(const String& id) @@ -84,6 +93,8 @@ void Service::RemoveComment(const String& id) String Service::GetCommentIDFromLegacyID(int id) { + boost::mutex::scoped_lock lock(m_CommentMutex); + map::iterator it = m_LegacyCommentsCache.find(id); if (it == m_LegacyCommentsCache.end()) @@ -94,7 +105,7 @@ String Service::GetCommentIDFromLegacyID(int id) Service::Ptr Service::GetOwnerByCommentID(const String& id) { - ValidateCommentsCache(); + boost::mutex::scoped_lock lock(m_CommentMutex); return m_CommentsCache[id].lock(); } @@ -118,61 +129,60 @@ Dictionary::Ptr Service::GetCommentByID(const String& id) bool Service::IsCommentExpired(const Dictionary::Ptr& comment) { + ObjectLock olock(comment); double expire_time = comment->Get("expire_time"); return (expire_time != 0 && expire_time < Utility::GetTime()); } -void Service::InvalidateCommentsCache(void) +void Service::RefreshCommentsCache(void) { - m_CommentsCacheValid = false; - m_CommentsCache.clear(); - m_LegacyCommentsCache.clear(); -} - -void Service::AddCommentsToCache(void) -{ - Dictionary::Ptr comments = m_Comments; - - if (!comments) - return; - - String id; - Dictionary::Ptr comment; - BOOST_FOREACH(tie(id, comment), comments) { - int legacy_id = comment->Get("legacy_id"); - - if (legacy_id >= m_NextCommentID) - m_NextCommentID = legacy_id + 1; - - if (m_LegacyCommentsCache.find(legacy_id) != m_LegacyCommentsCache.end()) { - /* The legacy_id is already in use by another comment; - * this shouldn't usually happen - assign it a new ID */ - - legacy_id = m_NextCommentID++; - comment->Set("legacy_id", legacy_id); - Touch("comments"); - } - - m_LegacyCommentsCache[legacy_id] = id; - m_CommentsCache[id] = GetSelf(); - } -} - -void Service::ValidateCommentsCache(void) -{ - if (m_CommentsCacheValid) - return; - - m_CommentsCache.clear(); - m_LegacyCommentsCache.clear(); + map newLegacyCommentsCache; + map newCommentsCache; BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Service")) { Service::Ptr service = dynamic_pointer_cast(object); - service->AddCommentsToCache(); + + Dictionary::Ptr comments; + + { + ObjectLock olock(service); + comments = service->GetComments(); + } + + if (!comments) + continue; + + ObjectLock olock(comments); + + String id; + Dictionary::Ptr comment; + BOOST_FOREACH(tie(id, comment), comments) { + ObjectLock clock(comment); + + int legacy_id = comment->Get("legacy_id"); + + if (legacy_id >= m_NextCommentID) + m_NextCommentID = legacy_id + 1; + + if (newLegacyCommentsCache.find(legacy_id) != newLegacyCommentsCache.end()) { + /* The legacy_id is already in use by another comment; + * this shouldn't usually happen - assign it a new ID */ + + legacy_id = m_NextCommentID++; + comment->Set("legacy_id", legacy_id); + service->Touch("comments"); + } + + newLegacyCommentsCache[legacy_id] = id; + newCommentsCache[id] = service; + } } - m_CommentsCacheValid = true; + boost::mutex::scoped_lock lock(m_CommentMutex); + + m_CommentsCache.swap(newCommentsCache); + m_LegacyCommentsCache.swap(newLegacyCommentsCache); if (!m_CommentsExpireTimer) { m_CommentsExpireTimer = boost::make_shared(); @@ -191,6 +201,8 @@ void Service::RemoveExpiredComments(void) vector expiredComments; + ObjectLock olock(comments); + String id; Dictionary::Ptr comment; BOOST_FOREACH(tie(id, comment), comments) { diff --git a/lib/icinga/service-downtime.cpp b/lib/icinga/service-downtime.cpp index d63195580..a24d7abab 100644 --- a/lib/icinga/service-downtime.cpp +++ b/lib/icinga/service-downtime.cpp @@ -22,9 +22,9 @@ using namespace icinga; int Service::m_NextDowntimeID = 1; +boost::mutex Service::m_DowntimeMutex; map Service::m_LegacyDowntimesCache; map Service::m_DowntimesCache; -bool Service::m_DowntimesCacheValid; Timer::Ptr Service::m_DowntimesExpireTimer; int Service::GetNextDowntimeID(void) @@ -52,7 +52,15 @@ String Service::AddDowntime(const String& author, const String& comment, downtime->Set("triggered_by", triggeredBy); downtime->Set("triggers", boost::make_shared()); downtime->Set("trigger_time", 0); - downtime->Set("legacy_id", m_NextDowntimeID++); + + int legacy_id; + + { + boost::mutex::scoped_lock lock(m_DowntimeMutex); + legacy_id = m_NextDowntimeID++; + } + + downtime->Set("legacy_id", legacy_id); if (!triggeredBy.IsEmpty()) { Service::Ptr otherOwner = GetOwnerByDowntimeID(triggeredBy); @@ -89,7 +97,11 @@ void Service::RemoveDowntime(const String& id) if (!downtimes) return; - downtimes->Remove(id); + { + ObjectLock olock(downtimes); + downtimes->Remove(id); + } + owner->Touch("downtimes"); } @@ -100,6 +112,8 @@ void Service::TriggerDowntimes(void) if (!downtimes) return; + ObjectLock olock(downtimes); + String id; BOOST_FOREACH(tie(id, tuples::ignore), downtimes) { TriggerDowntime(id); @@ -111,8 +125,13 @@ void Service::TriggerDowntime(const String& id) Service::Ptr owner = GetOwnerByDowntimeID(id); Dictionary::Ptr downtime = GetDowntimeByID(id); + if (!downtime) + return; + double now = Utility::GetTime(); + ObjectLock olock(downtime); + if (now < downtime->Get("start_time") || now > downtime->Get("end_time")) return; @@ -121,6 +140,7 @@ void Service::TriggerDowntime(const String& id) downtime->Set("trigger_time", now); Dictionary::Ptr triggers = downtime->Get("triggers"); + ObjectLock tlock(triggers); String tid; BOOST_FOREACH(tie(tid, tuples::ignore), triggers) { TriggerDowntime(tid); @@ -131,7 +151,7 @@ void Service::TriggerDowntime(const String& id) String Service::GetDowntimeIDFromLegacyID(int id) { - ValidateDowntimesCache(); + boost::mutex::scoped_lock lock(m_DowntimeMutex); map::iterator it = m_LegacyDowntimesCache.find(id); @@ -143,8 +163,7 @@ String Service::GetDowntimeIDFromLegacyID(int id) Service::Ptr Service::GetOwnerByDowntimeID(const String& id) { - ValidateDowntimesCache(); - + boost::mutex::scoped_lock lock(m_DowntimeMutex); return m_DowntimesCache[id].lock(); } @@ -158,6 +177,7 @@ Dictionary::Ptr Service::GetDowntimeByID(const String& id) Dictionary::Ptr downtimes = owner->m_Downtimes; if (downtimes) { + ObjectLock olock(downtimes); Dictionary::Ptr downtime = downtimes->Get(id); return downtime; } @@ -169,6 +189,8 @@ bool Service::IsDowntimeActive(const Dictionary::Ptr& downtime) { double now = Utility::GetTime(); + ObjectLock olock(downtime); + if (now < downtime->Get("start_time") || now > downtime->Get("end_time")) return false; @@ -186,58 +208,56 @@ bool Service::IsDowntimeActive(const Dictionary::Ptr& downtime) bool Service::IsDowntimeExpired(const Dictionary::Ptr& downtime) { + ObjectLock olock(downtime); return (downtime->Get("end_time") < Utility::GetTime()); } -void Service::InvalidateDowntimesCache(void) +void Service::RefreshDowntimesCache(void) { - m_DowntimesCacheValid = false; - m_DowntimesCache.clear(); - m_LegacyDowntimesCache.clear(); -} - -void Service::AddDowntimesToCache(void) -{ - Dictionary::Ptr downtimes = m_Downtimes; - - if (!downtimes) - return; - - String id; - Dictionary::Ptr downtime; - BOOST_FOREACH(tie(id, downtime), downtimes) { - int legacy_id = downtime->Get("legacy_id"); - - if (legacy_id >= m_NextDowntimeID) - m_NextDowntimeID = legacy_id + 1; - - if (m_LegacyDowntimesCache.find(legacy_id) != m_LegacyDowntimesCache.end()) { - /* The legacy_id is already in use by another downtime; - * this shouldn't usually happen - assign it a new ID. */ - legacy_id = m_NextDowntimeID++; - downtime->Set("legacy_id", legacy_id); - Touch("downtimes"); - } - - m_LegacyDowntimesCache[legacy_id] = id; - m_DowntimesCache[id] = GetSelf(); - } -} - -void Service::ValidateDowntimesCache(void) -{ - if (m_DowntimesCacheValid) - return; - - m_DowntimesCache.clear(); - m_LegacyDowntimesCache.clear(); + map newLegacyDowntimesCache; + map newDowntimesCache; BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Service")) { Service::Ptr service = dynamic_pointer_cast(object); - service->AddDowntimesToCache(); + + Dictionary::Ptr downtimes; + + { + ObjectLock olock(service); + downtimes = service->GetDowntimes(); + } + + if (!downtimes) + continue; + + ObjectLock olock(downtimes); + + String id; + Dictionary::Ptr downtime; + BOOST_FOREACH(tie(id, downtime), downtimes) { + ObjectLock dlock(downtime); + int legacy_id = downtime->Get("legacy_id"); + + if (legacy_id >= m_NextDowntimeID) + m_NextDowntimeID = legacy_id + 1; + + if (newLegacyDowntimesCache.find(legacy_id) != newLegacyDowntimesCache.end()) { + /* The legacy_id is already in use by another downtime; + * this shouldn't usually happen - assign it a new ID. */ + legacy_id = m_NextDowntimeID++; + downtime->Set("legacy_id", legacy_id); + service->Touch("downtimes"); + } + + newLegacyDowntimesCache[legacy_id] = id; + newDowntimesCache[id] = service; + } } - m_DowntimesCacheValid = true; + boost::mutex::scoped_lock lock(m_DowntimeMutex); + + m_DowntimesCache.swap(newDowntimesCache); + m_LegacyDowntimesCache.swap(newLegacyDowntimesCache); if (!m_DowntimesExpireTimer) { m_DowntimesExpireTimer = boost::make_shared(); @@ -256,6 +276,8 @@ void Service::RemoveExpiredDowntimes(void) vector expiredDowntimes; + ObjectLock olock(downtimes); + String id; Dictionary::Ptr downtime; BOOST_FOREACH(tie(id, downtime), downtimes) { @@ -288,6 +310,8 @@ bool Service::IsInDowntime(void) const if (!downtimes) return false; + ObjectLock olock(downtimes); + Dictionary::Ptr downtime; BOOST_FOREACH(tie(tuples::ignore, downtime), downtimes) { if (Service::IsDowntimeActive(downtime)) diff --git a/lib/icinga/service-notification.cpp b/lib/icinga/service-notification.cpp index 88d4aa43d..dcf08d370 100644 --- a/lib/icinga/service-notification.cpp +++ b/lib/icinga/service-notification.cpp @@ -21,10 +21,10 @@ using namespace icinga; +boost::mutex Service::m_NotificationMutex; map > Service::m_NotificationsCache; -bool Service::m_NotificationsCacheValid; -void Service::RequestNotifications(NotificationType type) const +void Service::RequestNotifications(NotificationType type) { RequestMessage msg; msg.SetMethod("icinga::SendNotifications"); @@ -37,21 +37,32 @@ void Service::RequestNotifications(NotificationType type) const Logger::Write(LogDebug, "icinga", "Sending notification anycast request for service '" + GetName() + "'"); EndpointManager::GetInstance()->SendAnycastMessage(Endpoint::Ptr(), msg); + + SetLastNotification(Utility::GetTime()); } -void Service::SendNotifications(NotificationType type) +void Service::SendNotifications(const Service::Ptr& self, NotificationType type) { - if (!GetEnableNotifications()) { - Logger::Write(LogInformation, "icinga", "Notifications are disabled for service '" + GetName() + "'."); + String service_name; + bool enable_notifications; + + { + ObjectLock olock(self); + service_name = self->GetName(); + enable_notifications = self->GetEnableNotifications(); + } + + if (!enable_notifications) { + Logger::Write(LogInformation, "icinga", "Notifications are disabled for service '" + service_name + "'."); return; } - Logger::Write(LogInformation, "icinga", "Sending notifications for service '" + GetName() + "'"); + Logger::Write(LogInformation, "icinga", "Sending notifications for service '" + service_name + "'"); - set notifications = GetNotifications(); + set notifications = GetNotifications(self); if (notifications.size() == 0) - Logger::Write(LogInformation, "icinga", "Service '" + GetName() + "' does not have any notifications."); + Logger::Write(LogInformation, "icinga", "Service '" + service_name + "' does not have any notifications."); BOOST_FOREACH(const Notification::Ptr& notification, notifications) { try { @@ -59,51 +70,64 @@ void Service::SendNotifications(NotificationType type) } catch (const exception& ex) { stringstream msgbuf; msgbuf << "Exception occured during notification for service '" - << GetName() << "': " << diagnostic_information(ex); + << service_name << "': " << diagnostic_information(ex); String message = msgbuf.str(); Logger::Write(LogWarning, "icinga", message); } } - - SetLastNotification(Utility::GetTime()); } -void Service::InvalidateNotificationsCache(void) +void Service::RefreshNotificationsCache(void) { - m_NotificationsCacheValid = false; - m_NotificationsCache.clear(); -} - -void Service::ValidateNotificationsCache(void) -{ - if (m_NotificationsCacheValid) - return; - - m_NotificationsCache.clear(); + map > newNotificationsCache; BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Notification")) { const Notification::Ptr& notification = static_pointer_cast(object); - m_NotificationsCache[notification->GetService()->GetName()].insert(notification); + Service::Ptr service; + + { + ObjectLock olock(notification); + service = notification->GetService(); + } + + String service_name; + + { + ObjectLock olock(service); + service_name = service->GetName(); + } + + newNotificationsCache[service_name].insert(notification); } - m_NotificationsCacheValid = true; + boost::mutex::scoped_lock lock(m_NotificationMutex); + m_NotificationsCache.swap(newNotificationsCache); } -set Service::GetNotifications(void) const +set Service::GetNotifications(const Service::Ptr& self) { + String name; + + { + ObjectLock olock(self); + name = self->GetName(); + } + set notifications; - ValidateNotificationsCache(); + { + boost::mutex::scoped_lock lock(m_NotificationMutex); - BOOST_FOREACH(const Notification::WeakPtr& wservice, m_NotificationsCache[GetName()]) { - Notification::Ptr notification = wservice.lock(); + BOOST_FOREACH(const Notification::WeakPtr& wservice, m_NotificationsCache[name]) { + Notification::Ptr notification = wservice.lock(); - if (!notification) - continue; + if (!notification) + continue; - notifications.insert(notification); + notifications.insert(notification); + } } return notifications; @@ -269,3 +293,11 @@ void Service::SetEnableNotifications(bool enabled) m_EnableNotifications = enabled; Touch("enable_notifications"); } + +double Service::GetNotificationInterval(void) const +{ + if (m_NotificationInterval.IsEmpty()) + return 300; + else + return m_NotificationInterval; +} diff --git a/lib/icinga/service.cpp b/lib/icinga/service.cpp index cf18b6a07..8f003a4cd 100644 --- a/lib/icinga/service.cpp +++ b/lib/icinga/service.cpp @@ -63,31 +63,25 @@ Service::Service(const Dictionary::Ptr& serializedObject) RegisterAttribute("enable_notifications", Attribute_Replicated, &m_EnableNotifications); RegisterAttribute("last_notification", Attribute_Replicated, &m_LastNotification); + RegisterAttribute("notification_interval", Attribute_Config, &m_NotificationInterval); SetSchedulingOffset(rand()); } +Service::~Service(void) +{ + ServiceGroup::RefreshMembersCache(); + Host::RefreshServicesCache(); + Service::RefreshDowntimesCache(); + Service::RefreshCommentsCache(); +} + void Service::OnRegistrationCompleted(void) { DynamicObject::OnRegistrationCompleted(); - ServiceGroup::InvalidateMembersCache(); - Host::InvalidateServicesCache(); - Service::InvalidateDowntimesCache(); - Service::InvalidateCommentsCache(); - - { - ObjectLock olock(this); - m_Host = Host::GetByName(GetHostName()); - } -} - -Service::~Service(void) -{ - ServiceGroup::InvalidateMembersCache(); - Host::InvalidateServicesCache(); - Service::InvalidateDowntimesCache(); - Service::InvalidateCommentsCache(); + ServiceGroup::RefreshMembersCache(); + Host::RefreshServicesCache(); } String Service::GetDisplayName(void) const @@ -126,8 +120,7 @@ Service::Ptr Service::GetByNamePair(const String& hostName, const String& servic { if (!hostName.IsEmpty()) { Host::Ptr host = Host::GetByName(hostName); - ObjectLock olock(host); - return host->GetServiceByShortName(serviceName); + return Host::GetServiceByShortName(host, serviceName); } else { return Service::GetByName(serviceName); } @@ -135,7 +128,7 @@ Service::Ptr Service::GetByNamePair(const String& hostName, const String& servic Host::Ptr Service::GetHost(void) const { - return m_Host; + return Host::GetByName(m_HostName); } Dictionary::Ptr Service::GetMacros(void) const @@ -173,14 +166,7 @@ String Service::GetShortName(void) const bool Service::IsReachable(const Service::Ptr& self) { - set parentServices; - - { - ObjectLock olock(self); - parentServices = self->GetParentServices(); - } - - BOOST_FOREACH(const Service::Ptr& service, parentServices) { + BOOST_FOREACH(const Service::Ptr& service, Service::GetParentServices(self)) { ObjectLock olock(service); /* ignore pending services */ @@ -199,20 +185,9 @@ bool Service::IsReachable(const Service::Ptr& self) return false; } - set parentHosts; - - { - ObjectLock olock(self); - parentHosts = self->GetParentHosts(); - } - - BOOST_FOREACH(const Host::Ptr& host, parentHosts) { - Service::Ptr hc; - - { - ObjectLock olock(host); - hc = host->GetHostCheckService(); - } + BOOST_FOREACH(const Host::Ptr& host, Service::GetParentHosts(self)) { + Service::Ptr hc = Host::GetHostCheckService(host); + ObjectLock olock(hc); /* ignore hosts that are up */ if (hc && hc->GetState() == StateOK) @@ -291,18 +266,15 @@ void Service::OnAttributeChanged(const String& name, const Value& oldValue) else if (name == "next_check") OnNextCheckChanged(GetSelf(), oldValue); else if (name == "servicegroups") - ServiceGroup::InvalidateMembersCache(); + ServiceGroup::RefreshMembersCache(); else if (name == "host_name" || name == "short_name") { - Host::InvalidateServicesCache(); + Host::RefreshServicesCache(); UpdateSlaveNotifications(GetSelf()); - - if (name == "host_name") - m_Host = Host::GetByName(GetHostName()); } else if (name == "downtimes") - Service::InvalidateDowntimesCache(); + Service::RefreshDowntimesCache(); else if (name == "comments") - Service::InvalidateCommentsCache(); + Service::RefreshCommentsCache(); else if (name == "notifications") UpdateSlaveNotifications(GetSelf()); else if (name == "check_interval") { @@ -315,14 +287,21 @@ void Service::OnAttributeChanged(const String& name, const Value& oldValue) } } -set Service::GetParentHosts(void) const +set Service::GetParentHosts(const Service::Ptr& self) { set parents; - /* The service's host is implicitly a parent. */ - parents.insert(GetHost()); + Dictionary::Ptr dependencies; + + { + ObjectLock olock(self); + + /* The service's host is implicitly a parent. */ + parents.insert(self->GetHost()); + + dependencies = self->GetHostDependencies(); + } - Dictionary::Ptr dependencies = GetHostDependencies(); if (dependencies) { String key; @@ -334,19 +313,29 @@ set Service::GetParentHosts(void) const return parents; } -set Service::GetParentServices(void) const +set Service::GetParentServices(const Service::Ptr& self) { set parents; - Dictionary::Ptr dependencies = GetServiceDependencies(); + Dictionary::Ptr dependencies; + Host::Ptr host; + String service_name; + + { + ObjectLock olock(self); + dependencies = self->GetServiceDependencies(); + host = self->GetHost(); + service_name = self->GetName(); + } if (dependencies) { String key; Value value; BOOST_FOREACH(tie(key, value), dependencies) { - Service::Ptr service = GetHost()->GetServiceByShortName(value); + Service::Ptr service = Host::GetServiceByShortName(host, value); + ObjectLock olock(service); - if (service->GetName() == GetName()) + if (service->GetName() == service_name) continue; parents.insert(service); diff --git a/lib/icinga/service.h b/lib/icinga/service.h index f6677041f..0f050d00a 100644 --- a/lib/icinga/service.h +++ b/lib/icinga/service.h @@ -109,8 +109,8 @@ public: static Dictionary::Ptr CalculateDynamicMacros(const Service::Ptr& self); - set GetParentHosts(void) const; - set GetParentServices(void) const; + static set GetParentHosts(const Service::Ptr& self); + static set GetParentServices(const Service::Ptr& self); bool IsReachable(const Service::Ptr& self); @@ -208,8 +208,7 @@ public: static bool IsDowntimeActive(const Dictionary::Ptr& downtime); static bool IsDowntimeExpired(const Dictionary::Ptr& downtime); - static void InvalidateDowntimesCache(void); - static void ValidateDowntimesCache(void); + static void RefreshDowntimesCache(void); bool IsInDowntime(void) const; bool IsAcknowledged(void); @@ -231,20 +230,20 @@ public: static bool IsCommentExpired(const Dictionary::Ptr& comment); - static void InvalidateCommentsCache(void); - static void ValidateCommentsCache(void); + static void RefreshCommentsCache(void); /* Notifications */ bool GetEnableNotifications(void) const; void SetEnableNotifications(bool enabled); - void RequestNotifications(NotificationType type) const; - void SendNotifications(NotificationType type); + double GetNotificationInterval(void) const; - static void InvalidateNotificationsCache(void); - static void ValidateNotificationsCache(void); + void RequestNotifications(NotificationType type); + static void SendNotifications(const Service::Ptr& self, NotificationType type); - set GetNotifications(void) const; + static set GetNotifications(const Service::Ptr& self); + + static void RefreshNotificationsCache(void); static void UpdateSlaveNotifications(const Service::Ptr& self); @@ -256,7 +255,6 @@ protected: virtual void OnAttributeChanged(const String& name, const Value& oldValue); private: - Host::Ptr m_Host; Dictionary::Ptr m_SlaveNotifications; Attribute m_DisplayName; @@ -298,14 +296,13 @@ private: static int m_NextDowntimeID; + static boost::mutex m_DowntimeMutex; static map m_LegacyDowntimesCache; static map m_DowntimesCache; - static bool m_DowntimesCacheValid; static Timer::Ptr m_DowntimesExpireTimer; static void DowntimesExpireTimerHandler(void); - void AddDowntimesToCache(void); void RemoveExpiredDowntimes(void); /* Comments */ @@ -313,9 +310,9 @@ private: static int m_NextCommentID; + static boost::mutex m_CommentMutex; static map m_LegacyCommentsCache; static map m_CommentsCache; - static bool m_CommentsCacheValid; static Timer::Ptr m_CommentsExpireTimer; static void CommentsExpireTimerHandler(void); @@ -326,9 +323,10 @@ private: /* Notifications */ Attribute m_EnableNotifications; Attribute m_LastNotification; + Attribute m_NotificationInterval; + static boost::mutex m_NotificationMutex; static map > m_NotificationsCache; - static bool m_NotificationsCacheValid; }; } diff --git a/lib/icinga/servicegroup.cpp b/lib/icinga/servicegroup.cpp index d96b4e255..34790672a 100644 --- a/lib/icinga/servicegroup.cpp +++ b/lib/icinga/servicegroup.cpp @@ -23,7 +23,6 @@ using namespace icinga; boost::mutex ServiceGroup::m_Mutex; map > ServiceGroup::m_MembersCache; -bool ServiceGroup::m_MembersCacheValid; REGISTER_TYPE(ServiceGroup, NULL); @@ -35,6 +34,16 @@ ServiceGroup::ServiceGroup(const Dictionary::Ptr& properties) RegisterAttribute("action_url", Attribute_Config, &m_ActionUrl); } +ServiceGroup::~ServiceGroup(void) +{ + RefreshMembersCache(); +} + +void ServiceGroup::OnRegistrationCompleted(void) +{ + RefreshMembersCache(); +} + String ServiceGroup::GetDisplayName(void) const { if (!m_DisplayName.Get().IsEmpty()) @@ -88,10 +97,8 @@ set ServiceGroup::GetMembers(const ServiceGroup::Ptr& self) { boost::mutex::scoped_lock lock(m_Mutex); - ValidateMembersCache(); - - BOOST_FOREACH(const Service::WeakPtr& svc, m_MembersCache[name]) { - Service::Ptr service = svc.lock(); + BOOST_FOREACH(const Service::WeakPtr& wservice, m_MembersCache[name]) { + Service::Ptr service = wservice.lock(); if (!service) continue; @@ -103,22 +110,9 @@ set ServiceGroup::GetMembers(const ServiceGroup::Ptr& self) return services; } -void ServiceGroup::InvalidateMembersCache(void) +void ServiceGroup::RefreshMembersCache(void) { - boost::mutex::scoped_lock lock(m_Mutex); - m_MembersCacheValid = false; - m_MembersCache.clear(); -} - -/** - * @threadsafety Caller must hold m_Mutex. - */ -void ServiceGroup::ValidateMembersCache(void) -{ - if (m_MembersCacheValid) - return; - - m_MembersCache.clear(); + map > > newMembersCache; BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Service")) { const Service::Ptr& service = static_pointer_cast(object); @@ -132,12 +126,14 @@ void ServiceGroup::ValidateMembersCache(void) Value servicegroup; BOOST_FOREACH(tie(tuples::ignore, servicegroup), dict) { if (!ServiceGroup::Exists(servicegroup)) - Logger::Write(LogWarning, "icinga", "Service group '" + static_cast(servicegroup) + "' used but not defined."); + continue; - m_MembersCache[servicegroup].push_back(service); + newMembersCache[servicegroup].push_back(service); } } } - m_MembersCacheValid = true; + boost::mutex::scoped_lock lock(m_Mutex); + m_MembersCache.swap(newMembersCache); + } diff --git a/lib/icinga/servicegroup.h b/lib/icinga/servicegroup.h index c75508117..5ea5a6fc5 100644 --- a/lib/icinga/servicegroup.h +++ b/lib/icinga/servicegroup.h @@ -35,6 +35,7 @@ public: typedef weak_ptr WeakPtr; ServiceGroup(const Dictionary::Ptr& properties); + ~ServiceGroup(void); static bool Exists(const String& name); static ServiceGroup::Ptr GetByName(const String& name); @@ -44,7 +45,11 @@ public: String GetActionUrl(void) const; static set GetMembers(const ServiceGroup::Ptr& self); - static void InvalidateMembersCache(void); + + static void RefreshMembersCache(void); + +protected: + virtual void OnRegistrationCompleted(void); private: Attribute m_DisplayName; @@ -53,9 +58,6 @@ private: static boost::mutex m_Mutex; static map > > m_MembersCache; - static bool m_MembersCacheValid; - - static void ValidateMembersCache(void); }; }