Salome HOME
spns #8849 : generation d'un launcher python3
[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
44 def generate_launch_file(config,
45                          logger,
46                          launcher_name,
47                          pathlauncher,
48                          display=True,
49                          additional_env={}):
50     '''Generates the launcher file.
51     
52     :param config Config: The global configuration
53     :param logger Logger: The logger instance to use for the display 
54                           and logging
55     :param launcher_name str: The name of the launcher to generate
56     :param pathlauncher str: The path to the launcher to generate
57     :param display boolean: If False, do not print anything in the terminal
58     :param additional_env dict: The dict giving additional 
59                                 environment variables
60     :return: The launcher file path.
61     :rtype: str
62     '''
63     
64     # Compute the default launcher path if it is not provided in pathlauncher
65     # parameter
66     filepath = os.path.join(pathlauncher, launcher_name)
67
68     # Remove the file if it exists in order to replace it
69     if os.path.exists(filepath):
70         os.remove(filepath)
71
72     # Add the APPLI variable
73     additional_env['APPLI'] = filepath
74
75
76
77     # get KERNEL bin installation path 
78     # (in order for the launcher to get python salomeContext API)
79     kernel_cfg = src.product.get_product_config(config, "KERNEL")
80     if not src.product.check_installation(kernel_cfg):
81         raise src.SatException(_("KERNEL is not installed"))
82     kernel_root_dir = kernel_cfg.install_dir
83
84     # set kernel bin dir (considering fhs property)
85     if src.get_property_in_product_cfg(kernel_cfg, "fhs"):
86         bin_kernel_install_dir = os.path.join(kernel_root_dir,"bin") 
87     else:
88         bin_kernel_install_dir = os.path.join(kernel_root_dir,"bin","salome") 
89
90     # check if the application contains an application module
91     l_product_info = src.product.get_products_infos(config.APPLICATION.products.keys(),
92                                                     config)
93     salome_application_name="Not defined" 
94     for prod_name, prod_info in l_product_info:
95         # look for a salome application
96         if src.get_property_in_product_cfg(prod_info, "is_salome_application") == "yes":
97             salome_application_name=prod_info.install_dir
98             continue
99
100     # if the application contains an application module, we set ABSOLUTE_APPLI_PATH to it.
101     # if not we set it to KERNEL_INSTALL_DIR, which is sufficient, except for salome test
102     if salome_application_name == "Not defined":
103         app_root_dir=kernel_root_dir
104     else:
105         app_root_dir=salome_application_name
106
107     # Get the launcher template (python3 or python2)
108     if "python3" in config.APPLICATION and config.APPLICATION.python3 == "yes":
109         withProfile = src.fileEnviron.withProfile3\
110                          .replace("BIN_KERNEL_INSTALL_DIR", bin_kernel_install_dir)\
111                          .replace("KERNEL_INSTALL_DIR", app_root_dir)
112     else:
113         withProfile = src.fileEnviron.withProfile\
114                          .replace("BIN_KERNEL_INSTALL_DIR", bin_kernel_install_dir)\
115                          .replace("KERNEL_INSTALL_DIR", app_root_dir)
116
117     before, after = withProfile.split(
118                                 "# here your local standalone environment\n")
119
120     # create an environment file writer
121     writer = src.environment.FileEnvWriter(config,
122                                            logger,
123                                            pathlauncher,
124                                            src_root=None,
125                                            env_info=None)
126
127     # Display some information
128     if display:
129         # Write the launcher file
130         logger.write(_("Generating launcher for %s :\n") % 
131                      src.printcolors.printcLabel(config.VARS.application), 1)
132         logger.write("  %s\n" % src.printcolors.printcLabel(filepath), 1)
133     
134     # open the file and write into it
135     launch_file = open(filepath, "w")
136     launch_file.write(before)
137     # Write
138     writer.write_cfgForPy_file(launch_file, additional_env=additional_env)
139     launch_file.write(after)
140     launch_file.close()
141     
142     # change the rights in order to make the file executable for everybody
143     os.chmod(filepath,
144              stat.S_IRUSR |
145              stat.S_IRGRP |
146              stat.S_IROTH |
147              stat.S_IWUSR |
148              stat.S_IXUSR |
149              stat.S_IXGRP |
150              stat.S_IXOTH)
151     return filepath
152
153
154 def generate_catalog(machines, config, logger):
155     """Generates an xml catalog file from a list of machines.
156     
157     :param machines List: The list of machines to add in the catalog   
158     :param config Config: The global configuration
159     :param logger Logger: The logger instance to use for the display 
160                           and logging
161     :return: The catalog file path.
162     :rtype: str
163     """
164     # remove empty machines
165     machines = map(lambda l: l.strip(), machines)
166     machines = filter(lambda l: len(l) > 0, machines)
167     
168     # log something
169     src.printcolors.print_value(logger, _("Generate Resources Catalog"),
170                                 ", ".join(machines), 4)
171     
172     # The command to execute on each machine in order to get some information
173     cmd = '"cat /proc/cpuinfo | grep MHz ; cat /proc/meminfo | grep MemTotal"'
174     user = getpass.getuser()
175
176     # Create the catalog path
177     catfile = src.get_tmp_filename(config, "CatalogResources.xml")
178     catalog = file(catfile, "w")
179     
180     # Write into it
181     catalog.write("<!DOCTYPE ResourcesCatalog>\n<resources>\n")
182     for k in machines:
183         logger.write("    ssh %s " % (k + " ").ljust(20, '.'), 4)
184         logger.flush()
185
186         # Verify that the machine is accessible
187         ssh_cmd = 'ssh -o "StrictHostKeyChecking no" %s %s' % (k, cmd)
188         p = subprocess.Popen(ssh_cmd, shell=True,
189                 stdin=subprocess.PIPE,
190                 stdout=subprocess.PIPE,
191                 stderr=subprocess.PIPE)
192         p.wait()
193
194         if p.returncode != 0: # The machine is not accessible
195             logger.write(src.printcolors.printc(src.KO_STATUS) + "\n", 4)
196             logger.write("    " + 
197                          src.printcolors.printcWarning(p.stderr.read()), 2)
198         else:
199             # The machine is accessible, write the corresponding section on
200             # the xml file
201             logger.write(src.printcolors.printc(src.OK_STATUS) + "\n", 4)
202             lines = p.stdout.readlines()
203             freq = lines[0][:-1].split(':')[-1].split('.')[0].strip()
204             nb_proc = len(lines) -1
205             memory = lines[-1].split(':')[-1].split()[0].strip()
206             memory = int(memory) / 1000
207
208             catalog.write("    <machine\n")
209             catalog.write("        protocol=\"ssh\"\n")
210             catalog.write("        nbOfNodes=\"1\"\n")
211             catalog.write("        mode=\"interactif\"\n")
212             catalog.write("        OS=\"LINUX\"\n")
213             catalog.write("        CPUFreqMHz=\"%s\"\n" % freq)
214             catalog.write("        nbOfProcPerNode=\"%s\"\n" % nb_proc)
215             catalog.write("        memInMB=\"%s\"\n" % memory)
216             catalog.write("        userName=\"%s\"\n" % user)
217             catalog.write("        name=\"%s\"\n" % k)
218             catalog.write("        hostname=\"%s\"\n" % k)
219             catalog.write("    >\n")
220             catalog.write("    </machine>\n")
221
222     catalog.write("</resources>\n")
223     catalog.close()
224     return catfile
225
226 def copy_catalog(config, catalog_path):
227     """Copy the xml catalog file into the right location
228     
229     :param config Config: The global configuration
230     :param catalog_path str: the catalog file path
231     :return: The environment dictionary corresponding to the file path.
232     :rtype: Dict
233     """
234     # Verify the existence of the file
235     if not os.path.exists(catalog_path):
236         raise IOError(_("Catalog not found: %s") % catalog_path)
237     # Get the application directory and copy catalog inside
238     out_dir = config.APPLICATION.workdir
239     new_catalog_path = os.path.join(out_dir, "CatalogResources.xml")
240     # Do the copy
241     shutil.copy(catalog_path, new_catalog_path)
242     additional_environ = {'USER_CATALOG_RESOURCES_FILE' : new_catalog_path}
243     return additional_environ
244
245
246
247 ##################################################
248
249 ##
250 # Describes the command
251 def description():
252     return _("The launcher command generates a SALOME launcher.\n\nexample:"
253              "\nsat launcher SALOME-master")
254
255 ##
256 # Runs the command.
257 def run(args, runner, logger):
258
259     # check for product
260     (options, args) = parser.parse_args(args)
261
262     # Verify that the command was called with an application
263     src.check_config_has_application( runner.cfg )
264     
265     # Determine the launcher name (from option, profile section or by default "salome")
266     if options.name:
267         launcher_name = options.name
268     else:
269         launcher_name = src.get_launcher_name(runner.cfg)
270
271     # set the launcher path
272     launcher_path = runner.cfg.APPLICATION.workdir
273
274     # Copy a catalog if the option is called
275     additional_environ = {}
276     if options.catalog:
277         additional_environ = copy_catalog(runner.cfg, options.catalog)
278
279     # Generate a catalog of resources if the corresponding option was called
280     if options.gencat:
281         catalog_path  = generate_catalog(options.gencat.split(","),
282                                          runner.cfg,
283                                          logger)
284         additional_environ = copy_catalog(runner.cfg, catalog_path)
285
286     # activate mesa use in the generated launcher
287     if options.use_mesa:
288         src.activate_mesa_property(runner.cfg)
289
290     # Generate the launcher
291     launcherPath = generate_launch_file( runner.cfg,
292                                          logger,
293                                          launcher_name,
294                                          launcher_path,
295                                          additional_env = additional_environ )
296
297     return 0