This script can be used to check if an instance is running or stopped at
various points during a QA run. Environment variables are used to pass
the most essential information.
---
 qa/ganeti-qa.py   |   36 ++++++++++++++++++++++++++++++++----
 qa/qa-sample.json |    3 +++
 qa/qa_config.py   |   19 +++++++++++++++++++
 qa/qa_utils.py    |   35 +++++++++++++++++++++++++++++++++++
 4 files changed, 89 insertions(+), 4 deletions(-)

diff --git a/qa/ganeti-qa.py b/qa/ganeti-qa.py
index d35ebd6..01a2b1e 100755
--- a/qa/ganeti-qa.py
+++ b/qa/ganeti-qa.py
@@ -72,7 +72,7 @@ def _DescriptionOf(fn):
   return desc.rstrip(".")
 
 
-def RunTest(fn, *args):
+def RunTest(fn, *args, **kwargs):
   """Runs a test after printing a header.
 
   """
@@ -85,7 +85,7 @@ def RunTest(fn, *args):
   print _FormatHeader("%s start %s" % (tstart, desc))
 
   try:
-    retval = fn(*args)
+    retval = fn(*args, **kwargs)
     return retval
   finally:
     tstop = datetime.datetime.now()
@@ -93,7 +93,7 @@ def RunTest(fn, *args):
     print _FormatHeader("%s time=%s %s" % (tstop, tdelta, desc))
 
 
-def RunTestIf(testnames, fn, *args):
+def RunTestIf(testnames, fn, *args, **kwargs):
   """Runs a test conditionally.
 
   @param testnames: either a single test name in the configuration
@@ -101,7 +101,7 @@ def RunTestIf(testnames, fn, *args):
 
   """
   if qa_config.TestEnabled(testnames):
-    RunTest(fn, *args)
+    RunTest(fn, *args, **kwargs)
   else:
     tstart = datetime.datetime.now()
     desc = _DescriptionOf(fn)
@@ -227,18 +227,29 @@ def RunCommonInstanceTests(instance):
   """Runs a few tests that are common to all disk types.
 
   """
+  RunTest(qa_utils.CheckInstance, instance)
   RunTestIf("instance-shutdown", qa_instance.TestInstanceShutdown, instance)
+  RunTestIf("instance-shutdown", qa_utils.CheckInstance,
+            instance, running=False)
   RunTestIf(["instance-shutdown", "instance-console", "rapi"],
             qa_rapi.TestRapiStoppedInstanceConsole, instance)
   RunTestIf(["instance-shutdown", "instance-modify"],
             qa_instance.TestInstanceStoppedModify, instance)
+  if (qa_config.TestEnabled(["instance-console", "rapi"]) or
+      qa_config.TestEnabled("instance-modify")):
+    RunTestIf(["instance-shutdown"], qa_utils.CheckInstance,
+              instance, running=False)
   RunTestIf("instance-shutdown", qa_instance.TestInstanceStartup, instance)
+  RunTestIf("instance-shutdown", qa_utils.CheckInstance, instance)
 
   # Test shutdown/start via RAPI
   RunTestIf(["instance-shutdown", "rapi"],
             qa_rapi.TestRapiInstanceShutdown, instance)
+  RunTestIf(["instance-shutdown", "rapi"], qa_utils.CheckInstance,
+            instance, running=False)
   RunTestIf(["instance-shutdown", "rapi"],
             qa_rapi.TestRapiInstanceStartup, instance)
+  RunTestIf(["instance-shutdown", "rapi"], qa_utils.CheckInstance, instance)
 
   RunTestIf("instance-list", qa_instance.TestInstanceList)
 
@@ -247,6 +258,7 @@ def RunCommonInstanceTests(instance):
   RunTestIf("instance-modify", qa_instance.TestInstanceModify, instance)
   RunTestIf(["instance-modify", "rapi"],
             qa_rapi.TestRapiInstanceModify, instance)
+  RunTestIf("instance-modify", qa_utils.CheckInstance, instance)
 
   RunTestIf("instance-console", qa_instance.TestInstanceConsole, instance)
   RunTestIf(["instance-console", "rapi"],
@@ -256,6 +268,7 @@ def RunCommonInstanceTests(instance):
       qa_config.TestEnabled("instance-rename")):
     # shutdown instance for any 'down' tests
     RunTest(qa_instance.TestInstanceShutdown, instance)
+    RunTest(qa_utils.CheckInstance, instance, running=False)
 
     # now run the 'down' state tests
     RunTestIf("instance-reinstall", qa_instance.TestInstanceReinstall, 
instance)
@@ -263,6 +276,7 @@ def RunCommonInstanceTests(instance):
               qa_rapi.TestRapiInstanceReinstall, instance)
     # RAPI reinstall will leave the instance up by default, so we have
     # to stop it again
+    RunTestIf(["instance-reinstall", "rapi"], qa_utils.CheckInstance, instance)
     RunTestIf(["instance-reinstall", "rapi"],
               qa_rapi.TestRapiInstanceShutdown, instance)
 
@@ -281,10 +295,13 @@ def RunCommonInstanceTests(instance):
         RunTestIf("rapi", qa_rapi.TestRapiInstanceRenameAndBack,
                   rename_source, rename_target)
 
+    RunTest(qa_utils.CheckInstance, instance, running=False)
+
     # Start the instance again
     RunTest(qa_instance.TestInstanceStartup, instance)
 
   RunTestIf("instance-reboot", qa_instance.TestInstanceReboot, instance)
+  RunTestIf("instance-reboot", qa_utils.CheckInstance, instance)
 
   RunTestIf("tags", qa_tags.TestInstanceTags, instance)
 
@@ -298,6 +315,8 @@ def RunCommonInstanceTests(instance):
   # Some jobs have been run, let's test listing them
   RunTestIf("job-list", qa_job.TestJobList)
 
+  RunTestIf("instance-reboot", qa_utils.CheckInstance, instance)
+
 
 def RunCommonNodeTests():
   """Run a few common node tests.
@@ -350,7 +369,9 @@ def RunExportImportTests(instance, pnode, snode):
         try:
           RunTest(qa_instance.TestInstanceImport, pnode, newinst,
                   expnode, name)
+          RunTest(qa_utils.CheckInstance, newinst)
           RunTest(qa_instance.TestInstanceRemove, newinst)
+          RunTest(qa_utils.CheckInstance, newinst, running=False)
         finally:
           qa_config.ReleaseInstance(newinst)
     finally:
@@ -462,6 +483,7 @@ def RunQa():
                                   use_client)
           RunCommonInstanceTests(rapi_instance)
           RunTest(qa_rapi.TestRapiInstanceRemove, rapi_instance, use_client)
+          RunTest(qa_utils.CheckInstance, rapi_instance, running=False)
           del rapi_instance
 
     if qa_config.TestEnabled("instance-add-plain-disk"):
@@ -473,6 +495,7 @@ def RunQa():
       RunDaemonTests(instance)
       RunRepairDiskSizes()
       RunTest(qa_instance.TestInstanceRemove, instance)
+      RunTest(qa_utils.CheckInstance, instance, running=False)
       del instance
 
     multinode_tests = [
@@ -493,11 +516,14 @@ def RunQa():
           if qa_config.TestEnabled("instance-convert-disk"):
             RunTest(qa_instance.TestInstanceShutdown, instance)
             RunTest(qa_instance.TestInstanceConvertDisk, instance, snode)
+            RunTest(qa_utils.CheckInstance, instance, running=False)
             RunTest(qa_instance.TestInstanceStartup, instance)
+          RunTest(qa_utils.CheckInstance, instance)
           RunExportImportTests(instance, pnode, snode)
           RunHardwareFailureTests(instance, pnode, snode)
           RunRepairDiskSizes()
           RunTest(qa_instance.TestInstanceRemove, instance)
+          RunTest(qa_utils.CheckInstance, instance, running=False)
           del instance
         finally:
           qa_config.ReleaseNode(snode)
@@ -507,9 +533,11 @@ def RunQa():
         instance = RunTest(qa_instance.TestInstanceAddWithPlainDisk, pnode)
         expnode = qa_config.AcquireNode(exclude=pnode)
         try:
+          RunTest(qa_utils.CheckInstance, instance)
           if shutdown:
             # Stop instance before exporting and removing it
             RunTest(qa_instance.TestInstanceShutdown, instance)
+            RunTest(qa_utils.CheckInstance, instance, running=False)
           RunTest(qa_instance.TestInstanceExportWithRemove, instance, expnode)
           RunTest(qa_instance.TestBackupList, expnode)
         finally:
diff --git a/qa/qa-sample.json b/qa/qa-sample.json
index 0cfdf2e..a8f98ff 100644
--- a/qa/qa-sample.json
+++ b/qa/qa-sample.json
@@ -24,6 +24,9 @@
   "disk": ["1G", "512M"],
   "disk-growth": ["2G", "768M"],
 
+  "# Script to check instance status": null,
+  "instance-check": null,
+
   "nodes": [
     {
       "# Master node": null,
diff --git a/qa/qa_config.py b/qa/qa_config.py
index e058a71..ccd141f 100644
--- a/qa/qa_config.py
+++ b/qa/qa_config.py
@@ -23,6 +23,7 @@
 
 """
 
+import os
 
 from ganeti import utils
 from ganeti import serializer
@@ -31,6 +32,9 @@ from ganeti import compat
 import qa_error
 
 
+_INSTANCE_CHECK_KEY = "instance-check"
+
+
 cfg = None
 options = None
 
@@ -55,6 +59,14 @@ def Validate():
     raise qa_error.Error("Config options 'disk' and 'disk-growth' must have"
                          " the same number of items")
 
+  check = GetInstanceCheckScript()
+  if check:
+    try:
+      os.stat(check)
+    except EnvironmentError, err:
+      raise qa_error.Error("Can't find instance check script '%s': %s" %
+                           (check, err))
+
 
 def get(name, default=None):
   return cfg.get(name, default)
@@ -78,6 +90,13 @@ def TestEnabled(tests):
   return compat.all(all_tests.get(name, default) for name in tests)
 
 
+def GetInstanceCheckScript():
+  """Returns path to instance check script or C{None}.
+
+  """
+  return cfg.get(_INSTANCE_CHECK_KEY, None)
+
+
 def GetMasterNode():
   return cfg["nodes"][0]
 
diff --git a/qa/qa_utils.py b/qa/qa_utils.py
index f0ef63c..63c708b 100644
--- a/qa/qa_utils.py
+++ b/qa/qa_utils.py
@@ -45,6 +45,9 @@ _RESET_SEQ = None
 
 _MULTIPLEXERS = {}
 
+#: Unique ID per QA run
+_RUN_UUID = utils.NewUUID()
+
 
 def _SetupColours():
   """Initializes the colour constants.
@@ -374,6 +377,38 @@ def GetNodeInstances(node, secondaries=False):
   return instances
 
 
+def CheckInstance(instance, running=True):
+  """Instance check.
+
+  """
+  script = qa_config.GetInstanceCheckScript()
+  if not script:
+    return
+
+  master_node = qa_config.GetMasterNode()
+
+  # Build command to connect to master node
+  master_ssh = GetSSHCommand(master_node["primary"], "--")
+
+  if running:
+    running_shellval = "1"
+  else:
+    running_shellval = ""
+
+  args = [script, instance["name"]]
+  env = {
+    "PATH": constants.HOOKS_PATH,
+    "RUN_UUID": _RUN_UUID,
+    "MASTER_SSH": utils.ShellQuoteArgs(master_ssh),
+    "INSTANCE_NAME": instance["name"],
+    "INSTANCE_RUNNING": running_shellval,
+    }
+
+  result = os.spawnve(os.P_WAIT, script, args, env)
+  if result != 0:
+    raise qa_error.Error("Instance check failed with result %s" % result)
+
+
 def _SelectQueryFields(rnd, fields):
   """Generates a list of fields for query tests.
 
-- 
1.7.6

Reply via email to