#!/usr/bin/env python
# coding: utf-8
# Copyright (C) 1994-2018 Altair Engineering, Inc.
# For more information, contact Altair at www.altair.com.
#
# This file is part of the PBS Professional ("PBS Pro") software.
#
# Open Source License Information:
#
# PBS Pro is free software. You can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# PBS Pro 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
# Commercial License Information:
#
# For a copy of the commercial license terms and conditions,
# go to: (http://www.pbspro.com/UserArea/agreement.html)
# or contact the Altair Legal Department.
#
# Altair’s dual-license business model allows companies, individuals, and
# organizations to create proprietary derivative works of PBS Pro and
# distribute them - whether embedded or bundled with other software -
# under a commercial license agreement.
#
# Use of Altair’s trademarks, including but not limited to "PBS™",
# "PBS Professional®", and "PBS Pro™" and Altair’s logos is subject to Altair's
# trademark licensing policies.
import os
import sys
import socket
import getopt
import tempfile
import logging
import logging.config
import errno
import ptl
import ptl.lib
from ptl.utils.pbs_cliutils import CliUtils
from ptl.utils.pbs_dshutils import DshUtils
from ptl.lib.pbs_testlib import PtlConfig
# trap SIGINT and SIGPIPE
def trap_exceptions(etype, value, tb):
sys.excepthook = sys.__excepthook__
if issubclass(etype, KeyboardInterrupt):
pass
elif issubclass(etype, IOError) and value.errno == errno.EPIPE:
pass
else:
sys.__excepthook__(etype, value, tb)
sys.excepthook = trap_exceptions
# A basic SWIG interface definition to wrap PBS IFL
swiginter = '''\
%module pbs_ifl
%typemap(out) char ** {
int len,i;
len = 0;
while ($1[len]) len++;
$result = PyList_New(len);
for (i = 0; i < len; i++) {
PyList_SetItem($result,i,PyString_FromString($1[i]));
}
}
%typemap(in) char ** {
/* Check if is a list */
if (PyList_Check($input)) {
int size = PyList_Size($input);
int i = 0;
$1 = (char **) malloc((size+1)*sizeof(char *));
for (i = 0; i < size; i++) {
PyObject *o = PyList_GetItem($input,i);
if (PyString_Check(o))
$1[i] = PyString_AsString(PyList_GetItem($input,i));
else {
PyErr_SetString(PyExc_TypeError,"list must contain strings");
free($1);
return NULL;
}
}
$1[i] = 0;
} else {
PyErr_SetString(PyExc_TypeError,"not a list");
return NULL;
}
}
%typemap(out) struct batch_status * {
struct batch_status *head_bs, *bs;
struct attrl *attribs;
char *resource;
char *str;
int i, j;
int len;
char buf[4096];
static char *id = "id";
head_bs = $1;
bs = $1;
for (len=0; bs != NULL; len++)
bs = bs->next;
$result = PyList_New(len);
bs = head_bs;
for (i=0; i < len; i++) {
PyObject *dict;
PyObject *a, *v, *tmpv;
dict = PyDict_New();
PyList_SetItem($result, i, dict);
a = PyString_FromString(id);
v = PyString_FromString(bs->name);
PyDict_SetItem(dict, a, v);
attribs = bs->attribs;
while (attribs) {
resource = attribs->resource;
if (resource != NULL) {
/* +2 to account for the '.' between name and resource */
str = malloc(strlen(attribs->name) + strlen(resource) + 2);
sprintf(str, "%s.%s", attribs->name, attribs->resource);
a = PyString_FromString(str);
}
else {
a = PyString_FromString(attribs->name);
}
tmpv = PyDict_GetItem(dict, a);
/* if the key already exists, append as comma-separated */
if (tmpv != NULL) {
char *s = PyString_AsString(tmpv);
/* +4 for the quotes, the comma, and a NULL byte */
str = malloc(strlen(attribs->value) + strlen(s) + 4);
sprintf(str, "%s,%s", attribs->value, s);
v = PyString_FromString(str);
}
else {
v = PyString_FromString(attribs->value);
}
PyDict_SetItem(dict, a, v);
attribs = attribs->next;
}
bs = bs->next;
}
}
%{
#include "pbs_ifl.h"
int pbs_py_spawn(int, char *, char **, char **);
%}
%include "pbs_ifl.h"
int pbs_py_spawn(int, char *, char **, char **);
'''
def _remove_file(workdir, filename):
f = os.path.join(workdir, filename)
if os.path.isfile(f):
logging.debug('removing intermediary file ' + filename)
os.remove(f)
def usage():
msg = []
msg += ['Usage: ' + os.path.basename(sys.argv[0]) + ' [OPTION]\n\n']
msg += [' Produce Python wrappers for PBS IFL API\n\n']
msg += ['-c : path to pbs.conf\n']
msg += [
'-f: force overwrite of _pbs_ifl.so and pbs_ifl.py when present\n']
msg += ['-h: display usage information\n']
msg += ['-i : path to swig interface file\n']
msg += ['-I : path to python include directory\n']
msg += ['-l : logging level\n']
msg += ['-s : path to swig binary to use\n']
msg += ['-t : hostname to operate on\n']
msg += ['-w : path to working directory\n']
msg += ['--log-conf=: logging config file\n']
msg += ['--version: print version number and exit\n']
print "".join(msg)
if __name__ == '__main__':
workdir = tempfile.gettempdir()
targethost = socket.gethostname()
config = None
interface = None
pythoninc = None
level = 'INFO'
force = False
swigbin = 'swig'
logconf = None
if 'PBS_CONF_FILE' in os.environ:
config = os.environ['PBS_CONF_FILE']
else:
config = '/etc/pbs.conf'
opts, args = getopt.getopt(sys.argv[1:], "t:i:I:c:w:l:s:hf",
["log-conf=", "version"])
for o, val in opts:
if o == '-t':
targethost = val
elif o == '-w':
workdir = CliUtils.expand_abs_path(val)
elif o == '-i':
interface = val
elif o == '-l':
level = val
elif o == '-c':
config = val
elif o == '-I':
pythoninc = CliUtils.expand_abs_path(val)
elif o == '-f':
force = True
elif o == '-s':
swigbin = CliUtils.expand_abs_path(val)
elif o == '--log-conf':
logconf = val
elif o == '--version':
print ptl.__version__
sys.exit(0)
elif o == '-h':
usage()
sys.exit(0)
else:
sys.stderr.write("Unrecognized option\n")
usage()
sys.exit(1)
cu = CliUtils()
if logconf:
logging.config.fileConfig(logconf)
else:
l = cu.get_logging_level(level)
logging.basicConfig(level=l)
b = cu.check_bin(swigbin)
if not b:
logging.error("swig is missing, exiting")
sys.exit(1)
b = cu.check_bin("gcc")
if not b:
logging.error("gcc is missing, exiting")
sys.exit(1)
if pythoninc is None:
logging.error("Path to Python include directory is mandatory")
usage()
sys.exit(1)
if targethost != socket.gethostname():
logging.error("This command only works on localhost")
sys.exit(1)
PtlConfig()
du = DshUtils()
pbs_conf = du.parse_pbs_config(targethost, file=config)
os.chdir(workdir)
if interface is None:
interface = os.path.join(workdir, "pbs_ifl.i")
f = open(interface, 'w')
f.write(swiginter)
f.close()
srcdir = os.getcwd()
else:
srcdir = os.path.dirname(interface)
if 'PBS_EXEC' in pbs_conf:
pbsinclude = os.path.join(pbs_conf['PBS_EXEC'], 'include')
cmd = [swigbin, '-python', '-I' + pbsinclude, interface]
logging.debug(du.run_cmd(targethost, cmd))
if srcdir != os.getcwd():
logging.debug(du.run_copy(targethost,
os.path.join(srcdir, "pbs_ifl_wrap.c"),
workdir))
logging.debug(du.run_copy(targethost,
os.path.join(srcdir, "pbs_ifl.py"),
workdir))
cmd = ['gcc', '-Wall', '-Wno-unused-variable', '-fPIC', '-shared',
'-I' + pbsinclude]
cmd += ['-I' + pythoninc]
cmd += ['pbs_ifl_wrap.c']
cmd += ['-L' + os.path.join(pbs_conf['PBS_EXEC'], 'lib')]
cmd += ['-lpbs']
cmd += ['-o', '_pbs_ifl.so']
cmd += ['-lcrypto', '-lssl']
logging.debug(du.run_cmd(targethost, cmd))
libdir = os.path.dirname(ptl.lib.__file__)
if force or not os.path.isfile(libdir + '/_pbs_ifo.so'):
du.run_copy(targethost,
os.path.join(workdir, '_pbs_ifl.so'),
os.path.join(libdir, '_pbs_ifl.so'), sudo=True)
if force or not os.path.isfile(os.path.join(libdir, '/pbs_ifl.py')):
du.run_copy(targethost,
os.path.join(workdir, 'pbs_ifl.py'),
os.path.join(libdir, 'pbs_ifl.py'), sudo=True)
_remove_file(workdir, "pbs_ifl.py")
_remove_file(workdir, "_pbs_ifl.so")
_remove_file(workdir, "pbs_ifl_wrap.c")
_remove_file(workdir, "pbs_ifl.i")