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