Commit d45574de authored by Sebastian Gebhard's avatar Sebastian Gebhard Committed by Klaus Aehlig
Browse files

Mock RPC for unit tests

This patch enables patching the rpc module to create a mocked version
which can be used to mock a rpc.DnsOnlyRunner(). This is needed for
unit testing LUNodeAdd, as it need to run RPCs against nodes not yet
present in the configuration.

Also, the default return IP of _GetHostnameMock needs to be changed, since
x.x.x.1 is the default IP of the first mocked node and will cause problems.

Parts of this patch were written by Thomas Thrainer.
Signed-off-by: default avatarThomas Thrainer <>
Signed-off-by: default avatarSebastian Gebhard <>
Signed-off-by: default avatarKlaus Aehlig <>
Reviewed-by: default avatarKlaus Aehlig <>
parent 34be621a
......@@ -34,11 +34,12 @@ from cmdlib.testsupport.netutils_mock import patchNetutils, \
from cmdlib.testsupport.processor_mock import ProcessorMock
from cmdlib.testsupport.rpc_runner_mock import CreateRpcRunnerMock, \
RpcResultsBuilder, patchRpc, SetupDefaultRpcModuleMock
from cmdlib.testsupport.ssh_mock import patchSsh
from ganeti.cmdlib.base import LogicalUnit
from ganeti import errors
from ganeti import locking
from ganeti import objects
from ganeti import opcodes
from ganeti import runtime
......@@ -57,6 +58,19 @@ class GanetiContextMock(object):
def __init__(self, test_case):
self._test_case = test_case
def AddNode(self, node, ec_id):
self._test_case.cfg.AddNode(node, ec_id)
self._test_case.glm.add(locking.LEVEL_NODE, node.uuid)
self._test_case.glm.add(locking.LEVEL_NODE_RES, node.uuid)
def ReaddNode(self, node):
def RemoveNode(self, node):
self._test_case.glm.remove(locking.LEVEL_NODE, node.uuid)
self._test_case.glm.remove(locking.LEVEL_NODE_RES, node.uuid)
class MockLU(LogicalUnit):
def BuildHooksNodes(self):
......@@ -109,6 +123,7 @@ class CmdlibTestCase(testutils.GanetiTestCase):
self._iallocator_patcher = None
self._netutils_patcher = None
self._ssh_patcher = None
self._rpc_patcher = None
......@@ -128,6 +143,9 @@ class CmdlibTestCase(testutils.GanetiTestCase):
if self._ssh_patcher is not None:
self._ssh_patcher = None
if self._rpc_patcher is not None:
self._rpc_patcher = None
def tearDown(self):
super(CmdlibTestCase, self).tearDown()
......@@ -178,6 +196,14 @@ class CmdlibTestCase(testutils.GanetiTestCase):
# this test module does not use ssh, no patching performed
self._ssh_patcher = None
self._rpc_patcher = patchRpc(self._GetTestModule())
self.rpc_mod = self._rpc_patcher.start()
except (ImportError, AttributeError):
# this test module does not use rpc, no patching performed
self._rpc_patcher = None
def GetMockLU(self):
"""Creates a mock L{LogialUnit} with access to the mocked config etc.
......@@ -72,7 +72,7 @@ def _GetHostnameMock(cfg, mock_fct, name=None, family=None):
if node is not None:
return HostnameMock(, node.primary_ip)
return HostnameMock(name, "")
return HostnameMock(name, "")
# pylint: disable=W0613
......@@ -27,6 +27,8 @@ import mock
from ganeti import objects
from ganeti import rpc
from cmdlib.testsupport.util import patchModule
def CreateRpcRunnerMock():
"""Creates a new L{mock.MagicMock} tailored for L{rpc.RpcRunner}
......@@ -179,3 +181,28 @@ class RpcResultsBuilder(object):
@rtype: dict
return dict((result.node, result) for result in self._results)
# pylint: disable=C0103
def patchRpc(module_under_test):
"""Patches the L{ganeti.rpc} module for tests.
This function is meant to be used as a decorator for test methods.
@type module_under_test: string
@param module_under_test: the module within cmdlib which is tested. The
"ganeti.cmdlib" prefix is optional.
return patchModule(module_under_test, "rpc", wraps=rpc)
def SetupDefaultRpcModuleMock(rpc_mod):
"""Configures the given rpc_mod.
All relevant functions in rpc_mod are stubbed in a sensible way.
@param rpc_mod: the mock module to configure
rpc_mod.DnsOnlyRunner.return_value = CreateRpcRunnerMock()
......@@ -26,7 +26,7 @@ import mock
# pylint: disable=C0103
def patchModule(module_under_test, mock_module):
def patchModule(module_under_test, mock_module, **kwargs):
"""Computes the module prefix required to mock parts of the Ganeti code.
@type module_under_test: string
......@@ -38,4 +38,4 @@ def patchModule(module_under_test, mock_module):
if not module_under_test.startswith("ganeti.cmdlib"):
module_under_test = "ganeti.cmdlib." + module_under_test
return mock.patch("%s.%s" % (module_under_test, mock_module))
return mock.patch("%s.%s" % (module_under_test, mock_module), **kwargs)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment