From 1672a0d12146179e1ce5d7f32149c7f1109ee894 Mon Sep 17 00:00:00 2001
From: Michael Hanselmann <hansmi@google.com>
Date: Thu, 8 Nov 2007 11:50:30 +0000
Subject: [PATCH] Implement hooks infrastructure.

Reviewed-by: schreiberal
---
 qa/ganeti-qa.py   |  2 ++
 qa/qa-sample.yaml |  3 ++
 qa/qa_utils.py    | 82 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 87 insertions(+)

diff --git a/qa/ganeti-qa.py b/qa/ganeti-qa.py
index a0cbcea15..9f6f6ace6 100755
--- a/qa/ganeti-qa.py
+++ b/qa/ganeti-qa.py
@@ -40,6 +40,7 @@ import qa_node
 import qa_os
 import qa_other
 import qa_tags
+import qa_utils
 
 
 def RunTest(fn, *args):
@@ -236,6 +237,7 @@ def main():
     sys.exit(1)
 
   qa_config.Load(config_file)
+  qa_utils.LoadHooks()
 
   RunTest(qa_other.UploadKnownHostsFile, known_hosts_file)
 
diff --git a/qa/qa-sample.yaml b/qa/qa-sample.yaml
index 49ecef01e..75bbd3840 100644
--- a/qa/qa-sample.yaml
+++ b/qa/qa-sample.yaml
@@ -70,3 +70,6 @@ tests:
 # Other settings
 options:
   burnin-instances: 2
+
+  # Directory containing QA hooks
+  #hooks-dir: hooks/
diff --git a/qa/qa_utils.py b/qa/qa_utils.py
index 70650c1b9..e5ae41416 100644
--- a/qa/qa_utils.py
+++ b/qa/qa_utils.py
@@ -36,6 +36,10 @@ _ERROR_SEQ = None
 _RESET_SEQ = None
 
 
+# List of all hooks
+_hooks = []
+
+
 def _SetupColours():
   """Initializes the colour constants.
 
@@ -214,3 +218,81 @@ def _FormatWithColor(text, seq):
 FormatWarning = lambda text: _FormatWithColor(text, _WARNING_SEQ)
 FormatError = lambda text: _FormatWithColor(text, _ERROR_SEQ)
 FormatInfo = lambda text: _FormatWithColor(text, _INFO_SEQ)
+
+
+def LoadHooks():
+  """Load all QA hooks.
+
+  """
+  hooks_dir = qa_config.get('options', {}).get('hooks-dir', None)
+  if not hooks_dir:
+    return
+  if hooks_dir not in sys.path:
+    sys.path.insert(0, hooks_dir)
+  for name in utils.ListVisibleFiles(hooks_dir):
+    if name.endswith('.py'):
+      # Load and instanciate hook
+      _hooks.append(__import__(name[:-3], None, None, ['']).hook())
+
+
+class QaHookContext:
+  name = None
+  phase = None
+  success = None
+  args = None
+  kwargs = None
+
+
+def _CallHooks(ctx):
+  """Calls all hooks with the given context.
+
+  """
+  if not _hooks:
+    return
+
+  name = "%s-%s" % (ctx.phase, ctx.name)
+  if ctx.success is not None:
+    msg = "%s (success=%s)" % (name, ctx.success)
+  else:
+    msg = name
+  print FormatInfo("Begin %s" % msg)
+  for hook in _hooks:
+    hook.run(ctx)
+  print FormatInfo("End %s" % name)
+
+
+def DefineHook(name):
+  """Wraps a function with calls to hooks.
+
+  Usage: prefix function with @qa_utils.DefineHook(...)
+
+  """
+  def wrapper(fn):
+    def new_f(*args, **kwargs):
+      # Create context
+      ctx = QaHookContext()
+      ctx.name = name
+      ctx.phase = 'pre'
+      ctx.args = args
+      ctx.kwargs = kwargs
+
+      _CallHooks(ctx)
+      try:
+        ctx.phase = 'post'
+        ctx.success = True
+        try:
+          # Call real function
+          return fn(*args, **kwargs)
+        except:
+          ctx.success = False
+          raise
+      finally:
+        _CallHooks(ctx)
+
+    # Override function metadata
+    new_f.func_name = fn.func_name
+    new_f.func_doc = fn.func_doc
+
+    return new_f
+
+  return wrapper
-- 
GitLab