module Rcmd

This module is for parellel remote (SSH) execution of a single command string on Multiple hosts. The module itself consists of one method for execution (Rcmd.run_command) which does not accept any arguments. The required arguments are set as variables through the use of accessors.

Constants

VERSION

VERSION - Version number string

Attributes

command[RW]

String containing the command to be used. (Manditory)

debug[RW]

Boolean for debug output

host_list[RW]

An array of hosts to run the given command on. (Manditory)

nthreads[RW]

Prefered/requested number of threads to be used. (Manditory)

quiet[RW]

Boolean for disabling STDOUT output. SDTERR is always displayed. (Optional)

threads[RW]

Array containing the current iterations thread objects.

user[RW]

What user should we connect as. (Manditory)

Public Class Methods

run_command() click to toggle source

Main method for this module which should be called after the correct variables have been set.

We iterate over the host list until it is empty, creating all needed threads based upon the prefered number of threads for execution. Before creating the threads, the method first checks if the preferred number of threads is greater then the number of hosts remaining in the host list. If false (threads > num hosts) then the number of remaining hosts becomes the thread count. This prevents spawning of unneeded threads.

Manditory values to be set

  • :user - User to run the command as

  • :command - Command to be executed

  • :host_list - Array containing the hosts for command execution.

  • :nthreads - Preferred max number of threads

Optional Values

  • :quiet - Do not print to STDOUT. STDERR is always printed

Specifically for the method only

  • :threads - Array of the current threads

Example

require 'rcmd'
Rcmd.host_list= ["host1", "host2", "host3", "host4", "host 5", "host6"]
Rcmd.user= 'root'
Rcmd.command= 'rpm -qa kernel\*'
Rcmd.nthreads= 6
Rcmd.run_command
# File lib/rcmd.rb, line 77
def Rcmd.run_command() 
  if not @command
    raise ArgumentError.new("No command set for execution")
  end
  if not @host_list.count >= 1
    raise ArgumentError.new("host_list must contain at least one system")
  end
  @host_list.each do |host|
    @queue << host
  end
  until @queue.empty?
    # Don't start more threads then hosts.
    num_threads = @nthreads <= @queue.length ? @nthreads : @queue.length
    # Prepare threads
    @threads = [ ]
    num_threads.times do |i|
      @threads[i] = Thread.new {
        begin
          conn_options = { :user => @user, :host => @queue.pop, :password => nil, :quiet => @quiet, :debug => @debug }
          STDERR.print "DEBUG :: Connecting to #{conn_options[:host]}\n" if conn_options[:debug]
          Net::SSH.start(conn_options[:host], conn_options[:user], :password => conn_options[:passwd]) do |session|
            # Open channel for input/output control
            session.open_channel do |channel|
              channel.on_data do |ch, data|
                # Print recieved data if quiet is not true
                STDOUT.print "#{conn_options[:host]} :: #{data}" unless conn_options[:quiet]
              end
              channel.on_extended_data do |ch,type,data|
                # Always print stderr data
                STDERR.print "#{conn_options[:host]} :: ERROR :: #{data}"
              end
              # Execute command
              channel.exec @command
            end
            # Loop until command completes
            session.loop
          end
        rescue
          STDERR.print "#{conn_options[:host]} :: CONNECT ERROR :: Unable to connect to host!\n"
        end
      }
      # Execute threads
    end
    @threads.each { |t| t.join }
  end
  unless @threads.each.map {|t| t.alive?}.none?
    sleep 1
  end
end