NAME
gofer - execute multiple ssh sessions in parallel
SYNOPSIS
# This expects a list of host connection definitions on STDIN,
# then executes the command or command defined in the arguments
# on each:
echo =host1 =host2 | gofer [options] <commands>
DESCRIPTION
This script will log in and execute commands on remote ssh servers.
It is WORK IN PROGRESS! Expect rough edges.
In simple terms, the format of the host connections on STDIN should be one or more terms like this:
='<connection>' 'login'='<name>' 'password'='<password>' 'port'='<port>' 'hostname'='<hostname>'
This is designed to be easy to parse using core Perl modules (specifically, Text::ParseWords::parse_line(qr/\s*(=\s*|\s+)/, 0, $term)
).
The first item must start with an equals character, and <connection
> indicates an (arbitrary) name for the connection. The other items can be omitted in certain circumstances. Only connection
is required in all circumstances. Other name-value parameters can be added (they will be ignored).
Multiple terms can be combined in one line, assuming the terms start with an equals character (i.e. no items contain unquoted spaces, such as 'foo' ='bar'
).
connection
is used as the default hostname passed to ssh if no explicit hostname
parameter is given.
So if your .ssh/config defines connections to hosts with aliases foo
and bar
, you can use a concise form like this:
echo =foo =bar | gofer id
Instead of requiring the longer equivalents:
echo -e "=foo\n=bar\n" | gofer id
echo -e "=foo 'hostname'='foo'\n=bar 'hostname'='bar'\n" | gofer id
Both examples would run id
on both foo
and bar
.
By default, you will be prompted for a password for each connection (whether or not you are sudoing or have ssh keys set up, etc.) There are some ways to make this easier.
First you can define keys and pass the --p=sudoing
option, which means you will only be prompted if the commands require a sudo.
Second, you can pass a password
parameter:
echo -e "=foo 'password'='offsite'\n=bar 'password'='offsite'\n" | gofer 'sudo id'
How this is interpreted can vary. By default the value is interpreted as a label for the password (not the password itself!). Then gofer will only prompt you to enter a password once for each label.
The point of this is to avoid explicit passwords on the command line, whilst also avoiding having to type any more passwords than necessary.
You can specify other interpretations for the password
parameter using the --password-broker
option. These allow you to specify the password literally on the command line (not recommended in general), or in an obfuscated form (better). The default is to prompt.
EXAMPLE OUTPUT
An example showing what you might get invoking:
echo =alpha password=xxx =beta password=xxx | gofer --file git-status.sh
With ~/.ssh/config
defining key authenticated access for hosts with aliases alpha
and beta
, and git-status.sh
containing:
# bla de bla
sudo cd /root/configuration; git status
Then you might get output like the following:
password for xxx:
----[ alpha ]-----------------------------------------------------------
# bla de bla
$ sudo "cd /root/configuration; git status"
# On branch WORK_IN_PROGRESS
nothing to commit (working directory clean)
----(4s elapsed)
----[ beta ]------------------------------------------------------------
# bla de bla
$ sudo "cd /root/configuration; git status"
# On branch BETA_LOCAL_MODS
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
#
# modified: foo/src/stuff.h
#
no changes added to commit (use "git add" and/or "git commit -a")
----(4s elapsed)
done
parse_map %options
Parses a sequence of lines of the following form:
C<host user:password@host:port>
Returns an array of <($name =
\%params)>> pairs, each defining a connection name and a parameter hash as accepted by <Net::SSH::Mechanize::ConnectParams-
new>>, which can be passed directly to <Net::SSH::Mechanize::Multi-
add>>.