1 #!/usr/bin/env python
\r
2 #-*- coding:utf-8 -*-
\r
3 # Copyright (C) 2010-2013 CEA/DEN
\r
5 # This library is free software; you can redistribute it and/or
\r
6 # modify it under the terms of the GNU Lesser General Public
\r
7 # License as published by the Free Software Foundation; either
\r
8 # version 2.1 of the License.
\r
10 # This library is distributed in the hope that it will be useful,
\r
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
13 # Lesser General Public License for more details.
\r
15 # You should have received a copy of the GNU Lesser General Public
\r
16 # License along with this library; if not, write to the Free Software
\r
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\r
28 parser = src.options.Options()
\r
30 parser.add_option('n', 'name', 'string', 'name', _('The name of the launcher '
\r
31 '(default is APPLICATION.profile.launcher_name)'))
\r
32 parser.add_option('c', 'catalog', 'string', 'catalog',
\r
33 _('The resources catalog to use'))
\r
34 parser.add_option('', 'gencat', 'string', 'gencat',
\r
35 _("Create a resources catalog for the specified machines "
\r
36 "(separated with ',') \n\tNOTICE: this command will ssh to retrieve"
\r
37 " information to each machine in the list"))
\r
39 def generate_launch_file(config,
\r
45 '''Generates the launcher file.
\r
47 :param config Config: The global configuration
\r
48 :param logger Logger: The logger instance to use for the display
\r
50 :param env_info str: The list of products to add in the files.
\r
51 :param pathlauncher str: The path to the launcher to generate
\r
52 :param src_root str: The path to the directory where the sources are
\r
53 :param display boolean: If False, do not print anything in the terminal
\r
54 :param additional_env dict: The dict giving additional
\r
55 environment variables
\r
56 :return: The launcher file path.
\r
59 # Get the application directory and the profile directory
\r
60 out_dir = config.APPLICATION.workdir
\r
61 profile = config.APPLICATION.profile
\r
62 profile_install_dir = get_profile_dir(config)
\r
64 # Compute the default launcher path if it is not provided in pathlauncher
\r
66 if pathlauncher is None:
\r
67 filepath = os.path.join( os.path.join( profile_install_dir,
\r
70 profile['launcher_name'] )
\r
72 filepath = os.path.join(pathlauncher, profile['launcher_name'])
\r
74 # Remove the file if it exists in order to replace it
\r
75 if os.path.exists(filepath):
\r
78 # Add the APPLI variable
\r
79 additional_env['APPLI'] = filepath
\r
81 # Get the launcher template
\r
82 withProfile = src.fileEnviron.withProfile.replace( "PROFILE_INSTALL_DIR",
\r
83 profile_install_dir )
\r
84 before, after = withProfile.split(
\r
85 "# here your local standalone environment\n")
\r
87 # create an environment file writer
\r
88 writer = src.environment.FileEnvWriter(config,
\r
94 # Display some information
\r
96 # Write the launcher file
\r
97 logger.write(_("Generating launcher for %s :\n") %
\r
98 src.printcolors.printcLabel(config.VARS.application), 1)
\r
99 logger.write(" %s\n" %src.printcolors.printcLabel(filepath), 1)
\r
101 # open the file and write into it
\r
102 launch_file = open(filepath, "w")
\r
103 launch_file.write(before)
\r
105 writer.write_cfgForPy_file(launch_file, additional_env=additional_env)
\r
106 launch_file.write(after)
\r
107 launch_file.close()
\r
109 # change the rights in order to make the file executable for everybody
\r
120 def generate_launch_link(config,
\r
125 packageLauncher=False):
\r
126 '''Generates the launcher link that sources Python
\r
127 and call the actual launcher.
\r
129 :param config Config: The global configuration
\r
130 :param logger Logger: The logger instance to use for the display
\r
132 :param launcherPath str: The path to the launcher to call
\r
133 :param pathlauncher str: The path to the launcher (link) to generate
\r
134 :param display boolean: If False, do not print anything in the terminal
\r
135 :param packageLauncher boolean: if True, use a relative path (for package)
\r
136 :return: The launcher link file path.
\r
139 if pathlauncher is None:
\r
140 # Make an executable file that sources python, then launch the launcher
\r
141 # produced by generate_launch_file method
\r
142 sourceLauncher = os.path.join(config.APPLICATION.workdir,
\r
143 config.APPLICATION.profile.launcher_name)
\r
145 sourceLauncher = os.path.join(pathlauncher,
\r
146 config.APPLICATION.profile.launcher_name)
\r
148 # Change the extension for the windows case
\r
149 if platform.system() == "Windows" :
\r
150 sourceLauncher += '.bat'
\r
152 # display some information
\r
154 logger.write(_("\nGenerating the executable that sources"
\r
155 " python and runs the launcher :\n") , 1)
\r
156 logger.write(" %s\n" %src.printcolors.printcLabel(sourceLauncher), 1)
\r
158 # open the file to write
\r
159 f = open(sourceLauncher, "w")
\r
161 # Write the set up of the environment
\r
162 if platform.system() == "Windows" :
\r
167 # Write the Python environment files
\r
168 env = src.environment.SalomeEnviron( config,
\r
169 src.fileEnviron.get_file_environ( f, shell, config ) )
\r
170 env.set_a_product( "Python", logger)
\r
172 # Write the call to the original launcher
\r
174 if packageLauncher:
\r
175 cmd = os.path.join('${out_dir_Path}', launcherPath)
\r
179 if platform.system() == "Windows" :
\r
180 cmd = 'python ' + cmd + ' %*'
\r
187 # Write the cleaning of the environment
\r
190 # Close new launcher
\r
192 os.chmod(sourceLauncher,
\r
202 return sourceLauncher
\r
204 def generate_catalog(machines, config, logger):
\r
205 """Generates an xml catalog file from a list of machines.
\r
207 :param machines List: The list of machines to add in the catalog
\r
208 :param config Config: The global configuration
\r
209 :param logger Logger: The logger instance to use for the display
\r
211 :return: The catalog file path.
\r
214 # remove empty machines
\r
215 machines = map(lambda l: l.strip(), machines)
\r
216 machines = filter(lambda l: len(l) > 0, machines)
\r
219 src.printcolors.print_value(logger, _("Generate Resources Catalog"),
\r
220 ", ".join(machines), 4)
\r
222 # The command to execute on each machine in order to get some information
\r
223 cmd = '"cat /proc/cpuinfo | grep MHz ; cat /proc/meminfo | grep MemTotal"'
\r
224 user = getpass.getuser()
\r
226 # Create the catalog path
\r
227 catfile = src.get_tmp_filename(config, "CatalogResources.xml")
\r
228 catalog = file(catfile, "w")
\r
231 catalog.write("<!DOCTYPE ResourcesCatalog>\n<resources>\n")
\r
233 logger.write(" ssh %s " % (k + " ").ljust(20, '.'), 4)
\r
236 # Verify that the machine is accessible
\r
237 ssh_cmd = 'ssh -o "StrictHostKeyChecking no" %s %s' % (k, cmd)
\r
238 p = subprocess.Popen(ssh_cmd, shell=True,
\r
239 stdin=subprocess.PIPE,
\r
240 stdout=subprocess.PIPE,
\r
241 stderr=subprocess.PIPE)
\r
244 if p.returncode != 0: # The machine is not accessible
\r
245 logger.write(src.printcolors.printc(src.KO_STATUS) + "\n", 4)
\r
246 logger.write(" " +
\r
247 src.printcolors.printcWarning(p.stderr.read()), 2)
\r
249 # The machine is accessible, write the corresponding section on
\r
251 logger.write(src.printcolors.printc(src.OK_STATUS) + "\n", 4)
\r
252 lines = p.stdout.readlines()
\r
253 freq = lines[0][:-1].split(':')[-1].split('.')[0].strip()
\r
254 nb_proc = len(lines) -1
\r
255 memory = lines[-1].split(':')[-1].split()[0].strip()
\r
256 memory = int(memory) / 1000
\r
258 catalog.write(" <machine\n")
\r
259 catalog.write(" protocol=\"ssh\"\n")
\r
260 catalog.write(" nbOfNodes=\"1\"\n")
\r
261 catalog.write(" mode=\"interactif\"\n")
\r
262 catalog.write(" OS=\"LINUX\"\n")
\r
263 catalog.write(" CPUFreqMHz=\"%s\"\n" % freq)
\r
264 catalog.write(" nbOfProcPerNode=\"%s\"\n" % nb_proc)
\r
265 catalog.write(" memInMB=\"%s\"\n" % memory)
\r
266 catalog.write(" userName=\"%s\"\n" % user)
\r
267 catalog.write(" name=\"%s\"\n" % k)
\r
268 catalog.write(" hostname=\"%s\"\n" % k)
\r
269 catalog.write(" >\n")
\r
270 catalog.write(" </machine>\n")
\r
272 catalog.write("</resources>\n")
\r
276 def copy_catalog(config, catalog_path):
\r
277 """Copy the xml catalog file into the right location
\r
279 :param config Config: The global configuration
\r
280 :param catalog_path str: the catalog file path
\r
281 :return: The environment dictionary corresponding to the file path.
\r
284 # Verify the existence of the file
\r
285 if not os.path.exists(catalog_path):
\r
286 raise IOError(_("Catalog not found: %s") % catalog_path)
\r
287 # Compute the location where to copy the file
\r
288 profile_dir = get_profile_dir(config)
\r
289 new_catalog_path = os.path.join(profile_dir, "CatalogResources.xml")
\r
291 shutil.copy(catalog_path, new_catalog_path)
\r
292 additional_environ = {'USER_CATALOG_RESOURCES_FILE' : new_catalog_path}
\r
293 return additional_environ
\r
295 def get_profile_dir(config):
\r
296 """Get the profile directory from the config
\r
298 :param config Config: The global configuration
\r
299 :return: The profile install directory
\r
302 profile_name = config.APPLICATION.profile.product
\r
303 profile_info = src.product.get_product_config(config, profile_name)
\r
304 return profile_info.install_dir
\r
306 def finish_profile_install(config, launcherPath):
\r
307 """Add some symlinks required for SALOME
\r
309 :param config Config: The global configuration
\r
310 :param launcherPath str: the launcher file path
\r
312 # Create a USERS directory
\r
313 profile_dir = get_profile_dir(config)
\r
314 user_dir = os.path.join(profile_dir, 'USERS')
\r
315 if not os.path.exists(user_dir):
\r
316 os.makedirs(user_dir)
\r
317 # change rights of USERS directory
\r
329 # create a link in root directory to the launcher
\r
330 if platform.system() != "Windows" :
331 link_path = os.path.join(config.APPLICATION.workdir, 'salome')
\r
332 if not os.path.exists(link_path):
\r
334 os.symlink(launcherPath, link_path)
\r
336 os.remove(link_path)
\r
337 os.symlink(launcherPath, link_path)
\r
339 link_path = os.path.join(profile_dir, 'salome')
\r
340 if not os.path.exists(link_path):
\r
342 os.symlink(launcherPath, link_path)
\r
344 os.remove(link_path)
\r
345 os.symlink(launcherPath, link_path)
347 ##################################################
\r
350 # Describes the command
\r
352 return _("The launcher command generates a salome launcher.")
\r
355 # Runs the command.
\r
356 def run(args, runner, logger):
\r
358 # check for product
\r
359 (options, args) = parser.parse_args(args)
\r
361 # Verify that the command was called with an application
\r
362 src.check_config_has_application( runner.cfg )
\r
364 # Verify that the APPLICATION section has a profile section
\r
365 src.check_config_has_profile( runner.cfg )
\r
367 # Verify that the profile is installed
\r
368 if not src.product.check_installation(
\r
369 src.product.get_product_config(
\r
371 runner.cfg.APPLICATION.profile.product)):
\r
372 msg = _("The profile of the application is not correctly installed.")
\r
373 logger.write(src.printcolors.printcError(msg), 1)
\r
376 # Change the name of the file to create
\r
377 # if the corresponding option was called
\r
379 runner.cfg.APPLICATION.profile['launcher_name'] = options.name
\r
381 # Copy a catalog if the option is called
\r
382 additional_environ = {}
\r
383 if options.catalog:
\r
384 additional_environ = copy_catalog(runner.cfg, options.catalog)
\r
386 # Generate a catalog of resources if the corresponding option was called
\r
388 catalog_path = generate_catalog(options.gencat.split(","),
\r
391 additional_environ = copy_catalog(runner.cfg, catalog_path)
\r
393 # Generate the launcher
\r
394 launcherPath = generate_launch_file( runner.cfg,
\r
396 additional_env = additional_environ )
\r
398 # Create the link (bash file that sources python and then call
\r
399 # the actual launcher) to the launcher
\r
400 generate_launch_link( runner.cfg, logger, launcherPath)
\r
403 finish_profile_install(runner.cfg, launcherPath)
\r