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.

Parts of this patch were written by Thomas Thrainer.

Signed-off-by: Thomas Thrainer <[email protected]>
Signed-off-by: Sebastian Gebhard <[email protected]>
---
 test/py/cmdlib/testsupport/cmdlib_testcase.py | 28 ++++++++++++++++++++++++++-
 test/py/cmdlib/testsupport/netutils_mock.py   |  2 +-
 test/py/cmdlib/testsupport/rpc_runner_mock.py | 27 ++++++++++++++++++++++++++
 test/py/cmdlib/testsupport/util.py            |  4 ++--
 4 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/test/py/cmdlib/testsupport/cmdlib_testcase.py 
b/test/py/cmdlib/testsupport/cmdlib_testcase.py
index 811d455..52fcc06 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, 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):
+    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 = patchRpc(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/netutils_mock.py 
b/test/py/cmdlib/testsupport/netutils_mock.py
index b8ee351..798051a 100644
--- a/test/py/cmdlib/testsupport/netutils_mock.py
+++ b/test/py/cmdlib/testsupport/netutils_mock.py
@@ -72,7 +72,7 @@ def _GetHostnameMock(cfg, mock_fct, name=None, family=None):
   if node is not None:
     return HostnameMock(node.name, node.primary_ip)
 
-  return HostnameMock(name, "203.0.113.1")
+  return HostnameMock(name, "192.0.2.253")
 
 
 # pylint: disable=W0613
diff --git a/test/py/cmdlib/testsupport/rpc_runner_mock.py 
b/test/py/cmdlib/testsupport/rpc_runner_mock.py
index 93845fb..8238566 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,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()
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