#!/bin/bash # # SCRIPT: generic_FS_rsync_copy.bash # AUTHOR: Randy Michael # DATE: 1/14/2008 # REV: 1.2.1 # # PURPOSE: This script is used to replicate # # # EXIT CODES: # 0 ==> Normal execution. # 2 ==> Remote host is not pingable. # 3 ==> Copy verification failed. One or # more local files are not the same # size as the remote files. # 4 ==> No machines are defined to copy # data to. # 5 ==> Exited on a trapped signal. # # set -x # Uncomment to debug this script # # set -n # Uncomment to check the script.s syntax # # without any execution. Do not forget to # # recomment this line! # ######################################## # REVISION LIST: ######################################## # # IF YOU MODIFY THIS SCRIPT, DOCUMENT THE CHANGE(S)!!! # ######################################## # # Revised by: # Revision Date: # Revision: # ############################################## # DEFINE FILES AND GLOBAL VARIABLES HERE ############################################## # Define the target machines to copy data to. # To specify more than one host enclose the # hostnames in double quotes and put at least # one space between each hostname # # EXAMPLE: MACHINE_LIST="fred yogi booboo" MACHINE_LIST="fred" # The FS_PATTERN variable defines the regular expression # matching the filesystems we want to replicate with rsync. FS_PATTERN="/data0[1-5]" # Add /usr/local/bin to the PATH export PATH=$PATH:/usr/local/bin # Define the directory containing all of the # output files this shell script will produce WORK_DIR=/usr/local/bin # Define the rsync script log file LOGFILE=${WORK_DIR}/generic_FS_rsync_copy.log # Capture the shell script file name THIS_SCRIPT=$(basename $0) # Initialize the background process ID list # variable to NULL BG_PID_LIST= # Define the file containing a list of files to verify # the sizes match locally and remotely LS_FILES=ls_output.dat >$LS_FILES # Initialize the TOTAL variable to 0 TOTAL=0 # Query the system for the hostname THIS_HOST=$(hostname) # Query the system for the UNIX flavor THIS_OS=$(uname) ############################################## # DEFINE FUNCTIONS HERE ############################################## verify_copy () { # set -x MYLOGFILE=${WORK_DIR}/verify_rsync_copy_day.log >$MYLOGFILE ERROR=0 # Enclose this loop so we can redirect output to the log file # with one assignment at the bottom of the function { # Put a header for the verification log file echo -e "\nRsync copy verification between $THIS_HOST and machine(s) \ $MACHINE_LIST\n\n" >> $MYLOGFILE # Loop through each machine in the $MACHINE_LIST for M in $MACHINE_LIST do # Loop through each filesystem that matches the regular # regular expression point to by "$FS_PATTERN". It is # important that this variable is enclosed within double # quotes. Also note the column for the mount point is 6; # in AIX this should be changed to 7. for MP in $(df | grep "$FS_PATTERN" | awk '{print $6}') do # For each filesystem mount point, $MP, execute a # find command to find all files in the filesystem. # and store them in the $LS_FILES file. We will use this # file list to verify the file sizes on the remote # machines match the file sizes on the local machine. find $MP -type f > $LS_FILES # Loop through the file list for FL in $(cat $LS_FILES) do # Find the local file size LOC_FS=$(ls -l $FL | awk '{print $5}' 2>&1) # Find the remote file size using a remote shell command REM_FS=$(rsh $M ls -l $FL | awk '{print $5}' 2>&1) echo "Checking File: $FL" echo -e "Local $THIS_HOST size:\t$LOC_FS" echo -e "Remote $M size:\t$REM_FS" # Ensure the file sizes match if [ "$LOC_FS" -ne "$REM_FS" ] then echo "ERROR: File size mismatch between $THIS_HOST and $M" echo "File is: $FL" ERROR=1 fi done done done if (( ERROR != 0 )) then # Record the failure in the log file echo -e "\n\nRSYNC ERROR: $THIS_HOST Rsync copy failed...file size mismatch...\n\n" | tee -a $MYLOGFILE echo -e "\nERROR: Rsync copy Failed!" echo -e "\n\nCheck log file: $MYLOGFILE\n\n" echo -e "\n...Exiting...\n" return 3 else echo -e "\nSUCCESS: Rsync copy completed successfully..." echo -e "\nAll file sizes match...\n" fi } | 2>&1 tee -a $MYLOGFILE } ############################################## elapsed_time () { SEC=$1 (( SEC < 60 )) && echo -e "[Elapsed time: $SEC seconds]\c" (( SEC >= 60 && SEC < 3600 )) && echo -e "[Elapsed time: $(( SEC / 60 )) min $(( SEC % 60 )) sec]\c" (( SEC > 3600 )) && echo -e "[Elapsed time: $(( SEC / 3600 )) hr $(( (SEC % 3600) / 60 )) min $(( (SEC % 3600) % 60 )) sec]\c" } ############################################## # BEGINNING OF MAIN ############################################## # Enclose the entire main part of the script in # curly braces so we can redirect all output of # the shell script with a single redirection # at the bottom of the script to the $LOGFILE { # Save a copy of the last log file cp -f $LOGFILE ${LOGFILE}.yesterday echo -e "\n[[ $THIS_SCRIPT started execution $(date) ]]\n" # Ensure that target machines are defined if [ -z "$MACHINE_LIST" ] then echo -e "\nERROR: No machines are defined to copy data to..." echo "...Unable to continue...Exiting..." exit 4 fi # Ensure the remote machines are reachable through # the network by sending 1 ping. echo -e "Verifying the target machines are pingable...\n" for M in $MACHINE_LIST do echo "Pinging $M..." ping -c1 $M >/dev/null 2>&1 if (( $? != 0 )) then echo -e "\nERROR: $M host is not pingable...cannot continue..." echo -e "...EXITING...\n" exit 2 else echo "Pinging $M succeeded..." fi done # Start all of the rsync copy sessions at once by looping # through each of the mount points and issuing an rsync # command in the background, and incrementing a counter. echo -e "\nStarting a bunch of rsync sessions...\n" for M in $MACHINE_LIST do # Loop through each filesystem that matches the regular # expression point to by "$FS_PATTERN". It is # important that this variable is enclosed within double # quotes. Also note the column for the mount point is 6; # in AIX this should be changed to 7. for MP in $(df | grep "$FS_PATTERN" | awk '{print $6}') do # Start the rsync session in the background time rsync -avz ${MP}/ ${M}:${MP} 2>&1 & # Keep a running total of the number of rsync # sessions started (( TOTAL = TOTAL + 1 )) done done # Sleep a few seconds before monitoring processes sleep 10 # Find the number of rsync sessions that are still running REM_SESSIONS=$(ps x | grep "rsync -avz" | grep "$FS_PATTERN" \ | grep -v grep | awk '{print $1}' | wc -l) # Give some feedback to the end user. if (( REM_SESSIONS > 0 )) then echo -e "\n$REM_SESSIONS of $TOTAL rsync copy sessions require \ further updating..." echo -e "\nProcessing rsync copies from $THIS_HOST to both \ $MACHINE_LIST machines" echo -e "\nPlease be patient, this process may take a very long \ time...\n" echo -e "Rsync is running [Start time: $(date)]\c" else echo -e "\nAll files appear to be in sync...verifying file sizes...\ Please wait...\n" fi # While the rsync processes are executing this loop # will place a . (dot) every 60 seconds as feedback # to the end user that the background rsync copy # processes are still executing. When the remaining # rsync sessions are less than the total number of # sessions (normally 36) this loop will give feedback # as to the number of remaining rsync session, as well # as the elapsed time of the processing, every 5 minutes.. SECONDS=10 MIN_COUNTER=0 # Loop until all of the local rsync sessions have completed until (( REM_SESSIONS == 0 )) do # sleep 60 seconds between loop iterations sleep 60 # Display a dot on the screen every 60 seconds echo -e ".\c" # Find the number of remaining rsync sessions REM_SESSIONS=$(ps x | grep "rsync -avz" | grep "$FS_PATTERN" \ | grep -v grep | '{print $1}' | wc -l) # Have any of the sessions completed? If so start giving # the user updates every 5 minutes, specifying the number # remaining rsync sessions and the elapsed time. if (( REM_SESSIONS < TOTAL )) then # Count every 5 minutes (( MIN_COUNTER = MIN_COUNTER + 1 )) # 5 minutes timed out yet? if (( MIN_COUNTER >= 5 )) then # Reset the minute counter MIN_COUNTER=0 # Update the user with a new progress report echo -e "\n$REM_SESSIONS of $TOTAL rsync sessions \ remaining $(elapsed_time $SECONDS)\c" # Have three-fourths of the rsync sessions completed? if (( REM_SESSIONS <= $(( TOTAL / 4 )) )) then # Display the list of remaining rsync sessions # that continue to run. echo -e "\nRemaining rsync sessions include:\n" ps x | grep "rsync -avz" | grep "$FS_PATTERN" \ | grep -v grep echo fi fi fi done echo -e "\n...Local rsync processing completed on $THIS_HOST...$(date)" echo -e "\n...Checking remote target machines: $MACHINE_LIST..." # Just because the local rsync processes have completed does # not mean that the remote rsync processes have completed # execution. This loop verifies that all of the remote # rsync processes completed execution. The end user will # receive feedback if any of the remote processes are running. for M in $MACHINE_LIST do # Loop through each matching filesystem for MP in $(df | grep "$FS_PATTERN" | awk '{print $7}') do # Find the remote process Ids. RPID=$(rsh $M ps x | grep rsync | grep $MP | grep -v grep \ | awk '{print $1}') # Loop while remote rsync sessions continue to run. # NOTE: I have never found remote rsync sessions running # after the local sessions have completed. This is just # an extra sanity check until [ -z "$RPID" ] do echo "rsync is processing ${MP} on ${M}...sleeping one minute..." sleep 60 RPID=$(rsh $M ps x | grep rsync | grep $MP | grep -v grep \ | awk '{print $1}') done done done echo -e "\n...Remote rsync processing completed $(date)\n" # Verify the copy process verify_copy # Check the verify_copy return code if (( $? != 0 )) then exit 3 fi echo -e "\nRsync copy completed $(date)" echo -e "\n[[ $THIS_SCRIPT completed execution $(date) ]]\n" echo -e "\n[[ Elapsed Time: $(elapsed_time $SECONDS) ]]\n" } | 2>&1 tee -a $LOGFILE ############################################### # END OF SCRIPT ###############################################