From: Gunnar Beutner Date: Sun, 24 Feb 2013 00:10:34 +0000 (+0100) Subject: Fine-grained locks (WIP, Part 9). X-Git-Tag: v0.0.2~372 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2ef255b9f66864c4bb7b4804715f0d94b9f2207d;p=icinga2 Fine-grained locks (WIP, Part 9). --- diff --git a/components/checker/checkercomponent.cpp b/components/checker/checkercomponent.cpp index e9136beb6..565d52ad1 100644 --- a/components/checker/checkercomponent.cpp +++ b/components/checker/checkercomponent.cpp @@ -21,7 +21,7 @@ using namespace icinga; -EXPORT_COMPONENT(checker, CheckerComponent); +REGISTER_COMPONENT("checker", CheckerComponent); void CheckerComponent::Start(void) { @@ -59,12 +59,9 @@ void CheckerComponent::Stop(void) void CheckerComponent::CheckThreadProc(void) { - for (;;) { - vector services; - Service::Ptr service; - - boost::mutex::scoped_lock lock(m_Mutex); + boost::mutex::scoped_lock lock(m_Mutex); + for (;;) { typedef nth_index::type CheckTimeView; CheckTimeView& idx = boost::get<1>(m_IdleServices); @@ -75,7 +72,7 @@ void CheckerComponent::CheckThreadProc(void) break; CheckTimeView::iterator it = idx.begin(); - service = it->lock(); + Service::Ptr service = it->lock(); if (!service) { idx.erase(it); @@ -131,20 +128,16 @@ void CheckerComponent::CheckThreadProc(void) m_IdleServices.erase(service); m_PendingServices.insert(service); - double rwait = service->GetNextCheck() - Utility::GetTime(); - - if (rwait < -5) - Logger::Write(LogWarning, "checker", "Check delayed: " + Convert::ToString(-rwait)); - try { - service->BeginExecuteCheck(boost::bind(&CheckerComponent::CheckCompletedHandler, this, service)); + olock.Unlock(); + Service::BeginExecuteCheck(service, boost::bind(&CheckerComponent::CheckCompletedHandler, this, service)); } catch (const exception& ex) { + olock.Lock(); Logger::Write(LogCritical, "checker", "Exception occured while checking service '" + service->GetName() + "': " + diagnostic_information(ex)); } } } - void CheckerComponent::CheckCompletedHandler(const Service::Ptr& service) { boost::mutex::scoped_lock lock(m_Mutex); @@ -217,4 +210,3 @@ void CheckerComponent::NextCheckChangedHandler(const Service::Ptr& service) idx.insert(service); m_CV.notify_all(); } - diff --git a/components/compat/compatcomponent.cpp b/components/compat/compatcomponent.cpp index 5507b5801..0fc59f6ca 100644 --- a/components/compat/compatcomponent.cpp +++ b/components/compat/compatcomponent.cpp @@ -21,7 +21,7 @@ using namespace icinga; -EXPORT_COMPONENT(compat, CompatComponent); +REGISTER_COMPONENT("compat", CompatComponent); /** * Hint: The reason why we're using "\n" rather than std::endl is because @@ -91,7 +91,7 @@ String CompatComponent::GetCommandPath(void) const void CompatComponent::Start(void) { m_StatusTimer = boost::make_shared(); - m_StatusTimer->SetInterval(60); + m_StatusTimer->SetInterval(15); m_StatusTimer->OnTimerExpired.connect(boost::bind(&CompatComponent::StatusTimerHandler, this)); m_StatusTimer->Start(); m_StatusTimer->Reschedule(0); @@ -323,11 +323,10 @@ void CompatComponent::DumpServiceStatusAttrs(ofstream& fp, const Service::Ptr& s { String output; String perfdata; - double schedule_start = -1, schedule_end = -1; - double execution_start = -1, execution_end = -1; + double schedule_end = -1; Dictionary::Ptr cr; - int state; + int state, state_type; Host::Ptr host; { @@ -335,21 +334,16 @@ void CompatComponent::DumpServiceStatusAttrs(ofstream& fp, const Service::Ptr& s cr = service->GetLastCheckResult(); state = service->GetState(); + state_type = service->GetStateType(); host = service->GetHost(); } if (cr) { output = cr->Get("output"); - schedule_start = cr->Get("schedule_start"); schedule_end = cr->Get("schedule_end"); - execution_start = cr->Get("execution_start"); - execution_end = cr->Get("execution_end"); perfdata = cr->Get("performance_data_raw"); } - double execution_time = (execution_end - execution_start); - double latency = (schedule_end - schedule_start) - execution_time; - if (state > StateUnknown) state = StateUnknown; @@ -370,10 +364,10 @@ void CompatComponent::DumpServiceStatusAttrs(ofstream& fp, const Service::Ptr& s << "\t" << "retry_interval=" << service->GetRetryInterval() / 60.0 << "\n" << "\t" << "has_been_checked=" << (service->GetLastCheckResult() ? 1 : 0) << "\n" << "\t" << "should_be_scheduled=1" << "\n" - << "\t" << "check_execution_time=" << execution_time << "\n" - << "\t" << "check_latency=" << latency << "\n" + << "\t" << "check_execution_time=" << Service::CalculateExecutionTime(cr) << "\n" + << "\t" << "check_latency=" << Service::CalculateLatency(cr) << "\n" << "\t" << "current_state=" << state << "\n" - << "\t" << "state_type=" << service->GetStateType() << "\n" + << "\t" << "state_type=" << state_type << "\n" << "\t" << "plugin_output=" << output << "\n" << "\t" << "performance_data=" << perfdata << "\n" << "\t" << "last_check=" << schedule_end << "\n" @@ -518,7 +512,8 @@ void CompatComponent::StatusTimerHandler(void) << "\t" << "passive_host_checks_enabled=0" << "\n" << "\t" << "check_service_freshness=0" << "\n" << "\t" << "check_host_freshness=0" << "\n" - << "\t" << "enable_flap_detection=1" << "\n" + << "\t" << "enable_notifications=1" << "\n" + << "\t" << "enable_flap_detection=0" << "\n" << "\t" << "enable_failure_prediction=0" << "\n" << "\t" << "active_scheduled_service_check_stats=" << CIB::GetActiveChecksStatistics(60) << "," << CIB::GetActiveChecksStatistics(5 * 60) << "," << CIB::GetActiveChecksStatistics(15 * 60) << "\n" << "\t" << "passive_service_check_stats=" << CIB::GetPassiveChecksStatistics(60) << "," << CIB::GetPassiveChecksStatistics(5 * 60) << "," << CIB::GetPassiveChecksStatistics(15 * 60) << "\n" diff --git a/components/compatido/compatidocomponent.cpp b/components/compatido/compatidocomponent.cpp index 33c3dd5c7..69bf055ad 100644 --- a/components/compatido/compatidocomponent.cpp +++ b/components/compatido/compatidocomponent.cpp @@ -21,7 +21,7 @@ using namespace icinga; -EXPORT_COMPONENT(compatido, CompatIdoComponent); +REGISTER_COMPONENT("compatido", CompatIdoComponent); const String CompatIdoComponent::DefaultSocketAddress = "127.0.0.1"; const String CompatIdoComponent::DefaultSocketPort = "5668"; diff --git a/components/delegation/delegationcomponent.cpp b/components/delegation/delegationcomponent.cpp index 09836eb1e..1191bce94 100644 --- a/components/delegation/delegationcomponent.cpp +++ b/components/delegation/delegationcomponent.cpp @@ -22,7 +22,7 @@ using namespace icinga; -EXPORT_COMPONENT(delegation, DelegationComponent); +REGISTER_COMPONENT("delegation", DelegationComponent); void DelegationComponent::Start(void) { diff --git a/components/demo/democomponent.cpp b/components/demo/democomponent.cpp index 31b931c19..355f5014c 100644 --- a/components/demo/democomponent.cpp +++ b/components/demo/democomponent.cpp @@ -21,7 +21,7 @@ using namespace icinga; -EXPORT_COMPONENT(demo, DemoComponent); +REGISTER_COMPONENT("demo", DemoComponent); /** * Starts the component. diff --git a/components/notification/notificationcomponent.cpp b/components/notification/notificationcomponent.cpp index d6e5a9151..ae5178b52 100644 --- a/components/notification/notificationcomponent.cpp +++ b/components/notification/notificationcomponent.cpp @@ -21,7 +21,7 @@ using namespace icinga; -EXPORT_COMPONENT(notification, NotificationComponent); +REGISTER_COMPONENT("notification", NotificationComponent); /** * Starts the component. diff --git a/components/replication/replicationcomponent.cpp b/components/replication/replicationcomponent.cpp index cb81abf99..de1c1b4e0 100644 --- a/components/replication/replicationcomponent.cpp +++ b/components/replication/replicationcomponent.cpp @@ -21,7 +21,7 @@ using namespace icinga; -EXPORT_COMPONENT(replication, ReplicationComponent); +REGISTER_COMPONENT("replication", ReplicationComponent); /** * Starts the component. diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp index 124c7d68f..3445c762e 100644 --- a/icinga-app/icinga.cpp +++ b/icinga-app/icinga.cpp @@ -210,7 +210,7 @@ int main(int argc, char **argv) Component::AddSearchDir(Application::GetPkgLibDir()); - Utility::LoadIcingaLibrary("icinga", false); + (void) Utility::LoadIcingaLibrary("icinga", false); if (g_AppParams.count("library")) { BOOST_FOREACH(const String& libraryName, g_AppParams["library"].as >()) { diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index 60ffcb8cc..1f606540e 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -34,6 +34,8 @@ libbase_la_SOURCES = \ netstring.h \ object.cpp \ object.h \ + objectlock.cpp \ + objectlock.h \ process.cpp \ process-unix.cpp \ process-windows.cpp \ diff --git a/lib/base/application.cpp b/lib/base/application.cpp index 8cc0cc271..99453a992 100644 --- a/lib/base/application.cpp +++ b/lib/base/application.cpp @@ -120,8 +120,12 @@ void Application::ProfileTimerHandler(void) void Application::ShutdownTimerHandler(void) { - if (m_ShuttingDown) + if (m_ShuttingDown) { + Application::GetInstance()->OnShutdown(); + DynamicObject::DeactivateObjects(); GetEQ().Stop(); + m_ShuttingDown = false; + } } /** @@ -424,8 +428,6 @@ int Application::Run(void) result = Main(); - DynamicObject::DeactivateObjects(); - return result; } diff --git a/lib/base/application.h b/lib/base/application.h index bfeb4ef53..dfb27e766 100644 --- a/lib/base/application.h +++ b/lib/base/application.h @@ -84,6 +84,8 @@ public: protected: void RunEventLoop(void) const; + virtual void OnShutdown(void) = 0; + private: static Application *m_Instance; /**< The application instance. */ diff --git a/lib/base/asynctask.h b/lib/base/asynctask.h index 0c1e9115a..af2f4ca8a 100644 --- a/lib/base/asynctask.h +++ b/lib/base/asynctask.h @@ -66,7 +66,7 @@ public: void Start(const CompletionCallback& completionCallback = CompletionCallback()) { m_CompletionCallback = completionCallback; - Utility::QueueAsyncCallback(boost::bind(&AsyncTask::Run, this)); + Utility::QueueAsyncCallback(boost::bind(&AsyncTask::RunInternal, this)); } /** @@ -166,6 +166,18 @@ private: Utility::QueueAsyncCallback(boost::bind(callback, GetSelf())); } + /** + * Calls the Run() method and catches exceptions. + */ + void RunInternal(void) + { + try { + Run(); + } catch (const exception& ex) { + FinishException(boost::current_exception()); + } + } + mutable boost::mutex m_Mutex; boost::condition_variable m_CV; CompletionCallback m_CompletionCallback; /**< The completion callback. */ diff --git a/lib/base/component.cpp b/lib/base/component.cpp index 86074b7c7..bc503d67b 100644 --- a/lib/base/component.cpp +++ b/lib/base/component.cpp @@ -23,6 +23,8 @@ using namespace icinga; REGISTER_TYPE(Component, NULL); +map Component::m_Factories; + /** * Constructor for the component class. */ @@ -32,51 +34,20 @@ Component::Component(const Dictionary::Ptr& properties) if (!IsLocal()) BOOST_THROW_EXCEPTION(runtime_error("Component objects must be local.")); -#ifdef _WIN32 - HMODULE -#else /* _WIN32 */ - lt_dlhandle -#endif /* _WIN32 */ - hModule; - Logger::Write(LogInformation, "base", "Loading component '" + GetName() + "'"); - hModule = Utility::LoadIcingaLibrary(GetName(), true); - - CreateComponentFunction pCreateComponent; - -#ifdef _WIN32 - pCreateComponent = reinterpret_cast(GetProcAddress(hModule, - "CreateComponent")); -#else /* _WIN32 */ -# ifdef __GNUC__ - /* suppress compiler warning for void * cast */ - __extension__ -# endif - pCreateComponent = reinterpret_cast(lt_dlsym(hModule, - "CreateComponent")); -#endif /* _WIN32 */ + (void) Utility::LoadIcingaLibrary(GetName(), true); - IComponent::Ptr impl; + map::iterator it; + it = m_Factories.find(GetName()); - try { - if (pCreateComponent == NULL) - BOOST_THROW_EXCEPTION(runtime_error("Loadable module does not contain " - "CreateComponent function")); + if (it == m_Factories.end()) + BOOST_THROW_EXCEPTION(invalid_argument("Unknown component: " + GetName())); - /* pCreateComponent returns a raw pointer which we must wrap in a shared_ptr */ - impl = IComponent::Ptr(pCreateComponent()); + IComponent::Ptr impl = it->second(); - if (!impl) - BOOST_THROW_EXCEPTION(runtime_error("CreateComponent function returned NULL.")); - } catch (...) { -#ifdef _WIN32 - FreeLibrary(hModule); -#else /* _WIN32 */ - lt_dlclose(hModule); -#endif /* _WIN32 */ - throw; - } + if (!impl) + BOOST_THROW_EXCEPTION(runtime_error("Component factory returned NULL.")); m_Impl = impl; } @@ -142,3 +113,11 @@ void IComponent::Stop(void) { /* Nothing to do in the default implementation. */ } + +/** + * Registers a component factory. + */ +void Component::Register(const String& name, const Component::Factory& factory) +{ + m_Factories[name] = factory; +} diff --git a/lib/base/component.h b/lib/base/component.h index 377342ac3..003729da9 100644 --- a/lib/base/component.h +++ b/lib/base/component.h @@ -59,6 +59,8 @@ public: typedef shared_ptr Ptr; typedef weak_ptr WeakPtr; + typedef function Factory; + Component(const Dictionary::Ptr& properties); ~Component(void); @@ -66,30 +68,43 @@ public: static void AddSearchDir(const String& componentDirectory); + static void Register(const String& name, const Factory& factory); + private: IComponent::Ptr m_Impl; /**< The implementation object for this component. */ -}; -typedef IComponent *(*CreateComponentFunction)(void); - -#ifdef _WIN32 -# define SYM_CREATECOMPONENT(component) CreateComponent -#else /* _WIN32 */ -# define SYM_CREATECOMPONENT(component) component ## _LTX_CreateComponent -#endif /* _WIN32 */ + static map m_Factories; +}; /** - * Implements the loader function for a component. + * Helper class for registering Component implementation classes. * - * @param component The name of the component. - * @param klass The component class. + * @ingroup base */ -#define EXPORT_COMPONENT(component, klass) \ - extern "C" I2_EXPORT icinga::IComponent *SYM_CREATECOMPONENT(component)(void) \ - { \ - return new klass(); \ +class RegisterComponentHelper +{ +public: + RegisterComponentHelper(const String& name, const Component::Factory& factory) + { + Component::Register(name, factory); } +}; + +/** + * Factory function for IComponent-based classes. + * + * @ingroup base + */ +template +IComponent::Ptr ComponentFactory(void) +{ + return boost::make_shared(); +} + + +#define REGISTER_COMPONENT(name, klass) \ + static RegisterComponentHelper g_RegisterSF_ ## type(name, ComponentFactory) } diff --git a/lib/base/dictionary.cpp b/lib/base/dictionary.cpp index cd41e5ed3..6657959d2 100644 --- a/lib/base/dictionary.cpp +++ b/lib/base/dictionary.cpp @@ -54,6 +54,13 @@ struct DictionaryKeyLessComparer } }; +/** + * Constructor for the Dictionary class. + */ +Dictionary::Dictionary(void) + : m_Sealed(false) +{ } + /** * Restrieves a value from a dictionary. * @@ -98,6 +105,8 @@ void Dictionary::Set(const String& key, const Value& value) { ObjectLock olock(this); + assert(!m_Sealed); + if (value.IsEmpty()) { Remove(key); return; @@ -213,6 +222,15 @@ void Dictionary::Remove(Dictionary::Iterator it) m_Data.erase(it); } +/** + * Marks the dictionary as read-only. Attempting to modify a sealed + * dictionary is an error. + */ +void Dictionary::Seal(void) +{ + m_Sealed = true; +} + /** * Makes a shallow copy of a dictionary. * diff --git a/lib/base/dictionary.h b/lib/base/dictionary.h index fc5065eee..d2921df22 100644 --- a/lib/base/dictionary.h +++ b/lib/base/dictionary.h @@ -39,11 +39,14 @@ public: */ typedef map::iterator Iterator; + Dictionary(void); + Value Get(const char *key) const; Value Get(const String& key) const; void Set(const String& key, const Value& value); String Add(const Value& value); bool Contains(const String& key) const; + void Seal(void); Iterator Begin(void); Iterator End(void); @@ -60,6 +63,7 @@ public: private: map m_Data; /**< The data for the dictionary. */ + bool m_Sealed; /**< Whether the dictionary is read-only. */ }; inline Dictionary::Iterator range_begin(Dictionary::Ptr x) diff --git a/lib/base/dynamicobject.cpp b/lib/base/dynamicobject.cpp index bf4e714f7..3a985c7d6 100644 --- a/lib/base/dynamicobject.cpp +++ b/lib/base/dynamicobject.cpp @@ -122,6 +122,8 @@ Dictionary::Ptr DynamicObject::BuildUpdate(double sinceTx, int attributeTypes) c attrs->Set(it->first, attr); } + attrs->Seal(); + Dictionary::Ptr update = boost::make_shared(); update->Set("attrs", attrs); @@ -130,6 +132,8 @@ Dictionary::Ptr DynamicObject::BuildUpdate(double sinceTx, int attributeTypes) c else if (attrs->GetLength() == 0) return Dictionary::Ptr(); + update->Seal(); + return update; } @@ -243,8 +247,12 @@ void DynamicObject::InternalSetAttribute(const String& name, const Value& data, * object to the list of modified objects later on if we can't * do it here. */ - boost::mutex::scoped_lock lock(m_TransactionMutex); - m_ModifiedObjects.insert(GetSelf()); + DynamicObject::Ptr self = GetSelf(); + + { + boost::mutex::scoped_lock lock(m_TransactionMutex); + m_ModifiedObjects.insert(self); + } } /* Use insert() rather than [] so we don't overwrite @@ -496,7 +504,7 @@ void DynamicObject::RestoreObjects(const String& filename) stringstream msgbuf; msgbuf << "Restored " << restored << " objects"; - Logger::Write(LogDebug, "base", msgbuf.str()); + Logger::Write(LogInformation, "base", msgbuf.str()); } void DynamicObject::DeactivateObjects(void) diff --git a/lib/base/eventqueue.cpp b/lib/base/eventqueue.cpp index 55d8f7fce..8cb486464 100644 --- a/lib/base/eventqueue.cpp +++ b/lib/base/eventqueue.cpp @@ -81,7 +81,7 @@ void EventQueue::QueueThreadProc(void) while (m_Events.empty() && !m_Stopped) m_CV.wait(lock); - if (m_Stopped) + if (m_Events.empty() && m_Stopped) break; events.swap(m_Events); @@ -94,7 +94,7 @@ void EventQueue::QueueThreadProc(void) double et = Utility::GetTime(); - if (et - st > 1.0) { + if (et - st > 0.25) { stringstream msgbuf; msgbuf << "Event call took " << et - st << " seconds."; Logger::Write(LogWarning, "base", msgbuf.str()); @@ -118,7 +118,7 @@ void EventQueue::Post(const EventQueue::Callback& callback) int pending = m_Events.size(); double now = Utility::GetTime(); if (pending > 1000 && now - m_LastReport > 5) { - Logger::Write(LogWarning, "base", "More than 1000 pending events: " + Convert::ToString(pending)); + Logger::Write(LogCritical, "base", "More than 1000 pending events: " + Convert::ToString(pending)); m_LastReport = now; } } diff --git a/lib/base/i2-base.h b/lib/base/i2-base.h index 04e9684f9..ee80e4dfa 100644 --- a/lib/base/i2-base.h +++ b/lib/base/i2-base.h @@ -190,6 +190,7 @@ namespace signals2 = boost::signals2; #include "qstring.h" #include "utility.h" #include "object.h" +#include "objectlock.h" #include "exception.h" #include "eventqueue.h" #include "value.h" diff --git a/lib/base/object.h b/lib/base/object.h index ce39a2b40..923c20826 100644 --- a/lib/base/object.h +++ b/lib/base/object.h @@ -106,38 +106,6 @@ private: mutable recursive_mutex m_Mutex; }; -/** - * A scoped lock for Objects. - */ -struct ObjectLock { -public: - ObjectLock(void) - : m_Lock() - { } - - ObjectLock(const Object::Ptr& object) - : m_Lock() - { - if (object) - m_Lock = recursive_mutex::scoped_lock(object->GetMutex()); - } - - ObjectLock(const Object *object) - : m_Lock() - { - if (object) - m_Lock = recursive_mutex::scoped_lock(object->GetMutex()); - } - - void Unlock(void) - { - m_Lock = recursive_mutex::scoped_lock(); - } - -private: - recursive_mutex::scoped_lock m_Lock; -}; - /** * Compares a weak pointer with a raw pointer. * diff --git a/lib/base/process-unix.cpp b/lib/base/process-unix.cpp index 9e852a765..d7fdae546 100644 --- a/lib/base/process-unix.cpp +++ b/lib/base/process-unix.cpp @@ -24,6 +24,7 @@ using namespace icinga; int Process::m_TaskFd; +Timer::Ptr Process::m_StatusTimer; extern char **environ; void Process::Initialize(void) @@ -37,7 +38,7 @@ void Process::Initialize(void) if (pipe(fds) < 0) BOOST_THROW_EXCEPTION(PosixException("pipe() failed.", errno)); - /* Don't bother setting fds[1] to clo-exec as we'll only + /* Don't bother setting fds[0] to clo-exec as we'll only * use it in the following dup() call. */ Utility::SetCloExec(fds[1]); @@ -59,6 +60,11 @@ void Process::Initialize(void) } (void) close(fds[0]); + + m_StatusTimer = boost::make_shared(); + m_StatusTimer->OnTimerExpired.connect(boost::bind(&Process::StatusTimerHandler)); + m_StatusTimer->SetInterval(5); + m_StatusTimer->Start(); } void Process::WorkerThreadProc(int taskFd) @@ -314,4 +320,12 @@ bool Process::RunTask(void) return false; } +void Process::StatusTimerHandler(void) +{ + boost::mutex::scoped_lock lock(m_Mutex); + if (m_Tasks.size() > 50) + Logger::Write(LogCritical, "base", "More than 50 waiting Process tasks: " + + Convert::ToString(m_Tasks.size())); +} + #endif /* _WIN32 */ diff --git a/lib/base/process.cpp b/lib/base/process.cpp index 7e9a08a6f..3197dcda2 100644 --- a/lib/base/process.cpp +++ b/lib/base/process.cpp @@ -24,7 +24,6 @@ using namespace icinga; boost::once_flag Process::m_ThreadOnce = BOOST_ONCE_INIT; boost::mutex Process::m_Mutex; deque Process::m_Tasks; -double Process::m_LastReport = 0; Process::Process(const vector& arguments, const Dictionary::Ptr& extraEnvironment) : AsyncTask(), m_Arguments(arguments), m_ExtraEnvironment(extraEnvironment) @@ -66,18 +65,9 @@ vector Process::SplitCommand(const Value& command) void Process::Run(void) { - int count; - { boost::mutex::scoped_lock lock(m_Mutex); m_Tasks.push_back(GetSelf()); - count = m_Tasks.size(); - } - - if (count > 50 && Utility::GetTime() - m_LastReport > 5) { - Logger::Write(LogInformation, "base", "More than 50 pending Process tasks: " + - Convert::ToString(count)); - m_LastReport = Utility::GetTime(); } NotifyWorker(); diff --git a/lib/base/process.h b/lib/base/process.h index 018dc24ee..e141accff 100644 --- a/lib/base/process.h +++ b/lib/base/process.h @@ -69,10 +69,11 @@ private: virtual void Run(void); static boost::mutex m_Mutex; - static double m_LastReport; static deque m_Tasks; #ifndef _WIN32 static int m_TaskFd; + + static Timer::Ptr m_StatusTimer; #endif /* _WIN32 */ static void NotifyWorker(void); @@ -83,6 +84,8 @@ private: static void WorkerThreadProc(void); #else /* _WIN32 */ static void WorkerThreadProc(int taskFd); + + static void StatusTimerHandler(void); #endif /* _WIN32 */ void InitTask(void); diff --git a/lib/base/value.cpp b/lib/base/value.cpp index 60a852948..d4a2486ae 100644 --- a/lib/base/value.cpp +++ b/lib/base/value.cpp @@ -54,6 +54,38 @@ bool Value::IsObject(void) const return !IsEmpty() && (m_Value.type() == typeid(Object::Ptr)); } +Value::operator double(void) const +{ + if (m_Value.type() != typeid(double)) { + return boost::lexical_cast(m_Value); + } else { + return boost::get(m_Value); + } +} + +Value::operator String(void) const +{ + Object *object; + double integral, fractional; + + switch (GetType()) { + case ValueEmpty: + return String(); + case ValueNumber: + fractional = modf(boost::get(m_Value), &integral); + + if (fractional != 0) + return boost::lexical_cast(m_Value); + else + return boost::lexical_cast((long)integral); + case ValueString: + return boost::get(m_Value); + case ValueObject: + object = boost::get(m_Value).get(); + return "Object of type '" + Utility::GetTypeName(typeid(*object)) + "'"; + } +} + /** * Converts a JSON object into a variant. * diff --git a/lib/base/value.h b/lib/base/value.h index 45ece754f..db84fc2ff 100644 --- a/lib/base/value.h +++ b/lib/base/value.h @@ -85,27 +85,8 @@ public: m_Value = object; } - operator double(void) const - { - if (m_Value.type() != typeid(double)) { - return boost::lexical_cast(m_Value); - } else { - return boost::get(m_Value); - } - } - - operator String(void) const - { - if (IsEmpty()) - return String(); - - if (m_Value.type() != typeid(String)) { - String result = boost::lexical_cast(m_Value); - m_Value = result; - } - - return boost::get(m_Value); - } + operator double(void) const; + operator String(void) const; template operator shared_ptr(void) const diff --git a/lib/config/config_lexer.cc b/lib/config/config_lexer.cc index b00d99097..eab6bbbaa 100644 --- a/lib/config/config_lexer.cc +++ b/lib/config/config_lexer.cc @@ -386,7 +386,7 @@ static yyconst flex_int16_t yy_accept[193] = 54, 49, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 54, 18, 19, 12, 3, 2, 55, 15, 15, 21, 0, 0, 0, 0, 0, 42, 52, - 50, 48, 51, 16, 0, 53, 0, 45, 46, 47, + 50, 48, 51, 16, 20, 53, 0, 45, 46, 47, 0, 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 0, 18, 17, 12, 11, 4, 5, 9, 10, 6, 8, 7, 0, 0, @@ -793,6 +793,10 @@ int yyget_lineno (yyscan_t yyscanner ); void yyset_lineno (int line_number ,yyscan_t yyscanner ); +int yyget_column (yyscan_t yyscanner ); + +void yyset_column (int column_no ,yyscan_t yyscanner ); + YYSTYPE * yyget_lval (yyscan_t yyscanner ); void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); @@ -948,7 +952,7 @@ YY_DECL lex_buf string_buf; -#line 952 "config_lexer.cc" +#line 956 "config_lexer.cc" yylval = yylval_param; @@ -1377,7 +1381,7 @@ YY_RULE_SETUP #line 216 "config_lexer.ll" ECHO; YY_BREAK -#line 1381 "config_lexer.cc" +#line 1385 "config_lexer.cc" case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(C_COMMENT): case YY_STATE_EOF(STRING): diff --git a/lib/config/config_lexer.ll b/lib/config/config_lexer.ll index bc1fd1dcd..42666ecf9 100644 --- a/lib/config/config_lexer.ll +++ b/lib/config/config_lexer.ll @@ -173,7 +173,7 @@ static void lb_append_char(lex_buf *lb, char new_char) "*" /* ignore star */ } -\/\/[^\n]+ /* ignore C++-style comments */ +\/\/[^\n]* /* ignore C++-style comments */ [ \t\r\n]+ /* ignore whitespace */ { diff --git a/lib/icinga/host.cpp b/lib/icinga/host.cpp index 581794b18..095147b60 100644 --- a/lib/icinga/host.cpp +++ b/lib/icinga/host.cpp @@ -288,6 +288,8 @@ void Host::UpdateSlaveServices(void) } } + newServices->Seal(); + Set("slave_services", newServices); } @@ -479,23 +481,69 @@ set Host::GetParentServices(void) const return parents; } -Dictionary::Ptr Host::CalculateDynamicMacros(void) const +Dictionary::Ptr Host::CalculateDynamicMacros(const Host::Ptr& self) { Dictionary::Ptr macros = boost::make_shared(); - macros->Set("HOSTNAME", GetName()); - macros->Set("HOSTALIAS", GetName()); - macros->Set("HOSTDISPLAYNAME", GetDisplayName()); - macros->Set("HOSTSTATE", "DERP"); + 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; + + if (hc) { + ObjectLock olock(hc); + + String state; + int stateid; + + switch (hc->GetState()) { + case StateOK: + case StateWarning: + state = "UP"; + stateid = 0; + break; + default: + state = "DOWN"; + stateid = 1; + break; + } + + if (!reachable) { + state = "UNREACHABLE"; + stateid = 2; + } - Service::Ptr hostcheck = GetHostCheckService(); + macros->Set("HOSTSTATE", state); + macros->Set("HOSTSTATEID", stateid); + macros->Set("HOSTSTATETYPE", Service::StateTypeToString(hc->GetStateType())); + macros->Set("HOSTATTEMPT", hc->GetCurrentCheckAttempt()); + macros->Set("MAXHOSTATTEMPT", hc->GetMaxCheckAttempts()); - if (hostcheck) { - macros->Set("HOSTSTATEID", 99); - macros->Set("HOSTSTATETYPE", Service::StateTypeToString(hostcheck->GetStateType())); - macros->Set("HOSTATTEMPT", hostcheck->GetCurrentCheckAttempt()); - macros->Set("MAXHOSTATTEMPT", hostcheck->GetMaxCheckAttempts()); + cr = hc->GetLastCheckResult(); } + if (cr) { + macros->Set("HOSTLATENCY", Service::CalculateLatency(cr)); + macros->Set("HOSTEXECUTIONTIME", Service::CalculateExecutionTime(cr)); + + ObjectLock olock(cr); + + macros->Set("HOSTOUTPUT", cr->Get("output")); + macros->Set("HOSTPERFDATA", cr->Get("performance_data_raw")); + } + + macros->Seal(); + return macros; } diff --git a/lib/icinga/host.h b/lib/icinga/host.h index 244232f09..2308f1d23 100644 --- a/lib/icinga/host.h +++ b/lib/icinga/host.h @@ -50,7 +50,7 @@ public: Dictionary::Ptr GetServiceDependencies(void) const; String GetHostCheck(void) const; - Dictionary::Ptr CalculateDynamicMacros(void) const; + static Dictionary::Ptr CalculateDynamicMacros(const Host::Ptr& self); shared_ptr GetHostCheckService(void) const; set GetParentHosts(void) const; diff --git a/lib/icinga/icingaapplication.cpp b/lib/icinga/icingaapplication.cpp index 96eda887d..73c36e3eb 100644 --- a/lib/icinga/icingaapplication.cpp +++ b/lib/icinga/icingaapplication.cpp @@ -72,14 +72,20 @@ int IcingaApplication::Main(void) RunEventLoop(); - DumpProgramState(); - Logger::Write(LogInformation, "icinga", "Icinga has shut down."); return EXIT_SUCCESS; } -void IcingaApplication::DumpProgramState(void) { +void IcingaApplication::OnShutdown(void) +{ + m_RetentionTimer->Stop(); + + DumpProgramState(); +} + +void IcingaApplication::DumpProgramState(void) +{ DynamicObject::DumpObjects(GetStatePath()); } @@ -142,3 +148,12 @@ shared_ptr IcingaApplication::GetSSLContext(void) const { return m_SSLContext; } + +Dictionary::Ptr IcingaApplication::CalculateDynamicMacros(const IcingaApplication::Ptr& self) +{ + Dictionary::Ptr macros = boost::make_shared(); + + macros->Set("TIMET", (long)Utility::GetTime()); + + return macros; +} diff --git a/lib/icinga/icingaapplication.h b/lib/icinga/icingaapplication.h index 3bf7a8a07..fb294655b 100644 --- a/lib/icinga/icingaapplication.h +++ b/lib/icinga/icingaapplication.h @@ -51,6 +51,8 @@ public: double GetStartTime(void) const; + static Dictionary::Ptr CalculateDynamicMacros(const IcingaApplication::Ptr& self); + private: shared_ptr m_SSLContext; @@ -59,6 +61,8 @@ private: Timer::Ptr m_RetentionTimer; void DumpProgramState(void); + + virtual void OnShutdown(void); }; } diff --git a/lib/icinga/macroprocessor.cpp b/lib/icinga/macroprocessor.cpp index 431843ddc..803fb21b6 100644 --- a/lib/icinga/macroprocessor.cpp +++ b/lib/icinga/macroprocessor.cpp @@ -88,5 +88,7 @@ Dictionary::Ptr MacroProcessor::MergeMacroDicts(const vector& d } } + result->Seal(); + return result; } diff --git a/lib/icinga/notification.cpp b/lib/icinga/notification.cpp index 5d90a6d35..dd7d67e98 100644 --- a/lib/icinga/notification.cpp +++ b/lib/icinga/notification.cpp @@ -70,24 +70,94 @@ Dictionary::Ptr Notification::GetMacros(void) const return Get("macros"); } -void Notification::SendNotification(NotificationType type) +String Notification::NotificationTypeToString(NotificationType type) { + switch (type) { + case NotificationDowntimeStart: + return "DOWNTIMESTART"; + case NotificationDowntimeEnd: + return "DOWNTIMEEND"; + case NotificationDowntimeRemoved: + return "DOWNTIMECANCELLED"; + case NotificationCustom: + return "DOWNTIMECUSTOM"; + case NotificationProblem: + return "PROBLEM"; + case NotificationRecovery: + return "RECOVERY"; + default: + return "UNKNOWN_NOTIFICATION"; + } +} + +void Notification::BeginExecuteNotification(const Notification::Ptr& self, NotificationType type) +{ + + vector macroDicts; + + Dictionary::Ptr notificationMacros = boost::make_shared(); + notificationMacros->Set("NOTIFICATIONTYPE", NotificationTypeToString(type)); + macroDicts.push_back(notificationMacros); + + Service::Ptr service; + + { + ObjectLock olock(self); + macroDicts.push_back(self->GetMacros()); + service = self->GetService(); + } + + Host::Ptr host; + String service_name; + + { + ObjectLock olock(service); + macroDicts.push_back(service->GetMacros()); + service_name = service->GetName(); + host = service->GetHost(); + } + + macroDicts.push_back(Service::CalculateDynamicMacros(service)); + + { + ObjectLock olock(host); + macroDicts.push_back(host->GetMacros()); + macroDicts.push_back(Host::CalculateDynamicMacros(host)); + } + + IcingaApplication::Ptr app = IcingaApplication::GetInstance(); + + { + ObjectLock olock(app); + macroDicts.push_back(app->GetMacros()); + } + + macroDicts.push_back(IcingaApplication::CalculateDynamicMacros(app)); + + Dictionary::Ptr macros = MacroProcessor::MergeMacroDicts(macroDicts); + vector arguments; - arguments.push_back(static_cast(GetSelf())); + arguments.push_back(self); + arguments.push_back(macros); arguments.push_back(type); - ScriptTask::Ptr task = MakeMethodTask("notify", arguments); + ScriptTask::Ptr task; - if (!task) { - Logger::Write(LogWarning, "icinga", "Notification object '" + GetName() + "' doesn't have a 'notify' method."); + { + ObjectLock olock(self); + task = self->MakeMethodTask("notify", arguments); - return; - } + if (!task) { + Logger::Write(LogWarning, "icinga", "Notification object '" + self->GetName() + "' doesn't have a 'notify' method."); - /* We need to keep the task object alive until the completion handler is called. */ - m_Tasks.insert(task); + return; + } + + /* We need to keep the task object alive until the completion handler is called. */ + self->m_Tasks.insert(task); + } - task->Start(boost::bind(&Notification::NotificationCompletedHandler, this, _1)); + task->Start(boost::bind(&Notification::NotificationCompletedHandler, self, _1)); } void Notification::NotificationCompletedHandler(const ScriptTask::Ptr& task) diff --git a/lib/icinga/notification.h b/lib/icinga/notification.h index ef5e82dbf..4aa16a1a2 100644 --- a/lib/icinga/notification.h +++ b/lib/icinga/notification.h @@ -34,7 +34,8 @@ enum NotificationType NotificationDowntimeEnd, NotificationDowntimeRemoved, NotificationCustom, - NotificationStateChange + NotificationProblem, + NotificationRecovery }; class Service; @@ -60,7 +61,9 @@ public: Value GetNotificationCommand(void) const; Dictionary::Ptr GetMacros(void) const; - void SendNotification(NotificationType type); + static void BeginExecuteNotification(const Notification::Ptr& self, NotificationType type); + + static String NotificationTypeToString(NotificationType type); protected: void OnAttributeChanged(const String& name, const Value& oldValue); diff --git a/lib/icinga/pluginchecktask.cpp b/lib/icinga/pluginchecktask.cpp index c63560efb..6870ceb83 100644 --- a/lib/icinga/pluginchecktask.cpp +++ b/lib/icinga/pluginchecktask.cpp @@ -32,37 +32,19 @@ void PluginCheckTask::ScriptFunc(const ScriptTask::Ptr& task, const vector()) - BOOST_THROW_EXCEPTION(invalid_argument("Argument must be a service.")); + if (arguments.size() < 2) + BOOST_THROW_EXCEPTION(invalid_argument("Missing argument: Macros must be specified.")); + + Service::Ptr service = arguments[0]; + Dictionary::Ptr macros = arguments[1]; - vector macroDicts; Value raw_command; - Host::Ptr host; { - Service::Ptr service = vservice; ObjectLock olock(service); - macroDicts.push_back(service->GetMacros()); - macroDicts.push_back(service->CalculateDynamicMacros()); raw_command = service->GetCheckCommand(); - host = service->GetHost(); - } - - { - ObjectLock olock(host); - macroDicts.push_back(host->GetMacros()); - macroDicts.push_back(host->CalculateDynamicMacros()); } - { - IcingaApplication::Ptr app = IcingaApplication::GetInstance(); - ObjectLock olock(app); - macroDicts.push_back(app->GetMacros()); - } - - Dictionary::Ptr macros = MacroProcessor::MergeMacroDicts(macroDicts); - Value command = MacroProcessor::ResolveMacros(raw_command, macros); Process::Ptr process = boost::make_shared(Process::SplitCommand(command), macros); diff --git a/lib/icinga/pluginnotificationtask.cpp b/lib/icinga/pluginnotificationtask.cpp index c66671180..4c0a35862 100644 --- a/lib/icinga/pluginnotificationtask.cpp +++ b/lib/icinga/pluginnotificationtask.cpp @@ -37,49 +37,30 @@ void PluginNotificationTask::ScriptFunc(const ScriptTask::Ptr& task, const vecto BOOST_THROW_EXCEPTION(invalid_argument("Missing argument: Notification target must be specified.")); if (arguments.size() < 2) - BOOST_THROW_EXCEPTION(invalid_argument("Missing argument: Notification type must be specified.")); + BOOST_THROW_EXCEPTION(invalid_argument("Missing argument: Macros must be specified.")); - if (!arguments[0].IsObjectType()) - BOOST_THROW_EXCEPTION(invalid_argument("Argument must be a service.")); + if (arguments.size() < 3) + BOOST_THROW_EXCEPTION(invalid_argument("Missing argument: Notification type must be specified.")); - NotificationType type = static_cast(static_cast(arguments[1])); + Notification::Ptr notification = arguments[0]; + Dictionary::Ptr macros = arguments[1]; + NotificationType type = static_cast(static_cast(arguments[2])); - vector macroDicts; Value raw_command; - Service::Ptr service; - Host::Ptr host; String service_name; + Service::Ptr service; { - Notification::Ptr notification = arguments[0]; ObjectLock olock(notification); - macroDicts.push_back(notification->GetMacros()); raw_command = notification->GetNotificationCommand(); service = notification->GetService(); } { ObjectLock olock(service); - macroDicts.push_back(service->GetMacros()); - macroDicts.push_back(service->CalculateDynamicMacros()); service_name = service->GetName(); - host = service->GetHost(); } - { - ObjectLock olock(host); - macroDicts.push_back(host->GetMacros()); - macroDicts.push_back(host->CalculateDynamicMacros()); - } - - { - IcingaApplication::Ptr app = IcingaApplication::GetInstance(); - ObjectLock olock(app); - macroDicts.push_back(app->GetMacros()); - } - - Dictionary::Ptr macros = MacroProcessor::MergeMacroDicts(macroDicts); - Value command = MacroProcessor::ResolveMacros(raw_command, macros); Process::Ptr process = boost::make_shared(Process::SplitCommand(command), macros); diff --git a/lib/icinga/service-check.cpp b/lib/icinga/service-check.cpp index 3db70d3fd..2f2759e97 100644 --- a/lib/icinga/service-check.cpp +++ b/lib/icinga/service-check.cpp @@ -287,6 +287,7 @@ void Service::ApplyCheckResult(const Dictionary::Ptr& cr) ServiceState old_state = GetState(); ServiceStateType old_stateType = GetStateType(); bool hardChange = false; + bool recovery; long attempt = GetCurrentCheckAttempt(); @@ -298,6 +299,7 @@ void Service::ApplyCheckResult(const Dictionary::Ptr& cr) SetStateType(StateTypeHard); attempt = 1; + recovery = true; } else { if (attempt >= GetMaxCheckAttempts()) { SetStateType(StateTypeHard); @@ -307,6 +309,8 @@ void Service::ApplyCheckResult(const Dictionary::Ptr& cr) SetStateType(StateTypeSoft); attempt++; } + + recovery = false; } SetCurrentCheckAttempt(attempt); @@ -353,19 +357,19 @@ void Service::ApplyCheckResult(const Dictionary::Ptr& cr) Flush(); if (IsReachable(GetSelf()) && !IsInDowntime() && !IsAcknowledged()) - RequestNotifications(NotificationStateChange); + RequestNotifications(recovery ? NotificationRecovery : NotificationProblem); } } ServiceState Service::StateFromString(const String& state) { - if (state == "ok") + if (state == "OK") return StateOK; - else if (state == "warning") + else if (state == "WARNING") return StateWarning; - else if (state == "critical") + else if (state == "CRITICAL") return StateCritical; - else if (state == "uncheckable") + else if (state == "UNCHECKABLE") return StateUncheckable; else return StateUnknown; @@ -375,22 +379,22 @@ String Service::StateToString(ServiceState state) { switch (state) { case StateOK: - return "ok"; + return "OK"; case StateWarning: - return "warning"; + return "WARNING"; case StateCritical: - return "critical"; + return "CRITICAL"; case StateUncheckable: - return "uncheckable"; + return "UNCHECKABLE"; case StateUnknown: default: - return "unknown"; + return "UNKNOWN"; } } ServiceStateType Service::StateTypeFromString(const String& type) { - if (type == "soft") + if (type == "SOFT") return StateTypeSoft; else return StateTypeHard; @@ -399,9 +403,9 @@ ServiceStateType Service::StateTypeFromString(const String& type) String Service::StateTypeToString(ServiceStateType type) { if (type == StateTypeSoft) - return "soft"; + return "SOFT"; else - return "hard"; + return "HARD"; } bool Service::IsAllowedChecker(const String& checker) const @@ -420,10 +424,14 @@ bool Service::IsAllowedChecker(const String& checker) const return false; } -void Service::BeginExecuteCheck(const function& callback) +void Service::BeginExecuteCheck(const Service::Ptr& self, const function& callback) { + ObjectLock slock(self); + /* don't run another check if there is one pending */ - if (!Get("current_task").IsEmpty()) { + if (!self->Get("current_task").IsEmpty()) { + slock.Unlock(); + /* we need to call the callback anyway */ callback(); @@ -431,28 +439,59 @@ void Service::BeginExecuteCheck(const function& callback) } /* keep track of scheduling info in case the check type doesn't provide its own information */ - Dictionary::Ptr scheduleInfo = boost::make_shared(); - scheduleInfo->Set("schedule_start", GetNextCheck()); - scheduleInfo->Set("execution_start", Utility::GetTime()); + Dictionary::Ptr checkInfo = boost::make_shared(); + checkInfo->Set("schedule_start", self->GetNextCheck()); + checkInfo->Set("execution_start", Utility::GetTime()); + + vector macroDicts; + macroDicts.push_back(self->GetMacros()); + macroDicts.push_back(Service::CalculateDynamicMacros(self)); + + Value raw_command = self->GetCheckCommand(); + + Host::Ptr host = self->GetHost(); + + slock.Unlock(); + + { + ObjectLock olock(host); + macroDicts.push_back(host->GetMacros()); + macroDicts.push_back(Host::CalculateDynamicMacros(host)); + } + + IcingaApplication::Ptr app = IcingaApplication::GetInstance(); + + { + ObjectLock olock(app); + macroDicts.push_back(app->GetMacros()); + } + + macroDicts.push_back(IcingaApplication::CalculateDynamicMacros(app)); + + Dictionary::Ptr macros = MacroProcessor::MergeMacroDicts(macroDicts); + + checkInfo->Set("macros", macros); vector arguments; - arguments.push_back(static_cast(GetSelf())); + arguments.push_back(self); + arguments.push_back(macros); + + ScriptTask::Ptr task; - ScriptTask::Ptr task = MakeMethodTask("check", arguments); - Set("current_task", task); + { + ObjectLock olock(self); + task = self->MakeMethodTask("check", arguments); + self->Set("current_task", task); + } - task->Start(boost::bind(&Service::CheckCompletedHandler, this, scheduleInfo, _1, callback)); + task->Start(boost::bind(&Service::CheckCompletedHandler, self, checkInfo, _1, callback)); } -void Service::CheckCompletedHandler(const Dictionary::Ptr& scheduleInfo, +void Service::CheckCompletedHandler(const Dictionary::Ptr& checkInfo, const ScriptTask::Ptr& task, const function& callback) { - ObjectLock olock(this); - - Set("current_task", Empty); - - scheduleInfo->Set("execution_end", Utility::GetTime()); - scheduleInfo->Set("schedule_end", Utility::GetTime()); + checkInfo->Set("execution_end", Utility::GetTime()); + checkInfo->Set("schedule_end", Utility::GetTime()); Dictionary::Ptr result; @@ -481,32 +520,43 @@ void Service::CheckCompletedHandler(const Dictionary::Ptr& scheduleInfo, if (result) { if (!result->Contains("schedule_start")) - result->Set("schedule_start", scheduleInfo->Get("schedule_start")); + result->Set("schedule_start", checkInfo->Get("schedule_start")); if (!result->Contains("schedule_end")) - result->Set("schedule_end", scheduleInfo->Get("schedule_end")); + result->Set("schedule_end", checkInfo->Get("schedule_end")); if (!result->Contains("execution_start")) - result->Set("execution_start", scheduleInfo->Get("execution_start")); + result->Set("execution_start", checkInfo->Get("execution_start")); if (!result->Contains("execution_end")) - result->Set("execution_end", scheduleInfo->Get("execution_end")); + result->Set("execution_end", checkInfo->Get("execution_end")); + + if (!result->Contains("macros")) + result->Set("macros", checkInfo->Get("macros")); if (!result->Contains("active")) result->Set("active", 1); - if (!result->Contains("checker")) - result->Set("checker", EndpointManager::GetInstance()->GetIdentity()); + if (!result->Contains("checker")) { + EndpointManager::Ptr em = EndpointManager::GetInstance(); + ObjectLock olock(em); - ProcessCheckResult(result); + result->Set("checker", em->GetIdentity()); + } } - /* figure out when the next check is for this service; the call to - * ApplyCheckResult() should've already done this but lets do it again - * just in case there was no check result. */ - UpdateNextCheck(); + { + ObjectLock olock(this); + if (result) + ProcessCheckResult(result); - olock.Unlock(); + Set("current_task", Empty); + + /* figure out when the next check is for this service; the call to + * ApplyCheckResult() should've already done this but lets do it again + * just in case there was no check result. */ + UpdateNextCheck(); + } callback(); } @@ -551,3 +601,40 @@ void Service::UpdateStatistics(const Dictionary::Ptr& cr) else CIB::UpdatePassiveChecksStatistics(ts, 1); } + +double Service::CalculateExecutionTime(const Dictionary::Ptr& cr) +{ + ObjectLock olock(cr); + + double execution_start = 0, execution_end = 0; + + if (cr) { + ObjectLock olock(cr); + + if (!cr->Contains("execution_start") || !cr->Contains("execution_end")) + return 0; + + execution_start = cr->Get("execution_start"); + execution_end = cr->Get("execution_end"); + } + + return (execution_end - execution_start); +} + +double Service::CalculateLatency(const Dictionary::Ptr& cr) +{ + double schedule_start = 0, schedule_end = 0; + + if (cr) { + ObjectLock olock(cr); + + if (!cr->Contains("schedule_start") || !cr->Contains("schedule_end")) + return 0; + + schedule_start = cr->Get("schedule_start"); + schedule_end = cr->Get("schedule_end"); + } + + return (schedule_end - schedule_start) - CalculateExecutionTime(cr); + +} diff --git a/lib/icinga/service-notification.cpp b/lib/icinga/service-notification.cpp index 6cc305506..36ade2b66 100644 --- a/lib/icinga/service-notification.cpp +++ b/lib/icinga/service-notification.cpp @@ -49,7 +49,7 @@ void Service::SendNotifications(NotificationType type) Logger::Write(LogInformation, "icinga", "Service '" + GetName() + "' does not have any notifications."); BOOST_FOREACH(const Notification::Ptr& notification, notifications) { - notification->SendNotification(type); + Notification::BeginExecuteNotification(notification, type); } SetLastNotification(Utility::GetTime()); diff --git a/lib/icinga/service.cpp b/lib/icinga/service.cpp index 355df9899..291e79517 100644 --- a/lib/icinga/service.cpp +++ b/lib/icinga/service.cpp @@ -76,10 +76,10 @@ String Service::GetDisplayName(void) const { String value = Get("display_name"); - if (!value.IsEmpty()) - return value; + if (value.IsEmpty()) + return GetShortName(); - return GetName(); + return value; } /** @@ -330,27 +330,36 @@ set Service::GetParentServices(void) const return parents; } -Dictionary::Ptr Service::CalculateDynamicMacros(void) const +Dictionary::Ptr Service::CalculateDynamicMacros(const Service::Ptr& self) { Dictionary::Ptr macros = boost::make_shared(); - macros->Set("SERVICEDESC", GetShortName()); - macros->Set("SERVICEDISPLAYNAME", GetDisplayName()); - macros->Set("SERVICESTATE", StateToString(GetState())); - macros->Set("SERVICESTATEID", GetState()); - macros->Set("SERVICESTATETYPE", StateTypeToString(GetStateType())); - macros->Set("SERVICEATTEMPT", GetCurrentCheckAttempt()); - macros->Set("MAXSERVICEATTEMPT", GetMaxCheckAttempts()); + Dictionary::Ptr cr; - Dictionary::Ptr cr = GetLastCheckResult(); + { + ObjectLock olock(self); + macros->Set("SERVICEDESC", self->GetShortName()); + macros->Set("SERVICEDISPLAYNAME", self->GetDisplayName()); + macros->Set("SERVICESTATE", StateToString(self->GetState())); + macros->Set("SERVICESTATEID", self->GetState()); + macros->Set("SERVICESTATETYPE", StateTypeToString(self->GetStateType())); + macros->Set("SERVICEATTEMPT", self->GetCurrentCheckAttempt()); + macros->Set("MAXSERVICEATTEMPT", self->GetMaxCheckAttempts()); + + cr = self->GetLastCheckResult(); + } if (cr) { + macros->Set("SERVICELATENCY", Service::CalculateLatency(cr)); + macros->Set("SERVICEEXECUTIONTIME", Service::CalculateExecutionTime(cr)); + + ObjectLock olock(cr); + macros->Set("SERVICEOUTPUT", cr->Get("output")); macros->Set("SERVICEPERFDATA", cr->Get("performance_data_raw")); - } else { - macros->Set("SERVICEOUTPUT", ""); - macros->Set("SERVICEPERFDATA", ""); } + macros->Seal(); + return macros; } diff --git a/lib/icinga/service.h b/lib/icinga/service.h index 22da15818..7dba9058c 100644 --- a/lib/icinga/service.h +++ b/lib/icinga/service.h @@ -106,7 +106,7 @@ public: Dictionary::Ptr GetGroups(void) const; String GetShortName(void) const; - Dictionary::Ptr CalculateDynamicMacros(void) const; + static Dictionary::Ptr CalculateDynamicMacros(const Service::Ptr& self); set GetParentHosts(void) const; set GetParentServices(void) const; @@ -171,9 +171,12 @@ public: void ApplyCheckResult(const Dictionary::Ptr& cr); static void UpdateStatistics(const Dictionary::Ptr& cr); - void BeginExecuteCheck(const function& callback); + static void BeginExecuteCheck(const Service::Ptr& self, const function& callback); void ProcessCheckResult(const Dictionary::Ptr& cr); + static double CalculateExecutionTime(const Dictionary::Ptr& cr); + static double CalculateLatency(const Dictionary::Ptr& cr); + static ServiceState StateFromString(const String& state); static String StateToString(ServiceState state); @@ -252,7 +255,7 @@ protected: virtual void OnAttributeChanged(const String& name, const Value& oldValue); private: - void CheckCompletedHandler(const Dictionary::Ptr& scheduleInfo, + void CheckCompletedHandler(const Dictionary::Ptr& checkInfo, const ScriptTask::Ptr& task, const function& callback); /* Downtimes */ diff --git a/lib/remoting/endpointmanager.cpp b/lib/remoting/endpointmanager.cpp index 16cc00492..6ae387791 100644 --- a/lib/remoting/endpointmanager.cpp +++ b/lib/remoting/endpointmanager.cpp @@ -341,6 +341,8 @@ void EndpointManager::SubscriptionTimerHandler(void) } } + subscriptions->Seal(); + if (m_Endpoint) m_Endpoint->SetSubscriptions(subscriptions); }