X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/5ca84bdd5abf39dd723569954b43db5218aeb553..48aaca91efa214b37dba94f28582be73f3c90dbd:/test/ganeti.hooks_unittest.py diff --git a/test/ganeti.hooks_unittest.py b/test/ganeti.hooks_unittest.py index b8c26e8..e20fc11 100755 --- a/test/ganeti.hooks_unittest.py +++ b/test/ganeti.hooks_unittest.py @@ -34,14 +34,25 @@ from ganeti import mcpu from ganeti import backend from ganeti import constants from ganeti import cmdlib +from ganeti import rpc +from ganeti import compat +from ganeti import pathutils from ganeti.constants import HKR_SUCCESS, HKR_FAIL, HKR_SKIP -from mocks import FakeConfig, FakeSStore +from mocks import FakeConfig, FakeProc, FakeContext + +import testutils + class FakeLU(cmdlib.LogicalUnit): HPATH = "test" + def BuildHooksEnv(self): - return {}, ["localhost"], ["localhost"] + return {} + + def BuildHooksNodes(self): + return ["localhost"], ["localhost"] + class TestHooksRunner(unittest.TestCase): """Testing case for HooksRunner""" @@ -181,11 +192,24 @@ class TestHooksRunner(unittest.TestCase): os.symlink("/usr/bin/env", fname) self.torm.append((fname, False)) env_snt = {"PHASE": phase} - env_exp = "PHASE=%s\n" % phase + env_exp = "PHASE=%s" % phase self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, env_snt), [(self._rname(fname), HKR_SUCCESS, env_exp)]) +def FakeHooksRpcSuccess(node_list, hpath, phase, env): + """Fake call_hooks_runner function. + + @rtype: dict of node -> L{rpc.RpcResult} with a successful script result + @return: script execution from all nodes + + """ + rr = rpc.RpcResult + return dict([(node, rr((True, [("utest", constants.HKR_SUCCESS, "ok")]), + node=node, call="FakeScriptOk")) + for node in node_list]) + + class TestHooksMaster(unittest.TestCase): """Testing case for HooksMaster""" @@ -197,76 +221,279 @@ class TestHooksMaster(unittest.TestCase): def _call_nodes_false(node_list, hpath, phase, env): """Fake call_hooks_runner function. - Returns: - - list of False values with the same len as the node_list argument + @rtype: dict of node -> L{rpc.RpcResult} with an rpc error + @return: rpc failure from all nodes """ - return [False for node_name in node_list] + return dict([(node, rpc.RpcResult("error", failed=True, + node=node, call="FakeError")) for node in node_list]) @staticmethod def _call_script_fail(node_list, hpath, phase, env): """Fake call_hooks_runner function. - Returns: - - list of False values with the same len as the node_list argument + @rtype: dict of node -> L{rpc.RpcResult} with a failed script result + @return: script execution failure from all nodes """ - return dict([(node_name, [("unittest", constants.HKR_FAIL, "error")]) - for node_name in node_list]) - - @staticmethod - def _call_script_succeed(node_list, hpath, phase, env): - """Fake call_hooks_runner function. + rr = rpc.RpcResult + return dict([(node, rr((True, [("utest", constants.HKR_FAIL, "err")]), + node=node, call="FakeScriptFail")) + for node in node_list]) - Returns: - - list of False values with the same len as the node_list argument - - """ - return dict([(node_name, [("unittest", constants.HKR_SUCCESS, "ok")]) - for node_name in node_list]) + def setUp(self): + self.op = opcodes.OpCode() + self.context = FakeContext() + # WARNING: here we pass None as RpcRunner instance since we know + # our usage via HooksMaster will not use lu.rpc + self.lu = FakeLU(FakeProc(), self.op, self.context, None) def testTotalFalse(self): """Test complete rpc failure""" - cfg = FakeConfig() - sstore = FakeSStore() - op = opcodes.OpCode() - lu = FakeLU(None, op, cfg, sstore) - hm = mcpu.HooksMaster(self._call_false, cfg, sstore, lu) + hm = mcpu.HooksMaster.BuildFromLu(self._call_false, self.lu) self.failUnlessRaises(errors.HooksFailure, hm.RunPhase, constants.HOOKS_PHASE_PRE) hm.RunPhase(constants.HOOKS_PHASE_POST) def testIndividualFalse(self): - """Test individual rpc failure""" - cfg = FakeConfig() - sstore = FakeSStore() - op = opcodes.OpCode() - lu = FakeLU(None, op, cfg, sstore) - hm = mcpu.HooksMaster(self._call_nodes_false, cfg, sstore, lu) - self.failUnlessRaises(errors.HooksFailure, - hm.RunPhase, constants.HOOKS_PHASE_PRE) + """Test individual node failure""" + hm = mcpu.HooksMaster.BuildFromLu(self._call_nodes_false, self.lu) + hm.RunPhase(constants.HOOKS_PHASE_PRE) + #self.failUnlessRaises(errors.HooksFailure, + # hm.RunPhase, constants.HOOKS_PHASE_PRE) hm.RunPhase(constants.HOOKS_PHASE_POST) def testScriptFalse(self): """Test individual rpc failure""" - cfg = FakeConfig() - op = opcodes.OpCode() - sstore = FakeSStore() - lu = FakeLU(None, op, cfg, sstore) - hm = mcpu.HooksMaster(self._call_script_fail, cfg, sstore, lu) + hm = mcpu.HooksMaster.BuildFromLu(self._call_script_fail, self.lu) self.failUnlessRaises(errors.HooksAbort, hm.RunPhase, constants.HOOKS_PHASE_PRE) hm.RunPhase(constants.HOOKS_PHASE_POST) def testScriptSucceed(self): """Test individual rpc failure""" - cfg = FakeConfig() - op = opcodes.OpCode() - sstore = FakeSStore() - lu = FakeLU(None, op, cfg, sstore) - hm = mcpu.HooksMaster(self._call_script_succeed, cfg, sstore, lu) + hm = mcpu.HooksMaster.BuildFromLu(FakeHooksRpcSuccess, self.lu) for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST): hm.RunPhase(phase) -if __name__ == '__main__': - unittest.main() + +class FakeEnvLU(cmdlib.LogicalUnit): + HPATH = "env_test_lu" + HTYPE = constants.HTYPE_GROUP + + def __init__(self, *args): + cmdlib.LogicalUnit.__init__(self, *args) + self.hook_env = None + + def BuildHooksEnv(self): + assert self.hook_env is not None + return self.hook_env + + def BuildHooksNodes(self): + return (["localhost"], ["localhost"]) + + +class FakeNoHooksLU(cmdlib.NoHooksLU): + pass + + +class TestHooksRunnerEnv(unittest.TestCase): + def setUp(self): + self._rpcs = [] + + self.op = opcodes.OpTestDummy(result=False, messages=[], fail=False) + self.lu = FakeEnvLU(FakeProc(), self.op, FakeContext(), None) + + def _HooksRpc(self, *args): + self._rpcs.append(args) + return FakeHooksRpcSuccess(*args) + + def _CheckEnv(self, env, phase, hpath): + self.assertTrue(env["PATH"].startswith("/sbin")) + self.assertEqual(env["GANETI_HOOKS_PHASE"], phase) + self.assertEqual(env["GANETI_HOOKS_PATH"], hpath) + self.assertEqual(env["GANETI_OP_CODE"], self.op.OP_ID) + self.assertEqual(env["GANETI_HOOKS_VERSION"], str(constants.HOOKS_VERSION)) + self.assertEqual(env["GANETI_DATA_DIR"], pathutils.DATA_DIR) + if "GANETI_OBJECT_TYPE" in env: + self.assertEqual(env["GANETI_OBJECT_TYPE"], constants.HTYPE_GROUP) + else: + self.assertTrue(self.lu.HTYPE is None) + + def testEmptyEnv(self): + # Check pre-phase hook + self.lu.hook_env = {} + hm = mcpu.HooksMaster.BuildFromLu(self._HooksRpc, self.lu) + hm.RunPhase(constants.HOOKS_PHASE_PRE) + + (node_list, hpath, phase, env) = self._rpcs.pop(0) + self.assertEqual(node_list, set(["localhost"])) + self.assertEqual(hpath, self.lu.HPATH) + self.assertEqual(phase, constants.HOOKS_PHASE_PRE) + self._CheckEnv(env, constants.HOOKS_PHASE_PRE, self.lu.HPATH) + + # Check post-phase hook + self.lu.hook_env = {} + hm.RunPhase(constants.HOOKS_PHASE_POST) + + (node_list, hpath, phase, env) = self._rpcs.pop(0) + self.assertEqual(node_list, set(["localhost"])) + self.assertEqual(hpath, self.lu.HPATH) + self.assertEqual(phase, constants.HOOKS_PHASE_POST) + self._CheckEnv(env, constants.HOOKS_PHASE_POST, self.lu.HPATH) + + self.assertRaises(IndexError, self._rpcs.pop) + + def testEnv(self): + # Check pre-phase hook + self.lu.hook_env = { + "FOO": "pre-foo-value", + } + hm = mcpu.HooksMaster.BuildFromLu(self._HooksRpc, self.lu) + hm.RunPhase(constants.HOOKS_PHASE_PRE) + + (node_list, hpath, phase, env) = self._rpcs.pop(0) + self.assertEqual(node_list, set(["localhost"])) + self.assertEqual(hpath, self.lu.HPATH) + self.assertEqual(phase, constants.HOOKS_PHASE_PRE) + self.assertEqual(env["GANETI_FOO"], "pre-foo-value") + self.assertFalse(compat.any(key.startswith("GANETI_POST") for key in env)) + self._CheckEnv(env, constants.HOOKS_PHASE_PRE, self.lu.HPATH) + + # Check post-phase hook + self.lu.hook_env = { + "FOO": "post-value", + "BAR": 123, + } + hm.RunPhase(constants.HOOKS_PHASE_POST) + + (node_list, hpath, phase, env) = self._rpcs.pop(0) + self.assertEqual(node_list, set(["localhost"])) + self.assertEqual(hpath, self.lu.HPATH) + self.assertEqual(phase, constants.HOOKS_PHASE_POST) + self.assertEqual(env["GANETI_FOO"], "pre-foo-value") + self.assertEqual(env["GANETI_POST_FOO"], "post-value") + self.assertEqual(env["GANETI_POST_BAR"], "123") + self.assertFalse("GANETI_BAR" in env) + self._CheckEnv(env, constants.HOOKS_PHASE_POST, self.lu.HPATH) + + self.assertRaises(IndexError, self._rpcs.pop) + + # Check configuration update hook + hm.RunConfigUpdate() + (node_list, hpath, phase, env) = self._rpcs.pop(0) + self.assertEqual(set(node_list), set([self.lu.cfg.GetMasterNode()])) + self.assertEqual(hpath, constants.HOOKS_NAME_CFGUPDATE) + self.assertEqual(phase, constants.HOOKS_PHASE_POST) + self._CheckEnv(env, constants.HOOKS_PHASE_POST, + constants.HOOKS_NAME_CFGUPDATE) + self.assertFalse(compat.any(key.startswith("GANETI_POST") for key in env)) + self.assertEqual(env["GANETI_FOO"], "pre-foo-value") + self.assertRaises(IndexError, self._rpcs.pop) + + def testConflict(self): + for name in ["DATA_DIR", "OP_CODE"]: + self.lu.hook_env = { name: "value" } + + # Test using a clean HooksMaster instance + hm = mcpu.HooksMaster.BuildFromLu(self._HooksRpc, self.lu) + + for phase in [constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST]: + self.assertRaises(AssertionError, hm.RunPhase, phase) + self.assertRaises(IndexError, self._rpcs.pop) + + def testNoNodes(self): + self.lu.hook_env = {} + hm = mcpu.HooksMaster.BuildFromLu(self._HooksRpc, self.lu) + hm.RunPhase(constants.HOOKS_PHASE_PRE, nodes=[]) + self.assertRaises(IndexError, self._rpcs.pop) + + def testSpecificNodes(self): + self.lu.hook_env = {} + + nodes = [ + "node1.example.com", + "node93782.example.net", + ] + + hm = mcpu.HooksMaster.BuildFromLu(self._HooksRpc, self.lu) + + for phase in [constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST]: + hm.RunPhase(phase, nodes=nodes) + + (node_list, hpath, rpc_phase, env) = self._rpcs.pop(0) + self.assertEqual(set(node_list), set(nodes)) + self.assertEqual(hpath, self.lu.HPATH) + self.assertEqual(rpc_phase, phase) + self._CheckEnv(env, phase, self.lu.HPATH) + + self.assertRaises(IndexError, self._rpcs.pop) + + def testRunConfigUpdateNoPre(self): + self.lu.hook_env = { + "FOO": "value", + } + + hm = mcpu.HooksMaster.BuildFromLu(self._HooksRpc, self.lu) + hm.RunConfigUpdate() + + (node_list, hpath, phase, env) = self._rpcs.pop(0) + self.assertEqual(set(node_list), set([self.lu.cfg.GetMasterNode()])) + self.assertEqual(hpath, constants.HOOKS_NAME_CFGUPDATE) + self.assertEqual(phase, constants.HOOKS_PHASE_POST) + self.assertEqual(env["GANETI_FOO"], "value") + self.assertFalse(compat.any(key.startswith("GANETI_POST") for key in env)) + self._CheckEnv(env, constants.HOOKS_PHASE_POST, + constants.HOOKS_NAME_CFGUPDATE) + + self.assertRaises(IndexError, self._rpcs.pop) + + def testNoPreBeforePost(self): + self.lu.hook_env = { + "FOO": "value", + } + + hm = mcpu.HooksMaster.BuildFromLu(self._HooksRpc, self.lu) + hm.RunPhase(constants.HOOKS_PHASE_POST) + + (node_list, hpath, phase, env) = self._rpcs.pop(0) + self.assertEqual(node_list, set(["localhost"])) + self.assertEqual(hpath, self.lu.HPATH) + self.assertEqual(phase, constants.HOOKS_PHASE_POST) + self.assertEqual(env["GANETI_FOO"], "value") + self.assertEqual(env["GANETI_POST_FOO"], "value") + self._CheckEnv(env, constants.HOOKS_PHASE_POST, self.lu.HPATH) + + self.assertRaises(IndexError, self._rpcs.pop) + + def testNoHooksLU(self): + self.lu = FakeNoHooksLU(FakeProc(), self.op, FakeContext(), None) + self.assertRaises(AssertionError, self.lu.BuildHooksEnv) + self.assertRaises(AssertionError, self.lu.BuildHooksNodes) + + hm = mcpu.HooksMaster.BuildFromLu(self._HooksRpc, self.lu) + self.assertEqual(hm.pre_env, {}) + self.assertRaises(IndexError, self._rpcs.pop) + + hm.RunPhase(constants.HOOKS_PHASE_PRE) + self.assertRaises(IndexError, self._rpcs.pop) + + hm.RunPhase(constants.HOOKS_PHASE_POST) + self.assertRaises(IndexError, self._rpcs.pop) + + hm.RunConfigUpdate() + + (node_list, hpath, phase, env) = self._rpcs.pop(0) + self.assertEqual(set(node_list), set([self.lu.cfg.GetMasterNode()])) + self.assertEqual(hpath, constants.HOOKS_NAME_CFGUPDATE) + self.assertEqual(phase, constants.HOOKS_PHASE_POST) + self.assertFalse(compat.any(key.startswith("GANETI_POST") for key in env)) + self._CheckEnv(env, constants.HOOKS_PHASE_POST, + constants.HOOKS_NAME_CFGUPDATE) + self.assertRaises(IndexError, self._rpcs.pop) + + assert isinstance(self.lu, FakeNoHooksLU), "LU was replaced" + + +if __name__ == "__main__": + testutils.GanetiTestProgram()