pbs_swigify 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. #!/usr/bin/env python
  2. # coding: utf-8
  3. # Copyright (C) 1994-2018 Altair Engineering, Inc.
  4. # For more information, contact Altair at www.altair.com.
  5. #
  6. # This file is part of the PBS Professional ("PBS Pro") software.
  7. #
  8. # Open Source License Information:
  9. #
  10. # PBS Pro is free software. You can redistribute it and/or modify it under the
  11. # terms of the GNU Affero General Public License as published by the Free
  12. # Software Foundation, either version 3 of the License, or (at your option) any
  13. # later version.
  14. #
  15. # PBS Pro is distributed in the hope that it will be useful, but WITHOUT ANY
  16. # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  17. # FOR A PARTICULAR PURPOSE.
  18. # See the GNU Affero General Public License for more details.
  19. #
  20. # You should have received a copy of the GNU Affero General Public License
  21. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. #
  23. # Commercial License Information:
  24. #
  25. # For a copy of the commercial license terms and conditions,
  26. # go to: (http://www.pbspro.com/UserArea/agreement.html)
  27. # or contact the Altair Legal Department.
  28. #
  29. # Altair’s dual-license business model allows companies, individuals, and
  30. # organizations to create proprietary derivative works of PBS Pro and
  31. # distribute them - whether embedded or bundled with other software -
  32. # under a commercial license agreement.
  33. #
  34. # Use of Altair’s trademarks, including but not limited to "PBS™",
  35. # "PBS Professional®", and "PBS Pro™" and Altair’s logos is subject to Altair's
  36. # trademark licensing policies.
  37. import os
  38. import sys
  39. import socket
  40. import getopt
  41. import tempfile
  42. import logging
  43. import logging.config
  44. import errno
  45. import ptl
  46. import ptl.lib
  47. from ptl.utils.pbs_cliutils import CliUtils
  48. from ptl.utils.pbs_dshutils import DshUtils
  49. from ptl.lib.pbs_testlib import PtlConfig
  50. # trap SIGINT and SIGPIPE
  51. def trap_exceptions(etype, value, tb):
  52. sys.excepthook = sys.__excepthook__
  53. if issubclass(etype, KeyboardInterrupt):
  54. pass
  55. elif issubclass(etype, IOError) and value.errno == errno.EPIPE:
  56. pass
  57. else:
  58. sys.__excepthook__(etype, value, tb)
  59. sys.excepthook = trap_exceptions
  60. # A basic SWIG interface definition to wrap PBS IFL
  61. swiginter = '''\
  62. %module pbs_ifl
  63. %typemap(out) char ** {
  64. int len,i;
  65. len = 0;
  66. while ($1[len]) len++;
  67. $result = PyList_New(len);
  68. for (i = 0; i < len; i++) {
  69. PyList_SetItem($result,i,PyString_FromString($1[i]));
  70. }
  71. }
  72. %typemap(in) char ** {
  73. /* Check if is a list */
  74. if (PyList_Check($input)) {
  75. int size = PyList_Size($input);
  76. int i = 0;
  77. $1 = (char **) malloc((size+1)*sizeof(char *));
  78. for (i = 0; i < size; i++) {
  79. PyObject *o = PyList_GetItem($input,i);
  80. if (PyString_Check(o))
  81. $1[i] = PyString_AsString(PyList_GetItem($input,i));
  82. else {
  83. PyErr_SetString(PyExc_TypeError,"list must contain strings");
  84. free($1);
  85. return NULL;
  86. }
  87. }
  88. $1[i] = 0;
  89. } else {
  90. PyErr_SetString(PyExc_TypeError,"not a list");
  91. return NULL;
  92. }
  93. }
  94. %typemap(out) struct batch_status * {
  95. struct batch_status *head_bs, *bs;
  96. struct attrl *attribs;
  97. char *resource;
  98. char *str;
  99. int i, j;
  100. int len;
  101. char buf[4096];
  102. static char *id = "id";
  103. head_bs = $1;
  104. bs = $1;
  105. for (len=0; bs != NULL; len++)
  106. bs = bs->next;
  107. $result = PyList_New(len);
  108. bs = head_bs;
  109. for (i=0; i < len; i++) {
  110. PyObject *dict;
  111. PyObject *a, *v, *tmpv;
  112. dict = PyDict_New();
  113. PyList_SetItem($result, i, dict);
  114. a = PyString_FromString(id);
  115. v = PyString_FromString(bs->name);
  116. PyDict_SetItem(dict, a, v);
  117. attribs = bs->attribs;
  118. while (attribs) {
  119. resource = attribs->resource;
  120. if (resource != NULL) {
  121. /* +2 to account for the '.' between name and resource */
  122. str = malloc(strlen(attribs->name) + strlen(resource) + 2);
  123. sprintf(str, "%s.%s", attribs->name, attribs->resource);
  124. a = PyString_FromString(str);
  125. }
  126. else {
  127. a = PyString_FromString(attribs->name);
  128. }
  129. tmpv = PyDict_GetItem(dict, a);
  130. /* if the key already exists, append as comma-separated */
  131. if (tmpv != NULL) {
  132. char *s = PyString_AsString(tmpv);
  133. /* +4 for the quotes, the comma, and a NULL byte */
  134. str = malloc(strlen(attribs->value) + strlen(s) + 4);
  135. sprintf(str, "%s,%s", attribs->value, s);
  136. v = PyString_FromString(str);
  137. }
  138. else {
  139. v = PyString_FromString(attribs->value);
  140. }
  141. PyDict_SetItem(dict, a, v);
  142. attribs = attribs->next;
  143. }
  144. bs = bs->next;
  145. }
  146. }
  147. %{
  148. #include "pbs_ifl.h"
  149. int pbs_py_spawn(int, char *, char **, char **);
  150. %}
  151. %include "pbs_ifl.h"
  152. int pbs_py_spawn(int, char *, char **, char **);
  153. '''
  154. def _remove_file(workdir, filename):
  155. f = os.path.join(workdir, filename)
  156. if os.path.isfile(f):
  157. logging.debug('removing intermediary file ' + filename)
  158. os.remove(f)
  159. def usage():
  160. msg = []
  161. msg += ['Usage: ' + os.path.basename(sys.argv[0]) + ' [OPTION]\n\n']
  162. msg += [' Produce Python wrappers for PBS IFL API\n\n']
  163. msg += ['-c <pbs_conf>: path to pbs.conf\n']
  164. msg += [
  165. '-f: force overwrite of _pbs_ifl.so and pbs_ifl.py when present\n']
  166. msg += ['-h: display usage information\n']
  167. msg += ['-i <swig.i>: path to swig interface file\n']
  168. msg += ['-I <python_include>: path to python include directory\n']
  169. msg += ['-l <level>: logging level\n']
  170. msg += ['-s <swig>: path to swig binary to use\n']
  171. msg += ['-t <targethost>: hostname to operate on\n']
  172. msg += ['-w <workdir>: path to working directory\n']
  173. msg += ['--log-conf=<file>: logging config file\n']
  174. msg += ['--version: print version number and exit\n']
  175. print "".join(msg)
  176. if __name__ == '__main__':
  177. workdir = tempfile.gettempdir()
  178. targethost = socket.gethostname()
  179. config = None
  180. interface = None
  181. pythoninc = None
  182. level = 'INFO'
  183. force = False
  184. swigbin = 'swig'
  185. logconf = None
  186. if 'PBS_CONF_FILE' in os.environ:
  187. config = os.environ['PBS_CONF_FILE']
  188. else:
  189. config = '/etc/pbs.conf'
  190. opts, args = getopt.getopt(sys.argv[1:], "t:i:I:c:w:l:s:hf",
  191. ["log-conf=", "version"])
  192. for o, val in opts:
  193. if o == '-t':
  194. targethost = val
  195. elif o == '-w':
  196. workdir = CliUtils.expand_abs_path(val)
  197. elif o == '-i':
  198. interface = val
  199. elif o == '-l':
  200. level = val
  201. elif o == '-c':
  202. config = val
  203. elif o == '-I':
  204. pythoninc = CliUtils.expand_abs_path(val)
  205. elif o == '-f':
  206. force = True
  207. elif o == '-s':
  208. swigbin = CliUtils.expand_abs_path(val)
  209. elif o == '--log-conf':
  210. logconf = val
  211. elif o == '--version':
  212. print ptl.__version__
  213. sys.exit(0)
  214. elif o == '-h':
  215. usage()
  216. sys.exit(0)
  217. else:
  218. sys.stderr.write("Unrecognized option\n")
  219. usage()
  220. sys.exit(1)
  221. cu = CliUtils()
  222. if logconf:
  223. logging.config.fileConfig(logconf)
  224. else:
  225. l = cu.get_logging_level(level)
  226. logging.basicConfig(level=l)
  227. b = cu.check_bin(swigbin)
  228. if not b:
  229. logging.error("swig is missing, exiting")
  230. sys.exit(1)
  231. b = cu.check_bin("gcc")
  232. if not b:
  233. logging.error("gcc is missing, exiting")
  234. sys.exit(1)
  235. if pythoninc is None:
  236. logging.error("Path to Python include directory is mandatory")
  237. usage()
  238. sys.exit(1)
  239. if targethost != socket.gethostname():
  240. logging.error("This command only works on localhost")
  241. sys.exit(1)
  242. PtlConfig()
  243. du = DshUtils()
  244. pbs_conf = du.parse_pbs_config(targethost, file=config)
  245. os.chdir(workdir)
  246. if interface is None:
  247. interface = os.path.join(workdir, "pbs_ifl.i")
  248. f = open(interface, 'w')
  249. f.write(swiginter)
  250. f.close()
  251. srcdir = os.getcwd()
  252. else:
  253. srcdir = os.path.dirname(interface)
  254. if 'PBS_EXEC' in pbs_conf:
  255. pbsinclude = os.path.join(pbs_conf['PBS_EXEC'], 'include')
  256. cmd = [swigbin, '-python', '-I' + pbsinclude, interface]
  257. logging.debug(du.run_cmd(targethost, cmd))
  258. if srcdir != os.getcwd():
  259. logging.debug(du.run_copy(targethost,
  260. os.path.join(srcdir, "pbs_ifl_wrap.c"),
  261. workdir))
  262. logging.debug(du.run_copy(targethost,
  263. os.path.join(srcdir, "pbs_ifl.py"),
  264. workdir))
  265. cmd = ['gcc', '-Wall', '-Wno-unused-variable', '-fPIC', '-shared',
  266. '-I' + pbsinclude]
  267. cmd += ['-I' + pythoninc]
  268. cmd += ['pbs_ifl_wrap.c']
  269. cmd += ['-L' + os.path.join(pbs_conf['PBS_EXEC'], 'lib')]
  270. cmd += ['-lpbs']
  271. cmd += ['-o', '_pbs_ifl.so']
  272. cmd += ['-lcrypto', '-lssl']
  273. logging.debug(du.run_cmd(targethost, cmd))
  274. libdir = os.path.dirname(ptl.lib.__file__)
  275. if force or not os.path.isfile(libdir + '/_pbs_ifo.so'):
  276. du.run_copy(targethost,
  277. os.path.join(workdir, '_pbs_ifl.so'),
  278. os.path.join(libdir, '_pbs_ifl.so'), sudo=True)
  279. if force or not os.path.isfile(os.path.join(libdir, '/pbs_ifl.py')):
  280. du.run_copy(targethost,
  281. os.path.join(workdir, 'pbs_ifl.py'),
  282. os.path.join(libdir, 'pbs_ifl.py'), sudo=True)
  283. _remove_file(workdir, "pbs_ifl.py")
  284. _remove_file(workdir, "_pbs_ifl.so")
  285. _remove_file(workdir, "pbs_ifl_wrap.c")
  286. _remove_file(workdir, "pbs_ifl.i")