From: Gunnar Beutner Date: Tue, 19 Mar 2013 12:04:30 +0000 (+0100) Subject: Implement the CompatLog type. X-Git-Tag: v0.0.2~211 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a21cf21d5269c987911ac982307b21a133d3c871;p=icinga2 Implement the CompatLog type. --- diff --git a/Makefile.am b/Makefile.am index ca8dc863c..8f692d116 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,7 +26,7 @@ icinga2doc_DATA = \ NEWS install-data-local: - $(MKDIR_P) ${localstatedir}/log/${PACKAGE} + $(MKDIR_P) ${localstatedir}/log/${PACKAGE}/compat/archives $(MKDIR_P) ${localstatedir}/cache/${PACKAGE} $(MKDIR_P) ${localstatedir}/lib/${PACKAGE} $(MKDIR_P) ${localstatedir}/run/${PACKAGE} diff --git a/components/compat/Makefile.am b/components/compat/Makefile.am index 712938ff4..70638ce99 100644 --- a/components/compat/Makefile.am +++ b/components/compat/Makefile.am @@ -12,6 +12,8 @@ EXTRA_DIST = \ libcompat_la_SOURCES = \ compatcomponent.cpp \ compatcomponent.h \ + compatlog.cpp \ + compatlog.h \ compat-type.cpp libcompat_la_CPPFLAGS = \ diff --git a/components/compat/compat-type.conf b/components/compat/compat-type.conf index 56e1fd8cc..cbffdac5d 100644 --- a/components/compat/compat-type.conf +++ b/components/compat/compat-type.conf @@ -20,6 +20,10 @@ type CompatComponent { %attribute string "status_path", %attribute string "objects_path", - %attribute string "log_path", %attribute string "command_path" } + +type CompatLog { + %attribute string "path_prefix", + %attribute number "rotation_interval" +} diff --git a/components/compat/compatcomponent.cpp b/components/compat/compatcomponent.cpp index 368ae4c97..87fcdeeee 100644 --- a/components/compat/compatcomponent.cpp +++ b/components/compat/compatcomponent.cpp @@ -89,20 +89,6 @@ String CompatComponent::GetObjectsPath(void) const return objectsPath; } -/** - * Retrieves the log path. - * - * @returns log path - */ -String CompatComponent::GetLogPath(void) const -{ - Value logPath = m_LogPath; - if (logPath.IsEmpty()) - return Application::GetLocalStateDir() + "/log/icinga2/compat"; - else - return logPath; -} - /** * Retrieves the icinga.cmd path. * @@ -122,7 +108,6 @@ CompatComponent::CompatComponent(const Dictionary::Ptr& serializedUpdate) { RegisterAttribute("status_path", Attribute_Config, &m_StatusPath); RegisterAttribute("objects_path", Attribute_Config, &m_ObjectsPath); - RegisterAttribute("log_path", Attribute_Config, &m_LogPath); RegisterAttribute("command_path", Attribute_Config, &m_CommandPath); } diff --git a/components/compat/compatcomponent.h b/components/compat/compatcomponent.h index d90bfe948..f8232b12d 100644 --- a/components/compat/compatcomponent.h +++ b/components/compat/compatcomponent.h @@ -50,7 +50,6 @@ public: private: Attribute m_StatusPath; Attribute m_ObjectsPath; - Attribute m_LogPath; Attribute m_CommandPath; #ifndef _WIN32 @@ -63,7 +62,6 @@ private: String GetStatusPath(void) const; String GetObjectsPath(void) const; - String GetLogPath(void) const; String GetCommandPath(void) const; void DumpDowntimes(std::ostream& fp, const Service::Ptr& owner, CompatObjectType type); diff --git a/components/compat/compatlog.cpp b/components/compat/compatlog.cpp new file mode 100644 index 000000000..01081626d --- /dev/null +++ b/components/compat/compatlog.cpp @@ -0,0 +1,274 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "compat/compatlog.h" +#include "icinga/checkresultmessage.h" +#include "icinga/service.h" +#include "icinga/macroprocessor.h" +#include "base/dynamictype.h" +#include "base/objectlock.h" +#include "base/logger_fwd.h" +#include "base/convert.h" +#include "base/application.h" +#include +#include + +using namespace icinga; + +REGISTER_TYPE(CompatLog); + +CompatLog::CompatLog(const Dictionary::Ptr& properties) + : DynamicObject(properties) +{ + RegisterAttribute("log_dir", Attribute_Config, &m_LogDir); + RegisterAttribute("rotation_interval", Attribute_Config, &m_RotationInterval); +} + +CompatLog::~CompatLog(void) +{ +} + +/** + * @threadsafety Always. + */ +void CompatLog::OnAttributeChanged(const String& name) +{ + ASSERT(!OwnsLock()); + + if (name == "rotation_interval") { + m_RotationTimer->SetInterval(GetRotationInterval()); + } +} + +/** + * @threadsafety Always. + */ +void CompatLog::Start(void) +{ + m_Endpoint = Endpoint::MakeEndpoint("compatlog_" + GetName(), false); + m_Endpoint->RegisterTopicHandler("checker::CheckResult", + boost::bind(&CompatLog::CheckResultRequestHandler, this, _3)); + + m_RotationTimer = boost::make_shared(); + m_RotationTimer->OnTimerExpired.connect(boost::bind(&CompatLog::RotationTimerHandler, this)); + m_RotationTimer->SetInterval(GetRotationInterval()); + m_RotationTimer->Start(); + + RotateFile(); +} + +/** + * @threadsafety Always. + */ +CompatLog::Ptr CompatLog::GetByName(const String& name) +{ + DynamicObject::Ptr configObject = DynamicObject::GetObject("CompatLog", name); + + return dynamic_pointer_cast(configObject); +} + +/** + * @threadsafety Always. + */ +String CompatLog::GetLogDir(void) const +{ + if (!m_LogDir.IsEmpty()) + return m_LogDir; + else + return Application::GetLocalStateDir() + "/log/icinga2/compat/"; +} + +/** + * @threadsafety Always. + */ +double CompatLog::GetRotationInterval(void) const +{ + if (!m_RotationInterval.IsEmpty()) + return m_RotationInterval; + else + return 3600; +} + +/** + * @threadsafety Always. + */ +void CompatLog::CheckResultRequestHandler(const RequestMessage& request) +{ + CheckResultMessage params; + if (!request.GetParams(¶ms)) + return; + + String svcname = params.GetService(); + Service::Ptr service = Service::GetByName(svcname); + + Host::Ptr host = service->GetHost(); + + if (!host) + return; + + Dictionary::Ptr cr = params.GetCheckResult(); + if (!cr) + return; + + Dictionary::Ptr vars_after = cr->Get("vars_after"); + + long state_after = vars_after->Get("state"); + long stateType_after = vars_after->Get("state_type"); + long attempt_after = vars_after->Get("attempt"); + bool reachable_after = vars_after->Get("reachable"); + bool host_reachable_after = vars_after->Get("host_reachable"); + + Dictionary::Ptr vars_before = cr->Get("vars_before"); + + if (vars_before) { + long state_before = vars_before->Get("state"); + long stateType_before = vars_before->Get("state_type"); + long attempt_before = vars_before->Get("attempt"); + bool reachable_before = vars_before->Get("reachable"); + + if (state_before == state_after && stateType_before == stateType_after && + attempt_before == attempt_after && reachable_before == reachable_after) + return; /* Nothing changed, ignore this checkresult. */ + } + + std::ostringstream msgbuf; + msgbuf << "SERVICE ALERT: " + << host->GetName() << ";" + << service->GetShortName() << ";" + << Service::StateToString(static_cast(state_after)) << ";" + << Service::StateTypeToString(static_cast(stateType_after)) << ";" + << attempt_after << ";" + << ""; + + WriteLine(msgbuf.str()); + + if (service == host->GetHostCheckService()) { + std::ostringstream msgbuf; + msgbuf << "HOST ALERT: " + << host->GetName() << ";" + << Host::StateToString(Host::CalculateState(static_cast(state_after), host_reachable_after)) << ";" + << Service::StateTypeToString(static_cast(stateType_after)) << ";" + << attempt_after << ";" + << ""; + + WriteLine(msgbuf.str()); + } + + Flush(); +} + +void CompatLog::WriteLine(const String& line) +{ + ASSERT(OwnsLock()); + + if (!m_OutputFile.good()) + return; + + m_OutputFile << "[" << (long)Utility::GetTime() << "] " << line << "\n"; +} + +void CompatLog::Flush(void) +{ + ASSERT(OwnsLock()); + + if (!m_OutputFile.good()) + return; + + m_OutputFile << std::flush; +} + +/** + * @threadsafety Always. + */ +void CompatLog::RotateFile(void) +{ + ObjectLock olock(this); + + String tempFile = GetLogDir() + "/icinga.log"; + + if (m_OutputFile.good()) { + m_OutputFile.close(); + + String finalFile = GetLogDir() + "/archives/icinga-" + Convert::ToString((long)Utility::GetTime()) + ".log"; + (void) rename(tempFile.CStr(), finalFile.CStr()); + } + + m_OutputFile.open(tempFile.CStr()); + + if (!m_OutputFile.good()) { + Log(LogWarning, "icinga", "Could not open compat log file '" + tempFile + "' for writing. Log output will be lost."); + + return; + } + + WriteLine("LOG VERSION: 2.0"); + + BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Host")) { + Host::Ptr host = static_pointer_cast(object); + + Service::Ptr hc = host->GetHostCheckService(); + + if (!hc) + continue; + + bool reachable = host->IsReachable(); + + ObjectLock olock(hc); + + std::ostringstream msgbuf; + msgbuf << "HOST STATE: CURRENT;" + << host->GetName() << ";" + << Host::StateToString(Host::CalculateState(hc->GetState(), reachable)) << ";" + << Service::StateTypeToString(hc->GetStateType()) << ";" + << hc->GetCurrentCheckAttempt() << ";" + << ""; + + WriteLine(msgbuf.str()); + } + + BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Service")) { + Service::Ptr service = static_pointer_cast(object); + + Host::Ptr host = service->GetHost(); + + if (!host) + continue; + + std::ostringstream msgbuf; + msgbuf << "SERVICE STATE: CURRENT;" + << host->GetName() << ";" + << service->GetShortName() << ";" + << Service::StateToString(service->GetState()) << ";" + << Service::StateTypeToString(service->GetStateType()) << ";" + << service->GetCurrentCheckAttempt() << ";" + << ""; + + WriteLine(msgbuf.str()); + } + + Flush(); +} + +/** + * @threadsafety Always. + */ +void CompatLog::RotationTimerHandler(void) +{ + RotateFile(); +} diff --git a/components/compat/compatlog.h b/components/compat/compatlog.h new file mode 100644 index 000000000..db2310f4d --- /dev/null +++ b/components/compat/compatlog.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software Foundation * + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef COMPATLOG_H +#define COMPATLOG_H + +#include "icinga/i2-icinga.h" +#include "remoting/endpoint.h" +#include "base/dynamicobject.h" +#include "base/timer.h" +#include + +namespace icinga +{ + +/** + * An Icinga compat log writer. + * + * @ingroup compat + */ +class I2_ICINGA_API CompatLog : public DynamicObject +{ +public: + typedef shared_ptr Ptr; + typedef weak_ptr WeakPtr; + + CompatLog(const Dictionary::Ptr& properties); + ~CompatLog(void); + + static CompatLog::Ptr GetByName(const String& name); + + String GetLogDir(void) const; + double GetRotationInterval(void) const; + +protected: + virtual void OnAttributeChanged(const String& name); + virtual void Start(void); + +private: + Attribute m_LogDir; + Attribute m_RotationInterval; + + void WriteLine(const String& line); + void Flush(void); + + Endpoint::Ptr m_Endpoint; + void CheckResultRequestHandler(const RequestMessage& request); + + Timer::Ptr m_RotationTimer; + void RotationTimerHandler(void); + + std::ofstream m_OutputFile; + void RotateFile(void); +}; + +} + +#endif /* COMPATLOG_H */ diff --git a/lib/icinga/host.cpp b/lib/icinga/host.cpp index 04abac980..5ad346036 100644 --- a/lib/icinga/host.cpp +++ b/lib/icinga/host.cpp @@ -516,17 +516,12 @@ std::set Host::GetParentServices(void) const return parents; } -HostState Host::GetState(void) const +HostState Host::CalculateState(ServiceState state, bool reachable) { - if (!IsReachable()) + if (!reachable) return HostUnreachable; - Service::Ptr hc = GetHostCheckService(); - - if (!hc) - return HostUp; - - switch (hc->GetState()) { + switch (state) { case StateOK: case StateWarning: return HostUp; @@ -535,16 +530,6 @@ HostState Host::GetState(void) const } } -StateType Host::GetStateType(void) const -{ - Service::Ptr hc = GetHostCheckService(); - - if (!hc) - return StateTypeHard; - - return hc->GetStateType(); -} - HostState Host::GetLastState(void) const { ASSERT(!OwnsLock()); @@ -576,7 +561,7 @@ StateType Host::GetLastStateType(void) const return hc->GetLastStateType(); } -String Host::HostStateToString(HostState state) +String Host::StateToString(HostState state) { switch (state) { case HostUp: @@ -611,13 +596,16 @@ Dictionary::Ptr Host::CalculateDynamicMacros(void) const if (hc) { ObjectLock olock(hc); - macros->Set("HOSTSTATE", HostStateToString(GetState())); - macros->Set("HOSTSTATEID", GetState()); + ServiceState state = hc->GetState(); + bool reachable = IsReachable(); + + macros->Set("HOSTSTATE", CalculateState(state, reachable)); + macros->Set("HOSTSTATEID", state); macros->Set("HOSTSTATETYPE", Service::StateTypeToString(hc->GetStateType())); macros->Set("HOSTATTEMPT", hc->GetCurrentCheckAttempt()); macros->Set("MAXHOSTATTEMPT", hc->GetMaxCheckAttempts()); - macros->Set("LASTHOSTSTATE", HostStateToString(GetLastState())); + macros->Set("LASTHOSTSTATE", StateToString(GetLastState())); macros->Set("LASTHOSTSTATEID", GetLastState()); macros->Set("LASTHOSTSTATETYPE", Service::StateTypeToString(GetLastStateType())); macros->Set("LASTHOSTSTATECHANGE", (long)hc->GetLastStateChange()); diff --git a/lib/icinga/host.h b/lib/icinga/host.h index adcb29c6d..98c527169 100644 --- a/lib/icinga/host.h +++ b/lib/icinga/host.h @@ -42,6 +42,20 @@ enum HostState HostUnreachable = 2 }; +/** + * The state of a service. + * + * @ingroup icinga + */ +enum ServiceState +{ + StateOK = 0, + StateWarning = 1, + StateCritical = 2, + StateUnknown = 3, + StateUncheckable = 4, +}; + /** * The state type of a host or service. * @@ -93,13 +107,12 @@ public: static void ValidateServiceDictionary(const ScriptTask::Ptr& task, const std::vector& arguments); - HostState GetState(void) const; - StateType GetStateType(void) const; + static HostState CalculateState(ServiceState state, bool reachable); HostState GetLastState(void) const; StateType GetLastStateType(void) const; - static String HostStateToString(HostState state); + static String StateToString(HostState state); protected: virtual void OnRegistrationCompleted(void); diff --git a/lib/icinga/service-check.cpp b/lib/icinga/service-check.cpp index 44d8542c1..76c02bb77 100644 --- a/lib/icinga/service-check.cpp +++ b/lib/icinga/service-check.cpp @@ -267,6 +267,26 @@ StateType Service::GetLastStateType(void) const return static_cast(ivalue); } +/** + * @threadsafety Always. + */ +void Service::SetLastReachable(bool reachable) +{ + m_LastReachable = reachable; + Touch("last_reachable"); +} + +/** + * @threadsafety Always. + */ +bool Service::GetLastReachable(void) const +{ + if (m_LastReachable.IsEmpty()) + return true; + + return m_LastReachable; +} + /** * @threadsafety Always. */ @@ -391,11 +411,19 @@ void Service::ProcessCheckResult(const Dictionary::Ptr& cr) { bool reachable = IsReachable(); + Host::Ptr host = GetHost(); + bool host_reachable = true; + + if (host) + host_reachable = host->IsReachable(); + ASSERT(!OwnsLock()); ObjectLock olock(this); + Dictionary::Ptr old_cr = GetLastCheckResult(); ServiceState old_state = GetState(); StateType old_stateType = GetStateType(); + long old_attempt = GetCurrentCheckAttempt(); bool hardChange = false; bool recovery; @@ -403,8 +431,9 @@ void Service::ProcessCheckResult(const Dictionary::Ptr& cr) * in case this was a passive check result. */ SetLastState(old_state); SetLastStateType(old_stateType); + SetLastReachable(reachable); - long attempt = GetCurrentCheckAttempt(); + long attempt; if (cr->Get("state") == StateOK) { if (old_state != StateOK && old_stateType == StateTypeHard) @@ -419,13 +448,15 @@ void Service::ProcessCheckResult(const Dictionary::Ptr& cr) attempt = 1; recovery = true; } else { - if (attempt >= GetMaxCheckAttempts()) { + if (old_attempt >= GetMaxCheckAttempts()) { SetStateType(StateTypeHard); attempt = 1; hardChange = true; } else if (GetStateType() == StateTypeSoft || GetState() == StateOK) { SetStateType(StateTypeSoft); - attempt++; + attempt = old_attempt + 1; + } else { + attempt = old_attempt; } recovery = false; @@ -482,6 +513,18 @@ void Service::ProcessCheckResult(const Dictionary::Ptr& cr) olock.Unlock(); + Dictionary::Ptr vars_after = boost::make_shared(); + vars_after->Set("state", GetState()); + vars_after->Set("state_type", GetStateType()); + vars_after->Set("attempt", GetCurrentCheckAttempt()); + vars_after->Set("reachable", reachable); + vars_after->Set("host_reachable", host_reachable); + + if (old_cr) + cr->Set("vars_before", old_cr->Get("vars_after")); + + cr->Set("vars_after", vars_after); + /* Update macros - these are used by event handlers and notifications. */ cr->Set("macros", CalculateAllMacros(cr)); @@ -619,6 +662,8 @@ void Service::BeginExecuteCheck(const boost::function& callback) SetLastStateType(GetLastStateType()); } + SetLastReachable(IsReachable()); + /* keep track of scheduling info in case the check type doesn't provide its own information */ Dictionary::Ptr checkInfo = boost::make_shared(); checkInfo->Set("schedule_start", GetNextCheck()); diff --git a/lib/icinga/service.cpp b/lib/icinga/service.cpp index 6314030a2..1b14e8860 100644 --- a/lib/icinga/service.cpp +++ b/lib/icinga/service.cpp @@ -55,6 +55,7 @@ Service::Service(const Dictionary::Ptr& serializedObject) RegisterAttribute("state_type", Attribute_Replicated, &m_StateType); RegisterAttribute("last_state", Attribute_Replicated, &m_LastState); RegisterAttribute("last_state_type", Attribute_Replicated, &m_LastStateType); + RegisterAttribute("last_reachable", Attribute_Replicated, &m_LastReachable); RegisterAttribute("last_result", Attribute_Replicated, &m_LastResult); RegisterAttribute("last_state_change", Attribute_Replicated, &m_LastStateChange); RegisterAttribute("last_hard_state_change", Attribute_Replicated, &m_LastHardStateChange); diff --git a/lib/icinga/service.h b/lib/icinga/service.h index b6052c43c..b80108173 100644 --- a/lib/icinga/service.h +++ b/lib/icinga/service.h @@ -31,20 +31,6 @@ namespace icinga { -/** - * The state of a service. - * - * @ingroup icinga - */ -enum ServiceState -{ - StateOK, - StateWarning, - StateCritical, - StateUnknown, - StateUncheckable, -}; - /** * The acknowledgement type of a service. * @@ -158,6 +144,9 @@ public: void SetLastHardStateChange(double ts); double GetLastHardStateChange(void) const; + void SetLastReachable(bool reachable); + bool GetLastReachable(void) const; + bool GetEnableActiveChecks(void) const; void SetEnableActiveChecks(bool enabled); @@ -284,6 +273,7 @@ private: Attribute m_StateType; Attribute m_LastState; Attribute m_LastStateType; + Attribute m_LastReachable; Attribute m_LastResult; Attribute m_LastStateChange; Attribute m_LastHardStateChange;