By default it'll now evacuate all instances from the node, not
just secondaries.
---
 lib/cli.py             |   13 ++++++++
 lib/client/gnt_node.py |   73 +++++++++++++++++++++++++++++++-----------------
 man/gnt-node.rst       |   11 ++++--
 3 files changed, 67 insertions(+), 30 deletions(-)

diff --git a/lib/cli.py b/lib/cli.py
index aba1f1b..6d260ed 100644
--- a/lib/cli.py
+++ b/lib/cli.py
@@ -143,6 +143,7 @@ __all__ = [
   "POWER_DELAY_OPT",
   "PREALLOC_WIPE_DISKS_OPT",
   "PRIMARY_IP_VERSION_OPT",
+  "PRIMARY_ONLY_OPT",
   "PRIORITY_OPT",
   "RAPI_CERT_OPT",
   "READD_OPT",
@@ -152,6 +153,7 @@ __all__ = [
   "RESERVED_LVS_OPT",
   "ROMAN_OPT",
   "SECONDARY_IP_OPT",
+  "SECONDARY_ONLY_OPT",
   "SELECT_OS_OPT",
   "SEP_OPT",
   "SHOWCMD_OPT",
@@ -1205,6 +1207,17 @@ NO_REMEMBER_OPT = cli_option("--no-remember",
                              help="Perform but do not record the change"
                              " in the configuration")
 
+PRIMARY_ONLY_OPT = cli_option("-p", "--primary-only",
+                              default=False, action="store_true",
+                              help="Evacuate primary instances only")
+
+SECONDARY_ONLY_OPT = cli_option("-s", "--secondary-only",
+                                default=False, action="store_true",
+                                help="Evacuate secondary instances only"
+                                     " (applies only to internally mirrored"
+                                     " disk templates, e.g. %s)" %
+                                     utils.CommaJoin(constants.DTS_INT_MIRROR))
+
 
 #: Options provided by all commands
 COMMON_OPTS = [DEBUG_OPT]
diff --git a/lib/client/gnt_node.py b/lib/client/gnt_node.py
index a56e449..7da4e66 100644
--- a/lib/client/gnt_node.py
+++ b/lib/client/gnt_node.py
@@ -26,6 +26,8 @@
 # W0614: Unused import %s from wildcard import (since we need cli)
 # C0103: Invalid name gnt-node
 
+import itertools
+
 from ganeti.cli import *
 from ganeti import cli
 from ganeti import bootstrap
@@ -263,47 +265,66 @@ def EvacuateNode(opts, args):
   @return: the desired exit code
 
   """
+  if opts.dst_node is not None:
+    ToStderr("New secondary node given (disabling iallocator), hence 
evacuating"
+             " secondary instances only.")
+    opts.secondary_only = True
+    opts.primary_only = False
+
+  if opts.secondary_only and opts.primary_only:
+    raise errors.OpPrereqError("Only one of the --primary-only and"
+                               " --secondary-only options can be passed",
+                               errors.ECODE_INVAL)
+
   cl = GetClient()
-  force = opts.force
 
-  dst_node = opts.dst_node
-  iallocator = opts.iallocator
+  # Determine affected instances
+  fields = []
 
-  op = opcodes.OpNodeEvacStrategy(nodes=args,
-                                  iallocator=iallocator,
-                                  remote_node=dst_node)
+  if not opts.secondary_only:
+    fields.append("pinst_list")
+  if not opts.primary_only:
+    fields.append("sinst_list")
 
-  result = SubmitOpCode(op, cl=cl, opts=opts)
-  if not result:
-    # no instances to migrate
-    ToStderr("No secondary instances on node(s) %s, exiting.",
+  result = cl.QueryNodes(names=args, fields=fields, use_locking=False)
+  instances = set(itertools.chain(*itertools.chain(*itertools.chain(result))))
+
+  if not instances:
+    # No instances to evacuate
+    ToStderr("No instances to evacuate on node(s) %s, exiting.",
              utils.CommaJoin(args))
     return constants.EXIT_SUCCESS
 
-  if not force and not AskUser("Relocate instance(s) %s from node(s) %s?" %
-                               (",".join("'%s'" % name[0] for name in result),
-                               utils.CommaJoin(args))):
+  if not (opts.force or
+          AskUser("Relocate instance(s) %s from node(s) %s?" %
+                  (utils.CommaJoin(utils.NiceSort(instances)),
+                   utils.CommaJoin(args)))):
     return constants.EXIT_CONFIRMATION
 
+  # Evacuate node
+  op = opcodes.OpNodeEvacuate(nodes=args,
+                              remote_node=opts.dst_node,
+                              iallocator=opts.iallocator,
+                              primary=not opts.secondary_only,
+                              secondary=not opts.primary_only,
+                              early_release=opts.early_release)
+  result = SubmitOpCode(op, cl=cl, opts=opts)
+
+  # Keep track of submitted jobs
   jex = JobExecutor(cl=cl, opts=opts)
-  for row in result:
-    iname = row[0]
-    node = row[1]
-    ToStdout("Will relocate instance %s to node %s", iname, node)
-    op = opcodes.OpInstanceReplaceDisks(instance_name=iname,
-                                        remote_node=node, disks=[],
-                                        mode=constants.REPLACE_DISK_CHG,
-                                        early_release=opts.early_release)
-    jex.QueueJob(iname, op)
+
+  for (status, job_id) in result[constants.JOB_IDS_KEY]:
+    jex.AddJobId(None, status, job_id)
+
   results = jex.GetResults()
   bad_cnt = len([row for row in results if not row[0]])
   if bad_cnt == 0:
-    ToStdout("All %d instance(s) failed over successfully.", len(results))
+    ToStdout("All instances evacuated successfully.")
     rcode = constants.EXIT_SUCCESS
   else:
-    ToStdout("There were errors during the failover:\n"
-             "%d error(s) out of %d instance(s).", bad_cnt, len(results))
+    ToStdout("There were %s errors during the evacuation.", bad_cnt)
     rcode = constants.EXIT_FAILURE
+
   return rcode
 
 
@@ -838,7 +859,7 @@ commands = {
   'evacuate': (
     EvacuateNode, [ArgNode(min=1)],
     [FORCE_OPT, IALLOCATOR_OPT, NEW_SECONDARY_OPT, EARLY_RELEASE_OPT,
-     PRIORITY_OPT],
+     PRIORITY_OPT, PRIMARY_ONLY_OPT, SECONDARY_ONLY_OPT],
     "[-f] {-I <iallocator> | -n <dst>} <node>",
     "Relocate the secondary instances from a node"
     " to other nodes (only for instances with drbd disk template)"),
diff --git a/man/gnt-node.rst b/man/gnt-node.rst
index aa3a9f5..86683e9 100644
--- a/man/gnt-node.rst
+++ b/man/gnt-node.rst
@@ -87,10 +87,14 @@ EVACUATE
 ~~~~~~~~
 
 **evacuate** [-f] [--early-release] [--iallocator *NAME* \|
---new-secondary *destination\_node*] {*node*...}
+--new-secondary *destination\_node*]
+[--primary-only \| --secondary-only] [--early-release] {*node*...}
 
-This command will move all secondary instances away from the given
-node(s). It works only for instances having a drbd disk template.
+This command will move instances away from the given node(s). If
+``--primary-only`` is given, only primary instances are evacuated, with
+``--secondary-only`` only secondaries. If neither is given, all
+instances are evacuated. It works only for instances having a drbd disk
+template.
 
 The new location for the instances can be specified in two ways:
 
@@ -101,7 +105,6 @@ The new location for the instances can be specified in two 
ways:
   parameter, so each instance will be in turn placed on the (per the
   script) optimal node
 
-
 The ``--early-release`` changes the code so that the old storage on
 node being evacuated is removed early (before the resync is
 completed) and the internal Ganeti locks are also released for both
-- 
1.7.3.5

Reply via email to