1 # -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE
4 # Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
5 # CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
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.
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.
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
21 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
26 from optparse import OptionParser
27 from NSparam import getNSparams
31 from salomeContextUtils import getScriptsAndArgs, formatScriptsAndArgs, getShortAndExtraArgs
33 # Use to display newlines (\n) in epilog
34 class MyParser(OptionParser):
35 def format_epilog(self, formatter):
39 class SessionParameters:
40 def __init__(self, mode, port, machine, user, directory):
43 self.machine = machine
45 self.directory = directory
48 msg = "\n# Session Parameters:\n"
49 msg += " * mode: %s\n"%self.mode
50 msg += " * port: %s\n"%self.port
51 msg += " * machine: %s\n"%self.machine
52 msg += " * user: %s\n"%self.user
53 msg += " * directory: %s\n"%self.directory
58 def configureSession(args=[], exe=None):
60 usage = "Usage: %s [options] [command] [-- <extra>]"%exe
62 usage = "Usage: %prog [options] [command] [-- <extra>]"
64 If command is not given a shell is opened; else execute the given command.\n
65 * Command may be an executable script or program, either identified by its
66 full path or located in a directory pointed by a system variable (e.g.
68 * Command may also be a series of Python scripts with arguments:
69 [PYTHON_FILE [args] [PYTHON_FILE [args]...]]
70 Python file arguments, if any, must be comma-separated (without blank
71 characters) and prefixed by "args:" keyword (without quotes).
73 salome shell hello.py add.py args:1,2 hello.py args:you
74 will successively say hello, add 1+2, and say hello to you.\n
75 The double dash (--) syntax indicates an extra command to be run "as is". It
76 allows calling a extern program or system command with options and
77 arguments, using the syntax: -- <program> [options] [arguments].
79 salome shell -- ls -l *.py
80 salome shell -- python -tt hello.py
82 If PORT and MACHINE are not given, try to connect to the last active session
84 If PORT and MACHINE are given, try to connect to the remote session associated
86 If MACHINE is not given, try to connect to the session associated to PORT on
88 If PORT is not given, try to connect to the remote session associated to port
90 If MACHINE is remote, the following options MUST be provided:
91 * DIRECTORY: The full path to the salome command on remote machine.
92 * USER: The user on the computer to connect to.\n
93 In case of remote call, syntax "out:res1,res2,..." can be used to get results
96 salome shell -m remotemachine -p 2810 -u myself -d /full/path/to/salome
97 concatenate.py args:file1.txt,file2.txt out:result.txt
98 User "myself" connects to remotemachine to run the script concatenate.py in
99 a SALOME session on port 2810; the script takes two input parameters and
100 produces one result file.\n
102 parser = MyParser(usage=usage, epilog=epilog)
103 parser.add_option("-p", "--port", metavar="<port>", default=0,
104 action="store", type="string", dest="port",
105 help="The port to connect to."
107 parser.add_option("-m", "--machine", metavar="<machine>", default=0,
108 action="store", type="string", dest="host",
109 help="The machine to connect to."
111 parser.add_option('-d', '--directory', dest="directory", default=None,
112 help="[Remote mode] The full path to the salome command on remote machine."
114 parser.add_option('-u', '--user', dest="user", default=None,
115 help="[Remote mode] The user on the computer to connect to."
118 short_args, extra_args = getShortAndExtraArgs(args)
120 (options, args) = parser.parse_args(short_args)
128 # :GLITCH: this code defines specific environment variables (OMNIORB_CONFIG, NSPORT,
129 # NSHOST) which are later used by other modules. Working, but not really "safe"...
132 # neither MACHINE nor PORT are given
133 # --- set omniORB configuration to current session if any
134 omniorbUserPath = os.environ['OMNIORB_USER_PATH']
135 fileOmniConfig = omniorbUserPath + '/.omniORB_' + os.environ['USER'] + '_last.cfg'
136 if os.path.isfile(fileOmniConfig):
137 os.environ['OMNIORB_CONFIG'] = fileOmniConfig
138 # --- set environment variables for port and hostname of NamingService
139 host, port = getNSparams()
145 # only MACHINE is given
147 _writeConfigFile(port, host)
152 host = socket.gethostname()
153 # both MACHINE and PORT are given
154 _writeConfigFile(port, host)
156 os.environ['NSPORT'] = port
157 os.environ['NSHOST'] = host
159 # determine running mode, taht is either 'local' or 'remote'
160 here = socket.gethostname()
162 if host != here and host != "localhost" and host != "no_host":
165 params = SessionParameters(mode, port, host, options.user, options.directory)
166 return params, args+extra_args
169 # --- set the OMNIORB_CONFIG file and environment relative to this run of SALOME
170 def _writeConfigFile(port, host):
171 path = os.environ['OMNIORB_USER_PATH']
172 kwargs = {'with_username' : os.environ['USER']}
174 from ORBConfigFile import writeORBConfigFile
175 [ filename, msgSize ] = writeORBConfigFile(path, host, port, kwargs)
177 os.environ['OMNIORB_CONFIG'] = filename
180 # command looks like a Bash command-line:
181 # script1.py [args] ; script2.py [args] ; ...
182 def __runLocalSession(command):
185 if sys.platform == "win32":
187 command = command.split(sep)
191 single_cmd = cmd.strip().split(' ')
194 proc = subprocess.Popen(single_cmd)
195 (stdoutdata, stderrdata) = proc.communicate() # Wait for process to terminate
197 outmsg.append(stdoutdata)
199 errmsg.append(stderrdata)
201 if proc.returncode != 0:
208 errmsg.append("Error raised when executing command: %s\n"%cmd)
210 sys.stdout.write("".join(outmsg))
212 sys.stderr.write("".join(errmsg))
215 return ("".join(outmsg), "".join(errmsg))
217 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
218 cmd = ["/bin/bash", "--rcfile", absoluteAppliPath + "/.bashrc" ]
219 proc = subprocess.Popen(cmd, shell=False, close_fds=True)
220 return proc.communicate()
223 def __copyFiles(user, machine, script, infiles, outfiles):
224 """Modify script, copy files to remote computer and return lists of copied files."""
226 namescript = os.path.basename(script)
228 logname = getpass.getuser()
229 tmp_script = "/tmp/%s_%s_%s" % (logname, os.getpid(), namescript)
230 with open(script, 'r') as fscript:
231 script_text = fscript.read()
236 for infile in infiles:
237 # generate a temporary file name
238 namefile = os.path.basename(infile)
239 tmp_file = "/tmp/%s_%s_i%s_%s" % (logname, os.getpid(), n, namefile)
241 # modify the salome script
242 script_text = re.sub(infile, tmp_file, script_text)
244 # copy the infile to the remote server
245 cmd = "scp %s %s@%s:%s" % (infile, user, machine, tmp_file)
249 list_infiles.append(tmp_file)
253 for outfile in outfiles:
254 # generate a temporary file name
255 namefile = os.path.basename(outfile)
256 tmp_file = "/tmp/%s_%s_o%s_%s" % (logname, os.getpid(), n, namefile)
258 # modify the salome script
259 script_text = re.sub(outfile, tmp_file, script_text)
261 list_outfiles.append(tmp_file)
265 with open(tmp_script,'w') as fscript:
266 fscript.write(script_text)
268 # copy the salome script on the remote server
269 cmd = "scp %s %s@%s:%s" % (tmp_script, user, machine, tmp_script)
273 return list_infiles, list_outfiles, tmp_script
276 # sa_obj is a ScriptAndArgs object (from salomeContextUtils)
277 def __runRemoteSession(sa_obj, params):
279 print "ERROR: The user login on remote machine MUST be given."
281 if not params.directory:
282 print "ERROR: The remote directory MUST be given."
285 # sa_obj.script may be 'python script.py' --> only process .py file
286 header = " ".join(sa_obj.script.split()[:-1])
287 script = sa_obj.script.split()[-1]
289 tmp_in, tmp_out, tmp_script = __copyFiles(params.user, params.machine, script, sa_obj.args or [], sa_obj.out or [])
291 # execute command on the remote SALOME application
292 command = "ssh %s@%s %s/salome shell " % (params.user, params.machine, params.directory)
294 command = command + "-p %s "%params.port
295 command = command + " %s %s args:%s"%(header, tmp_script, ",".join(tmp_in))
296 print '[ SSH ] ' + command
299 # Get remote files and clean
300 temp_files = tmp_in + tmp_out + [tmp_script]
303 for outfile in (sa_obj.out or []):
304 remote_outfile = tmp_out.pop(0)
305 command = "scp %s@%s:%s %s" %(params.user, params.machine, remote_outfile, outfile)
306 print "[ SCP ] " + command
309 # clean temporary files
310 command = "ssh %s@%s \\rm -f %s" % (params.user, params.machine, " ".join(temp_files))
311 print '[ SSH ] ' + command
313 os.remove(tmp_script)
317 def runSession(params, args):
318 scriptArgs = getScriptsAndArgs(args)
320 if params.mode == "local":
321 command = formatScriptsAndArgs(scriptArgs)
322 return __runLocalSession(command)
324 elif params.mode == "remote":
325 for sa_obj in scriptArgs:
326 __runRemoteSession(sa_obj, params)