gnt-node modify: Adding --node-powered=yes|no
[ganeti-local] / lib / client / gnt_node.py
index 1819a0d..24a9b25 100644 (file)
@@ -27,6 +27,7 @@
 # C0103: Invalid name gnt-node
 
 from ganeti.cli import *
+from ganeti import cli
 from ganeti import bootstrap
 from ganeti import opcodes
 from ganeti import utils
@@ -59,6 +60,10 @@ _LIST_STOR_DEF_FIELDS = [
   ]
 
 
+#: default list of power commands
+_LIST_POWER_COMMANDS = ["on", "off", "cycle", "status"]
+
+
 #: headers (and full field list) for L{ListStorage}
 _LIST_STOR_HEADERS = {
   constants.SF_NODE: "Node",
@@ -211,7 +216,7 @@ def ListNodes(opts, args):
   selected_fields = ParseFields(opts.output, _LIST_DEF_FIELDS)
 
   fmtoverride = dict.fromkeys(["pinst_list", "sinst_list", "tags"],
-                              (lambda value: ",".join(value), False))
+                              (",".join, False))
 
   return GenericList(constants.QR_NODE, selected_fields, args, opts.units,
                      opts.separator, not opts.no_headers,
@@ -452,6 +457,53 @@ def PowercycleNode(opts, args):
   return 0
 
 
+def PowerNode(opts, args):
+  """Change/ask power state of a node.
+
+  @param opts: the command line options selected by the user
+  @type args: list
+  @param args: should contain only one element, the name of
+      the node to be removed
+  @rtype: int
+  @return: the desired exit code
+
+  """
+  command = args[0]
+  node = args[1]
+
+  if command not in _LIST_POWER_COMMANDS:
+    ToStderr("power subcommand %s not supported." % command)
+    return constants.EXIT_FAILURE
+
+  oob_command = "power-%s" % command
+
+  opcodelist = []
+  if oob_command == constants.OOB_POWER_OFF:
+    opcodelist.append(opcodes.OpSetNodeParams(node_name=node, offline=True,
+                                              auto_promote=opts.auto_promote))
+
+  opcodelist.append(opcodes.OpOobCommand(node_name=node, command=oob_command))
+
+  cli.SetGenericOpcodeOpts(opcodelist, opts)
+
+  job_id = cli.SendJob(opcodelist)
+
+  # We just want the OOB Opcode status
+  # If it fails PollJob gives us the error message in it
+  result = cli.PollJob(job_id)[-1]
+
+  if result:
+    if oob_command == constants.OOB_POWER_STATUS:
+      text = "The machine is %spowered"
+      if result[constants.OOB_POWER_STATUS_POWERED]:
+        result = text % ""
+      else:
+        result = text % "not "
+    ToStderr(result)
+
+  return constants.EXIT_SUCCESS
+
+
 def ListVolumes(opts, args):
   """List logical volumes on node(s).
 
@@ -628,7 +680,8 @@ def SetNodeParams(opts, args):
                                secondary_ip=opts.secondary_ip,
                                force=opts.force,
                                ndparams=opts.ndparams,
-                               auto_promote=opts.auto_promote)
+                               auto_promote=opts.auto_promote,
+                               powered=opts.node_powered)
 
   # even if here we process the result, we allow submit only
   result = SubmitOrSend(op, opts)
@@ -687,12 +740,20 @@ commands = {
     SetNodeParams, ARGS_ONE_NODE,
     [FORCE_OPT, SUBMIT_OPT, MC_OPT, DRAINED_OPT, OFFLINE_OPT,
      CAPAB_MASTER_OPT, CAPAB_VM_OPT, SECONDARY_IP_OPT,
-     AUTO_PROMOTE_OPT, DRY_RUN_OPT, PRIORITY_OPT, NODE_PARAMS_OPT],
+     AUTO_PROMOTE_OPT, DRY_RUN_OPT, PRIORITY_OPT, NODE_PARAMS_OPT,
+     NODE_POWERED_OPT],
     "<node_name>", "Alters the parameters of a node"),
   'powercycle': (
     PowercycleNode, ARGS_ONE_NODE,
     [FORCE_OPT, CONFIRM_OPT, DRY_RUN_OPT, PRIORITY_OPT],
     "<node_name>", "Tries to forcefully powercycle a node"),
+  'power': (
+    PowerNode,
+    [ArgChoice(min=1, max=1, choices=_LIST_POWER_COMMANDS),
+     ArgNode(min=1, max=1)],
+    [SUBMIT_OPT, AUTO_PROMOTE_OPT, PRIORITY_OPT],
+    "on|off|cycle|status <node>",
+    "Change power state of node by calling out-of-band helper."),
   'remove': (
     RemoveNode, ARGS_ONE_NODE, [DRY_RUN_OPT, PRIORITY_OPT],
     "<node_name>", "Removes a node from the cluster"),