Implemented rudimentary service checks.

This commit is contained in:
Gunnar Beutner 2012-06-13 13:42:55 +02:00
parent 01c2da2853
commit 31407fd07f
20 changed files with 479 additions and 16 deletions

View File

@ -21,12 +21,14 @@
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);
}
}

View 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;

View 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)

View 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());

View 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"
}
}

22
icinga/checktask.cpp Normal file
View File

@ -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);
}

46
icinga/checktask.h Normal file
View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */

23
icinga/host.cpp Normal file
View File

@ -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);
}

21
icinga/host.h Normal file
View File

@ -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 */

View File

@ -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 */

View File

@ -11,20 +11,32 @@
</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">

View 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)) {

28
icinga/macroprocessor.cpp Normal file
View File

@ -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;
}

15
icinga/macroprocessor.h Normal file
View File

@ -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 */

View File

@ -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);
}

22
icinga/nagioschecktask.h Normal file
View File

@ -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 */

64
icinga/service.cpp Normal file
View File

@ -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;
}

26
icinga/service.h Normal file
View File

@ -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 */