From 4117287e950eeac3f17c4f2868a1777b6dda2610 Mon Sep 17 00:00:00 2001 From: SupraJames Date: Fri, 24 Oct 2025 16:56:57 +0100 Subject: [PATCH] intial commit --- list_mv.sh | 32 +++++ oracle_funcs.sh | 304 +++++++++++++++++++++++++++++++++++++++++++ rbk_api.conf.example | 26 ++++ rman_db.sh | 121 +++++++++++++++++ rman_logs.sh | 136 +++++++++++++++++++ rubrik_mv_op.sh | 54 ++++++++ 6 files changed, 673 insertions(+) create mode 100755 list_mv.sh create mode 100755 oracle_funcs.sh create mode 100755 rbk_api.conf.example create mode 100755 rman_db.sh create mode 100755 rman_logs.sh create mode 100755 rubrik_mv_op.sh diff --git a/list_mv.sh b/list_mv.sh new file mode 100755 index 0000000..a7dab38 --- /dev/null +++ b/list_mv.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# +# List MVs using API call to CDM, for diagnostic purposes +# Written for Avon +# v1.1 - James Pattinson - October 2023 +# +# usage: list_mv.sh + + +MYDIR="$(dirname "$(readlink -f "$0")")" +source $MYDIR/rbk_api.conf +source $MYDIR/oracle_funcs.sh + +# Script starts here + +echo Service account in use is $ID + +echo ALL MVs + +ENDPOINT="https://$RUBRIK_IP/api/internal/managed_volume" +rest_api_get + +grep -Eo '"name"[^,]*' /tmp/rbkresponse.$$ + +echo NonRelic MVs + +ENDPOINT="https://$RUBRIK_IP/api/internal/managed_volume?is_relic=false" +rest_api_get + +grep -Eo '"name"[^,]*' /tmp/rbkresponse.$$ + +cleanup diff --git a/oracle_funcs.sh b/oracle_funcs.sh new file mode 100755 index 0000000..e237682 --- /dev/null +++ b/oracle_funcs.sh @@ -0,0 +1,304 @@ +#!/bin/bash +# +# Oracle shell script support functions +# Written for Avon +# v1.0 - James Pattinson - October 2023 + +nowait=0 + +LOGFILE=$API_LOG_DIR/api_calls.log +mkdir -p $API_LOG_DIR +tabwidth=25 + +HOST=$(hostname -s) + +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 + + cat /tmp/rbkresponse.$$ | mailx -s "Backup error on ${HOST} for ${ORACLE_SID}. Please investigate" $ALERT_EMAILS + rm -f /tmp/rbkresponse.$$ + rm -f /tmp/mountedDBs.$$ + rm -f $PIDFILE + 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 +} + +check_pid () { + + if [ -f $PIDFILE ] + then + PID=$(cat $PIDFILE) + ps -p $PID > /dev/null 2>&1 + if [ $? -eq 0 ] + then + echo "ERROR: MV already being used by process ID $PID" + ps -fp $PID + exit_with_error + else + ## Process not found assume not running + echo $$ > $PIDFILE + if [ $? -ne 0 ] + then + echo "ERROR: Could not create mvLock file" + exit_with_error + fi + fi + else + echo $$ > $PIDFILE + if [ $? -ne 0 ] + then + echo "ERROR: Could not create mvLock file" + exit_with_error + fi + fi + +} + +check_get_token () { + + if [ -z "${AUTH_TOKEN}" ]; then + + # RSC + id_string=$(echo $ID | cut -d\| -f 2) + # CDM + 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 -u -d $expiration) -lt $(( $($DATE +%s) + 1800 )) ]; 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=$(grep -Eo '"token"[^,]*' /tmp/rbkresponse.$$ | grep -Eo '[^:]*$' | sed 's/\"//g') + SESSION=$(grep -Eo '"sessionId"[^,]*' /tmp/rbkresponse.$$ | grep -Eo '[^:]*$' | sed 's/\"//g') + EXPIRATION=$(grep -Eo '"expirationTime"[^,]*' /tmp/rbkresponse.$$ | cut -d: -f 2-4 | sed 's/\"//g' | sed 's/.000Z//;s/T/Z/') + + 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 +} + +get_mv () { + + ENDPOINT="https://$RUBRIK_IP/api/internal/managed_volume?name=$mv_name&is_relic=false" + rest_api_get + + mvId=$(grep -Eo '"id"[^,]*' /tmp/rbkresponse.$$ | grep -Eo 'ManagedVolume:::[a-z0-9-]*') + numChannels=$(grep -Eo '"numChannels"[^,]*' /tmp/rbkresponse.$$ | cut -d: -f2) + + if [[ $mvId == "" ]]; then + echo ERROR: MV with name $mv_name was not found + exit_with_error + fi + +} + +get_data_mv () { + + mv_name=${HOST}_${ORACLE_SID}_data + + ENDPOINT="https://$RUBRIK_IP/api/internal/managed_volume?name=$mv_name&is_relic=false" + rest_api_get + + mvId=$(grep -Eo '"id"[^,]*' /tmp/rbkresponse.$$ | grep -Eo 'ManagedVolume:::[a-z0-9-]*') + numChannels=$(grep -Eo '"numChannels"[^,]*' /tmp/rbkresponse.$$ | cut -d: -f2) + + if [[ $mvId == "" ]]; then + echo ERROR: MV with name $mv_name was not found + exit_with_error + fi + +} + +get_log_mv () { + + # Look for a log volume. If not present, return the data volume + + mv_name=${HOST}_${ORACLE_SID}_log + ENDPOINT="https://$RUBRIK_IP/api/internal/managed_volume?name=$mv_name&is_relic=false" + rest_api_get + + logMvId=$(grep -Eo '"id"[^,]*' /tmp/rbkresponse.$$ | grep -Eo 'ManagedVolume:::[a-z0-9-]*') + + if [[ $logMvId == "" ]]; then + echo "INFO: Log volume ($mv_name) not found. Logs will be written to DB volume" + logMvPresent=0 + mv_name=${HOST}_${ORACLE_SID}_data + + ENDPOINT="https://$RUBRIK_IP/api/internal/managed_volume?name=$mv_name&is_relic=false" + rest_api_get + + mvId=$(grep -Eo '"id"[^,]*' /tmp/rbkresponse.$$ | grep -Eo 'ManagedVolume:::[a-z0-9-]*') + numChannels=$(grep -Eo '"numChannels"[^,]*' /tmp/rbkresponse.$$ | cut -d: -f2) + + if [[ $mvId == "" ]]; then + echo ERROR: MV with name $mv_name was not found + exit_with_error + fi + else + mvId=$(grep -Eo '"id"[^,]*' /tmp/rbkresponse.$$ | grep -Eo 'ManagedVolume:::[a-z0-9-]*') + numChannels=$(grep -Eo '"numChannels"[^,]*' /tmp/rbkresponse.$$ | cut -d: -f2) + logMvPresent=1 + echo "INFO: Log volume ($mv_name) exists with $numChannels channels" + fi + +} + +open_mv () { + + PIDFILE=/tmp/mvLock_${mv_name}.pid + check_pid + ENDPOINT="https://$RUBRIK_IP/api/internal/managed_volume/$mvId/begin_snapshot" + echo "Opening MV $mv_name with ID $mvId (numChannels is $numChannels)" + rest_api_post_empty + +} + +close_mv () { + + if [ -z "${SLANAME}" ]; then + echo Closing MV $mv_name using default assigned SLA + ENDPOINT="https://$RUBRIK_IP/api/internal/managed_volume/$mvId/end_snapshot" + rest_api_post_empty + else + get_sla + echo Closing MV $mv_name as On Demand with SLA $SLANAME + ENDPOINT="https://$RUBRIK_IP/api/internal/managed_volume/$mvId/end_snapshot" + PAYLOAD="{\"retentionConfig\":{\"slaId\":\"$slaId\"}}" + rest_api_post + 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 $PIDFILE +} diff --git a/rbk_api.conf.example b/rbk_api.conf.example new file mode 100755 index 0000000..7f362be --- /dev/null +++ b/rbk_api.conf.example @@ -0,0 +1,26 @@ +# ID and Key for RSC Service Account (starts with client|) (use single quotes) +ID='client|xxxxxxxxxxxxxxxxxxx' +SECRET=xxxxxxxxxxxxxxxxxxxxxxxx + +# DNS name of Rubrik CDM +RUBRIK_IP= + +# Oracle Settings +MOUNTPOINT_PREFIX=/rubrik_ +# How many hours of archivelog to keep on host. 0 means do not purge logs +HOSTLOGRET=2 + +# Percentage threshold to warn if an MV filesystem is getting full +MV_SPACE_WARN=75 + +# Logging directories + +# API calls +API_LOG_DIR=/tmp/rubrik +# RMAN sessions +RMAN_LOG_DIR=/tmp/rubrik/rman + +# List of email addresses to send failure alerts to +ALERT_EMAILS=root,oracle +# Set to 1 to enable email alert on success also +EMAIL_SUCCESS=1 diff --git a/rman_db.sh b/rman_db.sh new file mode 100755 index 0000000..c049c66 --- /dev/null +++ b/rman_db.sh @@ -0,0 +1,121 @@ +#!/bin/bash +# +# RMAN DB backup with incremental Merge +# Written for Avon +# v1.0 - James Pattinson - October 2023 +# +# usage: rman_db.sh + +MYDIR="$(dirname "$(readlink -f "$0")")" + +export ORACLE_SID=$1 + +. $HOME/.profile + +export ORAENV_ASK=NO +export ORACLE_SID=$1 + +. oraenv + +export ORAENV_ASK=YES + +source $MYDIR/rbk_api.conf +source $MYDIR/oracle_funcs.sh + +#ORACLE_SID=$1 + +usage() { echo "Usage: $0 ]" 1>&2; exit 1; } + +if [ -z "${ORACLE_SID}" ]; then + usage +fi + +#ORAENV_ASK=NO +#. oraenv + +export NLS_DATE_FORMAT='mm-dd-yyyy hh24:mi:ss' +export NLS_LANG=AMERICAN_AMERICA.AL32UTF8 + +MOUNTPOINT=$MOUNTPOINT_PREFIX$ORACLE_SID + +mkdir -p $RMAN_LOG_DIR/$ORACLE_SID/ +RMAN_LOG=$RMAN_LOG_DIR/$ORACLE_SID/rman_${ORACLE_SID}_DB_$(date +%d%m%y).log + +# Disk space check +dusage=$(df -Ph | grep -E "$MOUNTPOINT" | sed s/%//g | awk -v spaceWarn=$MV_SPACE_WARN '{ if($5 >= spaceWarn) print $0;}') +if [ "$dusage" != "" ]; then + echo "WARNING: Disk Space Alert - sending email" + echo "$dusage" | mailx -s "WARNING: Rubrik MV Disk Space Alert On $(hostname) at $(date)" $ALERT_EMAILS +else + echo "INFO: Rubrik MV Disk usage is under threshold of ${MV_SPACE_WARN}%" +fi + +get_data_mv +open_mv + +if [ $? -ne 0 ]; then + echo ERROR: Unable to open MV, aborting + exit_with_error +fi + + +echo Running RMAN with log to $RMAN_LOG + +if [[ $numChannels -eq 1 ]]; then + allocate="allocate channel 'ch1' device type disk format '$MOUNTPOINT/%U';" + release="release channel ch1;" + channel0="$MOUNTPOINT" +else + + for i in $(seq 0 $(($numChannels - 1))); do + allocate+="allocate channel 'c$i' device type disk format '$MOUNTPOINT/c$i/%U';" + release+="release channel c$i;" + done + channel0="$MOUNTPOINT/c0" + +fi + +# Save the current time (minus one hour) to ensure we catch all archive logs +startTime=$(date +%m-%d-%Y\ %H:%M:%S -d '-1 hour') + +# RMAN Part Here +############################################################################### +#cat > /tmp/rman.cmd < /dev/null </dev/null + +close_mv \ No newline at end of file diff --git a/rman_logs.sh b/rman_logs.sh new file mode 100755 index 0000000..690c7e5 --- /dev/null +++ b/rman_logs.sh @@ -0,0 +1,136 @@ +#!/bin/bash +# +# RMAN Log backup +# Written for Avon +# v1.1 - James Pattinson - October 2023 +# +# usage: rman_logs.sh + +MYDIR="$(dirname "$(readlink -f "$0")")" +export ORACLE_SID=$1 + +. $HOME/.profile + +export ORAENV_ASK=NO +export ORACLE_SID=$1 + +. oraenv + +export ORAENV_ASK=YES + +source $MYDIR/rbk_api.conf +source $MYDIR/oracle_funcs.sh + +#ORACLE_SID=$1 + +usage() { echo "Usage: $0 ]" 1>&2; exit 1; } + +if [ -z "${ORACLE_SID}" ]; then + usage +fi + +#ORAENV_ASK=NO +#. oraenv + +export NLS_DATE_FORMAT='mm-dd-yyyy hh24:mi:ss' +export NLS_LANG=AMERICAN_AMERICA.AL32UTF8 + +mkdir -p $RMAN_LOG_DIR/$ORACLE_SID/ +RMAN_LOG=$RMAN_LOG_DIR/$ORACLE_SID/rman_${ORACLE_SID}_LOG_$(date +%d%m%y).log + +get_log_mv +open_mv + +if [ $? -ne 0 ]; then + echo ERROR: Unable to open MV, aborting + exit_with_error +fi + +if [[ $logMvPresent -eq 1 ]] && [[ $numChannels -eq 1 ]] ; then + MOUNTPOINT=$MOUNTPOINT_PREFIX${ORACLE_SID}_log +else + MOUNTPOINT=$MOUNTPOINT_PREFIX$ORACLE_SID +fi + +# Disk space check +dusage=$(df -Ph | grep -E "$MOUNTPOINT" | sed s/%//g | awk -v spaceWarn=$MV_SPACE_WARN '{ if($5 >= spaceWarn) print $0;}') +if [ "$dusage" != "" ]; then + echo "WARNING: Disk Space Alert - sending email" + echo "$dusage" | mailx -s "WARNING: Rubrik MV Disk Space Alert On $(hostname) at $(date)" $ALERT_EMAILS +else + echo "INFO: Rubrik MV Disk usage is under threshold of ${MV_SPACE_WARN}%" +fi + +echo Running RMAN with log to $RMAN_LOG + +if [[ $numChannels -eq 1 ]]; then + allocate="allocate channel 'ch1' device type disk format '$MOUNTPOINT/%U';" + release="release channel ch1;" + channel0="$MOUNTPOINT" +else + + for i in $(seq 0 $(($numChannels - 1))); do + allocate+="allocate channel 'c$i' device type disk format '$MOUNTPOINT/log_c$i/%U';" + release+="release channel c$i;" + done + channel0="$MOUNTPOINT/log_c0" + +fi + +# RMAN Part Here +############################################################################### +rman nocatalog log $RMAN_LOG append > /dev/null </dev/null + +close_mv + +if [[ $HOSTLOGRET -gt 0 ]]; then + + echo "Starting post-backup RMAN log purge" + + # Remove archivelogs from host after backup + ############################################################################### + rman nocatalog log $RMAN_LOG append > /dev/null < -v -o +# +# -d Oracle DBNAME +# -v Volume to operate on, logs or data +# -o Operation to perform - open or close the MV + +MYDIR="$(dirname "$(readlink -f "$0")")" +source $MYDIR/rbk_api.conf +source $MYDIR/oracle_funcs.sh + +usage() { echo "Usage: $0 -d -v -o " 1>&2; exit 1; } + +force=0 + +while getopts "d:v:o:" o; do + case "${o}" in + d) + DBNAME=${OPTARG} + ;; + v) + VOLUME=${OPTARG} + ;; + o) + OPCODE=${OPTARG} + ;; + *) + usage + ;; + esac +done +shift $((OPTIND-1)) + +if [ -z "${DBNAME}" ] || [ -z "${VOLUME}" ] || [ -z "${OPCODE}" ]; then + usage +fi + +# Script starts here + +mv_name=$(hostname -s)_${DBNAME}_${VOLUME} +get_mv + +case $OPCODE in + open) open_mv ;; + close) close_mv ;; + *) echo "ERROR: Invalid opcode. Specify open or close" ; exit_with_error +esac + +cleanup