Statistics
| Branch: | Tag: | Revision:

root / snf-tools / synnefo_tools / burnin / logger.py @ 9355a604

History | View | Annotate | Download (15.6 kB)

1 4c52d5bf Ilias Tsitsimpis
# Copyright 2013 GRNET S.A. All rights reserved.
2 4c52d5bf Ilias Tsitsimpis
#
3 4c52d5bf Ilias Tsitsimpis
# Redistribution and use in source and binary forms, with or
4 4c52d5bf Ilias Tsitsimpis
# without modification, are permitted provided that the following
5 4c52d5bf Ilias Tsitsimpis
# conditions are met:
6 4c52d5bf Ilias Tsitsimpis
#
7 4c52d5bf Ilias Tsitsimpis
#   1. Redistributions of source code must retain the above
8 4c52d5bf Ilias Tsitsimpis
#      copyright notice, this list of conditions and the following
9 4c52d5bf Ilias Tsitsimpis
#      disclaimer.
10 4c52d5bf Ilias Tsitsimpis
#
11 4c52d5bf Ilias Tsitsimpis
#   2. Redistributions in binary form must reproduce the above
12 4c52d5bf Ilias Tsitsimpis
#      copyright notice, this list of conditions and the following
13 4c52d5bf Ilias Tsitsimpis
#      disclaimer in the documentation and/or other materials
14 4c52d5bf Ilias Tsitsimpis
#      provided with the distribution.
15 4c52d5bf Ilias Tsitsimpis
#
16 4c52d5bf Ilias Tsitsimpis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 4c52d5bf Ilias Tsitsimpis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 4c52d5bf Ilias Tsitsimpis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 4c52d5bf Ilias Tsitsimpis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 4c52d5bf Ilias Tsitsimpis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 4c52d5bf Ilias Tsitsimpis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 4c52d5bf Ilias Tsitsimpis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 4c52d5bf Ilias Tsitsimpis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 4c52d5bf Ilias Tsitsimpis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 4c52d5bf Ilias Tsitsimpis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 4c52d5bf Ilias Tsitsimpis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 4c52d5bf Ilias Tsitsimpis
# POSSIBILITY OF SUCH DAMAGE.
28 4c52d5bf Ilias Tsitsimpis
#
29 4c52d5bf Ilias Tsitsimpis
# The views and conclusions contained in the software and
30 4c52d5bf Ilias Tsitsimpis
# documentation are those of the authors and should not be
31 4c52d5bf Ilias Tsitsimpis
# interpreted as representing official policies, either expressed
32 4c52d5bf Ilias Tsitsimpis
# or implied, of GRNET S.A.
33 4c52d5bf Ilias Tsitsimpis
34 4c52d5bf Ilias Tsitsimpis
"""
35 4c52d5bf Ilias Tsitsimpis
This is the logging class for burnin
36 4c52d5bf Ilias Tsitsimpis

37 4c52d5bf Ilias Tsitsimpis
It supports logging both for the stdout/stderr as well as file logging at the
38 4c52d5bf Ilias Tsitsimpis
same time.
39 4c52d5bf Ilias Tsitsimpis

40 4c52d5bf Ilias Tsitsimpis
The stdout/stderr logger supports verbose levels and colors but the file
41 4c52d5bf Ilias Tsitsimpis
logging doesn't (we use the info verbose level for our file logger).
42 4c52d5bf Ilias Tsitsimpis

43 4c52d5bf Ilias Tsitsimpis
Our loggers have primitive support for handling parallel execution (even though
44 4c52d5bf Ilias Tsitsimpis
burnin doesn't support it yet). To do so the stdout/stderr logger prepends the
45 4c52d5bf Ilias Tsitsimpis
name of the test under execution to every line it prints. On the other hand the
46 4c52d5bf Ilias Tsitsimpis
file logger waits to lock the file, then reads it, prints the message to the
47 4c52d5bf Ilias Tsitsimpis
corresponding line and closes the file.
48 4c52d5bf Ilias Tsitsimpis

49 4c52d5bf Ilias Tsitsimpis

50 4c52d5bf Ilias Tsitsimpis
"""
51 4c52d5bf Ilias Tsitsimpis
52 4c52d5bf Ilias Tsitsimpis
import os
53 4c52d5bf Ilias Tsitsimpis
import sys
54 4c52d5bf Ilias Tsitsimpis
import os.path
55 a64d8485 Ilias Tsitsimpis
import logging
56 4c52d5bf Ilias Tsitsimpis
import datetime
57 4c52d5bf Ilias Tsitsimpis
58 12ef696f Ilias Tsitsimpis
from synnefo_tools.burnin import filelocker
59 4c52d5bf Ilias Tsitsimpis
60 4c52d5bf Ilias Tsitsimpis
61 4c52d5bf Ilias Tsitsimpis
# --------------------------------------------------------------------
62 4c52d5bf Ilias Tsitsimpis
# Constant variables
63 4c52d5bf Ilias Tsitsimpis
LOCK_EXT = ".lock"
64 4c52d5bf Ilias Tsitsimpis
SECTION_SEPARATOR = \
65 4c52d5bf Ilias Tsitsimpis
    "-- -------------------------------------------------------------------"
66 4c52d5bf Ilias Tsitsimpis
SECTION_PREFIX = "-- "
67 06c9d4db Ilias Tsitsimpis
SECTION_RUNNED = "Tests Run"
68 4c52d5bf Ilias Tsitsimpis
SECTION_RESULTS = "Results"
69 4c52d5bf Ilias Tsitsimpis
SECTION_NEW = "__ADD_NEW_SECTION__"
70 4c52d5bf Ilias Tsitsimpis
SECTION_PASSED = "  * Passed:"
71 4c52d5bf Ilias Tsitsimpis
SECTION_FAILED = "  * Failed:"
72 4c52d5bf Ilias Tsitsimpis
73 a64d8485 Ilias Tsitsimpis
# Ignore `paramiko' logger
74 a64d8485 Ilias Tsitsimpis
logging.getLogger('paramiko').addHandler(logging.NullHandler())
75 a64d8485 Ilias Tsitsimpis
76 4c52d5bf Ilias Tsitsimpis
77 4c52d5bf Ilias Tsitsimpis
# --------------------------------------------------------------------
78 4c52d5bf Ilias Tsitsimpis
# Helper functions
79 3e5bbd85 Ilias Tsitsimpis
def _cyan(msg):
80 3e5bbd85 Ilias Tsitsimpis
    """Bold High Intensity Cyan color"""
81 3e5bbd85 Ilias Tsitsimpis
    return "\x1b[1;96m" + str(msg) + "\x1b[0m"
82 4c52d5bf Ilias Tsitsimpis
83 4c52d5bf Ilias Tsitsimpis
84 4c52d5bf Ilias Tsitsimpis
def _yellow(msg):
85 4c52d5bf Ilias Tsitsimpis
    """Yellow color"""
86 4c52d5bf Ilias Tsitsimpis
    return "\x1b[33m" + str(msg) + "\x1b[0m"
87 4c52d5bf Ilias Tsitsimpis
88 4c52d5bf Ilias Tsitsimpis
89 4c52d5bf Ilias Tsitsimpis
def _red(msg):
90 4c52d5bf Ilias Tsitsimpis
    """Yellow color"""
91 4c52d5bf Ilias Tsitsimpis
    return "\x1b[31m" + str(msg) + "\x1b[0m"
92 4c52d5bf Ilias Tsitsimpis
93 4c52d5bf Ilias Tsitsimpis
94 12ef696f Ilias Tsitsimpis
def _magenta(msg):
95 12ef696f Ilias Tsitsimpis
    """Magenta color"""
96 4c52d5bf Ilias Tsitsimpis
    return "\x1b[35m" + str(msg) + "\x1b[0m"
97 4c52d5bf Ilias Tsitsimpis
98 4c52d5bf Ilias Tsitsimpis
99 12ef696f Ilias Tsitsimpis
def _green(msg):
100 12ef696f Ilias Tsitsimpis
    """Green color"""
101 12ef696f Ilias Tsitsimpis
    return "\x1b[32m" + str(msg) + "\x1b[0m"
102 4c52d5bf Ilias Tsitsimpis
103 4c52d5bf Ilias Tsitsimpis
104 4c52d5bf Ilias Tsitsimpis
def _format_message(msg, *args):
105 4c52d5bf Ilias Tsitsimpis
    """Format the message using the args"""
106 6c78720b Ilias Tsitsimpis
    if args:
107 6c78720b Ilias Tsitsimpis
        return (msg % args) + "\n"
108 6c78720b Ilias Tsitsimpis
    else:
109 6c78720b Ilias Tsitsimpis
        return msg + "\n"
110 4c52d5bf Ilias Tsitsimpis
111 4c52d5bf Ilias Tsitsimpis
112 4c52d5bf Ilias Tsitsimpis
def _list_to_string(lst, append=""):
113 4c52d5bf Ilias Tsitsimpis
    """Convert a list of strings to string
114 4c52d5bf Ilias Tsitsimpis

115 4c52d5bf Ilias Tsitsimpis
    Append the value given in L{append} in front of all lines
116 4c52d5bf Ilias Tsitsimpis
    (except of the first line).
117 4c52d5bf Ilias Tsitsimpis

118 4c52d5bf Ilias Tsitsimpis
    """
119 4c52d5bf Ilias Tsitsimpis
    if isinstance(lst, list):
120 4c52d5bf Ilias Tsitsimpis
        return append.join(lst).rstrip('\n')
121 4c52d5bf Ilias Tsitsimpis
    else:
122 4c52d5bf Ilias Tsitsimpis
        return lst.rstrip('\n')
123 4c52d5bf Ilias Tsitsimpis
124 4c52d5bf Ilias Tsitsimpis
125 4c52d5bf Ilias Tsitsimpis
# --------------------------------------
126 4c52d5bf Ilias Tsitsimpis
def _locate_sections(contents):
127 4c52d5bf Ilias Tsitsimpis
    """Locate the sections inside the logging file"""
128 4c52d5bf Ilias Tsitsimpis
    i = 0
129 4c52d5bf Ilias Tsitsimpis
    res = []
130 4c52d5bf Ilias Tsitsimpis
    for cnt in contents:
131 4c52d5bf Ilias Tsitsimpis
        if SECTION_SEPARATOR in cnt:
132 4c52d5bf Ilias Tsitsimpis
            res.append(i+1)
133 4c52d5bf Ilias Tsitsimpis
        i += 1
134 4c52d5bf Ilias Tsitsimpis
    return res
135 4c52d5bf Ilias Tsitsimpis
136 4c52d5bf Ilias Tsitsimpis
137 4c52d5bf Ilias Tsitsimpis
def _locate_input(contents, section):
138 4c52d5bf Ilias Tsitsimpis
    """Locate position to insert text
139 4c52d5bf Ilias Tsitsimpis

140 4c52d5bf Ilias Tsitsimpis
    Given a section location the next possition to insert text inside that
141 4c52d5bf Ilias Tsitsimpis
    section.
142 4c52d5bf Ilias Tsitsimpis

143 4c52d5bf Ilias Tsitsimpis
    """
144 4c52d5bf Ilias Tsitsimpis
    sect_locs = _locate_sections(contents)
145 4c52d5bf Ilias Tsitsimpis
    if section == SECTION_NEW:
146 4c52d5bf Ilias Tsitsimpis
        # We want to add a new section
147 4c52d5bf Ilias Tsitsimpis
        # Just return the position of SECTION_RESULTS
148 4c52d5bf Ilias Tsitsimpis
        for obj in sect_locs:
149 4c52d5bf Ilias Tsitsimpis
            if SECTION_RESULTS in contents[obj]:
150 4c52d5bf Ilias Tsitsimpis
                return obj - 1
151 4c52d5bf Ilias Tsitsimpis
    else:
152 4c52d5bf Ilias Tsitsimpis
        # We will add our message in this location
153 4c52d5bf Ilias Tsitsimpis
        for (index, obj) in enumerate(sect_locs):
154 4c52d5bf Ilias Tsitsimpis
            if section in contents[obj]:
155 4c52d5bf Ilias Tsitsimpis
                return sect_locs[index+1] - 3
156 4c52d5bf Ilias Tsitsimpis
157 4c52d5bf Ilias Tsitsimpis
    # We didn't find our section??
158 4c52d5bf Ilias Tsitsimpis
    sys.stderr.write("Section %s could not be found in logging file\n"
159 4c52d5bf Ilias Tsitsimpis
                     % section)
160 12ef696f Ilias Tsitsimpis
    sys.exit("Error in logger._locate_input")
161 4c52d5bf Ilias Tsitsimpis
162 4c52d5bf Ilias Tsitsimpis
163 4c52d5bf Ilias Tsitsimpis
def _add_testsuite_results(contents, section, testsuite):
164 4c52d5bf Ilias Tsitsimpis
    """Add the given testsuite to results
165 4c52d5bf Ilias Tsitsimpis

166 4c52d5bf Ilias Tsitsimpis
    Well we know that SECTION_FAILED is the last line and SECTION_PASSED is the
167 4c52d5bf Ilias Tsitsimpis
    line before, so we are going to cheat here and use this information.
168 4c52d5bf Ilias Tsitsimpis

169 4c52d5bf Ilias Tsitsimpis
    """
170 4c52d5bf Ilias Tsitsimpis
    if section == SECTION_PASSED:
171 06c9d4db Ilias Tsitsimpis
        line = contents[-2].rstrip()
172 06c9d4db Ilias Tsitsimpis
        if line.endswith(":"):
173 06c9d4db Ilias Tsitsimpis
            new_line = line + " " + testsuite + "\n"
174 06c9d4db Ilias Tsitsimpis
        else:
175 06c9d4db Ilias Tsitsimpis
            new_line = line + ", " + testsuite + "\n"
176 4c52d5bf Ilias Tsitsimpis
        contents[-2] = new_line
177 4c52d5bf Ilias Tsitsimpis
    elif section == SECTION_FAILED:
178 06c9d4db Ilias Tsitsimpis
        line = contents[-1].rstrip()
179 06c9d4db Ilias Tsitsimpis
        if line.endswith(":"):
180 06c9d4db Ilias Tsitsimpis
            new_line = line.rstrip() + " " + testsuite + "\n"
181 06c9d4db Ilias Tsitsimpis
        else:
182 06c9d4db Ilias Tsitsimpis
            new_line = line.rstrip() + ", " + testsuite + "\n"
183 4c52d5bf Ilias Tsitsimpis
        contents[-1] = new_line
184 4c52d5bf Ilias Tsitsimpis
    else:
185 4c52d5bf Ilias Tsitsimpis
        sys.stderr.write("Unknown section %s in _add_testsuite_results\n"
186 4c52d5bf Ilias Tsitsimpis
                         % section)
187 12ef696f Ilias Tsitsimpis
        sys.exit("Error in logger._add_testsuite_results")
188 4c52d5bf Ilias Tsitsimpis
    return contents
189 4c52d5bf Ilias Tsitsimpis
190 4c52d5bf Ilias Tsitsimpis
191 4c52d5bf Ilias Tsitsimpis
def _write_log_file(file_location, section, message):
192 4c52d5bf Ilias Tsitsimpis
    """Write something to our log file
193 4c52d5bf Ilias Tsitsimpis

194 4c52d5bf Ilias Tsitsimpis
    For this we have to get the lock, read and parse the file add the new
195 4c52d5bf Ilias Tsitsimpis
    message and re-write the file.
196 4c52d5bf Ilias Tsitsimpis

197 4c52d5bf Ilias Tsitsimpis
    """
198 4c52d5bf Ilias Tsitsimpis
    # Get the lock
199 4c52d5bf Ilias Tsitsimpis
    file_lock = os.path.splitext(file_location)[0] + LOCK_EXT
200 4c52d5bf Ilias Tsitsimpis
    with filelocker.lock(file_lock, filelocker.LOCK_EX):
201 4c52d5bf Ilias Tsitsimpis
        with open(file_location, "r+") as log_file:
202 4c52d5bf Ilias Tsitsimpis
            contents = log_file.readlines()
203 4c52d5bf Ilias Tsitsimpis
            if section == SECTION_PASSED or section == SECTION_FAILED:
204 4c52d5bf Ilias Tsitsimpis
                # Add testsuite to results
205 4c52d5bf Ilias Tsitsimpis
                new_contents = \
206 4c52d5bf Ilias Tsitsimpis
                    _add_testsuite_results(contents, section, message)
207 4c52d5bf Ilias Tsitsimpis
            else:
208 4c52d5bf Ilias Tsitsimpis
                # Add message to its line
209 4c52d5bf Ilias Tsitsimpis
                input_loc = _locate_input(contents, section)
210 4c52d5bf Ilias Tsitsimpis
                new_contents = \
211 4c52d5bf Ilias Tsitsimpis
                    contents[:input_loc] + [message] + contents[input_loc:]
212 4c52d5bf Ilias Tsitsimpis
            log_file.seek(0)
213 4c52d5bf Ilias Tsitsimpis
            log_file.write("".join(new_contents))
214 4c52d5bf Ilias Tsitsimpis
215 4c52d5bf Ilias Tsitsimpis
216 4c52d5bf Ilias Tsitsimpis
# --------------------------------------------------------------------
217 4c52d5bf Ilias Tsitsimpis
# The Log class
218 4c52d5bf Ilias Tsitsimpis
class Log(object):
219 4c52d5bf Ilias Tsitsimpis
    """Burnin logger
220 4c52d5bf Ilias Tsitsimpis

221 4c52d5bf Ilias Tsitsimpis
    """
222 4c52d5bf Ilias Tsitsimpis
    # ----------------------------------
223 9355a604 Ilias Tsitsimpis
    # pylint: disable=too-many-arguments
224 4c52d5bf Ilias Tsitsimpis
    def __init__(self, output_dir, verbose=1, use_colors=True,
225 ee89df69 Ilias Tsitsimpis
                 in_parallel=False, log_level=0, curr_time=None):
226 4c52d5bf Ilias Tsitsimpis
        """Initialize our loggers
227 4c52d5bf Ilias Tsitsimpis

228 4c52d5bf Ilias Tsitsimpis
        The file to be used by our file logger will be created inside
229 4c52d5bf Ilias Tsitsimpis
        the L{output_dir} with name the current timestamp.
230 4c52d5bf Ilias Tsitsimpis

231 4c52d5bf Ilias Tsitsimpis
        @type output_dir: string
232 4c52d5bf Ilias Tsitsimpis
        @param output_dir: the directory to save the output file
233 4c52d5bf Ilias Tsitsimpis
        @type verbose: int
234 4c52d5bf Ilias Tsitsimpis
        @param verbose: the verbose level to use for stdout/stderr logger
235 4c52d5bf Ilias Tsitsimpis
            0: verbose at minimum level (only which test we are running now)
236 4c52d5bf Ilias Tsitsimpis
            1: verbose at info level (information about our running test)
237 4c52d5bf Ilias Tsitsimpis
            2: verbose at debug level
238 4c52d5bf Ilias Tsitsimpis
        @type use_colors: boolean
239 4c52d5bf Ilias Tsitsimpis
        @param use_colors: use colors for out stdout/stderr logger
240 4c52d5bf Ilias Tsitsimpis
        @type in_parallel: boolean
241 4c52d5bf Ilias Tsitsimpis
        @param in_parallel: this signifies that burnin is running in parallel
242 ee89df69 Ilias Tsitsimpis
        @type log_level: int
243 ee89df69 Ilias Tsitsimpis
        @param log_level: logging level
244 ee89df69 Ilias Tsitsimpis
            0: log to console and file
245 ee89df69 Ilias Tsitsimpis
            1: log to file only and output the results to console
246 ee89df69 Ilias Tsitsimpis
            2: don't log
247 ee89df69 Ilias Tsitsimpis
        @type curr_time: datetime.datetime()
248 ee89df69 Ilias Tsitsimpis
        @param curr_time: The current time (used as burnin's run id)
249 4c52d5bf Ilias Tsitsimpis

250 4c52d5bf Ilias Tsitsimpis
        """
251 4c52d5bf Ilias Tsitsimpis
        self.verbose = verbose
252 4c52d5bf Ilias Tsitsimpis
        self.use_colors = use_colors
253 4c52d5bf Ilias Tsitsimpis
        self.in_parallel = in_parallel
254 ee89df69 Ilias Tsitsimpis
        self.log_level = log_level
255 4c52d5bf Ilias Tsitsimpis
256 12ef696f Ilias Tsitsimpis
        assert output_dir
257 12ef696f Ilias Tsitsimpis
258 2afd10bf Ilias Tsitsimpis
        if curr_time is None:
259 2afd10bf Ilias Tsitsimpis
            curr_time = datetime.datetime.now()
260 4c52d5bf Ilias Tsitsimpis
        timestamp = datetime.datetime.strftime(
261 2afd10bf Ilias Tsitsimpis
            curr_time, "%Y%m%d%H%M%S (%a %b %d %Y %H:%M)")
262 4c52d5bf Ilias Tsitsimpis
        file_name = timestamp + ".log"
263 4c52d5bf Ilias Tsitsimpis
        self.file_location = os.path.join(output_dir, file_name)
264 4c52d5bf Ilias Tsitsimpis
265 2afd10bf Ilias Tsitsimpis
        self._write_to_stdout(None, "Starting burnin with id %s\n" % timestamp)
266 4c52d5bf Ilias Tsitsimpis
267 4c52d5bf Ilias Tsitsimpis
        # Create the logging file
268 828bbf06 Ilias Tsitsimpis
        self._create_logging_file(timestamp, output_dir)
269 4c52d5bf Ilias Tsitsimpis
270 828bbf06 Ilias Tsitsimpis
    def _create_logging_file(self, timestamp, output_dir):
271 4c52d5bf Ilias Tsitsimpis
        """Create the logging file"""
272 ee89df69 Ilias Tsitsimpis
        if self.log_level > 1:
273 ee89df69 Ilias Tsitsimpis
            return
274 828bbf06 Ilias Tsitsimpis
275 828bbf06 Ilias Tsitsimpis
        # Create file for logging
276 828bbf06 Ilias Tsitsimpis
        output_dir = os.path.expanduser(output_dir)
277 828bbf06 Ilias Tsitsimpis
        if not os.path.exists(output_dir):
278 828bbf06 Ilias Tsitsimpis
            self.debug(None, "Creating directory %s", output_dir)
279 828bbf06 Ilias Tsitsimpis
            try:
280 828bbf06 Ilias Tsitsimpis
                os.makedirs(output_dir)
281 828bbf06 Ilias Tsitsimpis
            except OSError as err:
282 828bbf06 Ilias Tsitsimpis
                msg = ("Failed to create folder \"%s\" with error: %s\n"
283 828bbf06 Ilias Tsitsimpis
                       % (output_dir, err))
284 828bbf06 Ilias Tsitsimpis
                sys.stderr.write(msg)
285 828bbf06 Ilias Tsitsimpis
                sys.exit("Failed to create log folder")
286 828bbf06 Ilias Tsitsimpis
287 4c52d5bf Ilias Tsitsimpis
        self.debug(None, "Using \"%s\" file for logging", self.file_location)
288 4c52d5bf Ilias Tsitsimpis
        with open(self.file_location, 'w') as out_file:
289 4c52d5bf Ilias Tsitsimpis
            out_file.write(SECTION_SEPARATOR + "\n")
290 828bbf06 Ilias Tsitsimpis
            out_file.write("%s%s with id %s:\n\n\n\n" %
291 4c52d5bf Ilias Tsitsimpis
                           (SECTION_PREFIX, SECTION_RUNNED, timestamp))
292 4c52d5bf Ilias Tsitsimpis
            out_file.write(SECTION_SEPARATOR + "\n")
293 4c52d5bf Ilias Tsitsimpis
            out_file.write("%s%s:\n\n" % (SECTION_PREFIX, SECTION_RESULTS))
294 4c52d5bf Ilias Tsitsimpis
            out_file.write(SECTION_PASSED + "\n" + SECTION_FAILED + "\n")
295 4c52d5bf Ilias Tsitsimpis
296 4c52d5bf Ilias Tsitsimpis
    def __del__(self):
297 4c52d5bf Ilias Tsitsimpis
        """Delete the Log object"""
298 ee89df69 Ilias Tsitsimpis
        self.print_logfile_to_stdout()
299 4c52d5bf Ilias Tsitsimpis
        # Remove the lock file
300 12ef696f Ilias Tsitsimpis
        if hasattr(self, "file_location"):
301 12ef696f Ilias Tsitsimpis
            file_lock = os.path.splitext(self.file_location)[0] + LOCK_EXT
302 12ef696f Ilias Tsitsimpis
            try:
303 12ef696f Ilias Tsitsimpis
                os.remove(file_lock)
304 12ef696f Ilias Tsitsimpis
            except OSError:
305 12ef696f Ilias Tsitsimpis
                self.debug(None, "Couldn't delete lock file")
306 4c52d5bf Ilias Tsitsimpis
307 0c1833c8 Ilias Tsitsimpis
    def print_logfile_to_stdout(self):
308 0c1833c8 Ilias Tsitsimpis
        """Print the contents of our log file to stdout"""
309 ee89df69 Ilias Tsitsimpis
        if self.log_level == 1:
310 ee89df69 Ilias Tsitsimpis
            with open(self.file_location, 'r') as fin:
311 ee89df69 Ilias Tsitsimpis
                sys.stdout.write(fin.read())
312 0c1833c8 Ilias Tsitsimpis
313 4c52d5bf Ilias Tsitsimpis
    # ----------------------------------
314 4c52d5bf Ilias Tsitsimpis
    # Logging methods
315 4c52d5bf Ilias Tsitsimpis
    def debug(self, section, msg, *args):
316 4c52d5bf Ilias Tsitsimpis
        """Debug messages (verbose 2)
317 4c52d5bf Ilias Tsitsimpis

318 4c52d5bf Ilias Tsitsimpis
        We show debug messages only to stdout. The message will be formatted
319 4c52d5bf Ilias Tsitsimpis
        using the args.
320 4c52d5bf Ilias Tsitsimpis

321 4c52d5bf Ilias Tsitsimpis
        """
322 4c52d5bf Ilias Tsitsimpis
        msg = "  (DD) " + _list_to_string(msg, append="       ")
323 4c52d5bf Ilias Tsitsimpis
        if self.verbose >= 2:
324 4c52d5bf Ilias Tsitsimpis
            colored_msg = self._color_message(None, msg, *args)
325 4c52d5bf Ilias Tsitsimpis
            self._write_to_stdout(section, colored_msg)
326 4c52d5bf Ilias Tsitsimpis
327 4c52d5bf Ilias Tsitsimpis
    def log(self, section, msg, *args):
328 4c52d5bf Ilias Tsitsimpis
        """Normal messages (verbose 0)"""
329 4c52d5bf Ilias Tsitsimpis
        assert section, "Section can not be empty"
330 4c52d5bf Ilias Tsitsimpis
331 4c52d5bf Ilias Tsitsimpis
        msg = _list_to_string(msg)
332 4c52d5bf Ilias Tsitsimpis
333 4c52d5bf Ilias Tsitsimpis
        colored_msg = self._color_message(None, msg, *args)
334 4c52d5bf Ilias Tsitsimpis
        self._write_to_stdout(section, colored_msg)
335 4c52d5bf Ilias Tsitsimpis
336 4c52d5bf Ilias Tsitsimpis
        plain_msg = _format_message(msg, *args)
337 4c52d5bf Ilias Tsitsimpis
        self._write_to_file(section, plain_msg)
338 4c52d5bf Ilias Tsitsimpis
339 4c52d5bf Ilias Tsitsimpis
    def info(self, section, msg, *args):
340 4c52d5bf Ilias Tsitsimpis
        """Info messages (verbose 1)
341 4c52d5bf Ilias Tsitsimpis

342 4c52d5bf Ilias Tsitsimpis
        Prepare message and write it to file logger and stdout logger
343 4c52d5bf Ilias Tsitsimpis

344 4c52d5bf Ilias Tsitsimpis
        """
345 4c52d5bf Ilias Tsitsimpis
        assert section, "Section can not be empty"
346 4c52d5bf Ilias Tsitsimpis
347 4c52d5bf Ilias Tsitsimpis
        msg = "  " + _list_to_string(msg, "  ")
348 4c52d5bf Ilias Tsitsimpis
        if self.verbose >= 1:
349 4c52d5bf Ilias Tsitsimpis
            colored_msg = self._color_message(None, msg, *args)
350 4c52d5bf Ilias Tsitsimpis
            self._write_to_stdout(section, colored_msg)
351 4c52d5bf Ilias Tsitsimpis
352 4c52d5bf Ilias Tsitsimpis
        plain_msg = _format_message(msg, *args)
353 4c52d5bf Ilias Tsitsimpis
        self._write_to_file(section, plain_msg)
354 4c52d5bf Ilias Tsitsimpis
355 4c52d5bf Ilias Tsitsimpis
    def warning(self, section, msg, *args):
356 4c52d5bf Ilias Tsitsimpis
        """Warning messages"""
357 4c52d5bf Ilias Tsitsimpis
        assert section, "Section can not be empty"
358 4c52d5bf Ilias Tsitsimpis
359 4c52d5bf Ilias Tsitsimpis
        msg = "  (WW) " + _list_to_string(msg, "       ")
360 4c52d5bf Ilias Tsitsimpis
361 4c52d5bf Ilias Tsitsimpis
        colored_msg = self._color_message(_yellow, msg, *args)
362 4c52d5bf Ilias Tsitsimpis
        self._write_to_stderr(section, colored_msg)
363 4c52d5bf Ilias Tsitsimpis
364 4c52d5bf Ilias Tsitsimpis
        plain_msg = _format_message(msg, *args)
365 4c52d5bf Ilias Tsitsimpis
        self._write_to_file(section, plain_msg)
366 4c52d5bf Ilias Tsitsimpis
367 4c52d5bf Ilias Tsitsimpis
    def error(self, section, msg, *args):
368 4c52d5bf Ilias Tsitsimpis
        """Error messages"""
369 4c52d5bf Ilias Tsitsimpis
        assert section, "Section can not be empty"
370 4c52d5bf Ilias Tsitsimpis
371 4c52d5bf Ilias Tsitsimpis
        msg = "  (EE) " + _list_to_string(msg, "       ")
372 4c52d5bf Ilias Tsitsimpis
373 4c52d5bf Ilias Tsitsimpis
        colored_msg = self._color_message(_red, msg, *args)
374 4c52d5bf Ilias Tsitsimpis
        self._write_to_stderr(section, colored_msg)
375 4c52d5bf Ilias Tsitsimpis
376 4c52d5bf Ilias Tsitsimpis
        plain_msg = _format_message(msg, *args)
377 4c52d5bf Ilias Tsitsimpis
        self._write_to_file(section, plain_msg)
378 4c52d5bf Ilias Tsitsimpis
379 4c52d5bf Ilias Tsitsimpis
    def _write_to_stdout(self, section, msg):
380 4c52d5bf Ilias Tsitsimpis
        """Write to stdout"""
381 ee89df69 Ilias Tsitsimpis
        if self.log_level > 0:
382 ee89df69 Ilias Tsitsimpis
            return
383 ee89df69 Ilias Tsitsimpis
        if section is not None and self.in_parallel:
384 ee89df69 Ilias Tsitsimpis
            sys.stdout.write(section + ": " + msg)
385 ee89df69 Ilias Tsitsimpis
        else:
386 ee89df69 Ilias Tsitsimpis
            sys.stdout.write(msg)
387 4c52d5bf Ilias Tsitsimpis
388 4c52d5bf Ilias Tsitsimpis
    def _write_to_stderr(self, section, msg):
389 4c52d5bf Ilias Tsitsimpis
        """Write to stderr"""
390 ee89df69 Ilias Tsitsimpis
        if self.log_level > 0:
391 ee89df69 Ilias Tsitsimpis
            return
392 ee89df69 Ilias Tsitsimpis
        if section is not None and self.in_parallel:
393 ee89df69 Ilias Tsitsimpis
            sys.stderr.write(section + ": " + msg)
394 ee89df69 Ilias Tsitsimpis
        else:
395 ee89df69 Ilias Tsitsimpis
            sys.stderr.write(msg)
396 4c52d5bf Ilias Tsitsimpis
397 4c52d5bf Ilias Tsitsimpis
    def _write_to_file(self, section, msg):
398 4c52d5bf Ilias Tsitsimpis
        """Write to file"""
399 ee89df69 Ilias Tsitsimpis
        if self.log_level > 1:
400 ee89df69 Ilias Tsitsimpis
            return
401 4c52d5bf Ilias Tsitsimpis
        _write_log_file(self.file_location, section, msg)
402 4c52d5bf Ilias Tsitsimpis
403 4c52d5bf Ilias Tsitsimpis
    # ----------------------------------
404 4c52d5bf Ilias Tsitsimpis
    # Handle testsuites
405 4c52d5bf Ilias Tsitsimpis
    def testsuite_start(self, testsuite):
406 4c52d5bf Ilias Tsitsimpis
        """Start a new testsuite
407 4c52d5bf Ilias Tsitsimpis

408 4c52d5bf Ilias Tsitsimpis
        Add a new section in the logging file
409 4c52d5bf Ilias Tsitsimpis

410 4c52d5bf Ilias Tsitsimpis
        """
411 4c52d5bf Ilias Tsitsimpis
        assert testsuite, "Testsuite name can not be emtpy"
412 4c52d5bf Ilias Tsitsimpis
413 4c52d5bf Ilias Tsitsimpis
        # Add a new section in the logging file
414 4c52d5bf Ilias Tsitsimpis
        test_runned = "  * " + testsuite + "\n"
415 ee89df69 Ilias Tsitsimpis
        self._write_to_file(SECTION_RUNNED, test_runned)
416 4c52d5bf Ilias Tsitsimpis
417 4c52d5bf Ilias Tsitsimpis
        new_section_entry = \
418 4c52d5bf Ilias Tsitsimpis
            SECTION_SEPARATOR + "\n" + SECTION_PREFIX + testsuite + "\n\n\n\n"
419 ee89df69 Ilias Tsitsimpis
        self._write_to_file(SECTION_NEW, new_section_entry)
420 4c52d5bf Ilias Tsitsimpis
421 4c52d5bf Ilias Tsitsimpis
        # Add new section to the stdout
422 4c52d5bf Ilias Tsitsimpis
        msg = "Starting testsuite %s" % testsuite
423 12ef696f Ilias Tsitsimpis
        colored_msg = self._color_message(_magenta, msg)
424 4c52d5bf Ilias Tsitsimpis
        self._write_to_stdout(None, colored_msg)
425 4c52d5bf Ilias Tsitsimpis
426 4c52d5bf Ilias Tsitsimpis
    def testsuite_success(self, testsuite):
427 4c52d5bf Ilias Tsitsimpis
        """A testsuite has successfully finished
428 4c52d5bf Ilias Tsitsimpis

429 4c52d5bf Ilias Tsitsimpis
        Update Results
430 4c52d5bf Ilias Tsitsimpis

431 4c52d5bf Ilias Tsitsimpis
        """
432 4c52d5bf Ilias Tsitsimpis
        assert testsuite, "Testsuite name can not be emtpy"
433 4c52d5bf Ilias Tsitsimpis
434 4c52d5bf Ilias Tsitsimpis
        # Add our testsuite to Results
435 ee89df69 Ilias Tsitsimpis
        self._write_to_file(SECTION_PASSED, testsuite)
436 4c52d5bf Ilias Tsitsimpis
437 4c52d5bf Ilias Tsitsimpis
        # Add success to stdout
438 4c52d5bf Ilias Tsitsimpis
        msg = "Testsuite %s passed" % testsuite
439 12ef696f Ilias Tsitsimpis
        colored_msg = self._color_message(_green, msg)
440 4c52d5bf Ilias Tsitsimpis
        self._write_to_stdout(None, colored_msg)
441 4c52d5bf Ilias Tsitsimpis
442 4c52d5bf Ilias Tsitsimpis
    def testsuite_failure(self, testsuite):
443 4c52d5bf Ilias Tsitsimpis
        """A testsuite has failed
444 4c52d5bf Ilias Tsitsimpis

445 4c52d5bf Ilias Tsitsimpis
        Update Results
446 4c52d5bf Ilias Tsitsimpis

447 4c52d5bf Ilias Tsitsimpis
        """
448 4c52d5bf Ilias Tsitsimpis
        assert testsuite, "Testsuite name can not be emtpy"
449 4c52d5bf Ilias Tsitsimpis
450 4c52d5bf Ilias Tsitsimpis
        # Add our testsuite to Results
451 ee89df69 Ilias Tsitsimpis
        self._write_to_file(SECTION_FAILED, testsuite)
452 4c52d5bf Ilias Tsitsimpis
453 4c52d5bf Ilias Tsitsimpis
        # Add success to stdout
454 4c52d5bf Ilias Tsitsimpis
        msg = "Testsuite %s failed" % testsuite
455 12ef696f Ilias Tsitsimpis
        colored_msg = self._color_message(_red, msg)
456 4c52d5bf Ilias Tsitsimpis
        self._write_to_stdout(None, colored_msg)
457 4c52d5bf Ilias Tsitsimpis
458 4c52d5bf Ilias Tsitsimpis
    # ----------------------------------
459 4c52d5bf Ilias Tsitsimpis
    # Colors
460 4c52d5bf Ilias Tsitsimpis
    def _color_message(self, color_fun, msg, *args):
461 4c52d5bf Ilias Tsitsimpis
        """Color a message before printing it
462 4c52d5bf Ilias Tsitsimpis

463 4c52d5bf Ilias Tsitsimpis
        The color_fun parameter is used when we want the whole message to be
464 4c52d5bf Ilias Tsitsimpis
        colored.
465 4c52d5bf Ilias Tsitsimpis

466 4c52d5bf Ilias Tsitsimpis
        """
467 4c52d5bf Ilias Tsitsimpis
        if self.use_colors:
468 4c52d5bf Ilias Tsitsimpis
            if callable(color_fun):
469 6c78720b Ilias Tsitsimpis
                if args:
470 6c78720b Ilias Tsitsimpis
                    return color_fun((msg % args)) + "\n"
471 6c78720b Ilias Tsitsimpis
                else:
472 6c78720b Ilias Tsitsimpis
                    return color_fun(msg) + "\n"
473 4c52d5bf Ilias Tsitsimpis
            else:
474 3e5bbd85 Ilias Tsitsimpis
                args = tuple([_cyan(arg) for arg in args])
475 4c52d5bf Ilias Tsitsimpis
                return _format_message(msg, *args)
476 4c52d5bf Ilias Tsitsimpis
        else:
477 4c52d5bf Ilias Tsitsimpis
            return _format_message(msg, *args)