Wouldn’t it be lovely if you could run some command on a bunch of servers?
Of course, you can! Most of us would do something like that:
1 |
for HOST in host1 host2 host3; do { ssh "${HOST}" "echo ZALOOPA > /var/tmp/zaloopa"; } done |
But what if there are dozens of servers with different usernames to log-in with, different SSH-keys to use and even different ports to connect to? It would be quite convenient to use some conficuration file with all these login-details:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# # The format is following: # hostname:username:port:ssh-key #comments #tags # All parameters besides of the first are optional # # Regular servers host1 host2 host3 # The servers where we shall use an alternative username to log in host4:user # Servers where we shall use an alternative port to log in host5::30022 host6:user:30022 # Servers where we shall use an alternative SSH-key to log in host7:::~/.ssh/id_rsa.zaloopa host8:user::~/.ssh/id_rsa.zaloopa host9:user:30666:~/.ssh/id_rsa.zaloopa # Servers we need to be found by some hashtags (so we can run the script with the -g parameter: "-g '#huerga'") host10 #huerga host11:user #huerga host12:user:30022 #huerga #zaebis #pizdets host13:user:30022:~/.ssh/id_rsa.zaloppa #huerga #pizdets |
And here goes the script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
#!/bin/bash OPTS=$(getopt -n $(basename ${0}) -o c:g:y -l command:,grep-criterias:yes -- "${@}") if [ "${?}" -ne 0 ]; then echo "Can't get options" >&2 exit 1 fi eval set -- "${OPTS}" while :; do case "${1}" in -c|--command) COMMAND="${2}"; shift 2;; -g|--grep-criterias) CRITERIAS="${2}"; shift 2;; -y|--yes) SURE="Y"; shift 1;; --) shift; break;; *) echo "Can't recognize the option: ${1}"; exit 1;; esac done TARGETS=$(cat ~/etc/servers | grep -E "${CRITERIAS}" | sed -e 's/[[:space:]]*#.*$//' -e '/^$/d') if [ -z "${COMMAND}" ]; then echo "The command is undefined" >&2 exit 1 fi while :; do echo -e "Our targets are:\n${TARGETS}" >&2 [ "${SURE}" != "Y" ] && read -p "Are you sure you want to run '${COMMAND}'? (Y/N) " SURE case "${SURE}" in [Yy]) for TARGET in ${TARGETS}; do HOST=$(echo "${TARGET}" | sed -r -n -e 's/^([^:]+)(:([^:]*))?(:([^:]*))?(:([^:]*))?$/\1/gp') USER=$(echo "${TARGET}" | sed -r -n -e 's/^([^:]+)(:([^:]*))?(:([^:]*))?(:([^:]*))?$/\3/gp') PORT=$(echo "${TARGET}" | sed -r -n -e 's/^([^:]+)(:([^:]*))?(:([^:]*))?(:([^:]*))?$/\5/gp') SKEY=$(echo "${TARGET}" | sed -r -n -e 's/^([^:]+)(:([^:]*))?(:([^:]*))?(:([^:]*))?$/\7/gp') [ -z "${USER}" ] && USER=$(whoami) [ -z "${PORT}" ] && PORT=22 [ -z "${SKEY}" ] && SKEY=~/.ssh/id_rsa echo "Running '${COMMAND}' on ${HOST}:${PORT}..." >&2 ssh -i "${SKEY}" -p "${PORT}" -l "${USER}" "${HOST}" "${COMMAND}" done break ;; [Nn]) break ;; *) echo "So, Y or N?" >&2 ;; esac done |
Now you can do something like that:
1 |
spread.sh --command "yum -y upgrade && reboot" --grep-criteria '(#centos|#pizdos)' --yes |
If I ever update this script with some new features, you could always get the latest verion from GitHub: https://gist.github.com/melnik13/be1160a5bf73e246cd5f.
Enjoy your great power, but don’t forget about great responsibility comes with it! 🙂