177 lines
4.9 KiB
Bash
Executable File
177 lines
4.9 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Perform a Clone of an Oracle DB
|
|
# v1.0 - James Pattinson - October 2025
|
|
#
|
|
# usage: oracle_clone.sh [options] <srcSID> <tgtHOSTNAME>
|
|
#
|
|
# Options:
|
|
# -h <dbhost> Source DB hostname
|
|
# -t <timestamp> Recovery point timestamp "YYYY-MM-DD HH:MM:SS" (uses latest if not specified)
|
|
# -n <newsid> New database SID for the clone
|
|
# -p <pfilepath> Custom pfile for the clone
|
|
# -a <key,value> Advanced Cloning Options (can be used multiple times)
|
|
# -b <pdb1,pdb2> Comma-separated list of PDBs to clone (include only these PDBs. PDB$SEED is always included)
|
|
# -d Dry run mode - show API payload without executing
|
|
#
|
|
# 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
|
|
|
|
usage() { echo "Usage: $0 [options] <srcSID> <tgtHOSTNAME>" 1>&2
|
|
echo "Options:" 1>&2
|
|
echo " -h <dbhost> Source DB hostname" 1>&2
|
|
echo " -t <timestamp> Recovery point \"YYYY-MM-DD HH:MM:SS\"" 1>&2
|
|
echo " -n <newsid> New database SID for clone" 1>&2
|
|
echo " -p <pfilepath> Custom pfile for the clone" 1>&2
|
|
echo " -a <key,value> Advanced Cloning Options (can be used multiple times)" 1>&2
|
|
echo " -b <pdb1,pdb2> Comma-separated list of PDBs to clone (include only these PDBs)" 1>&2
|
|
echo " -d Dry run mode" 1>&2
|
|
exit 1; }
|
|
|
|
declare -a config_pairs
|
|
declare -a pdb_list
|
|
dryrun=false
|
|
|
|
while getopts "h:dt:n:p:a:b:" o; do
|
|
case "${o}" in
|
|
h)
|
|
RBK_HOST=${OPTARG}
|
|
;;
|
|
t)
|
|
datestring=${OPTARG}
|
|
;;
|
|
n)
|
|
newsid=${OPTARG}
|
|
;;
|
|
p)
|
|
custompfile=${OPTARG}
|
|
;;
|
|
a)
|
|
config_pairs+=("${OPTARG}")
|
|
;;
|
|
b)
|
|
IFS=',' read -ra pdb_list <<< "${OPTARG}"
|
|
;;
|
|
d)
|
|
dryrun=true
|
|
;;
|
|
*)
|
|
usage
|
|
;;
|
|
esac
|
|
done
|
|
shift $((OPTIND-1))
|
|
|
|
RBK_SID=$1
|
|
RBK_TGT=$2
|
|
|
|
if [ -z "${RBK_SID}" ] || [ -z "${RBK_TGT}" ]; then
|
|
usage
|
|
fi
|
|
|
|
echo Connecting to Rubrik with IP $RUBRIK_IP
|
|
|
|
# API call to list Oracle DBs
|
|
find_database
|
|
|
|
# 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 Target host name of $RBK_TGT does not map to a single host:
|
|
cat /tmp/rbkresponse.$$ | jq -r '.data[].name'
|
|
exit_with_error
|
|
fi
|
|
|
|
target_id=$(cat /tmp/rbkresponse.$$ | jq -r '.data[0].id')
|
|
|
|
# convert datestamp from string into milliseconds
|
|
if [ -z "$datestring" ]; then
|
|
echo "No timestamp specified, determining latest recoverable"
|
|
ENDPOINT="https://$RUBRIK_IP/api/internal/oracle/db/$db_id/recoverable_range"
|
|
rest_api_get
|
|
datestring=$(cat /tmp/rbkresponse.$$ | jq -r '[.data[].endTime] | max')
|
|
fi
|
|
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))
|
|
|
|
configmap=""
|
|
# Build configmap from -a options
|
|
for pair in "${config_pairs[@]}"; do
|
|
key=$(echo "$pair" | cut -d',' -f1)
|
|
value=$(echo "$pair" | cut -d',' -f2-)
|
|
echo "ACO: $key = $value"
|
|
if [ -n "$configmap" ]; then
|
|
configmap="$configmap,"
|
|
fi
|
|
configmap="$configmap\"$key\":\"$value\""
|
|
done
|
|
|
|
# Build the payload
|
|
PAYLOAD="{\"recoveryPoint\":{\"timestampMs\":$millis},\"targetOracleHostOrRacId\":\"$target_id\",\"shouldRestoreFilesOnly\":false"
|
|
|
|
if [ -n "$newsid" ]; then
|
|
PAYLOAD="$PAYLOAD,\"cloneDbName\":\"$newsid\""
|
|
fi
|
|
|
|
if [ -n "$custompfile" ]; then
|
|
PAYLOAD="$PAYLOAD,\"customPfilePath\":\"$custompfile\""
|
|
fi
|
|
|
|
# Add pdbsToClone array if -b specified
|
|
if [ ${#pdb_list[@]} -gt 0 ]; then
|
|
pdbs_json="\"PDB\$SEED\""
|
|
for pdb in "${pdb_list[@]}"; do
|
|
pdbs_json="$pdbs_json,\"$pdb\""
|
|
done
|
|
echo "PDB: Including PDBs in clone: PDB\$SEED ${pdb_list[*]}"
|
|
PAYLOAD="$PAYLOAD,\"pdbsToClone\":[${pdbs_json}]"
|
|
fi
|
|
|
|
PAYLOAD="$PAYLOAD,\"advancedRecoveryConfigMap\":{$configmap}}"
|
|
|
|
ENDPOINT="https://$RUBRIK_IP/api/internal/oracle/db/$db_id/export"
|
|
|
|
echo "$PAYLOAD" > /tmp/payload.$$
|
|
|
|
if [ "$dryrun" = true ]; then
|
|
echo "Dry run mode - API payload that would be sent:"
|
|
echo $PAYLOAD | jq
|
|
exit 0
|
|
fi
|
|
|
|
rest_api_post_file
|
|
|
|
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 10 seconds
|
|
else
|
|
if [ $status != "SUCCEEDED" ]; then
|
|
echo CLONE FAILED WITH STATUS $status
|
|
cat /tmp/rbkresponse.$$ | jq
|
|
exit_with_error
|
|
else
|
|
echo CLONE SUCCEEDED
|
|
cat /tmp/rbkresponse.$$ | jq
|
|
exit 0
|
|
fi
|
|
fi
|
|
sleep 10
|
|
done
|
|
|
|
cleanup |