/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */ 

##############################################
  qrsh: a Qpid-based remote shell utility

  Last updated:  3 Aug 09    Mick Goulish
##############################################



=============================
Overview
=============================

  You're writing a multi-box test, and you want to write a 
  shell script in which you start processes on other boxes 
  and kill them (or send arbitrary signals to them).  

  But ssh doesn't let you signal them, and bash isn't the 
  greatest language in the world for creating data structures 
  (like you need to associate the PIDs with box names and 
  executable names.)

  Qsh is a utility implemented on Qpid that you can use from 
  within your bash script, or any other scripting language.
  With it, you can:

    1. run any executable on any box in your cluster.

    2. don't worry about PIDs and box-names.  You associate 
       your own abstract names with the executable instances, 
       and then use those names in the rest of your script.  
       I.e. "broker_1" "sender_3" etc.

    3. Launch the executable and wait until it returns, and
       get its exit code.

    4. Launch your executable and do other stuff, then come 
       back later and see if it has exited.

    5. Get whatever it sent to stdout or stderr.

    6. Get the contents of any other file.

    7. send a command to all your boxes at once

    8. send a command to a randomly selected box.

    9. define groups of boxes, and send a command simultaneously
       to all boxes in a given group.




=============================
Using It
=============================

   1. You need to run a Qpid broker.

   2. You start a Qpid client ( which is called a qrsh_server )
      on all the boxes you care about.  And you give them all 
      names like "mrg13", "mrg14" etc.  The names can be anything
      you want, but I've always used one qrsh_server per box, 
      and given it the box name.   ( However, you can run two on
      one box, they won't collide. )

   3. After you start all servers, send a "start" command to any
      one of them:

   4. The qrsh_servers use the fanout exchange to talk to each 
      other.

   5. In your script, you run an executable called "qrsh".  It knows
      how to talk to the servers, do what you want, and retrieve 
      the data you want.


   example start script:  (this does 4 servers on the same box)
   -------------------------------------------------------------

       echo "Starting server mrg22 ..."
       ./qrsh_server mrg22  ./qrsh_run 127.0.0.1  5813  &

       echo "Starting server mrg23 ..."
       ./qrsh_server mrg23  ./qrsh_run 127.0.0.1  5813  &

       echo "Starting server mrg24 ..."
       ./qrsh_server mrg24  ./qrsh_run 127.0.0.1  5813  &

       echo "Starting server mrg25 ..."
       ./qrsh_server mrg25  ./qrsh_run 127.0.0.1  5813  &

       echo "Issuing start command..."
       sleep 2
       ./qrsh 127.0.0.1  5813 mrg22 start
       sleep 1

       echo "Ready."

       # end of script.






=============================
Qrsh Syntax
=============================
  
     qrsh  host port server_name  command_name  arg*


       "host" and "port" specify the Qpid server to connect to.

       "server_name" can be anything you want.  I always use the name 
         of the box that the server is running on.

       "command_name" is the name that you choose to assign to 
         the process you are running.  Each process that you decide
         to name must have a unique name within this script.

         Or it could be a reserved command name, that Qsh 
         interprets in a special way.

         Reserved command names are:
             
             exec
             exec_wait
             exited
             get

         "exec" means "interpret the rest of the command line as a
           command to be executed by the designated server.

         "exec_wait" means same as "exec", but wait for the command
           to terminate, and return its exit code.

         "exited" -- you provide 1 arg, which is an abstract 
           process name.  qrsh returns 1 if that process has exited, 
           else 0.

         "get" -- you provide one arg which is a path.  qrsh returns
           (by printing to stdout) the contents of that file.

       "arg*" is zero or more arguments.  They are interpreted 
         differently depending on whether you are using one of
         the above reserved command names, or making up your own
         abstract name for a command.




=============================
Examples
=============================

  1. Run a process on a remote box.
     
         qrsh mrg23 command_1 /usr/sbin/whatever foo bar baz

     Returns immediately.



  2. Kill a process that you started earlier:

         qrsh mrg23 exec kill -9 command_1

     After the word "exec" put any command line you want.
     The server you're sending this to will replace all abstract
     names in the command with process IDs.  ( In this example, 
     just the word "command_1" will be replaced. )  Then it will 
     execute the command.



  3. Execute a command, and wait for it to finish

         qrsh mrg23 exec_wait command_name args



  4. Check on whether a command you issude earlier has exited.

         ./qrsh mrg23 exited command_3

     Returns 1 if it has exited, else 0.



  5. Get the contents of a file from the remote system:

        ./qrsh mrg23 get /tmp/foo

     Prints the contents to stdout.



  6. Send a command to all servers at once:

        # This example causes them all to print thir names to stderr.
        ./qrsh all sayName


  7. Define a group of servers and send a command to that group.

      #! /bin/bash
      
      # Make a group of two of the servers, using "alias",
      # and send the group a command.
      
      qrsh 127.0.0.1 5813 \
           mrg22 alias group_1

      qrsh 127.0.0.1 5813 \
          mrg23 alias group_1
      
      echo "Asking group_1 to say their names... "
      qrsh 127.0.0.1 5813 \
           group_1 sayName

      # end of script.
      
  


  8. Execute a command and get its stdout and stderr contents.

        #! /bin/bash

        echo "Run a command..."
        ./qrsh 127.0.0.1 5813 \
               mrg23 command_4 my_command foo bar baz

        echo "Wait for a while..."
        sleep 10

        echo "Get stderr output:"
        echo "-------------  begin stderr ---------------"
        ./qrsh 127.0.0.1 5813 \
               mrg23 get command_4 stderr
        echo "-------------  end stderr ---------------"
        echo " "

        echo " "
        echo "Get stdout output:"
        echo "-------------  begin stdout ---------------"
        ./qrsh 127.0.0.1 5813 \
               mrg23 get command_4 stdout
        echo "-------------  end stdout ---------------"

        # end of script.
    



  9. Send a command to one of your servers, selected
     at random.

           #! /bin/bash

           # I do it multiple times here, so I can see 
           # that it really is selecting randomly.

          echo "asking any server to say his name ..."
          ./qrsh 127.0.0.1 5813 \
                 any sayName
          sleep 1

          echo "asking any server to say his name ..."
          ./qrsh 127.0.0.1 5813 \
                 any sayName
          sleep 1

          echo "asking any server to say his name ..."
          ./qrsh 127.0.0.1 5813 \
                 any sayName
          sleep 1

          echo "asking any server to say his name ..."
          ./qrsh 127.0.0.1 5813 \
                 any sayName

        # end of script.




