Salome HOME
Bug fix: the command "salome shell" was broken on some machines.
[modules/kernel.git] / bin / runSession.py
1 #  -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
3 #
4 # Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
5 # CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 #
7 # This library is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU Lesser General Public
9 # License as published by the Free Software Foundation; either
10 # version 2.1 of the License, or (at your option) any later version.
11 #
12 # This library is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 # Lesser General Public License for more details.
16 #
17 # You should have received a copy of the GNU Lesser General Public
18 # License along with this library; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
20 #
21 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 #
23
24 import os
25 import sys
26 from optparse import OptionParser
27 from NSparam import getNSparams
28 import socket
29 import subprocess
30 import re
31 from salomeContextUtils import getScriptsAndArgs, formatScriptsAndArgs
32
33 # Use to display newlines (\n) in epilog
34 class MyParser(OptionParser):
35     def format_epilog(self, formatter):
36         return self.epilog
37 #
38
39 class SessionParameters:
40   def __init__(self, mode, port, machine, user, directory):
41     self.mode = mode
42     self.port = port
43     self.machine = machine
44     self.user = user
45     self.directory = directory
46 #
47
48 def configureSession(args=None):
49   if args is None:
50     args = []
51   usage = "Usage: %prog [options] [command]"
52   epilog  = """\n
53 If the command is not given a shell is opened; else execute the given command.
54 Command may be a series of Python scripts with arguments: [PYTHON_FILE [args] [PYTHON_FILE [args]...]]
55 Python file arguments, if any, must be comma-separated (without blank characters) and prefixed by "args:" (without quotes), e.g. myscript.py args:arg1,arg2=val,...
56 \n
57 If PORT and MACHINE are not given, try to connect to the last active session on the local machine.
58 If PORT and MACHINE are given, try to connect to the remote session associated with PORT on MACHINE.
59 If MACHINE is not given, try to connect to the session associated to PORT on the local machine.
60 If PORT is not given, try to connect to the remote session associated to port 2810 on MACHINE.
61 \n
62 If MACHINE is remote, the following options MUST be provided:
63      * DIRECTORY: The full path to the salome command on remote machine.
64      * USER: The user on the computer to connect to.
65 """
66   parser = MyParser(usage=usage, epilog=epilog)
67   parser.add_option("-p", "--port", metavar="<port>", default=0,
68                     action="store", type="string", dest="port",
69                     help="The port to connect to."
70                     )
71   parser.add_option("-m", "--machine", metavar="<machine>", default=0,
72                     action="store", type="string", dest="host",
73                     help="The machine to connect to."
74                     )
75   parser.add_option('-d', '--directory', dest="directory", default=None,
76                     help="[Remote mode] The full path to the salome command on remote machine."
77                     )
78   parser.add_option('-u', '--user', dest="user", default=None,
79                     help="[Remote mode] The user on the computer to connect to."
80                     )
81   try:
82     (options, args) = parser.parse_args(args)
83   except Exception, e:
84     print e
85     return
86
87   port = options.port
88   host = options.host
89
90   # :GLITCH: this code defines specific environment variables (OMNIORB_CONFIG, NSPORT,
91   # NSHOST) which are later used by other modules. Working, but not really "safe"...
92   if not port:
93     if not host:
94       # neither MACHINE nor PORT are given
95       # --- set omniORB configuration to current session if any
96       omniorbUserPath = os.environ['OMNIORB_USER_PATH']
97       fileOmniConfig = omniorbUserPath + '/.omniORB_' + os.environ['USER'] + '_last.cfg'
98       if os.path.isfile(fileOmniConfig):
99         os.environ['OMNIORB_CONFIG'] = fileOmniConfig
100         # --- set environment variables for port and hostname of NamingService
101         host, port = getNSparams()
102       else:
103         # No running session
104         host = "no_host"
105         port = "no_port"
106     else:
107       # only MACHINE is given
108       port = '2810'
109       _writeConfigFile(port, host)
110     #
111   else:
112     if not host:
113       # only PORT is given
114       host = socket.gethostname()
115     # both MACHINE and PORT are given
116     _writeConfigFile(port, host)
117   #
118   os.environ['NSPORT'] = port
119   os.environ['NSHOST'] = host
120
121   # determine running mode, taht is either 'local' or 'remote'
122   here = socket.gethostname()
123   mode = "local"
124   if host != here and host != "localhost" and host != "no_host":
125     mode="remote"
126     pass
127   params = SessionParameters(mode, port, host, options.user, options.directory)
128   return params, args
129 #
130
131 # --- set the OMNIORB_CONFIG file and environment relative to this run of SALOME
132 def _writeConfigFile(port, host):
133   path = os.environ['OMNIORB_USER_PATH']
134   kwargs = {'with_username' : os.environ['USER']}
135
136   from ORBConfigFile import writeORBConfigFile
137   [ filename, msgSize ] = writeORBConfigFile(path, host, port, kwargs)
138
139   os.environ['OMNIORB_CONFIG'] = filename
140 #
141
142 # command looks like a Bash command-line:
143 # script1.py [args] ; script2.py [args] ; ...
144 def __runLocalSession(command):
145   if command:
146     sep = ";"
147     if sys.platform == "win32":
148       sep= "&"
149     command = command.split(sep)
150     outmsg = []
151     errmsg = []
152     for cmd in command:
153       single_cmd = cmd.strip().split(' ')
154       proc = subprocess.Popen(single_cmd)
155       (stdoutdata, stderrdata) = proc.communicate() # Wait for process to terminate
156       if stdoutdata:
157         outmsg.append(stdoutdata)
158       if stderrdata:
159         errmsg.append(stderrdata)
160
161       if proc.returncode != 0:
162         errmsg.append("Error raised when executing command: %s\n"%cmd)
163         if outmsg:
164           sys.stdout.write("".join(outmsg))
165         if errmsg:
166           sys.stderr.write("".join(errmsg))
167         sys.exit(proc.returncode)
168
169     return ("".join(outmsg), "".join(errmsg))
170   else:
171     absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
172     cmd = ["/bin/bash",  "--rcfile", absoluteAppliPath + "/.bashrc" ]
173     proc = subprocess.Popen(cmd, shell=False, close_fds=True)
174     return proc.communicate()
175 #
176
177 def __copyFiles(user, machine, script, infiles, outfiles):
178   """Modify script, copy files to remote computer and return lists of copied files."""
179
180   namescript = os.path.basename(script)
181   import getpass
182   logname = getpass.getuser()
183   tmp_script = "/tmp/%s_%s_%s" % (logname, os.getpid(), namescript)
184   with open(script, 'r') as fscript:
185     script_text = fscript.read()
186
187   list_infiles = []
188   list_outfiles = []
189   n = 0
190   for infile in infiles:
191     # generate a temporary file name
192     namefile = os.path.basename(infile)
193     tmp_file = "/tmp/%s_%s_i%s_%s" % (logname, os.getpid(), n, namefile)
194
195     # modify the salome script
196     script_text = re.sub(infile, tmp_file, script_text)
197
198     # copy the infile to the remote server
199     cmd = "scp %s %s@%s:%s" % (infile, user, machine, tmp_file)
200     print "[  SCP  ]", cmd
201     os.system(cmd)
202
203     list_infiles.append(tmp_file)
204     n = n + 1
205   #
206   n = 0
207   for outfile in outfiles:
208     # generate a temporary file name
209     namefile = os.path.basename(outfile)
210     tmp_file = "/tmp/%s_%s_o%s_%s" % (logname, os.getpid(), n, namefile)
211
212     # modify the salome script
213     script_text = re.sub(outfile, tmp_file, script_text)
214
215     list_outfiles.append(tmp_file)
216     n = n + 1
217   #
218
219   with open(tmp_script,'w') as fscript:
220     fscript.write(script_text)
221
222   # copy the salome script on the remote server
223   cmd = "scp %s %s@%s:%s" % (tmp_script, user, machine, tmp_script)
224   print "[  SCP  ]", cmd
225   os.system(cmd)
226
227   return list_infiles, list_outfiles, tmp_script
228 #
229
230 # sa_obj is a ScriptAndArgs object (from salomeContextUtils)
231 def __runRemoteSession(sa_obj, params):
232   if not params.user:
233     print "ERROR: The user login on remote machine MUST be given."
234     return
235   if not params.directory:
236     print "ERROR: The remote directory MUST be given."
237     return
238
239   # sa_obj.script may be 'python script.py' --> only process .py file
240   header = " ".join(sa_obj.script.split()[:-1])
241   script = sa_obj.script.split()[-1]
242
243   tmp_in, tmp_out, tmp_script = __copyFiles(params.user, params.machine, script, sa_obj.args or [], sa_obj.out or [])
244
245   # execute command on the remote SALOME application
246   command = "ssh %s@%s %s/salome shell " % (params.user, params.machine, params.directory)
247   if params.port:
248     command = command + "-p %s "%params.port
249   command = command + " %s %s args:%s"%(header, tmp_script, ",".join(tmp_in))
250   print '[  SSH   ] ' + command
251   os.system(command)
252
253   # Get remote files and clean
254   temp_files = tmp_in + tmp_out + [tmp_script]
255
256   # get the outfiles
257   for outfile in (sa_obj.out or []):
258     remote_outfile = tmp_out.pop(0)
259     command = "scp %s@%s:%s %s" %(params.user, params.machine, remote_outfile, outfile)
260     print "[  SCP  ] " + command
261     os.system(command)
262
263   # clean temporary files
264   command = "ssh %s@%s \\rm -f %s" % (params.user, params.machine, " ".join(temp_files))
265   print '[  SSH   ] ' + command
266   os.system(command)
267   os.remove(tmp_script)
268
269 #
270
271 def runSession(params, args):
272   scriptArgs = getScriptsAndArgs(args)
273   command = formatScriptsAndArgs(scriptArgs)
274
275   if params.mode == "local":
276     command = formatScriptsAndArgs(scriptArgs)
277     return __runLocalSession(command)
278
279   elif params.mode == "remote":
280     for sa_obj in scriptArgs:
281       __runRemoteSession(sa_obj, params)
282 #