Statistics
| Branch: | Tag: | Revision:

root / test / py / cmdlib / testsupport / cmdlib_testcase.py @ e969a81f

History | View | Annotate | Download (6.2 kB)

1 3efa7659 Thomas Thrainer
#
2 3efa7659 Thomas Thrainer
#
3 3efa7659 Thomas Thrainer
4 3efa7659 Thomas Thrainer
# Copyright (C) 2013 Google Inc.
5 3efa7659 Thomas Thrainer
#
6 3efa7659 Thomas Thrainer
# This program is free software; you can redistribute it and/or modify
7 3efa7659 Thomas Thrainer
# it under the terms of the GNU General Public License as published by
8 3efa7659 Thomas Thrainer
# the Free Software Foundation; either version 2 of the License, or
9 3efa7659 Thomas Thrainer
# (at your option) any later version.
10 3efa7659 Thomas Thrainer
#
11 3efa7659 Thomas Thrainer
# This program is distributed in the hope that it will be useful, but
12 3efa7659 Thomas Thrainer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 3efa7659 Thomas Thrainer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 3efa7659 Thomas Thrainer
# General Public License for more details.
15 3efa7659 Thomas Thrainer
#
16 3efa7659 Thomas Thrainer
# You should have received a copy of the GNU General Public License
17 3efa7659 Thomas Thrainer
# along with this program; if not, write to the Free Software
18 3efa7659 Thomas Thrainer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 3efa7659 Thomas Thrainer
# 02110-1301, USA.
20 3efa7659 Thomas Thrainer
21 bd39b6bb Thomas Thrainer
22 bd39b6bb Thomas Thrainer
"""Main module of the cmdlib test framework"""
23 bd39b6bb Thomas Thrainer
24 bd39b6bb Thomas Thrainer
25 e969a81f Thomas Thrainer
import inspect
26 e969a81f Thomas Thrainer
import re
27 e969a81f Thomas Thrainer
import traceback
28 e969a81f Thomas Thrainer
29 bd39b6bb Thomas Thrainer
from cmdlib.testsupport.config_mock import ConfigMock
30 e969a81f Thomas Thrainer
from cmdlib.testsupport.iallocator_mock import patchIAllocator
31 bd39b6bb Thomas Thrainer
from cmdlib.testsupport.lock_manager_mock import LockManagerMock
32 bd39b6bb Thomas Thrainer
from cmdlib.testsupport.processor_mock import ProcessorMock
33 bd39b6bb Thomas Thrainer
from cmdlib.testsupport.rpc_runner_mock import CreateRpcRunnerMock
34 3efa7659 Thomas Thrainer
35 e969a81f Thomas Thrainer
from ganeti import errors
36 e969a81f Thomas Thrainer
from ganeti import opcodes
37 e969a81f Thomas Thrainer
38 3efa7659 Thomas Thrainer
import testutils
39 3efa7659 Thomas Thrainer
40 3efa7659 Thomas Thrainer
41 3efa7659 Thomas Thrainer
class GanetiContextMock(object):
42 3efa7659 Thomas Thrainer
  def __init__(self, cfg, glm, rpc):
43 3efa7659 Thomas Thrainer
    self.cfg = cfg
44 3efa7659 Thomas Thrainer
    self.glm = glm
45 3efa7659 Thomas Thrainer
    self.rpc = rpc
46 3efa7659 Thomas Thrainer
47 3efa7659 Thomas Thrainer
48 bd39b6bb Thomas Thrainer
# pylint: disable=R0904
49 3efa7659 Thomas Thrainer
class CmdlibTestCase(testutils.GanetiTestCase):
50 3efa7659 Thomas Thrainer
  """Base class for cmdlib tests.
51 3efa7659 Thomas Thrainer

52 3efa7659 Thomas Thrainer
  This class sets up a mocked environment for the execution of
53 3efa7659 Thomas Thrainer
  L{ganeti.cmdlib.base.LogicalUnit} subclasses.
54 3efa7659 Thomas Thrainer

55 3efa7659 Thomas Thrainer
  The environment can be customized via the following fields:
56 3efa7659 Thomas Thrainer

57 3efa7659 Thomas Thrainer
    * C{cfg}: @see L{ConfigMock}
58 3efa7659 Thomas Thrainer
    * C{glm}: @see L{LockManagerMock}
59 3efa7659 Thomas Thrainer
    * C{rpc}: @see L{CreateRpcRunnerMock}
60 e969a81f Thomas Thrainer
    * C{iallocator_cls}: @see L{patchIAllocator}
61 3efa7659 Thomas Thrainer
    * C{mcpu}: @see L{ProcessorMock}
62 3efa7659 Thomas Thrainer

63 3efa7659 Thomas Thrainer
  """
64 e969a81f Thomas Thrainer
65 e969a81f Thomas Thrainer
  REMOVE = object()
66 e969a81f Thomas Thrainer
67 3efa7659 Thomas Thrainer
  def setUp(self):
68 3efa7659 Thomas Thrainer
    super(CmdlibTestCase, self).setUp()
69 e969a81f Thomas Thrainer
    self._iallocator_patcher = None
70 e969a81f Thomas Thrainer
71 e969a81f Thomas Thrainer
    self.ResetMocks()
72 e969a81f Thomas Thrainer
73 e969a81f Thomas Thrainer
  def _StopPatchers(self):
74 e969a81f Thomas Thrainer
    if self._iallocator_patcher is not None:
75 e969a81f Thomas Thrainer
      self._iallocator_patcher.stop()
76 e969a81f Thomas Thrainer
      self._iallocator_patcher = None
77 e969a81f Thomas Thrainer
78 e969a81f Thomas Thrainer
  def tearDown(self):
79 e969a81f Thomas Thrainer
    super(CmdlibTestCase, self).tearDown()
80 3efa7659 Thomas Thrainer
81 e969a81f Thomas Thrainer
    self._StopPatchers()
82 e969a81f Thomas Thrainer
83 e969a81f Thomas Thrainer
  def _GetTestModule(self):
84 e969a81f Thomas Thrainer
    module = inspect.getsourcefile(self.__class__).split("/")[-1]
85 e969a81f Thomas Thrainer
    suffix = "_unittest.py"
86 e969a81f Thomas Thrainer
    assert module.endswith(suffix), "Naming convention for cmdlib test" \
87 e969a81f Thomas Thrainer
                                    " modules is: <module>%s (found '%s')"\
88 e969a81f Thomas Thrainer
                                    % (suffix, module)
89 e969a81f Thomas Thrainer
    return module[:-len(suffix)]
90 e969a81f Thomas Thrainer
91 e969a81f Thomas Thrainer
  def ResetMocks(self):
92 e969a81f Thomas Thrainer
    """Resets all mocks back to their initial state.
93 e969a81f Thomas Thrainer

94 e969a81f Thomas Thrainer
    This is useful if you want to execute more than one opcode in a single
95 e969a81f Thomas Thrainer
    test.
96 e969a81f Thomas Thrainer

97 e969a81f Thomas Thrainer
    """
98 3efa7659 Thomas Thrainer
    self.cfg = ConfigMock()
99 3efa7659 Thomas Thrainer
    self.glm = LockManagerMock()
100 3efa7659 Thomas Thrainer
    self.rpc = CreateRpcRunnerMock()
101 e969a81f Thomas Thrainer
102 e969a81f Thomas Thrainer
    self._StopPatchers()
103 e969a81f Thomas Thrainer
    try:
104 e969a81f Thomas Thrainer
      self._iallocator_patcher = patchIAllocator(self._GetTestModule())
105 e969a81f Thomas Thrainer
      self.iallocator_cls = self._iallocator_patcher.start()
106 e969a81f Thomas Thrainer
    except ImportError:
107 e969a81f Thomas Thrainer
      # this test module does not use iallocator, no patching performed
108 e969a81f Thomas Thrainer
      self._iallocator_patcher = None
109 e969a81f Thomas Thrainer
110 3efa7659 Thomas Thrainer
    ctx = GanetiContextMock(self.cfg, self.glm, self.rpc)
111 3efa7659 Thomas Thrainer
    self.mcpu = ProcessorMock(ctx)
112 3efa7659 Thomas Thrainer
113 3efa7659 Thomas Thrainer
  def ExecOpCode(self, opcode):
114 3efa7659 Thomas Thrainer
    """Executes the given opcode.
115 3efa7659 Thomas Thrainer

116 3efa7659 Thomas Thrainer
    @param opcode: the opcode to execute
117 3efa7659 Thomas Thrainer
    @return: the result of the LU's C{Exec} method
118 e969a81f Thomas Thrainer

119 3efa7659 Thomas Thrainer
    """
120 3efa7659 Thomas Thrainer
    self.glm.AddLocksFromConfig(self.cfg)
121 3efa7659 Thomas Thrainer
122 3efa7659 Thomas Thrainer
    return self.mcpu.ExecOpCodeAndRecordOutput(opcode)
123 3efa7659 Thomas Thrainer
124 e969a81f Thomas Thrainer
  def ExecOpCodeExpectException(self, opcode,
125 e969a81f Thomas Thrainer
                                expected_exception,
126 e969a81f Thomas Thrainer
                                expected_regex=None):
127 e969a81f Thomas Thrainer
    """Executes the given opcode and expects an exception.
128 e969a81f Thomas Thrainer

129 e969a81f Thomas Thrainer
    @param opcode: @see L{ExecOpCode}
130 e969a81f Thomas Thrainer
    @type expected_exception: class
131 e969a81f Thomas Thrainer
    @param expected_exception: the exception which must be raised
132 e969a81f Thomas Thrainer
    @type expected_regex: string
133 e969a81f Thomas Thrainer
    @param expected_regex: if not C{None}, a regular expression which must be
134 e969a81f Thomas Thrainer
          present in the string representation of the exception
135 e969a81f Thomas Thrainer

136 e969a81f Thomas Thrainer
    """
137 e969a81f Thomas Thrainer
    try:
138 e969a81f Thomas Thrainer
      self.ExecOpCode(opcode)
139 e969a81f Thomas Thrainer
    except expected_exception, e:
140 e969a81f Thomas Thrainer
      if expected_regex is not None:
141 e969a81f Thomas Thrainer
        assert re.search(expected_regex, str(e)) is not None, \
142 e969a81f Thomas Thrainer
                "Caught exception '%s' did not match '%s'" % \
143 e969a81f Thomas Thrainer
                  (str(e), expected_regex)
144 e969a81f Thomas Thrainer
    except Exception, e:
145 e969a81f Thomas Thrainer
      tb = traceback.format_exc()
146 e969a81f Thomas Thrainer
      raise AssertionError("%s\n(See original exception above)\n"
147 e969a81f Thomas Thrainer
                           "Expected exception '%s' was not raised,"
148 e969a81f Thomas Thrainer
                           " got '%s' of class '%s' instead." %
149 e969a81f Thomas Thrainer
                           (tb, expected_exception, e, e.__class__))
150 e969a81f Thomas Thrainer
    else:
151 e969a81f Thomas Thrainer
      raise AssertionError("Expected exception '%s' was not raised" %
152 e969a81f Thomas Thrainer
                           expected_exception)
153 e969a81f Thomas Thrainer
154 e969a81f Thomas Thrainer
  def ExecOpCodeExpectOpPrereqError(self, opcode, expected_regex=None):
155 e969a81f Thomas Thrainer
    """Executes the given opcode and expects a L{errors.OpPrereqError}
156 e969a81f Thomas Thrainer

157 e969a81f Thomas Thrainer
    @see L{ExecOpCodeExpectException}
158 e969a81f Thomas Thrainer

159 e969a81f Thomas Thrainer
    """
160 e969a81f Thomas Thrainer
    self.ExecOpCodeExpectException(opcode, errors.OpPrereqError, expected_regex)
161 e969a81f Thomas Thrainer
162 e969a81f Thomas Thrainer
  def ExecOpCodeExpectOpExecError(self, opcode, expected_regex=None):
163 e969a81f Thomas Thrainer
    """Executes the given opcode and expects a L{errors.OpExecError}
164 e969a81f Thomas Thrainer

165 e969a81f Thomas Thrainer
    @see L{ExecOpCodeExpectException}
166 e969a81f Thomas Thrainer

167 e969a81f Thomas Thrainer
    """
168 e969a81f Thomas Thrainer
    self.ExecOpCodeExpectException(opcode, errors.OpExecError, expected_regex)
169 e969a81f Thomas Thrainer
170 3efa7659 Thomas Thrainer
  def assertLogContainsMessage(self, expected_msg):
171 3efa7659 Thomas Thrainer
    """Shortcut for L{ProcessorMock.assertLogContainsMessage}
172 3efa7659 Thomas Thrainer

173 3efa7659 Thomas Thrainer
    """
174 3efa7659 Thomas Thrainer
    self.mcpu.assertLogContainsMessage(expected_msg)
175 3efa7659 Thomas Thrainer
176 3efa7659 Thomas Thrainer
  def assertLogContainsRegex(self, expected_regex):
177 3efa7659 Thomas Thrainer
    """Shortcut for L{ProcessorMock.assertLogContainsRegex}
178 3efa7659 Thomas Thrainer

179 3efa7659 Thomas Thrainer
    """
180 3efa7659 Thomas Thrainer
    self.mcpu.assertLogContainsRegex(expected_regex)
181 bd39b6bb Thomas Thrainer
182 e969a81f Thomas Thrainer
  def CopyOpCode(self, opcode, **kwargs):
183 e969a81f Thomas Thrainer
    """Creates a copy of the given opcode and applies modifications to it
184 e969a81f Thomas Thrainer

185 e969a81f Thomas Thrainer
    @type opcode: opcode.OpCode
186 e969a81f Thomas Thrainer
    @param opcode: the opcode to copy
187 e969a81f Thomas Thrainer
    @type kwargs: dict
188 e969a81f Thomas Thrainer
    @param kwargs: dictionary of fields to overwrite in the copy. The special
189 e969a81f Thomas Thrainer
          value L{REMOVE} can be used to remove fields from the copy.
190 e969a81f Thomas Thrainer
    @return: a copy of the given opcode
191 e969a81f Thomas Thrainer

192 e969a81f Thomas Thrainer
    """
193 e969a81f Thomas Thrainer
    state = opcode.__getstate__()
194 e969a81f Thomas Thrainer
195 e969a81f Thomas Thrainer
    for key, value in kwargs.items():
196 e969a81f Thomas Thrainer
      if value == self.REMOVE:
197 e969a81f Thomas Thrainer
        del state[key]
198 e969a81f Thomas Thrainer
      else:
199 e969a81f Thomas Thrainer
        state[key] = value
200 e969a81f Thomas Thrainer
201 e969a81f Thomas Thrainer
    return opcodes.OpCode.LoadOpCode(state)