From 4e7738c5a58c9aea5d9d69cbb47d3f9bf1589056 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 16 Apr 2014 10:39:13 +0200 Subject: [PATCH] Implement CLR-based checks. --- contrib/icinga2clr.cs | 44 ++++++ itl/command.conf | 4 + lib/methods/CMakeLists.txt | 8 +- lib/methods/clrchecktask.cpp | 205 +++++++++++++++++++++++++ lib/methods/clrchecktask.h | 49 ++++++ lib/methods/pluginchecktask.cpp | 1 + lib/methods/plugineventtask.cpp | 1 + lib/methods/pluginnotificationtask.cpp | 1 + 8 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 contrib/icinga2clr.cs create mode 100644 lib/methods/clrchecktask.cpp create mode 100644 lib/methods/clrchecktask.h diff --git a/contrib/icinga2clr.cs b/contrib/icinga2clr.cs new file mode 100644 index 000000000..5a2464db1 --- /dev/null +++ b/contrib/icinga2clr.cs @@ -0,0 +1,44 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 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. * + ******************************************************************************/ + +using System; +using System.Collections; + +namespace Icinga +{ + public enum ServiceState + { + ServiceOK, + ServiceWarning, + ServiceCritical, + ServiceUnknown + } + + public class CheckResult + { + public ServiceState State; + public String Output; + public String PerformanceData; + } + + public interface ICheckPlugin + { + CheckResult Check(Hashtable args); + } +} diff --git a/itl/command.conf b/itl/command.conf index 972a2a1ac..c234b9881 100644 --- a/itl/command.conf +++ b/itl/command.conf @@ -35,6 +35,10 @@ template CheckCommand "agent-check-command" { methods.execute = "AgentCheck" } +template CheckCommand "clr-check-command" { + methods.execute = "ClrCheck" +} + template NotificationCommand "plugin-notification-command" { methods.execute = "PluginNotification" } diff --git a/lib/methods/CMakeLists.txt b/lib/methods/CMakeLists.txt index 6f271b292..59bc28d3d 100644 --- a/lib/methods/CMakeLists.txt +++ b/lib/methods/CMakeLists.txt @@ -15,10 +15,16 @@ # along with this program; if not, write to the Free Software Foundation # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +if(MSVC) + set(WindowsSources clrchecktask.cpp) +else() + set(WindowsSources "") +endif() + add_library(methods SHARED castfuncs.cpp icingachecktask.cpp nullchecktask.cpp nulleventtask.cpp pluginchecktask.cpp plugineventtask.cpp pluginnotificationtask.cpp - randomchecktask.cpp timeperiodtask.cpp + randomchecktask.cpp timeperiodtask.cpp ${WindowsSources} ) target_link_libraries(methods ${Boost_LIBRARIES} base config icinga) diff --git a/lib/methods/clrchecktask.cpp b/lib/methods/clrchecktask.cpp new file mode 100644 index 000000000..e3312a90a --- /dev/null +++ b/lib/methods/clrchecktask.cpp @@ -0,0 +1,205 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 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 "methods/clrchecktask.h" +#include "icinga/pluginutility.h" +#include "icinga/checkcommand.h" +#include "icinga/macroprocessor.h" +#include "icinga/icingaapplication.h" +#include "base/dynamictype.h" +#include "base/logger_fwd.h" +#include "base/scriptfunction.h" +#include "base/utility.h" +#include "base/process.h" +#include +#include +#include +#include +#include + +#import "mscorlib.tlb" +#pragma comment(lib, "mscoree.lib") + +using namespace icinga; + +REGISTER_SCRIPTFUNCTION(ClrCheck, &ClrCheckTask::ScriptFunc); + +static boost::once_flag l_OnceFlag = BOOST_ONCE_INIT; + +static boost::mutex l_ObjectsMutex; +static std::map l_Objects; + +static mscorlib::_AppDomainPtr l_AppDomain; + +static void InitializeClr(void) +{ + ICorRuntimeHost *runtimeHost; + + if (FAILED(CorBindToRuntimeEx(NULL, NULL, + STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_CONCURRENT_GC, + CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (void **)&runtimeHost))) { + return; + } + + runtimeHost->Start(); + + IUnknownPtr punkAppDomain = NULL; + runtimeHost->GetDefaultDomain(&punkAppDomain); + + punkAppDomain->QueryInterface(__uuidof(mscorlib::_AppDomain), (void **)&l_AppDomain); + + runtimeHost->Release(); +} + +static variant_t CreateClrType(const String& assemblyName, const String& typeName) +{ + boost::call_once(l_OnceFlag, &InitializeClr); + + try { + mscorlib::_ObjectHandlePtr pObjectHandle; + pObjectHandle = l_AppDomain->CreateInstanceFrom(assemblyName.CStr(), typeName.CStr()); + + return pObjectHandle->Unwrap(); + } catch (_com_error& error) { + BOOST_THROW_EXCEPTION(std::runtime_error("Could not load .NET type: " + String(error.Description()))); + } +} + +static variant_t InvokeClrMethod(const variant_t& vtObject, const String& methodName, const Dictionary::Ptr& args) +{ + CLSID clsid; + HRESULT hr = CLSIDFromProgID(L"System.Collections.Hashtable", &clsid); + + mscorlib::IDictionaryPtr pHashtable; + CoCreateInstance(clsid, NULL, CLSCTX_ALL, __uuidof(mscorlib::IDictionary), (void **)&pHashtable); + + ObjectLock olock(args); + BOOST_FOREACH(const Dictionary::Pair& kv, args) { + String value = kv.second; + pHashtable->Add(kv.first.CStr(), value.CStr()); + } + + mscorlib::_ObjectPtr pObject; + vtObject.pdispVal->QueryInterface(__uuidof(mscorlib::_Object), (void**)&pObject); + mscorlib::_TypePtr pType = pObject->GetType(); + + SAFEARRAY *psa = SafeArrayCreateVector(VT_VARIANT, 0, 1); + + variant_t vtHashtable = static_cast(pHashtable); + LONG idx = 0; + SafeArrayPutElement(psa, &idx, &vtHashtable); + + variant_t result = pType->InvokeMember_3(methodName.CStr(), + mscorlib::BindingFlags_InvokeMethod, + NULL, + vtObject, + psa); + + SafeArrayDestroy(psa); + + return result; +} + +static void FillCheckResult(const CheckResult::Ptr& cr, variant_t vtResult) +{ + mscorlib::_ObjectPtr pObject; + vtResult.pdispVal->QueryInterface(__uuidof(mscorlib::_Object), (void**)&pObject); + mscorlib::_TypePtr pType = pObject->GetType(); + + SAFEARRAY *psa = SafeArrayCreateVector(VT_VARIANT, 0, 0); + int lState = pType->InvokeMember_3("State", + mscorlib::BindingFlags_GetField, + NULL, + vtResult, + psa); + cr->SetState(static_cast(lState)); + bstr_t sOutput = pType->InvokeMember_3("Output", + mscorlib::BindingFlags_GetField, + NULL, + vtResult, + psa); + cr->SetOutput(static_cast(sOutput)); + bstr_t sPerformanceData = pType->InvokeMember_3("PerformanceData", + mscorlib::BindingFlags_GetField, + NULL, + vtResult, + psa); + SafeArrayDestroy(psa); + cr->SetPerformanceData(static_cast(sPerformanceData)); +} + +void ClrCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) +{ + CheckCommand::Ptr commandObj = checkable->GetCheckCommand(); + Value raw_command = commandObj->GetCommandLine(); + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + MacroProcessor::ResolverList resolvers; + if (service) + resolvers.push_back(std::make_pair("service", service)); + resolvers.push_back(std::make_pair("host", host)); + resolvers.push_back(std::make_pair("command", commandObj)); + resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance())); + + Dictionary::Ptr envMacros = make_shared(); + + Dictionary::Ptr env = commandObj->GetEnv(); + + if (env) { + ObjectLock olock(env); + BOOST_FOREACH(const Dictionary::Pair& kv, env) { + String name = kv.second; + + Value value = MacroProcessor::ResolveMacros(name, resolvers, checkable->GetLastCheckResult()); + + envMacros->Set(kv.first, value); + } + } + + variant_t vtObject; + + { + boost::mutex::scoped_lock lock(l_ObjectsMutex); + + std::map::iterator it = l_Objects.find(checkable); + + if (it != l_Objects.end()) { + vtObject = it->second; + } else { + String clr_assembly = MacroProcessor::ResolveMacros("$clr_assembly$", resolvers, checkable->GetLastCheckResult()); + String clr_type = MacroProcessor::ResolveMacros("$clr_type$", resolvers, checkable->GetLastCheckResult()); + + vtObject = CreateClrType(clr_assembly, clr_type); + l_Objects[checkable] = vtObject; + } + } + + try { + variant_t vtResult = InvokeClrMethod(vtObject, "Check", envMacros); + FillCheckResult(cr, vtResult); + checkable->ProcessCheckResult(cr); + } catch (_com_error& error) { + cr->SetOutput("Failed to invoke .NET method: " + String(error.Description())); + cr->SetState(ServiceUnknown); + checkable->ProcessCheckResult(cr); + } +} diff --git a/lib/methods/clrchecktask.h b/lib/methods/clrchecktask.h new file mode 100644 index 000000000..28f35f549 --- /dev/null +++ b/lib/methods/clrchecktask.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2014 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 CLRCHECKTASK_H +#define CLRCHECKTASK_H + +#include "methods/i2-methods.h" +#include "base/process.h" +#include "icinga/service.h" + +namespace icinga +{ + +/** + * Implements service checks based CLR libraries. + * + * @ingroup methods + */ +class I2_METHODS_API ClrCheckTask +{ +public: + static void ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr); + +private: + ClrCheckTask(void); + + static void ProcessFinishedHandler(const Checkable::Ptr& service, const CheckResult::Ptr& cr, const ProcessResult& pr); + +}; + +} + +#endif /* CLRCHECKTASK_H */ diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp index a5e4db767..3b87faa5f 100644 --- a/lib/methods/pluginchecktask.cpp +++ b/lib/methods/pluginchecktask.cpp @@ -58,6 +58,7 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes Dictionary::Ptr env = commandObj->GetEnv(); if (env) { + ObjectLock olock(env); BOOST_FOREACH(const Dictionary::Pair& kv, env) { String name = kv.second; diff --git a/lib/methods/plugineventtask.cpp b/lib/methods/plugineventtask.cpp index 355db7efd..8522956a4 100644 --- a/lib/methods/plugineventtask.cpp +++ b/lib/methods/plugineventtask.cpp @@ -55,6 +55,7 @@ void PluginEventTask::ScriptFunc(const Checkable::Ptr& checkable) Dictionary::Ptr env = commandObj->GetEnv(); if (env) { + ObjectLock olock(env); BOOST_FOREACH(const Dictionary::Pair& kv, env) { String name = kv.second; diff --git a/lib/methods/pluginnotificationtask.cpp b/lib/methods/pluginnotificationtask.cpp index 09967cf6c..1f69d6b1a 100644 --- a/lib/methods/pluginnotificationtask.cpp +++ b/lib/methods/pluginnotificationtask.cpp @@ -70,6 +70,7 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification, c Dictionary::Ptr env = commandObj->GetEnv(); if (env) { + ObjectLock olock(env); BOOST_FOREACH(const Dictionary::Pair& kv, env) { String name = kv.second; -- 2.40.0