first commit
This commit is contained in:
287
oracle_mount.sh
Normal file
287
oracle_mount.sh
Normal file
@@ -0,0 +1,287 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Perform a Live Mount of an Oracle DB
|
||||
# v0.2 - James Pattinson - August 2021
|
||||
#
|
||||
# usage: oracle_mount.sh [-h <dbhost>] [-d <oracle_home> [-f] <srcSID> <tgtHOSTNAME> <"timestamp">
|
||||
#
|
||||
# -d specify $ORACLE_HOME on target - needed for mount of Data Guard DBs
|
||||
# default is to performing a files-only mount
|
||||
# -l mount and create DB Live Mount
|
||||
#
|
||||
# Timestamp YYYY-MM-DD HH:MM:SS
|
||||
# Time is passed to 'date' on THIS machine, will use local timezone
|
||||
|
||||
MYDIR="$(dirname "$(realpath "$0")")"
|
||||
source $MYDIR/rbk_api.conf
|
||||
source $MYDIR/oracle_funcs.sh
|
||||
|
||||
# Local commands to mount and unmount NFS volumes
|
||||
MOUNT="sudo /usr/local/sysadmin/utils/manage_rubrik_restore_mvs.ksh"
|
||||
UMOUNT="sudo /usr/local/sysadmin/utils/manage_rubrik_restore_mvs.ksh"
|
||||
|
||||
usage() { echo "Usage: $0 <-d DBNAME> <-t \"timestamp\"> <-g tgthost || -r rachost> [-h <dbhost>] [-o <oracle_home>] [-m <mountpoint> [-l|-n]" 1>&2; exit 1; }
|
||||
|
||||
filesonly=true
|
||||
|
||||
while getopts "d:t:g:r:m:h:o:ln" o; do
|
||||
case "${o}" in
|
||||
d)
|
||||
RBK_SID=${OPTARG}
|
||||
;;
|
||||
t)
|
||||
datestring=${OPTARG}
|
||||
;;
|
||||
g)
|
||||
RBK_TGT=${OPTARG}
|
||||
;;
|
||||
r)
|
||||
RAC_TGT=${OPTARG}
|
||||
;;
|
||||
m)
|
||||
mountpoint=${OPTARG}
|
||||
;;
|
||||
h)
|
||||
RBK_HOST=${OPTARG}
|
||||
;;
|
||||
o)
|
||||
oraclehome=${OPTARG}
|
||||
;;
|
||||
l)
|
||||
filesonly=false
|
||||
;;
|
||||
n)
|
||||
nowait=true
|
||||
;;
|
||||
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
|
||||
if [ -z "${RBK_SID}" ] || [ -z "${datestring}" ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
#if [ -z "${RAC_TGT}" ] && [ -z "${RBK_TGT}" ]; then
|
||||
# usage
|
||||
#fi
|
||||
|
||||
if [ -n "${RBK_TGT}" ] && [ -n "${RAC_TGT}" ]; then
|
||||
echo "ERROR: Specify either -g or -r, not both"
|
||||
usage
|
||||
fi
|
||||
|
||||
if [ -z "${mountpoint}" ]; then
|
||||
mountpoint=/rbk
|
||||
fi
|
||||
|
||||
echo Connecting to Rubrik with IP $RUBRIK_IP
|
||||
|
||||
# API call to list Oracle DBs
|
||||
find_database
|
||||
|
||||
mount_mv () {
|
||||
|
||||
utctime=$($DATE -d"$datestring" +"%Y-%m-%d %H:%M:%S")
|
||||
if [ $? -ne 0 ]; then
|
||||
echo ERROR: Unable to convert supplied timestamp to UTC time
|
||||
exit_with_error
|
||||
fi
|
||||
echo INFO: Requested time is $datestring which is $utctime in UTC
|
||||
|
||||
ENDPOINT="https://$RUBRIK_IP/api/internal/managed_volume/$mvData/snapshot"
|
||||
rest_api_get
|
||||
|
||||
# Select snapshot of data volume with timestamp before requested time
|
||||
read mvDataSnap dtDataSnap < <(echo $(jq -r --arg t "$utctime" '
|
||||
[($t) | strptime("%Y-%m-%d %H:%M:%S")] as $r
|
||||
| .data | map(select(
|
||||
(.date[:19] | strptime("%Y-%m-%dT%H:%M:%S")) as $d
|
||||
| $d <= $r[0]
|
||||
)) | sort_by(.date) | last | "\(.id) \(.date)"' /tmp/rbkresponse.$$))
|
||||
|
||||
echo Snapshot ID to mount is $mvDataSnap from $dtDataSnap
|
||||
|
||||
# List MVs for logs volume
|
||||
ENDPOINT="https://$RUBRIK_IP/api/internal/managed_volume/$mvLogs/snapshot"
|
||||
rest_api_get
|
||||
|
||||
# Select snapshot of logs volume with timestamp after requested time
|
||||
read mvLogsSnap dtLogsSnap < <(echo $(jq -r --arg t "$utctime" '
|
||||
[($t) | strptime("%Y-%m-%d %H:%M:%S")] as $r
|
||||
| .data | map(select(
|
||||
(.date[:19] | strptime("%Y-%m-%dT%H:%M:%S")) as $d
|
||||
| $d >= $r[0]
|
||||
)) | sort_by(.date) | first | "\(.id) \(.date)"' /tmp/rbkresponse.$$))
|
||||
|
||||
echo Snapshot ID of Logs Volume to mount is $mvLogsSnap from $dtLogsSnap
|
||||
|
||||
if [ -z "${RBK_TGT}" ]; then
|
||||
clients=$(hostname)
|
||||
echo INFO: hostname $clients is not fully qualified, adding domain name to it. Use -g flag to override client name pattern
|
||||
clients=$(hostname).$(domainname)
|
||||
else
|
||||
clients=${RBK_TGT}
|
||||
fi
|
||||
|
||||
PAYLOAD="{\"hostPatterns\":[\"$clients\"]}"
|
||||
|
||||
ENDPOINT="https://$RUBRIK_IP/api/internal/managed_volume/snapshot/$mvDataSnap/export"
|
||||
rest_api_post
|
||||
DATA_ENDPOINT=$(cat /tmp/rbkresponse.$$ | jq -r '.links[0].href')
|
||||
|
||||
ENDPOINT="https://$RUBRIK_IP/api/internal/managed_volume/snapshot/$mvLogsSnap/export"
|
||||
rest_api_post
|
||||
LOGS_ENDPOINT=$(cat /tmp/rbkresponse.$$ | jq -r '.links[0].href')
|
||||
|
||||
datajobdone=0
|
||||
logsjobdone=0
|
||||
|
||||
while true; do
|
||||
if [ $datajobdone -eq 0 ]; then
|
||||
ENDPOINT=$DATA_ENDPOINT
|
||||
rest_api_get
|
||||
datastatus=$(cat /tmp/rbkresponse.$$ | jq -r '.status')
|
||||
echo "INFO: Data Mount status is $datastatus"
|
||||
if [[ $datastatus =~ SUCCEEDED|FAILED|CANCELED ]]; then
|
||||
datajobdone=1
|
||||
dataExport=$(cat /tmp/rbkresponse.$$ | jq -r ' .links | .[] | select(.rel=="result") .href')
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $logsjobdone -eq 0 ]; then
|
||||
ENDPOINT=$LOGS_ENDPOINT
|
||||
rest_api_get
|
||||
logsstatus=$(cat /tmp/rbkresponse.$$ | jq -r '.status')
|
||||
echo "INFO: Logs Mount status is $logsstatus"
|
||||
if [[ $logsstatus =~ SUCCEEDED|FAILED|CANCELED ]]; then
|
||||
logsjobdone=1
|
||||
logsExport=$(cat /tmp/rbkresponse.$$ | jq -r ' .links | .[] | select(.rel=="result") .href')
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $logsjobdone -eq 1 ] && [ $datajobdone -eq 1 ]; then
|
||||
echo Data Export details
|
||||
ENDPOINT=$dataExport
|
||||
rest_api_get
|
||||
dataMounts=$(cat /tmp/rbkresponse.$$ | jq -r '.channels[] | ( .ipAddress + ":" + .mountPoint)')
|
||||
ENDPOINT=$logsExport
|
||||
rest_api_get
|
||||
logMounts=$(cat /tmp/rbkresponse.$$ | jq -r '.channels[] | ( .ipAddress + ":" + .mountPoint)')
|
||||
for mountPath in $dataMounts $logMounts; do
|
||||
leaf=$(echo $mountPath | cut -d/ -f4)
|
||||
echo creating folder and mounting $mountPath at $mountpoint/$RBK_SID/$leaf
|
||||
mkdir -p $mountpoint/$RBK_SID/$leaf
|
||||
$MOUNT $mountPath $mountpoint/$RBK_SID/$leaf
|
||||
done
|
||||
|
||||
cleanup
|
||||
exit
|
||||
else
|
||||
sleep 10
|
||||
fi
|
||||
done
|
||||
|
||||
}
|
||||
|
||||
mount_snappable () {
|
||||
|
||||
if [ "$dg_type" == "DataGuardMember" ]; then
|
||||
echo Initiating mount from source $RBK_SID \($grp_name\)
|
||||
else
|
||||
[[ $rac_name != "null" ]] && [[ -n "$rac_name" ]] && string=$rac_name || string=$RBK_HOST
|
||||
echo Initiating mount from source $RBK_SID on $string
|
||||
fi
|
||||
|
||||
if [ -n "${RBK_TGT}" ]; then
|
||||
# API call to get the host ID of the target
|
||||
ENDPOINT="https://$RUBRIK_IP/api/internal/oracle/host?name=$RBK_TGT"
|
||||
rest_api_get
|
||||
|
||||
total=$(cat /tmp/rbkresponse.$$ | jq -r .total)
|
||||
if [ $total -ne 1 ]; then
|
||||
echo ERROR: Target host name of $RBK_TGT does not map to a single host.
|
||||
echo ERROR: Matching hosts are listed here. Please re-run using one of the following:
|
||||
cat /tmp/rbkresponse.$$ | jq -r '.data[].name'
|
||||
exit_with_error
|
||||
fi
|
||||
|
||||
target_id=$(cat /tmp/rbkresponse.$$ | jq -r '.data[0].id')
|
||||
else
|
||||
# API call to get the host ID of the target
|
||||
ENDPOINT="https://$RUBRIK_IP/api/internal/oracle/rac?name=$RAC_TGT"
|
||||
rest_api_get
|
||||
|
||||
total=$(cat /tmp/rbkresponse.$$ | jq -r .total)
|
||||
if [ $total -ne 1 ]; then
|
||||
echo ERROR: Target RAC Cluster name of $RAC_TGT does not map to a single entity:
|
||||
echo ERROR: Matching clusters are listed here. Please re-run using one of the following:
|
||||
cat /tmp/rbkresponse.$$ | jq -r '.data[].name'
|
||||
exit_with_error
|
||||
fi
|
||||
|
||||
target_id=$(cat /tmp/rbkresponse.$$ | jq -r '.data[0].id')
|
||||
fi
|
||||
|
||||
# convert datestamp from string into milliseconds
|
||||
echo requested timestamp is $datestring
|
||||
|
||||
ts=$(date -d"$datestring" +%s)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo Problem with timestamp
|
||||
exit_with_error
|
||||
fi
|
||||
((millis = $ts * 1000))
|
||||
|
||||
if [ ! -z ${oraclehome+x} ]; then
|
||||
configmap="\"ORACLE_HOME\":\"$oraclehome\""
|
||||
fi
|
||||
|
||||
# API call to perform the mount
|
||||
PAYLOAD="{\"recoveryPoint\":{\"timestampMs\":$millis},\"targetOracleHostOrRacId\":\"$target_id\",\"targetMountPath\":\"$mountpoint\",\"shouldMountFilesOnly\":$filesonly,\"advancedRecoveryConfigMap\":{$configmap}}"
|
||||
ENDPOINT="https://$RUBRIK_IP/api/internal/oracle/db/$db_id/mount"
|
||||
|
||||
rest_api_post
|
||||
|
||||
ENDPOINT=$(cat /tmp/rbkresponse.$$ | jq -r '.links[0].href')
|
||||
LOOP=0
|
||||
while true; do
|
||||
rest_api_get
|
||||
status=$(cat /tmp/rbkresponse.$$ | jq -r '.status')
|
||||
if [ $status != "SUCCEEDED" ] && [ $status != "FAILED" ]; then
|
||||
echo Status is $status, checking in 15 seconds
|
||||
if [ $status = "RUNNING" ] && [ ${LOOP} -ne 1 ] ; then
|
||||
LOOP=1
|
||||
sleep 5
|
||||
save=$ENDPOINT
|
||||
ENDPOINT="https://$RUBRIK_IP/api/internal/oracle/db/mount?source_database_name=$dbname"
|
||||
rest_api_get
|
||||
RBK_ID_LV=$(cat /tmp/rbkresponse.$$ | jq -r '.data[] | select(.status=="Mounting") | .id')
|
||||
ENDPOINT=$save
|
||||
fi
|
||||
else
|
||||
if [ $status != "SUCCEEDED" ]; then
|
||||
echo LIVE MOUNT FAILED WITH STATUS $status
|
||||
exit_with_error
|
||||
else
|
||||
echo LIVE MOUNT SUCCEEDED
|
||||
echo "The live mount id is: ${RBK_ID_LV}"
|
||||
# cat /tmp/rbkresponse.$$ | jq -r ' '
|
||||
cleanup
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
sleep 15
|
||||
done
|
||||
}
|
||||
|
||||
if [ "$db_id" == "ManagedVolume" ]; then
|
||||
mount_mv
|
||||
else
|
||||
mount_snappable
|
||||
fi
|
||||
|
||||
cleanup
|
||||
Reference in New Issue
Block a user