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

Reply via email to