This test used to leave a node with orphan logical volumes and unused DRBD
minors. Now, the expected leftovers are cleaned up after the test, and
cluster-verify is run.

Signed-off-by: Bernardo Dal Seno <[email protected]>
---
 qa/ganeti-qa.py   | 12 +++++-------
 qa/qa_instance.py | 38 ++++++++++++++++++++++++++++++++++++--
 2 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/qa/ganeti-qa.py b/qa/ganeti-qa.py
index 62f9550..5d6a73b 100755
--- a/qa/ganeti-qa.py
+++ b/qa/ganeti-qa.py
@@ -619,17 +619,15 @@ def RunQa():
       pnode = qa_config.AcquireNode(exclude=snode)
       try:
         instance = qa_instance.TestInstanceAddWithDrbdDisk([pnode, snode])
-        qa_node.MakeNodeOffline(snode, "yes")
-        try:
-          RunTest(qa_instance.TestInstanceRemove, instance)
-        finally:
-          qa_node.MakeNodeOffline(snode, "no")
+        set_offline = lambda node: qa_node.MakeNodeOffline(node, "yes")
+        set_online = lambda node: qa_node.MakeNodeOffline(node, "no")
+        RunTest(qa_instance.TestRemoveInstanceOfflineNode, instance, snode,
+                set_offline, set_online)
       finally:
         qa_config.ReleaseNode(pnode)
     finally:
       qa_config.ReleaseNode(snode)
-    # FIXME: This test leaves a DRBD device and two LVs behind
-    # Cluster-verify would fail
+    qa_cluster.AssertClusterVerify()
 
   RunTestIf("create-cluster", qa_node.TestNodeRemoveAll)
 
diff --git a/qa/qa_instance.py b/qa/qa_instance.py
index 2578f0c..bcc464a 100644
--- a/qa/qa_instance.py
+++ b/qa/qa_instance.py
@@ -89,9 +89,12 @@ def _GetInstanceInfo(instance):
 
   @type instance: string
   @param instance: the instance name
-  @return: a dictionary with two keys:
+  @return: a dictionary with the following keys:
       - "nodes": instance nodes, a list of strings
       - "volumes": instance volume IDs, a list of strings
+      - "drbd-minors": DRBD minors used by the instance, a dictionary where
+        keys are nodes, and values are lists of integers (or an empty
+        dictionary for non-DRBD instances)
 
   """
   master = qa_config.GetMasterNode()
@@ -106,8 +109,10 @@ def _GetInstanceInfo(instance):
   # FIXME This works with no more than 2 secondaries
   re_nodelist = re.compile(node_elem + "(?:," + node_elem + ")?$")
   re_vol = re.compile(r"^\s+logical_id:\s+(\S+)$")
+  re_drbdnode = re.compile(r"^\s+node[AB]:\s+([^\s,]+),\s+minor=([0-9]+)$")
   nodes = []
   vols = []
+  drbd_min = {}
   for line in info_out.splitlines():
     m = re_node.match(line)
     if m:
@@ -120,9 +125,17 @@ def _GetInstanceInfo(instance):
     m = re_vol.match(line)
     if m:
       vols.append(m.group(1))
+    m = re_drbdnode.match(line)
+    if m:
+      node = m.group(1)
+      minor = int(m.group(2))
+      if drbd_min.get(node) is not None:
+        drbd_min[node].append(minor)
+      else:
+        drbd_min[node] = [minor]
   assert vols
   assert nodes
-  return {"nodes": nodes, "volumes": vols}
+  return {"nodes": nodes, "volumes": vols, "drbd-minors": drbd_min}
 
 
 def _DestroyInstanceVolumes(instance):
@@ -698,3 +711,24 @@ def TestBackupList(expnode):
 def TestBackupListFields():
   """gnt-backup list-fields"""
   qa_utils.GenericQueryFieldsTest("gnt-backup", query.EXPORT_FIELDS.keys())
+
+
+def TestRemoveInstanceOfflineNode(instance, snode, set_offline, set_online):
+  """gtn-instance remove with an off-line node
+
+  @param instance: instance
+  @param snode: secondary node, to be set offline
+  @param set_offline: function to call to set the node off-line
+  @param set_online: function to call to set the node on-line
+
+  """
+  info = _GetInstanceInfo(instance["name"])
+  set_offline(snode)
+  try:
+    TestInstanceRemove(instance)
+  finally:
+    set_online(snode)
+  # Clean up the disks on the offline node
+  for minor in info["drbd-minors"][snode["primary"]]:
+    AssertCommand(["drbdsetup", str(minor), "down"], node=snode)
+  AssertCommand(["lvremove", "-f"] + info["volumes"], node=snode)
-- 
1.8.1

Reply via email to