#!/bin/bash
#
# This script refreshes an ASM-based Oracle database copy, including refreshing
# the snapshot on the XtremIO, renaming the ASM diskgroup, renaming the
# database, and then starting everything up
#
# THIS IS INTENDED AS AN EXAMPLE ONLY, AND SHOULD NOT BE CONFIGURED PRODUCTION
# READY.  IT IS PROVIDED "AS-IS" WITH NO WARRANTY THAT IT WILL DO ANYTHING
# EXCEPT TAKE UP SPACE ON YOUR SYSTEM (UNLESS YOU'RE USING DEDUP, IN WHICH
# CASE IT MAY NOT EVEN DO THAT!)
#
# Whilst this script can be used to remount a copy of the database on the same
# host as the original database, please be VERY careful doing this as it can
# result in corruption of the primary database if something goes wrong or if it
# is not configured correctly
#
# High-level Steps taken :
#    * Copy database is shutdown
#    * Copy ASM diskgroup is unmounted
#    * XtremIO snapshot is refreshed
#    * ASM diskgroup is renamed then mounted
#    * Datafile locations in database are changed to point to new diskgroup
#    * Database is started in mount mode
#    * Database is renamed using "nid"
#    * Database is opened with resetlogs (required after rename)
#
# The following is NOT handled during the process :
#    * Creation of init.ora file for database. This should be copied manually
#    * Modification of spfile location. Only pfile is supported
#    * Multiple diskgroups, although this could be fairly trivially added
#
#
# Error checking is minimal.  A logfile is created containing all command output
# which can be used to determine what failed.
#
#
# To setup a new copy of the database :
#    1. Create a Consistency Group for the required volumes on XtremIO
#    2. Create a Snapshot of that CG, giving the Snapshot Set a meaningful name
#    3. Map the Snapshot Set volumes to the target host
#    4. Set the CG and SS names (along with XMS details) in the script below
#    5. Create/copy an init*.ora for new name of the database. The ASM DG
#       name references the new pfile should be changed to the NEW DG name.
#    6. Run the refresh script
#
#  Version 1.0 - Scott Howard, scott.howard@emc.com  -  Original Version
#          1.1 - Scott Howard, scott.howard@emc.com  -  Improved and added
#                                                       same-host support
#

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#   CONFIGURATION SETTINGS  ---  CONFIGURATION SETTINGS
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

# SID for the original database and the snapshot copy
OLDDBNAME=MYPRODDB
NEWDBNAME=MYDEVDB

# ASM diskgroup for the original database and the snapshot copy
OLDDGNAME=PRODDG
NEWDGNAME=DEVDG

#
# Devices used by the SNAPSHOT devices.  This setting is manditory when
# re-mounting back onto the same host that the original devices are
# moutned on, otherwise it is optional.
# Do not /dev/sdXX devices, as these can change on reboot. Use udev to
# create something static for the device
#
NEWDGDEVICES='/dev/naa/3514f0c5263600001,/dev/naa/3514f0c5263600002,/dev/naa/3514f0c5263600003,/dev/naa/3514f0c5263600004'

#
# Credentials/cluster name for the XMS where the snapshot refresh will
# take place
#
USERNAME=admin
PASSWORD=Xtrem10
XMS=xms.example.com
CLUSTER=XtremIO1

#
# Consistency Group OR Snapshot Set to refresh FROM, and the Snapshot Set
# name to refresh TO. Only one of the FROM* settings should be supplied
#
FROMCGNAME=OracleProdCG         # <-- use when source is a CG
FROMSSNAME=                     # <-- use when source is a SS
TOSSNAME=OracleDev

#
# File containing environment variables for accessing GRID. This should
# set the correct PATH, ORACLE_HOME and ORACLE_SID for the ASM instance
#
GRIDENV=$HOME/.gridenv

# File to log output to
LOGFILE=log


# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#    In general, no changes are needed below this point
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

log() {
        D=`date +%H:%M:%S`
	echo $D - $*
	echo "---------------------------" >> $LOGFILE
	echo $D - $* >> $LOGFILE
}

fail() {
        echo $* 1>&2
        exit 1
}

dosql() {
	user=${2:-"/ as sysdba"}
        sqlplus -S $user << EOT
        whenever oserror exit 1;
        whenever sqlerror exit sql.sqlcode;
        $1
EOT
        rc=$?
        [ $rc -eq 0 ] || fail "Error $rc running SQL command"
}

# Clear out any previous log file
echo -n > $LOGFILE

[ -f $GRIDENV ] || fail Unable to access GRID environment file - $GRIDENV

if [ -n "$FROMCGNAME" ] && [ -n "$FROMSSNAME" ]; then
	fail Only one of FROMCGNAME or FROMSSNAME should be set
fi

#
# The snap source can either be a Consistency Group (GC) or a Snapshot
# Set (SS). Check which we are using, and set relevant variables to use
# throughout the script.
#
SnapSourceType="Consistency Group"
SnapSource=$FROMCGNAME
SnapSourceCmd="from-consistency-group-id"
if [ -n "$FROMSSNAME" ]; then
	SnapSourceType="Snapshot Set"
	SnapSource=$FROMSSNAME
	SnapSourceCmd="from-snapshot-set-id"
fi

# Print a summary and ask the user to confirm the refresh
cat << EOT
Refreshing FROM :
    Database $OLDDBNAME
    ASM diskgroup $OLDDGNAME
    XtremIO $SnapSourceType $SnapSource

Refreshing TO :
    Database $NEWDBNAME
    ASM diskgroup $NEWDGNAME
    XtremIO Snapshot Set $TOSSNAME

EOT

while true; do
    read -p "Are you sure? " yn
    case $yn in
        [Yy]* ) break;;
        [Nn]* ) exit;;
        * ) echo "Please answer yes or no.";;
    esac
done
echo


#
# Check that the database is under srvctl control, and if not add it
# This should only occur when setting up a new copy
#
srvctl status database -database $NEWDBNAME > /dev/null 2>&1
if [ $? -eq 1 ]; then
	srvctl add database -database $NEWDBNAME -oraclehome $ORACLE_HOME
fi

#
# If NEWDGDEVICES is not specified then check to make sure we do NOT have a
# diskgroup with the "old" name mounted
# 
#
if [ -z "$NEWDGDEVICES" ]; then
	(
		# Set grid environment. Note we are in a subshell so that this does not affect Oracle environment
		. $GRIDENV
		asmcmd lsdg $OLDDGNAME >> $LOGFILE 2>&1
		exit $?    # Exit the subshell but keep the return code from asmcmd
	)
	rc=$?
	if [ $rc -eq 127 ]; then
		fail "Unable to run asmcmd using grid environment"
	fi
	if [ $rc -eq 0 ]; then
		fail "Diskgroup $OLDDGNAME mounted on this host, but NEWDGDEVICES not specified"
	fi
fi

if [ ! -f "$ORACLE_HOME/dbs/init${NEWDBNAME}.ora" ]; then
	fail "pfile for copy database not found - $ORACLE_HOME/dbs/init${NEWDBNAME}.ora"
fi


#
# Stop the previous copy of the database and ASM diskgroup, if they are running
#
log Stopping $NEWDBNAME database
srvctl stop database -database $NEWDBNAME -o immediate
# Return code 2 means database is already stopped
rc=$?
[ $rc -eq 0 ] || [ $rc -eq 2 ] || fail Error stopping database $NEWDBNAME

log Stopping $NEWDGNAME diskgroup
srvctl status diskgroup -diskgroup $NEWDGNAME > /dev/null 2>&1
if [ $? -eq 1 ]; then
	IgnoreDGFail=1
fi
srvctl stop diskgroup -diskgroup $NEWDGNAME
# Return code 2 means diskgroup is already stopped
# If the status fails (eg, DG doesn't exist) then continue anyway
rc=$?
[ $rc -eq 0 ] || [ $rc -eq 2 ] || [ -n "$IgnoreDGFail" ] || fail Error stopping diskgroup $NEWDGNAME


#
# Refresh the snapshot on XtremIO, using the Consistengy Group/Snapshot
# Set (source) and snapshot set (destination) given.  The new snapshots
# set has a different name (which we specify), and is then renamed back
# to by the same as the original set name
#
# During this step errors can occur in two ways - either from curl itself
# (eg, host not reachable) which sets an exit code, or from the XMS (eg,
# Snapshot Set doesn't exist or incorrect password) which we need to check
# the curl output for.
#
log Refreshing snapshot on XtremIO

# Refresh the snapshot
tmpname=${TOSSNAME}.`date +%s`
out=`curl -X POST -d "{\"${SnapSourceCmd}\":\"${SnapSource}\",\"to-snapshot-set-id\":\"${TOSSNAME}\",\"no-backup\":\"true\",\"snapshot-set-name\":\"${tmpname}\"}" -u ${USERNAME}:${PASSWORD} -k -s "https://${XMS}/api/json/v2/types/snapshots?cluster-name=${CLUSTER}"`

[ $? -eq 0 ] || fail Error running curl during refresh

echo $out | grep -qv '"error_code":'
[ $? -eq 0 ] || fail "Error returned from XMS during refresh - $out"

# Rename the snapshot set
out=`curl -X PUT -d "{\"new-name\":\"${TOSSNAME}\"}" -u ${USERNAME}:${PASSWORD} -k -s "https://${XMS}/api/json/v2/types/snapshot-sets?cluster-name=${CLUSTER}&name=${tmpname}"`

[ $? -eq 0 ] || fail Error running curl during rename
echo $out | grep -qv '"error_code":'
[ $? -eq 0 ] || fail "Error returned from XMS during rename - $out"


#
# Rename the ASM diskgroup.  If there are multiple diskgroups (eg, one for
# data, one for logs) then this will need to be repeated for each diskgroup
#
log Renaming ASM Diskgroup $OLDDGNAME to $NEWDGNAME
ASMSTR=""
if [ -n "$NEWDGDEVICES" ]; then
	ASMSTR="asm_diskstring='$NEWDGDEVICES'"
fi
renamedg dgname=$OLDDGNAME newdgname=$NEWDGNAME $ASMSTR verbose=true >> $LOGFILE 2>&1
[ $? -eq 0 ] || fail Error renaming disk group - exiting


#
# Mount new diskgroup.  This first sources a file $GRIDENV which contains
# the required GRID environment details (including ORACLE_SID=+ASM)
# This is done in a subshell using ()'s so the environment is returned to
# normal once it is done
#
log Mounting diskgroup $NEWDGNAME
srvctl start diskgroup -diskgroup $NEWDGNAME 
[ $? -eq 0 ] || fail Error mounting disk group - exiting

# Modify the pfile to have the old DB name temporary, but with the new name as the DB Unique Name
TMPPFILE=`mktemp`
cat $ORACLE_HOME/dbs/init${NEWDBNAME}.ora | egrep -v '^(db_name|db_unique_name)' > $TMPPFILE
echo "db_name = $OLDDBNAME" >> $TMPPFILE
echo "db_unique_name = $NEWDBNAME" >> $TMPPFILE



#
# Start the database in mount mode, and then reconfigure the datafiles,
# tempfiles and logfiles to use the new diskgroup name.  If there are
# multiple diskgroups in use, the rename will need to be done once for
# each.
#
log "Reconfiguring Database to use new diskgroup name"
export ORACLE_SID=$NEWDBNAME
dosql "startup mount pfile='$TMPPFILE'" >> $LOGFILE
renamecmd=`dosql "@renamefile $OLDDGNAME $NEWDGNAME"`
dosql "$renamecmd" >> $LOGFILE


#
# Start the database to allow it to get to a consistent state, then shut
# it down so that we can rename it
#
log "Starting Database"
dosql "alter database open;" >> $LOGFILE
dosql "shutdown immediate" >> $LOGFILE
dosql "startup mount pfile='$TMPPFILE'" >> $LOGFILE


#
# Rename the datbase using "nid"
# nid shuts down the database once it's finished
#
log "Renaming Database $OLDDBNAME to $NEWDBNAME"
echo y | nid TARGET=/ DBNAME=$NEWDBNAME >> $LOGFILE 2>&1
[ $? -eq 0 ] || fail "Error renaming database"


#
# Start the database fully, opening it using resetlogs which is required
# after using nid
#
log Starting Database $NEWDBNAME
export ORACLE_SID=$NEWDBNAME
dosql 'startup mount' >> $LOGFILE
dosql 'alter database open resetlogs;' >> $LOGFILE

rm $TMPPFILE

log Done

