Salome HOME
save port to log file (--ns-port-log)
[modules/yacs.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 # mandatory
271       from multiprocessing import Process
272       from killSalomeWithPort import killMyPort
273       ports = PortManager.getBusyPorts()
274
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
283       p = Process(target = killMyPort, args=(2809,))
284       p.start()
285       p.join()
286     except ImportError:
287       from killSalome import killAllPorts
288       killAllPorts()
289       pass
290
291   #
292
293   def _showInfo(self, args=[]):
294     print "Running with python", platform.python_version()
295     self._runAppli(["--version"])
296   #
297
298   def _usage(self, unused=[]):
299     usage()
300   #
301
302   def _makeCoffee(self, args=[]):
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     print "          `\"\"--..,,_____            _____,,..--\"\"`"
324     print "                        `\"\"\"----\"\"\"`"
325     sys.exit(0)
326   #
327
328   # Add the following two methods since logger is not pickable
329   # Ref: http://stackoverflow.com/questions/2999638/how-to-stop-attributes-from-being-pickled-in-python
330   def __getstate__(self):
331     d = dict(self.__dict__)
332     if hasattr(self, '_logger'):
333       del d['_logger']
334     return d
335   #
336   def __setstate__(self, d):
337     self.__dict__.update(d) # I *think* this is a safe way to do it
338   #
339   # Excluding self._logger from pickle operation imply using the following method to access logger
340   def getLogger(self):
341     if not hasattr(self, '_logger'):
342       self._logger = logging.getLogger(__name__)
343       #self._logger.setLevel(logging.DEBUG)
344       self._logger.setLevel(logging.ERROR)
345     return self._logger;
346   #
347
348 ###
349 import pickle
350 if __name__ == "__main__":
351   if len(sys.argv) == 3:
352     runner = pickle.loads(sys.argv[1])
353     args = pickle.loads(sys.argv[2])
354     (out, err) = runner._getStarted(args)
355     if out:
356       sys.stdout.write(out)
357     if err:
358       sys.stderr.write(err)
359   else:
360     usage()
361 #