Test 3 main cases of global post hooks usage: - successful LU execution; - LU with the prerequisites failed; - disappeared LU process. All the tests are performed on the master node.
Signed-off-by: Oleg Ponomarev <[email protected]> --- Makefile.am | 1 + qa/ganeti-qa.py | 3 + qa/qa-sample.json | 2 + qa/qa_global_hooks.py | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++ qa/qa_utils.py | 13 +++- 5 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 qa/qa_global_hooks.py diff --git a/Makefile.am b/Makefile.am index 8ba84a5..0080f00 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1347,6 +1347,7 @@ qa_scripts = \ qa/qa_error.py \ qa/qa_filters.py \ qa/qa_group.py \ + qa/qa_global_hooks.py \ qa/qa_instance.py \ qa/qa_instance_utils.py \ qa/qa_iptables.py \ diff --git a/qa/ganeti-qa.py b/qa/ganeti-qa.py index 9b475e2..36a9263 100755 --- a/qa/ganeti-qa.py +++ b/qa/ganeti-qa.py @@ -48,6 +48,7 @@ import qa_env import qa_error import qa_filters import qa_group +import qa_global_hooks import qa_instance import qa_iptables import qa_maintd @@ -965,6 +966,8 @@ def RunQa(): RunTestBlock(RunNetworkTests) RunTestBlock(RunFilterTests) + RunTestIf("global-hooks", qa_global_hooks.RunGlobalHooksTests) + # The master shouldn't be readded or put offline; "delay" needs a non-master # node to test pnode = qa_config.AcquireNode(exclude=qa_config.GetMasterNode()) diff --git a/qa/qa-sample.json b/qa/qa-sample.json index ac10a05..9b0a1a1 100644 --- a/qa/qa-sample.json +++ b/qa/qa-sample.json @@ -253,6 +253,8 @@ "job-list": true, + "global-hooks": true, + "jobqueue-performance": true, "parallel-performance": true, diff --git a/qa/qa_global_hooks.py b/qa/qa_global_hooks.py new file mode 100644 index 0000000..8e5457a --- /dev/null +++ b/qa/qa_global_hooks.py @@ -0,0 +1,167 @@ +# +# + +# Copyright (C) 2015 Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +"""QA tests for the universal job hooks. + +""" + +import time + +from ganeti import constants +from ganeti import pathutils +from qa_config import GetMasterNode +from qa_job_utils import ExecuteJobProducingCommand +from qa_utils import AssertEqual, GetCommandOutput, IsFileExists + +PRE_PATH = "%s/global-pre.d" % pathutils.HOOKS_BASE_DIR +POST_PATH = "%s/global-post.d" % pathutils.HOOKS_BASE_DIR +H_DIR = "/var/log/ganeti/qa_global_hooks" + + +def _GetHookFilePath(job_id, phase, status=None): + """Returns the path to the qa hooks temporary files. + + """ + h_fname = H_DIR + "/%d_OP_TEST_DELAY_%s" % (job_id, phase) + if phase == "pre": + return h_fname + return h_fname + "_" + status + + +def TestHooksInitialize(): + """Creates global hooks on the master node + + """ + master = GetMasterNode().primary + GetCommandOutput(master, "mkdir -p %s" % pathutils.HOOKS_BASE_DIR) + GetCommandOutput(master, "mkdir -p %s" % PRE_PATH) + GetCommandOutput(master, "mkdir -p %s" % POST_PATH) + GetCommandOutput(master, "mkdir -p %s" % H_DIR) + h_name = "/qa_test_hook" + create_hook_common = """ +FOUT=%s +echo '#!/bin/sh' > $FOUT +echo 'touch %s/$GANETI_JOB_ID"_"$GANETI_OP_CODE%s' >> $FOUT +chmod +x $FOUT +""" + create_pre = create_hook_common % (PRE_PATH + h_name, H_DIR, '"_pre"') + create_post = create_hook_common % (POST_PATH + h_name, H_DIR, + '"_post_"$GANETI_POST_STATUS') + GetCommandOutput(master, create_pre) + GetCommandOutput(master, create_post) + + +def TestHookSucceeded(): + """Checks whether the global hooks have been executed (status succes). + + - Global pre hook should has been executed. + - Global post hook should with status *success* should has been executed. + - Global post hooks with failed statuses shouldn't have been executed. + """ + master = GetMasterNode().primary + job_id = ExecuteJobProducingCommand("gnt-debug delay --submit 1") + time.sleep(3) + AssertEqual(IsFileExists(master, _GetHookFilePath(job_id, "pre")), True, + "Global pre hook hasn't been executed.") + AssertEqual(IsFileExists(master, _GetHookFilePath(job_id, "post", + constants.POST_HOOKS_STATUS_SUCCESS)), True, + "Global post hook hasn't been executed with status *success*") + AssertEqual(IsFileExists(master, _GetHookFilePath(job_id, "post", + constants.POST_HOOKS_STATUS_ERROR)), False, + "Global post hook has been executed with status *error*") + AssertEqual(IsFileExists(master, _GetHookFilePath(job_id, "post", + constants.POST_HOOKS_STATUS_DISAPPEARED)), False, + "Global post hook has been executed with status *disappeared*") + + +def TestHookFailed(): + """Checks whether the global hooks have been executed (status error). + + - Global post hook should with status *error* should has been executed. + - Global post hook with other statuses shouldn't have been executed. + """ + master = GetMasterNode().primary + job_id = ExecuteJobProducingCommand("gnt-debug delay --submit 0") + time.sleep(1) + AssertEqual(IsFileExists(master, _GetHookFilePath(job_id, "post", + constants.POST_HOOKS_STATUS_SUCCESS)), False, + "Global post hook has been executed with status *success*") + AssertEqual(IsFileExists(master, _GetHookFilePath(job_id, "post", + constants.POST_HOOKS_STATUS_ERROR)), True, + "Global post hook hasn't been executed with status *error*") + AssertEqual(IsFileExists(master, _GetHookFilePath(job_id, "post", + constants.POST_HOOKS_STATUS_DISAPPEARED)), False, + "Global post hook has been executed with status *disappeared*") + + +def TestHookDisappeared(): + """Checks whether the global hooks have been executed (status disappeared). + + - Global pre hook should has been executed. + - Global post hook should with status *disappeared* should has been executed. + - Global post hook with other statuses shouldn't have been executed. + """ + master = GetMasterNode().primary + job_id = ExecuteJobProducingCommand("gnt-debug delay --submit 10") + time.sleep(1) + GetCommandOutput(master, "gnt-job cancel --kill --yes-do-it %d" % job_id) + time.sleep(10) + AssertEqual(IsFileExists(master, _GetHookFilePath(job_id, "pre")), True, + "Global pre hook hasn't been executed.") + AssertEqual(IsFileExists(master, _GetHookFilePath(job_id, "post", + constants.POST_HOOKS_STATUS_SUCCESS)), False, + "Global post hook has been executed with status *success*") + AssertEqual(IsFileExists(master, _GetHookFilePath(job_id, "post", + constants.POST_HOOKS_STATUS_ERROR)), False, + "Global post hook has been executed with status *error*") + AssertEqual(IsFileExists(master, _GetHookFilePath(job_id, "post", + constants.POST_HOOKS_STATUS_DISAPPEARED)), True, + "Global post hook hasn't been executed with status *disappeared*") + + +def TestHooksCleanup(): + """Remove the directories created by the tests + + """ + master = GetMasterNode().primary + GetCommandOutput(master, "rm %s/*" % PRE_PATH) + GetCommandOutput(master, "rm %s/*" % POST_PATH) + GetCommandOutput(master, "rm -rf %s" % H_DIR) + + +def RunGlobalHooksTests(): + """Runs tests for global hooks considering different job execution cases. + + """ + TestHooksInitialize() + TestHookSucceeded() + TestHookFailed() + TestHookDisappeared() + TestHooksCleanup() diff --git a/qa/qa_utils.py b/qa/qa_utils.py index f2b2c2e..94e1dce 100644 --- a/qa/qa_utils.py +++ b/qa/qa_utils.py @@ -1,7 +1,7 @@ # # -# Copyright (C) 2007, 2011, 2012, 2013 Google Inc. +# Copyright (C) 2007, 2011, 2012, 2013, 2015 Google Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -523,6 +523,17 @@ def BackupFile(node, path): return result +def IsFileExists(node, path): + """Checks if a file on the node exists. + + """ + cmd = ("[[ -f \"%s\" ]] && echo yes || echo no" % path) + + # Return temporary filename + result = GetCommandOutput(node, cmd).strip() + return True if result == "yes" else False + + @contextlib.contextmanager def CheckFileUnmodified(node, filename): """Checks that the content of a given file remains the same after running a -- 2.6.0.rc2.230.g3dd15c0
