From 6d846d0e768731381d98d5b151406a7918b54c5f Mon Sep 17 00:00:00 2001
From: Michael Hanselmann <hansmi@google.com>
Date: Thu, 15 Nov 2012 11:16:32 +0100
Subject: [PATCH] Add command line interface for running commands remotely
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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 | 47 ++++++++++++++++++++++++++++++++++++++++++
 man/gnt-node.rst       | 29 ++++++++++++++++++++++++++
 2 files changed, 76 insertions(+)

diff --git a/lib/client/gnt_node.py b/lib/client/gnt_node.py
index 7c8add06e..74d6a6ede 100644
--- a/lib/client/gnt_node.py
+++ b/lib/client/gnt_node.py
@@ -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
diff --git a/man/gnt-node.rst b/man/gnt-node.rst
index 5e18c2678..d03d7f0e9 100644
--- a/man/gnt-node.rst
+++ b/man/gnt-node.rst
@@ -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
-- 
GitLab