Revision fed67843

b/lib/client/gnt_node.py
114 114
                               help=("Ignore the Node(s) offline status"
115 115
                                     " (potentially DANGEROUS)"))
116 116

  
117
FORCE_MASTER_OPT = cli_option("--force-master", default=False,
118
                              action="store_true", dest="force_master",
119
                              help=("Operate on the master node too"
120
                                    " (potentially DANGEROUS)"))
121

  
122 117
OOB_TIMEOUT_OPT = cli_option("--oob-timeout", dest="oob_timeout", type="int",
123 118
                         default=constants.OOB_TIMEOUT,
124 119
                         help="Maximum time to wait for out-of-band helper")
125 120

  
126

  
127 121
def ConvertStorageType(user_storage_type):
128 122
  """Converts a user storage type to its internal name.
129 123

  
......
495 489
  @return: the desired exit code
496 490

  
497 491
  """
498
  client = GetClient()
499 492
  command = args.pop(0)
500 493

  
501 494
  if opts.no_headers:
......
507 500
    ToStderr("power subcommand %s not supported." % command)
508 501
    return constants.EXIT_FAILURE
509 502

  
510
  nodes = [node for (node, ) in client.QueryNodes(args, ["name"], False)]
511 503
  oob_command = "power-%s" % command
512 504

  
513 505
  if oob_command in _OOB_COMMAND_ASK:
514
    if not args and not opts.show_all:
515
      ToStderr("Please provide at least one node or use --all for this command"
516
               " as this is a potentially harmful command")
517
      return constants.EXIT_FAILURE
518
    elif args and opts.show_all:
519
      ToStderr("Please provide either nodes or use --all, can not use both at"
520
               " the same time")
506
    if not args:
507
      ToStderr("Please provide at least one node for this command")
521 508
      return constants.EXIT_FAILURE
522
    elif not opts.force and not ConfirmOperation(nodes, "nodes",
509
    elif not opts.force and not ConfirmOperation(args, "nodes",
523 510
                                                 "power %s" % command):
524 511
      return constants.EXIT_FAILURE
512
    assert len(args) > 0
525 513

  
526 514
  opcodelist = []
527 515
  if not opts.ignore_status and oob_command == constants.OOB_POWER_OFF:
528 516
    # TODO: This is a little ugly as we can't catch and revert
529
    for node in nodes:
517
    for node in args:
530 518
      opcodelist.append(opcodes.OpNodeSetParams(node_name=node, offline=True,
531 519
                                                auto_promote=opts.auto_promote))
532 520

  
533
  opcodelist.append(opcodes.OpOobCommand(node_names=nodes,
521
  opcodelist.append(opcodes.OpOobCommand(node_names=args,
534 522
                                         command=oob_command,
535 523
                                         ignore_status=opts.ignore_status,
536
                                         force_master=opts.force_master,
537 524
                                         timeout=opts.oob_timeout))
538 525

  
539 526
  cli.SetGenericOpcodeOpts(opcodelist, opts)
......
870 857
    [ArgChoice(min=1, max=1, choices=_LIST_POWER_COMMANDS),
871 858
     ArgNode()],
872 859
    [SUBMIT_OPT, AUTO_PROMOTE_OPT, PRIORITY_OPT, IGNORE_STATUS_OPT,
873
     FORCE_MASTER_OPT, FORCE_OPT, NOHDR_OPT, SEP_OPT, ALL_OPT,
874
     OOB_TIMEOUT_OPT],
860
     FORCE_OPT, NOHDR_OPT, SEP_OPT, OOB_TIMEOUT_OPT],
875 861
    "on|off|cycle|status [nodes...]",
876 862
    "Change power state of node by calling out-of-band helper."),
877 863
  'remove': (
b/lib/cmdlib.py
3216 3216

  
3217 3217
  """
3218 3218
  REG_BGL = False
3219
  _SKIP_MASTER = (constants.OOB_POWER_OFF, constants.OOB_POWER_CYCLE)
3219 3220

  
3220 3221
  def CheckPrereq(self):
3221 3222
    """Check prerequisites.
......
3230 3231
    self.nodes = []
3231 3232
    self.master_node = self.cfg.GetMasterNode()
3232 3233

  
3233
    if self.op.command in (constants.OOB_POWER_OFF, constants.OOB_POWER_CYCLE):
3234
      # This does two things, it checks if master is in the list and if so and
3235
      # force_master is set it puts it to the end so the master is done last
3236
      try:
3234
    if self.op.node_names:
3235
      if self.op.command in self._SKIP_MASTER:
3236
        if self.master_node in self.op.node_names:
3237
          master_node_obj = self.cfg.GetNodeInfo(self.master_node)
3238
          master_oob_handler = _SupportsOob(self.cfg, master_node_obj)
3239

  
3240
          if master_oob_handler:
3241
            additional_text = ("Run '%s %s %s' if you want to operate on the"
3242
                               " master regardless") % (master_oob_handler,
3243
                                                        self.op.command,
3244
                                                        self.master_node)
3245
          else:
3246
            additional_text = "The master node does not support out-of-band"
3247

  
3248
          raise errors.OpPrereqError(("Operating on the master node %s is not"
3249
                                      " allowed for %s\n%s") %
3250
                                     (self.master_node, self.op.command,
3251
                                      additional_text), errors.ECODE_INVAL)
3252
    else:
3253
      self.op.node_names = self.cfg.GetNodeList()
3254
      if self.op.command in self._SKIP_MASTER:
3237 3255
        self.op.node_names.remove(self.master_node)
3238
      except ValueError:
3239
        pass
3240
      else:
3241
        if self.op.force_master:
3242
          self.op.node_names.append(self.master_node)
3243
        else:
3244
          self.LogWarning("Master %s was skipped, use the force master"
3245
                          " option to operate on the master too",
3246
                          self.master_node)
3247
          if not self.op.node_names:
3248
            raise errors.OpPrereqError("No nodes left to operate on, aborting",
3249
                                       errors.ECODE_INVAL)
3250 3256

  
3251
      assert (self.master_node not in self.op.node_names or
3252
              self.op.node_names[-1] == self.master_node)
3257
    if self.op.command in self._SKIP_MASTER:
3258
      assert self.master_node not in self.op.node_names
3253 3259

  
3254 3260
    for node_name in self.op.node_names:
3255 3261
      node = self.cfg.GetNodeInfo(node_name)
......
3273 3279
    if self.op.node_names:
3274 3280
      self.op.node_names = [_ExpandNodeName(self.cfg, name)
3275 3281
                            for name in self.op.node_names]
3282
      lock_names = self.op.node_names
3276 3283
    else:
3277
      self.op.node_names = self.cfg.GetNodeList()
3284
      lock_names = locking.ALL_SET
3278 3285

  
3279 3286
    self.needed_locks = {
3280
      locking.LEVEL_NODE: self.op.node_names,
3287
      locking.LEVEL_NODE: lock_names,
3281 3288
      }
3282 3289

  
3283 3290
  def Exec(self, feedback_fn):
b/lib/opcodes.py
658 658
    ("command", None, ht.TElemOf(constants.OOB_COMMANDS), None),
659 659
    ("timeout", constants.OOB_TIMEOUT, ht.TInt, None),
660 660
    ("ignore_status", False, ht.TBool, None),
661
    ("force_master", False, ht.TBool, None),
662 661
    ]
663 662

  
664 663

  
b/qa/qa_node.py
253 253
  node = qa_config.AcquireNode(exclude=master)
254 254

  
255 255
  master_name = master["primary"]
256
  full_master_name = qa_utils.ResolveNodeName(master)
257 256
  node_name = node["primary"]
258 257
  full_node_name = qa_utils.ResolveNodeName(node)
259 258

  
......
276 275
    # Power off on master without options should fail
277 276
    AssertCommand(["gnt-node", "power", "-f", "off", master_name], fail=True)
278 277
    # With force master it should still fail
279
    AssertCommand(["gnt-node", "power", "-f", "--force-master", "off",
280
                   master_name], fail=True)
281 278
    AssertCommand(["gnt-node", "power", "-f",  "--ignore-status", "off",
282 279
                   master_name],
283 280
                  fail=True)
284
    # This should work again
285
    AssertCommand(["gnt-node", "power", "-f", "--ignore-status",
286
                   "--force-master", "off", master_name])
287
    _AssertOobCall(verify_path, "power-off %s" % full_master_name)
288 281

  
289 282
    # Verify we can't transform back to online when not yet powered on
290 283
    AssertCommand(["gnt-node", "modify", "-O", "no", node_name],
......
400 393
      AssertCommand(["gnt-node", "modify", "--node-parameters",
401 394
                     "oob_program=default", node_name])
402 395
      AssertCommand(["rm", "-f", oob_path2, verify_path2])
403

  
404
    verify_path3 = qa_utils.UploadData(master["primary"], "")
405
    oob_script = ("#!/bin/bash\n"
406
                  "echo \"$@\" >> %s\n") % verify_path3
407
    oob_path3 = qa_utils.UploadData(master["primary"], oob_script, mode=0700)
408

  
409
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
410
                   "oob_program=%s" % oob_path3])
411

  
412
    non_master_nodes = []
413
    for node in qa_config.get('nodes'):
414
      if node != master:
415
        non_master_nodes.append(qa_utils.ResolveNodeName(node))
416

  
417
    try:
418
      # Without the --force-master it should not have the master in it
419
      verify_content = ["power-cycle %s" % name for name in non_master_nodes]
420
      AssertCommand(["gnt-node", "power", "-f", "--all", "cycle"])
421
      _AssertOobCall(verify_path3, "\n".join(verify_content))
422

  
423
      # Empty file
424
      _UpdateOobFile(verify_path3, "")
425
      # With the --force-master it should have the master at last position
426
      verify_content.append("power-cycle %s" % full_master_name)
427
      AssertCommand(["gnt-node", "power", "--force-master", "-f", "--all",
428
                     "cycle"])
429
      _AssertOobCall(verify_path3, "\n".join(verify_content))
430

  
431
      # Empty file
432
      _UpdateOobFile(verify_path3, "")
433
      # With master as first argument it should still be called last
434
      cmd = ["gnt-node", "power", "--force-master", "-f", "cycle", master_name]
435
      cmd.extend(non_master_nodes)
436
      AssertCommand(cmd)
437
      _AssertOobCall(verify_path3, "\n".join(verify_content))
438
    finally:
439
      AssertCommand(["rm", "-f", oob_path3, verify_path3])
440 396
  finally:
441 397
    AssertCommand(["gnt-cluster", "modify", "--node-parameters",
442 398
                   "oob_program="])

Also available in: Unified diff