]> SALOME platform Git repositories - modules/kernel.git/commitdiff
Salome HOME
Merge from agr_portmanager_branch_131004 03/12/2013
authorvsr <vsr@opencascade.com>
Tue, 3 Dec 2013 08:01:13 +0000 (08:01 +0000)
committervsr <vsr@opencascade.com>
Tue, 3 Dec 2013 08:01:13 +0000 (08:01 +0000)
CMakeLists.txt
SalomeKERNELConfig.cmake.in
bin/CMakeLists.txt
bin/PortManager.py
bin/killSalomeWithPort.py
bin/launchConfigureParser.py
bin/salomeRunner.py
bin/searchFreePort.py
src/LifeCycleCORBA/SALOME_LifeCycleCORBA.cxx

index 2996e3354bbdcbfb1dc0f695aabdfccd23bf6a69..19a87994a8dacd4120d1719c551046d1a141d75e 100755 (executable)
@@ -63,7 +63,8 @@ OPTION(SALOME_USE_LIBBATCH "Use LibBatch in KERNEL" OFF)
 OPTION(SALOME_USE_SIMAN "Add SIMAN support" OFF)
 OPTION(SALOME_PACO_PARALLEL "Build with PACO (implies SALOME_USE_MPI)" OFF)
 OPTION(SALOME_LAUNCHER_ONLY "Build only the Launcher part" OFF)
-MARK_AS_ADVANCED(SALOME_LIGHT_ONLY SALOME_USE_LIBBATCH SALOME_USE_SIMAN SALOME_PACO_PARALLEL SALOME_LAUNCHER_ONLY)
+OPTION(SALOME_USE_PORTMANAGER "Add PortManager support" OFF)
+MARK_AS_ADVANCED(SALOME_LIGHT_ONLY SALOME_USE_LIBBATCH SALOME_USE_SIMAN SALOME_PACO_PARALLEL SALOME_LAUNCHER_ONLY SALOME_USE_PORTMANAGER)
 
 # Required prerequisites
 #   Find "big" prerequisites first - they reference themselves many others
@@ -105,6 +106,9 @@ ENDIF()
 IF(SALOME_PACO_PARALLEL)
   FIND_PACKAGE(SalomePaco REQUIRED)
 ENDIF()
+IF(SALOME_USE_PORTMANAGER)
+  ADD_DEFINITIONS(-DWITH_PORTMANAGER)
+ENDIF()
 IF(SALOME_BUILD_TESTS)
   ENABLE_TESTING()
   FIND_PACKAGE(SalomeCppUnit)
index a5e884660c1e5392b490bb0fc6f93359565c947c..3077cb59266fd44b23df3197db2cd55cfd104b1d 100644 (file)
@@ -73,6 +73,11 @@ IF(SALOME_USE_SIMAN)
   LIST(APPEND KERNEL_DEFINITIONS "-DWITH_SIMANIO")
 ENDIF()
 
+SET(SALOME_USE_PORTMANAGER     @SALOME_USE_PORTMANAGER@)
+IF(SALOME_USE_PORTMANAGER)
+  LIST(APPEND KERNEL_DEFINITIONS "-DWITH_PORTMANAGER")
+ENDIF()
+
 # Prerequisites:
 IF(SALOME_KERNEL_BUILD_TESTS)
   SET_AND_CHECK(CPPUNIT_ROOT_DIR_EXP  "@PACKAGE_CPPUNIT_ROOT_DIR@")
index a562f96f8fe3032c9568e09478c6ea9af8d00f58..b2847db74e8470a76c9feadad9a82fab0e6b0724 100755 (executable)
@@ -61,4 +61,13 @@ SET(SCRIPTS
   waitContainers.py
   waitNS.py
   )
+
+IF(SALOME_USE_PORTMANAGER)
+  SET(PORTMANAGER_SCRIPTS
+    PortManager.py
+    Singleton.py
+    )
+  LIST(APPEND SCRIPTS ${PORTMANAGER_SCRIPTS})
+ENDIF()
+
 SALOME_INSTALL_SCRIPTS("${SCRIPTS}" ${SALOME_INSTALL_SCRIPT_SCRIPTS})
index da1e06cd6732fa1c9e5ab4d12004e96512feae66..0ad853a9bc05e0594413c847706a8a4339d157db 100644 (file)
@@ -27,6 +27,7 @@ import multiprocessing
 import time
 import socket
 
+import os
 import sys
 import threading
 import SocketServer
@@ -39,6 +40,20 @@ except:
 import struct
 import ctypes
 
+import logging
+def createLogger():
+  logger = logging.getLogger(__name__)
+#  logger.setLevel(logging.DEBUG)
+  ch = logging.StreamHandler()
+  ch.setLevel(logging.DEBUG)
+  formatter = logging.Formatter("%(levelname)s:%(threadName)s:%(message)s")
+  ch.setFormatter(formatter)
+  logger.addHandler(ch)
+  return logger
+#
+logger = createLogger()
+
+
 if sys.platform == 'win32':
   import multiprocessing.reduction    # make sockets pickable/inheritable
 
@@ -78,6 +93,7 @@ class _PortManager(object): # :TODO: must manage lock owner
       return port
   #
   def releasePort(self, port):
+    logger.debug("PortManager.releasePort %s"%port)
     with self.__lock:
       if port in self.__lockedPorts:
         self.__lockedPorts.remove(port)
@@ -151,9 +167,11 @@ GET_PORT_MSG = "GET_PORT"
 GET_PREFERED_PORT_MSG = "GET_PREFERED_PORT"
 RELEASE_PORT_MSG = "RELEASE_PORT"
 STOP_SERVER_MSG = "STOP_SERVER"
+TEST_SERVER_MSG = "TEST_SERVER"
 
 GET_PORT_ACK_MSG = "GET_PORT"
 RELEASE_PORT_ACK_MSG = "RELEASE_PORT"
+TEST_SERVER_ACK_MSG = "TEST_SERVER"
 
 class _ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
   def handle(self):
@@ -175,35 +193,150 @@ class _ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
       pm.releasePort(port)
       response = "%s" % (RELEASE_PORT_ACK_MSG)
       _send(self.request, response)
-      #print "RELEASE_PORT:", port
+      logger.debug("RELEASE_PORT: %s"%port)
       if not pm.isBusy():
-        #print "Close server"
+        logger.debug("Close server")
+        config_file, lock_file = _getConfigurationFilename()
+        try:
+          os.remove(config_file)
+          pmlock.release()
+          os.remove(lock_file)
+        except:
+          pass
         self.server.shutdown()
       #print pm
     elif data == STOP_SERVER_MSG:
-      #print "Close server"
+      logger.debug("Close server")
       self.server.shutdown()
+    elif data == TEST_SERVER_MSG:
+      _send(self.request, TEST_SERVER_ACK_MSG)
 #
 
 class _ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
     pass
 
-pm_address = ('127.0.0.1', 51843)
-def __getServer(address):
+#------------------------------------
+# A file locker (Linux only)
+import fcntl
+class PortManagerLock:
+  def __init__(self, filename, readonly=False, blocking=True):
+    self.filename = filename
+    # This will create it if it does not exist already
+    logger.debug("Create lock on %s"%filename)
+    mode = 'w'
+    if readonly:
+      mode = 'r'
+    self.handle = open(filename, mode)
+    self.handle.seek(0) # go back to beginning of file to read it multiple times
+    self.__blocking = blocking
+
+  def acquire(self):
+    mode = fcntl.LOCK_EX
+    if not self.__blocking: # Raise an IOError exception if file has already been locked
+      mode = mode | fcntl.LOCK_NB
+    fcntl.flock(self.handle, mode)
+    logger.debug("lock acquired %s"%self.__blocking)
+
+  def release(self):
+    fcntl.flock(self.handle, fcntl.LOCK_UN)
+    logger.debug("lock released")
+
+  def __del__(self):
+    if logger:
+      logger.debug("Close lock file")
+    self.handle.close()
+#
+#------------------------------------
+
+# Server address has to be shared by different processes, without any common
+# ancestor.
+# The "simplest" solution is to define it here as a global variable. Unfortunately,
+# availability of the corresponding socket is not guaranted at all. If PortManager
+# tries to use a socket it does not own, server is not created (it is identified as
+# already existing), clients then connect on this socket but message passing
+# between clients and server will not work and SALOME launch will crash.
+# We can introduce a port verification procedure automatically called by importing
+# this module (i.e. when creating the server). This procedure consists in creating
+# a client which sends a specific message to the server that has to be tested. And
+# loop on port numbers until a free socket is found and associated to a new server.
+#
+# Another approach is to let Python socket API select a free port number, then store
+# it to a file on server host machine in order to be shared with clients.
+# The logical part can be defined as follows. When server is started (by importing
+# this module), write server port number to a specific file (do not use a temporary
+# file name). Each client then read server address from this same file ; if file is
+# not nound, it is an error (add appropriate processing).
+# Server could also check file existence and try to use the same address as previous
+# server in order to avoid opening too many unecessary sockets ; but we need to apply
+# the above verification procedure. This processing is not necessary because TCP socket
+# timeout will automatically close unused sockets.
+
+def _getConfigurationFilename():
+  omniorbUserPath = os.getenv("OMNIORB_USER_PATH")
+
+  from salome_utils import generateFileName
+  portmanager_config = generateFileName(omniorbUserPath,
+                                        prefix="omniORB",
+                                        suffix="PortManager",
+                                        extension="cfg",
+                                        hidden=True)
+  lock_file = portmanager_config + "-lock"
+  return (portmanager_config, lock_file)
+#
+
+def __checkServer():
+  while True:
+    logger.debug("CHECKING SERVER")
+    status = __newClient(TEST_SERVER_MSG)
+    if status == TEST_SERVER_ACK_MSG:
+      break
+  return (status == TEST_SERVER_ACK_MSG)
+#
+
+def __getServerAddress(readonly=True):
+  address = ("localhost", 0)
+  try:
+    config_file, lock_file = _getConfigurationFilename()
+    lock = PortManagerLock(config_file, readonly, blocking=True)
+    lock.acquire()
+    address = eval(lock.handle.read())
+    lock.release()
+  except (IOError, SyntaxError) as e:
+    logger.debug("no configuration file")
+    pass
+  finally:
+    return address
+#
+
+def __setServerAddress(address):
+  config_file, lock_file = _getConfigurationFilename()
+  lock = PortManagerLock(config_file, readonly=False, blocking=True)
+  lock.acquire()
+  logger.debug("setServerAddress: %s"%str(address))
+  lock.handle.write(str(address))
+  lock.release()
+#
+
+def __getServer():
+  address = __getServerAddress(readonly=False)
   SocketServer.ThreadingTCPServer.allow_reuse_address = True # can be restarted immediately
   server = _ThreadedTCPServer(address, _ThreadedTCPRequestHandler, False) # Do not automatically bind
   server.allow_reuse_address = True # Prevent 'cannot bind to address' errors on restart
+  server.server_bind()     # Manually bind, to support allow_reuse_address
+  __setServerAddress(server.server_address)
+  server.server_activate()
   return server
 #
 
+pmlock = None
 def __startServer():
-  global pm_address
   try:
-    server = __getServer(pm_address)
-    server.server_bind()     # Manually bind, to support allow_reuse_address
-    server.server_activate()
-    pm_address = server.server_address
+    config_file, lock_file = _getConfigurationFilename()
+    global pmlock
+    pmlock = PortManagerLock(lock_file, readonly=False, blocking=False)
+    pmlock.acquire()
 
+    server = __getServer()
     # Start a thread with the server -- that thread will then start one
     # more thread for each request
     server_thread = threading.Thread(target=server.serve_forever, name="SALOME_PortManager")
@@ -211,50 +344,54 @@ def __startServer():
     #server_thread.setDaemon(True)
     server_thread.start()
     #print "Server loop running in thread:", server_thread.getName()
-    #print "Server address:", pm_address
-    #return address
   except:
-    #print "Server already started"
-    #print "Server address:", pm_address
-    #return pm_address
+    logger.debug("Server already started")
     pass
 #
 
-def __newClient(address, message):
+def __newClient(message):
+  address = __getServerAddress(readonly=True)
   try:
     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    #print "connect client to", address
+    logger.debug("connect client to %s"%str(address))
     sock.connect(address)
     _send(sock, message)
     response = _receive(sock)
     if response.startswith(GET_PORT_ACK_MSG):
       port = int(response[len(GET_PORT_ACK_MSG)+1:])
-      #print "GET_PORT:", port
+      logger.debug("GET_PORT: %s"%port)
       return port
     elif response == RELEASE_PORT_ACK_MSG:
-      #print "Received: %s" % response
+      logger.debug("Received: %s" % response)
       return 0
       pass
+    elif response == TEST_SERVER_ACK_MSG:
+      logger.debug("Server is ok")
+      return TEST_SERVER_ACK_MSG
+      pass
     sock.close()
   except socket.error:
-    #print "Unable to connect to server"
+    logger.debug("Unable to connect to server")
     return -1
 #
 
 def getPort(preferedPort=None):
   if preferedPort:
-    return __newClient(pm_address, "%s: %s"%(GET_PREFERED_PORT_MSG,preferedPort))
+    return __newClient("%s: %s"%(GET_PREFERED_PORT_MSG,preferedPort))
   else:
-    return __newClient(pm_address, GET_PORT_MSG)
+    return __newClient(GET_PORT_MSG)
 #
 
 def releasePort(port):
-  __newClient(pm_address, "%s: %s"%(RELEASE_PORT_MSG,port))
+  logger.debug("application asks for releasePort %s"%port)
+  __newClient("%s: %s"%(RELEASE_PORT_MSG,port))
 #
 
 def stopServer():
-  __newClient(pm_address, STOP_SERVER_MSG)
+  __newClient(STOP_SERVER_MSG)
 #
 
 # Auto start: unique instance ; no effect if called multiple times
 __startServer()
+logger.debug("Server started... do check...")
+assert(__checkServer())
index 84774cef326c011c1713c1d34f3e1a1c2c56c27b..71dd451736d1ac088a003ae7f0a211eff0868b5c 100755 (executable)
@@ -141,6 +141,12 @@ def shutdownMyPort(port, cleanup=True):
     """
     if not port: return
 
+    try:
+        from PortManager import releasePort
+        releasePort(port)
+    except ImportError:
+        pass
+
     from salome_utils import generateFileName
 
     # set OMNIORB_CONFIG variable to the proper file
@@ -186,6 +192,12 @@ def killMyPort(port):
     Parameters:
     - port - port number
     """
+    try:
+        import PortManager
+        PortManager.releasePort(port)
+    except ImportError:
+        pass
+
     from salome_utils import getShortHostName, getHostName
 
     # try to shutdown session nomally
index bf280c91384f5726f6faae5794f86494ab6edabc..0fb63c0d21f9c4162e211c8dcf75a6d29486e765 100755 (executable)
@@ -923,6 +923,13 @@ def get_env(theAdditionalOptions=[], appname=salomeappname, cfgname=salomecfgnam
         from searchFreePort import searchFreePort
         searchFreePort({})
         print "port:%s"%(os.environ['NSPORT'])
+
+        try:
+            import PortManager
+            PortManager.releasePort(os.environ['NSPORT'])
+        except ImportError:
+            pass
+
         sys.exit(0)
         pass
 
index 859cdaef48a146a98479e19d6d757f1861695db5..f8b8deb407b3d74104a3194d738dd126e3827bab 100644 (file)
-import os\r
-import sys\r
-import logging\r
-import ConfigParser\r
-\r
-from parseConfigFile import parseConfigFile\r
-from parseConfigFile import convertEnvFileToConfigFile\r
-\r
-import tempfile\r
-import pickle\r
-import subprocess\r
-import platform\r
-\r
-from salomeLauncherUtils import SalomeRunnerException\r
-from salomeLauncherUtils import getScriptsAndArgs, formatScriptsAndArgs\r
-\r
-def usage():\r
-  #exeName = os.path.splitext(os.path.basename(__file__))[0]\r
-\r
-  msg = '''\\r
-Usage: salome [command] [options] [--config=file1,...,filen]\r
-\r
-Commands:\r
-    start         Launches SALOME virtual application [DEFAULT]\r
-    shell         Executes a script under SALOME application environment\r
-    connect       Connects a Python console to the active SALOME session\r
-    killall       Kill all SALOME running sessions\r
-    info          Display some information about SALOME\r
-    help          Show this message\r
-    coffee        Yes! SALOME can also make coffee!!"\r
-\r
-Use salome start --help or salome shell --help\r
-to show help on start and shell commands.\r
-'''\r
-\r
-  print msg\r
-#\r
-\r
-"""\r
-The SalomeRunner class in an API to configure SALOME environment then\r
-start SALOME using a single python command.\r
-\r
-"""\r
-class SalomeRunner:\r
-  """\r
-  Initialize environment from a list of configuration files\r
-  identified by their names.\r
-  These files should be in appropriate (new .cfg) format.\r
-  However you can give old .sh environment files; in this case,\r
-  the SalomeRunner class will try to automatically convert them\r
-  to .cfg format before setting the environment.\r
-  """\r
-  def __init__(self, configFileNames=[]):\r
-    #it could be None explicitely (if user use multiples setEnviron...for standalone)\r
-    if configFileNames==None:\r
-       return\r
-\r
-    if len(configFileNames) == 0:\r
-      raise SalomeRunnerException("No configuration files given")\r
-\r
-    reserved=['PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH']\r
-    for filename in configFileNames:\r
-      basename, extension = os.path.splitext(filename)\r
-      if extension == ".cfg":\r
-        self.__setEnvironmentFromConfigFile(filename, reserved)\r
-      elif extension == ".sh":\r
-        #new convert procedures, temporary could be use not to be automatically deleted\r
-        #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False)\r
-        temp = tempfile.NamedTemporaryFile(suffix='.cfg')\r
-        try:\r
-          convertEnvFileToConfigFile(filename, temp.name, reserved)\r
-          self.__setEnvironmentFromConfigFile(temp.name, reserved)\r
-        except ConfigParser.ParsingError, e:\r
-          self.getLogger().warning("Invalid token found when parsing file: %s\n"%(filename))\r
-          print e\r
-          print '\n'\r
-        finally:\r
-          # Automatically cleans up the file\r
-          temp.close()\r
-      else:\r
-        self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)\r
-  #\r
-\r
-  def go(self, args):\r
-    # Run this module as a script, in order to use appropriate Python interpreter\r
-    # according to current path (initialized from environment files).\r
-    absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')\r
-    proc = subprocess.Popen(['python', os.path.join(absoluteAppliPath,"bin","salome","salomeRunner.py"), pickle.dumps(self),  pickle.dumps(args)], shell=False, close_fds=True)\r
-    proc.wait()\r
-  #\r
-\r
-  """Append value to PATH environment variable"""\r
-  def addToPath(self, value):\r
-    self.addToEnviron('PATH', value)\r
-  #\r
-\r
-  """Append value to LD_LIBRARY_PATH environment variable"""\r
-  def addToLdLibraryPath(self, value):\r
-    self.addToEnviron('LD_LIBRARY_PATH', value)\r
-  #\r
-\r
-  """Append value to PYTHONPATH environment variable"""\r
-  def addToPythonPath(self, value):\r
-    self.addToEnviron('PYTHONPATH', value)\r
-  #\r
-\r
-  """Set environment variable to value"""\r
-  def setEnviron(self, name, value, overwrite=False):\r
-    env = os.getenv(name, '')\r
-    if env and not overwrite:\r
-      self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)\r
-      return\r
-\r
-    if env:\r
-      self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)\r
-\r
-    value = os.path.expandvars(value) # expand environment variables\r
-    self.getLogger().debug("Set environment variable: %s=%s", name, value)\r
-    os.environ[name] = value\r
-  #\r
-\r
-  """Unset environment variable"""\r
-  def unsetEnviron(self, name):\r
-    if os.environ.has_key(name):\r
-      del os.environ[name]\r
-  #\r
-\r
-  """Append value to environment variable"""\r
-  def addToEnviron(self, name, value, separator=os.pathsep):\r
-    if value == '':\r
-      return\r
-\r
-    value = os.path.expandvars(value) # expand environment variables\r
-    self.getLogger().debug("Add to %s: %s", name, value)\r
-    env = os.getenv(name, None)\r
-    if env is None:\r
-      os.environ[name] = value\r
-    else:\r
-      os.environ[name] = value + separator + env\r
-  #\r
-\r
-  ###################################\r
-  # This begins the private section #\r
-  ###################################\r
-\r
-  def __parseArguments(self, args):\r
-    if len(args) == 0 or args[0].startswith("-"):\r
-      return None, args\r
-\r
-    command = args[0]\r
-    options = args[1:]\r
-\r
-    availableCommands = {\r
-      'start' :   '_runAppli',\r
-      'shell' :   '_runSession',\r
-      'connect' : '_runConsole',\r
-      'killall':  '_killAll',\r
-      'info':     '_showInfo',\r
-      'help':     '_usage',\r
-      'coffee' :  '_makeCoffee'\r
-      }\r
-\r
-    if not command in availableCommands.keys():\r
-      command = "start"\r
-      options = args\r
-\r
-    return availableCommands[command], options\r
-  #\r
-\r
-  """\r
-  Run SALOME!\r
-  Args consist in a mandatory command followed by optionnal parameters.\r
-  See usage for details on commands.\r
-  """\r
-  def _getStarted(self, args):\r
-    command, options = self.__parseArguments(args)\r
-    sys.argv = options\r
-\r
-    if command is None:\r
-      if args and args[0] in ["-h","--help","help"]:\r
-        usage()\r
-        sys.exit(0)\r
-      # try to default to "start" command\r
-      command = "_runAppli"\r
-\r
-    try:\r
-      res = getattr(self, command)(options) # run appropriate method\r
-      return res or (None, None)\r
-    except SystemExit, exc:\r
-      if exc==0:\r
-        sys.exit(0) #catch sys.exit(0) happy end no warning\r
-      if exc==1:\r
-        self.getLogger().warning("SystemExit 1 in method %s.", command)\r
-      sys.exit(1)\r
-    except StandardError:\r
-      self.getLogger().error("Unexpected error:")\r
-      import traceback\r
-      traceback.print_exc()\r
-      sys.exit(1)\r
-    except SalomeRunnerException, e:\r
-      self.getLogger().error(e)\r
-      sys.exit(1)\r
-  #\r
-\r
-  def __setEnvironmentFromConfigFile(self, filename, reserved=[]):\r
-    unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)\r
-\r
-    # unset variables\r
-    for var in unsetVars:\r
-      self.unsetEnviron(var)\r
-\r
-    # set environment\r
-    for reserved in reservedDict:\r
-      a = filter(None, reservedDict[reserved]) # remove empty elements\r
-      reformattedVals = ':'.join(a)\r
-      self.addToEnviron(reserved, reformattedVals)\r
-      pass\r
-\r
-    for key,val in configVars:\r
-      self.setEnviron(key, val, overwrite=True)\r
-      pass\r
-\r
-    sys.path[:0] = os.getenv('PYTHONPATH','').split(':')\r
-  #\r
-\r
-  def _runAppli(self, args=[]):\r
-    # Initialize SALOME environment\r
-    sys.argv = ['runSalome'] + args\r
-    import setenv\r
-    setenv.main(True)\r
-\r
-    import runSalome\r
-    runSalome.runSalome()\r
-  #\r
-\r
-  def _runSession(self, args=[]):\r
-    sys.argv = ['runSession'] + args\r
-    import runSession\r
-    runSession.configureSession(args)\r
-\r
-    import setenv\r
-    setenv.main(True)\r
-\r
-    scriptArgs = getScriptsAndArgs(args)\r
-    command = formatScriptsAndArgs(scriptArgs)\r
-    if command:\r
-      proc = subprocess.Popen(command, shell=True, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\r
-      return proc.communicate()\r
-    else:\r
-      absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')\r
-      cmd = ["/bin/bash",  "--rcfile", absoluteAppliPath + "/.bashrc" ]\r
-      proc = subprocess.Popen(cmd, shell=False, close_fds=True)\r
-      proc.wait()\r
-  #\r
-\r
-  def _runConsole(self, args=[]):\r
-    # Initialize SALOME environment\r
-    sys.argv = ['runConsole'] + args\r
-    import setenv\r
-    setenv.main(True)\r
-\r
-    import runConsole\r
-    runConsole.connect()\r
-  #\r
-\r
-  def _killAll(self, args=[]):\r
-    from killSalome import killAllPorts\r
-    killAllPorts()\r
-  #\r
-\r
-  def _showInfo(self, args=[]):\r
-    print "Running with python", platform.python_version()\r
-    self._runAppli(["--version"])\r
-  #\r
-\r
-  def _usage(self, unused=[]):\r
-    usage()\r
-  #\r
-\r
-  def _makeCoffee(self, args=[]):\r
-    print "                        ("\r
-    print "                          )     ("\r
-    print "                   ___...(-------)-....___"\r
-    print "               .-\"\"       )    (          \"\"-."\r
-    print "         .-\'``\'|-._             )         _.-|"\r
-    print "        /  .--.|   `\"\"---...........---\"\"`   |"\r
-    print "       /  /    |                             |"\r
-    print "       |  |    |                             |"\r
-    print "        \\  \\   |                             |"\r
-    print "         `\\ `\\ |                             |"\r
-    print "           `\\ `|                             |"\r
-    print "           _/ /\\                             /"\r
-    print "          (__/  \\                           /"\r
-    print "       _..---\"\"` \\                         /`\"\"---.._"\r
-    print "    .-\'           \\                       /          \'-."\r
-    print "   :               `-.__             __.-\'              :"\r
-    print "   :                  ) \"\"---...---\"\" (                 :"\r
-    print "    \'._               `\"--...___...--\"`              _.\'"\r
-    print "      \\\"\"--..__                              __..--\"\"/"\r
-    print "       \'._     \"\"\"----.....______.....----\"\"\"     _.\'"\r
-    print "          `\"\"--..,,_____            _____,,..--\"\"`"\r
-    print "                        `\"\"\"----\"\"\"`"\r
-    sys.exit(0)\r
-  #\r
-\r
-  # Add the following two methods since logger is not pickable\r
-  # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python\r
-  def __getstate__(self):\r
-    d = dict(self.__dict__)\r
-    if hasattr(self, '_logger'):\r
-      del d['_logger']\r
-    return d\r
-  #\r
-  def __setstate__(self, d):\r
-    self.__dict__.update(d) # I *think* this is a safe way to do it\r
-  #\r
-  # Excluding self._logger from pickle operation imply using the following method to access logger\r
-  def getLogger(self):\r
-    if not hasattr(self, '_logger'):\r
-      self._logger = logging.getLogger(__name__)\r
-      #self._logger.setLevel(logging.DEBUG)\r
-      self._logger.setLevel(logging.ERROR)\r
-    return self._logger;\r
-  #\r
-\r
-###\r
-import pickle\r
-if __name__ == "__main__":\r
-  if len(sys.argv) == 3:\r
-    runner = pickle.loads(sys.argv[1])\r
-    args = pickle.loads(sys.argv[2])\r
-    (out, err) = runner._getStarted(args)\r
-    if out:\r
-      sys.stdout.write(out)\r
-    if err:\r
-      sys.stderr.write(err)\r
-  else:\r
-    usage()\r
-#\r
+import os
+import sys
+import logging
+import ConfigParser
+
+from parseConfigFile import parseConfigFile
+from parseConfigFile import convertEnvFileToConfigFile
+
+import tempfile
+import pickle
+import subprocess
+import platform
+
+from salomeLauncherUtils import SalomeRunnerException
+from salomeLauncherUtils import getScriptsAndArgs, formatScriptsAndArgs
+
+def usage():
+  #exeName = os.path.splitext(os.path.basename(__file__))[0]
+
+  msg = '''\
+Usage: salome [command] [options] [--config=file1,...,filen]
+
+Commands:
+    start         Launches SALOME virtual application [DEFAULT]
+    shell         Executes a script under SALOME application environment
+    connect       Connects a Python console to the active SALOME session
+    killall       Kill all SALOME running sessions
+    info          Display some information about SALOME
+    help          Show this message
+    coffee        Yes! SALOME can also make coffee!!"
+
+Use salome start --help or salome shell --help
+to show help on start and shell commands.
+'''
+
+  print msg
+#
+
+"""
+The SalomeRunner class in an API to configure SALOME environment then
+start SALOME using a single python command.
+
+"""
+class SalomeRunner:
+  """
+  Initialize environment from a list of configuration files
+  identified by their names.
+  These files should be in appropriate (new .cfg) format.
+  However you can give old .sh environment files; in this case,
+  the SalomeRunner class will try to automatically convert them
+  to .cfg format before setting the environment.
+  """
+  def __init__(self, configFileNames=[]):
+    #it could be None explicitely (if user use multiples setEnviron...for standalone)
+    if configFileNames==None:
+       return
+
+    if len(configFileNames) == 0:
+      raise SalomeRunnerException("No configuration files given")
+
+    reserved=['PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH']
+    for filename in configFileNames:
+      basename, extension = os.path.splitext(filename)
+      if extension == ".cfg":
+        self.__setEnvironmentFromConfigFile(filename, reserved)
+      elif extension == ".sh":
+        #new convert procedures, temporary could be use not to be automatically deleted
+        #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False)
+        temp = tempfile.NamedTemporaryFile(suffix='.cfg')
+        try:
+          convertEnvFileToConfigFile(filename, temp.name, reserved)
+          self.__setEnvironmentFromConfigFile(temp.name, reserved)
+        except ConfigParser.ParsingError, e:
+          self.getLogger().warning("Invalid token found when parsing file: %s\n"%(filename))
+          print e
+          print '\n'
+        finally:
+          # Automatically cleans up the file
+          temp.close()
+      else:
+        self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)
+  #
+
+  def go(self, args):
+    # Run this module as a script, in order to use appropriate Python interpreter
+    # according to current path (initialized from environment files).
+    absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
+    proc = subprocess.Popen(['python', os.path.join(absoluteAppliPath,"bin","salome","salomeRunner.py"), pickle.dumps(self),  pickle.dumps(args)], shell=False, close_fds=True)
+    proc.wait()
+  #
+
+  """Append value to PATH environment variable"""
+  def addToPath(self, value):
+    self.addToEnviron('PATH', value)
+  #
+
+  """Append value to LD_LIBRARY_PATH environment variable"""
+  def addToLdLibraryPath(self, value):
+    self.addToEnviron('LD_LIBRARY_PATH', value)
+  #
+
+  """Append value to PYTHONPATH environment variable"""
+  def addToPythonPath(self, value):
+    self.addToEnviron('PYTHONPATH', value)
+  #
+
+  """Set environment variable to value"""
+  def setEnviron(self, name, value, overwrite=False):
+    env = os.getenv(name, '')
+    if env and not overwrite:
+      self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
+      return
+
+    if env:
+      self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
+
+    value = os.path.expandvars(value) # expand environment variables
+    self.getLogger().debug("Set environment variable: %s=%s", name, value)
+    os.environ[name] = value
+  #
+
+  """Unset environment variable"""
+  def unsetEnviron(self, name):
+    if os.environ.has_key(name):
+      del os.environ[name]
+  #
+
+  """Append value to environment variable"""
+  def addToEnviron(self, name, value, separator=os.pathsep):
+    if value == '':
+      return
+
+    value = os.path.expandvars(value) # expand environment variables
+    self.getLogger().debug("Add to %s: %s", name, value)
+    env = os.getenv(name, None)
+    if env is None:
+      os.environ[name] = value
+    else:
+      os.environ[name] = value + separator + env
+  #
+
+  ###################################
+  # This begins the private section #
+  ###################################
+
+  def __parseArguments(self, args):
+    if len(args) == 0 or args[0].startswith("-"):
+      return None, args
+
+    command = args[0]
+    options = args[1:]
+
+    availableCommands = {
+      'start' :   '_runAppli',
+      'shell' :   '_runSession',
+      'connect' : '_runConsole',
+      'killall':  '_killAll',
+      'info':     '_showInfo',
+      'help':     '_usage',
+      'coffee' :  '_makeCoffee'
+      }
+
+    if not command in availableCommands.keys():
+      command = "start"
+      options = args
+
+    return availableCommands[command], options
+  #
+
+  """
+  Run SALOME!
+  Args consist in a mandatory command followed by optionnal parameters.
+  See usage for details on commands.
+  """
+  def _getStarted(self, args):
+    command, options = self.__parseArguments(args)
+    sys.argv = options
+
+    if command is None:
+      if args and args[0] in ["-h","--help","help"]:
+        usage()
+        sys.exit(0)
+      # try to default to "start" command
+      command = "_runAppli"
+
+    try:
+      res = getattr(self, command)(options) # run appropriate method
+      return res or (None, None)
+    except SystemExit, exc:
+      if exc==0:
+        sys.exit(0) #catch sys.exit(0) happy end no warning
+      if exc==1:
+        self.getLogger().warning("SystemExit 1 in method %s.", command)
+      sys.exit(1)
+    except StandardError:
+      self.getLogger().error("Unexpected error:")
+      import traceback
+      traceback.print_exc()
+      sys.exit(1)
+    except SalomeRunnerException, e:
+      self.getLogger().error(e)
+      sys.exit(1)
+  #
+
+  def __setEnvironmentFromConfigFile(self, filename, reserved=[]):
+    unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
+
+    # unset variables
+    for var in unsetVars:
+      self.unsetEnviron(var)
+
+    # set environment
+    for reserved in reservedDict:
+      a = filter(None, reservedDict[reserved]) # remove empty elements
+      reformattedVals = ':'.join(a)
+      self.addToEnviron(reserved, reformattedVals)
+      pass
+
+    for key,val in configVars:
+      self.setEnviron(key, val, overwrite=True)
+      pass
+
+    sys.path[:0] = os.getenv('PYTHONPATH','').split(':')
+  #
+
+  def _runAppli(self, args=[]):
+    # Initialize SALOME environment
+    sys.argv = ['runSalome'] + args
+    import setenv
+    setenv.main(True)
+
+    import runSalome
+    runSalome.runSalome()
+  #
+
+  def _runSession(self, args=[]):
+    sys.argv = ['runSession'] + args
+    import runSession
+    runSession.configureSession(args)
+
+    import setenv
+    setenv.main(True)
+
+    scriptArgs = getScriptsAndArgs(args)
+    command = formatScriptsAndArgs(scriptArgs)
+    if command:
+      proc = subprocess.Popen(command, shell=True, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+      return proc.communicate()
+    else:
+      absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
+      cmd = ["/bin/bash",  "--rcfile", absoluteAppliPath + "/.bashrc" ]
+      proc = subprocess.Popen(cmd, shell=False, close_fds=True)
+      proc.wait()
+  #
+
+  def _runConsole(self, args=[]):
+    # Initialize SALOME environment
+    sys.argv = ['runConsole'] + args
+    import setenv
+    setenv.main(True)
+
+    import runConsole
+    runConsole.connect()
+  #
+
+  def _killAll(self, args=[]):
+    from killSalome import killAllPorts
+    killAllPorts()
+  #
+
+  def _showInfo(self, args=[]):
+    print "Running with python", platform.python_version()
+    self._runAppli(["--version"])
+  #
+
+  def _usage(self, unused=[]):
+    usage()
+  #
+
+  def _makeCoffee(self, args=[]):
+    print "                        ("
+    print "                          )     ("
+    print "                   ___...(-------)-....___"
+    print "               .-\"\"       )    (          \"\"-."
+    print "         .-\'``\'|-._             )         _.-|"
+    print "        /  .--.|   `\"\"---...........---\"\"`   |"
+    print "       /  /    |                             |"
+    print "       |  |    |                             |"
+    print "        \\  \\   |                             |"
+    print "         `\\ `\\ |                             |"
+    print "           `\\ `|                             |"
+    print "           _/ /\\                             /"
+    print "          (__/  \\                           /"
+    print "       _..---\"\"` \\                         /`\"\"---.._"
+    print "    .-\'           \\                       /          \'-."
+    print "   :               `-.__             __.-\'              :"
+    print "   :                  ) \"\"---...---\"\" (                 :"
+    print "    \'._               `\"--...___...--\"`              _.\'"
+    print "      \\\"\"--..__                              __..--\"\"/"
+    print "       \'._     \"\"\"----.....______.....----\"\"\"     _.\'"
+    print "          `\"\"--..,,_____            _____,,..--\"\"`"
+    print "                        `\"\"\"----\"\"\"`"
+    sys.exit(0)
+  #
+
+  # Add the following two methods since logger is not pickable
+  # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
+  def __getstate__(self):
+    d = dict(self.__dict__)
+    if hasattr(self, '_logger'):
+      del d['_logger']
+    return d
+  #
+  def __setstate__(self, d):
+    self.__dict__.update(d) # I *think* this is a safe way to do it
+  #
+  # Excluding self._logger from pickle operation imply using the following method to access logger
+  def getLogger(self):
+    if not hasattr(self, '_logger'):
+      self._logger = logging.getLogger(__name__)
+      #self._logger.setLevel(logging.DEBUG)
+      self._logger.setLevel(logging.ERROR)
+    return self._logger;
+  #
+
+###
+import pickle
+if __name__ == "__main__":
+  if len(sys.argv) == 3:
+    runner = pickle.loads(sys.argv[1])
+    args = pickle.loads(sys.argv[2])
+    (out, err) = runner._getStarted(args)
+    if out:
+      sys.stdout.write(out)
+    if err:
+      sys.stderr.write(err)
+  else:
+    usage()
+#
index 07540095d5289eda6b396edc0a7e87ae36ff661c..83e52873453451133abbe2a643dcc7f6344e4af3 100644 (file)
 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
 #
 
-def searchFreePort(args={}, save_config=1, use_port=None):
-  """
-  Search free port for SALOME session.
-  Returns first found free port number.
-  """
-  import sys, os, re, shutil
+import os
+import sys
 
+def __setup_config(nsport, args, save_config):
+  #
+  from salome_utils import generateFileName, getHostName
+  hostname = getHostName()
+  #
+  omniorbUserPath = os.getenv("OMNIORB_USER_PATH")
+  kwargs={}
+  if omniorbUserPath is not None:
+    kwargs["with_username"]=True
+  #
+  from ORBConfigFile import writeORBConfigFile
+  omniorb_config, giopsize = writeORBConfigFile(omniorbUserPath, hostname, nsport, kwargs)
+  args['port'] = os.environ['NSPORT']
+  #
+  if save_config:
+    last_running_config = generateFileName(omniorbUserPath, prefix="omniORB",
+                                           suffix="last",
+                                           extension="cfg",
+                                           hidden=True,
+                                           **kwargs)
+    os.environ['LAST_RUNNING_CONFIG'] = last_running_config
+    try:
+      if sys.platform == "win32":
+        import shutil
+        shutil.copyfile(omniorb_config, last_running_config)
+      else:
+        try:
+          if os.access(last_running_config, os.F_OK):
+            os.remove(last_running_config)
+        except OSError:
+          pass
+        os.symlink(omniorb_config, last_running_config)
+        pass
+      pass
+    except:
+      pass
+  #
+#
+
+def searchFreePort_withoutPortManager(args={}, save_config=1, use_port=None):
   # :NOTE: Under windows:
   #        netstat options -l and -t are unavailable
   #        grep command is unavailable
-
   from subprocess import Popen, PIPE
   (stdout, stderr) = Popen(['netstat','-an'], stdout=PIPE).communicate()
   import StringIO
@@ -41,6 +76,7 @@ def searchFreePort(args={}, save_config=1, use_port=None):
 
   #
   def portIsUsed(port, data):
+    import re
     regObj = re.compile( ".*tcp.*:([0-9]+).*:.*listen", re.IGNORECASE );
     for item in data:
       try:
@@ -53,49 +89,11 @@ def searchFreePort(args={}, save_config=1, use_port=None):
     return False
   #
 
-  def setup_config(nsport):
-      #
-      from salome_utils import generateFileName, getHostName
-      hostname = getHostName()
-      #
-      omniorbUserPath = os.getenv("OMNIORB_USER_PATH")
-      kwargs={}
-      if omniorbUserPath is not None:
-        kwargs["with_username"]=True
-      #
-      from ORBConfigFile import writeORBConfigFile
-      omniorb_config, giopsize = writeORBConfigFile(omniorbUserPath, hostname, nsport, kwargs)
-      args['port'] = os.environ['NSPORT']
-      #
-      if save_config:
-        last_running_config = generateFileName(omniorbUserPath, prefix="omniORB",
-                                               suffix="last",
-                                               extension="cfg",
-                                               hidden=True,
-                                               **kwargs)
-        os.environ['LAST_RUNNING_CONFIG'] = last_running_config
-        try:
-          if sys.platform == "win32":
-            import shutil
-            shutil.copyfile(omniorb_config, last_running_config)
-          else:
-            try:
-              if os.access(last_running_config, os.F_OK):
-                os.remove(last_running_config)
-            except OSError:
-              pass
-            os.symlink(omniorb_config, last_running_config)
-            pass
-          pass
-        except:
-          pass
-      #
-
   if use_port:
     print "Check if port can be used: %d" % use_port,
     if not portIsUsed(use_port, ports):
       print "- OK"
-      setup_config(use_port)
+      __setup_config(use_port, args, save_config)
       return
     else:
       print "- KO: port is busy"
@@ -112,7 +110,7 @@ def searchFreePort(args={}, save_config=1, use_port=None):
   while 1:
     if not portIsUsed(NSPORT, ports):
       print "%s - OK"%(NSPORT)
-      setup_config(NSPORT)
+      __setup_config(NSPORT, args, save_config)
       break
     print "%s"%(NSPORT),
     if NSPORT == limit:
@@ -123,5 +121,41 @@ def searchFreePort(args={}, save_config=1, use_port=None):
     NSPORT=NSPORT+1
     pass
   #
+#
+
+def searchFreePort_withPortManager(args={}, save_config=1, use_port=None):
+  from PortManager import getPort
+  port = getPort(use_port)
 
-  return
+  if use_port:
+    print "Check if port can be used: %d" % use_port,
+    if port == use_port and port != -1:
+      print "- OK"
+      __setup_config(use_port, args, save_config)
+      return
+    else:
+      print "- KO: port is busy"
+      pass
+  #
+  print "Searching for a free port for naming service:",
+  if port == -1: # try again
+    port = getPort(use_port)
+
+  if port != -1:
+    print "%s - OK"%(port)
+    __setup_config(port, args, save_config)
+  else:
+    print "Unable to obtain port"
+#
+
+def searchFreePort(args={}, save_config=1, use_port=None):
+  """
+  Search free port for SALOME session.
+  Returns first found free port number.
+  """
+  try:
+    import PortManager
+    searchFreePort_withPortManager(args, save_config, use_port)
+  except ImportError:
+    searchFreePort_withoutPortManager(args, save_config, use_port)
+#
index 1ae97eef7d288eca7ef0fafd6e3429468cd1aa3a..13634f4984e69ef0713bcc48449f992f47bff8d9 100644 (file)
@@ -620,6 +620,18 @@ void SALOME_LifeCycleCORBA::killOmniNames()
     MESSAGE(cmd);
     system( cmd.c_str() );
   }
+
+#ifdef WITH_PORTMANAGER
+  // shutdown portmanager
+  if ( !portNumber.empty() )
+  {
+    std::string cmd = ("from PortManager import releasePort; ");
+    cmd += std::string("releasePort(") + portNumber + "); ";
+    cmd  = std::string("python -c \"") + cmd +"\" > /dev/null 2> /dev/null";
+    MESSAGE(cmd);
+    system( cmd.c_str() );
+  }
+#endif
 }
 
 //=============================================================================