Compare commits
15 Commits
bfa3d78fca
...
c661ccd8eb
Author | SHA1 | Date |
---|---|---|
Matteo Cypriani | c661ccd8eb | |
Matteo Cypriani | 81988f3c00 | |
Matteo Cypriani | 665ca65cd2 | |
Matteo Cypriani | a2f5e9a48a | |
Matteo Cypriani | 4f007e1473 | |
Matteo Cypriani | eed73bf99f | |
Matteo Cypriani | 2b47bb65d2 | |
Matteo Cypriani | 410ac3789b | |
Matteo Cypriani | 8ae30d5e91 | |
Matteo Cypriani | 0e865d9025 | |
Matteo Cypriani | 5e6359cf8d | |
Matteo Cypriani | f3a51827f2 | |
Matteo Cypriani | 1a0c6183c4 | |
Matteo Cypriani | a8b48a2771 | |
Matteo Cypriani | 20b84f300e |
|
@ -1 +0,0 @@
|
|||
../cluster/cluster-deploy.sh
|
|
@ -1 +0,0 @@
|
|||
../cluster/cluster-ping.sh
|
|
@ -1 +0,0 @@
|
|||
../cluster/cluster-run.sh
|
|
@ -1 +0,0 @@
|
|||
../cluster/cluster-ssh.sh
|
|
@ -0,0 +1 @@
|
|||
../ssh_tools/multicopy.sh
|
|
@ -0,0 +1 @@
|
|||
../ssh_tools/multiping.sh
|
|
@ -0,0 +1 @@
|
|||
../ssh_tools/multissh.sh
|
|
@ -0,0 +1 @@
|
|||
../ssh_tools/tabssh.sh
|
|
@ -1,81 +0,0 @@
|
|||
The cluster utilities are script allowing to work in parallel with a
|
||||
number of remote hosts, using utilities such as Parallel SSH (pssh) or
|
||||
fping. They all take as first and mandatory positional argument a host
|
||||
list name “NAME”; the list will be searched according to the following
|
||||
pattern:
|
||||
|
||||
$XDG_CONFIG_HOME/cluster/NAME.lst
|
||||
|
||||
(`$XDG_CONFIG_HOME` being equivalent to `$HOME/.config` if unset.)
|
||||
For example, one can call the `cluster-ping.sh` script typing:
|
||||
|
||||
cluster-ping my_hosts
|
||||
|
||||
to use the hosts' file `$XDG_CONFIG_HOME/cluster/my_hosts.lst`.
|
||||
|
||||
The format of such a .lst file is one line per host name or IP address,
|
||||
for example:
|
||||
|
||||
```
|
||||
192.168.42.1
|
||||
192.168.42.3
|
||||
priam
|
||||
```
|
||||
|
||||
The scripts based on SSH use the local user's login name as remote login;
|
||||
this can be changed using the `-l` option, that must appear before the
|
||||
hosts list on the command line, for example:
|
||||
|
||||
cluster-run -l root openwrt_machines opkg install screen
|
||||
|
||||
The provided scripts are detailed in the sequel.
|
||||
|
||||
|
||||
cluster-ping.sh
|
||||
---------------
|
||||
|
||||
Test the connectivity with the hosts of the list by sending them a ICMP
|
||||
echo packet.
|
||||
|
||||
**Dependency**: `fping`
|
||||
|
||||
|
||||
cluster-ssh.sh
|
||||
--------------
|
||||
|
||||
Open a GNU Screen tab with an interactive SSH session for each host of
|
||||
the hosts list. Must be run inside of an existing Screen.
|
||||
|
||||
**Dependencies**: `screen`, `ssh`
|
||||
|
||||
|
||||
cluster-run.sh
|
||||
--------------
|
||||
|
||||
Run the same command on every host of the hosts list.
|
||||
|
||||
**Dependency**: `parallel-ssh` (`pssh`)
|
||||
|
||||
|
||||
cluster-deploy.sh
|
||||
-----------------
|
||||
|
||||
Deploy one or more files in parallel on every host of the hosts list.
|
||||
The files are copied in the remote user's home directory.
|
||||
|
||||
By default, the files are transferred one by one, to make sure you notice
|
||||
the errors for each file (bad permissions, out of space, etc.), but you
|
||||
can use the `-P` switch to transfer them all at once (faster).
|
||||
|
||||
While it is possible to transfer directories with the default option, to
|
||||
synchronise directories one would rather use the `-r` option, that uses
|
||||
prsync rather than pscp to transfer the files. With the `-R` option, the
|
||||
remote files that are not in the local copy will be deleted (rsync's
|
||||
`--delete` option). In both cases, the transfer is done with rsync's `-a`
|
||||
(archive) option.
|
||||
|
||||
Please note that unlike rsync, prsync cannot handle multiple local files,
|
||||
therefore the `-P` option is ignored when passed along with `-r` or `-R`.
|
||||
|
||||
**Dependencies**: `parallel-scp` (`pscp`), `parallel-rsync` (`prsync`),
|
||||
`cluster-ping` (see above)
|
|
@ -1 +0,0 @@
|
|||
cluster-deploy: add option to set a destination directory.
|
|
@ -1,153 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# cluster-deploy.sh, Copyright © 2013 Matteo Cypriani <mcy@lm7.fr>
|
||||
#
|
||||
# This program is free software. It comes without any warranty, to
|
||||
# the extent permitted by applicable law. You can redistribute it
|
||||
# and/or modify it under the terms of the Do What The Fuck You Want
|
||||
# To Public License, Version 2, as published by Sam Hocevar. See
|
||||
# http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
#
|
||||
# Deploy files on a number of remote hosts using Parallel SCP.
|
||||
|
||||
#set -x
|
||||
|
||||
print_usage()
|
||||
{
|
||||
echo "Usage:"
|
||||
echo " $0 [-P] [-r|-R] [-l login] <hosts_list> <file1> [file2 [...]]"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " -P: transfer files in parallel instead of one by one if possible"
|
||||
echo " (ignored when -r or -R is used)."
|
||||
echo " -r: use prsync instead of pscp."
|
||||
echo " -R: use prsync with option --delete (delete remote files that are"
|
||||
echo " not in the local copy)."
|
||||
}
|
||||
|
||||
# Parse the optional arguments
|
||||
while [ $# -gt 2 ] ; do
|
||||
case $1 in
|
||||
"-l")
|
||||
LOGIN="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
"-P")
|
||||
PARALLEL=1
|
||||
shift
|
||||
;;
|
||||
"-r")
|
||||
RSYNC=1
|
||||
shift
|
||||
;;
|
||||
"-R")
|
||||
DELETE=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Do we still have at least a host list and a file?
|
||||
# Note: if the program was wrongly called but still has 2 arguments, for
|
||||
# example with "cluster-deploy -l root -P my_file", it will try to use
|
||||
# "-P" as hosts' list and "my_file" as the file to be transfered.
|
||||
if [ $# -lt 2 ] ; then
|
||||
print_usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check the usage of -r and -R
|
||||
if [ "$DELETE" = "1" ] ; then
|
||||
if [ "$RSYNC" = "1" ] ; then
|
||||
echo "Use either -r or -R, but not both!"
|
||||
echo
|
||||
print_usage
|
||||
exit 2
|
||||
fi
|
||||
RSYNC=1
|
||||
DELETE="-X --delete"
|
||||
fi
|
||||
|
||||
# Ignore -P if rsync is going to be used
|
||||
if [ "$RSYNC" = "1" ] && [ "$PARALLEL" = "1" ] ; then
|
||||
echo "Cannot transfer in parallel when using rsync: ignoring -P."
|
||||
PARALLEL=""
|
||||
fi
|
||||
|
||||
# Check dependencies
|
||||
PSCP=$(command -v parallel-scp || command -v pscp.pssh || command -v pscp)
|
||||
PRSYNC=$(command -v parallel-rsync || command -v prsync)
|
||||
CLUSTERPING=$(command -v cluster-ping)
|
||||
if [ -z "$RSYNC" ] && [ -z "$PSCP" ] ; then
|
||||
echo "Parallel SSH (pssh) is required for this script to work."
|
||||
exit 4
|
||||
elif [ -z "$PRSYNC" ] ; then
|
||||
echo "Parallel rsync (prsync) is required for this script to work."
|
||||
exit 5
|
||||
elif [ -z "$CLUSTERPING" ] ; then
|
||||
echo "cluster-ping (which should have been provided along with this"
|
||||
echo "script) is required for this script to work."
|
||||
exit 6
|
||||
fi
|
||||
|
||||
# Hosts' list file
|
||||
HOSTS_LIST_NAME="$1"
|
||||
if [ -z "$XDG_CONFIG_HOME" ] ; then
|
||||
XDG_CONFIG_HOME="$HOME/.config"
|
||||
fi
|
||||
HOSTS="$XDG_CONFIG_HOME/cluster/${HOSTS_LIST_NAME}.lst"
|
||||
shift
|
||||
echo "Using file '$HOSTS' as hosts' list."
|
||||
if [ ! -f "$HOSTS" ] ; then
|
||||
echo "The hosts' list file doesn't exist or is not a regular file."
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# Login
|
||||
if [ -n "$LOGIN" ] ; then
|
||||
echo "Login: $LOGIN"
|
||||
SSH_LOGIN="${LOGIN}@"
|
||||
LOGIN="-l $LOGIN"
|
||||
fi
|
||||
|
||||
# Test the connection to the first host and get the destination
|
||||
# directory (home directory of the remote user)
|
||||
FIRST_HOST=$($CLUSTERPING "$HOSTS_LIST_NAME" 2>/dev/null \
|
||||
| sed -n "s/ is alive$//p" | head -n1)
|
||||
if [ -z "$FIRST_HOST" ] ; then
|
||||
echo "None of the remote hosts is alive."
|
||||
exit 7
|
||||
fi
|
||||
echo "Testing connection to $FIRST_HOST..."
|
||||
DEST_DIR="$(ssh "${SSH_LOGIN}${FIRST_HOST}" 'echo $HOME' 2>/dev/null)"
|
||||
if [ -z "$DEST_DIR" ] ; then
|
||||
echo "Cannot connect to the first alive host. Aborting."
|
||||
exit 7
|
||||
fi
|
||||
echo "Destination directory: $DEST_DIR"
|
||||
|
||||
# Transfer the files in parallel...
|
||||
if [ "$PARALLEL" = "1" ] ; then
|
||||
# shellcheck disable=SC2086
|
||||
exec $PSCP -r $LOGIN -h "$HOSTS" -- "$@" "$DEST_DIR"
|
||||
fi
|
||||
|
||||
# ... or one by one
|
||||
for FILE in "$@" ; do
|
||||
echo "Deploying '$FILE'..."
|
||||
if [ ! -e "$FILE" ] ; then
|
||||
echo "This file doesn't exist, skipping."
|
||||
continue
|
||||
fi
|
||||
if [ "$RSYNC" = "1" ] ; then
|
||||
# shellcheck disable=SC2086
|
||||
$PRSYNC -a $DELETE $LOGIN -h "$HOSTS" -- "$FILE" "$DEST_DIR"
|
||||
else
|
||||
# shellcheck disable=SC2086
|
||||
$PSCP -r $LOGIN -h "$HOSTS" -- "$FILE" "$DEST_DIR"
|
||||
fi
|
||||
done
|
|
@ -1,32 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# cluster-ping.sh, Copyright © 2013 Matteo Cypriani <mcy@lm7.fr>
|
||||
#
|
||||
# This program is free software. It comes without any warranty, to
|
||||
# the extent permitted by applicable law. You can redistribute it
|
||||
# and/or modify it under the terms of the Do What The Fuck You Want
|
||||
# To Public License, Version 2, as published by Sam Hocevar. See
|
||||
# http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
#
|
||||
# Ping a list of remote hosts using fping.
|
||||
|
||||
# Check arguments
|
||||
if [ $# -ne 1 ] ; then
|
||||
echo "Usage: $0 <hosts_list>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Hosts' list file
|
||||
if [ -z "$XDG_CONFIG_HOME" ] ; then
|
||||
XDG_CONFIG_HOME="$HOME/.config"
|
||||
fi
|
||||
HOSTS="$XDG_CONFIG_HOME/cluster/$1.lst"
|
||||
echo "Using file '$HOSTS' as hosts' list."
|
||||
if [ ! -f "$HOSTS" ] ; then
|
||||
echo "The hosts' list file doesn't exist or is not a regular file."
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Go!
|
||||
# shellcheck disable=SC2046
|
||||
exec fping $(cat "$HOSTS") 2>/dev/null
|
|
@ -1,67 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# cluster-run.sh, Copyright © 2013 Matteo Cypriani <mcy@lm7.fr>
|
||||
#
|
||||
# This program is free software. It comes without any warranty, to
|
||||
# the extent permitted by applicable law. You can redistribute it
|
||||
# and/or modify it under the terms of the Do What The Fuck You Want
|
||||
# To Public License, Version 2, as published by Sam Hocevar. See
|
||||
# http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
#
|
||||
# Run a command on a list of remote hosts using Parallel SSH.
|
||||
|
||||
#set -x
|
||||
|
||||
print_usage()
|
||||
{
|
||||
echo "Usage: $0 [-l login] <hosts_list> <command>"
|
||||
}
|
||||
|
||||
# Do we have at least a host list and a command?
|
||||
if [ $# -lt 2 ] ; then
|
||||
print_usage >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse the optional arguments
|
||||
if [ "$1" = "-l" ] ; then
|
||||
LOGIN="$2"
|
||||
shift
|
||||
shift
|
||||
|
||||
# Do we still have at least a host list and a command?
|
||||
if [ $# -lt 2 ] ; then
|
||||
print_usage
|
||||
exit 3
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check dependencies
|
||||
PSSH=$(command -v parallel-ssh || command -v pssh)
|
||||
if [ -z "$PSSH" ] ; then
|
||||
echo "Parallel SSH (pssh) is required for this script to work."
|
||||
exit 4
|
||||
fi
|
||||
|
||||
# Hosts' list file
|
||||
if [ -z "$XDG_CONFIG_HOME" ] ; then
|
||||
XDG_CONFIG_HOME="$HOME/.config"
|
||||
fi
|
||||
HOSTS="$XDG_CONFIG_HOME/cluster/$1.lst"
|
||||
shift
|
||||
echo "Using file '$HOSTS' as hosts' list."
|
||||
if [ ! -f "$HOSTS" ] ; then
|
||||
echo "The hosts' list file doesn't exist or is not a regular file."
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Login
|
||||
if [ -n "$LOGIN" ] ; then
|
||||
echo "Login: $LOGIN"
|
||||
LOGIN="-l $LOGIN"
|
||||
fi
|
||||
|
||||
echo "Command:" "$@"
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
exec $PSSH --print $LOGIN -h "$HOSTS" -- "$@"
|
|
@ -1,60 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# cluster-ssh.sh, Copyright © 2013 Matteo Cypriani <mcy@lm7.fr>
|
||||
#
|
||||
# This program is free software. It comes without any warranty, to
|
||||
# the extent permitted by applicable law. You can redistribute it
|
||||
# and/or modify it under the terms of the Do What The Fuck You Want
|
||||
# To Public License, Version 2, as published by Sam Hocevar. See
|
||||
# http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
#
|
||||
# Open an SSH interactive shell on a list of remote hosts, opening one
|
||||
# GNU Screen tab per host.
|
||||
|
||||
print_usage()
|
||||
{
|
||||
echo "Usage: $0 [-l login] <hosts_list>"
|
||||
}
|
||||
|
||||
# We must have either 3 arguments...
|
||||
if [ $# -eq 3 ] ; then
|
||||
# Parse the optional arguments
|
||||
if [ "$1" = "-l" ] ; then
|
||||
LOGIN="$2"
|
||||
shift
|
||||
shift
|
||||
else # Option unknown
|
||||
print_usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ... or only 1
|
||||
elif [ $# -ne 1 ] ; then
|
||||
print_usage
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# Hosts' list file
|
||||
if [ -z "$XDG_CONFIG_HOME" ] ; then
|
||||
XDG_CONFIG_HOME="$HOME/.config"
|
||||
fi
|
||||
HOSTS_FILE="$XDG_CONFIG_HOME/cluster/$1.lst"
|
||||
shift
|
||||
echo "Using file '$HOSTS_FILE' as hosts' list."
|
||||
if [ ! -f "$HOSTS_FILE" ] ; then
|
||||
echo "The hosts' list file doesn't exist or is not a regular file."
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Login
|
||||
if [ -n "$LOGIN" ] ; then
|
||||
echo "Login: $LOGIN"
|
||||
LOGIN="${LOGIN}@"
|
||||
fi
|
||||
|
||||
# Create the screen tabs
|
||||
while read -r HOST ; do
|
||||
SSH="ssh ${LOGIN}${HOST}"
|
||||
# shellcheck disable=SC2086
|
||||
screen -t "$SSH" $SSH
|
||||
done <"$HOSTS_FILE"
|
|
@ -0,0 +1,105 @@
|
|||
Use Ansible instead
|
||||
===================
|
||||
|
||||
These utilities are script allowing one to work in parallel with a number of
|
||||
remote hosts, using utilities such as Parallel SSH (pssh) or fping. Nowadays,
|
||||
you're much better off using Ansible, which covers everything these scripts do,
|
||||
and much more. The only exception might be `tabssh.sh`.
|
||||
|
||||
Original documentation
|
||||
======================
|
||||
|
||||
All these scripts take as first and mandatory positional argument a host list
|
||||
name “NAME”; the list will be searched according to the following pattern:
|
||||
|
||||
$XDG_CONFIG_HOME/ssh_tools/NAME.lst
|
||||
|
||||
(`$XDG_CONFIG_HOME` being equivalent to `$HOME/.config` if unset.)
|
||||
For example, one can call the `multiping.sh` script typing:
|
||||
|
||||
multiping my_hosts
|
||||
|
||||
to use the hosts' file `$XDG_CONFIG_HOME/ssh_tools/my_hosts.lst`.
|
||||
|
||||
The format of such a .lst file is one line per host name or IP address,
|
||||
for example:
|
||||
|
||||
```
|
||||
192.168.42.1
|
||||
192.168.42.3
|
||||
priam
|
||||
```
|
||||
|
||||
The scripts based on SSH use the local user's login name as remote login;
|
||||
this can be changed using the `-l` option, that must appear before the
|
||||
hosts list on the command line, for example:
|
||||
|
||||
multissh -l root openwrt_machines opkg install screen
|
||||
|
||||
The provided scripts are detailed in the sequel.
|
||||
|
||||
multiping.sh
|
||||
------------
|
||||
|
||||
Tests the connectivity with the hosts of the list by sending them a ICMP
|
||||
echo packet.
|
||||
|
||||
**Dependency**: `fping`
|
||||
|
||||
**Ansible replacement**:
|
||||
|
||||
ansible <group> -m ping
|
||||
|
||||
tabssh.sh
|
||||
---------
|
||||
|
||||
Opens a GNU Screen tab with an interactive SSH session for each host of
|
||||
the hosts list. Must be run inside of an existing Screen.
|
||||
|
||||
**Dependencies**: `screen`, `ssh`
|
||||
|
||||
**Ansible replacement**: not really. Maybe see this StackOverflow question for
|
||||
inspiration:
|
||||
<https://stackoverflow.com/questions/47887379/how-could-i-open-a-ssh-shell-to-remote-with-ansible>
|
||||
|
||||
multissh.sh
|
||||
-----------
|
||||
|
||||
Runs the same command on every host of the hosts list.
|
||||
|
||||
**Dependency**: `parallel-ssh` (`pssh`)
|
||||
|
||||
**Ansible replacement**: use Ansible's `command` module (i.e. the default
|
||||
module), or the `shell` module if using pipes or redirections.
|
||||
|
||||
ansible <group> -a '<command>'
|
||||
ansible <group> -m shell -a '<command with pipes>'
|
||||
|
||||
multicopy.sh
|
||||
------------
|
||||
|
||||
Deploys one or more files in parallel on every host of the hosts list.
|
||||
The files are copied in the remote user's home directory.
|
||||
|
||||
By default, the files are transferred one by one, to make sure you notice
|
||||
the errors for each file (bad permissions, out of space, etc.), but you
|
||||
can use the `-P` switch to transfer them all at once (which is much faster).
|
||||
|
||||
While it is possible to transfer directories with the default option, to
|
||||
synchronise directories one would rather use the `-r` option, that uses
|
||||
prsync rather than pscp to transfer the files. With the `-R` option, the
|
||||
remote files that are not in the local copy will be deleted (rsync's
|
||||
`--delete` option). In both cases, the transfer is done with rsync's `-a`
|
||||
(archive) option.
|
||||
|
||||
Please note that unlike rsync, prsync cannot handle multiple local files,
|
||||
therefore the `-P` option is ignored when passed along with `-r` or `-R`.
|
||||
|
||||
**Dependencies (optional)**: `parallel-scp` (`pscp`), `parallel-rsync`
|
||||
(`prsync`), `multiping` (see above)
|
||||
|
||||
**Ansible replacement**: use Ansible `copy` module. See also modules `fetch`
|
||||
(to retrieve files instead of pushing them), `synchronize`, `rsync` and
|
||||
`unarchive`.
|
||||
|
||||
ansible <group> -m copy -a 'src=/local/path dest=/remote/path'
|
|
@ -0,0 +1 @@
|
|||
multicopy: add option to set a destination directory.
|
|
@ -0,0 +1,203 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# multicopy.sh, Copyright © 2013, 2019 Matteo Cypriani <mcy@lm7.fr>
|
||||
# (Formerly named cluster-deploy.sh)
|
||||
#
|
||||
# This program is free software. It comes without any warranty, to
|
||||
# the extent permitted by applicable law. You can redistribute it
|
||||
# and/or modify it under the terms of the Do What The Fuck You Want
|
||||
# To Public License, Version 2, as published by Sam Hocevar. See
|
||||
# http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
#
|
||||
# Deploy files on a number of remote hosts using Parallel SCP.
|
||||
|
||||
#set -x
|
||||
set -u
|
||||
|
||||
# Exit codes
|
||||
readonly EXIT_SUCCESS=0
|
||||
readonly EXIT_USAGE=127
|
||||
readonly EXIT_DEPENDENCY=4
|
||||
readonly EXIT_HOSTS_LIST=6
|
||||
readonly EXIT_HOSTS_DEAD=7
|
||||
readonly EXIT_CONNECTION=8
|
||||
|
||||
print_usage()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage:
|
||||
$0 -h
|
||||
$0 [-P] [-r|-R] [-l <login>] <hosts_list> <file1> [file2 [...]]
|
||||
|
||||
Options:
|
||||
-h Print this help message.
|
||||
-l <login>
|
||||
Login remotely as <login>.
|
||||
-P Transfer files in parallel instead of one by one whenever possible
|
||||
(ignored when -r or -R is used).
|
||||
-r Use prsync instead of pscp.
|
||||
-R Use prsync's --delete option (delete remote files that are not in
|
||||
the local copy).
|
||||
|
||||
<hosts_list> is the list of remote hosts on which the file(s) will be copied.
|
||||
EOF
|
||||
}
|
||||
|
||||
bad_usage()
|
||||
{
|
||||
err "$@"
|
||||
echo >&2
|
||||
print_usage >&2
|
||||
exit $EXIT_USAGE
|
||||
}
|
||||
|
||||
err()
|
||||
{
|
||||
printf 'Error! ' >&2
|
||||
printf '%s\n' "$@" >&2
|
||||
}
|
||||
|
||||
warn()
|
||||
{
|
||||
printf 'Warning! ' >&2
|
||||
printf '%s\n' "$@" >&2
|
||||
}
|
||||
|
||||
# Copy file $FILE to the remote hosts using plain old SCP
|
||||
scp_copy()
|
||||
{
|
||||
grep -v '^#' "$HOSTS" | while read -r host ; do
|
||||
[ -z "$host" ] && continue
|
||||
echo "Deploying '$FILE' to '$host'..."
|
||||
scp -r "$FILE" "${SSH_LOGIN}${host}:${DEST_DIR}"
|
||||
done
|
||||
}
|
||||
|
||||
# Parse CLI options
|
||||
LOGIN=
|
||||
PARALLEL=
|
||||
RSYNC=
|
||||
DELETE=
|
||||
while getopts hl:PrR option ; do
|
||||
case $option in
|
||||
h) print_usage
|
||||
exit $EXIT_SUCCESS
|
||||
;;
|
||||
l) LOGIN="$OPTARG" ;;
|
||||
P) PARALLEL=1 ;;
|
||||
r) RSYNC=1 ;;
|
||||
R) DELETE=1 ;;
|
||||
\?) bad_usage "Bad option."
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND - 1))
|
||||
|
||||
# Do we still have at least a host list and a file?
|
||||
[ $# -lt 2 ] && bad_usage "Wrong number of arguments."
|
||||
|
||||
# Check the usage of -r and -R
|
||||
if [ "$DELETE" = 1 ] ; then
|
||||
[ "$RSYNC" = 1 ] && bad_usage "Use either -r or -R, but not both."
|
||||
RSYNC=1
|
||||
DELETE="-X --delete"
|
||||
fi
|
||||
|
||||
# Ignore -P if rsync is going to be used
|
||||
if [ "$RSYNC" = 1 ] && [ "$PARALLEL" = 1 ] ; then
|
||||
warn "Cannot transfer in parallel when using rsync: ignoring -P."
|
||||
PARALLEL=
|
||||
fi
|
||||
|
||||
# Check dependencies
|
||||
if [ "$RSYNC" = 1 ] ; then
|
||||
PRSYNC=$(command -v parallel-rsync || command -v prsync)
|
||||
if [ -z "$PRSYNC" ] ; then
|
||||
err "Parallel rsync (prsync) is required for this script to work with -r or -R."
|
||||
exit $EXIT_DEPENDENCY
|
||||
fi
|
||||
|
||||
else # $RSYNC != 1
|
||||
PSCP=$(command -v parallel-scp || command -v pscp.pssh || command -v pscp)
|
||||
if [ -z "$PSCP" ] ; then
|
||||
warn "Parallel SSH (pssh) is not installed. Install it for best results."
|
||||
if [ "$PARALLEL" = 1 ] ; then
|
||||
warn "Parallel copy will not work without Parallel SSH. Ignoring -P."
|
||||
PARALLEL=
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
MULTIPING=$(command -v multiping)
|
||||
if [ -z "$MULTIPING" ] ; then
|
||||
warn "multiping (which should have been provided along with this" \
|
||||
"script) is not available. For best results, make sure it is" \
|
||||
"reachable from your PATH."
|
||||
fi
|
||||
|
||||
# Hosts list file
|
||||
HOSTS_LIST_NAME="$1"
|
||||
shift
|
||||
[ -z ${XDG_CONFIG_HOME+x} ] && XDG_CONFIG_HOME="$HOME/.config"
|
||||
HOSTS="$XDG_CONFIG_HOME/ssh_tools/${HOSTS_LIST_NAME}.lst"
|
||||
echo "Using file '$HOSTS' as hosts list."
|
||||
if [ ! -f "$HOSTS" ] ; then
|
||||
err "The hosts list file doesn't exist or is not a regular file."
|
||||
exit $EXIT_HOSTS_LIST
|
||||
fi
|
||||
|
||||
# Login
|
||||
SSH_LOGIN=
|
||||
if [ -n "$LOGIN" ] ; then
|
||||
echo "Login: $LOGIN"
|
||||
SSH_LOGIN="${LOGIN}@"
|
||||
LOGIN="-l $LOGIN"
|
||||
fi
|
||||
|
||||
# Test the connection to the first alive host
|
||||
if [ -n "$MULTIPING" ] ; then
|
||||
FIRST_HOST=$($MULTIPING "$HOSTS_LIST_NAME" 2>/dev/null \
|
||||
| sed -n "s/ is alive$//p" | head -n1)
|
||||
if [ -z "$FIRST_HOST" ] ; then
|
||||
err "None of the remote hosts is alive."
|
||||
exit $EXIT_HOSTS_DEAD
|
||||
fi
|
||||
else # multiping is not available
|
||||
warn "multiping is not available, using the first host in the hosts list."
|
||||
FIRST_HOST="$(grep -v '^#' "$HOSTS" | head -n 1)"
|
||||
fi
|
||||
|
||||
# Get the destination directory (home directory of the remote user)
|
||||
echo "Testing connection to $FIRST_HOST..."
|
||||
DEST_DIR="$(ssh "${SSH_LOGIN}${FIRST_HOST}" 'echo $HOME' 2>/dev/null)"
|
||||
if [ -z "$DEST_DIR" ] ; then
|
||||
err "Cannot connect to the first alive host. Aborting."
|
||||
exit $EXIT_CONNECTION
|
||||
fi
|
||||
echo "Destination directory: $DEST_DIR"
|
||||
|
||||
# Transfer the files in parallel...
|
||||
if [ "$PARALLEL" = 1 ] ; then
|
||||
# shellcheck disable=SC2086
|
||||
exec $PSCP -r $LOGIN -h "$HOSTS" -- "$@" "$DEST_DIR"
|
||||
fi
|
||||
|
||||
# ... or one by one
|
||||
for FILE in "$@" ; do
|
||||
echo "Deploying '$FILE'..."
|
||||
if [ ! -e "$FILE" ] ; then
|
||||
warn "This file doesn't exist, skipping."
|
||||
continue
|
||||
fi
|
||||
if [ "$RSYNC" = 1 ] ; then
|
||||
# shellcheck disable=SC2086
|
||||
$PRSYNC -a $DELETE $LOGIN -h "$HOSTS" -- "$FILE" "$DEST_DIR"
|
||||
continue
|
||||
fi
|
||||
if [ -n "$PSCP" ] ; then
|
||||
# shellcheck disable=SC2086
|
||||
$PSCP -r $LOGIN -h "$HOSTS" -- "$FILE" "$DEST_DIR"
|
||||
continue
|
||||
fi
|
||||
# If we're still here, that means we'll have to copy with plain SCP
|
||||
scp_copy
|
||||
done
|
|
@ -0,0 +1,64 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# multiping.sh, Copyright © 2013, 2019 Matteo Cypriani <mcy@lm7.fr>
|
||||
# (Formerly named cluster-ping.sh)
|
||||
#
|
||||
# This program is free software. It comes without any warranty, to
|
||||
# the extent permitted by applicable law. You can redistribute it
|
||||
# and/or modify it under the terms of the Do What The Fuck You Want
|
||||
# To Public License, Version 2, as published by Sam Hocevar. See
|
||||
# http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
#
|
||||
# Ping a list of remote hosts using fping.
|
||||
|
||||
#set -x
|
||||
set -u
|
||||
|
||||
readonly EXIT_USAGE=127
|
||||
readonly EXIT_HOSTS_LIST=6
|
||||
|
||||
err()
|
||||
{
|
||||
printf 'Error! ' >&2
|
||||
printf '%s\n' "$@" >&2
|
||||
}
|
||||
|
||||
warn()
|
||||
{
|
||||
printf 'Warning! ' >&2
|
||||
printf '%s\n' "$@" >&2
|
||||
}
|
||||
|
||||
ping_hosts()
|
||||
{
|
||||
grep -v '^#' "$HOSTS" | while read -r host ; do
|
||||
[ -z "$host" ] && continue
|
||||
ping -c 1 -W 1 "$host" >/dev/null 2>&1 && echo "$host is alive"
|
||||
done
|
||||
}
|
||||
|
||||
# Check arguments
|
||||
if [ $# -ne 1 ] ; then
|
||||
echo "Usage: $0 <hosts_list>" >&2
|
||||
exit $EXIT_USAGE
|
||||
fi
|
||||
|
||||
FPING=$(command -v fping)
|
||||
[ -z "$FPING" ] && warn "fping is not available. Install it for best results."
|
||||
|
||||
# Hosts list file
|
||||
[ -z ${XDG_CONFIG_HOME+x} ] && XDG_CONFIG_HOME="$HOME/.config"
|
||||
HOSTS="$XDG_CONFIG_HOME/ssh_tools/$1.lst"
|
||||
echo "Using file '$HOSTS' as hosts list."
|
||||
if [ ! -f "$HOSTS" ] ; then
|
||||
err "The hosts list file doesn't exist or is not a regular file."
|
||||
exit $EXIT_HOSTS_LIST
|
||||
fi
|
||||
|
||||
if [ -n "$FPING" ] ; then
|
||||
# shellcheck disable=SC2046
|
||||
exec "$FPING" $(cat "$HOSTS") 2>/dev/null
|
||||
fi
|
||||
|
||||
# Fall back to good old ping if fping is not available
|
||||
ping_hosts
|
|
@ -0,0 +1,102 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# multissh.sh, Copyright © 2013, 2019 Matteo Cypriani <mcy@lm7.fr>
|
||||
# (Formerly named cluster-run.sh)
|
||||
#
|
||||
# This program is free software. It comes without any warranty, to
|
||||
# the extent permitted by applicable law. You can redistribute it
|
||||
# and/or modify it under the terms of the Do What The Fuck You Want
|
||||
# To Public License, Version 2, as published by Sam Hocevar. See
|
||||
# http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
#
|
||||
# Run a command on a list of remote hosts using Parallel SSH.
|
||||
|
||||
#set -x
|
||||
set -u
|
||||
|
||||
# Exit codes
|
||||
readonly EXIT_SUCCESS=0
|
||||
readonly EXIT_USAGE=127
|
||||
readonly EXIT_HOSTS_LIST=6
|
||||
|
||||
print_usage()
|
||||
{
|
||||
echo "Usage: $0 [-l login] <hosts_list> <command>"
|
||||
}
|
||||
|
||||
bad_usage()
|
||||
{
|
||||
err "$@"
|
||||
echo >&2
|
||||
print_usage >&2
|
||||
exit $EXIT_USAGE
|
||||
}
|
||||
|
||||
err()
|
||||
{
|
||||
printf 'Error! ' >&2
|
||||
printf '%s\n' "$@" >&2
|
||||
}
|
||||
|
||||
warn()
|
||||
{
|
||||
printf 'Warning! ' >&2
|
||||
printf '%s\n' "$@" >&2
|
||||
}
|
||||
|
||||
ssh_command()
|
||||
{
|
||||
grep -v '^#' "$HOSTS" | while read -r host ; do
|
||||
[ -z "$host" ] && continue
|
||||
echo "Executing command on '$host'..."
|
||||
# shellcheck disable=SC2086
|
||||
ssh ${LOGIN} "${host}" -- "$@" </dev/null
|
||||
# Note: </dev/null is to prevent ssh from swallowing stdin (see SC2095).
|
||||
done
|
||||
}
|
||||
|
||||
# Do we have at least a host list and a command?
|
||||
[ $# -lt 2 ] && bad_usage "Wrong number of arguments."
|
||||
|
||||
# Parse the optional arguments
|
||||
LOGIN=
|
||||
if [ "$1" = "-l" ] ; then
|
||||
LOGIN="$2"
|
||||
shift
|
||||
shift
|
||||
|
||||
# Do we still have at least a host list and a command?
|
||||
[ $# -lt 2 ] && bad_usage "Wrong number of arguments."
|
||||
fi
|
||||
|
||||
# Check dependencies
|
||||
PSSH=$(command -v parallel-ssh || command -v pssh)
|
||||
if [ -z "$PSSH" ] ; then
|
||||
warn "Parallel SSH (pssh) is not available. Install it for best results."
|
||||
fi
|
||||
|
||||
# Hosts list file
|
||||
[ -z ${XDG_CONFIG_HOME+x} ] && XDG_CONFIG_HOME="$HOME/.config"
|
||||
HOSTS_LIST_NAME="$1"
|
||||
shift
|
||||
HOSTS="$XDG_CONFIG_HOME/ssh_tools/${HOSTS_LIST_NAME}.lst"
|
||||
echo "Using file '$HOSTS' as hosts list."
|
||||
if [ ! -f "$HOSTS" ] ; then
|
||||
err "The hosts list file doesn't exist or is not a regular file."
|
||||
exit $EXIT_HOSTS_LIST
|
||||
fi
|
||||
|
||||
# Login
|
||||
if [ -n "$LOGIN" ] ; then
|
||||
echo "Login: $LOGIN"
|
||||
LOGIN="-l $LOGIN"
|
||||
fi
|
||||
|
||||
echo "Command:" "$@"
|
||||
|
||||
if [ -n "$PSSH" ] ; then
|
||||
# shellcheck disable=SC2086
|
||||
exec $PSSH --print $LOGIN -h "$HOSTS" -- "$@"
|
||||
else
|
||||
ssh_command "$@"
|
||||
fi
|
|
@ -0,0 +1,87 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# tabssh.sh, Copyright © 2013 Matteo Cypriani <mcy@lm7.fr>
|
||||
# (Formerly named cluster-ssh.sh)
|
||||
#
|
||||
# This program is free software. It comes without any warranty, to
|
||||
# the extent permitted by applicable law. You can redistribute it
|
||||
# and/or modify it under the terms of the Do What The Fuck You Want
|
||||
# To Public License, Version 2, as published by Sam Hocevar. See
|
||||
# http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
#
|
||||
# Open an SSH interactive shell on a list of remote hosts, opening one
|
||||
# GNU Screen tab per host.
|
||||
|
||||
#set -x
|
||||
set -u
|
||||
|
||||
# Exit codes
|
||||
readonly EXIT_SUCCESS=0
|
||||
readonly EXIT_USAGE=127
|
||||
readonly EXIT_DEPENDENCY=4
|
||||
readonly EXIT_HOSTS_LIST=6
|
||||
|
||||
print_usage()
|
||||
{
|
||||
echo "Usage: $0 [-l login] <hosts_list>"
|
||||
}
|
||||
|
||||
bad_usage()
|
||||
{
|
||||
err "$@"
|
||||
echo >&2
|
||||
print_usage >&2
|
||||
exit $EXIT_USAGE
|
||||
}
|
||||
|
||||
err()
|
||||
{
|
||||
printf 'Error! ' >&2
|
||||
printf '%s\n' "$@" >&2
|
||||
}
|
||||
|
||||
LOGIN=
|
||||
|
||||
# We must have either 3 arguments...
|
||||
if [ $# -eq 3 ] ; then
|
||||
# Only -l is a valid option
|
||||
[ "$1" = "-l" ] || bad_usage "Option '$1' unknown."
|
||||
LOGIN="$2"
|
||||
shift
|
||||
shift
|
||||
|
||||
# ... or only 1 argument
|
||||
elif [ $# -ne 1 ] ; then
|
||||
bad_usage "Wrong number of arguments."
|
||||
fi
|
||||
|
||||
# Check dependencies
|
||||
SCREEN=$(command -v screen)
|
||||
if [ -z "$SCREEN" ] ; then
|
||||
err "GNU screen is required by this script."
|
||||
exit $EXIT_DEPENDENCY
|
||||
fi
|
||||
|
||||
# Hosts list file
|
||||
[ -z ${XDG_CONFIG_HOME+x} ] && XDG_CONFIG_HOME="$HOME/.config"
|
||||
HOSTS_LIST_NAME="$1"
|
||||
shift
|
||||
HOSTS="$XDG_CONFIG_HOME/ssh_tools/${HOSTS_LIST_NAME}.lst"
|
||||
echo "Using file '$HOSTS' as hosts list."
|
||||
if [ ! -f "$HOSTS" ] ; then
|
||||
err "The hosts list file doesn't exist or is not a regular file."
|
||||
exit $EXIT_HOSTS_LIST
|
||||
fi
|
||||
|
||||
# Login
|
||||
if [ -n "$LOGIN" ] ; then
|
||||
echo "Login: $LOGIN"
|
||||
LOGIN="${LOGIN}@"
|
||||
fi
|
||||
|
||||
# Create the screen tabs
|
||||
while read -r HOST ; do
|
||||
SSH="ssh ${LOGIN}${HOST}"
|
||||
# shellcheck disable=SC2086
|
||||
$SCREEN -t "$SSH" $SSH
|
||||
done <"$HOSTS"
|
Loading…
Reference in New Issue