Oracle Cluster Status Script
Oracle Cluster Status Script
/bin/bash
# Quickly shows a status of all running instances accross a 11g, 12c, 18c+ cluster
# The script just needs to have a working oraenv, if rac-status.sh hangs, you may suffer from
http://bit.ly/2IODPJo (alternatively ,see the -e option)
# History :
# 20190906 - Fred Denis - A new -V option to show the version of the script
# 20190830 - Fred Denis - Show a red "x" also when instances and listeners are disabled
# 20190829 - Fred Denis - Show a red "x" if a service is disabled as well as a legend below the services
table
# 20190828 - Fred Denis - Option -L to always show full hostnames; also fixed a bug with the name of the
cluster shown
# 20190725 - Fred Denis - When STATUS and TARGET are different, shows with a WITH_BACK2
background color and a legend
# Fixed a bug where the recently restarted legend was shown when it should not
# 20190701 - Fred Denis - Minor fixes, alignements issues with the sorting
# - Option -w now also supports d for day, w for week, m for month and y for year to specify
the delay
# 20190620 - Fred Denis - Fixed an issue with the sorting when there was recently restarted instances
# 20190606 - Fred Denis - Show a yellow background when a resource has been restarted less than
DIFF_HOURS hours
# A new -w option can be use to specify a number of hours through the command line
# Owners and groups which contained numbers were ignored, this is fixed
# 20190524 - Fred Denis - Fixed a bug when hostnames had more than 1 "db" pattern in their names
# 20190508 - Fred Denis - Show the whole service name and not only part of it when it contains "."
# 20190104 - Fred Denis - A new -r option to Reverse the colors (useful for clear terminal backgrounds)
# 20190325 - Fred Denis - Solaris sed does not support sed -i, use gsed instead
# New -e option to NOT use oraenv to set the ASM environment but to use the current
manually set environment
# 20190318 - Fred Denis - Dont show the owner:group legend about '' menaing same as above if only 1
Home
# 20190307 - Fred Denis - Added owner:group behind the ORACLE_HOME (useful when owner are
different) -- thanks Andrey for the feature idea !
# Also removed the P for Primary and S for Stanby legend; it looks self explanatory enough
already
# 20190130 - Fred Denis - 11g support (BREAK_HERE); 11g and 12c crsctl outputs are quite different
# 20190122 - Fred Denis - Multi OS support for AWK (especially for Solaris)
# 20181110 - Fred Denis - Show short names in the tables instead of the whole hostnames if possible for
better visibility
# - Col 1 and col 2 now align dynamically depending on the largest element to keep all the
tables well aligned
# - Dynamic calculation of an offser for the status column size depending on the number of
nodes
# - This can also be fixed by setting a non 0 value to COL_NODE_OFFSET on top of the script
# - Better alignements, centered databases and service were not nice, they are now left
aligned which is more clear
# Added default value and options to show and hide some resources (./rac-status.sh -h for
more information)
# 20181009 - Fred Denis - Show the usual blue "-" when a target is offline on purpose instead of a red
"Offline" which was confusing
# 20180227 - Fred Denis - Make the the size of the DB column dynamic to handle very long database
names (Thanks Michael)
# - Added a (P) for Primary databases and a (S) for Stanby for color blind people who
# may not see the difference between white and red (Thanks Michael)
# 20180225 - Fred Denis - Make the multi status like "Mounted (Closed),Readonly,Open Initiated" clear
in the table by showing only the first one
# 20180205 - Fred Denis - There was a version alignement issue with more than 10 different
ORACLE_HOMEs
# - Better colors for the label "White for PRIMARY, Red for STANBY"
# 20171218 - Fred Denis - Modify the regexp to better accomodate how the version can be in the path
(cannot get it from crsctl)
# 20170620 - Fred Denis - Parameters for the size of the columns and some formatting
# 20170619 - Fred Denis - Add a column type (RAC / RacOneNode / Single Instance) and color it
depending on the role of the database
# 20170616 - Fred Denis - Shows an ORACLE_HOME reference in the Version column and an
ORACLE_HOME list below the table
# 20170606 - Fred Denis - A new 12cR2 GI feature now shows the ORACLE_HOME in the STATE_DETAILS
column from "crsctl -v"
# - Example : STATE_DETAILS=Open,HOME=/u01/app/oracle/product/11.2.0.3/dbdev_1
instead of STATE_DETAILS=Open in 12cR1
# 20170518 - Fred Denis - Add a readable check on the ${DBMACHINE} file - it happens that it exists but
is only root readable
# Variables
TMP=/tmp/status$$.tmp # A tempfile
USE_ORAENV="YES" # Use oraenv to set the ASM env (-e changes this to
NO)
REVERSE="NO" # Revert the colors to make them visible, useful for
clear terminal backgrounds
# Choose the information what you want to see -- the last uncommented value wins
SHOW_DB="YES" # Databases
#SHOW_DB="NO"
SHOW_LSNR="YES" # Listeners
#SHOW_LSNR="NO"
SHOW_SVC="YES" # Services
SHOW_SVC="NO"
# Number of spaces between the status and the "|" of the column - this applies before and after the
status
# A value of 2 would print 2 spaces before and after the status and like | Open |
# A value of 99 means that this parameter is dynamically calculated depending on the number of nodes
COL_NODE_OFFSET=99
#
# Different OS support
OS=`uname`
case ${OS} in
SunOS)
AWK=`which gawk` ;
SED=`which gsed` ;;
Linux)
AWK=`which awk` ;
SED=`which sed` ;;
HP-UX)
AWK=`which awk` ;
SED=`which sed` ;;
AIX)
AWK=`which gawk` ;
SED=`which sed` ;;
exit 666 ;;
esac
if [[ ! -f ${AWK} ]]
then
printf "\n\t\033[1;31m%s" "No awk found on your system, cannot continue, if you run Solaris,
please ensure that gawk is in your path"
fi
if [[ ! -f ${SED} ]]
then
printf "\n\t\033[1;31m%s" "No sed found on your system, cannot continue, if you run Solaris, please
ensure that gsed is in your path"
exit 679
fi
show_version()
# An usage function
usage()
`basename $0` - A nice overview of databases, listeners and services running across a GI 12c
END
printf "\n\033[1;37m%-8s\033[m\n" "SYNOPSIS" ;
$0 [-a] [-n] [-d] [-l] [-s] [-o] [-f] [-r] [-u] [-h]
END
`basename $0` needs to be executed with a user allowed to query GI using crsctl; oraenv also has to
be working
`basename $0` will show what is running or not running accross all the nodes of a GI 12c :
- The databases instances (and the ORACLE_HOME they are running against)
- The services
With no option, `basename $0` will show what is defined by the variables :
These variables can be modified in the script itself or you can use command line option to
revert their value (see below)
END
-n Show nothing regardless of the default behavior defined with SHOW_DB, SHOW_LSNR and
SHOW_SVC
-d Revert the behavior defined by SHOW_DB ; if SHOW_DB is set to YES to show the databases
by default, then the -d option will hide the databases
-l Revert the behavior defined by SHOW_LSNR; if SHOW_LSNR is set to YES to show the listeners
by default, then the -l option will hide the listeners
-s Revert the behavior defined by SHOW_SVC ; if SHOW_SVC is set to YES to show the services
by default, then the -s option will hide the services
-g Act as a grep command to grep a pattern from the output (key sensitive)
-g and -v examples :
-c Column to sort by, please have a look at "Sort the database output" in http://bit.ly/2MFkzDw
for more details on this -c option
-o Specify a file to save the crsctl commands output
$ ./rac-status.sh -o /tmp/rac-status_output.log
-f A file to use as input file (one generated by the -o option for example)
$ ./rac-status.sh -f /tmp/rac-status_output.log
-e Do not use oraenv to set the ASM environment but relies on the current environment
-L Do not try to shorten the host names, show the entire host names
-u Shows the Uncolored output (no colors); set WITH_COLORS="NO" on top of the script to have
it permanently
-w Shows a yellow background when a resource has been restarted less than the number of
hours in parameter (default is $DIFF_HOURS)
h for hours (default) d for day, w for week, m for month and y for year can be used to specify
the delay:
$ ./rac-status.sh -w 24 # 24 hours
$ ./rac-status.sh -w 2d # 2 days
$ ./rac-status.sh -w 3m # 3 months
Note : the options are cumulative and can be combined with a "the last one wins" behavior :
$ $0 -a -l # Show everything but the listeners (-a will force show everything then -l will
hide the listeners)
$ $0 -n -d # Show only the databases (-n will force hide everything then -d with
show the databases)
END
exit 123
# Options
case ${OPT} in
L) LONG_NAMES="YES" ;;
g) GREP=${OPTARG} ;;
c) SORT_BY=${OPTARG} ;;
v) UNGREP=${OPTARG} ;;
f) FILE=${OPTARG} ;;
o) OUT=${OPTARG} ;;
e) USE_ORAENV="NO" ;;
r) REVERSE="YES" ;;
w) DIFF_HOURS=${OPTARG} ;;
u) WITH_COLORS="NO" ;;
h) usage ;;
esac
done
DIFF_HOURS_UNIT=${DIFF_HOURS: -1}
if [[ ! "${DIFF_HOURS_UNIT}" =~ [0-9] ]]
then
case ${DIFF_HOURS_UNIT} in
h) NB_HOURS=1 ;;
d) NB_HOURS=24 ;;
w) NB_HOURS=$((24*7)) ;;
m) NB_HOURS=$((24*7*31)) ;;
y) NB_HOURS=$((24*7*31*365)) ;;
esac
DIFF_HOURS=$(($HOURS * $NB_HOURS))
else
DIFF_HOURS_UNIT="h"
HOURS=${DIFF_HOURS}
fi
if [ "$SHOW_DB" = "NO" ]
then
SORT_BY=""
fi
if [[ "${REVERSE}" == "YES" ]]
then
WHITE="30m" ; # Black
fi
if [ -n "$FILE" ] # Input file specified, we wont run any crsctl command and rely on the file as input
then
if [ ! -f ${FILE} ]
then
printf "\n\t\033[1;31m%s\033[m\n\n" "Cannot find the ${FILE} input file; cannot continue"
exit 222
fi
fi
then
if [[ "${USE_ORAENV}" == "YES" ]]
then
ORACLE_SID=`ps -ef | grep pmon | grep asm | ${AWK} '{print $NF}' | sed s'/asm_pmon_//' |
egrep "^[+]"`
export ORAENV_ASK=NO
fi
then
printf "\n\t\033[1;31m%s\033[m\n\n" "Cannot find crsctl, cannot continue, please check if
oraenv works or set your environment manually and use the -e option." ;
exit 777
fi
# Try to find if there is "db" in the hostname, if yes we can delete the common "<clustername>"
pattern from the hosts for visibility
SHORT_NAMES="NO"
then
SHORT_NAMES="YES"
else
CLUSTER_NAME=`olsnodes -c`
fi
NAME_OF_THE_CLUSTER=`olsnodes -c`
# if oracle restart, olsnodes is here but returns nothing, we then set the NODES with the current
hostname
if [ -z "${NODES}" ]
then
NODES=`hostname -s`
fi
if [[ "$WITH_COLORS" == "YES" ]]
then
COLOR_FOR_CLUSTER="\e[1;"${WHITE}
else
COLOR_FOR_CLUSTER=""
fi
then
else
printf "\n"
fi
printf "\n"
then
fi
if [ "$SHOW_LSNR" = "YES" ]
then
fi
if [ "$SHOW_SVC" = "YES" ]
then
crsctl stat res -p -w "TYPE = ora.service.type" >> $TMP # not used, in case we
need it one day
fi
awk '{if ($1 ~ /^NAME=/) {print "BREAK_HERE"; print $0} else {print $0}}' $TMP > $TMP2
cp ${TMP2} ${TMP}
if [ "$SHORT_NAMES" = "YES" ]
then
NB_NODES=`olsnodes | wc -l`
cp ${FILE} ${TMP}
NODES=`grep LAST_SERVER $TMP | awk -F"=" '{print $2}' | sort | uniq | grep -v "^$" | awk '{if
(NR<2){txt=$0} else{txt=txt","$0}} END {print txt}'`
NB_NODES=`grep LAST_SERVER $TMP | awk -F"=" '{print $2}' | sort | uniq | wc -l`
fi # End if [ -z "$FILE" ]
# Define the offset to apply to the status column depending on the number of nodes to make the tables
visible for big implementations
if [ "$COL_NODE_OFFSET" = "99" ]
then
COL_NODE_OFFSET=3 ;
fi
-v REVERSE="$REVERSE" \
-v DIFF_HOURS="$DIFF_HOURS" \
-v HOURS="$HOURS" \
-v DIFF_HOURS_UNIT="$DIFF_HOURS_UNIT" \
'BEGIN\
{ FS = "=" ;
n = split(NODES, nodes, ",") ; # Make a table with the nodes of the cluster
# some colors
COLOR_BEGIN = "\033[1;" ;
#COLOR_BEGIN = "\033[" ;
COLOR_END = "\033[m" ;
RED = "31m" ;
GREEN = "32m" ;
YELLOW = "33m" ;
BLUE = "34m" ;
TEAL = "36m" ;
WHITE = "37m" ;
if (REVERSE == "YES")
COL_NODE = 0 ;
COL_DB = 12 ;
COL_VER = 15 ;
COL_TYPE = 14 ;
return sprintf(COLOR_BEGIN color "%" left "s%s%" right "s" COLOR_END sep, "", str, "" ) ;
}
# Colorize a string
# Get a date in format MM/DD/YYYY HH24:MI:SS and return the rounded number hours difference
between this date and the current date
function diff_hours(a_date)
# Get a string and return it with a nice case: first character in upper case ad the others in lower case
(ABCD => Abcd)
function nice_case(str)
# Print a legend for the recent restarted instances, listeners and services
#
function print_legend_recent_restarted()
if (RECENT_RESTARTED == 1)
printf(COLOR_BEGIN WHITE " %-s\n " COLOR_END, ": Has been restarted less than
"HOURS" "UNIT" ago") ;
function print_legend_status_issue()
if (STATUS_ISSUE == 1)
{ if (a_variable == 1)
function print_a_line(size)
if ( ! size)
{ size = COL_DB+COL_VER+(COL_NODE*n)+COL_TYPE+n+3 ;
printf("%s", COLOR_END"\n") ;
}
# Fill 2 tables with the OH and the version from "crsctl stat res -p -w "TYPE = ora.database.type""
if ($1 == "NAME")
{ type = "DB" ;
tab_lsnr[$2] = $2 ;
type = "LISTENER" ;
tab_svc[$2]=$2 ;
service=$2 ;
{ COL_VER = length(service) +1 ;
type = "SERVICE" ;
DB=$2 ;
{ COL_DB = length(temp[1]) +1 ;
getline; getline ;
if (type == "DB")
while (getline)
if ($1 == "ORACLE_HOME")
{ OH = $2 ;
VERSION = substr($2,RSTART,RLENGTH) ;
dbtype[DB] = $2 ;
}
if ($1 == "ROLE") # Primary / Standby expected
here
{ role[DB] = $2 ;
{ is_enabled[DB,nodes[i]]= $2 ;
while(getline)
{ if ($1 ~ /ENABLED@SERVERNAME/ )
is_enabled[DB,$1] = $2 ;
} else {
break ;
if ($0 ~ /^$/)
{ version[DB] = VERSION ;
oh[DB] = OH ;
if (!(OH in oh_list))
oh_ref++ ;
oh_list[OH] = oh_ref ;
o_list[OH] = OWNER[1] ;
g_list[OH] = GROUP[1] ;
break ;
if (type == "SERVICE")
{ while(getline)
{ is_enabled[DB,nodes[i]]= $2 ;
while(getline)
{ if ($1 ~ /ENABLED@SERVERNAME/ )
is_enabled[DB,$1] = $2 ;
} else {
break ;
if ($0 ~ /^$/)
{ break ;
if (type == "LISTENER")
while(getline)
{ is_enabled[DB,nodes[i]]= $2 ;
while(getline)
{ if ($1 ~ /ENABLED@SERVERNAME/ )
is_enabled[DB,$1] = $2 ;
} else {
break ;
if ($1 == "ENDPOINTS")
port[DB] = $2 ;
break ;
SERVER = $2 ;
while (getline)
status[DB,SERVER] = $2 ;
}
if ($1 == "TARGET") { target[DB,SERVER]=$2 ;}
{ status_details[DB,SERVER] = $0 ;
}
{ COL_NODE = length(status_details[DB,SERVER]) +
COL_NODE_OFFSET ;
}
END { #
# Listeners
printf("\n") ;
print_a_line() ;
x=asorti(tab_lsnr, lsnr_sorted) ;
# It may happen that listeners listen on many ports then it wont fit this column
print_port_later = 1 ;
} else {
dbstatus = status[lsnr_sorted[j],nodes[i]] ;
dbtarget = target[lsnr_sorted[j],nodes[i]] ;
dbdetail = status_details[lsnr_sorted[j],nodes[i]] ;
{ COL_ONLINE=WITH_BACK ;
COL_OTHER=WITH_BACK ;
RECENT_RESTARTED=1 ;
} else {
COL_ONLINE=GREEN ;
COL_OTHER=RED ;
if (dbstatus != dbtarget)
{ COL_ONLINE=WITH_BACK2 ;
COL_OTHER=WITH_BACK2 ;
STATUS_ISSUE=1 ;
}
LISTENER_DISABLED = 1 ;
} else {
if (toupper(lsnr_sorted[j]) ~ /SCAN/)
{ LSNR_TYPE = "SCAN" ;
} else {
LSNR_TYPE = "Listener" ;
{ print_port_later = 0 ;
printf("\n") ;
print_a_line() ;
print_legend_disabled(LISTENER_DISABLED, "Listener") ;
printf("\n") ;
# Services
printf("\n")
# a "---" line under the header
print_a_line(COL_DB+COL_NODE*n+COL_VER+n+2) ;
x=asorti(tab_svc, svc_sorted) ;
service = svc_sorted[j] ;
previous_db = to_print[1] ;
}else {
dbstatus = status[svc_sorted[j],nodes[i]] ;
dbtarget = target[svc_sorted[j],nodes[i]] ;
dbdetail = status_details[svc_sorted[j],nodes[i]] ;
{ COL_ONLINE=WITH_BACK ;
COL_OTHER=WITH_BACK ;
RECENT_RESTARTED=1 ;
} else {
COL_ONLINE=GREEN ;
COL_OTHER=RED ;
if (dbstatus != dbtarget)
COL_ONLINE=WITH_BACK2 ;
COL_OTHER=WITH_BACK2 ;
STATUS_ISSUE=1 ;
SERVICE_DISABLED = 1 ;
} else {
printf("\n") ;
print_a_line(COL_DB+COL_NODE*n+COL_VER+n+2) ;
print_legend_disabled(SERVICE_DISABLED, "Service") ;
printf("\n") ;
# Databases
printf("\n") ;
print_a_line() ;
m=asorti(version, version_sorted) ;
dbstatus = status[version_sorted[j],nodes[i]] ;
dbtarget = target[version_sorted[j],nodes[i]] ;
dbdetail = status_details[version_sorted[j],nodes[i]] ;
#
# Print the status here, all that are not listed in that if ladder will appear in
RED
{ COL_OPEN=WITH_BACK ;
COL_READONLY=WITH_BACK ;
COL_SHUT=WITH_BACK ;
COL_OTHER=WITH_BACK ;
RECENT_RESTARTED=1 ;
} else {
COL_OPEN=GREEN ;
COL_READONLY=WHITE ;
COL_SHUT=YELLOW ;
COL_OTHER=RED ;
if (dbstatus != dbtarget)
COL_OPEN=WITH_BACK2 ;
COL_READONLY=WITH_BACK2 ;
COL_SHUT=WITH_BACK2 ;
COL_OTHER=WITH_BACK2 ;
STATUS_ISSUE=1 ;
if (is_enabled[version_sorted[j],nodes[i]] == 0) # Instance
disabled
{
INSTANCE_DISABLED = 1 ;
} else {
#
# Color the DB Type column depending on the ROLE of the database (20170619)
printf("\n") ;
print_a_line() ;
# Print the OH list and a legend for the DB Type colors underneath the table
if (oh_ref > 1)
printf ("\n\n") ;
previous_group = "" ;
previous_owner = "" ;
if (COL_OWNER%2) { COL_OWNER++ }
if (COL_GROUP%2) { COL_GROUP++ }
g_same_as_above=sprintf(COLOR_BEGIN TEAL "%"(COL_GROUP/2)-1"s%s"
COLOR_END, "", "\47\47") ;
for (x in oh_list)
to_print[oh_list[x]] = x ;
the_oh=to_print[i] ;
owner=o_list[to_print[i]] ;
group=g_list[to_print[i]] ;
previous_group = group ;
previous_owner = owner ;
}
printf ("\n") ;
print_legend_disabled(INSTANCE_DISABLED, "Instance") ;
print_legend_recent_restarted() ;
print_legend_status_issue() ;
print $0 ;
} else {
print $0 ;
}' | sed s'/^/ /' > ${TMP2} # We can reuse TMP2 here
then
if [[ "${SORT_COL}" =~ [1-9] ]]
then SORT_NODE=${SORT_COL}
SORT_COL="c"
fi
# Sort order can only be "r" for reverse or "" for normal
if [[ "${SORT_ORDER}" != "r" ]]
then SORT_ORDER=""
else SORT_ORDER="r"
fi
if [[ ! "${SORT_NODE}" =~ [1-9] ]]
then SORT_NODE=1
fi
SORT_NUM=1
case ${SORT_COL} in
then
SORT_NUM=$(((${SORT_NODE}*2)+2))
SORT_NUM2=$((${SORT_NUM}-1))
s ) SORT_NUM=$(((${SORT_NODE}*2)+6)) ; # Sort by
status (Shutdown, Open)
SORT_NUM2=$((${SORT_NUM}-1)) ;;
SORT_NUM=$(((${TYPE_COL}*2)+1)) ;; # Sort by
Type
esac
cat ${TMP2} | awk 'BEGIN {FS="|"} {print $0; if ($2 ~ "Version"){getline; print $0; exit;}}' > $
{TMP}
tac ${TMP2} | awk '{print $0; if ($0 ~ /---------------/){exit;}}' | tac >> ${TMP}
cp ${TMP} ${TMP2}
fi
if [[ "$WITH_COLORS" == "YES" ]]
then
cat ${TMP2}
else
fi
printf "\n"
if [ -f ${TMP} ]
then
if [ -n "$OUT" ]
then
cp $TMP $OUT
fi
rm -f ${TMP}
fi
if [ -f ${TMP2} ]
then
rm -f ${TMP2}
fi
#************************************************************************************
*********************
# END OF SOURCE
#************************************************************************************
*********************