#!/bin/bash # # Oracle shell script support functions # v0.2 - James Pattinson - March 2022 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 else DATE=$(which 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 } check_http_error () { # All good responses start with a 2 if [ ${http_response:0:1} != "2" ]; then echo FATAL: HTTP error from API call: $http_response. The server responded with: cat /tmp/rbkresponse.$$ ; echo ; exit_with_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" rest_api_get cat /tmp/rbkresponse.$$ | jq -r --arg SID "$RBK_SID" '.data[] | select(.mountedDatabaseId!=null and .mountedDatabaseName==$SID) | .mountedDatabaseId' > /tmp/mountedDBs.$$ # Now get a list of Oracle DBs ENDPOINT="https://$RUBRIK_IP/api/v1/oracle/db?name=$RBK_SID" rest_api_get # 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) for db in $myDBs $myDGs; do id=$(echo $db | cut -d: -f 4) if grep -q $id /tmp/mountedDBs.$$; then continue else 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 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')) fi if [ "$RBK_HOST" = "null" ] && [ "$racname" != "null" ]; then RBK_HOST=$racname fi if [ "$dg_type" == "DataGuardMember" ] && [ "$hostspecified" != "true" ] || [[ "$specifiedhost" == DG_GROUP* ]]; 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" rest_api_get 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 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 else AUTH_TOKEN=$token fi else get_token fi fi } get_token () { 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 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 } # 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 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 } # Given an ENDPOINT of an async job, monitor it check_status () { if [ $nowait -ne 1 ]; then # Check the status in a loop 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 30 seconds else if [ $status != "SUCCEEDED" ]; then echo OPERATION FAILED WITH STATUS $status exit_with_error else echo OPERATION SUCCEEDED return 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.$$ }