source: nscp/modules/PythonScript/PythonScript.cpp @ 393a00f

0.4.2
Last change on this file since 393a00f was 393a00f, checked in by Michael Medin <michael@…>, 11 months ago
  • Improved settings API with new protocolbuffers command (available from python)
  • Improved registration API with new protocolbuffers command (available from python)
  • Created new python documentation module docs.py
  • Improved command line syntax so executable commands now take an optional module prefix
  • Property mode set to 100644
File size: 18.3 KB
Line 
1/**************************************************************************
2*   Copyright (C) 2004-2007 by Michael Medin <michael@medin.name>         *
3*                                                                         *
4*   This code is part of NSClient++ - http://trac.nakednuns.org/nscp      *
5*                                                                         *
6*   This program is free software; you can redistribute it and/or modify  *
7*   it under the terms of the GNU General Public License as published by  *
8*   the Free Software Foundation; either version 2 of the License, or     *
9*   (at your option) any later version.                                   *
10*                                                                         *
11*   This program is distributed in the hope that it will be useful,       *
12*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14*   GNU General Public License for more details.                          *
15*                                                                         *
16*   You should have received a copy of the GNU General Public License     *
17*   along with this program; if not, write to the                         *
18*   Free Software Foundation, Inc.,                                       *
19*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
20***************************************************************************/
21#include "stdafx.h"
22#include "PythonScript.h"
23#include <time.h>
24#include <error.hpp>
25
26#include <boost/python.hpp>
27#include <boost/program_options.hpp>
28
29#include <strEx.h>
30#include <file_helpers.hpp>
31#include <settings/client/settings_client.hpp>
32#include <nscapi/functions.hpp>
33
34#include "script_wrapper.hpp"
35
36PythonScript::PythonScript() {
37}
38PythonScript::~PythonScript() {
39}
40
41namespace sh = nscapi::settings_helper;
42namespace po = boost::program_options;
43
44bool PythonScript::loadModule() {
45        return false;
46}
47using namespace boost::python;
48
49BOOST_PYTHON_MODULE(NSCP)
50{
51        class_<script_wrapper::settings_wrapper, boost::shared_ptr<script_wrapper::settings_wrapper> >("Settings", no_init)
52                .def("get",&script_wrapper::settings_wrapper::create)
53                .staticmethod("get")
54                .def("create",&script_wrapper::settings_wrapper::create)
55                .staticmethod("create")
56                .def("get_section", &script_wrapper::settings_wrapper::get_section)
57                .def("get_string", &script_wrapper::settings_wrapper::get_string)
58                .def("set_string", &script_wrapper::settings_wrapper::set_string)
59                .def("get_bool", &script_wrapper::settings_wrapper::get_bool)
60                .def("set_bool", &script_wrapper::settings_wrapper::set_bool)
61                .def("get_int", &script_wrapper::settings_wrapper::get_int)
62                .def("set_int", &script_wrapper::settings_wrapper::set_int)
63                .def("save", &script_wrapper::settings_wrapper::save)
64                .def("register_path", &script_wrapper::settings_wrapper::settings_register_path)
65                .def("register_key", &script_wrapper::settings_wrapper::settings_register_key)
66                .def("query", &script_wrapper::settings_wrapper::query)
67                ;
68        class_<script_wrapper::function_wrapper, boost::shared_ptr<script_wrapper::function_wrapper> >("Registry", no_init)
69                .def("get",&script_wrapper::function_wrapper::create)
70                .staticmethod("get")
71                .def("create",&script_wrapper::function_wrapper::create)
72                .staticmethod("create")
73                .def("function", &script_wrapper::function_wrapper::register_function)
74                .def("simple_function", &script_wrapper::function_wrapper::register_simple_function)
75                .def("cmdline", &script_wrapper::function_wrapper::register_cmdline)
76                .def("simple_cmdline", &script_wrapper::function_wrapper::register_simple_cmdline)
77                .def("subscription", &script_wrapper::function_wrapper::subscribe_function)
78                .def("simple_subscription", &script_wrapper::function_wrapper::subscribe_simple_function)
79                .def("query", &script_wrapper::function_wrapper::query)
80                ;
81        class_<script_wrapper::command_wrapper, boost::shared_ptr<script_wrapper::command_wrapper> >("Core", init<>())
82                .def("get",&script_wrapper::command_wrapper::create)
83                .staticmethod("get")
84                .def("create",&script_wrapper::command_wrapper::create)
85                .staticmethod("create")
86                .def("simple_query", &script_wrapper::command_wrapper::simple_query)
87                .def("query", &script_wrapper::command_wrapper::query)
88                .def("simple_exec", &script_wrapper::command_wrapper::simple_exec)
89                .def("exec", &script_wrapper::command_wrapper::exec)
90                .def("simple_submit", &script_wrapper::command_wrapper::simple_submit)
91                .def("submit", &script_wrapper::command_wrapper::submit)
92                .def("reload", &script_wrapper::command_wrapper::reload)
93                .def("expand_path", &script_wrapper::command_wrapper::expand_path)
94                ;
95
96        enum_<script_wrapper::status>("status")
97                .value("CRITICAL", script_wrapper::CRIT)
98                .value("WARNING", script_wrapper::WARN)
99                .value("UNKNOWN", script_wrapper::UNKNOWN)
100                .value("OK", script_wrapper::OK)
101                ;
102        def("log", script_wrapper::log_msg);
103        def("log_err", script_wrapper::log_error);
104        def("log_deb", script_wrapper::log_debug);
105        def("log_error", script_wrapper::log_error);
106        def("log_debug", script_wrapper::log_debug);
107        def("sleep", script_wrapper::sleep);
108//      def("get_module_alias", script_wrapper::get_module_alias);
109//      def("get_script_alias", script_wrapper::get_script_alias);
110}
111
112python_script::python_script(unsigned int plugin_id, const std::string alias, const script_container& script)
113        : alias(alias)
114        , plugin_id(plugin_id) {
115        NSC_DEBUG_MSG_STD(_T("Loading python script: ") + script.script.string());
116        std::wstring err;
117        if (!script.validate(err)) {
118                NSC_LOG_ERROR(err);
119                return;
120        }
121        _exec(utf8::cvt<std::string>(script.script.file_string()));
122        callFunction("init", plugin_id, alias, utf8::cvt<std::string>(script.alias));
123}
124python_script::~python_script(){
125        callFunction("shutdown");
126}
127bool python_script::callFunction(const std::string& functionName) {
128        try {
129                script_wrapper::thread_locker locker;
130                try     {
131                        if (!localDict.has_key(functionName))
132                                return true;
133                        object scriptFunction = extract<object>(localDict[functionName]);
134                        if( scriptFunction )
135                                scriptFunction();
136                        return true;
137                } catch( error_already_set e) {
138                        script_wrapper::log_exception();
139                        return false;
140                }
141        } catch (...) {
142                NSC_LOG_ERROR(_T("Unknown exception"));
143                return false;
144        }
145}
146bool python_script::callFunction(const std::string& functionName, const std::vector<std::wstring> args) {
147        try {
148                script_wrapper::thread_locker locker;
149                try     {
150                        if (!localDict.has_key(functionName))
151                                return true;
152                        object scriptFunction = extract<object>(localDict[functionName]);
153                        if (scriptFunction)
154                                scriptFunction(script_wrapper::convert(args));
155                        return true;
156                } catch( error_already_set e) {
157                        script_wrapper::log_exception();
158                        return false;
159                }
160        } catch (...) {
161                NSC_LOG_ERROR(_T("Unknown exception"));
162                return false;
163        }
164}
165bool python_script::callFunction(const std::string& functionName, unsigned int i1, const std::string &s1, const std::string &s2){
166        try {
167                script_wrapper::thread_locker locker;
168                try     {
169                        if (!localDict.has_key(functionName))
170                                return true;
171                        object scriptFunction = extract<object>(localDict[functionName]);
172                        if(scriptFunction)
173                                scriptFunction(i1, s1, s2);
174                        return true;
175                } catch(error_already_set e) {
176                        script_wrapper::log_exception();
177                        return false;
178                }
179        } catch (...) {
180                NSC_LOG_ERROR(_T("Unknown exception"));
181                return false;
182        }
183}
184void python_script::_exec(const std::string &scriptfile){
185        try {
186                script_wrapper::thread_locker locker;
187                try     {
188                        object main_module = import("__main__");
189                        dict globalDict = extract<dict>(main_module.attr("__dict__"));
190                        localDict = globalDict.copy();
191                        //localDict.attr("plugin_id") = plugin_id;
192                        //localDict.attr("plugin_alias") = alias;
193
194                        PyRun_SimpleString("import cStringIO");
195                        PyRun_SimpleString("import sys");
196                        PyRun_SimpleString("sys.stderr = cStringIO.StringIO()");
197                        boost::filesystem::wpath path = GET_CORE()->getBasePath();
198                        path /= _T("scripts");
199                        path /= _T("python");
200                        path /= _T("lib");
201                        NSC_DEBUG_MSG(_T("Lib path: ") + path.string());
202                        PyRun_SimpleString(("sys.path.append('" + utf8::cvt<std::string>(path.string()) + "')").c_str());
203
204                        object ignored = exec_file(scriptfile.c_str(), localDict, localDict);   
205                } catch( error_already_set e) {
206                        script_wrapper::log_exception();
207                } catch(...) {
208                        NSC_LOG_ERROR(_T("Failed to run python script: ") + utf8::to_unicode(scriptfile));
209                }
210        } catch (...) {
211                NSC_LOG_ERROR(_T("Unknown exception"));
212        }
213}
214static bool has_init = false;
215bool PythonScript::loadModuleEx(std::wstring alias, NSCAPI::moduleLoadMode mode) {
216        alias_ = alias;
217        NSC_DEBUG_MSG_STD(_T("LoadEx in PythonScript as ") + alias);
218        try {
219                root_ = get_core()->getBasePath();
220
221                sh::settings_registry settings(get_settings_proxy());
222                settings.set_alias(alias, _T("python"));
223
224                settings.alias().add_path_to_settings()
225                        (_T("LUA SCRIPT SECTION"), _T("Section for the PythonScripts module."))
226
227                        (_T("scripts"), sh::fun_values_path(boost::bind(&PythonScript::loadScript, this, _1, _2)),
228                        _T("LUA SCRIPTS SECTION"), _T("A list of scripts available to run from the PythonScript module."))
229                        ;
230
231                settings.register_all();
232                settings.notify();
233
234                bool do_init = false;
235                if (!has_init) {
236                        has_init = true;
237                        Py_Initialize();
238                        PyEval_InitThreads();
239                        //PyEval_ReleaseLock();
240
241                        PyEval_SaveThread();
242                        do_init = true;
243                }
244
245                try {
246                        {
247                                script_wrapper::thread_locker locker;
248                                try {
249
250                                        PyRun_SimpleString("import cStringIO");
251                                        PyRun_SimpleString("import sys");
252                                        PyRun_SimpleString("sys.stderr = cStringIO.StringIO()");
253
254                                        if (do_init)
255                                                initNSCP();
256
257                                } catch( error_already_set e) {
258                                        script_wrapper::log_exception();
259                                }
260
261                        }
262                        //PyEval_ReleaseLock();
263                        BOOST_FOREACH(script_container &script, scripts_) {
264                                instances_.push_back(boost::shared_ptr<python_script>(new python_script(get_id(), utf8::cvt<std::string>(alias), script)));
265                        }
266
267                } catch (std::exception &e) {
268                        NSC_LOG_ERROR_STD(_T("Exception: Failed to load python scripts: ") + utf8::cvt<std::wstring>(e.what()));
269                } catch (...) {
270                        NSC_LOG_ERROR_STD(_T("Exception: Failed to load python scripts"));
271                }
272        } catch (...) {
273                NSC_LOG_ERROR_STD(_T("Exception caught: <UNKNOWN EXCEPTION>"));
274                return false;
275        }
276        return true;
277}
278
279boost::optional<boost::filesystem::wpath> PythonScript::find_file(std::wstring file) {
280        std::list<boost::filesystem::wpath> checks;
281        checks.push_back(file);
282        checks.push_back((file + _T(".py")));
283        checks.push_back(root_ / _T("scripts") / _T("python") / file);
284        checks.push_back(root_ / _T("scripts") / _T("python") / (file + _T(".py")));
285        checks.push_back(root_ / _T("scripts") / file);
286        checks.push_back(root_ / _T("scripts") / (file + _T(".py")));
287        checks.push_back(root_ / file);
288        BOOST_FOREACH(boost::filesystem::wpath c, checks) {
289                NSC_DEBUG_MSG_STD(_T("Looking for: ") + c.string());
290                if (boost::filesystem::exists(c))
291                        return boost::optional<boost::filesystem::wpath>(c);
292        }
293        NSC_LOG_ERROR(_T("Script not found: ") + file);
294        return boost::optional<boost::filesystem::wpath>();
295}
296
297NSCAPI::nagiosReturn PythonScript::execute_and_load_python(std::list<std::wstring> args, std::wstring &message) {
298        try {
299                po::options_description desc("Options for the following commands: (exec, execute)");
300                boost::program_options::variables_map vm;
301                std::wstring file;
302                desc.add_options()
303                        ("help", "Display help")
304                        ("script", po::wvalue<std::wstring>(&file), "The script to run")
305                        ("file", po::wvalue<std::wstring>(&file), "The script to run")
306                        ;
307
308                std::vector<std::wstring> vargs(args.begin(), args.end());
309                po::wparsed_options parsed = po::basic_command_line_parser<wchar_t>(vargs).options(desc).allow_unregistered().run();
310                po::store(parsed, vm);
311                po::notify(vm);
312
313                if (vm.count("help") > 0) {
314                        std::stringstream ss;
315                        ss << desc;
316                        message = utf8::to_unicode(ss.str());
317                        return NSCAPI::returnUNKNOWN;
318                }
319                std::vector<std::wstring> py_args = po::collect_unrecognized(parsed.options, po::include_positional);
320
321                boost::optional<boost::filesystem::wpath> ofile = find_file(file);
322                if (!ofile)
323                        return false;
324                script_container sc(*ofile);
325                python_script script(get_id(), "", sc);
326                if (!script.callFunction("__main__", py_args)) {
327                        message = _T("Failed to execute script: __main__");
328                        return NSCAPI::returnUNKNOWN;
329                }
330                message = _T("Script execute successfully...");
331                return NSCAPI::returnOK;
332        } catch (const std::exception &e) {
333                message = _T("Failed to execute script ") + utf8::to_unicode(e.what());
334                NSC_LOG_ERROR_STD(message);
335                return NSCAPI::returnUNKNOWN;
336        } catch (...) {
337                message = _T("Failed to execute script ");
338                NSC_LOG_ERROR_STD(message);
339                return NSCAPI::returnUNKNOWN;
340        }
341}
342
343bool PythonScript::loadScript(std::wstring alias, std::wstring file) {
344        try {
345                if (file.empty()) {
346                        file = alias;
347                        alias = _T("");
348                }
349                boost::optional<boost::filesystem::wpath> ofile = find_file(file);
350                if (!ofile)
351                        return false;
352                script_container::push(scripts_, alias, *ofile);
353                NSC_DEBUG_MSG_STD(_T("Adding script: ") + alias + _T(" (") + ofile->string() + _T(")"));
354                return true;
355        } catch (...) {
356                NSC_LOG_ERROR_STD(_T("Could not find script: (Unknown exception) ") + file);
357        }
358        return false;
359}
360
361
362bool PythonScript::unloadModule() {
363        instances_.clear();
364        //Py_Finalize();
365        return true;
366}
367
368bool PythonScript::hasCommandHandler() {
369        return true;
370}
371bool PythonScript::hasMessageHandler() {
372        return true;
373}
374bool PythonScript::hasNotificationHandler() {
375        return true;
376}
377
378bool PythonScript::reload(std::wstring &message) {
379        /*
380        bool error = false;
381        commands_.clear();
382        for (script_list::const_iterator cit = scripts_.begin(); cit != scripts_.end() ; ++cit) {
383                try {
384                        (*cit)->reload(this);
385                } catch (script_wrapper::LUAException e) {
386                        error = true;
387                        message += _T("Exception when reloading script: ") + (*cit)->get_script() + _T(": ") + e.getMessage();
388                        NSC_LOG_ERROR_STD(_T("Exception when reloading script: ") + (*cit)->get_script() + _T(": ") + e.getMessage());
389                } catch (...) {
390                        error = true;
391                        message += _T("Unhandeled Exception when reloading script: ") + (*cit)->get_script();
392                        NSC_LOG_ERROR_STD(_T("Unhandeled Exception when reloading script: ") + (*cit)->get_script());
393                }
394        }
395        if (!error)
396                message = _T("LUA scripts Reloaded...");
397        return !error;
398        */
399        return false;
400}
401
402NSCAPI::nagiosReturn PythonScript::commandRAWLineExec(const wchar_t* char_command, const std::string &request, std::string &response) {
403        std::wstring command = char_command;
404        if (command == _T("help")) {
405                std::list<std::wstring> args;
406                args.push_back(_T("--help"));
407                std::wstring result;
408                int ret = execute_and_load_python(args, result);
409                nscapi::functions::create_simple_exec_response<std::wstring>(_T("help"), ret, result, response);
410                return ret;
411        } else if (command == _T("execute-and-load-python") || command == _T("execute-python") || command == _T("python-script")
412                || command == _T("run") || command == _T("execute") || command == _T("exec") || command == _T("")) {
413                nscapi::functions::decoded_simple_command_data data = nscapi::functions::parse_simple_exec_request(char_command, request);
414                if (data.command == _T("") && data.args.size() == 0)
415                        return NSCAPI::returnIgnored;
416                std::wstring result;
417                int ret = execute_and_load_python(data.args, result);
418                nscapi::functions::create_simple_exec_response(data.command, ret, result, response);
419                return ret;
420        }
421        boost::shared_ptr<script_wrapper::function_wrapper> inst = script_wrapper::function_wrapper::create(get_id());
422        std::string cmd = utf8::cvt<std::string>(char_command);
423        if (inst->has_cmdline(cmd)) {
424                return inst->handle_exec(cmd, request, response);
425        }
426        if (inst->has_simple_cmdline(cmd)) {
427                nscapi::functions::decoded_simple_command_data data = nscapi::functions::parse_simple_exec_request(char_command, request);
428                std::wstring result;
429                NSCAPI::nagiosReturn ret = inst->handle_simple_exec(cmd, data.args, result);
430                nscapi::functions::create_simple_exec_response(data.command, ret, result, response);
431                return ret;
432        }
433        return NSCAPI::returnIgnored;
434}
435
436
437NSCAPI::nagiosReturn PythonScript::handleRAWCommand(const wchar_t* command, const std::string &request, std::string &response) {
438        boost::shared_ptr<script_wrapper::function_wrapper> inst = script_wrapper::function_wrapper::create(get_id());
439        std::string cmd = utf8::cvt<std::string>(command);
440        if (inst->has_function(cmd)) {
441                return inst->handle_query(cmd, request, response);
442        }
443        if (inst->has_simple(cmd)) {
444                nscapi::functions::decoded_simple_command_data data = nscapi::functions::parse_simple_query_request(command, request);
445                std::wstring msg, perf;
446                NSCAPI::nagiosReturn ret = inst->handle_simple_query(cmd, data.args, msg, perf);
447                nscapi::functions::create_simple_query_response(data.command, ret, msg, perf, response);
448                return ret;
449        }
450        NSC_LOG_ERROR_STD(_T("Could not find python commands for: ") + command + _T(" (avalible python commands are: ") + inst->get_commands() + _T(")"));
451        /*
452        if (command == _T("luareload")) {
453                return reload(message)?NSCAPI::returnOK:NSCAPI::returnCRIT;
454        }
455        */
456        return NSCAPI::returnIgnored;
457}
458
459
460NSCAPI::nagiosReturn PythonScript::handleRAWNotification(const std::wstring &channel, std::string &request, std::string &response) {
461        boost::shared_ptr<script_wrapper::function_wrapper> inst = script_wrapper::function_wrapper::create(get_id());
462        std::string chnl = utf8::cvt<std::string>(channel);
463        if (inst->has_message_handler(chnl)) {
464                NSCAPI::nagiosReturn ret = inst->handle_message(chnl, request, response);
465                if (ret != NSCAPI::returnIgnored)
466                        return ret;
467        }
468        if (inst->has_simple_message_handler(chnl)) {
469                std::wstring src, cmd, msg, perf;
470                int code = nscapi::functions::parse_simple_submit_request(request, src, cmd, msg, perf);
471                int ret = inst->handle_simple_message(chnl, utf8::cvt<std::string>(src), utf8::cvt<std::string>(cmd), code, msg, perf);
472                nscapi::functions::create_simple_submit_response(channel, cmd, ret, _T(""), response);
473                return ret;
474        }
475        return NSCAPI::returnIgnored;
476}
477
478NSC_WRAP_DLL()
479NSC_WRAPPERS_MAIN_DEF(PythonScript, _T("python"))
480NSC_WRAPPERS_IGNORE_MSG_DEF()
481NSC_WRAPPERS_HANDLE_CMD_DEF()
482NSC_WRAPPERS_CLI_DEF()
483NSC_WRAPPERS_HANDLE_NOTIFICATION_DEF()
Note: See TracBrowser for help on using the repository browser.