The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

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>>.