Files
mastering-unix-ss/chapter7/rsync_daily_copy.ksh
Fabio Scotto di Santolo 4cc88d2f6e initial commit
2020-07-28 19:28:25 +02:00

546 lines
16 KiB
Bash
Executable File

#!/bin/ksh
#
# SCRIPT: rsync_daily_copy.ksh
# AUTHOR: Randy Michael
# DATE: 11/10/2007
# REV: 3.2.Prod
#
# PURPOSE: This script is used to replicate Oracle .dbf
# files between the "master" DB server and the two
# OLTP Oracle servers. The copy method used is
# rsync. For this method to work, the Oracle DBA must
# first put the database tables that reside in the
# copy filesystems into READ-ONLY mode. Before starting
# rsync copy sessions, this script searches for a file,
# defined by the $READYTORUN_FILE variable, that is
# placed on the system by the Oracle DBA team; when
# the file is found this script will execute
# all 36 rsync sessions, 18 to each OLTP server,
# at the same time, then waits for all sessions to
# complete, both locally and on the remote servers.
# After the rsync copy sessions complete the copy is
# verified by matching file sizes between the master
# copy file and the target copy files. When verified
# successful this script writes a file to the system,
# defined by the $COMPLETE_FILE variable, to signal
# the Oracle DBAs to put the DB back into READ-WRITE
# mode, copy the metadata over, build the tables and
# attach the DB on the OLTP side servers. Should a
# failure occur a file, defined by the $RSYNCFAILED_FILE
# variable, is written to the system to signal to the
# Oracle DBA team an rsync copy process failure.
#
#
# EXIT CODES:
# 0 ==> Normal execution.
# 1 ==> The value assigned to DAY is not
# an integer 1 or 2.
# 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: Randy Michael
# Revision Date: 7/23/2007
# Revision: Changed the script to process
# all 36 mount points at a time.
#
########################################
#
# Revised by:
# Revision Date:
# Revision:
#
##############################################
# DEFINE FILES AND GLOBAL VARIABLES HERE
##############################################
typeset -i DAY
EMAIL_FROM=data_support@gamma
export PATH=$PATH:/usr/local/bin
WORK_DIR=/usr/local/bin
LOGFILE=${WORK_DIR}/rsync_daily_copy.log
SEARCH_DIR=/orabin/apps/oracle/dbadm/general/bin
READYTORUN_FILE=${SEARCH_DIR}/readytocopy.txt
COMPLETE_FILE=${SEARCH_DIR}/copycomplete.txt
RSYNCFAILED_FILE=${SEARCH_DIR}/copyfailed.txt
MAILMESSAGEFILE=${WORK_DIR}/email_message.out
THIS_SCRIPT=$(basename $0)
BG_PID_LIST=
TOTAL=0
THIS_HOST=$(hostname)
[[ $THIS_HOST = gamma ]] && MACHINE_LIST="alpha-rsync bravo-rsync"
[[ $THIS_HOST = gamma-dg ]] && MACHINE_LIST="alpha-rsync bravo-rsync"
# Setup the correct echo command usage. Many Linux
# distributions will execute in BASH even if the
# script specifies Korn shell. BASH shell requires
# we use echo -e when we use \n, \c, etc.
case $SHELL in
*/bin/bash) alias echo="echo -e"
;;
esac
##############################################
# DEFINE FUNCTIONS HERE
##############################################
usage ()
{
echo "\nUSAGE: $THIS_SCRIPT Day"
echo "\nWhere Day is 1 or 2\n"
}
##############################################
cleanup_exit ()
{
# If this script is executing, then something failed!
[ $1 ] && EXIT_CODE=$1
echo "\n$THIS_SCRIPT is exiting on non-zero exit code: $EXIT_CODE"
echo "\nPerforming cleanup..."
echo "Removing $READYTORUN_FILE"
rm -f $READYTORUN_FILE >/dev/null 2>&1
echo "Removing $COMPLETE_FILE"
rm -f $COMPLETE_FILE >/dev/null 2>&1
echo "\nCreating $RSYNCFAILED_FILE"
echo "\nRsync failed on $THIS_HOST with exit code $EXIT_CODE $(date)\n"\
| tee -a $RSYNCFAILED_FILE
echo "\nCleanup Complete...Exiting..."
return $EXIT_CODE
exit $EXIT_CODE
}
##############################################
trap_exit ()
{
echo "\nERROR: EXITING ON A TRAPPED SIGNAL!\n"
echo "\nRSYNC ERROR: $THIS_HOST -- Rsync process exited abnormally on a \
trapped signal $(date)!" > $MAILMESSAGEFILE
mailx -r "$EMAIL_FROM" -s "SYNC ERROR: $THIS_HOST -- Rsync process exited \
abnormally on a trapped signal!" data_support < $MAILMESSAGEFILE
sleep 2 # Allow the email to go out
cleanup_exit 5
return 5
exit 5
}
##############################################
verify_copy ()
{
#set -x
MYLOGFILE=${WORK_DIR}/verify_rsync_copy_day${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 "\nRsync copy verification between $THIS_HOST and machines $MACHINE_LIST\n\n" >$MYLOGFILE
for M in $MACHINE_LIST
do
for LOC_MP in $(df | grep oradata_dm_[0-2][0-9] | awk '{print $7}')
do
LS_FILES=$(find $LOC_MP -type f)
for FL in $LS_FILES
do
LOC_FS=$(ls -l $FL | awk '{print $5}' 2>&1)
# This sed statement changes the "m" to $DAY
REM_FL=$(echo $FL | sed s/oradata_dm_/oradata_d${DAY}_/g)
REM_FS=$(rsh $M ls -l $REM_FL | awk '{print $5}' 2>&1)
echo "Checking File: $FL"
echo "Local $THIS_HOST size:\t$LOC_FS"
echo "Checking Remote File: $REM_FL"
echo "$M size:\t$REM_FS"
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 "\n\nRSYNC ERROR: $THIS_HOST Rsync copy failed...file size \
mismatch...\n\n" | tee -a $MYLOGFILE
# Send email notification with file size log
mailx -r "$EMAIL_FROM" -s "RSYNC ERROR: $THIS_HOST Rsync copy failed\
...file size mismatch -- log attached" data_support < $MYLOGFILE
echo "\nERROR: Rsync copy Failed!"
echo "\n\nCheck log file: $MYLOGFILE\n\n"
echo "\n...Exiting...\n"
cleanup_exit 3
return 3
else
echo "\nSUCCESS: Rsync copy completed successfully..."
echo "\nAll file sizes match...\n"
fi
} | tee -a $MYLOGFILE
}
########################################
ready_to_run ()
{
# set -x
# This function looks for a file on the system
# defined by the $READYTORUN_FILE variable. The
# presents presence of this file indicates we are ready
# to run this script. This file will contain a
# number 1 or 2 identifying the day we are
# working with.
if [ -r ${READYTORUN_FILE} ]
then
cat ${READYTORUN_FILE}
else
echo "NOT_READY"
fi
}
##############################################
elapsed_time ()
{
SEC=$1
(( SEC < 60 )) && echo "[Elapsed time: $SEC seconds]\c"
(( SEC >= 60 && SEC < 3600 )) && echo "[Elapsed time: $(( SEC / 60 )) \
min $(( SEC % 60 )) sec]\c"
(( SEC > 3600 )) && echo "[Elapsed time: $(( SEC / 3600 )) hr $(( (SEC % 3600) / 60 )) \
min $(( (SEC % 3600) % 60 )) sec]\c"
}
##############################################
# BEGINNING OF MAIN
##############################################
# Set a trap
trap 'trap_exit' 1 2 3 5 6 11 14 15 17
# Save the old log file
cp -f $LOGFILE ${LOGFILE}.yesterday \
>$LOGFILE
# 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
{
echo "\n[[ $THIS_SCRIPT started execution $(date) ]]\n"
# Ensure that target machines are defined
if [ -z "$MACHINE_LIST" ]
then
echo "\nERROR: No machines are defined to copy data to..."
echo "\nRSYNC ERROR: $THIS_HOST has no machines are defined \
to copy data to..." > $MAILMESSAGEFILE
mailx -r "$EMAIL_FROM" -s "RSYNC ERROR: $THIS_HOST has no machines \
defined to copy data to" data_support < $MAILMESSAGEFILE
echo "...Unable to continue...Exiting..."
cleanup_exit 4
exit 4
fi
# Checking for currently running versions of this script
echo "Checking for Currently Runnning Versions of this Script"
MYPID=$$ # Capture this scripts PID
MYOTHERPROCESSES=$(ps -ef | grep $THIS_SCRIPT | grep -v $MYPID \
| grep -v grep | awk '{print $2}')
if [[ "$MYOTHERPROCESSES" != "" ]]
then
echo "\WARNING: Another version of this script is running...Killing it!"
echo "Killing the following processe(es)...$MYOTHERPROCESSES"
kill -9 $MYOTHERPROCESSES
echo "\nNOTICE: Sleeping for 1 minute to allow both local
and remote rsync sessions to terminate, if they exist...\n"
sleep 60 # Allow any rsync sessions to stop both locally and remotely
else
echo "No other versions running...proceeding"
fi
# Remove the file that indicates the rsync copy
# is mounted and ready to use, if it exists.
rm -f $COMPLETE_FILE >/dev/null 2>&1
# Remove the file that indicates the rsync copy
# failed and exited on a non-zero exit code, if
# it exists.
rm -f $RSYNCFAILED_FILE >/dev/null 2>&1
# Search for the file indicating we are ready to proceed
# Send notification
echo "\nDaily Rsync Copy Process Began on Host $THIS_HOST $(date)" \
> $MAILMESSAGEFILE
echo "\nStarted looking for $READYTORUN_FILE" >> $MAILMESSAGEFILE
mailx -r "$EMAIL_FROM" -s "Rsync Copy Process Began Looking for Startup \
File on $THIS_HOST" data_support < $MAILMESSAGEFILE
echo "\nSearching for the file: ${READYTORUN_FILE}"
RUN_STATUS=$(ready_to_run)
until [[ $RUN_STATUS != "NOT_READY" ]]
do
date
echo "$READYTORUN_FILE is not present...Sleeping 5 minutes..."
sleep 300
RUN_STATUS=$(ready_to_run)
done
date
echo "Found file: $READYTORUN_FILE -- Ready to proceed..."
DAY=$RUN_STATUS
# Test the value assigned to the DAY variable
echo "Testing variable assignment for the DAY variable"
if (( DAY != 1 && DAY != 2 ))
then
echo "\nRSYNC ERROR: $THIS_HOST -- The value assigned to \
the DAY variable" > $MAILMESSAGEFILE
echo "==> $DAY - is not an integer 1 or 2...Exiting..."
>> $MAILMESSAGEFILE
mailx -r "$EMAIL_FROM" -s "ERROR: $THIS_HOST -- Value assigned \
to the DAY variable $DAY is invalid" data_support < $MAILMESSAGEFILE
usage
cleanup_exit
exit 1
fi
# Ensure the remote machines are reachable through
# the network by sending 1 ping.
echo "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 "RSYNC ERROR: $M is not pingable from $THIS_HOST" \
> $MAILMESSAGEFILE
echo "The rsync copy process cannot continue...Exiting" \
>> $MAILMESSAGEFILE
mailx -r "$EMAIL_FROM" -s "RSYNC ERROR: $M is not pingable
from $THIS_HOST" data_support < $MAILMESSAGEFILE
echo "\nERROR: $M host is not pingable...cannot continue..."
echo "...EXITING...\n"
sleep 2
cleanup_exit 2
exit 2
else
echo "Pinging $M succeeded..."
fi
done
# Notify start of rsync processing by email
echo "Rsync Copy Process for Day $DAY Starting Execution on \
$THIS_HOST $(date)" > $MAILMESSAGEFILE
mailx -r "$EMAIL_FROM" -s "Rsync copy process for day $DAY starting \
on $THIS_HOST" data_support < $MAILMESSAGEFILE
# 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 "\nStarting a bunch of rsync sessions...\n"
# This script copies from the DM filesystems to the
# Day 1 or Day 2 filesystems on the OLTP servers.
for M in $MACHINE_LIST
do
for LOC_MP in $(df | grep oradata_dm_[0-2][0-9] | awk '{print $7}')
do
# This sed statement changes the "m" to $DAY
REM_MP=$(echo $LOC_MP | sed s/m/$DAY/g)
time rsync -avz ${LOC_MP}/ ${M}:${REM_MP} 2>&1 &
(( TOTAL = TOTAL + 1 ))
done
done
# Sleep a few seconds before monitoring processes
sleep 10
# Give some feedback to the end user.
REM_SESSIONS=$(ps -ef | grep "rsync -avz" | grep oradata_dm_[0-2][0-9] \
| grep -v grep | awk '{print $2}' | wc -l)
if (( REM_SESSIONS > 0 ))
then
echo "\n$REM_SESSIONS of $TOTAL rsync copy sessions require further updating..."
echo "\nProcessing rsync copies from $THIS_HOST to both $MACHINE_LIST machines"
echo "\nPlease be patient, this process may take longer than two hours...\n"
echo "Rsync is running [Start time: $(date)]\c"
else
echo "\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 procesing, every 5 minutes.
SECONDS=10
MIN_COUNTER=0
until (( REM_SESSIONS == 0 ))
do
sleep 60
echo ".\c"
REM_SESSIONS=$(ps -ef | grep "rsync -avz" | grep oradata_dm_[0-2][0-9] \
| grep -v grep | awk '{print $2}' | wc -l)
if (( REM_SESSIONS < TOTAL ))
then
(( MIN_COUNTER = MIN_COUNTER + 1 ))
if (( MIN_COUNTER >= 5 ))
then
MIN_COUNTER=0
echo "\n$REM_SESSIONS of $TOTAL rsync sessions \
remaining $(elapsed_time $SECONDS)\c"
if (( REM_SESSIONS <= $(( TOTAL / 4 )) ))
then
echo "\nRemaining rsync sessions include:\n"
ps -ef | grep "rsync -avz" | grep oradata_dm_[0-2][0-9] \
| grep -v grep
echo
fi
fi
fi
done
echo "\n...Local rsync processing completed on $THIS_HOST...$(date)"
echo "\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
for LOC_MP in $(df | grep oradata_dm_[0-2][0-9] | awk '{print $7}')
do
# This sed statement changes the "m" to $DAY
REM_MP=$(echo $LOC_MP | sed s/m/$DAY/g)
RPID=$(rsh $M ps -ef | grep rsync | grep $REM_MP \
| grep -v grep | awk '{print $2}')
until [ -z "$RPID" ]
do
echo "rsync is processing ${REM_MP} on ${M}...sleeping one minute..."
sleep 60
RPID=$(rsh $M ps -ef | grep rsync | grep $REM_MP \
| grep -v grep | awk '{print $2}')
done
done
done
echo "\n...Remote rsync processing completed $(date)\n"
# Verify the copy process
verify_copy
if (( $? != 0 ))
then
exit 3
fi
# Write to the $COMPLETE_FILE to signal the Oracle DBAs
# that the copy has completed
echo "Rsync copy from $THIS_HOST to machines $MACHINE_LIST \
completed successfully $(date)" | tee -a $COMPLETE_FILE
# Remove the ready to run file
rm -f $READYTORUN_FILE >/dev/null 2>&1
# Notify completion by email
echo "Rsync Copy for Day $DAY Completed Successful Execution \
on $THIS_HOST $(date)\n" > $MAILMESSAGEFILE
elapsed_time $SECONDS >> $MAILMESSAGEFILE
mailx -r "$EMAIL_FROM" -s "Rsync copy for day $DAY completed \
successfully on $THIS_HOST" data_support < $MAILMESSAGEFILE
echo "\nRsync copy completed $(date)"
echo "\n[[ $THIS_SCRIPT completed execution $(date) ]]\n"
exit 0
} | 2>&1 tee -a $LOGFILE
###############################################
# END OF SCRIPT
###############################################