def setUp(self): super(TestKvmRuntime, self).setUp() kvm_class = 'ganeti.hypervisor.hv_kvm.KVMHypervisor' self.MockOut('qmp', mock.patch('ganeti.hypervisor.hv_kvm.QmpConnection')) self.MockOut('run_cmd', mock.patch('ganeti.utils.RunCmd')) self.MockOut('ensure_dirs', mock.patch('ganeti.utils.EnsureDirs')) self.MockOut('write_file', mock.patch('ganeti.utils.WriteFile')) self.MockOut(mock.patch(kvm_class + '.ValidateParameters')) self.MockOut( mock.patch('ganeti.hypervisor.hv_kvm.OpenTap', return_value=('test_nic', [], []))) self.MockOut(mock.patch(kvm_class + '._ConfigureNIC')) self.MockOut( 'pid_alive', mock.patch(kvm_class + '._InstancePidAlive', return_value=('file', -1, False))) self.MockOut(mock.patch(kvm_class + '._ExecuteCpuAffinity')) self.MockOut(mock.patch(kvm_class + '._CallMonitorCommand')) self.cfg = ConfigMock() params = constants.HVC_DEFAULTS[constants.HT_KVM].copy() beparams = constants.BEC_DEFAULTS.copy() self.instance = self.cfg.AddNewInstance(name='name.example.com', hypervisor='kvm', hvparams=params, beparams=beparams)
class TestKvmRuntime(testutils.GanetiTestCase): """The _ExecuteKvmRuntime is at the core of all KVM operations.""" def setUp(self): super(TestKvmRuntime, self).setUp() kvm_class = 'ganeti.hypervisor.hv_kvm.KVMHypervisor' self.MockOut('qmp', mock.patch('ganeti.hypervisor.hv_kvm.QmpConnection')) self.MockOut('run_cmd', mock.patch('ganeti.utils.RunCmd')) self.MockOut('ensure_dirs', mock.patch('ganeti.utils.EnsureDirs')) self.MockOut('write_file', mock.patch('ganeti.utils.WriteFile')) self.MockOut(mock.patch(kvm_class + '.ValidateParameters')) self.MockOut(mock.patch('ganeti.hypervisor.hv_kvm.OpenTap', return_value=('test_nic', [], []))) self.MockOut(mock.patch(kvm_class + '._ConfigureNIC')) self.MockOut('pid_alive', mock.patch(kvm_class + '._InstancePidAlive', return_value=('file', -1, False))) self.MockOut(mock.patch(kvm_class + '._ExecuteCpuAffinity')) self.MockOut(mock.patch(kvm_class + '._CallMonitorCommand')) self.cfg = ConfigMock() self.params = constants.HVC_DEFAULTS[constants.HT_KVM].copy() self.beparams = constants.BEC_DEFAULTS.copy() self.instance = self.cfg.AddNewInstance(name='name.example.com', hypervisor='kvm', hvparams=self.params, beparams=self.beparams) def testDirectoriesCreated(self): hypervisor = hv_kvm.KVMHypervisor() self.mocks['ensure_dirs'].assert_called_with([ (PostfixMatcher('/run/ganeti/kvm-hypervisor'), 0775), (PostfixMatcher('/run/ganeti/kvm-hypervisor/pid'), 0775), (PostfixMatcher('/run/ganeti/kvm-hypervisor/uid'), 0775), (PostfixMatcher('/run/ganeti/kvm-hypervisor/ctrl'), 0775), (PostfixMatcher('/run/ganeti/kvm-hypervisor/conf'), 0775), (PostfixMatcher('/run/ganeti/kvm-hypervisor/nic'), 0775), (PostfixMatcher('/run/ganeti/kvm-hypervisor/chroot'), 0775), (PostfixMatcher('/run/ganeti/kvm-hypervisor/chroot-quarantine'), 0775), (PostfixMatcher('/run/ganeti/kvm-hypervisor/keymap'), 0775)]) def testStartInstance(self): hypervisor = hv_kvm.KVMHypervisor() def RunCmd(cmd, **kwargs): if '--help' in cmd: return mock.Mock( failed=False, output=testutils.ReadTestData("kvm_1.1.2_help.txt")) if '-S' in cmd: self.mocks['pid_alive'].return_value = ('file', -1, True) return mock.Mock(failed=False) elif '-M' in cmd: return mock.Mock(failed=False, output='') elif '-device' in cmd: return mock.Mock(failed=False, output='name "virtio-blk-pci"') else: raise errors.ProgrammerError('Unexpected command: %s' % cmd) self.mocks['run_cmd'].side_effect = RunCmd hypervisor.StartInstance(self.instance, [], False)
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 _get_object_mock(self): """Returns a mocked instance of ConfigWriter""" cfg = ConfigMock(cfg_file=self.cfg_file) return cfg
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)