From 8d8d650c475c0b1882433410e71eb9a0ee52b6bd Mon Sep 17 00:00:00 2001
From: Michael Hanselmann <hansmi@google.com>
Date: Fri, 29 Jan 2010 18:44:42 +0100
Subject: [PATCH] Add command line options for instance removal on export

Signed-off-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: Iustin Pop <iustin@google.com>
---
 lib/cli.py          | 14 ++++++++++++++
 lib/objects.py      |  1 +
 man/gnt-backup.sgml |  8 ++++++++
 qa/ganeti-qa.py     | 13 +++++++++++++
 qa/qa_instance.py   | 10 ++++++++++
 scripts/gnt-backup  | 11 +++++++++--
 6 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/lib/cli.py b/lib/cli.py
index 14be846b6..3c5e97a40 100644
--- a/lib/cli.py
+++ b/lib/cli.py
@@ -70,6 +70,7 @@ __all__ = [
   "IALLOCATOR_OPT",
   "IGNORE_CONSIST_OPT",
   "IGNORE_FAILURES_OPT",
+  "IGNORE_REMOVE_FAILURES_OPT",
   "IGNORE_SECONDARIES_OPT",
   "IGNORE_SIZE_OPT",
   "MAC_PREFIX_OPT",
@@ -101,6 +102,7 @@ __all__ = [
   "OS_SIZE_OPT",
   "READD_OPT",
   "REBOOT_TYPE_OPT",
+  "REMOVE_INSTANCE_OPT",
   "SECONDARY_IP_OPT",
   "SELECT_OS_OPT",
   "SEP_OPT",
@@ -684,6 +686,18 @@ IGNORE_FAILURES_OPT = cli_option("--ignore-failures", dest="ignore_failures",
                                  " configuration even if there are failures"
                                  " during the removal process")
 
+IGNORE_REMOVE_FAILURES_OPT = cli_option("--ignore-remove-failures",
+                                        dest="ignore_remove_failures",
+                                        action="store_true", default=False,
+                                        help="Remove the instance from the"
+                                        " cluster configuration even if there"
+                                        " are failures during the removal"
+                                        " process")
+
+REMOVE_INSTANCE_OPT = cli_option("--remove-instance", dest="remove_instance",
+                                 action="store_true", default=False,
+                                 help="Remove the instance from the cluster")
+
 NEW_SECONDARY_OPT = cli_option("-n", "--new-secondary", dest="dst_node",
                                help="Specifies the new secondary node",
                                metavar="NODE", default=None,
diff --git a/lib/objects.py b/lib/objects.py
index 76ca042c4..81cde182d 100644
--- a/lib/objects.py
+++ b/lib/objects.py
@@ -48,6 +48,7 @@ __all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance",
 _TIMESTAMPS = ["ctime", "mtime"]
 _UUID = ["uuid"]
 
+
 def FillDict(defaults_dict, custom_dict, skip_keys=None):
   """Basic function to apply settings on top a default dict.
 
diff --git a/man/gnt-backup.sgml b/man/gnt-backup.sgml
index d9c1fe74b..ff6c3cee9 100644
--- a/man/gnt-backup.sgml
+++ b/man/gnt-backup.sgml
@@ -65,6 +65,8 @@
         <arg choice="req">-n <replaceable>node</replaceable></arg>
         <arg>--shutdown-timeout=<replaceable>N</replaceable></arg>
         <arg>--noshutdown</arg>
+        <arg>--remove-instance</arg>
+        <arg>--ignore-remove-failures</arg>
         <arg choice="req"><replaceable>instance</replaceable></arg>
 
       </cmdsynopsis>
@@ -91,6 +93,12 @@
         in the exported dump.
       </para>
 
+      <para>
+        The <option>--remove</option> option can be used to remove the
+        instance after it was exported. This is useful to make one last
+        backup before removing the instance.
+      </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
diff --git a/qa/ganeti-qa.py b/qa/ganeti-qa.py
index 5c399e72c..990418538 100755
--- a/qa/ganeti-qa.py
+++ b/qa/ganeti-qa.py
@@ -169,6 +169,7 @@ def RunCommonInstanceTests(instance):
   if qa_rapi.Enabled():
     RunTest(qa_rapi.TestInstance, instance)
 
+
 def RunExportImportTests(instance, pnode):
   """Tries to export and import the instance.
 
@@ -312,6 +313,18 @@ def main():
         finally:
           qa_config.ReleaseNode(snode)
 
+    if (qa_config.TestEnabled('instance-add-plain-disk') and
+        qa_config.TestEnabled("instance-export")):
+      instance = RunTest(qa_instance.TestInstanceAddWithPlainDisk, pnode)
+      expnode = qa_config.AcquireNode(exclude=pnode)
+      try:
+        RunTest(qa_instance.TestInstanceExportWithRemove, instance, expnode)
+        RunTest(qa_instance.TestBackupList, expnode)
+      finally:
+        qa_config.ReleaseNode(expnode)
+      del expnode
+      del instance
+
   finally:
     qa_config.ReleaseNode(pnode)
 
diff --git a/qa/qa_instance.py b/qa/qa_instance.py
index af3afeb6a..b0d9a6574 100644
--- a/qa/qa_instance.py
+++ b/qa/qa_instance.py
@@ -249,6 +249,16 @@ def TestInstanceExport(instance, node):
   return qa_utils.ResolveInstanceName(instance)
 
 
+def TestInstanceExportWithRemove(instance, node):
+  """gnt-backup export --remove-instance"""
+  master = qa_config.GetMasterNode()
+
+  cmd = ['gnt-backup', 'export', '-n', node['primary'], "--remove-instance",
+         instance['name']]
+  AssertEqual(StartSSH(master['primary'],
+                       utils.ShellQuoteArgs(cmd)).wait(), 0)
+
+
 def TestInstanceImport(node, newinst, expnode, name):
   """gnt-backup import"""
   master = qa_config.GetMasterNode()
diff --git a/scripts/gnt-backup b/scripts/gnt-backup
index fd04a9295..fb1b767cf 100755
--- a/scripts/gnt-backup
+++ b/scripts/gnt-backup
@@ -71,10 +71,14 @@ def ExportInstance(opts, args):
   @return: the desired exit code
 
   """
+  ignore_remove_failures = opts.ignore_remove_failures
+
   op = opcodes.OpExportInstance(instance_name=args[0],
                                 target_node=opts.node,
                                 shutdown=opts.shutdown,
-                                shutdown_timeout=opts.shutdown_timeout)
+                                shutdown_timeout=opts.shutdown_timeout,
+                                remove_instance=opts.remove_instance,
+                                ignore_remove_failures=ignore_remove_failures)
 
   fin_resu, dlist = SubmitOpCode(op)
   if not isinstance(dlist, list):
@@ -97,6 +101,7 @@ def ExportInstance(opts, args):
     rcode = 1
   return rcode
 
+
 def ImportInstance(opts, args):
   """Add an instance to the cluster.
 
@@ -144,6 +149,7 @@ import_opts = [
   SUBMIT_OPT,
   ]
 
+
 commands = {
   'list': (
     PrintExportList, ARGS_NONE,
@@ -151,7 +157,8 @@ commands = {
     "", "Lists instance exports available in the ganeti cluster"),
   'export': (
     ExportInstance, ARGS_ONE_INSTANCE,
-    [FORCE_OPT, SINGLE_NODE_OPT, NOSHUTDOWN_OPT, SHUTDOWN_TIMEOUT_OPT],
+    [FORCE_OPT, SINGLE_NODE_OPT, NOSHUTDOWN_OPT, SHUTDOWN_TIMEOUT_OPT,
+     REMOVE_INSTANCE_OPT, IGNORE_REMOVE_FAILURES_OPT],
     "-n <target_node> [opts...] <name>",
     "Exports an instance to an image"),
   'import': (
-- 
GitLab