]> SALOME platform Git repositories - modules/yacs.git/blob - bin/PortManager.py
Salome HOME
[PortManager] Separate ports according to their owner (this or other application)
[modules/yacs.git] / bin / PortManager.py
1 #!/usr/bin/env python
2 #  -*- coding: iso-8859-1 -*-
3 # Copyright (C) 2007-2017  CEA/DEN, EDF R&D, OPEN CASCADE
4 #
5 # Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
6 # CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
7 #
8 # This library is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
12 #
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 # Lesser General Public License for more details.
17 #
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21 #
22 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 #
24 import os
25 import sys
26
27 try:
28   import cPickle as pickle #@UnusedImport
29 except:
30   import pickle #@Reimport
31
32 __PORT_MIN_NUMBER = 2810
33 __PORT_MAX_NUMBER = 2910
34
35 import logging
36 def createLogger():
37   logger = logging.getLogger(__name__)
38 #  logger.setLevel(logging.DEBUG)
39   logger.setLevel(logging.INFO)
40   ch = logging.StreamHandler()
41   ch.setLevel(logging.DEBUG)
42   formatter = logging.Formatter("%(levelname)s:%(threadName)s:%(message)s")
43   ch.setFormatter(formatter)
44   logger.addHandler(ch)
45   return logger
46 #
47 logger = createLogger()
48
49 #------------------------------------
50 # A file locker
51 def __acquire_lock(lock):
52   if sys.platform == "win32":
53     import msvcrt
54     # lock 1 byte: file is supposed to be zero-byte long
55     msvcrt.locking(lock.fileno(), msvcrt.LK_LOCK, 1)
56   else:
57     import fcntl
58     fcntl.flock(lock, fcntl.LOCK_EX)
59 #
60 def __release_lock(lock):
61   if sys.platform == "win32":
62     import msvcrt
63     msvcrt.locking(lock.fileno(), msvcrt.LK_UNLCK, 1)
64   else:
65     import fcntl
66     fcntl.flock(lock, fcntl.LOCK_UN)
67 #
68 #------------------------------------
69
70 def _getConfigurationFilename():
71   omniorbUserPath = os.getenv("OMNIORB_USER_PATH")
72
73   from salome_utils import generateFileName
74   portmanager_config = generateFileName(omniorbUserPath,
75                                         prefix="omniORB",
76                                         suffix="PortManager",
77                                         extension="cfg",
78                                         hidden=True)
79   import tempfile
80   temp = tempfile.NamedTemporaryFile()
81   lock_file = os.path.join(os.path.dirname(temp.name), ".omniORB_PortManager.lock")
82   temp.close()
83
84   return (portmanager_config, lock_file)
85 #
86
87 def __isPortUsed(port, busy_ports):
88   return (port in busy_ports) or __isNetworkConnectionActiveOnPort(port)
89 #
90
91 def __isNetworkConnectionActiveOnPort(port):
92   # :NOTE: Under windows:
93   #        netstat options -l and -t are unavailable
94   #        grep command is unavailable
95   if sys.platform == "win32":
96     cmd = ['netstat','-a','-n','-p tcp']
97   else:
98     cmd = ['netstat','-ant']
99     pass
100
101   err = None
102   try:
103     from subprocess import Popen, PIPE, STDOUT
104     p = Popen(cmd, stdout=PIPE, stderr=STDOUT)
105     out, err = p.communicate()
106   except:
107     print "Error when trying to access active network connections."
108     if err: print err
109     import traceback
110     traceback.print_exc()
111     return False
112
113   import StringIO
114   buf = StringIO.StringIO(out)
115   ports = buf.readlines()
116   # search for TCP - LISTEN connections
117   import re
118   regObj = re.compile( ".*tcp.*:([0-9]+).*:.*listen", re.IGNORECASE );
119   for item in ports:
120     try:
121       p = int(regObj.match(item).group(1))
122       if p == port: return True
123     except:
124       pass
125   return False
126 #
127
128 def getPort(preferedPort=None):
129   logger.debug("GET PORT")
130
131   config_file, lock_file = _getConfigurationFilename()
132   oldmask = os.umask(0)
133   with open(lock_file, 'w') as lock:
134     # acquire lock
135     __acquire_lock(lock)
136
137     # read config
138     config = {'busy_ports':[]}
139     logger.debug("read configuration file")
140     try:
141       with open(config_file, 'r') as f:
142         config = pickle.load(f)
143     except:
144       logger.info("Problem loading PortManager file: %s"%config_file)
145       # In this case config dictionary is reset
146
147     logger.debug("load busy_ports: %s"%str(config["busy_ports"]))
148
149     # append port
150     ports_info = config["busy_ports"]
151     busy_ports = [x[0] for x in ports_info]
152     port = preferedPort
153     if not port or __isPortUsed(port, busy_ports):
154       port = __PORT_MIN_NUMBER
155       while __isPortUsed(port, busy_ports):
156         if port == __PORT_MAX_NUMBER:
157           msg  = "\n"
158           msg += "Can't find a free port to launch omniNames\n"
159           msg += "Try to kill the running servers and then launch SALOME again.\n"
160           raise RuntimeError, msg
161         logger.debug("Port %s seems to be busy"%str(port))
162         if not port in busy_ports:
163           config["busy_ports"].append((port,"other"))
164         port = port + 1
165     logger.debug("found free port: %s"%str(port))
166     config["busy_ports"].append((port,"this"))
167
168     # write config, for this application only (i.e. no 'other' ports)
169     logger.debug("all busy_ports: %s"%str(config["busy_ports"]))
170     ports_info = config["busy_ports"]
171     config["busy_ports"] = [x for x in ports_info if x[1] == "this"]
172     logger.debug("write busy_ports: %s"%str(config["busy_ports"]))
173     try:
174       with open(config_file, 'w') as f:
175         pickle.dump(config, f)
176     except IOError:
177       pass
178
179     # release lock
180     __release_lock(lock)
181   #
182
183   os.umask(oldmask)
184   logger.debug("get port: %s"%str(port))
185   return port
186 #
187
188 def releasePort(port):
189   port = int(port)
190   logger.debug("RELEASE PORT (%s)"%port)
191
192   config_file, lock_file = _getConfigurationFilename()
193   oldmask = os.umask(0)
194   with open(lock_file, 'w') as lock:
195     # acquire lock
196     __acquire_lock(lock)
197
198     # read config
199     config = {'busy_ports':[]}
200     logger.debug("read configuration file")
201     try:
202       with open(config_file, 'r') as f:
203         config = pickle.load(f)
204     except IOError: # empty file
205       pass
206
207     logger.debug("load busy_ports: %s"%str(config["busy_ports"]))
208
209     # remove port from list
210     ports_info = config["busy_ports"]
211     config["busy_ports"] = [x for x in ports_info if x[0] != port]
212
213     # write config, for this application only (i.e. no 'other' ports)
214     logger.debug("all busy_ports: %s"%str(config["busy_ports"]))
215     ports_info = config["busy_ports"]
216     config["busy_ports"] = [x for x in ports_info if x[1] == "this"]
217     logger.debug("write busy_ports: %s"%str(config["busy_ports"]))
218     try:
219       with open(config_file, 'w') as f:
220         pickle.dump(config, f)
221     except IOError:
222       pass
223
224     # release lock
225     __release_lock(lock)
226
227     logger.debug("released port port: %s"%str(port))
228
229   os.umask(oldmask)
230 #
231
232 def getBusyPorts():
233   config_file, lock_file = _getConfigurationFilename()
234   oldmask = os.umask(0)
235   with open(lock_file, 'w') as lock:
236     # acquire lock
237     __acquire_lock(lock)
238
239     # read config
240     config = {'busy_ports':[]}
241     logger.debug("read configuration file")
242     try:
243       with open(config_file, 'r') as f:
244         config = pickle.load(f)
245     except IOError: # empty file
246       pass
247
248     logger.debug("load busy_ports: %s"%str(config["busy_ports"]))
249
250     # Scan all possible ports to determine which ones are owned by other applications
251     ports_info = config["busy_ports"]
252     busy_ports = [x[0] for x in ports_info]
253     for port in range(__PORT_MIN_NUMBER, __PORT_MAX_NUMBER):
254       if __isPortUsed(port, busy_ports):
255         logger.debug("Port %s seems to be busy"%str(port))
256         if not port in busy_ports:
257           config["busy_ports"].append((port,"other"))
258
259     logger.debug("all busy_ports: %s"%str(config["busy_ports"]))
260
261     ports_info = config["busy_ports"]
262     ports_info.sort(key=lambda x: x[0])
263
264     # release lock
265     __release_lock(lock)
266
267   os.umask(oldmask)
268   return ports_info
269 #