X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/7b80424fbcbf46b2753ee619814ba6fb554ae6ee..48aaca91efa214b37dba94f28582be73f3c90dbd:/test/ganeti.hooks_unittest.py diff --git a/test/ganeti.hooks_unittest.py b/test/ganeti.hooks_unittest.py index 55fef19..e20fc11 100755 --- a/test/ganeti.hooks_unittest.py +++ b/test/ganeti.hooks_unittest.py @@ -35,14 +35,24 @@ 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, 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""" @@ -187,6 +197,19 @@ class TestHooksRunner(unittest.TestCase): [(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""" @@ -202,8 +225,8 @@ class TestHooksMaster(unittest.TestCase): @return: rpc failure from all nodes """ - return dict([(node, rpc.RpcResult('error', failed=True, - node=node, call='FakeError')) for node 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): @@ -213,19 +236,10 @@ class TestHooksMaster(unittest.TestCase): @return: script execution failure from all nodes """ - return dict([(node, rpc.RpcResult([("utest", constants.HKR_FAIL, "err")], - node=node, call='FakeScriptFail')) for node in node_list]) - - @staticmethod - def _call_script_succeed(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 - - """ - return dict([(node, rpc.RpcResult([("utest", constants.HKR_SUCCESS, "ok")], - node=node, call='FakeScriptOk')) for node in node_list]) + rr = rpc.RpcResult + return dict([(node, rr((True, [("utest", constants.HKR_FAIL, "err")]), + node=node, call="FakeScriptFail")) + for node in node_list]) def setUp(self): self.op = opcodes.OpCode() @@ -236,14 +250,14 @@ class TestHooksMaster(unittest.TestCase): def testTotalFalse(self): """Test complete rpc failure""" - hm = mcpu.HooksMaster(self._call_false, FakeProc(), self.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 node failure""" - hm = mcpu.HooksMaster(self._call_nodes_false, FakeProc(), self.lu) + 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) @@ -251,16 +265,235 @@ class TestHooksMaster(unittest.TestCase): def testScriptFalse(self): """Test individual rpc failure""" - hm = mcpu.HooksMaster(self._call_script_fail, FakeProc(), self.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""" - hm = mcpu.HooksMaster(self._call_script_succeed, FakeProc(), self.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()