mirror of https://github.com/Icinga/icinga2.git
Implemented calling ScriptFunctions from Python.
This commit is contained in:
parent
a022be9de2
commit
4998563a74
|
@ -25,7 +25,7 @@ boost::signal<void (const String&, const ScriptFunction::Ptr&)> ScriptFunction::
|
|||
boost::signal<void (const String&)> ScriptFunction::OnUnregistered;
|
||||
|
||||
ScriptFunction::ScriptFunction(const Callback& function)
|
||||
: m_Callback(function)
|
||||
: m_Callback(function), m_ArgumentCount(-1)
|
||||
{ }
|
||||
|
||||
void ScriptFunction::Register(const String& name, const ScriptFunction::Ptr& function)
|
||||
|
@ -62,3 +62,43 @@ map<String, ScriptFunction::Ptr>& ScriptFunction::GetFunctions(void)
|
|||
static map<String, ScriptFunction::Ptr> functions;
|
||||
return functions;
|
||||
}
|
||||
|
||||
void ScriptFunction::SetArgumentCount(int count)
|
||||
{
|
||||
if (m_ArgumentCount >= 0)
|
||||
m_ArgumentHints.resize(count);
|
||||
|
||||
m_ArgumentCount = count;
|
||||
}
|
||||
|
||||
int ScriptFunction::GetArgumentCount(void) const
|
||||
{
|
||||
return m_ArgumentCount;
|
||||
}
|
||||
|
||||
void ScriptFunction::SetArgumentHint(int index, const ScriptArgumentHint& hint)
|
||||
{
|
||||
assert(index >= 0 && index < m_ArgumentCount);
|
||||
|
||||
m_ArgumentHints[index] = hint;
|
||||
}
|
||||
|
||||
ScriptArgumentHint ScriptFunction::GetArgumentHint(int index) const
|
||||
{
|
||||
if (m_ArgumentCount == -1 || index >= m_ArgumentCount)
|
||||
return ScriptArgumentHint();
|
||||
|
||||
assert(index >= 0 && index < m_ArgumentHints.size());
|
||||
|
||||
return m_ArgumentHints[index];
|
||||
}
|
||||
|
||||
void ScriptFunction::SetReturnHint(const ScriptArgumentHint& hint)
|
||||
{
|
||||
m_ReturnHint = hint;
|
||||
}
|
||||
|
||||
ScriptArgumentHint ScriptFunction::GetReturnHint(void) const
|
||||
{
|
||||
return m_ReturnHint;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,20 @@ namespace icinga
|
|||
|
||||
class ScriptTask;
|
||||
|
||||
/**
|
||||
* A type hint.
|
||||
*/
|
||||
struct ScriptArgumentHint
|
||||
{
|
||||
bool RestrictType;
|
||||
ValueType Type;
|
||||
String Class;
|
||||
|
||||
ScriptArgumentHint(void)
|
||||
: RestrictType(false), Type(ValueEmpty), Class()
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* A script function that can be used to execute a script task.
|
||||
*
|
||||
|
@ -46,6 +60,15 @@ public:
|
|||
|
||||
void Invoke(const shared_ptr<ScriptTask>& task, const vector<Value>& arguments);
|
||||
|
||||
void SetArgumentCount(int count);
|
||||
int GetArgumentCount(void) const;
|
||||
|
||||
void SetArgumentHint(int index, const ScriptArgumentHint& hint);
|
||||
ScriptArgumentHint GetArgumentHint(int index) const;
|
||||
|
||||
void SetReturnHint(const ScriptArgumentHint& hint);
|
||||
ScriptArgumentHint GetReturnHint(void) const;
|
||||
|
||||
static map<String, ScriptFunction::Ptr>& GetFunctions(void);
|
||||
|
||||
static boost::signal<void (const String&, const ScriptFunction::Ptr&)> OnRegistered;
|
||||
|
@ -53,6 +76,9 @@ public:
|
|||
|
||||
private:
|
||||
Callback m_Callback;
|
||||
int m_ArgumentCount;
|
||||
vector<ScriptArgumentHint> m_ArgumentHints;
|
||||
ScriptArgumentHint m_ReturnHint;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -40,6 +40,9 @@ public:
|
|||
|
||||
virtual ScriptInterpreter::Ptr CreateInterpreter(const Script::Ptr& script) = 0;
|
||||
|
||||
void SubscribeFunction(const String& name);
|
||||
void UnsubscribeFunction(const String& name);
|
||||
|
||||
protected:
|
||||
ScriptLanguage(void);
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ pkglib_LTLIBRARIES = \
|
|||
libicinga.la
|
||||
|
||||
libicinga_la_SOURCES = \
|
||||
api.cpp \
|
||||
api.h \
|
||||
checkresultmessage.cpp \
|
||||
checkresultmessage.h \
|
||||
cib.cpp \
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/******************************************************************************
|
||||
* 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 "i2-icinga.h"
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
REGISTER_SCRIPTFUNCTION("GetAnswerToEverything", &API::GetAnswerToEverything);
|
||||
|
||||
void API::GetAnswerToEverything(const ScriptTask::Ptr& task, const vector<Value>& arguments)
|
||||
{
|
||||
if (arguments.size() < 1)
|
||||
BOOST_THROW_EXCEPTION(invalid_argument("Text argument required."));
|
||||
|
||||
String text = arguments[0];
|
||||
|
||||
Logger::Write(LogInformation, "icinga", "Hello from the Icinga 2 API: " + text);
|
||||
|
||||
task->FinishResult(42);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/******************************************************************************
|
||||
* 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 API_H
|
||||
#define API_H
|
||||
|
||||
namespace icinga
|
||||
{
|
||||
|
||||
/**
|
||||
* A state change message for a service.
|
||||
*
|
||||
* @ingroup icinga
|
||||
*/
|
||||
class I2_ICINGA_API API
|
||||
{
|
||||
public:
|
||||
static void GetAnswerToEverything(const ScriptTask::Ptr& task, const vector<Value>& arguments);
|
||||
|
||||
private:
|
||||
API(void);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* API_H */
|
|
@ -64,4 +64,6 @@ using boost::algorithm::is_any_of;
|
|||
|
||||
#include "cib.h"
|
||||
|
||||
#include "api.h"
|
||||
|
||||
#endif /* I2ICINGA_H */
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="api.cpp" />
|
||||
<ClCompile Include="checkresultmessage.cpp" />
|
||||
<ClCompile Include="cib.cpp" />
|
||||
<ClCompile Include="externalcommandprocessor.cpp" />
|
||||
|
@ -45,6 +46,7 @@
|
|||
<ClCompile Include="servicegroup.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="api.h" />
|
||||
<ClInclude Include="checkresultmessage.h" />
|
||||
<ClInclude Include="cib.h" />
|
||||
<ClInclude Include="externalcommandprocessor.h" />
|
||||
|
|
|
@ -51,7 +51,7 @@ PythonInterpreter::~PythonInterpreter(void)
|
|||
PyEval_ReleaseLock();
|
||||
}
|
||||
|
||||
void PythonInterpreter::RegisterFunction(const String& name, PyObject *function)
|
||||
void PythonInterpreter::RegisterPythonFunction(const String& name, PyObject *function)
|
||||
{
|
||||
SubscribeFunction(name);
|
||||
|
||||
|
@ -59,7 +59,7 @@ void PythonInterpreter::RegisterFunction(const String& name, PyObject *function)
|
|||
m_Functions[name] = function;
|
||||
}
|
||||
|
||||
void PythonInterpreter::UnregisterFunction(const String& name)
|
||||
void PythonInterpreter::UnregisterPythonFunction(const String& name)
|
||||
{
|
||||
UnsubscribeFunction(name);
|
||||
|
||||
|
|
|
@ -37,8 +37,8 @@ public:
|
|||
PythonInterpreter(const PythonLanguage::Ptr& language, const Script::Ptr& script);
|
||||
~PythonInterpreter(void);
|
||||
|
||||
void RegisterFunction(const String& name, PyObject *function);
|
||||
void UnregisterFunction(const String& name);
|
||||
void RegisterPythonFunction(const String& name, PyObject *function);
|
||||
void UnregisterPythonFunction(const String& name);
|
||||
|
||||
protected:
|
||||
PythonLanguage::Ptr m_Language;
|
||||
|
|
|
@ -37,9 +37,19 @@ PythonLanguage::PythonLanguage(void)
|
|||
|
||||
m_MainThreadState = PyThreadState_Get();
|
||||
|
||||
PyThreadState_Swap(NULL);
|
||||
m_NativeModule = Py_InitModule("ire", NULL);
|
||||
|
||||
(void) PyThreadState_Swap(NULL);
|
||||
PyEval_ReleaseLock();
|
||||
|
||||
String name;
|
||||
ScriptFunction::Ptr function;
|
||||
BOOST_FOREACH(tie(name, function), ScriptFunction::GetFunctions()) {
|
||||
RegisterNativeFunction(name, function);
|
||||
}
|
||||
|
||||
ScriptFunction::OnRegistered.connect(boost::bind(&PythonLanguage::RegisterNativeFunction, this, _1, _2));
|
||||
ScriptFunction::OnUnregistered.connect(boost::bind(&PythonLanguage::UnregisterNativeFunction, this, _1));
|
||||
}
|
||||
|
||||
PythonLanguage::~PythonLanguage(void)
|
||||
|
@ -58,3 +68,164 @@ PyThreadState *PythonLanguage::GetMainThreadState(void) const
|
|||
{
|
||||
return m_MainThreadState;
|
||||
}
|
||||
|
||||
PyObject *PythonLanguage::MarshalToPython(const Value& value, const ScriptArgumentHint& hint)
|
||||
{
|
||||
String svalue;
|
||||
|
||||
switch (value.GetType()) {
|
||||
case ValueEmpty:
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
|
||||
case ValueNumber:
|
||||
return PyFloat_FromDouble(value);
|
||||
|
||||
case ValueString:
|
||||
svalue = value;
|
||||
return PyString_FromString(svalue.CStr());
|
||||
|
||||
case ValueObject:
|
||||
if (value.IsObjectType<DynamicObject>()) {
|
||||
DynamicObject::Ptr dobj = value;
|
||||
|
||||
String type = dobj->GetType()->GetName();
|
||||
String name = dobj->GetName();
|
||||
|
||||
PyObject *ptype = PyString_FromString(type.CStr());
|
||||
|
||||
if (ptype == NULL)
|
||||
return NULL;
|
||||
|
||||
PyObject *pname = PyString_FromString(name.CStr());
|
||||
|
||||
if (pname == NULL) {
|
||||
Py_DECREF(ptype);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *result = PyTuple_New(2);
|
||||
|
||||
if (result == NULL) {
|
||||
Py_DECREF(ptype);
|
||||
Py_DECREF(pname);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(void) PyTuple_SetItem(result, 0, ptype);
|
||||
(void) PyTuple_SetItem(result, 1, pname);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
|
||||
default:
|
||||
BOOST_THROW_EXCEPTION(invalid_argument("Unexpected variant type."));
|
||||
}
|
||||
}
|
||||
|
||||
Value PythonLanguage::MarshalFromPython(PyObject *value, const ScriptArgumentHint& hint)
|
||||
{
|
||||
if (value == Py_None) {
|
||||
return Empty;
|
||||
} else if (PyTuple_Check(value)) {
|
||||
// TODO: look up object
|
||||
} else if (PyFloat_Check(value)) {
|
||||
return PyFloat_AsDouble(value);
|
||||
} else if (PyString_Check(value)) {
|
||||
return PyString_AsString(value);
|
||||
} else {
|
||||
return Empty;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject *PythonLanguage::CallNativeFunction(PyObject *self, PyObject *args)
|
||||
{
|
||||
assert(PyString_Check(self));
|
||||
|
||||
char *name = PyString_AsString(self);
|
||||
|
||||
ScriptFunction::Ptr function = ScriptFunction::GetByName(name);
|
||||
|
||||
vector<Value> arguments;
|
||||
|
||||
if (args != NULL) {
|
||||
if (PyTuple_Check(args)) {
|
||||
for (Py_ssize_t i = 0; i < PyTuple_Size(args); i++) {
|
||||
PyObject *arg = PyTuple_GetItem(args, i);
|
||||
|
||||
arguments.push_back(MarshalFromPython(arg, function->GetArgumentHint(i)));
|
||||
}
|
||||
} else {
|
||||
arguments.push_back(MarshalFromPython(args, function->GetArgumentHint(0)));
|
||||
}
|
||||
}
|
||||
|
||||
ScriptTask::Ptr task = boost::make_shared<ScriptTask>(function, arguments);
|
||||
task->Start();
|
||||
task->Wait();
|
||||
|
||||
try {
|
||||
Value result = task->GetResult();
|
||||
|
||||
return MarshalToPython(result, function->GetReturnHint());
|
||||
} catch (const std::exception& ex) {
|
||||
String message = diagnostic_information(ex);
|
||||
PyErr_SetString(PyExc_RuntimeError, message.CStr());
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a native function.
|
||||
*
|
||||
* @param name The name of the native function.
|
||||
* @param function The function.
|
||||
*/
|
||||
void PythonLanguage::RegisterNativeFunction(const String& name, const ScriptFunction::Ptr& function)
|
||||
{
|
||||
(void) PyThreadState_Swap(m_MainThreadState);
|
||||
|
||||
PyObject *pname = PyString_FromString(name.CStr());
|
||||
|
||||
PyMethodDef *md = new PyMethodDef;
|
||||
md->ml_name = strdup(name.CStr());
|
||||
md->ml_meth = &PythonLanguage::CallNativeFunction;
|
||||
md->ml_flags = METH_VARARGS;
|
||||
md->ml_doc = NULL;
|
||||
|
||||
PyObject *pfunc = PyCFunction_NewEx(md, pname, m_NativeModule);
|
||||
(void) PyModule_AddObject(m_NativeModule, name.CStr(), pfunc);
|
||||
|
||||
(void) PyThreadState_Swap(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a native function.
|
||||
*
|
||||
* @param name The name of the native function.
|
||||
*/
|
||||
void PythonLanguage::UnregisterNativeFunction(const String& name)
|
||||
{
|
||||
(void) PyThreadState_Swap(m_MainThreadState);
|
||||
|
||||
PyObject *pdict = PyModule_GetDict(m_NativeModule);
|
||||
PyObject *pname = PyString_FromString(name.CStr());
|
||||
PyCFunctionObject *pfunc = (PyCFunctionObject *)PyDict_GetItem(pdict, pname);
|
||||
|
||||
if (pfunc && PyCFunction_Check(pfunc)) {
|
||||
/* Eww. */
|
||||
free(const_cast<char *>(pfunc->m_ml->ml_name));
|
||||
delete pfunc->m_ml;
|
||||
}
|
||||
|
||||
(void) PyDict_DelItem(pdict, pname);
|
||||
Py_DECREF(pname);
|
||||
|
||||
(void) PyThreadState_Swap(NULL);
|
||||
}
|
||||
|
|
|
@ -40,8 +40,18 @@ public:
|
|||
virtual ScriptInterpreter::Ptr CreateInterpreter(const Script::Ptr& script);
|
||||
|
||||
PyThreadState *GetMainThreadState(void) const;
|
||||
|
||||
private:
|
||||
PyThreadState *m_MainThreadState;
|
||||
PyObject *m_NativeModule;
|
||||
|
||||
void RegisterNativeFunction(const String& name, const ScriptFunction::Ptr& function);
|
||||
void UnregisterNativeFunction(const String& name);
|
||||
|
||||
static PyObject *CallNativeFunction(PyObject *self, PyObject *args);
|
||||
|
||||
static PyObject *MarshalToPython(const Value& value, const ScriptArgumentHint& hint);
|
||||
static Value MarshalFromPython(PyObject *value, const ScriptArgumentHint& hint);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue