]> granicus.if.org Git - icinga2/commitdiff
Implemented rudimentary service checks.
authorGunnar Beutner <gunnar@beutner.name>
Wed, 13 Jun 2012 11:42:55 +0000 (13:42 +0200)
committerGunnar Beutner <gunnar@beutner.name>
Wed, 13 Jun 2012 11:43:12 +0000 (13:43 +0200)
20 files changed:
base/configobject.cpp
base/configobject.h
components/configrpc/configrpccomponent.cpp
dyn/configitem.cpp
icinga-app/icinga-standalone.conf
icinga/checktask.cpp [new file with mode: 0644]
icinga/checktask.h [new file with mode: 0644]
icinga/configobjectadapter.cpp [new file with mode: 0644]
icinga/configobjectadapter.h [new file with mode: 0644]
icinga/host.cpp [new file with mode: 0644]
icinga/host.h [new file with mode: 0644]
icinga/i2-icinga.h
icinga/icinga.vcxproj
icinga/icingaapplication.cpp
icinga/macroprocessor.cpp [new file with mode: 0644]
icinga/macroprocessor.h [new file with mode: 0644]
icinga/nagioschecktask.cpp [new file with mode: 0644]
icinga/nagioschecktask.h [new file with mode: 0644]
icinga/service.cpp [new file with mode: 0644]
icinga/service.h [new file with mode: 0644]

index 163b1e21010830c2651f5e12119196c36eba97f4..fd5efcf7db5fad685e1934d1f0a3a48558b3023f 100644 (file)
 
 using namespace icinga;
 
-ConfigObject::ConfigObject(Dictionary::Ptr properties)
-       : m_Properties(properties), m_Tags(make_shared<Dictionary>())
+ConfigObject::ConfigObject(Dictionary::Ptr properties, const ConfigObject::Set::Ptr& container)
+       : m_Container(container ? container : GetAllObjects()),
+       m_Properties(properties), m_Tags(make_shared<Dictionary>())
 { }
 
-ConfigObject::ConfigObject(string type, string name)
-       : m_Properties(make_shared<Dictionary>()), m_Tags(make_shared<Dictionary>())
+ConfigObject::ConfigObject(string type, string name, const ConfigObject::Set::Ptr& container)
+       : m_Container(container ? container : GetAllObjects()),
+       m_Properties(make_shared<Dictionary>()), m_Tags(make_shared<Dictionary>())
 {
        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<ConfigObject>(shared_from_this());
        assert(!dobj || dobj == self);
-       GetAllObjects()->CheckObject(self);
+       m_Container->CheckObject(self);
 }
 
 void ConfigObject::Unregister(void)
 {
        ConfigObject::Ptr self = static_pointer_cast<ConfigObject>(shared_from_this());
-       GetAllObjects()->RemoveObject(self);
+       m_Container->RemoveObject(self);
 }
 
 ObjectSet<ConfigObject::Ptr>::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
+}
index 52f530c610c5ee31a271544305575b027e1e7cc2..05472ed4bd8ec9a87f85250e0fe806a5b04d324a 100644 (file)
@@ -33,8 +33,8 @@ public:
        typedef ObjectMap<string, ConfigObject::Ptr> TMap;
        typedef ObjectSet<ConfigObject::Ptr> 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<bool (ConfigObject::Ptr)> MakeTypePredicate(string type);
 
 private:
+       Set::Ptr m_Container;
        Dictionary::Ptr m_Properties;
        Dictionary::Ptr m_Tags;
 
index 6e5b02a7a0e5f856af4333c7a6110d17ea743dd9..2504510ce2d0dd494b5e440707da1d715cfb0581 100644 (file)
@@ -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)
index ea274cec3cdefa69dcdead4ca3623e8e105c77ac..43eca579e00f5b51969864aa94350420224e62f4 100644 (file)
@@ -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<ConfigItem>(shared_from_this());
index f64c5cfc4ca55e0046682dee175c5af400cf7ea7..d5e71f27b5403ce459e29f06f6405d50c26c53ae 100644 (file)
@@ -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 (file)
index 0000000..f346053
--- /dev/null
@@ -0,0 +1,22 @@
+#include "i2-icinga.h"
+
+using namespace icinga;
+
+map<string, CheckTask::Factory> CheckTask::m_Types;
+
+void CheckTask::RegisterType(string type, Factory factory)
+{
+       m_Types[type] = factory;
+}
+
+CheckTask::Ptr CheckTask::CreateTask(const Service& service)
+{
+       map<string, CheckTask::Factory>::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 (file)
index 0000000..622e018
--- /dev/null
@@ -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<CheckTask> Ptr;
+       typedef weak_ptr<CheckTask> WeakPtr;
+
+       typedef function<CheckTask::Ptr(const Service&)> Factory;
+
+       virtual CheckResult Execute(void) const = 0;
+
+       static void RegisterType(string type, Factory factory);
+       static CheckTask::Ptr CreateTask(const Service& service);
+
+private:
+       static map<string, Factory> m_Types;
+};
+
+}
+
+#endif /* CHECKTASK_H */
diff --git a/icinga/configobjectadapter.cpp b/icinga/configobjectadapter.cpp
new file mode 100644 (file)
index 0000000..7da5cdf
--- /dev/null
@@ -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 (file)
index 0000000..4ad327d
--- /dev/null
@@ -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 (file)
index 0000000..d9a9963
--- /dev/null
@@ -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 (file)
index 0000000..18331a7
--- /dev/null
@@ -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 */
index cc0f50cea2428d42202a2df70447d3bbe22e82b7..696ca65f01ec17952450920c96f0f1440ce09f19 100644 (file)
 #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 */
index c58a2675b515bb7435b24495c2c47343ed5be423..63ead4e0bcbf23d146cf46842e0b74d9dc884311 100644 (file)
     </ProjectConfiguration>
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="checktask.cpp" />
+    <ClCompile Include="configobjectadapter.cpp" />
     <ClCompile Include="endpoint.cpp" />
     <ClCompile Include="endpointmanager.cpp" />
+    <ClCompile Include="host.cpp" />
     <ClCompile Include="icingaapplication.cpp" />
     <ClCompile Include="icingacomponent.cpp" />
     <ClCompile Include="jsonrpcendpoint.cpp" />
+    <ClCompile Include="macroprocessor.cpp" />
+    <ClCompile Include="nagioschecktask.cpp" />
+    <ClCompile Include="service.cpp" />
     <ClCompile Include="virtualendpoint.cpp" />
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="checktask.h" />
+    <ClInclude Include="configobjectadapter.h" />
     <ClInclude Include="endpoint.h" />
     <ClInclude Include="endpointmanager.h" />
+    <ClInclude Include="host.h" />
     <ClInclude Include="i2-icinga.h" />
     <ClInclude Include="icingaapplication.h" />
     <ClInclude Include="icingacomponent.h" />
     <ClInclude Include="jsonrpcendpoint.h" />
+    <ClInclude Include="macroprocessor.h" />
+    <ClInclude Include="nagioschecktask.h" />
+    <ClInclude Include="service.h" />
     <ClInclude Include="virtualendpoint.h" />
   </ItemGroup>
   <PropertyGroup Label="Globals">
index cede383c49c8bf263f3654f0b7d98d738b824517..14dec6189c2e0ff55a7249c5053b6dfea7b18cd9 100644 (file)
@@ -91,6 +91,18 @@ int IcingaApplication::Main(const vector<string>& 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 ObjectSetEventArgs<ConfigObject
        
        /* don't allow replicated config objects */
        if (!object->IsLocal())
-               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 (file)
index 0000000..e5dca25
--- /dev/null
@@ -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 (file)
index 0000000..3f756c0
--- /dev/null
@@ -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 (file)
index 0000000..2fccdb2
--- /dev/null
@@ -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<NagiosCheckTask>(service);
+}
diff --git a/icinga/nagioschecktask.h b/icinga/nagioschecktask.h
new file mode 100644 (file)
index 0000000..60c1464
--- /dev/null
@@ -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 (file)
index 0000000..3e38f9e
--- /dev/null
@@ -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", &macros);
+       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 (file)
index 0000000..ca47ae3
--- /dev/null
@@ -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