Merge branch 'stable-2.16' into stable-2.17
[ganeti-github.git] / test / py / ganeti.utils.log_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2011 Google Inc.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
9 # met:
10 #
11 # 1. Redistributions of source code must retain the above copyright notice,
12 # this list of conditions and the following disclaimer.
13 #
14 # 2. Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in the
16 # documentation and/or other materials provided with the distribution.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19 # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30
31 """Script for testing ganeti.utils.log"""
32
33 import os
34 import unittest
35 import logging
36 import tempfile
37 import shutil
38 import threading
39 from cStringIO import StringIO
40
41 from ganeti import constants
42 from ganeti import errors
43 from ganeti import compat
44 from ganeti import utils
45
46 import testutils
47
48
49 class TestLogHandler(unittest.TestCase):
50 def testNormal(self):
51 tmpfile = tempfile.NamedTemporaryFile()
52
53 handler = utils.log._ReopenableLogHandler(tmpfile.name)
54 handler.setFormatter(logging.Formatter("%(asctime)s: %(message)s"))
55
56 logger = logging.Logger("TestLogger")
57 logger.addHandler(handler)
58 self.assertEqual(len(logger.handlers), 1)
59
60 logger.error("Test message ERROR")
61 logger.info("Test message INFO")
62
63 logger.removeHandler(handler)
64 self.assertFalse(logger.handlers)
65 handler.close()
66
67 self.assertEqual(len(utils.ReadFile(tmpfile.name).splitlines()), 2)
68
69 def testReopen(self):
70 tmpfile = tempfile.NamedTemporaryFile()
71 tmpfile2 = tempfile.NamedTemporaryFile()
72
73 handler = utils.log._ReopenableLogHandler(tmpfile.name)
74
75 self.assertFalse(utils.ReadFile(tmpfile.name))
76 self.assertFalse(utils.ReadFile(tmpfile2.name))
77
78 logger = logging.Logger("TestLoggerReopen")
79 logger.addHandler(handler)
80
81 for _ in range(3):
82 logger.error("Test message ERROR")
83 handler.flush()
84 self.assertEqual(len(utils.ReadFile(tmpfile.name).splitlines()), 3)
85 before_id = utils.GetFileID(tmpfile.name)
86
87 handler.RequestReopen()
88 self.assertTrue(handler._reopen)
89 self.assertTrue(utils.VerifyFileID(utils.GetFileID(tmpfile.name),
90 before_id))
91
92 # Rename only after requesting reopen
93 os.rename(tmpfile.name, tmpfile2.name)
94 assert not os.path.exists(tmpfile.name)
95
96 # Write another message, should reopen
97 for _ in range(4):
98 logger.info("Test message INFO")
99
100 # Flag must be reset
101 self.assertFalse(handler._reopen)
102
103 self.assertFalse(utils.VerifyFileID(utils.GetFileID(tmpfile.name),
104 before_id))
105
106 logger.removeHandler(handler)
107 self.assertFalse(logger.handlers)
108 handler.close()
109
110 self.assertEqual(len(utils.ReadFile(tmpfile.name).splitlines()), 4)
111 self.assertEqual(len(utils.ReadFile(tmpfile2.name).splitlines()), 3)
112
113 def testConsole(self):
114 for (console, check) in [(None, False),
115 (tempfile.NamedTemporaryFile(), True),
116 (self._FailingFile(os.devnull), False)]:
117 # Create a handler which will fail when handling errors
118 cls = utils.log._LogErrorsToConsole(self._FailingHandler)
119
120 # Instantiate handler with file which will fail when writing,
121 # provoking a write to the console
122 handler = cls(console, self._FailingFile(os.devnull))
123
124 logger = logging.Logger("TestLogger")
125 logger.addHandler(handler)
126 self.assertEqual(len(logger.handlers), 1)
127
128 # Provoke write
129 logger.error("Test message ERROR")
130
131 # Take everything apart
132 logger.removeHandler(handler)
133 self.assertFalse(logger.handlers)
134 handler.close()
135
136 if console and check:
137 console.flush()
138
139 # Check console output
140 consout = utils.ReadFile(console.name)
141 self.assertTrue("Cannot log message" in consout)
142 self.assertTrue("Test message ERROR" in consout)
143
144 class _FailingFile(file):
145 def write(self, _):
146 raise Exception
147
148 class _FailingHandler(logging.StreamHandler):
149 def handleError(self, _):
150 raise Exception
151
152
153 class TestSetupLogging(unittest.TestCase):
154 def setUp(self):
155 self.tmpdir = tempfile.mkdtemp()
156
157 def tearDown(self):
158 shutil.rmtree(self.tmpdir)
159
160 def testSimple(self):
161 logfile = utils.PathJoin(self.tmpdir, "basic.log")
162 logger = logging.Logger("TestLogger")
163 self.assertTrue(callable(utils.SetupLogging(logfile, "test",
164 console_logging=False,
165 syslog=constants.SYSLOG_NO,
166 stderr_logging=False,
167 multithreaded=False,
168 root_logger=logger)))
169 self.assertEqual(utils.ReadFile(logfile), "")
170 logger.error("This is a test")
171
172 # Ensure SetupLogging used custom logger
173 logging.error("This message should not show up in the test log file")
174
175 self.assertTrue(utils.ReadFile(logfile).endswith("This is a test\n"))
176
177 def testReopen(self):
178 logfile = utils.PathJoin(self.tmpdir, "reopen.log")
179 logfile2 = utils.PathJoin(self.tmpdir, "reopen.log.OLD")
180 logger = logging.Logger("TestLogger")
181 reopen_fn = utils.SetupLogging(logfile, "test",
182 console_logging=False,
183 syslog=constants.SYSLOG_NO,
184 stderr_logging=False,
185 multithreaded=False,
186 root_logger=logger)
187 self.assertTrue(callable(reopen_fn))
188
189 self.assertEqual(utils.ReadFile(logfile), "")
190 logger.error("This is a test")
191 self.assertTrue(utils.ReadFile(logfile).endswith("This is a test\n"))
192
193 os.rename(logfile, logfile2)
194 assert not os.path.exists(logfile)
195
196 # Notify logger to reopen on the next message
197 reopen_fn()
198 assert not os.path.exists(logfile)
199
200 # Provoke actual reopen
201 logger.error("First message")
202
203 self.assertTrue(utils.ReadFile(logfile).endswith("First message\n"))
204 self.assertTrue(utils.ReadFile(logfile2).endswith("This is a test\n"))
205
206
207 class TestSetupToolLogging(unittest.TestCase):
208 def test(self):
209 error_name = logging.getLevelName(logging.ERROR)
210 warn_name = logging.getLevelName(logging.WARNING)
211 info_name = logging.getLevelName(logging.INFO)
212 debug_name = logging.getLevelName(logging.DEBUG)
213
214 for debug in [False, True]:
215 for verbose in [False, True]:
216 logger = logging.Logger("TestLogger")
217 buf = StringIO()
218
219 utils.SetupToolLogging(debug, verbose, _root_logger=logger, _stream=buf)
220
221 logger.error("level=error")
222 logger.warning("level=warning")
223 logger.info("level=info")
224 logger.debug("level=debug")
225
226 lines = buf.getvalue().splitlines()
227
228 self.assertTrue(compat.all(line.count(":") == 3 for line in lines))
229
230 messages = [line.split(":", 3)[-1].strip() for line in lines]
231
232 if debug:
233 self.assertEqual(messages, [
234 "%s level=error" % error_name,
235 "%s level=warning" % warn_name,
236 "%s level=info" % info_name,
237 "%s level=debug" % debug_name,
238 ])
239 elif verbose:
240 self.assertEqual(messages, [
241 "%s level=error" % error_name,
242 "%s level=warning" % warn_name,
243 "%s level=info" % info_name,
244 ])
245 else:
246 self.assertEqual(messages, [
247 "level=error",
248 "level=warning",
249 ])
250
251 def testThreadName(self):
252 thread_name = threading.currentThread().getName()
253
254 for enable_threadname in [False, True]:
255 logger = logging.Logger("TestLogger")
256 buf = StringIO()
257
258 utils.SetupToolLogging(True, True, threadname=enable_threadname,
259 _root_logger=logger, _stream=buf)
260
261 logger.debug("test134042376")
262
263 lines = buf.getvalue().splitlines()
264 self.assertEqual(len(lines), 1)
265
266 if enable_threadname:
267 self.assertTrue((" %s " % thread_name) in lines[0])
268 else:
269 self.assertTrue(thread_name not in lines[0])
270
271
272 if __name__ == "__main__":
273 testutils.GanetiTestProgram()