2013-04-29 22:43:41 +02:00
|
|
|
#!/bin/sh
|
|
|
|
#
|
2019-10-21 13:30:04 +02:00
|
|
|
# multicopy.sh, Copyright © 2013, 2019 Matteo Cypriani <mcy@lm7.fr>
|
2019-10-21 11:29:29 +02:00
|
|
|
# (Formerly named cluster-deploy.sh)
|
2013-04-29 22:43:41 +02:00
|
|
|
#
|
|
|
|
# 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.
|
2013-05-02 17:18:24 +02:00
|
|
|
|
2013-05-02 16:56:28 +02:00
|
|
|
#set -x
|
2019-10-21 13:30:04 +02:00
|
|
|
set -u
|
|
|
|
|
|
|
|
# Exit codes
|
|
|
|
readonly EXIT_SUCCESS=0
|
2019-10-21 13:51:39 +02:00
|
|
|
readonly EXIT_USAGE=127
|
2019-10-21 13:30:04 +02:00
|
|
|
readonly EXIT_DEPENDENCY=4
|
|
|
|
readonly EXIT_HOSTS_LIST=6
|
|
|
|
readonly EXIT_HOSTS_DEAD=7
|
|
|
|
readonly EXIT_CONNECTION=8
|
2013-04-29 22:43:41 +02:00
|
|
|
|
2013-05-01 22:12:09 +02:00
|
|
|
print_usage()
|
|
|
|
{
|
2019-10-21 13:51:39 +02:00
|
|
|
cat <<EOF
|
|
|
|
Usage:
|
2019-10-21 15:18:21 +02:00
|
|
|
$0 -h
|
|
|
|
$0 [-P] [-r|-R] [-l <login>] <hosts_list> <file1> [file2 [...]]
|
2019-10-21 13:51:39 +02:00
|
|
|
|
|
|
|
Options:
|
|
|
|
-h Print this help message.
|
2019-10-21 15:18:21 +02:00
|
|
|
-l <login>
|
|
|
|
Login remotely as <login>.
|
2019-10-21 13:51:39 +02:00
|
|
|
-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).
|
2019-10-21 15:18:21 +02:00
|
|
|
|
|
|
|
<hosts_list> is the list of remote hosts on which the file(s) will be copied.
|
2019-10-21 13:51:39 +02:00
|
|
|
EOF
|
|
|
|
}
|
|
|
|
|
|
|
|
bad_usage()
|
|
|
|
{
|
|
|
|
err "$@"
|
|
|
|
echo >&2
|
|
|
|
print_usage >&2
|
|
|
|
exit $EXIT_USAGE
|
|
|
|
}
|
|
|
|
|
|
|
|
err()
|
|
|
|
{
|
|
|
|
printf 'Error! %s\n' "$@" >&2
|
|
|
|
}
|
|
|
|
|
|
|
|
warn()
|
|
|
|
{
|
|
|
|
printf 'Warning! %s\n' "$@" >&2
|
2013-05-01 22:12:09 +02:00
|
|
|
}
|
|
|
|
|
2019-10-21 14:48:01 +02:00
|
|
|
# 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."
|
2013-05-02 17:18:24 +02:00
|
|
|
esac
|
|
|
|
done
|
2019-10-21 14:48:01 +02:00
|
|
|
shift $((OPTIND - 1))
|
2013-05-02 17:18:24 +02:00
|
|
|
|
|
|
|
# Do we still have at least a host list and a file?
|
2019-10-21 15:18:21 +02:00
|
|
|
[ $# -lt 2 ] && bad_usage "Wrong number of arguments."
|
2013-05-01 22:12:09 +02:00
|
|
|
|
2013-05-02 18:17:22 +02:00
|
|
|
# Check the usage of -r and -R
|
|
|
|
if [ "$DELETE" = "1" ] ; then
|
2019-10-21 15:18:21 +02:00
|
|
|
[ "$RSYNC" = "1" ] && bad_usage "Use either -r or -R, but not both."
|
2013-05-02 18:17:22 +02:00
|
|
|
RSYNC=1
|
|
|
|
DELETE="-X --delete"
|
|
|
|
fi
|
|
|
|
|
|
|
|
# Ignore -P if rsync is going to be used
|
2018-04-11 22:56:01 +02:00
|
|
|
if [ "$RSYNC" = "1" ] && [ "$PARALLEL" = "1" ] ; then
|
2019-10-21 13:51:39 +02:00
|
|
|
warn "Cannot transfer in parallel when using rsync: ignoring -P."
|
2019-10-21 14:48:01 +02:00
|
|
|
PARALLEL=
|
2013-05-02 18:17:22 +02:00
|
|
|
fi
|
|
|
|
|
2013-05-01 22:12:09 +02:00
|
|
|
# Check dependencies
|
2013-04-29 22:43:41 +02:00
|
|
|
PSCP=$(command -v parallel-scp || command -v pscp.pssh || command -v pscp)
|
2013-05-02 18:17:22 +02:00
|
|
|
PRSYNC=$(command -v parallel-rsync || command -v prsync)
|
2019-10-21 11:29:29 +02:00
|
|
|
MULTIPING=$(command -v multiping)
|
2018-04-11 22:56:01 +02:00
|
|
|
if [ -z "$RSYNC" ] && [ -z "$PSCP" ] ; then
|
2019-10-21 13:51:39 +02:00
|
|
|
err "Parallel SSH (pssh) is required for this script to work."
|
2019-10-21 13:30:04 +02:00
|
|
|
exit $EXIT_DEPENDENCY
|
|
|
|
fi
|
|
|
|
if [ -z "$PRSYNC" ] ; then
|
2019-10-21 13:51:39 +02:00
|
|
|
err "Parallel rsync (prsync) is required for this script to work."
|
2019-10-21 13:30:04 +02:00
|
|
|
exit $EXIT_DEPENDENCY
|
|
|
|
fi
|
|
|
|
if [ -z "$MULTIPING" ] ; then
|
2019-10-21 13:51:39 +02:00
|
|
|
err "multiping (which should have been provided along with this" \
|
|
|
|
"script) is required for this script to work."
|
2019-10-21 13:30:04 +02:00
|
|
|
exit $EXIT_DEPENDENCY
|
2013-04-29 22:43:41 +02:00
|
|
|
fi
|
|
|
|
|
2019-10-21 15:18:21 +02:00
|
|
|
# Hosts list file
|
2013-05-20 20:35:49 +02:00
|
|
|
HOSTS_LIST_NAME="$1"
|
2013-04-29 22:43:41 +02:00
|
|
|
shift
|
2019-10-21 15:18:21 +02:00
|
|
|
[ -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."
|
2013-05-20 20:46:39 +02:00
|
|
|
if [ ! -f "$HOSTS" ] ; then
|
2019-10-21 15:18:21 +02:00
|
|
|
err "The hosts list file doesn't exist or is not a regular file."
|
2019-10-21 13:30:04 +02:00
|
|
|
exit $EXIT_HOSTS_LIST
|
2013-05-20 20:46:39 +02:00
|
|
|
fi
|
2013-04-29 22:43:41 +02:00
|
|
|
|
2013-05-01 22:12:09 +02:00
|
|
|
# Login
|
2019-10-21 14:48:01 +02:00
|
|
|
SSH_LOGIN=
|
2018-04-11 22:56:01 +02:00
|
|
|
if [ -n "$LOGIN" ] ; then
|
2013-05-01 22:12:09 +02:00
|
|
|
echo "Login: $LOGIN"
|
2013-05-20 20:35:49 +02:00
|
|
|
SSH_LOGIN="${LOGIN}@"
|
2013-05-01 22:12:09 +02:00
|
|
|
LOGIN="-l $LOGIN"
|
|
|
|
fi
|
|
|
|
|
2019-10-21 13:30:04 +02:00
|
|
|
# Test the connection to the first alive host and get the destination
|
2013-05-20 20:35:49 +02:00
|
|
|
# directory (home directory of the remote user)
|
2019-10-21 11:29:29 +02:00
|
|
|
FIRST_HOST=$($MULTIPING "$HOSTS_LIST_NAME" 2>/dev/null \
|
2018-04-11 22:56:01 +02:00
|
|
|
| sed -n "s/ is alive$//p" | head -n1)
|
|
|
|
if [ -z "$FIRST_HOST" ] ; then
|
2019-10-21 13:51:39 +02:00
|
|
|
err "None of the remote hosts is alive."
|
2019-10-21 13:30:04 +02:00
|
|
|
exit $EXIT_HOSTS_DEAD
|
2013-05-20 20:35:49 +02:00
|
|
|
fi
|
|
|
|
echo "Testing connection to $FIRST_HOST..."
|
2018-04-11 22:56:01 +02:00
|
|
|
DEST_DIR="$(ssh "${SSH_LOGIN}${FIRST_HOST}" 'echo $HOME' 2>/dev/null)"
|
|
|
|
if [ -z "$DEST_DIR" ] ; then
|
2019-10-21 13:51:39 +02:00
|
|
|
err "Cannot connect to the first alive host. Aborting."
|
2019-10-21 13:30:04 +02:00
|
|
|
exit $EXIT_CONNECTION
|
2013-05-20 20:35:49 +02:00
|
|
|
fi
|
|
|
|
echo "Destination directory: $DEST_DIR"
|
|
|
|
|
2013-05-02 17:18:24 +02:00
|
|
|
# Transfer the files in parallel...
|
|
|
|
if [ "$PARALLEL" = "1" ] ; then
|
2018-04-11 22:56:01 +02:00
|
|
|
# shellcheck disable=SC2086
|
2013-05-20 20:35:49 +02:00
|
|
|
exec $PSCP -r $LOGIN -h "$HOSTS" -- "$@" "$DEST_DIR"
|
2013-05-02 17:18:24 +02:00
|
|
|
fi
|
|
|
|
|
|
|
|
# ... or one by one
|
2013-05-02 16:56:28 +02:00
|
|
|
for FILE in "$@" ; do
|
2013-04-29 22:43:41 +02:00
|
|
|
echo "Deploying '$FILE'..."
|
2013-05-20 20:36:02 +02:00
|
|
|
if [ ! -e "$FILE" ] ; then
|
2019-10-21 13:51:39 +02:00
|
|
|
warn "This file doesn't exist, skipping."
|
2013-05-20 20:36:02 +02:00
|
|
|
continue
|
|
|
|
fi
|
2013-05-02 18:17:22 +02:00
|
|
|
if [ "$RSYNC" = "1" ] ; then
|
2018-04-11 22:56:01 +02:00
|
|
|
# shellcheck disable=SC2086
|
2013-05-20 20:35:49 +02:00
|
|
|
$PRSYNC -a $DELETE $LOGIN -h "$HOSTS" -- "$FILE" "$DEST_DIR"
|
2013-05-02 18:17:22 +02:00
|
|
|
else
|
2018-04-11 22:56:01 +02:00
|
|
|
# shellcheck disable=SC2086
|
2013-05-20 20:35:49 +02:00
|
|
|
$PSCP -r $LOGIN -h "$HOSTS" -- "$FILE" "$DEST_DIR"
|
2013-05-02 18:17:22 +02:00
|
|
|
fi
|
2013-04-29 22:43:41 +02:00
|
|
|
done
|