diff --git a/base/configobject.cpp b/base/configobject.cpp index 163b1e210..fd5efcf7d 100644 --- a/base/configobject.cpp +++ b/base/configobject.cpp @@ -21,12 +21,14 @@ using namespace icinga; -ConfigObject::ConfigObject(Dictionary::Ptr properties) - : m_Properties(properties), m_Tags(make_shared()) +ConfigObject::ConfigObject(Dictionary::Ptr properties, const ConfigObject::Set::Ptr& container) + : m_Container(container ? container : GetAllObjects()), + m_Properties(properties), m_Tags(make_shared()) { } -ConfigObject::ConfigObject(string type, string name) - : m_Properties(make_shared()), m_Tags(make_shared()) +ConfigObject::ConfigObject(string type, string name, const ConfigObject::Set::Ptr& container) + : m_Container(container ? container : GetAllObjects()), + m_Properties(make_shared()), m_Tags(make_shared()) { SetProperty("__type", type); SetProperty("__name", name); @@ -90,13 +92,13 @@ void ConfigObject::Commit(void) ConfigObject::Ptr dobj = GetObject(GetType(), GetName()); ConfigObject::Ptr self = static_pointer_cast(shared_from_this()); assert(!dobj || dobj == self); - GetAllObjects()->CheckObject(self); + m_Container->CheckObject(self); } void ConfigObject::Unregister(void) { ConfigObject::Ptr self = static_pointer_cast(shared_from_this()); - GetAllObjects()->RemoveObject(self); + m_Container->RemoveObject(self); } ObjectSet::Ptr ConfigObject::GetAllObjects(void) @@ -174,4 +176,4 @@ bool ConfigObject::TypeGetter(const ConfigObject::Ptr& object, string *key) ConfigObject::TMap::Range ConfigObject::GetObjects(string type) { return GetObjectsByType()->GetRange(type); -} \ No newline at end of file +} diff --git a/base/configobject.h b/base/configobject.h index 52f530c61..05472ed4b 100644 --- a/base/configobject.h +++ b/base/configobject.h @@ -33,8 +33,8 @@ public: typedef ObjectMap TMap; typedef ObjectSet Set; - ConfigObject(Dictionary::Ptr properties); - ConfigObject(string type, string name); + ConfigObject(Dictionary::Ptr properties, const Set::Ptr& container = Set::Ptr()); + ConfigObject(string type, string name, const Set::Ptr& container = Set::Ptr()); void SetProperties(Dictionary::Ptr config); Dictionary::Ptr GetProperties(void) const; @@ -89,6 +89,7 @@ public: static function MakeTypePredicate(string type); private: + Set::Ptr m_Container; Dictionary::Ptr m_Properties; Dictionary::Ptr m_Tags; diff --git a/components/configrpc/configrpccomponent.cpp b/components/configrpc/configrpccomponent.cpp index 6e5b02a7a..2504510ce 100644 --- a/components/configrpc/configrpccomponent.cpp +++ b/components/configrpc/configrpccomponent.cpp @@ -94,17 +94,14 @@ RequestMessage ConfigRpcComponent::MakeObjectMessage(const ConfigObject::Ptr& ob params.SetProperty("type", object->GetType()); if (includeProperties) - params.SetProperty("properties", object); + params.SetProperty("properties", object->GetProperties()); return msg; } bool ConfigRpcComponent::ShouldReplicateObject(const ConfigObject::Ptr& object) { - long replicate; - if (!object->GetProperty("replicate", &replicate)) - return true; - return (replicate != 0); + return (!object->IsLocal()); } int ConfigRpcComponent::FetchObjectsHandler(const NewRequestEventArgs& ea) diff --git a/dyn/configitem.cpp b/dyn/configitem.cpp index ea274cec3..43eca579e 100644 --- a/dyn/configitem.cpp +++ b/dyn/configitem.cpp @@ -122,7 +122,10 @@ void ConfigItem::Commit(void) m_ConfigObject = dobj; - dobj->Commit(); + if (dobj->IsAbstract()) + dobj->Unregister(); + else + dobj->Commit(); ConfigItem::Ptr ci = GetObject(GetType(), GetName()); ConfigItem::Ptr self = static_pointer_cast(shared_from_this()); diff --git a/icinga-app/icinga-standalone.conf b/icinga-app/icinga-standalone.conf index f64c5cfc4..d5e71f27b 100644 --- a/icinga-app/icinga-standalone.conf +++ b/icinga-app/icinga-standalone.conf @@ -5,3 +5,28 @@ local object application "icinga" { local object component "demo" { } + +object host "localhost" { + +} + +abstract object service "nagios-service" { + check_type = "nagios", + + macros = { + plugindir = "/usr/local/icinga/libexec" + } +} + +abstract object service "ping" inherits "nagios-service" { + check_type = "nagios", + check_command = "$plugindir$/check_ping -H $address$" +} + +object service "localhost-ping" inherits "ping" { + host_name = "localhost", + + macros += { + address = "localhost" + } +} \ No newline at end of file diff --git a/icinga/checktask.cpp b/icinga/checktask.cpp new file mode 100644 index 000000000..f346053fe --- /dev/null +++ b/icinga/checktask.cpp @@ -0,0 +1,22 @@ +#include "i2-icinga.h" + +using namespace icinga; + +map CheckTask::m_Types; + +void CheckTask::RegisterType(string type, Factory factory) +{ + m_Types[type] = factory; +} + +CheckTask::Ptr CheckTask::CreateTask(const Service& service) +{ + map::iterator it; + + it = m_Types.find(service.GetCheckType()); + + if (it == m_Types.end()) + throw runtime_error("Invalid check type specified for service '" + service.GetName() + "'"); + + return it->second(service); +} diff --git a/icinga/checktask.h b/icinga/checktask.h new file mode 100644 index 000000000..622e018aa --- /dev/null +++ b/icinga/checktask.h @@ -0,0 +1,46 @@ +#ifndef CHECKTASK_H +#define CHECKTASK_H + +namespace icinga +{ + +enum CheckState +{ + StateOK, + StateWarning, + StateCritical, + StateUnreachable, + StateUncheckable, + StateUnknown +}; + +struct CheckResult +{ + time_t StartTime; + time_t EndTime; + + CheckState State; + string Output; + Dictionary::Ptr PerformanceData; +}; + +class CheckTask : public Object +{ +public: + typedef shared_ptr Ptr; + typedef weak_ptr WeakPtr; + + typedef function Factory; + + virtual CheckResult Execute(void) const = 0; + + static void RegisterType(string type, Factory factory); + static CheckTask::Ptr CreateTask(const Service& service); + +private: + static map m_Types; +}; + +} + +#endif /* CHECKTASK_H */ diff --git a/icinga/configobjectadapter.cpp b/icinga/configobjectadapter.cpp new file mode 100644 index 000000000..7da5cdf2b --- /dev/null +++ b/icinga/configobjectadapter.cpp @@ -0,0 +1,23 @@ +#include "i2-icinga.h" + +using namespace icinga; + +string ConfigObjectAdapter::GetType(void) const +{ + return m_ConfigObject->GetType(); +} + +string ConfigObjectAdapter::GetName(void) const +{ + return m_ConfigObject->GetName(); +} + +bool ConfigObjectAdapter::IsLocal(void) const +{ + return m_ConfigObject->IsLocal(); +} + +ConfigObject::Ptr ConfigObjectAdapter::GetConfigObject() const +{ + return m_ConfigObject; +} diff --git a/icinga/configobjectadapter.h b/icinga/configobjectadapter.h new file mode 100644 index 000000000..4ad327ddd --- /dev/null +++ b/icinga/configobjectadapter.h @@ -0,0 +1,28 @@ +#ifndef CONFIGOBJECTADAPTER_H +#define CONFIGOBJECTADAPTER_H + +namespace icinga +{ + +class I2_ICINGA_API ConfigObjectAdapter +{ +public: + ConfigObjectAdapter(const ConfigObject::Ptr& configObject) + : m_ConfigObject(configObject) + { } + + string GetType(void) const; + string GetName(void) const; + + bool IsLocal(void) const; + +protected: + ConfigObject::Ptr GetConfigObject() const; + +private: + ConfigObject::Ptr m_ConfigObject; +}; + +} + +#endif /* CONFIGOBJECTADAPTER_H */ \ No newline at end of file diff --git a/icinga/host.cpp b/icinga/host.cpp new file mode 100644 index 000000000..d9a996363 --- /dev/null +++ b/icinga/host.cpp @@ -0,0 +1,23 @@ +#include "i2-icinga.h" + +using namespace icinga; + +string Host::GetDisplayName(void) const +{ + string value; + + if (GetConfigObject()->GetProperty("displayname", &value)) + return value; + + return GetName(); +} + +Host Host::GetByName(string name) +{ + ConfigObject::Ptr configObject = ConfigObject::GetObject("host", name); + + if (!configObject) + throw invalid_argument("Host '" + name + "' does not exist."); + + return Host(configObject); +} diff --git a/icinga/host.h b/icinga/host.h new file mode 100644 index 000000000..18331a730 --- /dev/null +++ b/icinga/host.h @@ -0,0 +1,21 @@ +#ifndef HOST_H +#define HOST_H + +namespace icinga +{ + +class I2_ICINGA_API Host : public ConfigObjectAdapter +{ +public: + Host(const ConfigObject::Ptr& configObject) + : ConfigObjectAdapter(configObject) + { } + + static Host GetByName(string name); + + string GetDisplayName(void) const; +}; + +} + +#endif /* HOST_H */ diff --git a/icinga/i2-icinga.h b/icinga/i2-icinga.h index cc0f50cea..696ca65f0 100644 --- a/icinga/i2-icinga.h +++ b/icinga/i2-icinga.h @@ -44,4 +44,12 @@ #include "icingaapplication.h" #include "icingacomponent.h" +#include "configobjectadapter.h" +#include "host.h" +#include "service.h" + +#include "macroprocessor.h" +#include "checktask.h" +#include "nagioschecktask.h" + #endif /* I2ICINGA_H */ diff --git a/icinga/icinga.vcxproj b/icinga/icinga.vcxproj index c58a2675b..63ead4e0b 100644 --- a/icinga/icinga.vcxproj +++ b/icinga/icinga.vcxproj @@ -11,20 +11,32 @@ + + + + + + + + + + + + diff --git a/icinga/icingaapplication.cpp b/icinga/icingaapplication.cpp index cede383c4..14dec6189 100644 --- a/icinga/icingaapplication.cpp +++ b/icinga/icingaapplication.cpp @@ -91,6 +91,18 @@ int IcingaApplication::Main(const vector& args) m_EndpointManager->SetSSLContext(sslContext); } + CheckTask::RegisterType("nagios", NagiosCheckTask::CreateTask); + + ConfigObject::TMap::Range range = ConfigObject::GetObjects("service"); + + for (ConfigObject::TMap::Iterator it = range.first; it != range.second; it++) { + ConfigObject::Ptr obj = it->second; + + Service svc = Service(obj); + CheckTask::Ptr ct = CheckTask::CreateTask(svc); + CheckResult cr = ct->Execute(); + } + /* create the primary RPC listener */ string service = GetService(); if (!service.empty()) @@ -117,7 +129,7 @@ int IcingaApplication::NewComponentHandler(const ObjectSetEventArgsIsLocal()) - return 0; + throw runtime_error("'component' objects must be 'local'"); string path; if (!object->GetProperty("path", &path)) { diff --git a/icinga/macroprocessor.cpp b/icinga/macroprocessor.cpp new file mode 100644 index 000000000..e5dca2506 --- /dev/null +++ b/icinga/macroprocessor.cpp @@ -0,0 +1,28 @@ +#include "i2-icinga.h" + +using namespace icinga; + +string MacroProcessor::ResolveMacros(string str, Dictionary::Ptr macros) +{ + string::size_type offset, pos_first, pos_second; + + offset = 0; + + while ((pos_first = str.find_first_of('$', offset)) != string::npos) { + pos_second = str.find_first_of('$', pos_first + 1); + + if (pos_second == string::npos) + throw runtime_error("Closing $ not found in macro format string."); + + string name = str.substr(pos_first + 1, pos_second - pos_first - 1); + string value; + if (!macros || !macros->GetProperty(name, &value)) + throw runtime_error("Macro '" + name + "' is not defined."); + + str.replace(pos_first, pos_second - pos_first + 1, value); + + offset = pos_first + value.size(); + } + + return str; +} diff --git a/icinga/macroprocessor.h b/icinga/macroprocessor.h new file mode 100644 index 000000000..3f756c097 --- /dev/null +++ b/icinga/macroprocessor.h @@ -0,0 +1,15 @@ +#ifndef MACROPROCESSOR_H +#define MACROPROCESSOR_H + +namespace icinga +{ + +class MacroProcessor +{ +public: + static string ResolveMacros(string str, Dictionary::Ptr macros); +}; + +} + +#endif /* MACROPROCESSOR_H */ \ No newline at end of file diff --git a/icinga/nagioschecktask.cpp b/icinga/nagioschecktask.cpp new file mode 100644 index 000000000..2fccdb237 --- /dev/null +++ b/icinga/nagioschecktask.cpp @@ -0,0 +1,85 @@ +#include "i2-icinga.h" + +using namespace icinga; + +NagiosCheckTask::NagiosCheckTask(const Service& service) +{ + string checkCommand = service.GetCheckCommand(); + m_Command = MacroProcessor::ResolveMacros(checkCommand, service.GetMacros()); +} + +CheckResult NagiosCheckTask::Execute(void) const +{ + CheckResult cr; + FILE *fp; + + time(&cr.StartTime); + + string command = m_Command + " 2>&1"; + +#ifdef _MSC_VER + fp = _popen(command.c_str(), "r"); +#else /* _MSC_VER */ + fp = popen(command.c_str(), "r"); +#endif /* _MSC_VER */ + + stringstream output; + + while (!feof(fp)) { + char buffer[128]; + size_t read = fread(buffer, 1, sizeof(buffer), fp); + + if (read == 0) + break; + + output << string(buffer, buffer + read); + } + + cr.Output = output.str(); + + int status, exitstatus; +#ifdef _MSC_VER + status = _pclose(fp); +#else /* _MSC_VER */ + status = pclose(fp); +#endif /* _MSC_VER */ + +#ifndef _MSC_VER + if (WIFEXITED(status)) { + exitcode = WEXITSTATUS(status); +#else /* _MSC_VER */ + exitstatus = status; +#endif /* _MSC_VER */ + + switch (exitstatus) { + case 0: + cr.State = StateOK; + break; + case 1: + cr.State = StateWarning; + break; + case 2: + cr.State = StateCritical; + break; + default: + cr.State = StateUnknown; + break; + } +#ifndef _MSC_VER + } else if (WIFSIGNALED(status)) { + cr.Output = "Process was terminated by signal " + WTERMSIG(status); + cr.Status = StateUnknown; + } +#endif /* _MSC_VER */ + + time(&cr.EndTime); + + return cr; +} + +CheckTask::Ptr NagiosCheckTask::CreateTask(const Service& service) +{ + assert(service.GetCheckType() == "nagios"); + + return make_shared(service); +} diff --git a/icinga/nagioschecktask.h b/icinga/nagioschecktask.h new file mode 100644 index 000000000..60c146414 --- /dev/null +++ b/icinga/nagioschecktask.h @@ -0,0 +1,22 @@ +#ifndef NAGIOSCHECKTASK_H +#define NAGIOSCHECKTASK_H + +namespace icinga +{ + +class NagiosCheckTask : public CheckTask +{ +public: + NagiosCheckTask(const Service& service); + + virtual CheckResult Execute(void) const; + + static CheckTask::Ptr CreateTask(const Service& service); + +private: + string m_Command; +}; + +} + +#endif /* NAGIOSCHECKTASK_H */ diff --git a/icinga/service.cpp b/icinga/service.cpp new file mode 100644 index 000000000..3e38f9e30 --- /dev/null +++ b/icinga/service.cpp @@ -0,0 +1,64 @@ +#include "i2-icinga.h" + +using namespace icinga; + +string Service::GetDisplayName(void) const +{ + string value; + + if (GetConfigObject()->GetProperty("displayname", &value)) + return value; + + return GetName(); +} + +Host Service::GetHost(void) const +{ + string hostname; + if (!GetConfigObject()->GetProperty("host_name", &hostname)) + throw runtime_error("Service object is missing the 'host_name' property."); + + return Host::GetByName(hostname); +} + +Dictionary::Ptr Service::GetMacros(void) const +{ + Dictionary::Ptr macros; + GetConfigObject()->GetProperty("macros", ¯os); + return macros; +} + +string Service::GetCheckType(void) const +{ + string value = "nagios"; + GetConfigObject()->GetProperty("check_type", &value); + return value; +} + +string Service::GetCheckCommand(void) const +{ + string value; + GetConfigObject()->GetProperty("check_command", &value); + return value; +} + +long Service::GetMaxCheckAttempts(void) const +{ + long value = 1; + GetConfigObject()->GetProperty("max_check_attempts", &value); + return value; +} + +long Service::GetCheckInterval(void) const +{ + long value = 1; + GetConfigObject()->GetProperty("check_interval", &value); + return value; +} + +long Service::GetRetryInterval(void) const +{ + long value = 1; + GetConfigObject()->GetProperty("retry_interval", &value); + return value; +} diff --git a/icinga/service.h b/icinga/service.h new file mode 100644 index 000000000..ca47ae3e1 --- /dev/null +++ b/icinga/service.h @@ -0,0 +1,26 @@ +#ifndef SERVICE_H +#define SERVICE_H + +namespace icinga +{ + +class I2_ICINGA_API Service : public ConfigObjectAdapter +{ +public: + Service(const ConfigObject::Ptr& configObject) + : ConfigObjectAdapter(configObject) + { } + + string GetDisplayName(void) const; + Host GetHost(void) const; + Dictionary::Ptr GetMacros(void) const; + string GetCheckType(void) const; + string GetCheckCommand(void) const; + long GetMaxCheckAttempts(void) const; + long GetCheckInterval(void) const; + long GetRetryInterval(void) const; +}; + +} + +#endif /* SERVICE_H */ \ No newline at end of file