Back to index

nordugrid-arc-nox  1.1.0~rc6
PythonBroker.cpp
Go to the documentation of this file.
00001 // -*- indent-tabs-mode: nil -*-
00002 
00003 #ifdef HAVE_CONFIG_H
00004 #include <config.h>
00005 #endif
00006 
00007 #include "PythonBroker.h"
00008 
00009 extern "C" {
00010 typedef struct {
00011   PyObject_HEAD
00012   void *ptr;
00013   // There are more members in this stuct, but they are not needed here...
00014 } PySwigObject;
00015 }
00016 
00017 namespace Arc {
00018 
00019   Logger PythonBroker::logger(Broker::logger, "PythonBroker");
00020   PyThreadState*PythonBroker::tstate = NULL;
00021   int PythonBroker::refcount = 0;
00022   Glib::Mutex PythonBroker::lock;
00023 
00024   class PythonLock {
00025   public:
00026     PythonLock() {
00027       gstate = PyGILState_Ensure();
00028     }
00029     ~PythonLock() {
00030       PyGILState_Release(gstate);
00031     }
00032   private:
00033     PyGILState_STATE gstate;
00034   };
00035 
00036   class PyObjectP {
00037   public:
00038     PyObjectP(PyObject *obj)
00039       : obj(obj) {}
00040     ~PyObjectP() {
00041       if (obj)
00042         Py_DECREF(obj);
00043     }
00044     operator bool() {
00045       return obj;
00046     }
00047     bool operator!() {
00048       return !obj;
00049     }
00050     operator PyObject*() {
00051       return obj;
00052     }
00053   private:
00054     PyObject *obj;
00055   };
00056 
00057   Plugin* PythonBroker::Instance(PluginArgument *arg) {
00058 
00059     BrokerPluginArgument *brokerarg = dynamic_cast<BrokerPluginArgument*>(arg);
00060     if (!brokerarg)
00061       return NULL;
00062 
00063     lock.lock();
00064 
00065     // Initialize the Python Interpreter
00066     if (!Py_IsInitialized()) {
00067 #ifdef HAVE_PYTHON_INITIALIZE_EX
00068       Py_InitializeEx(0);            // Python does not handle signals
00069 #endif
00070       PyEval_InitThreads();          // Main thread created and lock acquired
00071       tstate = PyThreadState_Get();  // Get current thread
00072       if (!tstate) {
00073         logger.msg(ERROR, "Failed to initialize main Python thread");
00074         return NULL;
00075       }
00076     }
00077     else {
00078       if (!tstate) {
00079         logger.msg(ERROR, "Main Python thread was not initialized");
00080         return NULL;
00081       }
00082       PyEval_AcquireThread(tstate);
00083     }
00084 
00085     refcount++;
00086 
00087     lock.unlock();
00088 
00089     logger.msg(DEBUG, "Loading python broker (%i)", refcount);
00090 
00091     Broker *broker = new PythonBroker(*brokerarg);
00092 
00093     PyEval_ReleaseThread(tstate); // Release current thread
00094 
00095     return broker;
00096   }
00097 
00098   PythonBroker::PythonBroker(const UserConfig& usercfg)
00099     : Broker(usercfg),
00100       arc_module(NULL),
00101       arc_userconfig_klass(NULL),
00102       arc_jobrepr_klass(NULL),
00103       arc_xtarget_klass(NULL),
00104       module(NULL),
00105       klass(NULL),
00106       object(NULL) {
00107 
00108     if (!tstate) {
00109       logger.msg(ERROR, "Main python thread is not initialized");
00110       return;
00111     }
00112 
00113     logger.msg(VERBOSE, "PythonBroker init");
00114 
00115     std::string args = usercfg.Broker().second;
00116     std::string::size_type pos = args.find(':');
00117     if (pos != std::string::npos)
00118       args.resize(pos);
00119     pos = args.rfind('.');
00120     if (pos == std::string::npos) {
00121       logger.msg(ERROR, "Invalid class name");
00122       return;
00123     }
00124     std::string module_name = args.substr(0, pos);
00125     std::string class_name = args.substr(pos + 1);
00126     logger.msg(VERBOSE, "class name: %s", class_name);
00127     logger.msg(VERBOSE, "module name: %s", module_name);
00128 
00129     // Import arc python module
00130     PyObjectP py_arc_module_name = PyString_FromString("arc");
00131     if (!py_arc_module_name) {
00132       logger.msg(ERROR, "Cannot convert arc module name to Python string");
00133       if (PyErr_Occurred())
00134         PyErr_Print();
00135       return;
00136     }
00137 
00138     arc_module = PyImport_Import(py_arc_module_name);
00139     if (!arc_module) {
00140       logger.msg(ERROR, "Cannot import arc module");
00141       if (PyErr_Occurred())
00142         PyErr_Print();
00143       return;
00144     }
00145 
00146     // Get dictionary of arc module content (borrowed reference)
00147     PyObject *arc_dict = PyModule_GetDict(arc_module);
00148     if (!arc_dict) {
00149       logger.msg(ERROR, "Cannot get dictionary of arc module");
00150       if (PyErr_Occurred())
00151         PyErr_Print();
00152       return;
00153     }
00154 
00155     // Get the Config class (borrowed reference)
00156     arc_userconfig_klass = PyDict_GetItemString(arc_dict, "UserConfig");
00157     if (!arc_userconfig_klass) {
00158       logger.msg(ERROR, "Cannot find arc UserConfig class");
00159       if (PyErr_Occurred())
00160         PyErr_Print();
00161       return;
00162     }
00163 
00164     // check is it really a class
00165     if (!PyCallable_Check(arc_userconfig_klass)) {
00166       logger.msg(ERROR, "UserConfig class is not an object");
00167       return;
00168     }
00169 
00170     // Get the JobDescription class (borrowed reference)
00171     arc_jobrepr_klass = PyDict_GetItemString(arc_dict, "JobDescription");
00172     if (!arc_jobrepr_klass) {
00173       logger.msg(ERROR, "Cannot find arc JobDescription class");
00174       if (PyErr_Occurred())
00175         PyErr_Print();
00176       return;
00177     }
00178 
00179     // check is it really a class
00180     if (!PyCallable_Check(arc_jobrepr_klass)) {
00181       logger.msg(ERROR, "JobDescription class is not an object");
00182       return;
00183     }
00184 
00185     // Get the ExecutionTarget class (borrowed reference)
00186     arc_xtarget_klass = PyDict_GetItemString(arc_dict, "ExecutionTarget");
00187     if (!arc_xtarget_klass) {
00188       logger.msg(ERROR, "Cannot find arc ExecutionTarget class");
00189       if (PyErr_Occurred())
00190         PyErr_Print();
00191       return;
00192     }
00193 
00194     // check is it really a class
00195     if (!PyCallable_Check(arc_xtarget_klass)) {
00196       logger.msg(ERROR, "ExecutionTarget class is not an object");
00197       return;
00198     }
00199 
00200     // Import custom broker module
00201     PyObjectP py_module_name = PyString_FromString(module_name.c_str());
00202     if (!py_module_name) {
00203       logger.msg(ERROR, "Cannot convert module name to Python string");
00204       if (PyErr_Occurred())
00205         PyErr_Print();
00206       return;
00207     }
00208 
00209     module = PyImport_Import(py_module_name);
00210     if (!module) {
00211       logger.msg(ERROR, "Cannot import module");
00212       if (PyErr_Occurred())
00213         PyErr_Print();
00214       return;
00215     }
00216 
00217     // Get dictionary of module content (borrowed reference)
00218     PyObject *dict = PyModule_GetDict(module);
00219     if (!dict) {
00220       logger.msg(ERROR, "Cannot get dictionary of custom broker module");
00221       if (PyErr_Occurred())
00222         PyErr_Print();
00223       return;
00224     }
00225 
00226     // Get the class (borrowed reference)
00227     klass = PyDict_GetItemString(dict, (char*)class_name.c_str());
00228     if (!klass) {
00229       logger.msg(ERROR, "Cannot find custom broker class");
00230       if (PyErr_Occurred())
00231         PyErr_Print();
00232       return;
00233     }
00234 
00235     // check is it really a class
00236     if (!PyCallable_Check(klass)) {
00237       logger.msg(ERROR, "%s class is not an object", class_name);
00238       return;
00239     }
00240 
00241     PyObjectP usercfgarg = Py_BuildValue("(l)", (long int)usercfg);
00242     if (!usercfgarg) {
00243       logger.msg(ERROR, "Cannot create UserConfig argument");
00244       if (PyErr_Occurred())
00245         PyErr_Print();
00246       return;
00247     }
00248 
00249     PyObject *py_usercfg = PyObject_CallObject(arc_userconfig_klass, usercfgarg);
00250     if (!py_usercfg) {
00251       logger.msg(ERROR, "Cannot convert UserConfig to python object");
00252       if (PyErr_Occurred())
00253         PyErr_Print();
00254       return;
00255     }
00256 
00257     PyObjectP arg = Py_BuildValue("(O)", py_usercfg);
00258     if (!arg) {
00259       logger.msg(ERROR, "Cannot create argument of the constructor");
00260       if (PyErr_Occurred())
00261         PyErr_Print();
00262       return;
00263     }
00264 
00265     // create instance of class
00266     object = PyObject_CallObject(klass, arg);
00267     if (!object) {
00268       logger.msg(ERROR, "Cannot create instance of python class");
00269       if (PyErr_Occurred())
00270         PyErr_Print();
00271       return;
00272     }
00273 
00274     logger.msg(VERBOSE, "Python broker constructor called (%d)", refcount);
00275   }
00276 
00277   PythonBroker::~PythonBroker() {
00278 
00279     if (module)
00280       Py_DECREF(module);
00281     if (arc_module)
00282       Py_DECREF(arc_module);
00283 
00284     lock.lock();
00285 
00286     refcount--;
00287 
00288     // Finish the Python Interpreter
00289     if (refcount == 0) {
00290       PyEval_AcquireThread(tstate);
00291       Py_Finalize();
00292     }
00293 
00294     lock.unlock();
00295 
00296     logger.msg(VERBOSE, "Python broker destructor called (%d)", refcount);
00297   }
00298 
00299   void PythonBroker::SortTargets() {
00300 
00301     PythonLock pylock;
00302 
00303     // Convert JobDescription to python object
00304     PyObjectP arg = Py_BuildValue("(l)", job);
00305     if (!arg) {
00306       logger.msg(ERROR, "Cannot create JobDescription argument");
00307       if (PyErr_Occurred())
00308         PyErr_Print();
00309       return;
00310     }
00311 
00312     PyObjectP py_job = PyObject_CallObject(arc_jobrepr_klass, arg);
00313     if (!py_job) {
00314       logger.msg(ERROR,
00315                  "Cannot convert JobDescription to python object");
00316       if (PyErr_Occurred())
00317         PyErr_Print();
00318       return;
00319     }
00320 
00321     // Convert incoming PossibleTargets to python list
00322     PyObjectP py_list = PyList_New(0);
00323     if (!py_list) {
00324       logger.msg(ERROR, "Cannot create python list");
00325       if (PyErr_Occurred())
00326         PyErr_Print();
00327       return;
00328     }
00329 
00330     for (std::list<ExecutionTarget*>::iterator it = PossibleTargets.begin();
00331          it != PossibleTargets.end(); it++) {
00332 
00333       PyObjectP arg = Py_BuildValue("(l)", (long int)&**it);
00334       if (!arg) {
00335         logger.msg(ERROR, "Cannot create ExecutionTarget argument");
00336         if (PyErr_Occurred())
00337           PyErr_Print();
00338         return;
00339       }
00340 
00341       PyObject *py_xtarget = PyObject_CallObject(arc_xtarget_klass, arg);
00342       if (!py_xtarget) {
00343         logger.msg(ERROR, "Cannot convert ExecutionTarget to python object");
00344         if (PyErr_Occurred())
00345           PyErr_Print();
00346         return;
00347       }
00348 
00349       PyList_Append(py_list, py_xtarget);
00350     }
00351 
00352     PyObjectP py_status = PyObject_CallMethod(object, (char*)"SortTargets",
00353                                               (char*)"(OO)",
00354                                               (PyObject*)py_list,
00355                                               (PyObject*)py_job);
00356     if (!py_status) {
00357       if (PyErr_Occurred())
00358         PyErr_Print();
00359       return;
00360     }
00361 
00362     PossibleTargets.clear();
00363 
00364     for (int i = 0; i < PyList_Size(py_list); i++) {
00365       PyObject *obj = PyList_GetItem(py_list, i);
00366       char this_str[] = "this";
00367       if (!PyObject_HasAttrString(obj, this_str))
00368         return;
00369       PyObject *thisattr = PyObject_GetAttrString(obj, this_str);
00370       if (!thisattr)
00371         return;
00372       void *ptr = ((PySwigObject*)thisattr)->ptr;
00373       PossibleTargets.push_back(((ExecutionTarget*)ptr));
00374       Py_DECREF(thisattr);
00375     }
00376 
00377     TargetSortingDone = true;
00378 
00379     return;
00380   }
00381 
00382 } // namespace Arc
00383 
00384 Arc::PluginDescriptor PLUGINS_TABLE_NAME[] = {
00385   { "PythonBroker", "HED:Broker", 0, &Arc::PythonBroker::Instance },
00386   { NULL, NULL, 0, NULL }
00387 };