Sample code for 30+ languages & platforms
Tcl

SSH Parallel Remote Commands on Multiple Servers

See more SSH Examples

Demonstrates running the same command on several servers simultaneously. It is simply a matter of using one Ssh object per server, starting the command on each with QuickCmdSend, and then polling each connection with QuickCmdCheck until all have finished.

Background: This is the fan-out pattern behind configuration-management and monitoring tools: query or update a whole fleet in roughly the time the slowest single host takes, rather than the sum of all of them. Each server needs its own object because each has its own connection and authentication state. Note that results come back in whatever order the servers finish, which is why the example tracks completion per connection. Production code would keep the objects in arrays instead of repeating the logic per server.

Chilkat Tcl Downloads

Tcl

load ./chilkat.dll

set success 0

#  This example requires the Chilkat API to have been previously unlocked.
#  See Global Unlock Sample for sample code.

#  Demonstrates running the same command on several servers simultaneously.  It is simply a
#  matter of using one Ssh object per server.

set ssh1 [new_CkSsh]

set ssh2 [new_CkSsh]

set ssh3 [new_CkSsh]

set port 22

#  Normally you would not hard-code the password in source.  You should instead obtain it
#  from an interactive prompt, environment variable, or a secrets vault.
set password "mySshPassword"

set success [CkSsh_Connect $ssh1 "ssh-server1.example.com" $port]
if {$success == 0} then {
    puts [CkSsh_lastErrorText $ssh1]
    delete_CkSsh $ssh1
    delete_CkSsh $ssh2
    delete_CkSsh $ssh3
    exit
}

set success [CkSsh_AuthenticatePw $ssh1 "mySshLogin" $password]
if {$success == 0} then {
    puts [CkSsh_lastErrorText $ssh1]
    delete_CkSsh $ssh1
    delete_CkSsh $ssh2
    delete_CkSsh $ssh3
    exit
}

set success [CkSsh_Connect $ssh2 "ssh-server2.example.com" $port]
if {$success == 0} then {
    puts [CkSsh_lastErrorText $ssh2]
    delete_CkSsh $ssh1
    delete_CkSsh $ssh2
    delete_CkSsh $ssh3
    exit
}

set success [CkSsh_AuthenticatePw $ssh2 "mySshLogin" $password]
if {$success == 0} then {
    puts [CkSsh_lastErrorText $ssh2]
    delete_CkSsh $ssh1
    delete_CkSsh $ssh2
    delete_CkSsh $ssh3
    exit
}

set success [CkSsh_Connect $ssh3 "ssh-server3.example.com" $port]
if {$success == 0} then {
    puts [CkSsh_lastErrorText $ssh3]
    delete_CkSsh $ssh1
    delete_CkSsh $ssh2
    delete_CkSsh $ssh3
    exit
}

set success [CkSsh_AuthenticatePw $ssh3 "mySshLogin" $password]
if {$success == 0} then {
    puts [CkSsh_lastErrorText $ssh3]
    delete_CkSsh $ssh1
    delete_CkSsh $ssh2
    delete_CkSsh $ssh3
    exit
}

#  This command sleeps for 5 seconds and then prints the system date/time, making the parallel
#  execution easy to observe.
set cmd "sleep 5; date"

#  Start the command on each server.  QuickCmdSend returns immediately.
set channel1 [CkSsh_QuickCmdSend $ssh1 $cmd]
if {$channel1 < 0} then {
    puts [CkSsh_lastErrorText $ssh1]
    delete_CkSsh $ssh1
    delete_CkSsh $ssh2
    delete_CkSsh $ssh3
    exit
}

set channel2 [CkSsh_QuickCmdSend $ssh2 $cmd]
if {$channel2 < 0} then {
    puts [CkSsh_lastErrorText $ssh2]
    delete_CkSsh $ssh1
    delete_CkSsh $ssh2
    delete_CkSsh $ssh3
    exit
}

set channel3 [CkSsh_QuickCmdSend $ssh3 $cmd]
if {$channel3 < 0} then {
    puts [CkSsh_lastErrorText $ssh3]
    delete_CkSsh $ssh1
    delete_CkSsh $ssh2
    delete_CkSsh $ssh3
    exit
}

#  The command is now running on all three servers at once.  Poll each connection until every
#  command has finished.  (Production code would keep the objects in arrays rather than
#  repeating the logic.)
set pollTimeoutMs 50
set numFinished 0
set finished1 0
set finished2 0
set finished3 0

while {$numFinished < 3} {
    if {!$finished1} then {
        set ch1 [CkSsh_QuickCmdCheck $ssh1 $pollTimeoutMs]
        if {$ch1 == -2} then {
            puts [CkSsh_lastErrorText $ssh1]
            delete_CkSsh $ssh1
            delete_CkSsh $ssh2
            delete_CkSsh $ssh3
            exit
        }

        if {$ch1 >= 0} then {
            puts "---- ssh1 channel $ch1 finished ----"
            set out1 [CkSsh_getReceivedText $ssh1 $ch1 "utf-8"]
            if {[CkSsh_get_LastMethodSuccess $ssh1] == 0} then {
                puts [CkSsh_lastErrorText $ssh1]
                delete_CkSsh $ssh1
                delete_CkSsh $ssh2
                delete_CkSsh $ssh3
                exit
            }

            puts "$out1"
            set finished1 1
            set numFinished [expr $numFinished + 1]
        }

    }

    if {!$finished2} then {
        set ch2 [CkSsh_QuickCmdCheck $ssh2 $pollTimeoutMs]
        if {$ch2 == -2} then {
            puts [CkSsh_lastErrorText $ssh2]
            delete_CkSsh $ssh1
            delete_CkSsh $ssh2
            delete_CkSsh $ssh3
            exit
        }

        if {$ch2 >= 0} then {
            puts "---- ssh2 channel $ch2 finished ----"
            set out2 [CkSsh_getReceivedText $ssh2 $ch2 "utf-8"]
            if {[CkSsh_get_LastMethodSuccess $ssh2] == 0} then {
                puts [CkSsh_lastErrorText $ssh2]
                delete_CkSsh $ssh1
                delete_CkSsh $ssh2
                delete_CkSsh $ssh3
                exit
            }

            puts "$out2"
            set finished2 1
            set numFinished [expr $numFinished + 1]
        }

    }

    if {!$finished3} then {
        set ch3 [CkSsh_QuickCmdCheck $ssh3 $pollTimeoutMs]
        if {$ch3 == -2} then {
            puts [CkSsh_lastErrorText $ssh3]
            delete_CkSsh $ssh1
            delete_CkSsh $ssh2
            delete_CkSsh $ssh3
            exit
        }

        if {$ch3 >= 0} then {
            puts "---- ssh3 channel $ch3 finished ----"
            set out3 [CkSsh_getReceivedText $ssh3 $ch3 "utf-8"]
            if {[CkSsh_get_LastMethodSuccess $ssh3] == 0} then {
                puts [CkSsh_lastErrorText $ssh3]
                delete_CkSsh $ssh1
                delete_CkSsh $ssh2
                delete_CkSsh $ssh3
                exit
            }

            puts "$out3"
            set finished3 1
            set numFinished [expr $numFinished + 1]
        }

    }

}

CkSsh_Disconnect $ssh1
CkSsh_Disconnect $ssh2
CkSsh_Disconnect $ssh3

delete_CkSsh $ssh1
delete_CkSsh $ssh2
delete_CkSsh $ssh3