Compare commits

...

1 Commits

Author SHA1 Message Date
aa07962766 Clone script improvements 2025-10-15 17:54:20 +01:00

View File

@@ -3,9 +3,9 @@
# Example RSC API call script
# v0.1 - James Pattinson - August 2025
#
# Perfoms a database clone operation
# Performs a database clone operation
#
# usage: rsc_clone.sh -n <newname> -o <optionsfile> -h <targethost> [-s sourcehost] [-t "YYYY-MM-DD HH:MM:SS"] [-d] [-c numChannels] <srcdb>
# usage: rsc_clone.sh -n <newname> -o <optionsfile> -h <targethost> [-s sourcehost] [-t "YYYY-MM-DD HH:MM:SS"] [-d] [-c numChannels] [-p customPfilePath] <srcdb>
#
# Options:
# -n <newname> : db_name / SID of the new cloned database
@@ -15,6 +15,7 @@
# -t "YYYY-MM-DD HH:MM:SS" : Optional timestamp for the recovery point, defaults to latest PIT
# -d : Dry-run mode - show mutation variables without executing the clone
# -c <numChannels> : Optional - number of RMAN channels to configure for the clone
# -p <customPfilePath> : Optional - custom pfile path for the clone
# <srcdb> : Source database name or RSC dbid (if known, can be used directly)
#
# Example options file content:
@@ -24,13 +25,13 @@
# DB_CREATE_FILE_DEST=/u01/app/oracle/oradata/NEWNAME/
# AUDIT_FILE_DEST='/u01/app/oracle/admin/NEWNAME/adump'
usage() { log_error "Usage: $0 -n <newname> -o <optionsfile> -h <targethost> [-s sourcehost] [-t \"YYYY-MM-DD HH:MM:SS\"] [-d] [-c numChannels] <srcdb>"; exit 1; }
usage() { log_error "Usage: $0 -n <newname> -o <optionsfile> -h <targethost> [-s sourcehost] [-t \"YYYY-MM-DD HH:MM:SS\"] [-d] [-c numChannels] [-p customPfilePath] <srcdb>"; exit 1; }
MYDIR="$(dirname "$(realpath "$0")")"
source $MYDIR/rsc_ops.sh
while getopts "n:o:t:h:s:d:v:c:" o; do
while getopts "n:o:t:h:s:dc:p:" o; do
case "${o}" in
n)
newName=${OPTARG}
@@ -58,6 +59,14 @@ while getopts "n:o:t:h:s:d:v:c:" o; do
exit_with_error
fi
;;
p)
customPfilePath=${OPTARG}
# Basic validation - ensure it's an absolute path
if [[ ! "$customPfilePath" =~ ^/ ]]; then
log_error "-p requires an absolute path (starting with /)"
exit_with_error
fi
;;
*)
usage
;;
@@ -147,7 +156,8 @@ get_latest_pit() {
rsc_gql_query
# Get latest endTime
latest_endtime=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleRecoverableRanges.data[] | .endTime' | sort -r | head -n 1)
response_content=$(cat /tmp/rbkresponse.$$)
latest_endtime=$(echo "$response_content" | jq -r '.data.oracleRecoverableRanges.data[] | .endTime' | sort -r | head -n 1)
log_info "Latest PIT (ISO8601): $latest_endtime"
# Convert to unixtime in milliseconds
@@ -194,11 +204,12 @@ get_oracle_host_id() {
rsc_gql_query
# Get all matching host IDs (portable, no mapfile)
host_ids=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleTopLevelDescendants.nodes[] | .id')
response_content=$(cat /tmp/rbkresponse.$$)
host_ids=$(echo "$response_content" | jq -r '.data.oracleTopLevelDescendants.nodes[] | .id')
host_count=$(echo "$host_ids" | grep -c .)
if [[ $host_count -ne 1 ]]; then
log_error "Multiple hosts found for '$1':"
cat /tmp/rbkresponse.$$ | jq -r '.data.oracleTopLevelDescendants.nodes[] | "\(.name) \(.id)"'
echo "$response_content" | jq -r '.data.oracleTopLevelDescendants.nodes[] | "\(.name) \(.id)"'
exit_with_error
fi
# Set the first match (or empty if none)
@@ -224,7 +235,8 @@ if [[ "$1" == *-* ]]; then
gqlVars="$(echo $variables)"
rsc_gql_query
cdmId=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabase.cluster.id')
response_content=$(cat /tmp/rbkresponse.$$)
cdmId=$(echo "$response_content" | jq -r '.data.oracleDatabase.cluster.id')
if [[ -z "$cdmId" ]]; then
log_error "Could not find CDM ID for dbid '$dbid'"
exit 1
@@ -270,27 +282,29 @@ oracleDatabases(filter: $filter) {
gqlVars="$(echo $variables)"
rsc_gql_query
response_content=$(cat /tmp/rbkresponse.$$)
if [[ -n "$node_name" ]]; then
# Filter by source host if specified
dbid=$(cat /tmp/rbkresponse.$$ | jq -r --arg NODE "$node_name" '.data.oracleDatabases.nodes[] | select(.logicalPath[]?.name | test("^" + $NODE + "(\\.|$)")) | .id')
cdmId=$(cat /tmp/rbkresponse.$$ | jq -r --arg NODE "$node_name" '.data.oracleDatabases.nodes[] | select(.logicalPath[]?.name | test("^" + $NODE + "(\\.|$)")) | .cluster.id')
dbid=$(echo "$response_content" | jq -r --arg NODE "$node_name" '.data.oracleDatabases.nodes[] | select(.logicalPath[]?.name | test("^" + $NODE + "(\\.|$)")) | .id')
cdmId=$(echo "$response_content" | jq -r --arg NODE "$node_name" '.data.oracleDatabases.nodes[] | select(.logicalPath[]?.name | test("^" + $NODE + "(\\.|$)")) | .cluster.id')
dbid_count=$(echo "$dbid" | grep -c .)
if [[ "$dbid_count" -ne 1 || -z "$dbid" ]]; then
log_error "Expected exactly one database running on node '$node_name', found $dbid_count:"
cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabases.nodes[] | "\(.dbUniqueName) \(.logicalPath[0].name) \(.id)"'
echo "$response_content" | jq -r '.data.oracleDatabases.nodes[] | "\(.dbUniqueName) \(.logicalPath[0].name) \(.id)"'
cleanup
exit 4
fi
else
# No source host specified, get all matching databases
dbid=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabases.nodes[].id')
cdmId=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabases.nodes[].cluster.id')
dbid=$(echo "$response_content" | jq -r '.data.oracleDatabases.nodes[].id')
cdmId=$(echo "$response_content" | jq -r '.data.oracleDatabases.nodes[].cluster.id')
dbid_count=$(echo "$dbid" | grep -c .)
if [[ "$dbid_count" -ne 1 || -z "$dbid" ]]; then
log_error "Expected exactly one database with name '$1', found $dbid_count:"
cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabases.nodes[] | "\(.dbUniqueName) \(.logicalPath[0].name) \(.id)"'
echo "$response_content" | jq -r '.data.oracleDatabases.nodes[] | "\(.dbUniqueName) \(.logicalPath[0].name) \(.id)"'
cleanup
exit 4
fi
@@ -301,14 +315,20 @@ fi
# Only run UTC conversion if -t was used
if [[ -n "${datestring:-}" ]]; then
utctime=$($DATE -d"$datestring" +"%Y-%m-%d %H:%M:%S")
if [ $? -ne 0 ]; then
echo ERROR: Unable to convert supplied timestamp to UTC time
# Try date first, then gdate for macOS compatibility
if ! utctime=$(date -d"$datestring" +"%Y-%m-%d %H:%M:%S" 2>/dev/null); then
if ! utctime=$(gdate -d"$datestring" +"%Y-%m-%d %H:%M:%S" 2>/dev/null); then
log_error "Unable to convert supplied timestamp to UTC time: $datestring"
exit_with_error
fi
unixtime=$($DATE -d"$datestring" +%s)
fi
# Get unixtime using the same date command that worked
if ! unixtime=$(date -d"$datestring" +%s 2>/dev/null); then
unixtime=$(gdate -d"$datestring" +%s 2>/dev/null)
fi
unixtime_ms=$((unixtime * 1000))
echo INFO: Requested time is $datestring which is $utctime in UTC, unixtime is $unixtime
log_info "Requested time is $datestring which is $utctime in UTC, unixtime is $unixtime"
else
log_info "No time specified, using latest PIT"
get_latest_pit
@@ -334,6 +354,13 @@ else
numChannelsPart=""
fi
# Optionally include customPfilePath in the config
if [[ -n "${customPfilePath:-}" ]]; then
customPfilePathPart="\"customPfilePath\": \"$customPfilePath\","
else
customPfilePathPart=""
fi
variables="
{
\"input\": {
@@ -341,6 +368,7 @@ variables="
\"id\": \"$dbid\",
\"config\": {
$numChannelsPart
$customPfilePathPart
\"targetOracleHostOrRacId\": \"$targetHostId\",
\"shouldRestoreFilesOnly\": false,
\"recoveryPoint\": {
@@ -386,11 +414,11 @@ if [ "$dryrun" = true ]; then
fi
rsc_gql_query
cat /tmp/rbkresponse.$$ | jq
response_content=$(cat /tmp/rbkresponse.$$)
echo "$response_content" | jq
# Save the id from the response
job_id=$(cat /tmp/rbkresponse.$$ | jq -r '.data.exportOracleDatabase.id')
job_id=$(echo "$response_content" | jq -r '.data.exportOracleDatabase.id')
log_info "Job id is $job_id"
gqlCheckStatus='query OracleDatabaseAsyncRequestDetails($input: GetOracleAsyncRequestStatusInput!) {
@@ -419,26 +447,27 @@ gqlVars="$(echo $variables)"
while true; do
rsc_gql_query
status=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabaseAsyncRequestDetails.status')
progress=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabaseAsyncRequestDetails.progress')
response_content=$(cat /tmp/rbkresponse.$$)
status=$(echo "$response_content" | jq -r '.data.oracleDatabaseAsyncRequestDetails.status')
progress=$(echo "$response_content" | jq -r '.data.oracleDatabaseAsyncRequestDetails.progress')
log_info "Job status: $status $progress percent"
if [[ "$status" == "FAILED" ]]; then
log_error "Database clone FAILED"
cat /tmp/rbkresponse.$$ | jq
echo "$response_content" | jq
cleanup
exit 2
elif [[ "$status" == "CANCELLED" ]]; then
log_warn "Database clone CANCELLED"
cleanup
exit 3
elif [[ "$status" == "SUCCEEDED" ]]; then
log_info "Database clone SUCCEEDED"
cat /tmp/rbkresponse.$$ | jq
echo "$response_content" | jq
cleanup
exit 0
fi
sleep 15
done