| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- #!/bin/sh
- readonly DEFAULT_OUT_FMT="%s.out"
- readonly CMD_FEED="$(mktemp -u /tmp/distributer-XXX.fifo)"
- readonly PROC_BUFFER=10
- readonly PROCS_PER_SERVER=6
- readonly MAX_PROCS=$(expr $(ulimit -u) / $PROCS_PER_SERVER - $PROC_BUFFER)
- readonly MAX_CMD_SIZE=512
- readonly READER="$(dirname $(realpath $0))/fixed-read"
- readonly TIMEOUT=600 # 10 minutes
- readonly MK_DIR_CMD="if [ ! -d \"$(dirname "$OUT_FMT")\" ]; then
- mkdir -p \"$(dirname "$OUT_FMT")\" > /dev/null
- fi"
- readonly S_SPACE="s/[[:space:].\/]/_/g"
- readonly R_DASH="s/-//g"
- readonly S_QUOTE="s/'/\\\\'/g"
- help() {
- printf "Usage: $(basename $0) [options] cmd_list server_list [out_file_fmt]\n" >&2
- printf " cmd_list: A text file containing a list of commands to run.\n" >&2
- printf " server_list: A text file containing a list of servers to\n" >&2
- printf " connect to and run commands on.\n" >&2
- printf " out_file_fmt: File name format to write the output of each\n" >&2
- printf " command to (default: %s).\n\n" "$DEFAULT_OUT_FMT" >&2
- printf "Options:.\n" >&2
- printf " -h -?: Print this help mesage and exit. \n" >&2
- printf " -d destination: Move files to destination as space becomes\n" >&2
- printf " unavailable. (Requires quota to be available)\n" >&2
- printf " -c processor: If present, instead of directly executing\n\n" >&2
- printf " cmd_list, will launch a processor on each server and \n" >&2
- printf " distribute cmds to the processors.\n\n" >&2
- printf "All commands will be allocated to the first available server.\n" >&2
- printf "Each command must be valid on every server.\n" >&2
- printf "The output will be saved to a text file on the remote systems.\n" >&2
- exit $1
- }
- clean_server() {
- cmd_feed="$1"
- rm "$cmd_feed"
- exit 2
- }
- run_processor() {
- server="$1"
- loop="$2"
- out_file="$3"
- {
- # Test that ssh is ready to receive.
- printf "echo\n"
- read -t $TIMEOUT line < "$loop"
- printf "Running processor: '$PROCESSOR'\n" "$out_file" >&2
- printf "$PROCESSOR\n" "$out_file"
- # Block until command completes
- while read -t $TIMEOUT line < "$loop" > /dev/null && \
- cmd=$("$READER" $MAX_CMD_SIZE)
- do
- cmd=$(printf "%s" "$cmd" |
- sed -e 's/^[[:space:]]*//' \
- -e 's/[[:space:]]*$//')
- printf "%s\n" "$cmd" >&2
- printf "%s\n" "$cmd"
- done
- } | ssh -oBatchMode=yes -oStrictHostKeyChecking=no "$server" "sh" > "$loop"
- }
- run_cmd_list() {
- server="$1"
- loop="$2"
- out_file="$3"
- {
- # Test that ssh is ready to receive.
- printf "echo\n"
- # Block until command completes
- while read -t $TIMEOUT line < "$loop" > /dev/null && \
- cmd=$("$READER" $MAX_CMD_SIZE)
- do
- cmd=$(printf "%s" "$cmd" |
- sed -e 's/^[[:space:]]*//' \
- -e 's/[[:space:]]*$//')
- printf "%s\n" "$cmd" >&2
- printf "printf \"%s: \" >> %s\n" "$cmd" "$out_file"
- printf "%s >> %s\n" "$cmd" "$out_file"
- printf "echo\n"
- done
- } | ssh -oBatchMode=yes -oStrictHostKeyChecking=no "$server" "sh" > "$loop"
- }
- run_server() {
- server="$1"
- loop="$(mktemp -u /tmp/$(basename $CMD_FEED .fifo)-$server-XXX.fifo)"
- out_file=$(printf "$OUT_FMT" "$(basename "$loop" .fifo)")
- mkfifo "$loop"
- trap "clean_server $loop" 2 15
- if [ -n "$PROCESSOR" ]; then
- run_processor "$server" "$loop" "$out_file"
- else
- run_cmd_list "$server" "$loop" "$out_file"
- fi # > "$loop"
- #| ssh -oBatchMode=yes -oStrictHostKeyChecking=no "$server" "sh" > "$loop"
- clean_server "$loop"
- echo "Finished!" >&2
- }
- clean_up() {
- pkill -P $$
- rm "$CMD_FEED"
- exit 2
- }
- prepend() {
- while read -r line; do
- printf "%s: %s\n" "$1" "$line"
- done
- }
- promote_subordinate_master() {
- server="$(head -1 -)"
- until server="$(head -1 -)";\
- ssh -oBatchMode=yes -oStrictHostKeyChecking=no "$server" \
- "$(realpath $0) -t"; do
- sleep 1
- done
- }
- launch_children() {
- for server in $(head -n$MAX_PROCS -); do
- run_server "$server" < "$CMD_FEED" 2>&1 | prepend "$server" &
- done
- # promote_subordinate_master &
- while pgrep -P $$ > /dev/null; do
- wait
- done
- }
- main() {
- mkfifo "$CMD_FEED"
- trap clean_up 2 15
- trap "" 10
- cat "$CONF_LIST" | sed '/^[[:space:]]*$/d' | \
- xargs -d'\n' printf "%-$MAX_CMD_SIZE.${MAX_CMD_SIZE}s" > "$CMD_FEED" &
- cat "$SERVER_LIST" | launch_children
- clean_up
- echo "All jobs finished!"
- }
- OPTIND=1
- while getopts "h?c:t" opt; do
- case "$opt" in
- h|\?)
- help 0
- ;;
- c) readonly PROCESSOR="$OPTARG"
- ;;
- t) launch_children
- ;;
- *) echo "Unknown option '$opt'" >&2
- help 1
- ;;
- esac
- done
- shift $(expr $OPTIND - 1)
- if [ "$#" -lt 2 -o "$#" -gt 3 ]; then
- help 1
- fi
- readonly CONF_LIST="$1"
- readonly SERVER_LIST="$2"
- readonly OUT_FMT="${3:-$DEFAULT_OUT_FMT}"
- main
|