From c4ed32cb662cf0437124568ccb7596846542d661 Mon Sep 17 00:00:00 2001
From: Iustin Pop <iustin@google.com>
Date: Tue, 3 Feb 2009 14:45:53 +0000
Subject: [PATCH] Allow gnt-node evacuate to use an iallocator

This is a partial implementation of fully automated node evacuation:
we allow passing an iallocator and all instance replace-disks will be
execute via that iallocator.

The individual OpReplaceDisks opcodes are submitted in a single job,
which causes them to be executed serially and thus keeps the iallocator
runs consistent. This also changes the behaviour so that the first
reallocation that failed will stop all the reallocations.

Reviewed-by: ultrotter
---
 scripts/gnt-node | 76 ++++++++++++++++++++++++++++--------------------
 1 file changed, 45 insertions(+), 31 deletions(-)

diff --git a/scripts/gnt-node b/scripts/gnt-node
index e35a8a43e..7eb7e6246 100755
--- a/scripts/gnt-node
+++ b/scripts/gnt-node
@@ -27,6 +27,7 @@ import sys
 from optparse import make_option
 
 from ganeti.cli import *
+from ganeti import cli
 from ganeti import opcodes
 from ganeti import utils
 from ganeti import constants
@@ -169,52 +170,57 @@ def EvacuateNode(opts, args):
   """
   cl = GetClient()
   force = opts.force
+
+  dst_node = opts.dst_node
+  iallocator = opts.iallocator
+
+  cnt = [dst_node, iallocator].count(None)
+  if cnt != 1:
+    raise errors.OpPrereqError("One and only one of the -n and -i"
+                               " options must be passed")
+
   selected_fields = ["name", "sinst_list"]
-  src_node, dst_node = args
+  src_node = args[0]
 
   op = opcodes.OpQueryNodes(output_fields=selected_fields, names=[src_node])
-  result = SubmitOpCode(op, cl=cl)
+  result = cl.QueryNodes(names=[src_node], fields=selected_fields)
   src_node, sinst = result[0]
-  op = opcodes.OpQueryNodes(output_fields=["name"], names=[dst_node])
-  result = SubmitOpCode(op, cl=cl)
-  dst_node = result[0][0]
-
-  if src_node == dst_node:
-    raise errors.OpPrereqError("Evacuate node needs different source and"
-                               " target nodes (node %s given twice)" %
-                               src_node)
 
   if not sinst:
     ToStderr("No secondary instances on node %s, exiting.", src_node)
     return constants.EXIT_SUCCESS
 
+  if dst_node is not None:
+    result = cl.QueryNodes(names=[dst_node], fields=["name"])
+    dst_node = result[0][0]
+
+    if src_node == dst_node:
+      raise errors.OpPrereqError("Evacuate node needs different source and"
+                                 " target nodes (node %s given twice)" %
+                                 src_node)
+    txt_msg = "to node %s" % dst_node
+  else:
+    txt_msg = "using iallocator %s" % iallocator
+
   sinst = utils.NiceSort(sinst)
 
   if not force and not AskUser("Relocate instance(s) %s from node\n"
-                               " %s to node\n %s?" %
+                               " %s %s?" %
                                (",".join("'%s'" % name for name in sinst),
-                               src_node, dst_node)):
+                               src_node, txt_msg)):
     return constants.EXIT_CONFIRMATION
 
-  jex = JobExecutor()
+  ops = []
   for iname in sinst:
     op = opcodes.OpReplaceDisks(instance_name=iname,
                                 remote_node=dst_node,
                                 mode=constants.REPLACE_DISK_CHG,
+                                iallocator=iallocator,
                                 disks=[])
-    jex.QueueJob(iname, op)
+    ops.append(op)
 
-  results = jex.GetResults()
-
-  bad_cnt = len([row for row in results if not row[0]])
-  if bad_cnt == 0:
-    ToStdout("All %d instance(s) relocated successfully.", len(results))
-    retcode = constants.EXIT_SUCCESS
-  else:
-    ToStdout("There were errors during the relocation:\n"
-             "%d error(s) out of %d instance(s).", bad_cnt, len(results))
-    retcode = constants.EXIT_FAILURE
-  return retcode
+  job_id = cli.SendJob(ops, cl=cl)
+  cli.PollJob(job_id, cl=cl)
 
 
 def FailoverNode(opts, args):
@@ -448,12 +454,20 @@ commands = {
            ],
           "[-s ip] [--readd] [--no-ssh-key-check] <node_name>",
           "Add a node to the cluster"),
-  'evacuate': (EvacuateNode, ARGS_FIXED(2),
-               [DEBUG_OPT, FORCE_OPT],
-               "[-f] <src> <dst>",
-               "Relocate the secondary instances from the first node"
-               " to the second one (only for instances with drbd disk template"
-               ),
+  'evacuate': (EvacuateNode, ARGS_ONE,
+               [DEBUG_OPT, FORCE_OPT,
+                make_option("-n", "--new-secondary", dest="dst_node",
+                            help="New secondary node", metavar="NODE",
+                            default=None),
+                make_option("-i", "--iallocator", metavar="<NAME>",
+                            help="Select new secondary for the instance"
+                            " automatically using the"
+                            " <NAME> iallocator plugin",
+                            default=None, type="string"),
+                ],
+               "[-f] {-i <iallocator> | -n <dst>} <node>",
+               "Relocate the secondary instances from a node"
+               " to other nodes (only for instances with drbd disk template)"),
   'failover': (FailoverNode, ARGS_ONE,
                [DEBUG_OPT, FORCE_OPT,
                 make_option("--ignore-consistency", dest="ignore_consistency",
-- 
GitLab