Salome HOME
sat job: add a red broken link when a command crashs
[tools/sat.git] / commands / job.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2012  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 sys
21 import traceback
22 import tempfile
23
24 import src
25
26 # Define all possible option for the make command :  sat make <options>
27 parser = src.options.Options()
28 parser.add_option('j', 'jobs_config', 'string', 'jobs_cfg', 
29                   _('Mandatory: The name of the config file that contains'
30                   ' the jobs configuration'))
31 parser.add_option('', 'name', 'string', 'job',
32     _('Mandatory: The job name from which to execute commands.'), "")
33
34 def description():
35     '''method that is called when salomeTools is called with --help option.
36     
37     :return: The text to display for the job command description.
38     :rtype: str
39     '''
40     return _("Executes the commands of the job defined"
41              " in the jobs configuration file\n\nexample:\nsat job "
42              "--jobs_config my_jobs --name my_job")
43   
44 def run(args, runner, logger):
45     '''method that is called when salomeTools is called with job parameter.
46     '''
47     
48     # Parse the options
49     (options, args) = parser.parse_args(args)
50          
51     l_cfg_dir = runner.cfg.PATHS.JOBPATH
52     
53     # Make sure the jobs_config option has been called
54     if not options.jobs_cfg:
55         message = _("The option --jobs_config is required\n")      
56         logger.write(src.printcolors.printcError(message))
57         return 1
58     
59     # Make sure the name option has been called
60     if not options.job:
61         message = _("The option --name is required\n")      
62         logger.write(src.printcolors.printcError(message))
63         return 1
64     
65     # Find the file in the directories
66     found = False
67     for cfg_dir in l_cfg_dir:
68         file_jobs_cfg = os.path.join(cfg_dir, options.jobs_cfg)
69         if not file_jobs_cfg.endswith('.pyconf'):
70             file_jobs_cfg += '.pyconf'
71         
72         if not os.path.exists(file_jobs_cfg):
73             continue
74         else:
75             found = True
76             break
77     
78     if not found:
79         msg = _("The file configuration %(name_file)s was not found."
80                 "\nUse the --list option to get the possible files.")
81         src.printcolors.printcError(msg)
82         return 1
83     
84     info = [
85     (_("Platform"), runner.cfg.VARS.dist),
86     (_("File containing the jobs configuration"), file_jobs_cfg)
87     ]
88     src.print_info(logger, info)
89     
90     # Read the config that is in the file
91     config_jobs = src.read_config_from_a_file(file_jobs_cfg)
92     
93     # Find the job and its commands
94     found = False
95     for job in config_jobs.jobs:
96         if job.name == options.job:
97             commands = job.commands
98             found = True
99             break
100     if not found:
101         msg = _("Impossible to find the job \"%(job_name)s\" in "
102                 "%(jobs_config_file)s" % {"job_name" : options.job,
103                                           "jobs_config_file" : file_jobs_cfg})
104         logger.write(src.printcolors.printcError(msg) + "\n")
105         return 1
106     
107     # Find the maximum length of the commands in order to format the display
108     len_max_command = max([len(cmd) for cmd in commands])
109     
110     # Loop over the commands and execute it
111     res = 0
112     nb_pass = 0
113     for command in commands:
114         # Determine if it is a sat command or a shell command
115         cmd_exe = command.split(" ")[0] # first part
116         if cmd_exe == "sat":
117             sat_command_name = command.split(" ")[1]
118             end_cmd = command.replace(cmd_exe + " " + sat_command_name, "")
119         else:
120             sat_command_name = "shell"
121             end_cmd = "--command " + command
122         
123         # Get dynamically the command function to call 
124         sat_command = runner.__getattr__(sat_command_name)
125         logger.write("Executing " + 
126                      src.printcolors.printcLabel(command) + " ", 3)
127         logger.write("." * (len_max_command - len(command)) + " ", 3)
128         logger.flush()
129         
130         error = ""
131         stack = ""
132         try:
133             # Execute the command
134             code = sat_command(end_cmd,
135                                batch = True,
136                                verbose = 0,
137                                logger_add_link = logger)
138         except Exception as e:
139             code = 1
140             # Get error
141             error = str(e)
142             # get stack
143             __, __, exc_traceback = sys.exc_info()
144             fp = tempfile.TemporaryFile()
145             traceback.print_tb(exc_traceback, file=fp)
146             fp.seek(0)
147             stack = fp.read()
148             logger.add_link(_("Dead Link"),
149                             sat_command_name,
150                             code,
151                             "ERROR: %s TRACEBACK: %s" % (error,
152                                                     stack.replace('"',"'")))
153             
154         # Print the status of the command
155         if code == 0:
156             nb_pass += 1
157             logger.write('%s\n' % src.printcolors.printc(src.OK_STATUS), 3)
158         else:
159             res = 1
160             logger.write('%s %s\n' % (src.printcolors.printc(src.KO_STATUS),
161                                       error), 3)
162             if len(stack) > 0:
163                 logger.write('stack: %s\n' % stack, 3)
164     
165     # Print the final state
166     if res == 0:
167         final_status = "OK"
168     else:
169         final_status = "KO"
170    
171     logger.write(_("\nCommands: %(status)s (%(valid_result)d/%(nb_products)d)\n") % \
172         { 'status': src.printcolors.printc(final_status), 
173           'valid_result': nb_pass,
174           'nb_products': len(commands) }, 3)
175     
176     return res