Salome HOME
restructuration fileEnviron, option --no_path_init pour sat launcher, gestion des...
[tools/sat.git] / commands / launcher.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2013  CEA/DEN
4 #
5 #  This library is free software; you can redistribute it and/or
6 #  modify it under the terms of the GNU Lesser General Public
7 #  License as published by the Free Software Foundation; either
8 #  version 2.1 of the License.
9 #
10 #  This library is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 #  Lesser General Public License for more details.
14 #
15 #  You should have received a copy of the GNU Lesser General Public
16 #  License along with this library; if not, write to the Free Software
17 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18
19 import os
20 import platform
21 import shutil
22 import getpass
23 import subprocess
24 import stat
25
26 import src
27 import src.debug as DBG
28
29 parser = src.options.Options()
30
31 parser.add_option('n', 'name', 'string', 'name', _('Optional: The name of the'
32                             ' launcher (default is '
33                             'APPLICATION.profile.launcher_name)'))
34 parser.add_option('c', 'catalog', 'string', 'catalog',
35     _('Optional: The resources catalog to use'))
36 parser.add_option('', 'gencat', 'string', 'gencat',
37     _("Optional: Create a resources catalog for the specified machines "
38       "(separated with ',') \n\tNOTICE: this command will ssh to retrieve"
39       " information to each machine in the list"))
40 parser.add_option('', 'use_mesa', 'boolean', 'use_mesa',
41     _("Optional: Create a launcher that will use mesa products\n\t"
42       "It can be usefull whan salome is used on a remote machine through ssh"))
43 parser.add_option('', 'no_path_init', 'boolean', 'no_path_init',
44     _("Optional: Create a launcher that will not reinitilise all path variables\n\t"
45       "By default only PATH is not reinitialised (its value is inherited from user's environment)\n\t"
46       "Use no_path_init option to suppress the reinitilisation of every paths (LD_LIBRARY_PATH, PYTHONPATH, ...)"))
47
48 def generate_launch_file(config,
49                          logger,
50                          launcher_name,
51                          pathlauncher,
52                          display=True,
53                          additional_env={},
54                          no_path_init=False):
55     '''Generates the launcher file.
56     
57     :param config Config: The global configuration
58     :param logger Logger: The logger instance to use for the display 
59                           and logging
60     :param launcher_name str: The name of the launcher to generate
61     :param pathlauncher str: The path to the launcher to generate
62     :param display boolean: If False, do not print anything in the terminal
63     :param additional_env dict: The dict giving additional 
64                                 environment variables
65     :return: The launcher file path.
66     :rtype: str
67     '''
68     
69     # Compute the default launcher path if it is not provided in pathlauncher
70     # parameter
71     filepath = os.path.join(pathlauncher, launcher_name)
72
73     # Remove the file if it exists in order to replace it
74     if os.path.exists(filepath):
75         os.remove(filepath)
76
77     # Add the APPLI variable
78     additional_env['APPLI'] = filepath
79
80
81
82     # get KERNEL bin installation path 
83     # (in order for the launcher to get python salomeContext API)
84     kernel_cfg = src.product.get_product_config(config, "KERNEL")
85     if not src.product.check_installation(config, kernel_cfg):
86         raise src.SatException(_("KERNEL is not installed"))
87     kernel_root_dir = kernel_cfg.install_dir
88
89     # set kernel bin dir (considering fhs property)
90     if src.get_property_in_product_cfg(kernel_cfg, "fhs"):
91         bin_kernel_install_dir = os.path.join(kernel_root_dir,"bin") 
92     else:
93         bin_kernel_install_dir = os.path.join(kernel_root_dir,"bin","salome") 
94
95     # check if the application contains an application module
96     l_product_info = src.product.get_products_infos(config.APPLICATION.products.keys(),
97                                                     config)
98     salome_application_name="Not defined" 
99     for prod_name, prod_info in l_product_info:
100         # look for a salome application
101         if src.get_property_in_product_cfg(prod_info, "is_salome_application") == "yes":
102             salome_application_name=prod_info.install_dir
103             continue
104
105     # if the application contains an application module, we set ABSOLUTE_APPLI_PATH to it.
106     # if not we set it to KERNEL_INSTALL_DIR, which is sufficient, except for salome test
107     if salome_application_name == "Not defined":
108         app_root_dir=kernel_root_dir
109     else:
110         app_root_dir=salome_application_name
111
112     # Get the launcher template (python3 or python2)
113     if "python3" in config.APPLICATION and config.APPLICATION.python3 == "yes":
114         withProfile = src.fileEnviron.withProfile3\
115                          .replace("BIN_KERNEL_INSTALL_DIR", bin_kernel_install_dir)\
116                          .replace("KERNEL_INSTALL_DIR", app_root_dir)
117     else:
118         withProfile = src.fileEnviron.withProfile\
119                          .replace("BIN_KERNEL_INSTALL_DIR", bin_kernel_install_dir)\
120                          .replace("KERNEL_INSTALL_DIR", app_root_dir)
121
122     before, after = withProfile.split("# here your local standalone environment\n")
123
124     # create an environment file writer
125     writer = src.environment.FileEnvWriter(config,
126                                            logger,
127                                            pathlauncher,
128                                            src_root=None,
129                                            env_info=None)
130
131     # Display some information
132     if display:
133         # Write the launcher file
134         logger.write(_("Generating launcher for %s :\n") % 
135                      src.printcolors.printcLabel(config.VARS.application), 1)
136         logger.write("  %s\n" % src.printcolors.printcLabel(filepath), 1)
137     
138     # open the file and write into it
139     launch_file = open(filepath, "w")
140     launch_file.write(before)
141     # Write
142     writer.write_cfgForPy_file(launch_file, additional_env=additional_env,
143                                no_path_init=no_path_init)
144     launch_file.write(after)
145     launch_file.close()
146     
147     # change the rights in order to make the file executable for everybody
148     os.chmod(filepath,
149              stat.S_IRUSR |
150              stat.S_IRGRP |
151              stat.S_IROTH |
152              stat.S_IWUSR |
153              stat.S_IXUSR |
154              stat.S_IXGRP |
155              stat.S_IXOTH)
156     return filepath
157
158
159 def generate_catalog(machines, config, logger):
160     """Generates an xml catalog file from a list of machines.
161     
162     :param machines List: The list of machines to add in the catalog   
163     :param config Config: The global configuration
164     :param logger Logger: The logger instance to use for the display 
165                           and logging
166     :return: The catalog file path.
167     :rtype: str
168     """
169     # remove empty machines
170     machines = map(lambda l: l.strip(), machines)
171     machines = filter(lambda l: len(l) > 0, machines)
172     
173     # log something
174     src.printcolors.print_value(logger, _("Generate Resources Catalog"),
175                                 ", ".join(machines), 4)
176     
177     # The command to execute on each machine in order to get some information
178     cmd = '"cat /proc/cpuinfo | grep MHz ; cat /proc/meminfo | grep MemTotal"'
179     user = getpass.getuser()
180
181     # Create the catalog path
182     catfile = src.get_tmp_filename(config, "CatalogResources.xml")
183     catalog = file(catfile, "w")
184     
185     # Write into it
186     catalog.write("<!DOCTYPE ResourcesCatalog>\n<resources>\n")
187     for k in machines:
188         if not src.architecture.is_windows(): 
189             logger.write("    ssh %s " % (k + " ").ljust(20, '.'), 4)
190             logger.flush()
191
192             # Verify that the machine is accessible
193             ssh_cmd = 'ssh -o "StrictHostKeyChecking no" %s %s' % (k, cmd)
194             p = subprocess.Popen(ssh_cmd, shell=True,
195                     stdin=subprocess.PIPE,
196                     stdout=subprocess.PIPE,
197                     stderr=subprocess.PIPE)
198             p.wait()
199
200             machine_access = (p.returncode == 0) 
201             if not machine_access: # The machine is not accessible
202                 logger.write(src.printcolors.printc(src.KO_STATUS) + "\n", 4)
203                 logger.write("    " + 
204                              src.printcolors.printcWarning(p.stderr.read()), 2)
205             else:
206                 # The machine is accessible, write the corresponding section on
207                 # the xml file
208                 logger.write(src.printcolors.printc(src.OK_STATUS) + "\n", 4)
209                 lines = p.stdout.readlines()
210                 freq = lines[0][:-1].split(':')[-1].split('.')[0].strip()
211                 nb_proc = len(lines) -1
212                 memory = lines[-1].split(':')[-1].split()[0].strip()
213                 memory = int(memory) / 1000
214
215         catalog.write("    <machine\n")
216         catalog.write("        protocol=\"ssh\"\n")
217         catalog.write("        nbOfNodes=\"1\"\n")
218         catalog.write("        mode=\"interactif\"\n")
219         catalog.write("        OS=\"LINUX\"\n")
220
221         if (not src.architecture.is_windows()) and machine_access :
222             catalog.write("        CPUFreqMHz=\"%s\"\n" % freq)
223             catalog.write("        nbOfProcPerNode=\"%s\"\n" % nb_proc)
224             catalog.write("        memInMB=\"%s\"\n" % memory)
225
226         catalog.write("        userName=\"%s\"\n" % user)
227         catalog.write("        name=\"%s\"\n" % k)
228         catalog.write("        hostname=\"%s\"\n" % k)
229         catalog.write("    >\n")
230         catalog.write("    </machine>\n")
231
232     catalog.write("</resources>\n")
233     catalog.close()
234     return catfile
235
236 def copy_catalog(config, catalog_path):
237     """Copy the xml catalog file into the right location
238     
239     :param config Config: The global configuration
240     :param catalog_path str: the catalog file path
241     :return: The environment dictionary corresponding to the file path.
242     :rtype: Dict
243     """
244     # Verify the existence of the file
245     if not os.path.exists(catalog_path):
246         raise IOError(_("Catalog not found: %s") % catalog_path)
247     # Get the application directory and copy catalog inside
248     out_dir = config.APPLICATION.workdir
249     new_catalog_path = os.path.join(out_dir, "CatalogResources.xml")
250     # Do the copy
251     if catalog_path != new_catalog_path:
252         shutil.copy(catalog_path, new_catalog_path)
253     additional_environ = {'USER_CATALOG_RESOURCES_FILE' : new_catalog_path}
254     return additional_environ
255
256
257
258 ##################################################
259
260 ##
261 # Describes the command
262 def description():
263     return _("The launcher command generates a SALOME launcher.\n\nexample:"
264              "\nsat launcher SALOME-master")
265
266 ##
267 # Runs the command.
268 def run(args, runner, logger):
269
270     # check for product
271     (options, args) = parser.parse_args(args)
272
273     # Verify that the command was called with an application
274     src.check_config_has_application( runner.cfg )
275     
276     # Determine the launcher name (from option, profile section or by default "salome")
277     if options.name:
278         launcher_name = options.name
279     else:
280         launcher_name = src.get_launcher_name(runner.cfg)
281
282     no_path_initialisation=False
283     if options.no_path_init:
284         no_path_initialisation = True
285         
286     # set the launcher path
287     launcher_path = runner.cfg.APPLICATION.workdir
288
289     # Copy a catalog if the option is called
290     additional_environ = {}
291     if options.catalog:
292         additional_environ = copy_catalog(runner.cfg, options.catalog)
293
294     # Generate a catalog of resources if the corresponding option was called
295     if options.gencat:
296         catalog_path  = generate_catalog(options.gencat.split(","),
297                                          runner.cfg,
298                                          logger)
299         additional_environ = copy_catalog(runner.cfg, catalog_path)
300
301     # activate mesa use in the generated launcher
302     if options.use_mesa:
303         src.activate_mesa_property(runner.cfg)
304
305     # Generate the launcher
306     launcherPath = generate_launch_file( runner.cfg,
307                                          logger,
308                                          launcher_name,
309                                          launcher_path,
310                                          additional_env = additional_environ,
311                                          no_path_init = no_path_initialisation )
312
313     return 0