Salome HOME
updated copyright message
[modules/kernel.git] / bin / runSession.py
index de0eb624f49a586de99e7f8ca9d9b6b1daa33573..39d20593d9f57b44787d1eab9513fa5094d6ec17 100644 (file)
@@ -1,5 +1,5 @@
 #  -*- coding: iso-8859-1 -*-
-# Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
+# Copyright (C) 2007-2023  CEA, EDF, OPEN CASCADE
 #
 # Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
 # CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
@@ -28,7 +28,8 @@ from NSparam import getNSparams
 import socket
 import subprocess
 import re
-from salomeContextUtils import getScriptsAndArgs, formatScriptsAndArgs
+from salomeContextUtils import getScriptsAndArgs, formatScriptsAndArgs, getShortAndExtraArgs
+from salome_utils import getUserName, getShortHostName
 
 # Use to display newlines (\n) in epilog
 class MyParser(OptionParser):
@@ -43,25 +44,63 @@ class SessionParameters:
     self.machine = machine
     self.user = user
     self.directory = directory
+  #
+  def __repr__(self):
+    msg = "\n# Session Parameters:\n"
+    msg += "     * mode: %s\n"%self.mode
+    msg += "     * port: %s\n"%self.port
+    msg += "     * machine: %s\n"%self.machine
+    msg += "     * user: %s\n"%self.user
+    msg += "     * directory: %s\n"%self.directory
+    return msg
+  #
 #
 
-def configureSession(args=None):
+def configureSession(args=None, exe=None):
   if args is None:
     args = []
-  usage = "Usage: %prog [options] [command]"
+  if exe:
+      usage = "Usage: %s [options] [command] [-- <extra>]"%exe
+  else:
+      usage = "Usage: %prog [options] [command] [-- <extra>]"
   epilog  = """\n
-If the command is not given a shell is opened; else execute the given command.
-Command may be a series of Python scripts with arguments: [PYTHON_FILE [args] [PYTHON_FILE [args]...]]
-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,...
-\n
-If PORT and MACHINE are not given, try to connect to the last active session on the local machine.
-If PORT and MACHINE are given, try to connect to the remote session associated with PORT on MACHINE.
-If MACHINE is not given, try to connect to the session associated to PORT on the local machine.
-If PORT is not given, try to connect to the remote session associated to port 2810 on MACHINE.
+If command is not given a shell is opened; else execute the given command.\n
+* Command may be an executable script or program, either identified by its
+  full path or located in a directory pointed by a system variable (e.g.
+  PATH).\n
+* Command may also be a series of Python scripts with arguments:
+  [PYTHON_FILE [args] [PYTHON_FILE [args]...]]
+Python file arguments, if any, must be comma-separated (without blank
+  characters) and prefixed by "args:" keyword (without quotes).
+For example:
+       salome shell hello.py add.py args:1,2 hello.py args:you
+will successively say hello, add 1+2, and say hello to you.\n
+The double dash (--) syntax indicates an extra command to be run "as is". It
+  allows calling a extern program or system command with options and
+  arguments, using the syntax: -- <program> [options] [arguments].
+For example:
+       salome shell -- ls -l *.py
+       salome shell -- python -tt hello.py
 \n
+If PORT and MACHINE are not given, try to connect to the last active session
+  on the local machine.
+If PORT and MACHINE are given, try to connect to the remote session associated
+  with PORT on MACHINE.
+If MACHINE is not given, try to connect to the session associated to PORT on
+  the local machine.
+If PORT is not given, try to connect to the remote session associated to port
+  2810 on MACHINE.\n
 If MACHINE is remote, the following options MUST be provided:
      * DIRECTORY: The full path to the salome command on remote machine.
-     * USER: The user on the computer to connect to.
+     * USER: The user on the computer to connect to.\n
+In case of remote call, syntax "out:res1,res2,..." can be used to get results
+  from remote machine.
+For example:
+       salome shell -m remotemachine -p 2810 -u myself -d /full/path/to/salome
+  concatenate.py args:file1.txt,file2.txt out:result.txt
+User "myself" connects to remotemachine to run the script concatenate.py in
+  a SALOME session on port 2810; the script takes two input parameters and
+  produces one result file.\n
 """
   parser = MyParser(usage=usage, epilog=epilog)
   parser.add_option("-p", "--port", metavar="<port>", default=0,
@@ -78,14 +117,26 @@ If MACHINE is remote, the following options MUST be provided:
   parser.add_option('-u', '--user', dest="user", default=None,
                     help="[Remote mode] The user on the computer to connect to."
                     )
+
+  parser.add_option('-l', '--launcher', dest="launcher", default=None,
+                    help="[Remote mode] The machine and the port to connect to."
+                    )
+
+  short_args, extra_args = getShortAndExtraArgs(args)
   try:
-    (options, args) = parser.parse_args(args)
-  except Exception, e:
-    print e
-    return
+    (options, args) = parser.parse_args(short_args)
+  except Exception as e:
+    print(e)
+    return None, []
 
   port = options.port
   host = options.host
+  launcher = options.launcher
+  if launcher is not None:
+    pos = launcher.find(":")
+    if pos != -1:
+      host = launcher[0:pos]
+      port = launcher[pos+1:]
 
   # :GLITCH: this code defines specific environment variables (OMNIORB_CONFIG, NSPORT,
   # NSHOST) which are later used by other modules. Working, but not really "safe"...
@@ -94,11 +145,16 @@ If MACHINE is remote, the following options MUST be provided:
       # neither MACHINE nor PORT are given
       # --- set omniORB configuration to current session if any
       omniorbUserPath = os.environ['OMNIORB_USER_PATH']
-      fileOmniConfig = omniorbUserPath + '/.omniORB_' + os.environ['USER'] + '_last.cfg'
+      fileOmniConfig = omniorbUserPath + '/.omniORB_' + getUserName() + '_last.cfg'
       if os.path.isfile(fileOmniConfig):
         os.environ['OMNIORB_CONFIG'] = fileOmniConfig
         # --- set environment variables for port and hostname of NamingService
         host, port = getNSparams()
+        try:
+            # keep short name for host, for a correct comparison with getShortHostName() later
+            host=host.split('.')[0]
+        except Exception:
+            pass
       else:
         # No running session
         host = "no_host"
@@ -111,27 +167,27 @@ If MACHINE is remote, the following options MUST be provided:
   else:
     if not host:
       # only PORT is given
-      host = socket.gethostname()
+      host = getShortHostName()
     # both MACHINE and PORT are given
     _writeConfigFile(port, host)
   #
   os.environ['NSPORT'] = port
   os.environ['NSHOST'] = host
 
-  # determine running mode, taht is either 'local' or 'remote'
-  here = socket.gethostname()
+  # determine running mode, that is either 'local' or 'remote'
+  here = getShortHostName()
   mode = "local"
-  if host != here and host != "localhost" and host != "no_host":
+  if host != here and host != "localhost" and host != "no_host" and launcher is None:
     mode="remote"
     pass
   params = SessionParameters(mode, port, host, options.user, options.directory)
-  return params, args
+  return params, args+extra_args
 #
 
 # --- set the OMNIORB_CONFIG file and environment relative to this run of SALOME
 def _writeConfigFile(port, host):
   path = os.environ['OMNIORB_USER_PATH']
-  kwargs = {'with_username' : os.environ['USER']}
+  kwargs = {'with_username' : getUserName()}
 
   from ORBConfigFile import writeORBConfigFile
   [ filename, msgSize ] = writeORBConfigFile(path, host, port, kwargs)
@@ -151,27 +207,41 @@ def __runLocalSession(command):
     errmsg = []
     for cmd in command:
       single_cmd = cmd.strip().split(' ')
-      proc = subprocess.Popen(single_cmd)
-      (stdoutdata, stderrdata) = proc.communicate() # Wait for process to terminate
-      if stdoutdata:
-        outmsg.append(stdoutdata)
-      if stderrdata:
-        errmsg.append(stderrdata)
-
-      if proc.returncode != 0:
+      any_error = False
+      error_code = 1
+      try:
+        proc = subprocess.Popen(single_cmd)
+        (stdoutdata, stderrdata) = proc.communicate() # Wait for process to terminate
+        if stdoutdata:
+          outmsg.append(stdoutdata)
+        if stderrdata:
+          errmsg.append(stderrdata)
+
+        if proc.returncode != 0:
+          any_error = True
+          error_code = proc.returncode
+      except Exception:
+          any_error = True
+          pass
+
+      if any_error:
         errmsg.append("Error raised when executing command: %s\n"%cmd)
         if outmsg:
           sys.stdout.write("".join(outmsg))
         if errmsg:
           sys.stderr.write("".join(errmsg))
-        sys.exit(proc.returncode)
+        sys.exit(error_code)
 
-    return ("".join(outmsg), "".join(errmsg))
+    return 0
   else:
     absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
-    cmd = ["/bin/bash",  "--rcfile", absoluteAppliPath + "/.bashrc" ]
+    if sys.platform == "win32":
+      cmd = ["cmd", "/K", "set PROMPT=[SALOME] $P$G"]
+    else:
+      cmd = ["/bin/bash",  "--rcfile", absoluteAppliPath + "/.bashrc" ]
     proc = subprocess.Popen(cmd, shell=False, close_fds=True)
-    return proc.communicate()
+    proc.communicate()
+    return proc.returncode
 #
 
 def __copyFiles(user, machine, script, infiles, outfiles):
@@ -197,7 +267,7 @@ def __copyFiles(user, machine, script, infiles, outfiles):
 
     # copy the infile to the remote server
     cmd = "scp %s %s@%s:%s" % (infile, user, machine, tmp_file)
-    print "[  SCP  ]", cmd
+    print("[  SCP  ]", cmd)
     os.system(cmd)
 
     list_infiles.append(tmp_file)
@@ -221,7 +291,7 @@ def __copyFiles(user, machine, script, infiles, outfiles):
 
   # copy the salome script on the remote server
   cmd = "scp %s %s@%s:%s" % (tmp_script, user, machine, tmp_script)
-  print "[  SCP  ]", cmd
+  print("[  SCP  ]", cmd)
   os.system(cmd)
 
   return list_infiles, list_outfiles, tmp_script
@@ -230,11 +300,11 @@ def __copyFiles(user, machine, script, infiles, outfiles):
 # sa_obj is a ScriptAndArgs object (from salomeContextUtils)
 def __runRemoteSession(sa_obj, params):
   if not params.user:
-    print "ERROR: The user login on remote machine MUST be given."
-    return
+    print("ERROR: The user login on remote machine MUST be given.")
+    return 1
   if not params.directory:
-    print "ERROR: The remote directory MUST be given."
-    return
+    print("ERROR: The remote directory MUST be given.")
+    return 1
 
   # sa_obj.script may be 'python script.py' --> only process .py file
   header = " ".join(sa_obj.script.split()[:-1])
@@ -243,11 +313,13 @@ def __runRemoteSession(sa_obj, params):
   tmp_in, tmp_out, tmp_script = __copyFiles(params.user, params.machine, script, sa_obj.args or [], sa_obj.out or [])
 
   # execute command on the remote SALOME application
-  command = "ssh %s@%s %s/salome shell " % (params.user, params.machine, params.directory)
+  command = "%s/salome shell" % (params.directory)
   if params.port:
-    command = command + "-p %s "%params.port
-  command = command + " %s %s args:%s"%(header, tmp_script, ",".join(tmp_in))
-  print '[  SSH   ] ' + command
+    command += " -p %s "%params.port
+  command += " %s %s args:%s"%(header, tmp_script, ",".join(tmp_in))
+  # salome shell command must run in a login shell because of module function
+  command = "ssh %s@%s -t 'bash -l -c \"%s\"'" % (params.user, params.machine, command)
+  print('[  SSH   ] ' + command)
   os.system(command)
 
   # Get remote files and clean
@@ -257,26 +329,30 @@ def __runRemoteSession(sa_obj, params):
   for outfile in (sa_obj.out or []):
     remote_outfile = tmp_out.pop(0)
     command = "scp %s@%s:%s %s" %(params.user, params.machine, remote_outfile, outfile)
-    print "[  SCP  ] " + command
+    print("[  SCP  ] " + command)
     os.system(command)
 
   # clean temporary files
   command = "ssh %s@%s \\rm -f %s" % (params.user, params.machine, " ".join(temp_files))
-  print '[  SSH   ] ' + command
+  print('[  SSH   ] ' + command)
   os.system(command)
   os.remove(tmp_script)
 
+  return 0
 #
 
 def runSession(params, args):
   scriptArgs = getScriptsAndArgs(args)
-  command = formatScriptsAndArgs(scriptArgs)
 
   if params.mode == "local":
     command = formatScriptsAndArgs(scriptArgs)
     return __runLocalSession(command)
 
   elif params.mode == "remote":
+    any_error = 0
     for sa_obj in scriptArgs:
-      __runRemoteSession(sa_obj, params)
+      ok = __runRemoteSession(sa_obj, params)
+      if not ok:
+        any_error = 1
+    return any_error
 #