3 # Copyright (C) 2010-2013 CEA/DEN
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.
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.
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
28 parser = src.options.Options()
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"))
40 def generate_launch_file(config,
46 '''Generates the launcher file.
48 :param config Config: The global configuration
49 :param logger Logger: The logger instance to use for the display
51 :param env_info str: The list of products to add in the files.
52 :param pathlauncher str: The path to the launcher to generate
53 :param src_root str: The path to the directory where the sources are
54 :param display boolean: If False, do not print anything in the terminal
55 :param additional_env dict: The dict giving additional
57 :return: The launcher file path.
60 # Get the application directory and the profile directory
61 out_dir = config.APPLICATION.workdir
62 profile = config.APPLICATION.profile
63 profile_install_dir = get_profile_dir(config)
65 # Compute the default launcher path if it is not provided in pathlauncher
67 if pathlauncher is None:
68 if platform.system() == "Windows" :
69 filepath = os.path.join( os.path.join( profile_install_dir,
72 profile['launcher_name'] )
74 filepath = os.path.join( out_dir,
75 profile['launcher_name'] )
77 filepath = os.path.join(pathlauncher, profile['launcher_name'])
79 # Remove the file if it exists in order to replace it
80 if os.path.exists(filepath):
83 # Add the APPLI variable
84 additional_env['APPLI'] = filepath
86 # Get the launcher template
87 withProfile = src.fileEnviron.withProfile.replace( "PROFILE_INSTALL_DIR",
89 before, after = withProfile.split(
90 "# here your local standalone environment\n")
92 # create an environment file writer
93 writer = src.environment.FileEnvWriter(config,
99 # Display some information
101 # Write the launcher file
102 logger.write(_("Generating launcher for %s :\n") %
103 src.printcolors.printcLabel(config.VARS.application), 1)
104 logger.write(" %s\n" % src.printcolors.printcLabel(filepath), 1)
106 # open the file and write into it
107 launch_file = open(filepath, "w")
108 launch_file.write(before)
110 writer.write_cfgForPy_file(launch_file, additional_env=additional_env)
111 launch_file.write(after)
114 # change the rights in order to make the file executable for everybody
125 def generate_launch_link(config,
130 packageLauncher=False):
131 '''Generates the launcher link that sources Python
132 and call the actual launcher.
134 :param config Config: The global configuration
135 :param logger Logger: The logger instance to use for the display
137 :param launcherPath str: The path to the launcher to call
138 :param pathlauncher str: The path to the launcher (link) to generate
139 :param display boolean: If False, do not print anything in the terminal
140 :param packageLauncher boolean: if True, use a relative path (for package)
141 :return: The launcher link file path.
144 if pathlauncher is None:
145 # Make an executable file that sources python, then launch the launcher
146 # produced by generate_launch_file method
147 sourceLauncher = os.path.join(config.APPLICATION.workdir,
148 config.APPLICATION.profile.launcher_name)
150 sourceLauncher = os.path.join(pathlauncher,
151 config.APPLICATION.profile.launcher_name)
153 # Change the extension for the windows case
154 if platform.system() == "Windows" :
155 sourceLauncher += '.bat'
157 # display some information
159 logger.write(_("\nGenerating the executable that sources"
160 " python and runs the launcher :\n") , 1)
161 logger.write(" %s\n" %src.printcolors.printcLabel(sourceLauncher), 1)
163 # open the file to write
164 f = open(sourceLauncher, "w")
166 # Write the set up of the environment
167 if platform.system() == "Windows" :
172 # Write the Python environment files
173 env = src.environment.SalomeEnviron( config,
174 src.fileEnviron.get_file_environ( f, shell, config ) )
175 env.set_a_product( "Python", logger)
177 # Write the call to the original launcher
180 cmd = os.path.join('${out_dir_Path}', launcherPath)
184 if platform.system() == "Windows" :
185 cmd = 'python ' + cmd + ' %*'
192 # Write the cleaning of the environment
197 os.chmod(sourceLauncher,
207 return sourceLauncher
209 def generate_catalog(machines, config, logger):
210 """Generates an xml catalog file from a list of machines.
212 :param machines List: The list of machines to add in the catalog
213 :param config Config: The global configuration
214 :param logger Logger: The logger instance to use for the display
216 :return: The catalog file path.
219 # remove empty machines
220 machines = map(lambda l: l.strip(), machines)
221 machines = filter(lambda l: len(l) > 0, machines)
224 src.printcolors.print_value(logger, _("Generate Resources Catalog"),
225 ", ".join(machines), 4)
227 # The command to execute on each machine in order to get some information
228 cmd = '"cat /proc/cpuinfo | grep MHz ; cat /proc/meminfo | grep MemTotal"'
229 user = getpass.getuser()
231 # Create the catalog path
232 catfile = src.get_tmp_filename(config, "CatalogResources.xml")
233 catalog = file(catfile, "w")
236 catalog.write("<!DOCTYPE ResourcesCatalog>\n<resources>\n")
238 logger.write(" ssh %s " % (k + " ").ljust(20, '.'), 4)
241 # Verify that the machine is accessible
242 ssh_cmd = 'ssh -o "StrictHostKeyChecking no" %s %s' % (k, cmd)
243 p = subprocess.Popen(ssh_cmd, shell=True,
244 stdin=subprocess.PIPE,
245 stdout=subprocess.PIPE,
246 stderr=subprocess.PIPE)
249 if p.returncode != 0: # The machine is not accessible
250 logger.write(src.printcolors.printc(src.KO_STATUS) + "\n", 4)
252 src.printcolors.printcWarning(p.stderr.read()), 2)
254 # The machine is accessible, write the corresponding section on
256 logger.write(src.printcolors.printc(src.OK_STATUS) + "\n", 4)
257 lines = p.stdout.readlines()
258 freq = lines[0][:-1].split(':')[-1].split('.')[0].strip()
259 nb_proc = len(lines) -1
260 memory = lines[-1].split(':')[-1].split()[0].strip()
261 memory = int(memory) / 1000
263 catalog.write(" <machine\n")
264 catalog.write(" protocol=\"ssh\"\n")
265 catalog.write(" nbOfNodes=\"1\"\n")
266 catalog.write(" mode=\"interactif\"\n")
267 catalog.write(" OS=\"LINUX\"\n")
268 catalog.write(" CPUFreqMHz=\"%s\"\n" % freq)
269 catalog.write(" nbOfProcPerNode=\"%s\"\n" % nb_proc)
270 catalog.write(" memInMB=\"%s\"\n" % memory)
271 catalog.write(" userName=\"%s\"\n" % user)
272 catalog.write(" name=\"%s\"\n" % k)
273 catalog.write(" hostname=\"%s\"\n" % k)
274 catalog.write(" >\n")
275 catalog.write(" </machine>\n")
277 catalog.write("</resources>\n")
281 def copy_catalog(config, catalog_path):
282 """Copy the xml catalog file into the right location
284 :param config Config: The global configuration
285 :param catalog_path str: the catalog file path
286 :return: The environment dictionary corresponding to the file path.
289 # Verify the existence of the file
290 if not os.path.exists(catalog_path):
291 raise IOError(_("Catalog not found: %s") % catalog_path)
292 # Compute the location where to copy the file
293 profile_dir = get_profile_dir(config)
294 new_catalog_path = os.path.join(profile_dir, "CatalogResources.xml")
296 shutil.copy(catalog_path, new_catalog_path)
297 additional_environ = {'USER_CATALOG_RESOURCES_FILE' : new_catalog_path}
298 return additional_environ
300 def get_profile_dir(config):
301 """Get the profile directory from the config
303 :param config Config: The global configuration
304 :return: The profile install directory
307 profile_name = config.APPLICATION.profile.product
308 profile_info = src.product.get_product_config(config, profile_name)
309 return profile_info.install_dir
311 def finish_profile_install(config, launcherPath):
312 """Add some symlinks required for SALOME
314 :param config Config: The global configuration
315 :param launcherPath str: the launcher file path
317 # Create a USERS directory
318 profile_dir = get_profile_dir(config)
319 user_dir = os.path.join(profile_dir, 'USERS')
320 if not os.path.exists(user_dir):
321 os.makedirs(user_dir)
322 # change rights of USERS directory
334 # create a link in root directory to the launcher
335 if platform.system() != "Windows" :
336 link_path = os.path.join(config.APPLICATION.workdir, 'salome')
337 if not os.path.exists(link_path):
339 os.symlink(launcherPath, link_path)
342 os.symlink(launcherPath, link_path)
344 link_path = os.path.join(profile_dir, 'salome')
345 relativeLauncherPath = "../../salome"
347 os.symlink(relativeLauncherPath, link_path)
350 os.symlink(relativeLauncherPath, link_path)
352 ##################################################
355 # Describes the command
357 return _("The launcher command generates a SALOME launcher.\n\nexample:"
358 "\nsat launcher SALOME-master")
362 def run(args, runner, logger):
365 (options, args) = parser.parse_args(args)
367 # Verify that the command was called with an application
368 src.check_config_has_application( runner.cfg )
370 # Verify that the APPLICATION section has a profile section
371 src.check_config_has_profile( runner.cfg )
373 # Verify that the profile is installed
374 if not src.product.check_installation(
375 src.product.get_product_config(
377 runner.cfg.APPLICATION.profile.product)):
378 msg = _("The profile of the application is not correctly installed.")
379 logger.write(src.printcolors.printcError(msg), 1)
382 # Change the name of the file to create
383 # if the corresponding option was called
385 runner.cfg.APPLICATION.profile['launcher_name'] = options.name
387 # Copy a catalog if the option is called
388 additional_environ = {}
390 additional_environ = copy_catalog(runner.cfg, options.catalog)
392 # Generate a catalog of resources if the corresponding option was called
394 catalog_path = generate_catalog(options.gencat.split(","),
397 additional_environ = copy_catalog(runner.cfg, catalog_path)
399 # Generate the launcher
400 launcherPath = generate_launch_file( runner.cfg,
402 additional_env = additional_environ )
404 if platform.system() == "Windows" :
405 # Create the link (bash file that sources python and then call
406 # the actual launcher) to the launcher
407 generate_launch_link( runner.cfg, logger, launcherPath)
410 finish_profile_install(runner.cfg, launcherPath)