Exemplo n.º 1
0
    def ResetMocks(self):
        """Resets all mocks back to their initial state.

    This is useful if you want to execute more than one opcode in a single
    test.

    """
        self.cfg = ConfigMock()
        self.rpc = CreateRpcRunnerMock()
        self.ctx = GanetiContextMock(self)
        self.wconfd = WConfdMock()
        self.mcpu = ProcessorMock(self.ctx, self.wconfd)

        self._StopPatchers()
        try:
            self._iallocator_patcher = patchIAllocator(self._GetTestModule())
            self.iallocator_cls = self._iallocator_patcher.start()
        except (ImportError, AttributeError):
            # this test module does not use iallocator, no patching performed
            self._iallocator_patcher = None

        try:
            self._netutils_patcher = patchNetutils(self._GetTestModule())
            self.netutils_mod = self._netutils_patcher.start()
            SetupDefaultNetutilsMock(self.netutils_mod, self.cfg)
        except (ImportError, AttributeError):
            # this test module does not use netutils, no patching performed
            self._netutils_patcher = None

        try:
            self._ssh_patcher = patchSsh(self._GetTestModule())
            self.ssh_mod = self._ssh_patcher.start()
        except (ImportError, AttributeError):
            # 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
Exemplo n.º 2
0
class CmdlibTestCase(testutils.GanetiTestCase):
    """Base class for cmdlib tests.

  This class sets up a mocked environment for the execution of
  L{ganeti.cmdlib.base.LogicalUnit} subclasses.

  The environment can be customized via the following fields:

    * C{cfg}: @see L{ConfigMock}
    * C{rpc}: @see L{CreateRpcRunnerMock}
    * C{iallocator_cls}: @see L{patchIAllocator}
    * C{mcpu}: @see L{ProcessorMock}
    * C{netutils_mod}: @see L{patchNetutils}
    * C{ssh_mod}: @see L{patchSsh}

  """

    REMOVE = object()

    cluster = property(fget=lambda self: self.cfg.GetClusterInfo(),
                       doc="Cluster configuration object")
    master = property(fget=lambda self: self.cfg.GetMasterNodeInfo(),
                      doc="Master node")
    master_uuid = property(fget=lambda self: self.cfg.GetMasterNode(),
                           doc="Master node UUID")
    # pylint: disable=W0212
    group = property(fget=lambda self: self._GetDefaultGroup(),
                     doc="Default node group")

    os = property(fget=lambda self: self.cfg.GetDefaultOs(), doc="Default OS")
    os_name_variant = property(fget=lambda self: self.os.name + objects.OS.
                               VARIANT_DELIM + self.os.supported_variants[0],
                               doc="OS name and variant string")

    def setUp(self):
        super(CmdlibTestCase, self).setUp()
        self._iallocator_patcher = None
        self._netutils_patcher = None
        self._ssh_patcher = None
        self._rpc_patcher = None

        try:
            runtime.InitArchInfo()
        except errors.ProgrammerError:
            # during tests, the arch info can be initialized multiple times
            pass

        self.ResetMocks()
        self._cleanups = []

    def _StopPatchers(self):
        if self._iallocator_patcher is not None:
            self._iallocator_patcher.stop()
            self._iallocator_patcher = None
        if self._netutils_patcher is not None:
            self._netutils_patcher.stop()
            self._netutils_patcher = None
        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()

        self._StopPatchers()

        while self._cleanups:
            f, args, kwargs = self._cleanups.pop(-1)
            try:
                f(*args, **kwargs)
            except BaseException as e:
                sys.stderr.write('Error in cleanup: %s\n' % e)

    def _GetTestModule(self):
        module = inspect.getsourcefile(self.__class__).split("/")[-1]
        suffix = "_unittest.py"
        assert module.endswith(suffix), "Naming convention for cmdlib test" \
                                        " modules is: <module>%s (found '%s')"\
                                        % (suffix, module)
        return module[:-len(suffix)]

    def ResetMocks(self):
        """Resets all mocks back to their initial state.

    This is useful if you want to execute more than one opcode in a single
    test.

    """
        self.cfg = ConfigMock()
        self.rpc = CreateRpcRunnerMock()
        self.ctx = GanetiContextMock(self)
        self.wconfd = WConfdMock()
        self.mcpu = ProcessorMock(self.ctx, self.wconfd)

        self._StopPatchers()
        try:
            self._iallocator_patcher = patchIAllocator(self._GetTestModule())
            self.iallocator_cls = self._iallocator_patcher.start()
        except (ImportError, AttributeError):
            # this test module does not use iallocator, no patching performed
            self._iallocator_patcher = None

        try:
            self._netutils_patcher = patchNetutils(self._GetTestModule())
            self.netutils_mod = self._netutils_patcher.start()
            SetupDefaultNetutilsMock(self.netutils_mod, self.cfg)
        except (ImportError, AttributeError):
            # this test module does not use netutils, no patching performed
            self._netutils_patcher = None

        try:
            self._ssh_patcher = patchSsh(self._GetTestModule())
            self.ssh_mod = self._ssh_patcher.start()
        except (ImportError, AttributeError):
            # 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.

    @rtype: L{LogialUnit}
    @return: A mock LU

    """
        return MockLU(self.mcpu, mock.MagicMock(), self.cfg, self.rpc,
                      (1234, "/tmp/mock/livelock"), self.wconfd)

    def RpcResultsBuilder(self, use_node_names=False):
        """Creates a pre-configured L{RpcResultBuilder}

    @type use_node_names: bool
    @param use_node_names: @see L{RpcResultBuilder}
    @rtype: L{RpcResultBuilder}
    @return: a pre-configured builder for RPC results

    """
        return RpcResultsBuilder(cfg=self.cfg, use_node_names=use_node_names)

    def ExecOpCode(self, opcode):
        """Executes the given opcode.

    @param opcode: the opcode to execute
    @return: the result of the LU's C{Exec} method

    """
        return self.mcpu.ExecOpCodeAndRecordOutput(opcode)

    def ExecOpCodeExpectException(self,
                                  opcode,
                                  expected_exception,
                                  expected_regex=None):
        """Executes the given opcode and expects an exception.

    @param opcode: @see L{ExecOpCode}
    @type expected_exception: class
    @param expected_exception: the exception which must be raised
    @type expected_regex: string
    @param expected_regex: if not C{None}, a regular expression which must be
          present in the string representation of the exception

    """
        try:
            self.ExecOpCode(opcode)
        except expected_exception as e:
            if expected_regex is not None:
                assert re.search(expected_regex, str(e)) is not None, \
                        "Caught exception '%s' did not match '%s'" % \
                          (str(e), expected_regex)
        except Exception as e:
            tb = traceback.format_exc()
            raise AssertionError("%s\n(See original exception above)\n"
                                 "Expected exception '%s' was not raised,"
                                 " got '%s' of class '%s' instead." %
                                 (tb, expected_exception, e, e.__class__))
        else:
            raise AssertionError("Expected exception '%s' was not raised" %
                                 expected_exception)

    def ExecOpCodeExpectOpPrereqError(self, opcode, expected_regex=None):
        """Executes the given opcode and expects a L{errors.OpPrereqError}

    @see L{ExecOpCodeExpectException}

    """
        self.ExecOpCodeExpectException(opcode, errors.OpPrereqError,
                                       expected_regex)

    def ExecOpCodeExpectOpExecError(self, opcode, expected_regex=None):
        """Executes the given opcode and expects a L{errors.OpExecError}

    @see L{ExecOpCodeExpectException}

    """
        self.ExecOpCodeExpectException(opcode, errors.OpExecError,
                                       expected_regex)

    def RunWithLockedLU(self, opcode, test_func):
        """Takes the given opcode, creates a LU and runs func on it.

    The passed LU did already perform locking, but no methods which actually
    require locking are executed on the LU.

    @param opcode: the opcode to get the LU for.
    @param test_func: the function to execute with the LU as parameter.
    @return: the result of test_func

    """
        return self.mcpu.RunWithLockedLU(opcode, test_func)

    def assertLogContainsMessage(self, expected_msg):
        """Shortcut for L{ProcessorMock.assertLogContainsMessage}

    """
        self.mcpu.assertLogContainsMessage(expected_msg)

    def assertLogContainsRegex(self, expected_regex):
        """Shortcut for L{ProcessorMock.assertLogContainsRegex}

    """
        self.mcpu.assertLogContainsRegex(expected_regex)

    def assertHooksCall(self,
                        nodes,
                        hook_path,
                        phase,
                        environment=None,
                        count=None,
                        index=0):
        """Asserts a call to C{rpc.call_hooks_runner}

    @type nodes: list of string
    @param nodes: node UUID's or names hooks run on
    @type hook_path: string
    @param hook_path: path (or name) of the hook run
    @type phase: string
    @param phase: phase in which the hook runs in
    @type environment: dict
    @param environment: the environment passed to the hooks. C{None} to skip
            asserting it
    @type count: int
    @param count: the number of hook invocations. C{None} to skip asserting it
    @type index: int
    @param index: the index of the hook invocation to assert

    """
        if count is not None:
            self.assertEqual(count, self.rpc.call_hooks_runner.call_count)

        args = self.rpc.call_hooks_runner.call_args[index]

        self.assertEqual(set(nodes), set(args[0]))
        self.assertEqual(hook_path, args[1])
        self.assertEqual(phase, args[2])
        if environment is not None:
            self.assertEqual(environment, args[3])

    def assertSingleHooksCall(self, nodes, hook_path, phase, environment=None):
        """Asserts a single call to C{rpc.call_hooks_runner}

    @see L{assertHooksCall} for parameter description.

    """
        self.assertHooksCall(nodes,
                             hook_path,
                             phase,
                             environment=environment,
                             count=1)

    def CopyOpCode(self, opcode, **kwargs):
        """Creates a copy of the given opcode and applies modifications to it

    @type opcode: opcode.OpCode
    @param opcode: the opcode to copy
    @type kwargs: dict
    @param kwargs: dictionary of fields to overwrite in the copy. The special
          value L{REMOVE} can be used to remove fields from the copy.
    @return: a copy of the given opcode

    """
        state = opcode.__getstate__()

        for key, value in kwargs.items():
            if value == self.REMOVE and key in state:
                del state[key]
            else:
                state[key] = value

        return opcodes.OpCode.LoadOpCode(state)

    def _GetDefaultGroup(self):
        for group in self.cfg.GetAllNodeGroupsInfo().values():
            if group.name == "default":
                return group
        assert False

    def _MatchMasterParams(self):
        return ConfigObjectMatcher(self.cfg.GetMasterNetworkParameters())

    def MockOut(self, *args, **kwargs):
        """Immediately start mock.patch.object."""
        patcher = mock.patch.object(*args, **kwargs)
        mocked = patcher.start()
        self.AddCleanup(patcher.stop)
        return mocked

    # Simplified backport of 2.7 feature
    def AddCleanup(self, func, *args, **kwargs):
        self._cleanups.append((func, args, kwargs))

    def assertIn(self, first, second, msg=None):
        if first not in second:
            if msg is None:
                msg = "%r not found in %r" % (first, second)
            self.fail(msg)
Exemplo n.º 3
0
class CmdlibTestCase(testutils.GanetiTestCase):
  """Base class for cmdlib tests.

  This class sets up a mocked environment for the execution of
  L{ganeti.cmdlib.base.LogicalUnit} subclasses.

  The environment can be customized via the following fields:

    * C{cfg}: @see L{ConfigMock}
    * C{rpc}: @see L{CreateRpcRunnerMock}
    * C{iallocator_cls}: @see L{patchIAllocator}
    * C{mcpu}: @see L{ProcessorMock}
    * C{netutils_mod}: @see L{patchNetutils}
    * C{ssh_mod}: @see L{patchSsh}

  """

  REMOVE = object()

  cluster = property(fget=lambda self: self.cfg.GetClusterInfo(),
                     doc="Cluster configuration object")
  master = property(fget=lambda self: self.cfg.GetMasterNodeInfo(),
                    doc="Master node")
  master_uuid = property(fget=lambda self: self.cfg.GetMasterNode(),
                         doc="Master node UUID")
  # pylint: disable=W0212
  group = property(fget=lambda self: self._GetDefaultGroup(),
                   doc="Default node group")

  os = property(fget=lambda self: self.cfg.GetDefaultOs(),
                doc="Default OS")
  os_name_variant = property(
    fget=lambda self: self.os.name + objects.OS.VARIANT_DELIM +
      self.os.supported_variants[0],
    doc="OS name and variant string")

  def setUp(self):
    super(CmdlibTestCase, self).setUp()
    self._iallocator_patcher = None
    self._netutils_patcher = None
    self._ssh_patcher = None
    self._rpc_patcher = None

    try:
      runtime.InitArchInfo()
    except errors.ProgrammerError:
      # during tests, the arch info can be initialized multiple times
      pass

    self.ResetMocks()

  def _StopPatchers(self):
    if self._iallocator_patcher is not None:
      self._iallocator_patcher.stop()
      self._iallocator_patcher = None
    if self._netutils_patcher is not None:
      self._netutils_patcher.stop()
      self._netutils_patcher = None
    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()

    self._StopPatchers()

  def _GetTestModule(self):
    module = inspect.getsourcefile(self.__class__).split("/")[-1]
    suffix = "_unittest.py"
    assert module.endswith(suffix), "Naming convention for cmdlib test" \
                                    " modules is: <module>%s (found '%s')"\
                                    % (suffix, module)
    return module[:-len(suffix)]

  def ResetMocks(self):
    """Resets all mocks back to their initial state.

    This is useful if you want to execute more than one opcode in a single
    test.

    """
    self.cfg = ConfigMock()
    self.rpc = CreateRpcRunnerMock()
    self.ctx = GanetiContextMock(self)
    self.mcpu = ProcessorMock(self.ctx)

    self._StopPatchers()
    try:
      self._iallocator_patcher = patchIAllocator(self._GetTestModule())
      self.iallocator_cls = self._iallocator_patcher.start()
    except (ImportError, AttributeError):
      # this test module does not use iallocator, no patching performed
      self._iallocator_patcher = None

    try:
      self._netutils_patcher = patchNetutils(self._GetTestModule())
      self.netutils_mod = self._netutils_patcher.start()
      SetupDefaultNetutilsMock(self.netutils_mod, self.cfg)
    except (ImportError, AttributeError):
      # this test module does not use netutils, no patching performed
      self._netutils_patcher = None

    try:
      self._ssh_patcher = patchSsh(self._GetTestModule())
      self.ssh_mod = self._ssh_patcher.start()
    except (ImportError, AttributeError):
      # 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.

    @rtype: L{LogialUnit}
    @return: A mock LU

    """
    return MockLU(self.mcpu, mock.MagicMock(), self.ctx, self.cfg, self.rpc,
                  (1234, "/tmp/mock/livelock"), WConfdMock())

  def RpcResultsBuilder(self, use_node_names=False):
    """Creates a pre-configured L{RpcResultBuilder}

    @type use_node_names: bool
    @param use_node_names: @see L{RpcResultBuilder}
    @rtype: L{RpcResultBuilder}
    @return: a pre-configured builder for RPC results

    """
    return RpcResultsBuilder(cfg=self.cfg, use_node_names=use_node_names)

  def ExecOpCode(self, opcode):
    """Executes the given opcode.

    @param opcode: the opcode to execute
    @return: the result of the LU's C{Exec} method

    """
    return self.mcpu.ExecOpCodeAndRecordOutput(opcode)

  def ExecOpCodeExpectException(self, opcode,
                                expected_exception,
                                expected_regex=None):
    """Executes the given opcode and expects an exception.

    @param opcode: @see L{ExecOpCode}
    @type expected_exception: class
    @param expected_exception: the exception which must be raised
    @type expected_regex: string
    @param expected_regex: if not C{None}, a regular expression which must be
          present in the string representation of the exception

    """
    try:
      self.ExecOpCode(opcode)
    except expected_exception, e:
      if expected_regex is not None:
        assert re.search(expected_regex, str(e)) is not None, \
                "Caught exception '%s' did not match '%s'" % \
                  (str(e), expected_regex)
    except Exception, e:
      tb = traceback.format_exc()
      raise AssertionError("%s\n(See original exception above)\n"
                           "Expected exception '%s' was not raised,"
                           " got '%s' of class '%s' instead." %
                           (tb, expected_exception, e, e.__class__))