Add command line interface for running commands remotely
authorMichael Hanselmann <hansmi@google.com>
Thu, 15 Nov 2012 10:16:32 +0000 (11:16 +0100)
committerMichael Hanselmann <hansmi@google.com>
Wed, 21 Nov 2012 13:43:00 +0000 (14:43 +0100)
This patch adds a new command, “gnt-node restricted-command”. Since the
semantics are different from “gnt-cluster command”, the same subcommand
shouldn't be re-used.

The included man page update also includes a small description of how to
configure and use remote commands.

Restricted commands can be run on a whole node group or on any number of
manually chosen nodes.

The output is similar to “gnt-cluster command”.

Signed-off-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: Guido Trotter <ultrotter@google.com>

lib/client/gnt_node.py
man/gnt-node.rst

index 7c8add0..74d6a6e 100644 (file)
@@ -968,6 +968,47 @@ def SetNodeParams(opts, args):
   return 0
 
 
+def RestrictedCommand(opts, args):
+  """Runs a remote command on node(s).
+
+  @param opts: Command line options selected by user
+  @type args: list
+  @param args: Command line arguments
+  @rtype: int
+  @return: Exit code
+
+  """
+  cl = GetClient()
+
+  if len(args) > 1 or opts.nodegroup:
+    # Expand node names
+    nodes = GetOnlineNodes(nodes=args[1:], cl=cl, nodegroup=opts.nodegroup)
+  else:
+    raise errors.OpPrereqError("Node group or node names must be given",
+                               errors.ECODE_INVAL)
+
+  op = opcodes.OpRestrictedCommand(command=args[0], nodes=nodes,
+                                   use_locking=opts.do_locking)
+  result = SubmitOrSend(op, opts, cl=cl)
+
+  exit_code = constants.EXIT_SUCCESS
+
+  for (node, (status, text)) in zip(nodes, result):
+    ToStdout("------------------------------------------------")
+    if status:
+      if opts.show_machine_names:
+        for line in text.splitlines():
+          ToStdout("%s: %s", node, line)
+      else:
+        ToStdout("Node: %s", node)
+        ToStdout(text)
+    else:
+      exit_code = constants.EXIT_FAILURE
+      ToStdout(text)
+
+  return exit_code
+
+
 class ReplyStatus(object):
   """Class holding a reply status for synchronous confd clients.
 
@@ -1058,6 +1099,7 @@ def ListDrbd(opts, args):
 
   return constants.EXIT_SUCCESS
 
+
 commands = {
   "add": (
     AddNode, [ArgHost(min=1, max=1)],
@@ -1173,6 +1215,11 @@ commands = {
     ListDrbd, ARGS_ONE_NODE,
     [NOHDR_OPT, SEP_OPT],
     "[<node_name>]", "Query the list of used DRBD minors on the given node"),
+  "restricted-command": (
+    RestrictedCommand, [ArgUnknown(min=1, max=1)] + ARGS_MANY_NODES,
+    [SYNC_OPT, PRIORITY_OPT, SUBMIT_OPT, SHOW_MACHINE_OPT, NODEGROUP_OPT],
+    "<command> <node_name> [<node_name>...]",
+    "Executes a restricted command on node(s)"),
   }
 
 #: dictionary with aliases for commands
index 5e18c26..d03d7f0 100644 (file)
@@ -612,6 +612,35 @@ specific and ``status`` can be one of ``OK``, ``WARNING``, ``CRITICAL`` or
 ``UNKNOWN``. Items with status ``WARNING`` or ``CRITICAL`` are logged and
 annotated in the command line output.
 
+
+RESTRICTED-COMMAND
+~~~~~~~~~~~~~~~~~~
+
+| **restricted-command** [-M] [--sync]
+| { -g *group* *command* | *command* *nodes*... }
+
+Executes a restricted command on the specified nodes. Restricted commands are
+not arbitrary, but must reside in
+``@SYSCONFDIR@/ganeti/remote-commands`` on a node, either as a regular
+file or as a symlink. The directory must be owned by root and not be
+world- or group-writable. If a command fails verification or otherwise
+fails to start, the node daemon log must be consulted for more detailed
+information.
+
+Example for running a command on two nodes::
+
+    # gnt-node restricted-command mycommand \
+      node1.example.com node2.example.com
+
+The ``-g`` option can be used to run a command only on a specific node
+group, e.g.::
+
+    # gnt-node restricted-command -g default mycommand
+
+The ``-M`` option can be used to prepend the node name to all command
+output lines. ``--sync`` forces the opcode to acquire the node lock(s)
+in exclusive mode.
+
 .. vim: set textwidth=72 :
 .. Local Variables:
 .. mode: rst