]> SALOME platform Git repositories - modules/kernel.git/blob - bin/salomeRunner.py
Salome HOME
Bug fix on launcher for concurrent execution
[modules/kernel.git] / bin / salomeRunner.py
1 import os
2 import sys
3 import logging
4 import ConfigParser
5
6 from parseConfigFile import parseConfigFile
7 from parseConfigFile import convertEnvFileToConfigFile
8
9 import tempfile
10 import pickle
11 import subprocess
12 import platform
13
14 from salomeLauncherUtils import SalomeRunnerException
15 from salomeLauncherUtils import getScriptsAndArgs, formatScriptsAndArgs
16
17 def usage():
18   #exeName = os.path.splitext(os.path.basename(__file__))[0]
19
20   msg = '''\
21 Usage: salome [command] [options] [--config=file1,...,filen]
22
23 Commands:
24     start         Launches SALOME virtual application [DEFAULT]
25     shell         Executes a script under SALOME application environment
26     connect       Connects a Python console to the active SALOME session
27     killall       Kill all SALOME running sessions
28     info          Display some information about SALOME
29     help          Show this message
30     coffee        Yes! SALOME can also make coffee!!"
31
32 Use salome start --help or salome shell --help
33 to show help on start and shell commands.
34 '''
35
36   print msg
37 #
38
39 """
40 The SalomeRunner class in an API to configure SALOME environment then
41 start SALOME using a single python command.
42
43 """
44 class SalomeRunner:
45   """
46   Initialize environment from a list of configuration files
47   identified by their names.
48   These files should be in appropriate (new .cfg) format.
49   However you can give old .sh environment files; in this case,
50   the SalomeRunner class will try to automatically convert them
51   to .cfg format before setting the environment.
52   """
53   def __init__(self, configFileNames=[]):
54     #it could be None explicitely (if user use multiples setEnviron...for standalone)
55     if configFileNames==None:
56        return
57
58     if len(configFileNames) == 0:
59       raise SalomeRunnerException("No configuration files given")
60
61     reserved=['PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'PV_PLUGIN_PATH']
62     for filename in configFileNames:
63       basename, extension = os.path.splitext(filename)
64       if extension == ".cfg":
65         self.__setEnvironmentFromConfigFile(filename, reserved)
66       elif extension == ".sh":
67         #new convert procedures, temporary could be use not to be automatically deleted
68         #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False)
69         temp = tempfile.NamedTemporaryFile(suffix='.cfg')
70         try:
71           convertEnvFileToConfigFile(filename, temp.name, reserved)
72           self.__setEnvironmentFromConfigFile(temp.name, reserved)
73         except ConfigParser.ParsingError, e:
74           self.getLogger().warning("Invalid token found when parsing file: %s\n"%(filename))
75           print e
76           print '\n'
77         finally:
78           # Automatically cleans up the file
79           temp.close()
80       else:
81         self.getLogger().warning("Unrecognized extension for configuration file: %s", filename)
82   #
83
84   def go(self, args):
85     # Run this module as a script, in order to use appropriate Python interpreter
86     # according to current path (initialized from environment files).
87     absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
88     proc = subprocess.Popen(['python', os.path.join(absoluteAppliPath,"bin","salome","salomeRunner.py"), pickle.dumps(self),  pickle.dumps(args)], shell=False, close_fds=True)
89     proc.communicate()
90   #
91
92   """Append value to PATH environment variable"""
93   def addToPath(self, value):
94     self.addToEnviron('PATH', value)
95   #
96
97   """Append value to LD_LIBRARY_PATH environment variable"""
98   def addToLdLibraryPath(self, value):
99     self.addToEnviron('LD_LIBRARY_PATH', value)
100   #
101
102   """Append value to PYTHONPATH environment variable"""
103   def addToPythonPath(self, value):
104     self.addToEnviron('PYTHONPATH', value)
105   #
106
107   """Set environment variable to value"""
108   def setEnviron(self, name, value, overwrite=False):
109     env = os.getenv(name, '')
110     if env and not overwrite:
111       self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value)
112       return
113
114     if env:
115       self.getLogger().warning("Overwriting environment variable: %s=%s", name, value)
116
117     value = os.path.expandvars(value) # expand environment variables
118     self.getLogger().debug("Set environment variable: %s=%s", name, value)
119     os.environ[name] = value
120   #
121
122   """Unset environment variable"""
123   def unsetEnviron(self, name):
124     if os.environ.has_key(name):
125       del os.environ[name]
126   #
127
128   """Append value to environment variable"""
129   def addToEnviron(self, name, value, separator=os.pathsep):
130     if value == '':
131       return
132
133     value = os.path.expandvars(value) # expand environment variables
134     self.getLogger().debug("Add to %s: %s", name, value)
135     env = os.getenv(name, None)
136     if env is None:
137       os.environ[name] = value
138     else:
139       os.environ[name] = value + separator + env
140   #
141
142   ###################################
143   # This begins the private section #
144   ###################################
145
146   def __parseArguments(self, args):
147     if len(args) == 0 or args[0].startswith("-"):
148       return None, args
149
150     command = args[0]
151     options = args[1:]
152
153     availableCommands = {
154       'start' :   '_runAppli',
155       'shell' :   '_runSession',
156       'connect' : '_runConsole',
157       'killall':  '_killAll',
158       'info':     '_showInfo',
159       'help':     '_usage',
160       'coffee' :  '_makeCoffee'
161       }
162
163     if not command in availableCommands.keys():
164       command = "start"
165       options = args
166
167     return availableCommands[command], options
168   #
169
170   """
171   Run SALOME!
172   Args consist in a mandatory command followed by optionnal parameters.
173   See usage for details on commands.
174   """
175   def _getStarted(self, args):
176     command, options = self.__parseArguments(args)
177     sys.argv = options
178
179     if command is None:
180       if args and args[0] in ["-h","--help","help"]:
181         usage()
182         sys.exit(0)
183       # try to default to "start" command
184       command = "_runAppli"
185
186     try:
187       res = getattr(self, command)(options) # run appropriate method
188       return res or (None, None)
189     except SystemExit, exc:
190       if exc==0:
191         sys.exit(0) #catch sys.exit(0) happy end no warning
192       if exc==1:
193         self.getLogger().warning("SystemExit 1 in method %s.", command)
194       sys.exit(1)
195     except StandardError:
196       self.getLogger().error("Unexpected error:")
197       import traceback
198       traceback.print_exc()
199       sys.exit(1)
200     except SalomeRunnerException, e:
201       self.getLogger().error(e)
202       sys.exit(1)
203   #
204
205   def __setEnvironmentFromConfigFile(self, filename, reserved=[]):
206     unsetVars, configVars, reservedDict = parseConfigFile(filename, reserved)
207
208     # unset variables
209     for var in unsetVars:
210       self.unsetEnviron(var)
211
212     # set environment
213     for reserved in reservedDict:
214       a = filter(None, reservedDict[reserved]) # remove empty elements
215       reformattedVals = ':'.join(a)
216       self.addToEnviron(reserved, reformattedVals)
217       pass
218
219     for key,val in configVars:
220       self.setEnviron(key, val, overwrite=True)
221       pass
222
223     sys.path[:0] = os.getenv('PYTHONPATH','').split(':')
224   #
225
226   def _runAppli(self, args=[]):
227     # Initialize SALOME environment
228     sys.argv = ['runSalome'] + args
229     import setenv
230     setenv.main(True)
231
232     import runSalome
233     runSalome.runSalome()
234   #
235
236   def _runSession(self, args=[]):
237     sys.argv = ['runSession'] + args
238     import runSession
239     runSession.configureSession(args)
240
241     import setenv
242     setenv.main(True)
243
244     scriptArgs = getScriptsAndArgs(args)
245     command = formatScriptsAndArgs(scriptArgs)
246     if command:
247       command = command.split(' ')
248       proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
249       return proc.communicate()
250     else:
251       absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
252       cmd = ["/bin/bash",  "--rcfile", absoluteAppliPath + "/.bashrc" ]
253       proc = subprocess.Popen(cmd, shell=False, close_fds=True)
254       proc.wait()
255   #
256
257   def _runConsole(self, args=[]):
258     # Initialize SALOME environment
259     sys.argv = ['runConsole'] + args
260     import setenv
261     setenv.main(True)
262
263     import runConsole
264     runConsole.connect()
265   #
266
267   def _killAll(self, args=[]):
268     absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
269     try:
270       import PortManager
271       ports = PortManager.getBusyPorts()
272
273       from multiprocessing import Process
274       from killSalomeWithPort import killMyPort
275       if ports:
276         import tempfile
277         for port in ports:
278           with tempfile.NamedTemporaryFile():
279             p = Process(target = killMyPort, args=(port,))
280             p.start()
281             p.join()
282     except ImportError:
283       pass
284
285     p = Process(target = killMyPort, args=(2809,))
286     p.start()
287     p.join()
288
289   #
290
291   def _showInfo(self, args=[]):
292     print "Running with python", platform.python_version()
293     self._runAppli(["--version"])
294   #
295
296   def _usage(self, unused=[]):
297     usage()
298   #
299
300   def _makeCoffee(self, args=[]):
301     print "                        ("
302     print "                          )     ("
303     print "                   ___...(-------)-....___"
304     print "               .-\"\"       )    (          \"\"-."
305     print "         .-\'``\'|-._             )         _.-|"
306     print "        /  .--.|   `\"\"---...........---\"\"`   |"
307     print "       /  /    |                             |"
308     print "       |  |    |                             |"
309     print "        \\  \\   |                             |"
310     print "         `\\ `\\ |                             |"
311     print "           `\\ `|                             |"
312     print "           _/ /\\                             /"
313     print "          (__/  \\                           /"
314     print "       _..---\"\"` \\                         /`\"\"---.._"
315     print "    .-\'           \\                       /          \'-."
316     print "   :               `-.__             __.-\'              :"
317     print "   :                  ) \"\"---...---\"\" (                 :"
318     print "    \'._               `\"--...___...--\"`              _.\'"
319     print "      \\\"\"--..__                              __..--\"\"/"
320     print "       \'._     \"\"\"----.....______.....----\"\"\"     _.\'"
321     print "          `\"\"--..,,_____            _____,,..--\"\"`"
322     print "                        `\"\"\"----\"\"\"`"
323     sys.exit(0)
324   #
325
326   # Add the following two methods since logger is not pickable
327   # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
328   def __getstate__(self):
329     d = dict(self.__dict__)
330     if hasattr(self, '_logger'):
331       del d['_logger']
332     return d
333   #
334   def __setstate__(self, d):
335     self.__dict__.update(d) # I *think* this is a safe way to do it
336   #
337   # Excluding self._logger from pickle operation imply using the following method to access logger
338   def getLogger(self):
339     if not hasattr(self, '_logger'):
340       self._logger = logging.getLogger(__name__)
341       #self._logger.setLevel(logging.DEBUG)
342       self._logger.setLevel(logging.ERROR)
343     return self._logger;
344   #
345
346 ###
347 import pickle
348 if __name__ == "__main__":
349   if len(sys.argv) == 3:
350     runner = pickle.loads(sys.argv[1])
351     args = pickle.loads(sys.argv[2])
352     (out, err) = runner._getStarted(args)
353     if out:
354       sys.stdout.write(out)
355     if err:
356       sys.stderr.write(err)
357   else:
358     usage()
359 #