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.

Signed-off-by: Sebastian Gebhard <[email protected]>
---
 Makefile.am                                   |  1 +
 test/py/cmdlib/testsupport/cmdlib_testcase.py | 28 ++++++++++++++++++++++++++-
 test/py/cmdlib/testsupport/rpc_runner_mock.py | 26 +++++++++++++++++++++++++
 test/py/cmdlib/testsupport/util.py            |  4 ++--
 4 files changed, 56 insertions(+), 3 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index e99707e..55c833b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1280,6 +1280,7 @@ python_tests = \
        test/py/cmdlib/group_unittest.py \
        test/py/cmdlib/instance_unittest.py \
        test/py/cmdlib/instance_storage_unittest.py \
+       test/py/cmdlib/node_unittest.py \
        test/py/cmdlib/test_unittest.py \
        test/py/cfgupgrade_unittest.py \
        test/py/docs_unittest.py \
diff --git a/test/py/cmdlib/testsupport/cmdlib_testcase.py 
b/test/py/cmdlib/testsupport/cmdlib_testcase.py
index 811d455..6901d05 100644
--- a/test/py/cmdlib/testsupport/cmdlib_testcase.py
+++ b/test/py/cmdlib/testsupport/cmdlib_testcase.py
@@ -34,11 +34,12 @@ from cmdlib.testsupport.netutils_mock import patchNetutils, 
\
   SetupDefaultNetutilsMock
 from cmdlib.testsupport.processor_mock import ProcessorMock
 from cmdlib.testsupport.rpc_runner_mock import CreateRpcRunnerMock, \
-  RpcResultsBuilder
+  RpcResultsBuilder, patchRpcModule, 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):
+    pass
+
+  def RemoveNode(self, node):
+    self._test_case.cfg.RemoveNode(node.uuid)
+    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
 
     try:
       runtime.InitArchInfo()
@@ -128,6 +143,9 @@ class CmdlibTestCase(testutils.GanetiTestCase):
     if self._ssh_patcher is not None:
       self._ssh_patcher.stop()
       self._ssh_patcher = None
+    if self._rpc_patcher is not None:
+      self._rpc_patcher.stop()
+      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
 
+    try:
+      self._rpc_patcher = patchRpcModule(self._GetTestModule())
+      self.rpc_mod = self._rpc_patcher.start()
+      SetupDefaultRpcModuleMock(self.rpc_mod)
+    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.
 
diff --git a/test/py/cmdlib/testsupport/rpc_runner_mock.py 
b/test/py/cmdlib/testsupport/rpc_runner_mock.py
index 93845fb..484f178 100644
--- a/test/py/cmdlib/testsupport/rpc_runner_mock.py
+++ b/test/py/cmdlib/testsupport/rpc_runner_mock.py
@@ -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,27 @@ class RpcResultsBuilder(object):
     @rtype: dict
     """
     return dict((result.node, result) for result in self._results)
+
+
+def patchRpcModule(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()
\ No newline at end of file
diff --git a/test/py/cmdlib/testsupport/util.py 
b/test/py/cmdlib/testsupport/util.py
index e2354ce..61305c9 100644
--- a/test/py/cmdlib/testsupport/util.py
+++ b/test/py/cmdlib/testsupport/util.py
@@ -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)
-- 
1.8.1.2

Reply via email to