diff --git a/check_recoverable_range.sh b/check_recoverable_range.sh new file mode 100755 index 0000000..88d5543 --- /dev/null +++ b/check_recoverable_range.sh @@ -0,0 +1,91 @@ +#!/bin/bash +# +# Fetch recoverable ranges for a given Oracle DB +# v0.2 - James Pattinson - August 2021 +# +# usage: oracle_get_rr.sh [-h ] + +SCRIPT=`basename $0` +echo "script ${SCRIPT}" + +MYDIR="$(dirname "$(realpath "$0")")" +# source $MYDIR/rbk_api.conf +source $MYDIR/oracle_funcs.sh + +usage() { echo "Usage: $0 [-h ] [-g] " 1>&2; exit 1; } + +while getopts "h:" o; do + case "${o}" in + h) + RBK_HOST=${OPTARG} + ;; + *) + usage + ;; + esac +done +shift $((OPTIND-1)) + +RBK_SID=$1 + +if [ -z "${RBK_SID}" ]; then + usage + exit 1 +fi + +if [ `ps -ef | grep ora_pmon_$RBK_SID | grep -v " grep " | wc -l` -eq 0 ]; then + #echo "Sid: $db is down, `hostname -s` " + ROLE="unknown" + STATUS=down +else + #echo "Sid: $db is up, `hostname -s` " + if [ `ps -ef | grep ora_arc[0-9]_$RBK_SID | grep -v " grep " | wc -l` -eq 0 ]; then + STATUS="no-/mnt" + else + STATUS=up + fi + if [ `ps -ef | grep ora_mrp[0-9]_$RBK_SID | grep -v " grep " | wc -l` -eq 0 ]; then + #echo "Sid: $db is stby, `hostname -s` " + ROLE=prim + else + #echo "Sid: $db is prim, `hostname -s` " + ROLE=stby + fi +fi + +echo Connecting to Rubrik with IP $RUBRIK_IP + +ret=0 +# API call to list Oracle DBs +find_database + +echo Fetching Recoverable Ranges for $RBK_SID on $RBK_HOST + +# API call to get the recoverable range +ENDPOINT="https://$RUBRIK_IP/api/internal/oracle/db/$db_id/recoverable_range" +# rest_api_get +# echo "http_response=$(curl -s -k -o /tmp/rbkresponse.$$ -w \"%{http_code}\" -X GET $ENDPOINT -H \"accept: application/json\" -H \"Authorization: Bearer $AUTH_TOKEN\")" +http_response=$(curl -s -k -o /tmp/rbkresponse.$$ -w "%{http_code}" -X GET $ENDPOINT -H "accept: application/json" -H "Authorization: Bearer $AUTH_TOKEN") + +# echo "ret $ret" + +echo -e "SID\t Hostname Status Role Start Time\t\t\tEnd Time\t\t\tStatus" +echo -e "-------- -------- ------- ------- ----------\t\t\t--------\t\t\t------" +if [ $ret -ne 0 ] ; then + RESULT_GET=" No_date No_date KO" + if [ ! -z "${INFO_RR_PROBLEM}" ] ; then + echo "${INFO_RR_PROBLEM}" + fi +else + RESULT_GET=`cat /tmp/rbkresponse.$$ | jq -r '.data[]' | jq -r ' "\(.beginTime)\t\(.endTime)\t\(.status)"'` + if [ -z "${RESULT_GET}" ] ; then + RESULT_GET=" No_date No_date KO" + if [ ! -z "${INFO_RR_PROBLEM}" ] ; then + echo "${INFO_RR_PROBLEM}" + fi + fi +fi + +echo "${RESULT_GET}" | sed "s/^/${RBK_SID} `hostname -s` ${STATUS} ${ROLE} /" | awk '{printf "%-10s %-13s %-7s %-7s %-30s %-31s %-12s\n" ,$1,$2,$3,$4,$5,$6,$7}' + +cleanup diff --git a/oracle_clone.sh b/oracle_clone.sh new file mode 100755 index 0000000..86d4704 --- /dev/null +++ b/oracle_clone.sh @@ -0,0 +1,131 @@ +#!/bin/bash +# +# Perform a Live Mount of an Oracle DB +# v0.2 - James Pattinson - August 2021 +# +# usage: oracle_mount.sh [-h ] [-d [-f] [-t ] +# +# -d specify $ORACLE_HOME on target - needed for mount of Data Guard DBs +# -f mount files only - do not create database +# +# 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 + +usage() { echo "Usage: $0 [-h ] [-d [-f|-n] <\"timestamp\">" 1>&2 + echo "Format Timestamp YYYY-MM-DD HH:MM:SS" + exit 1; } + +filesonly=false + +while getopts "h:d:t:f" o; do + case "${o}" in + h) + RBK_HOST=${OPTARG} + ;; + f) + filesonly=true + ;; + d) + oraclehome=${OPTARG} + ;; + t) + datestring=${OPTARG} + ;; + *) + usage + ;; + esac +done +shift $((OPTIND-1)) + +RBK_SID=$1 + +if [ -z "${RBK_SID}" ]; then + usage +fi + +RBK_SID=$1 +RBK_TGT=$2 + +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)) + +exit + +if [ ! -z ${oraclehome+x} ]; then + configmap="\"ORACLE_HOME\":\"$oraclehome\"" +fi + +# API call to perform the mount +PAYLOAD="{\"recoveryPoint\":{\"timestampMs\":$millis},\"targetOracleHostOrRacId\":\"$target_id\",\"targetMountPath\":\"/rbk/\",\"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 10 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 + # cat /tmp/rbkresponse.$$ | jq -r '.data[]' + 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 ' ' + exit 0 + fi + fi + sleep 10 +done + +cleanup diff --git a/oracle_funcs.sh b/oracle_funcs.sh old mode 100644 new mode 100755 index 17d5d44..0b3468e --- a/oracle_funcs.sh +++ b/oracle_funcs.sh @@ -1,44 +1,53 @@ #!/bin/bash # +#-------------------------------------------------------------------------------------------------------- # Oracle shell script support functions -# v0.2 - James Pattinson - March 2022 +# v0.2 - James Pattinson - August 2021 +# v0.3 - U701053 - 30.03.2022 - if database not found, try to search with the ENDPOINT datagurad +# v0.4 - James Pattinson 25/01/23 - Adding support for Service Accounts +#-------------------------------------------------------------------------------------------------------- + +MYDIR="$(dirname "$(realpath "$0")")" +source $MYDIR/rbk_api.conf + +# -- Mobi config --------- +#RBK_ENV=$OUTI/shell/rubrik/conf/rbk_env +# -- End Mobi config ---- + +# -- Mobi config ----------- +#if [ -f "${RBK_ENV}" ] ; then +# source ${RBK_ENV} +#else +# echo "The ${RBK_ENV} file is not found ...." +# exit_with_error +#fi +# -- End Mobi config ------- nowait=0 -LOGFILE=/tmp/rubrik_oracle.log -tabwidth=25 - -os=$(uname -o) -if [ $os == "Solaris" ]; then - # Use the GNU version of date binary - DATE=/usr/gnu/bin/date +if [[ "$OSTYPE" == "darwin"* ]]; then + DATE=gdate else - DATE=$(which date) + DATE=date fi -echo "`$DATE` -$$-: CALLED $0 $@" >> $LOGFILE - -trap ctrl_c INT - -function ctrl_c () { - echo "`$DATE` -$$-: TRAPPED CTRL-C - EXITING" >> $LOGFILE - exit_with_error -} - -function ctrl_c_inhibit () { - echo "`$DATE` -$$-: TRAPPED CTRL-C - CONTINUING" >> $LOGFILE -} - exit_with_error () { -# if [ $usingsatoken ]; then -# ENDPOINT="https://$RUBRIK_IP/api/v1/session/me" -# rest_api_delete -# check_http_error -# fi rm -f /tmp/rbkresponse.$$ - rm -f /tmp/mountedDBs.$$ echo Aborting Script! - echo "`$DATE` -$$-: EXITED WITH ERROR $0 $@" >> $LOGFILE + exit 1 +} + +# for canceled status +exit_with_cancel () { + rm -f /tmp/rbkresponse.$$ + echo Canceling Script! + exit 1 +} + +# for undoing status +exit_with_undoing () { + rm -f /tmp/rbkresponse.$$ + echo undoing Script! exit 1 } @@ -50,84 +59,8 @@ check_http_error () { fi } -resolve_db () { - -read name db_id sla_id dg_type dg_id RBK_HOST num_instances grp_name rac_name last_snap isrelic < <(echo $(cat /tmp/rbkresponse.$$ | jq -r --arg ID "OracleDatabase:::$id" '.data[] | select(.id==$ID) | .name, .id, .effectiveSlaDomainId, .dataGuardType, .dataGuardGroupId, .instances[0].hostName, .numInstances, .dataGuardGroupName, .racName, .lastSnapshotTime, .isRelic')) -if [ "$dg_id" != "null" ]; then - if [ "$last_snap" != "null" ]; then - [[ $rac_name != "null" ]] && string=$rac_name || string=$RBK_HOST - echo "WARNING: DG member on $string has snapshots created when DB was protected as standalone (non DG)" - echo "WARNING: Latest standalone snapshot taken $last_snap. Use -h $string to view or utilise them" - fi - id=$(echo $dg_id | cut -d: -f 4) - if [[ ! " ${DGDBs[*]} " =~ " ${id} " ]]; then - DGDBs+=("$id") - fi -else - actualDBs+=("$id") -fi - -} - -show_all_dbs () { - -liveDBs=() -liveDBsWithSnaps=() - -for id in "${actualDBs[@]}"; do -# cat /tmp/rbkresponse.$$ | jq -r - read name db_id sla_id dg_type dg_id RBK_HOST num_instances grp_name rac_name isrelic standaloneHostName last_snap db_unique_name < <(echo $(cat /tmp/rbkresponse.$$ | jq -r --arg ID "OracleDatabase:::$id" '.data[] | select(.id==$ID) | .name, .id, .effectiveSlaDomainId, .dataGuardType, .dataGuardGroupId, .instances[0].hostName, .numInstances, .dataGuardGroupName, .racName, .isRelic, .standaloneHostName, .lastSnapshotTime, .dbUniqueName')) - echo -n "WARNING: Object ID $id on " - [[ $standaloneHostName != "null" ]] && echo -n "host $standaloneHostName " - [[ $rac_name != "null" ]] && echo -n "RAC $rac_name " - [[ $grp_name != "null" ]] && echo "DG ID: $grp_name" - [[ $isrelic = "true" ]] && echo -n "(Relic)" || liveDBs+=("$id") - [[ $last_snap != "null" ]] && [[ $isrelic = "false" ]] && liveDBsWithSnaps+=("$id") - echo -done - -#declare -p liveDBs -#declare -p liveDBsWithSnaps - -for id in "${DGDBs[@]}"; do - echo -n "WARNING: Object ID $id on DG Group " - ENDPOINT="https://$RUBRIK_IP/api/v1/oracle/db/OracleDatabase:::$id" - rest_api_get_2 - dgname=$(cat /tmp/rbkresponse2.$$ | jq -r '.dbUniqueName') - rm -f /tmp/rbkresponse2.$$ - echo $dgname -done - -} - -select_db () { - -# We have more than one matching DB. Select the best one -if [ "${#DGDBs[@]}" -eq 1 ]; then - echo WARNING: Selecting Data Guard DB ${DGDBs[0]} - id="${DGDBs[0]}" -elif [ "${#liveDBs[@]}" -eq 1 ]; then - echo WARNING: Selecting object ID ${liveDBs[0]} as it is not a Relic - id="${liveDBs[0]}" -elif [ "${#liveDBsWithSnaps[@]}" -eq 1 ]; then - echo WARNING: Selecting object ID ${liveDBsWithSnaps[0]} as it is not a Relic and has snapshots - id="${liveDBsWithSnaps[0]}" -else - echo "ERROR: Unable to determine unique DB. Please specify one of the above" - exit_with_error -fi - -read name db_id sla_id dg_type dg_id RBK_HOST num_instances grp_name rac_name isrelic standaloneHostName last_snap < <(echo $(cat /tmp/rbkresponse.$$ | jq -r --arg ID "OracleDatabase:::$id" '.data[] | select(.id==$ID or .dataGuardGroupId==$ID) | .name, .id, .effectiveSlaDomainId, .dataGuardType, .dataGuardGroupId, .instances[0].hostName, .numInstances, .dataGuardGroupName, .racName, .isRelic, .standaloneHostName, .lastSnapshotTime')) - -#exit_with_error - - -} - # Given RBK_SID return $db_id of matching database find_database () { - - specifiedhost=$RBK_HOST # First get IDs of all the mounted DBs for this SID ENDPOINT="https://$RUBRIK_IP/api/internal/oracle/db/mount" @@ -139,14 +72,26 @@ find_database () { ENDPOINT="https://$RUBRIK_IP/api/v1/oracle/db?name=$RBK_SID" rest_api_get + # If no dtaabse is found, try with dataguard + if [ `cat /tmp/rbkresponse.$$ | grep "id\":" | wc -l` -eq 0 ]; then + echo " DB not found, try with dataguard..." + ENDPOINT="https://$RUBRIK_IP/api/v1/oracle/db?is_data_guard_group=true&is_relic=false&name=$RBK_SID" + rest_api_get + fi + +#echo "=================================" +#cat /tmp/rbkresponse.$$ | jq -r '.data[]' | tee /tmp/titi +#echo "=================================" +#echo "cat /tmp/rbkresponse.$$ | jq -r '.data[]'" + + # If no host is specified then just look for the DB with the right SID if [ -z $RBK_HOST ]; then # get list of DB IDs in scope (sid matches and not a relic) - myDBs=$(cat /tmp/rbkresponse.$$ | jq -r --arg SID "$RBK_SID" '.data[] | select(.name==$SID) | .id' | sort | uniq) - myDGs=$(cat /tmp/rbkresponse.$$ | jq -r --arg SID "$RBK_SID" '.data[] | select(.name==$SID) | .dataGuardGroupId' | sort | uniq) + myDBs=$(cat /tmp/rbkresponse.$$ | jq -r --arg SID "$RBK_SID" '.data[] | select(.name==$SID and .isRelic==false) | .id' ; cat /tmp/rbkresponse.$$ | jq -r --arg SID "$RBK_SID" '.data[] | select(.name==$SID and .isRelic==false) | .dataGuardGroupId' | sort | uniq) - for db in $myDBs $myDGs; do + for db in $myDBs; do id=$(echo $db | cut -d: -f 4) if grep -q $id /tmp/mountedDBs.$$; then continue @@ -154,37 +99,16 @@ find_database () { break fi done - - myDBs=( $(cat /tmp/rbkresponse.$$ | jq -r --arg SID "$RBK_SID" '.data[] | select(.name==$SID) | .id' | sort | uniq) ) - myDGs=( $(cat /tmp/rbkresponse.$$ | jq -r --arg SID "$RBK_SID" '.data[] | select(.name==$SID) | .dataGuardGroupId' | sort | uniq) ) - actualDBs=() - DGDBs=() - - for db in "${myDBs[@]}"; do - id=$(echo $db | cut -d: -f 4) - resolve_db - done - if [ "$(( ${#actualDBs[@]} + ${#DGDBs[@]} ))" -gt 1 ]; then - echo "WARNING: There are multiple instances of $RBK_SID. Please re run the script with the -h option" - echo "WARNING: to interact with them. Script will attempt to select the correct DB." - show_all_dbs - select_db - else - id="${actualDBs[0]}" - fi - + + # Get the details for the specific DB ID + read name db_id sla_id dg_type dg_id RBK_HOST num_instances grp_name < <(echo $(cat /tmp/rbkresponse.$$ | jq -r --arg ID "OracleDatabase:::$id" '.data[] | select(.id==$ID) | .name, .id, .effectiveSlaDomainId, .dataGuardType, .dataGuardGroupId, .instances[0].hostName, .numInstances, .dataGuardGroupName')) + # Host was specified else - # Host was specified - hostspecified=true - read name db_id sla_id dg_type dg_id RBK_HOST racname num_instances grp_name < <(echo $(cat /tmp/rbkresponse.$$ | jq -r --arg SID "$RBK_SID" --arg HOST "$RBK_HOST" '.data[] | select(.name==$SID and (.infraPath[0].name==$HOST or .dataGuardGroupName==$HOST)) | .name, .id, .effectiveSlaDomainId, .dataGuardType, .dataGuardGroupId, .standaloneHostName, .racName, .numInstances, .dataGuardGroupName')) + read name db_id sla_id dg_type dg_id RBK_HOST num_instances grp_name < <(echo $(cat /tmp/rbkresponse.$$ | jq -r --arg SID "$RBK_SID" --arg HOST "$RBK_HOST" '.data[] | select(.sid==$SID and .infraPath[0].name==$HOST and .isRelic==false) | .name, .id, .effectiveSlaDomainId, .dataGuardType, .dataGuardGroupId, .instances[0].hostName, .numInstances, .dataGuardGroupName')) fi - if [ "$RBK_HOST" = "null" ] && [ "$racname" != "null" ]; then - RBK_HOST=$racname - fi - - if [ "$dg_type" == "DataGuardMember" ] && [ "$hostspecified" != "true" ] || [[ "$specifiedhost" == DG_GROUP* ]]; then + if [ "$dg_type" == "DataGuardMember" ]; then db_id=$dg_id # Now find SLA of the DG GROUP not just the DB (which will be unprotected) ENDPOINT="https://$RUBRIK_IP/api/v1/oracle/db/$db_id" @@ -192,142 +116,98 @@ find_database () { sla_id=$(cat /tmp/rbkresponse.$$ | jq -r '.effectiveSlaDomainId') fi - if [ -z "$db_id" ]; then - locate_mv_db - fi - if [ -z "$db_id" ]; then - echo FATAL: No DB found with SID $RBK_SID on host / cluster $RBK_HOST - exit_with_error + echo FATAL: No DB found with SID $RBK_SID on host $RBK_HOST + if [ "${SCRIPT}" = "check_recoverable_range.sh" ] ; then + export ret=11 + else + exit_with_error + fi + else + echo " db_id is: $db_id" fi } -locate_mv_db () { - - # Look for a pair of MVs with names like $RBK_SID_data and $RBK_SID_logs - - # Get list of MVs - ENDPOINT="https://$RUBRIK_IP/api/internal/managed_volume" - rest_api_get - - mvData=$(cat /tmp/rbkresponse.$$ | jq -r --arg NAME ${RBK_SID}_data '.data[] | select(.name==$NAME)| .id') - mvLogs=$(cat /tmp/rbkresponse.$$ | jq -r --arg NAME ${RBK_SID}_logs '.data[] | select(.name==$NAME)| .id') - - if [ -n "$mvData" ] && [ -n "$mvLogs" ]; then - echo "INFO: MV found (Solaris Database)" - db_id=ManagedVolume - fi - -} - check_get_token () { if [ -z "${AUTH_TOKEN}" ]; then - id_string=$(echo $ID | cut -d: -f 4) - - if [ -f ~/.rbksession.$id_string ]; then - echo "`$DATE` -$$-: AUTH SESSION FILE EXISTS" >> $LOGFILE - read expiration token < <(echo $(cat ~/.rbksession.$id_string)) - if [ $($DATE +%s -d $expiration) -lt $(( $($DATE +%s) + 7200 )) ]; then - echo "`$DATE` -$$-: AUTH TOKEN EXPIRED" >> $LOGFILE - get_token + if [[ "${ID}" =~ ^client ]]; then + # Looks like an RSC service account + id_string=$(echo $ID | cut -d\| -f 2) else - AUTH_TOKEN=$token + # Not an RSC service account + id_string=$(echo $ID | cut -d: -f 4) + fi + + # If there is a cached credential file, use it + if [ -f ~/.rbksession.$id_string ]; then + read expiration token < <(echo $(cat ~/.rbksession.$id_string)) + # If token expires within 30 min, get a new one + if [ $($DATE +%s -d $expiration) -lt $(( $($DATE +%s) + 1800 )) ]; then + get_token + else + AUTH_TOKEN=$token + fi + else + get_token fi - else - get_token - fi fi } get_token () { + + MYENDPOINT="https://$RUBRIK_IP/api/v1/service_account/session" + MYPAYLOAD="{\"serviceAccountId\":\"$ID\",\"secret\":\"$SECRET\"}" - trap '' INT - echo "`$DATE` -$$-: AUTH USER $ID" >> $LOGFILE - MYENDPOINT="https://$RUBRIK_IP/api/v1/service_account/session" - MYPAYLOAD="{\"serviceAccountId\":\"$ID\",\"secret\":\"$SECRET\"}" + http_response=$(curl -s -k -o /tmp/rbkresponse.$$ -w "%{http_code}" -X POST $MYENDPOINT -H "accept: application/json" -H "Content-Type: application/json" -d $MYPAYLOAD) + check_http_error - http_response=$(curl -s -k -o /tmp/rbkresponse.$$ -w "%{http_code}" -X POST $MYENDPOINT -H "accept: application/json" -H "Content-Type: application/json" -d $MYPAYLOAD) - check_http_error - - AUTH_TOKEN=$(cat /tmp/rbkresponse.$$ | jq -r '.token') - SESSION=$(cat /tmp/rbkresponse.$$ | jq -r '.sessionId') - EXPIRATION=$(cat /tmp/rbkresponse.$$ | jq -r '.expirationTime') - echo "`$DATE` -$$-: AUTH SESSION $SESSION" >> $LOGFILE - echo "`$DATE` -$$-: SAVING TOKEN TO FILE" >> $LOGFILE - echo "$EXPIRATION $AUTH_TOKEN" > ~/.rbksession.$id_string - usingsatoken=1 - trap ctrl_c INT + AUTH_TOKEN=$(cat /tmp/rbkresponse.$$ | jq -r '.token') + SESSION=$(cat /tmp/rbkresponse.$$ | jq -r '.sessionId') + EXPIRATION=$(cat /tmp/rbkresponse.$$ | jq -r '.expirationTime') + echo "$EXPIRATION $AUTH_TOKEN" > ~/.rbksession.$id_string + } # HTTP GET: Given $ENDPOINT write output to file rest_api_get () { check_get_token http_response=$(curl -s -k -o /tmp/rbkresponse.$$ -w "%{http_code}" -X GET $ENDPOINT -H "accept: application/json" -H "Authorization: Bearer $AUTH_TOKEN") - echo "`$DATE` -$$-: REST API GET: ENDPOINT $ENDPOINT" >> $LOGFILE - echo "`$DATE` -$$-: REST API GET: RESPONSE STARTS" >> $LOGFILE - cat /tmp/rbkresponse.$$ >> $LOGFILE - echo >> $LOGFILE - echo "`$DATE` -$$-: REST API GET: RESPONSE ENDS" >> $LOGFILE check_http_error } -rest_api_get_2 () { - check_get_token - http_response=$(curl -s -k -o /tmp/rbkresponse2.$$ -w "%{http_code}" -X GET $ENDPOINT -H "accept: application/json" -H "Authorization: Bearer $AUTH_TOKEN") - echo "`$DATE` -$$-: REST API GET: ENDPOINT $ENDPOINT" >> $LOGFILE - echo "`$DATE` -$$-: REST API GET: RESPONSE STARTS" >> $LOGFILE - cat /tmp/rbkresponse2.$$ >> $LOGFILE - echo >> $LOGFILE - echo "`$DATE` -$$-: REST API GET: RESPONSE ENDS" >> $LOGFILE - check_http_error -} - # HTTP POST: Given $ENDPOINT and $PAYLOAD write output to file rest_api_post () { check_get_token http_response=$(curl -s -k -o /tmp/rbkresponse.$$ -w "%{http_code}" -X POST $ENDPOINT -H "accept: application/json" -H "Authorization: Bearer $AUTH_TOKEN" -H "Content-Type: application/json" -d $PAYLOAD) - echo "`$DATE` -$$-: REST API POST: ENDPOINT $ENDPOINT" >> $LOGFILE - echo "`$DATE` -$$-: REST API POST: PAYLOAD $PAYLOAD" >> $LOGFILE - echo "`$DATE` -$$-: REST API POST: RESPONSE STARTS" >> $LOGFILE - cat /tmp/rbkresponse.$$ >> $LOGFILE - echo >> $LOGFILE - echo "`$DATE` -$$-: REST API POST: RESPONSE ENDS" >> $LOGFILE check_http_error } rest_api_post_empty () { check_get_token http_response=$(curl -s -k -o /tmp/rbkresponse.$$ -w "%{http_code}" -X POST $ENDPOINT -H "accept: application/json" -H "Authorization: Bearer $AUTH_TOKEN" -H "Content-Type: application/json") - echo "`$DATE` -$$-: REST API POST: ENDPOINT $ENDPOINT" >> $LOGFILE - echo "`$DATE` -$$-: REST API POST: PAYLOAD " >> $LOGFILE - echo "`$DATE` -$$-: REST API POST: RESPONSE STARTS" >> $LOGFILE - cat /tmp/rbkresponse.$$ >> $LOGFILE - echo >> $LOGFILE - echo "`$DATE` -$$-: REST API POST: RESPONSE ENDS" >> $LOGFILE check_http_error } rest_api_patch () { check_get_token - http_response=$(curl -s -k -o /tmp/rbkresponse.$$ -w "%{http_code}" -X PATCH $ENDPOINT -H "accept: application/json" -H "Authorization: Bearer $AUTH_TOKEN" -H "Content-Type: application/json" -d "$PAYLOAD") - echo "`$DATE` -$$-: REST API PATCH: ENDPOINT $ENDPOINT" >> $LOGFILE - echo "`$DATE` -$$-: REST API PATCH: PAYLOAD $PAYLOAD" >> $LOGFILE - echo "`$DATE` -$$-: REST API PATCH: RESPONSE STARTS" >> $LOGFILE - cat /tmp/rbkresponse.$$ >> $LOGFILE - echo >> $LOGFILE - echo "`$DATE` -$$-: REST API PATCH: RESPONSE ENDS" >> $LOGFILE + http_response=$(curl -s -k -o /tmp/rbkresponse.$$ -w "%{http_code}" -X PATCH $ENDPOINT -H "accept: application/json" -H "Authorization: Bearer $AUTH_TOKEN" -H "Content-Type: application/json" -d $PAYLOAD) check_http_error } rest_api_delete () { check_get_token http_response=$(curl -s -k -o /tmp/rbkresponse.$$ -w "%{http_code}" -X DELETE $ENDPOINT -H "accept: application/json" -H "Authorization: Bearer $AUTH_TOKEN") - echo "`$DATE` -$$-: REST API DELETE: $http_response: ENDPOINT $ENDPOINT" >> $LOGFILE check_http_error } +get_cluster_uuid () { + ENDPOINT="https://$RUBRIK_IP/api/v1/cluster/me" + rest_api_get + cluster_uuid=$(cat /tmp/rbkresponse.$$ | jq -r .id) +} + # Given an ENDPOINT of an async job, monitor it check_status () { if [ $nowait -ne 1 ]; then @@ -335,34 +215,33 @@ check_status () { while true; do rest_api_get status=$(cat /tmp/rbkresponse.$$ | jq -r '.status') - if [ $status != "SUCCEEDED" ] && [ $status != "FAILED" ]; then + if [ $status != "SUCCEEDED" ] && [ $status != "FAILED" ] && [ $status != "CANCELED" ] && [ $status != "UNDOING" ]; then echo Status is $status, checking in 30 seconds else if [ $status != "SUCCEEDED" ]; then - echo OPERATION FAILED WITH STATUS $status - exit_with_error + + if [ $status = "CANCELED" ] ; then + echo OPERATION FAILED WITH STATUS $status + exit_with_cancel + elif [ $status = "UNDOING" ] ; then + echo OPERATION FAILED WITH STATUS $status + exit_with_undoing + else + echo OPERATION FAILED WITH STATUS $status + exit_with_error + fi else echo OPERATION SUCCEEDED - return + exit 0 fi fi sleep 30 done - else - # Show the Job ID - JOB_ID=$(cat /tmp/rbkresponse.$$ | jq -r '.id') - echo "Job submittsed. For more details of the job run:" - echo "oracle_jobs.sh -d $RBK_SID -j $JOB_ID" - echo "RMAN logs, if applioable, will be in /var/log/rubrik/script_logs/" fi } cleanup () { -# if [ $usingsatoken ]; then -# ENDPOINT="https://$RUBRIK_IP/api/v1/session/me" -# rest_api_delete -# fi - echo "`$DATE` -$$-: EXITED $0 $@" >> $LOGFILE rm -f /tmp/mountedDBs.$$ rm -f /tmp/rbkresponse.$$ + rm -f /tmp/payload.$$ } diff --git a/oracle_list_db.sh b/oracle_list_db.sh new file mode 100755 index 0000000..cf094ac --- /dev/null +++ b/oracle_list_db.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# +# Example API call script for Die Mobiliar +# v0.2 - James Pattinson - August 2021 +# +# Lists the registered DBs for a given Oracle Host or RAC +# and their assigned SLAs +# +# usage: oracle_list_db.sh + +MYDIR="$(dirname "$(realpath "$0")")" +# source $MYDIR/rbk_api.conf +source $MYDIR/oracle_funcs.sh + +if [ $# -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +RBK_HOST=$1 + +echo Connecting to Rubrik with IP $RUBRIK_IP + +# API call to list Oracle DBs +ENDPOINT="https://$RUBRIK_IP/api/internal/oracle/db" + +rest_api_get + +echo -e "SID\t\tSLA\t\t\tArchivelogMode\tDG Type\t\tDG Group" +echo -e "---\t\t---\t\t\t--------------\t-------\t\t--------" +cat /tmp/rbkresponse.$$ | jq -r --arg HOST $RBK_HOST '.data[] | select(.infraPath[0].name==$HOST and .isRelic==false) | {sid,effectiveSlaDomainName,isArchiveLogModeEnabled,dataGuardType,dataGuardGroupName} ' | jq -r ' "\(.sid)\t\(.effectiveSlaDomainName)\t\t\(.isArchiveLogModeEnabled)\t\t\(.dataGuardType)\t\(.dataGuardGroupName)"' + +cleanup diff --git a/oracle_mount.sh b/oracle_mount.sh old mode 100644 new mode 100755 index d321bd7..d33002e --- a/oracle_mount.sh +++ b/oracle_mount.sh @@ -6,54 +6,32 @@ # usage: oracle_mount.sh [-h ] [-d [-f] <"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 +# -f mount files only - do not create database # # 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/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 [-h ] [-d [-f|-n] <\"timestamp\">" 1>&2 + echo "Format Timestamp YYYY-MM-DD HH:MM:SS" + exit 1; } -usage() { echo "Usage: $0 <-d DBNAME> <-t \"timestamp\"> <-g tgthost || -r rachost> [-h ] [-o ] [-m [-l|-n]" 1>&2; exit 1; } +filesonly=false -filesonly=true - -while getopts "d:t:g:r:m:h:o:ln" o; do +while getopts "h:d:f" o; do case "${o}" in - d) - RBK_SID=${OPTARG} - ;; - t) - datestring=${OPTARG} - ;; - g) - RBK_TGT=${OPTARG} - ;; - r) - RAC_TGT=${OPTARG} + h) + RBK_HOST=${OPTARG} + ;; + f) + filesonly=true ;; - m) - mountpoint=${OPTARG} + d) + oraclehome=${OPTARG} ;; - h) - RBK_HOST=${OPTARG} - ;; - o) - oraclehome=${OPTARG} - ;; - l) - filesonly=false - ;; - n) - nowait=true - ;; - *) usage ;; @@ -61,227 +39,84 @@ while getopts "d:t:g:r:m:h:o:ln" o; do done shift $((OPTIND-1)) -if [ -z "${RBK_SID}" ] || [ -z "${datestring}" ]; then +RBK_SID=$1 + +if [ -z "${RBK_SID}" ]; 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 +RBK_SID=$1 +RBK_TGT=$2 echo Connecting to Rubrik with IP $RUBRIK_IP # API call to list Oracle DBs find_database -mount_mv () { +# API call to get the host ID of the target +ENDPOINT="https://$RUBRIK_IP/api/internal/oracle/host?name=$RBK_TGT" +rest_api_get - 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 +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 +datestring=$3 + +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\":\"/rbk/\",\"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 10 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 + # cat /tmp/rbkresponse.$$ | jq -r '.data[]' + 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 ' ' + exit 0 + fi + fi + sleep 10 +done + cleanup