Salome HOME
PRODUCT_ROOT_DIR not duplicate in write_cfgForPy_file method for sat launcher file
[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("# here your local standalone environment\n")
118
119     # create an environment file writer
120     writer = src.environment.FileEnvWriter(config,
121                                            logger,
122                                            pathlauncher,
123                                            src_root=None,
124                                            env_info=None)
125
126     # Display some information
127     if display:
128         # Write the launcher file
129         logger.write(_("Generating launcher for %s :\n") % 
130                      src.printcolors.printcLabel(config.VARS.application), 1)
131         logger.write("  %s\n" % src.printcolors.printcLabel(filepath), 1)
132     
133     # open the file and write into it
134     launch_file = open(filepath, "w")
135     launch_file.write(before)
136     # Write
137     writer.write_cfgForPy_file(launch_file, additional_env=additional_env)
138     launch_file.write(after)
139     launch_file.close()
140     
141     # change the rights in order to make the file executable for everybody
142     os.chmod(filepath,
143              stat.S_IRUSR |
144              stat.S_IRGRP |
145              stat.S_IROTH |
146              stat.S_IWUSR |
147              stat.S_IXUSR |
148              stat.S_IXGRP |
149              stat.S_IXOTH)
150     return filepath
151
152
153 def generate_catalog(machines, config, logger):
154     """Generates an xml catalog file from a list of machines.
155     
156     :param machines List: The list of machines to add in the catalog   
157     :param config Config: The global configuration
158     :param logger Logger: The logger instance to use for the display 
159                           and logging
160     :return: The catalog file path.
161     :rtype: str
162     """
163     # remove empty machines
164     machines = map(lambda l: l.strip(), machines)
165     machines = filter(lambda l: len(l) > 0, machines)
166     
167     # log something
168     src.printcolors.print_value(logger, _("Generate Resources Catalog"),
169                                 ", ".join(machines), 4)
170     
171     # The command to execute on each machine in order to get some information
172     cmd = '"cat /proc/cpuinfo | grep MHz ; cat /proc/meminfo | grep MemTotal"'
173     user = getpass.getuser()
174
175     # Create the catalog path
176     catfile = src.get_tmp_filename(config, "CatalogResources.xml")
177     catalog = file(catfile, "w")
178     
179     # Write into it
180     catalog.write("<!DOCTYPE ResourcesCatalog>\n<resources>\n")
181     for k in machines:
182         logger.write("    ssh %s " % (k + " ").ljust(20, '.'), 4)
183         logger.flush()
184
185         # Verify that the machine is accessible
186         ssh_cmd = 'ssh -o "StrictHostKeyChecking no" %s %s' % (k, cmd)
187         p = subprocess.Popen(ssh_cmd, shell=True,
188                 stdin=subprocess.PIPE,
189                 stdout=subprocess.PIPE,
190                 stderr=subprocess.PIPE)
191         p.wait()
192
193         if p.returncode != 0: # The machine is not accessible
194             logger.write(src.printcolors.printc(src.KO_STATUS) + "\n", 4)
195             logger.write("    " + 
196                          src.printcolors.printcWarning(p.stderr.read()), 2)
197         else:
198             # The machine is accessible, write the corresponding section on
199             # the xml file
200             logger.write(src.printcolors.printc(src.OK_STATUS) + "\n", 4)
201             lines = p.stdout.readlines()
202             freq = lines[0][:-1].split(':')[-1].split('.')[0].strip()
203             nb_proc = len(lines) -1
204             memory = lines[-1].split(':')[-1].split()[0].strip()
205             memory = int(memory) / 1000
206
207             catalog.write("    <machine\n")
208             catalog.write("        protocol=\"ssh\"\n")
209             catalog.write("        nbOfNodes=\"1\"\n")
210             catalog.write("        mode=\"interactif\"\n")
211             catalog.write("        OS=\"LINUX\"\n")
212             catalog.write("        CPUFreqMHz=\"%s\"\n" % freq)
213             catalog.write("        nbOfProcPerNode=\"%s\"\n" % nb_proc)
214             catalog.write("        memInMB=\"%s\"\n" % memory)
215             catalog.write("        userName=\"%s\"\n" % user)
216             catalog.write("        name=\"%s\"\n" % k)
217             catalog.write("        hostname=\"%s\"\n" % k)
218             catalog.write("    >\n")
219             catalog.write("    </machine>\n")
220
221     catalog.write("</resources>\n")
222     catalog.close()
223     return catfile
224
225 def copy_catalog(config, catalog_path):
226     """Copy the xml catalog file into the right location
227     
228     :param config Config: The global configuration
229     :param catalog_path str: the catalog file path
230     :return: The environment dictionary corresponding to the file path.
231     :rtype: Dict
232     """
233     # Verify the existence of the file
234     if not os.path.exists(catalog_path):
235         raise IOError(_("Catalog not found: %s") % catalog_path)
236     # Get the application directory and copy catalog inside
237     out_dir = config.APPLICATION.workdir
238     new_catalog_path = os.path.join(out_dir, "CatalogResources.xml")
239     # Do the copy
240     shutil.copy(catalog_path, new_catalog_path)
241     additional_environ = {'USER_CATALOG_RESOURCES_FILE' : new_catalog_path}
242     return additional_environ
243
244
245
246 ##################################################
247
248 ##
249 # Describes the command
250 def description():
251     return _("The launcher command generates a SALOME launcher.\n\nexample:"
252              "\nsat launcher SALOME-master")
253
254 ##
255 # Runs the command.
256 def run(args, runner, logger):
257
258     # check for product
259     (options, args) = parser.parse_args(args)
260
261     # Verify that the command was called with an application
262     src.check_config_has_application( runner.cfg )
263     
264     # Determine the launcher name (from option, profile section or by default "salome")
265     if options.name:
266         launcher_name = options.name
267     else:
268         launcher_name = src.get_launcher_name(runner.cfg)
269
270     # set the launcher path
271     launcher_path = runner.cfg.APPLICATION.workdir
272
273     # Copy a catalog if the option is called
274     additional_environ = {}
275     if options.catalog:
276         additional_environ = copy_catalog(runner.cfg, options.catalog)
277
278     # Generate a catalog of resources if the corresponding option was called
279     if options.gencat:
280         catalog_path  = generate_catalog(options.gencat.split(","),
281                                          runner.cfg,
282                                          logger)
283         additional_environ = copy_catalog(runner.cfg, catalog_path)
284
285     # activate mesa use in the generated launcher
286     if options.use_mesa:
287         src.activate_mesa_property(runner.cfg)
288
289     # Generate the launcher
290     launcherPath = generate_launch_file( runner.cfg,
291                                          logger,
292                                          launcher_name,
293                                          launcher_path,
294                                          additional_env = additional_environ )
295
296     return 0