Revision aafee533

b/lib/cli.py
143 143
  "POWER_DELAY_OPT",
144 144
  "PREALLOC_WIPE_DISKS_OPT",
145 145
  "PRIMARY_IP_VERSION_OPT",
146
  "PRIMARY_ONLY_OPT",
146 147
  "PRIORITY_OPT",
147 148
  "RAPI_CERT_OPT",
148 149
  "READD_OPT",
......
152 153
  "RESERVED_LVS_OPT",
153 154
  "ROMAN_OPT",
154 155
  "SECONDARY_IP_OPT",
156
  "SECONDARY_ONLY_OPT",
155 157
  "SELECT_OS_OPT",
156 158
  "SEP_OPT",
157 159
  "SHOWCMD_OPT",
......
1210 1212
                             help="Perform but do not record the change"
1211 1213
                             " in the configuration")
1212 1214

  
1215
PRIMARY_ONLY_OPT = cli_option("-p", "--primary-only",
1216
                              default=False, action="store_true",
1217
                              help="Evacuate primary instances only")
1218

  
1219
SECONDARY_ONLY_OPT = cli_option("-s", "--secondary-only",
1220
                                default=False, action="store_true",
1221
                                help="Evacuate secondary instances only"
1222
                                     " (applies only to internally mirrored"
1223
                                     " disk templates, e.g. %s)" %
1224
                                     utils.CommaJoin(constants.DTS_INT_MIRROR))
1225

  
1213 1226

  
1214 1227
#: Options provided by all commands
1215 1228
COMMON_OPTS = [DEBUG_OPT]
b/lib/client/gnt_node.py
26 26
# W0614: Unused import %s from wildcard import (since we need cli)
27 27
# C0103: Invalid name gnt-node
28 28

  
29
import itertools
30

  
29 31
from ganeti.cli import *
30 32
from ganeti import cli
31 33
from ganeti import bootstrap
......
263 265
  @return: the desired exit code
264 266

  
265 267
  """
266
  cl = GetClient()
267
  force = opts.force
268
  if opts.dst_node is not None:
269
    ToStderr("New secondary node given (disabling iallocator), hence evacuating"
270
             " secondary instances only.")
271
    opts.secondary_only = True
272
    opts.primary_only = False
273

  
274
  if opts.secondary_only and opts.primary_only:
275
    raise errors.OpPrereqError("Only one of the --primary-only and"
276
                               " --secondary-only options can be passed",
277
                               errors.ECODE_INVAL)
278
  elif opts.primary_only:
279
    mode = constants.IALLOCATOR_NEVAC_PRI
280
  elif opts.secondary_only:
281
    mode = constants.IALLOCATOR_NEVAC_SEC
282
  else:
283
    mode = constants.IALLOCATOR_NEVAC_ALL
268 284

  
269
  dst_node = opts.dst_node
270
  iallocator = opts.iallocator
285
  # Determine affected instances
286
  fields = []
271 287

  
272
  op = opcodes.OpNodeEvacStrategy(nodes=args,
273
                                  iallocator=iallocator,
274
                                  remote_node=dst_node)
288
  if not opts.secondary_only:
289
    fields.append("pinst_list")
290
  if not opts.primary_only:
291
    fields.append("sinst_list")
275 292

  
276
  result = SubmitOpCode(op, cl=cl, opts=opts)
277
  if not result:
278
    # no instances to migrate
279
    ToStderr("No secondary instances on node(s) %s, exiting.",
293
  cl = GetClient()
294

  
295
  result = cl.QueryNodes(names=args, fields=fields, use_locking=False)
296
  instances = set(itertools.chain(*itertools.chain(*itertools.chain(result))))
297

  
298
  if not instances:
299
    # No instances to evacuate
300
    ToStderr("No instances to evacuate on node(s) %s, exiting.",
280 301
             utils.CommaJoin(args))
281 302
    return constants.EXIT_SUCCESS
282 303

  
283
  if not force and not AskUser("Relocate instance(s) %s from node(s) %s?" %
284
                               (",".join("'%s'" % name[0] for name in result),
285
                               utils.CommaJoin(args))):
304
  if not (opts.force or
305
          AskUser("Relocate instance(s) %s from node(s) %s?" %
306
                  (utils.CommaJoin(utils.NiceSort(instances)),
307
                   utils.CommaJoin(args)))):
286 308
    return constants.EXIT_CONFIRMATION
287 309

  
310
  # Evacuate node
311
  op = opcodes.OpNodeEvacuate(node_name=args[0], mode=mode,
312
                              remote_node=opts.dst_node,
313
                              iallocator=opts.iallocator,
314
                              early_release=opts.early_release)
315
  result = SubmitOpCode(op, cl=cl, opts=opts)
316

  
317
  # Keep track of submitted jobs
288 318
  jex = JobExecutor(cl=cl, opts=opts)
289
  for row in result:
290
    iname = row[0]
291
    node = row[1]
292
    ToStdout("Will relocate instance %s to node %s", iname, node)
293
    op = opcodes.OpInstanceReplaceDisks(instance_name=iname,
294
                                        remote_node=node, disks=[],
295
                                        mode=constants.REPLACE_DISK_CHG,
296
                                        early_release=opts.early_release)
297
    jex.QueueJob(iname, op)
319

  
320
  for (status, job_id) in result[constants.JOB_IDS_KEY]:
321
    jex.AddJobId(None, status, job_id)
322

  
298 323
  results = jex.GetResults()
299 324
  bad_cnt = len([row for row in results if not row[0]])
300 325
  if bad_cnt == 0:
301
    ToStdout("All %d instance(s) failed over successfully.", len(results))
326
    ToStdout("All instances evacuated successfully.")
302 327
    rcode = constants.EXIT_SUCCESS
303 328
  else:
304
    ToStdout("There were errors during the failover:\n"
305
             "%d error(s) out of %d instance(s).", bad_cnt, len(results))
329
    ToStdout("There were %s errors during the evacuation.", bad_cnt)
306 330
    rcode = constants.EXIT_FAILURE
331

  
307 332
  return rcode
308 333

  
309 334

  
......
835 860
    " [--no-node-setup] [--verbose]"
836 861
    " <node_name>",
837 862
    "Add a node to the cluster"),
838
  'evacuate': (
839
    EvacuateNode, [ArgNode(min=1)],
863
  "evacuate": (
864
    EvacuateNode, ARGS_ONE_NODE,
840 865
    [FORCE_OPT, IALLOCATOR_OPT, NEW_SECONDARY_OPT, EARLY_RELEASE_OPT,
841
     PRIORITY_OPT],
866
     PRIORITY_OPT, PRIMARY_ONLY_OPT, SECONDARY_ONLY_OPT],
842 867
    "[-f] {-I <iallocator> | -n <dst>} <node>",
843 868
    "Relocate the secondary instances from a node"
844
    " to other nodes (only for instances with drbd disk template)"),
869
    " to other nodes"),
845 870
  'failover': (
846 871
    FailoverNode, ARGS_ONE_NODE, [FORCE_OPT, IGNORE_CONSIST_OPT,
847 872
                                  IALLOCATOR_OPT, PRIORITY_OPT],
b/man/gnt-node.rst
87 87
~~~~~~~~
88 88

  
89 89
**evacuate** [-f] [--early-release] [--iallocator *NAME* \|
90
--new-secondary *destination\_node*] {*node*...}
90
--new-secondary *destination\_node*]
91
[--primary-only \| --secondary-only] [--early-release] {*node*}
91 92

  
92
This command will move all secondary instances away from the given
93
node(s). It works only for instances having a drbd disk template.
93
This command will move instances away from the given node. If
94
``--primary-only`` is given, only primary instances are evacuated, with
95
``--secondary-only`` only secondaries. If neither is given, all
96
instances are evacuated. It works only for instances having a drbd disk
97
template.
94 98

  
95 99
The new location for the instances can be specified in two ways:
96 100

  
......
101 105
  parameter, so each instance will be in turn placed on the (per the
102 106
  script) optimal node
103 107

  
104

  
105 108
The ``--early-release`` changes the code so that the old storage on
106 109
node being evacuated is removed early (before the resync is
107 110
completed) and the internal Ganeti locks are also released for both

Also available in: Unified diff