From 084f05a5176cb3ea47e5f5b7f711ace5b8bdef07 Mon Sep 17 00:00:00 2001
From: Iustin Pop <iustin@google.com>
Date: Wed, 5 Aug 2009 13:02:48 +0200
Subject: [PATCH] export: add meaningful exit code
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Currently β€˜gnt-backup export’ always returns exit code zero, even in the
face of complete failure during backup (only failure to stop/start the
instance will cause job failure and thus non-zero exit code). This is
bad, since one cannot script the backup.

This patch adds some simple results from the LU so that the command line
script can return good exit code. It will:
  - return zero for full success (snapshot removal errors are ignored
    though)
  - return one for full failure (finalize export failure or all disks
    failure)
  - return two for partial failure (some disks backed up, some not)

Signed-off-by: Iustin Pop <iustin@google.com>
Reviewed-by: Michael Hanselmann <hansmi@google.com>
---
 lib/cmdlib.py       | 10 ++++++++++
 man/gnt-backup.sgml | 10 ++++++++++
 scripts/gnt-backup  | 23 +++++++++++++++++++++--
 3 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 951621d26..cea239bd0 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -6472,6 +6472,8 @@ class LUExportInstance(LogicalUnit):
     for disk in instance.disks:
       self.cfg.SetDiskID(disk, src_node)
 
+    # per-disk results
+    dresults = []
     try:
       for idx, disk in enumerate(instance.disks):
         # new_dev_name will be a snapshot of an lvm leaf of the one we passed
@@ -6505,15 +6507,22 @@ class LUExportInstance(LogicalUnit):
         if result.failed or not result.data:
           self.LogWarning("Could not export disk/%d from node %s to"
                           " node %s", idx, src_node, dst_node.name)
+          dresults.append(False)
+        else:
+          dresults.append(True)
         msg = self.rpc.call_blockdev_remove(src_node, dev).RemoteFailMsg()
         if msg:
           self.LogWarning("Could not remove snapshot for disk/%d from node"
                           " %s: %s", idx, src_node, msg)
+      else:
+        dresults.append(False)
 
     result = self.rpc.call_finalize_export(dst_node.name, instance, snap_disks)
+    fin_resu = True
     if result.failed or not result.data:
       self.LogWarning("Could not finalize export for instance %s on node %s",
                       instance.name, dst_node.name)
+      fin_resu = False
 
     nodelist = self.cfg.GetNodeList()
     nodelist.remove(dst_node.name)
@@ -6530,6 +6539,7 @@ class LUExportInstance(LogicalUnit):
           if not self.rpc.call_export_remove(node, instance.name):
             self.LogWarning("Could not remove older export for instance %s"
                             " on node %s", instance.name, node)
+    return fin_resu, dresults
 
 
 class LURemoveExport(NoHooksLU):
diff --git a/man/gnt-backup.sgml b/man/gnt-backup.sgml
index d432b6ef6..ae3122ac6 100644
--- a/man/gnt-backup.sgml
+++ b/man/gnt-backup.sgml
@@ -82,6 +82,16 @@
         in the exported dump.
       </para>
 
+      <para>
+        The exit code of the command is 0 if all disks were backed up
+        successfully, 1 if no data was backed up or if the
+        configuration export failed, and 2 if just some of the disks
+        failed to backup. The exact details of the failures will be
+        shown during the command execution (and will be stored in the
+        job log). It is recommended that for any non-zero exit code,
+        the backup is considered invalid, and retried.
+      </para>
+
       <para>
         Example:
         <screen>
diff --git a/scripts/gnt-backup b/scripts/gnt-backup
index 467b37d0a..cbbde51f4 100755
--- a/scripts/gnt-backup
+++ b/scripts/gnt-backup
@@ -74,8 +74,27 @@ def ExportInstance(opts, args):
                                 target_node=opts.node,
                                 shutdown=opts.shutdown)
 
-  SubmitOpCode(op)
-
+  fin_resu, dlist = SubmitOpCode(op)
+  if not isinstance(dlist, list):
+    ToStderr("Cannot parse execution results")
+    return 1
+  tot_dsk = len(dlist)
+  # TODO: handle diskless instances
+  if dlist.count(False) == 0:
+    # all OK
+    rcode = 0
+  elif dlist.count(True) == 0:
+    ToStderr("Error: No disks were backed up successfully."
+             " The export doesn't have any valid data,"
+             " it is recommended to retry the operation.")
+    rcode = 1
+  else:
+    ToStderr("Partial export failure: %d disks backed up, %d disks failed.",
+             dlist.count(True), dlist.count(False))
+    rcode = 2
+  if not fin_resu:
+    rcode = 1
+  return rcode
 
 def ImportInstance(opts, args):
   """Add an instance to the cluster.
-- 
GitLab