Wednesday, July 8, 2015

Shell Script–Applying a command or a SQL script to all databases in the same host

Hello everyone,

This is my first post on the oracle blogs section!

I really hope the posts I will publish here help some of you to have a more dynamic and comfortable work.

This time I'm publishing a simple KSH script that can allow you to apply a single command or a SQL script to all running databases in a single host.

#!/bin/ksh
###########################
##
## File: apply_to_all.ksh
##
## Author: Jesus Sanchez (jsanchez.consultant@gmail.com)
##
## Changelog:
## 2015-05-11 Jesus Sanchez Created this script
##
######################################################################
###############
## FUNCTIONS ##
###############
#####################
# Utility Functions #
#####################
export LAUNCH_DIR=`pwd`
export SOURCE_DIR=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )
source $SOURCE_DIR/utility_functions.ksh
export TEMPDIR=$LAUNCH_DIR
function printUsage
{
print "USAGE: this script has exlusive modes for running."
print "FILE MODE: PATH/TO/SCRIPT/apply_to_all_db.ksh -f <FILENAME>"
print "COMMAND MODE: PATH/TO/SCRIPT/apply_to_all_db.ksh -c <DOUBLE-QUOTED COMMAND ENDED BY SEMICOLON>"
}
#########################
# Function: crawl
#
# Description: this function will look for pmon running processes,
# gather instance names and "crawl" through them, running the
# command/SQL file on each instance on the same host.
#
# Usage:
#
########################################################
function crawl {
## Crawl through databases and execute command/file
for DBNAME in $(ps -ef | grep pmon | cut -d"_" -s -f3 | grep -v ASM)
do
. oraenv <<_EOORA_
${DBNAME}
_EOORA_
sqlplus / as sysdba<<_EOSQL_
select global_name from global_name;
!echo "[INFO] About to execute: $1";
$1
exit
_EOSQL_
done
msgPrint -info "All done!!!"
}
## MAIN ALGORITHM ##
## Check parameters ##
if [[ -z $2 ]];
then
printUsage
else
if [[ $1 = "-f" ]];
then
msgPrint -info "Using file mode"
crawl "@${2}"
elif [[ $1 = "-c" ]];
then
msgPrint -info "Using command mode"
crawl "${2}"
else
printUsage
fi
fi
exit 0

Now let's explain each part:

  • Functions
    • Utility Functions: refer to this post to check the utility functions I normally use.
      • You'll see debugPrint and msgPrint in most of my shell scripts. Those are used to give a nice format to the message I'm sending to the console/log.
    • Crawl
      • This is the main function that receives the command/sql file to be run in every instance.
  • Main Algorythm
    • for DBNAME in $(ps -ef | grep pmon | cut -d"_" -s -f3 | grep -v ASM)
      • Here, we cycle through all the running instances in the host. Looking for pmon processes and getting only the INSTANCE name from it.
    • crawl "@${2}"
      • In the SQL script mode see how the argument passed to the crawl function is appended to the @ sign, so that we don't have to check for it when running inside the function.
    • crawl "${2}"
      • In the command mode we pass the argument inside double quotes so that it is considered a single argument by the function while running.

With some light modifications, we can get this same schema to work with any of the command line utilities from Oracle. But that's a future project of mine. I will try to add support for RMAN in the next few months as my work allows me to.


Hope that some of you find this post useful.

Tuesday, July 7, 2015

Shell script–Utility functions

Hello fellow DBAs, Oracle and Linux enthusiasts.

Today I'm going to share the script I use to "source" utility functions to the shell scripts I commonly use for my daily DBA tasks.

#!/bin/ksh
###########################
##
## File: utility_functions.ksh
##
## Author: Jesus Sanchez (jsanchez.consultant@gmail.com)
##
## Changelog:
## 2015-05-14 Jesus Sanchez Created this script
##
######################################################################
###############
## FUNCTIONS ##
###############
#####################
# Utility Functions #
#####################
##########################
#
# Function: continueQuestion
#
# Description:
# This function asks the user if all is good to continue
#
# Usage: continueQuestion
#
##############################################
function continueQuestion {
STEP=$1
ANSWER="NONE"
while [[ $ANSWER != "y" && $ANSWER != "Y" && $ANSWER != "n" && $ANSWER != "N" ]]
do
if [[ $ANSWER != "NONE" ]]
then
print "[ATTENTION] Please enter \"y\" or \"n\" without the double quotes and press <ENTER>"
print " "
fi
print -n "[INPUT] Do you want to continue? (y/n) <ENTER> for [y]: "
read ANSWER
if [[ -z $ANSWER ]]
then
ANSWER="y"
fi
done
if [[ $ANSWER = "y" || $ANSWER = "Y" ]]
then
return 0
elif [[ $ANSWER = "n" || $ANSWER = "N" ]]
then
exit -1
fi
}
##########################
#
# Function name: debugPrint
#
# Description:
# This function will print debug messages if the
# main script was called with the -debug option
#
# Usage: debugPrint <double-quoted message>
#
####################
function debugPrint {
if [[ $DEBUG -eq 1 ]]
then
TS=$(date +"%Y-%m-%d %H:%M:%S")
print "[DEBUG][${TS}] $1 "
fi
}
##########################
#
# Function name: msgPrint
#
# Description:
# This function will print debug messages if the
# main script was called with the -debug option
#
# Usage:
# msgPrint -<info|warning|error|critical|input> <double-quoted message>
# msgPrint -<blank|separator>
#
####################
function msgPrint {
type=""
case $1 in
"-blank") type="BLANK";;
"-critical") type="CRITICAL";;
"-error") type="ERROR";;
"-debug") type="DEBUG"
if [[ $# -eq 2 ]]
then
shift
caller=$1
fi;;
"-info") type="INFO";;
"-input") type="INPUT";;
"-notice") type="NOTICE";;
"-none") type="NONE";;
"-separator") type="SEPARATOR";;
"-title") type="TITLE";;
"-warning") type="WARNING";;
*) type="NONE"
esac
shift
message="$*"
# Print
if [[ ${type} = "BLANK" ]]
then
print " "
elif [[ ${type} = "DEBUG" ]]
then
if [[ $DEBUG -eq 1 ]]
then
ts=$(date +"%Y-%m-%d %H:%M:%S")
print "[$type][$ts][$caller] $message "
fi
elif [[ ${type} = "INPUT" ]]
then
print -n "[$type] $message: "
elif [[ ${type} = "SEPARATOR" ]]
then
print "----------------------------------------------"
elif [[ ${type} = "TITLE" ]]
then
SPLITTER="================================================="
print " "
print "$SPLITTER"
print "$message"
print "$SPLITTER"
print " "
elif [[ ${type} = "NONE" ]]
then
print "[ $message ]"
else
print "[$type] $message"
fi
}
##########################
#
# Function name: getArgs
#
# Description:
# This function provides the getopts functionality
# while allowing the use of long operations and list of parameters.
# in the case of a list of arguments for only one option, this list
# will be returned as a single-space-separated list in one single string.
#
# Pre-reqs:
# None
#
# Output:
# GA_OPTION variable will hold the current option
# GA_VALUE variable will hold the value (or list of values) associated
# with the current option
#
# Usage:
# You have to source the function in order to be able to access the GA_OPTIONS
# and GA_VALUES variables
# . getArgs $*
#
####################
function getArgs {
# Variables to return the values out of the function
typeset -a GA_OPTIONS
typeset -a GA_VALUES
# Checking for number of arguments
if [[ -z $1 ]]
then
msgPrint -warning "No arguments found"
msgPrint -info "Please call this function as follows: . getArgs \$*"
exit 0
fi
# Grab the dash
dash=$(echo $1 | grep "-")
# Looking for short (-) or long (--) options
isOption=$(expr index "$dash" "-")
# Initialize the counter
counter=0
# Loop while there are arguments left
while [[ $# -gt 0 ]]
do
if [[ $dash && $isOption -eq 1 ]]
then
(( counter+=1 ))
GA_OPTIONS[$counter]=$1
shift
else
if [[ -z ${GA_VALUES[$counter]} ]]
then
GA_VALUES[$counter]=$1
else
GA_VALUES[$counter]="${GA_VALUES[$counter]} $1"
fi
shift
fi
dash=$(echo $1 | grep "-")
isOption=$(expr index "$dash" "-")
done
# Make the variables available to the main algorithm
export GA_OPTIONS
export GA_VALUES
msgPrint -debug "Please check the GA_OPTIONS and GA_VALUES arrays for options and arguments"
# Exit with success
return 0
}
##########################
#
# Function name: setupLogs
#
# Description:
# This function will setup alog redirection pipe
# that will allow to write to console as well as to a logfile.
#
# Pre-reqs:
# Use the variable $LOGFILE in the main algorythm with
# full path and file name.
#
# Usage:
# setupLog <start|stop> <LOGFILE>
#
####################
function setupLogs {
if [[ $1 = "start" ]]
then
# set up redirects
exec 3>&1 4>&2
FIFO=/tmp/fifo.$$
[[ -e $FIFO ]] || mkfifo $FIFO
if [[ -e $LOGFILE ]]
then
tee -a $LOGFILE < $FIFO >&3 &
else
tee $LOGFILE < $FIFO >&3 &
fi
PID=$!
exec > $FIFO 2>&1
return 0
elif [[ $1 = "stop" ]]
then
PIDLIST=""
for PROCID in $(ps -ef | grep -v grep | grep "$$" | grep tee | tr -s [:space:] | cut -d" " -f2)
do
if [[ -z $(echo $PROCID | grep tee |tr -d [:space:]) && -z $PIDLIST ]]
then
PIDLIST="$PROCID"
else
PIDLIST="$PIDLIST $PROCID"
fi
done
msgPrint -debug "PIDLIST: $PIDLIST"
exec 1>&3 2>&4 3>&- 4>&-
wait $PIDLIST
rm -f /tmp/fifo.$$ && return 0
fi
}
export FIFO
##########################
#
# Function name: setupProfiling
#
# Description:
# This function will setup a log where timestamps will
# be kept for calculating profiling information
#
# Pre-reqs:
# None
#
# Usage:
# setupProfiling <start|stop> <SCRIPT|FUNCTION NAME>
#
####################
function profiling {
caller=${2}
profileLog=/tmp/${caller}_$$.tmp
if [[ $1 == "start" || $1 == "START" ]]
then
touch $profileLog
TS=$(date +"%s")
echo "$2:START:$TS" >> $profileLog
return 0
elif [[ $1 == "stop" || $1 == "STOP" ]]
then
TS=$(date +"%s")
echo "$2:STOP:$TS" >> $profileLog
RT=0
RTH=0
RTM=0
RTS=0
cat $profileLog
STARTTS=$(cat $profileLog | grep START | cut -d":" -f3)
ENDTS=$(cat $profileLog | grep STOP | cut -d":" -f3)
RT=$(( ENDTS-STARTTS ))
if [[ $RT -lt 60 ]]
then
RTS=$RT
elif [[ $RT -lt 3600 ]]
then
RTM=$( echo $RT / 60 | bc)
RT=$( echo $RT % 60 | bc )
RTS=$RT
else
RTH=$( echo $RT / 3600 | bc)
RT=$(( $RT % 3600 ))
RTM=$( echo $RT / 60 | bc)
RT=$( echo $RT % 60 | bc )
RTS=$RT
fi
rm -f $profileLog
return 0
fi
}

Let's add some explanation:

  • continueQuestion
    • This function is a simple "Continue? (y/n)" question. As this is used a lot in interactive scripts, seemed like a good idea for me to add it to the utility functions script.
  • debugPrint
    • For debugging, it adds the timestamp prior to the message. Allows you to keep track of what, where and when something is happening.
  • msgPrint
    • Allows you to format in several ways the output that you want to send to console/log file.

I hope you find this useful and makes your job better!

Monday, July 6, 2015

Shell script–Quickly check Oracle listeners

Hello everyone,

Today I would like to share something with you. In my day to day work, I have encountered more than one place where DEV environments are spread through only some hosts. This normally leads to creating several databases on the same host and to create several listeners to be able to split communication through several ports.

Having this, after OS patches or any OS related activity that would require the database software to be shutdown, it's an usual case that people forget to start listeners.  For this I have this useful script:

#!/bin/ksh
########################
##
## File name: all_lsnr_check.sh
## author: Jesus Alejandro Sanchez
##
## This script allows you to scan the /etc/oratab file and look for all Oracle Homes defined
## there to access the listener.ora file and scan it for all listener names. Once that is done,
## The script will show you the status of each listener and wait for your input to START or STOP
## the listener or just continue to the next one.
##
## Pre-requisites: Set the Oracle environment
##
## 05/26/2014 Version 1.0 Jesus Alejandro Sanchez First version of the working script
##
##############################
# Look for all oracle homes in /etc/oratab
for CURRENT_HOME in `cat /etc/oratab | grep :/ | grep -v agent | cut -d":" -f2 | sort | uniq`
do
echo " "
echo " "
echo "USING $CURRENT_HOME as the current oracle home"
echo "==========================================================================="
ORACLE_HOME=$CURRENT_HOME
for CURRENT_LSNR in $(cat $ORACLE_HOME/network/admin/listener.ora | grep LISTENER |grep -v SID | grep -v ADR |grep -v "#" |cut -d"=" -f1 | sort | uniq)
do
echo " "
echo "CHECKING $CURRENT_LSNR"
echo "-------------------------------------------"
$ORACLE_HOME/bin/lsnrctl status $CURRENT_LSNR
ACTION="read it";
while [[ ! -z $ACTION ]]
do
echo " "
echo "********************************************"
echo "Type START to start the current listener"
echo "type STOP to stop the current listener"
echo "or press &lt;ENTER&gt; to continue"
read ACTION
case $ACTION in
START|start)
$ORACLE_HOME/bin/lsnrctl start $CURRENT_LSNR
$ORACLE_HOME/bin/lsnrctl status $CURRENT_LSNR
;;
STOP|stop)
$ORACLE_HOME/bin/lsnrctl stop $CURRENT_LSNR
$ORACLE_HOME/bin/lsnrctl status $CURRENT_LSNR
;;
*)
ACTION=
;;
esac
done
done
done

Enjoy.

Hope this helps in making your job easier.