pbs_py_spawn 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  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 sys
  38. import os
  39. import getopt
  40. import logging
  41. import tempfile
  42. import errno
  43. import ptl
  44. try:
  45. from ptl.lib.pbs_ifl import pbs_py_spawn
  46. from ptl.lib.pbs_testlib import Server, MoM, JOB, ResourceResv
  47. from ptl.utils.pbs_cliutils import CliUtils
  48. except:
  49. sys.stderr.write("API wrapping is required, see pbs_swigify utility")
  50. exit(1)
  51. # trap SIGINT and SIGPIPE
  52. def trap_exceptions(etype, value, tb):
  53. sys.excepthook = sys.__excepthook__
  54. if issubclass(etype, KeyboardInterrupt):
  55. pass
  56. elif issubclass(etype, IOError) and value.errno == errno.EPIPE:
  57. pass
  58. else:
  59. sys.__excepthook__(etype, value, tb)
  60. sys.excepthook = trap_exceptions
  61. # Helper script to allow a py_spawned script to run detached from the
  62. # session associated to a PBS Job
  63. # sys.argv[1] must be a valid path to the pbs_attach command
  64. # sys.argv[2] must be a valid job identifier
  65. # sys.argv[3:] must be a valid path to a Python script and args to run in
  66. # the background. The first output of this script must be its PID (e.g., a
  67. # shell script shall echo $$)
  68. _wrapper_body = """import os
  69. import sys
  70. from subprocess import Popen, PIPE
  71. if len(sys.argv) < 3:
  72. exit(1)
  73. (r, w) = os.pipe()
  74. p = os.fork()
  75. if p < 0:
  76. exit(1)
  77. elif p > 0:
  78. os.close(w)
  79. pid = os.read(r, 256)
  80. p = Popen([sys.argv[1], "-j", sys.argv[2], "-p", str(pid)], stdout=PIPE)
  81. p.communicate()
  82. os.close(r)
  83. exit(p.retcode)
  84. # child
  85. os.close(r)
  86. p = Popen(["setsid"] + sys.argv[3:], stdout=PIPE)
  87. os.write(w, p.stdout.readline())
  88. os.close(w)
  89. """
  90. def usage():
  91. msg = []
  92. msg += ['Usage: ' + os.path.basename(sys.argv[0]) + ' [OPTION] '
  93. '<path> <args>\n\n']
  94. msg += [' Run a py_spawn command\n\n']
  95. msg += ['-e <envs>: comma-separated list of environment options\n']
  96. msg += ['-j <jobid>: jobid to which the spawned process is run\n']
  97. msg += ['-l <level>: logging level, INFO, DEBUG, ..., defaults to ERROR\n']
  98. msg += ['-t <hostname>: target hostname to operate on\n']
  99. msg += ['--detach: If set, run Python script in background\n']
  100. msg += ['--wrapper=<path>: Optional path to wrapper script, to use with '
  101. 'detach\n']
  102. msg += ['--version: print version number and exit\n']
  103. print "".join(msg)
  104. if __name__ == '__main__':
  105. if len(sys.argv) < 2:
  106. usage()
  107. sys.exit(0)
  108. jobid = None
  109. envs = []
  110. detach = False
  111. lvl = logging.ERROR
  112. wrapper = None
  113. cleanup_wrapper = False
  114. hostname = None
  115. try:
  116. opts, args = getopt.getopt(sys.argv[1:], "e:j:l:t:hw",
  117. ['detach', 'wrapper='])
  118. except:
  119. usage()
  120. sys.exit(1)
  121. for o, val in opts:
  122. if o == '-j':
  123. jobid = val
  124. elif o == '-e':
  125. envs = val.split(',')
  126. elif o == '-l':
  127. lvl = CliUtils.get_logging_level(val)
  128. elif o == '-h':
  129. usage()
  130. sys.exit(0)
  131. elif o == '-t':
  132. hostname = val
  133. elif o == '--detach':
  134. detach = True
  135. elif o == '--wrapper':
  136. wrapper = val
  137. else:
  138. sys.stderr.write("Unrecognized option")
  139. usage()
  140. sys.exit(1)
  141. logging.basicConfig(level=lvl)
  142. s = Server(hostname)
  143. if detach:
  144. d = s.status(JOB, 'exec_host', id=jobid)
  145. if d and 'exec_host' in d[0]:
  146. hosts = ResourceResv.get_hosts(d[0]['exec_host'])
  147. pconf = MoM(hosts[0]).pbs_conf['PBS_EXEC']
  148. # Use path to pbs_attach on natural vnode of the job
  149. pbs_attach = os.path.join(pconf, 'bin', 'pbs_attach')
  150. if wrapper is None:
  151. (fd, fn) = tempfile.mkstemp()
  152. os.write(fd, _wrapper_body)
  153. os.close(fd)
  154. os.chmod(fn, 0755)
  155. wrapper = fn
  156. cleanup_wrapper = True
  157. a = [wrapper, pbs_attach, jobid] + args
  158. logging.debug(str(a))
  159. pbs_py_spawn(s._conn, jobid, a, envs)
  160. if cleanup_wrapper:
  161. os.remove(wrapper)
  162. else:
  163. pbs_py_spawn(s._conn, jobid, args, envs)
  164. sys.exit(0)