5884dcfd04062b99795df044b2a28c7be1777d8c
[ganeti-local] / test / py / cmdlib / testsupport / processor_mock.py
1 #
2 #
3
4 # Copyright (C) 2013 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 """Support for mocking the opcode processor"""
23
24
25 import re
26
27 from ganeti import constants
28 from ganeti import mcpu
29
30
31 class LogRecordingCallback(mcpu.OpExecCbBase):
32   """Helper class for log output recording.
33
34   """
35   def __init__(self, processor):
36     self.processor = processor
37
38   def Feedback(self, *args):
39     assert len(args) < 3
40
41     if len(args) == 1:
42       log_type = constants.ELOG_MESSAGE
43       log_msg = args[0]
44     else:
45       (log_type, log_msg) = args
46
47     self.processor.log_entries.append((log_type, log_msg))
48
49   def SubmitManyJobs(self, jobs):
50     results = []
51     for idx, _ in enumerate(jobs):
52       results.append((True, idx))
53     return results
54
55
56 class ProcessorMock(mcpu.Processor):
57   """Mocked opcode processor for tests.
58
59   This class actually performs much more than a mock, as it drives the
60   execution of LU's. But it also provides access to the log output of the LU
61   the result of the execution.
62
63   See L{ExecOpCodeAndRecordOutput} for the main method of this class.
64
65   """
66
67   def __init__(self, context):
68     super(ProcessorMock, self).__init__(context, 1, True)
69     self.log_entries = []
70     self._lu_test_func = None
71
72   def ExecOpCodeAndRecordOutput(self, op):
73     """Executes the given opcode and records the output for further inspection.
74
75     @param op: the opcode to execute.
76     @return: see L{mcpu.Processor.ExecOpCode}
77
78     """
79     return self.ExecOpCode(op, LogRecordingCallback(self))
80
81   def _ExecLU(self, lu):
82     if not self._lu_test_func:
83       return super(ProcessorMock, self)._ExecLU(lu)
84     else:
85       # required by a lot LU's, and usually passed in Exec
86       lu._feedback_fn = self.Log
87       return self._lu_test_func(lu)
88
89   def _CheckLUResult(self, op, result):
90     if not self._lu_test_func:
91       return super(ProcessorMock, self)._CheckLUResult(op, result)
92     else:
93       pass
94
95   def RunWithLockedLU(self, op, func):
96     """Takes the given opcode, creates a LU and runs func with it.
97
98     @param op: the opcode to get the LU for.
99     @param func: the function to run with the created and locked LU.
100     @return: the result of func.
101
102     """
103     self._lu_test_func = func
104     try:
105       return self.ExecOpCodeAndRecordOutput(op)
106     finally:
107       self._lu_test_func = None
108
109   def GetLogEntries(self):
110     """Return the list of recorded log entries.
111
112     @rtype: list of (string, string) tuples
113     @return: the list of recorded log entries
114
115     """
116     return self.log_entries
117
118   def GetLogMessages(self):
119     """Return the list of recorded log messages.
120
121     @rtype: list of string
122     @return: the list of recorded log messages
123
124     """
125     return [msg for _, msg in self.log_entries]
126
127   def GetLogEntriesString(self):
128     """Return a string with all log entries separated by a newline.
129
130     """
131     return "\n".join("%s: %s" % (log_type, msg)
132                      for log_type, msg in self.GetLogEntries())
133
134   def GetLogMessagesString(self):
135     """Return a string with all log messages separated by a newline.
136
137     """
138     return "\n".join("%s" % msg for _, msg in self.GetLogEntries())
139
140   def assertLogContainsEntry(self, expected_type, expected_msg):
141     """Asserts that the log contains the exact given entry.
142
143     @type expected_type: string
144     @param expected_type: the expected type
145     @type expected_msg: string
146     @param expected_msg: the expected message
147
148     """
149     for log_type, msg in self.log_entries:
150       if log_type == expected_type and msg == expected_msg:
151         return
152
153     raise AssertionError(
154       "Could not find '%s' (type '%s') in LU log messages. Log is:\n%s" %
155       (expected_msg, expected_type, self.GetLogEntriesString()))
156
157   def assertLogContainsMessage(self, expected_msg):
158     """Asserts that the log contains the exact given message.
159
160     @type expected_msg: string
161     @param expected_msg: the expected message
162
163     """
164     for msg in self.GetLogMessages():
165       if msg == expected_msg:
166         return
167
168     raise AssertionError(
169       "Could not find '%s' in LU log messages. Log is:\n%s" %
170       (expected_msg, self.GetLogMessagesString()))
171
172   def assertLogContainsRegex(self, expected_regex):
173     """Asserts that the log contains a message which matches the regex.
174
175     @type expected_regex: string
176     @param expected_regex: regular expression to match messages with.
177
178     """
179     for msg in self.GetLogMessages():
180       if re.search(expected_regex, msg) is not None:
181         return
182
183     raise AssertionError(
184       "Could not find '%s' in LU log messages. Log is:\n%s" %
185       (expected_regex, self.GetLogMessagesString())
186     )
187
188   def assertLogContainsInLine(self, expected):
189     """Asserts that the log contains a message which contains a string.
190
191     @type expected: string
192     @param expected: string to search in messages.
193
194     """
195     self.assertLogContainsRegex(re.escape(expected))
196
197   def assertLogDoesNotContainRegex(self, expected_regex):
198     """Asserts that the log does not contain a message which matches the regex.
199
200     @type expected_regex: string
201     @param expected_regex: regular expression to match messages with.
202
203     """
204     for msg in self.GetLogMessages():
205       if re.search(expected_regex, msg) is not None:
206         raise AssertionError(
207           "Found '%s' in LU log messages. Log is:\n%s" %
208           (expected_regex, self.GetLogMessagesString())
209         )
210
211   def assertLogIsEmpty(self):
212     """Asserts that the log does not contain any message.
213
214     """
215     if len(self.GetLogMessages()) > 0:
216       raise AssertionError("Log is not empty. Log is:\n%s" %
217                            self.GetLogMessagesString())
218
219   def ClearLogMessages(self):
220     """Clears all recorded log messages.
221
222     This is useful if you use L{GetLockedLU} and want to test multiple calls
223     on it.
224
225     """
226     self.log_entries = []