Search This Blog

Total Pageviews

Friday 29 September 2023

Oracle patch Info


Oracle patch Info 


from web

list-ohpatches.sh



#!/bin/bash
# Fred Denis -- fred.denis3@gmail.com -- May 21st 2021
#
# Show nice tables of the installed and/or missing patches for some GI/DB Oracle Homes
#
set -o pipefail
#
# Variables
#
     TS="date "+%Y-%m-%d_%H%M%S""                           # A timestamp for a nice outut in a logfile
   GREP="."                                                 # What we grep                  -- default is everything
 UNGREP="nothing_to_ungrep_unless_v_option_is_used$$"       # What we don't grep (grep -v)  -- default is nothing
   COLS=$(tput cols)                                        # Size of the screen
 ORACLE="oracle"                                            # User to run opatch lspatches if script ran as root
#
# Cleanup on exit -- this will be executed on normal exit as well as if the script is killed
# The place to cleanup things / send emails whatever happens to the script
#
cleanup() {
    err=$?
    if [[ -s "${TEMP2}" ]]; then
        if [[ "${err}" == "0" ]]; then  # If already an error, no need to check for nb missing patches
            # Check for errors
            NB_ERR=$(cat "${TEMP2}" | awk '{cpt+=$1} END {print cpt}')
            exit "${NB_ERR}"
        fi
    fi
    # Delete tempfiles
    rm -f "${TEMP}" "${TEMP2}"
    exit ${err}
}
sig_cleanup() {
    printf "\033[1;31m%s\033[m\n" "$($TS) [ERROR] I have been killed !" >&2
    exit 666
}
trap     cleanup EXIT
trap sig_cleanup INT TERM QUIT
#
# Usage function
#
usage() {
    printf "\n\033[1;37m%-8s\033[m\n" "NAME"                ;
    cat << END
        $(basename $0) - Show nice tables of the installed and/or missing patches for some GI/DB Oracle Homes
END

    printf "\n\033[1;37m%-8s\033[m\n" "SYNOPSIS"            ;
    cat << END
        $0 [-g] [-c] [-G] [-v] [-s] [-u] [-h]
END

    printf "\n\033[1;37m%-8s\033[m\n" "DESCRIPTION"         ;
    cat << END
        $(basename $0) Based on oratab, show nice tables of the installed and/or missing patches for some GI/DB Oracle Homes
                       You can then quickly find a missing patch across a RAC system
        $(basename $0) will by default check all the nodes of a cluster (based on olsnodes) which requires ASM to be running
                       and oraenv to be working with the ASM aslias defined in oratab; If you have no ASM alias in oratab,
                       you may suffer from https://unknowndba.blogspot.com/2019/01/lost-entries-in-oratab-after-gi-122.html
                       You can specify a comma separated list of host or a file containing one host per line
        $(basename $0) by default checks all the homes defined in oratab, you can use --grep/--home and --ungrep/--ignore to limit your home selection (see examples below)
        $(basename $0) relies on opatch lspatches which must run as oracle user (and not root); if the script is started as root,
                       the opatch lspatches commands will be run after su - ${ORACLE} (see -u | --oracleuser for more on this)

END

    printf "\n\033[1;37m%-8s\033[m\n" "OPTIONS"             ;
    cat << END
        -g | --groupfile            ) A group file containing a list of hosts
        -c | --commalist  | --hosts ) A comma separated list of hosts
        -G | --grep | --oh | --home ) Pattern to grep from /etc/oratab
        -v | --ungrep | --ignore    ) Pattern to grep -v (ignore) from /etc/oratab
        -s | --showhomes | --show   ) Just show the homes from oratab resolving the grep/ungrep combinations
        -u | --oracleuser           ) User to use to run opatch lspatches if the script is started as root, default is ${ORACLE}
        -h | --help                 ) Shows this help

END

    printf "\n\033[1;37m%-8s\033[m\n" "EXAMPLES"            ;
    cat << END
       $0                                                       # Analyze and show all the homes of nodes of a cluster
       $0 --show                                                # Show the homes from oratab (only show, dont do anything else)
       $0 --grep grid                                           # Analyze the grid home
       $0 --grep db --ungrep 12                                 # Only the DB homes but not the 12 ones
       $0 --grep db --ungrep 12 --groupfile ~/dbs_group         # Same as above on the hosts contained in the ~/dbs_group file
       $0 --home db --ignore 12 --hosts exa01,exa06             # Same as above but only on hosts exa02 and exa06
       $0 --home db --ignore 12 --hosts exa01,exa06 -u oracle2  # Same as above but started as root; will then su - oracle2 automatically

END
exit 999
}
#
# Options -- Long and Short, options needs to be separa
# Options are comma separated list, options requiring a parameter need to be followed by a ":"
#
SHORT="g:,c:,g:,v:,u:,sh"
 LONG="groupfile:,commalist:,hosts:,grep:,oh:,home:,ungrep:,ignore:,oracleuser:,showhomes,help"
# Check if the specified options are good
options=$(getopt -a --longoptions "${LONG}" --options "${SHORT}" -n "$0" -- "$@")
# If not, show the usage and exit
if [[ $? -ne 0 ]]; then
    printf "\033[1;31m%s\033[m\n" "$($TS) [ERROR] Invalid options provided: $*; use -h for help; cannot continue." >&2
    exit 864
fi
#
eval set -- "${options}"
# Option management, not the "shift 2" when an option requires a parameter and "shift" when no parameter needed
while true; do
    case "$1" in
        -g | --groupfile           )      GROUP="$2"        ; shift 2 ;;
        -c | --commalist | --hosts )      HOSTS="$2"        ; shift 2 ;;
        -G | --grep | --oh | --home)       GREP="$2"        ; shift 2 ;;
        -v | --ungrep | --ignore   )     UNGREP="$2"        ; shift 2 ;;
        -u | --oracleuser          )     ORACLE="$2"        ; shift 2 ;;
        -s | --showhomes           ) SHOW_HOMES="True"      ; shift   ;;
        -h | --help                ) usage                  ; shift   ;;
        --                         ) shift                  ; break   ;;
    esac
done
#
# Different OS support
#
OS=$(uname)
case ${OS} in
        SunOS)
                    ORATAB=/var/opt/oracle/oratab
                       AWK=/usr/bin/gawk                        ;;
        Linux)
                    ORATAB=/etc/oratab
                       AWK=`which awk`                          ;;
        HP-UX)
                    ORATAB=/etc/oratab
                       AWK=`which awk`                          ;;
        AIX)
                    ORATAB=/etc/oratab
                       AWK=`which awk`                          ;;
        *)          echo "Unsupported OS, cannot continue."
                    exit 666                                    ;;
esac
#
# Options verifications
#
if [[ $(id -u) -eq 0 ]]; then                    # We are root
    su - "${ORACLE}" -c id > /dev/null 2>&1      # Check if we can su as ${ORACLE}
    if [ $? -ne 0 ]; then
        printf "\033[1;31m%s\033[m\n" "$($TS) [ERROR] Script has been executed as root and the user to use to run opatch lspatches is ${ORACLE}; unfortunately we were unable to connect to this user; cannot continue." >&2
        exit 122
    fi
fi
if [[ ! -f "${ORATAB}" ]]; then
    printf "\033[1;31m%s\033[m\n" "$($TS) [ERROR] Cannot find ${ORATAB}; cannot continue." >&2
    exit 123
fi
if [[ ! -f "${AWK}" ]]; then
    printf "\033[1;31m%s\033[m\n" "$($TS) [ERROR] Cannot find a modern versin of awk; cannot continue." >&2
    exit 124
fi
#
# Show Homes only if -s option specified
#
if [[ "${SHOW_HOMES}" == "True" ]]; then
    printf "\n\033[1;37m%-8s\033[m\n\n" "ORACLE_HOMEs in ${ORATAB}:"                    ;
    cat ${ORATAB} | grep -v "^#" | grep -v "^$" | grep -v agent | ${AWK} 'BEGIN {FS=":"} { printf("\t%s\n", $2)}' | grep ${GREP} | grep -v ${UNGREP} | sort | uniq
    printf "\n"
    exit 0
fi
if [[ -z "${HOSTS}" ]]; then           # HOSTS is empty, -c option not provided
    if [[ -f "${GROUP}" ]]; then       # Group file exists, we make it a comma separated list
        HOSTS=$(cat "${GROUP}" | grep -v "^$" | grep -v "^#" | ${AWK} '{printf("%s,", $1)}' | sed s'/,$//')
    else                               # No group file nor hosts lists, lets get the node list from olsnodes
        . oraenv <<< $(ps -ef | grep pmon | grep asm | awk '{print $NF}' | sed s'/.*+/+/') > /dev/null 2>&1
        if [ $? -ne 0 ]; then
            printf "\033[1;31m%s\033[m\n" "$($TS) [ERROR] ASM does not seem to be running and/or oraenv not working so we cannot guess the node list; please use -g or -c to specify a node list then and restart." >&2
            exit 125
        else                           # ASM env all set, lets get the nodes list automatically
            HOSTS=$($(which olsnodes) | ${AWK} '{printf ("%s,",$1)}' | sed s'/,$//')
        fi
    fi
fi
#
#
#
TEMP2=$(mktemp)
printf "\033[1;36m%s\033[m\n" "$($TS) [INFO] Starting collecting GI/OH patch information"
printf "\033[1;33m%s\033[m\n" "$($TS) [WARNING] It may be a bit slow if you have many nodes and patches as opatch lspatches is slow"
for OH in $(cat ${ORATAB} | grep -v "^#" | grep -v "^$" | grep -v agent | grep ${GREP} | grep -v ${UNGREP} | awk 'BEGIN {FS=":"} {print $2}'| sort | uniq); do
    if [[ -f "${OH}/OPatch/opatch" ]] && [[ -x "${OH}/OPatch/opatch" ]]; then
        TEMP=$(mktemp)
        [[ $(id -u) -eq 0 ]] && chmod 777 "${TEMP}"
        printf "\033[1;36m%s\033[m\n" "$($TS) [INFO] Proceeding with ${OH} . . ."
        for HOST in $(echo ${HOSTS} | sed 's/,/ /g'); do
            if [[ $(id -u) -eq 0 ]]; then                   # Script started as root, need to sudo as oracle for opatch lspatches
                su - "${ORACLE}" << END
                    ssh -q "${HOST}" "${OH}/OPatch/opatch lspatches" | grep "^[1-9]" | sort | awk -v H="${HOST}" -F ";" '{print H";"\$1";"\$2}' | sed 's/(.*)//g' >> "${TEMP}"
END
            else
                ssh -q "${HOST}" "${OH}/OPatch/opatch lspatches" | grep "^[1-9]" | sort | awk -v H="${HOST}" -F ";" '{print H";"$1";"$2}' | sed 's/(.*)//g' >> "${TEMP}"
            fi
        done
        "${AWK}" -v hosts="${HOSTS}" -v cols="${COLS}" -v tempfile="${TEMP2}" \
        'BEGIN {          FS =       ";"                    ;
                    # some colors
                 COLOR_BEGIN =       "\033[1;"              ;
                   COLOR_END =       "\033[m"               ;
                         RED =       "31m"                  ;
                       GREEN =       "32m"                  ;
                      YELLOW =       "33m"                  ;
                        BLUE =       "34m"                  ;
                        TEAL =       "36m"                  ;
                       WHITE =       "37m"                  ;

                     MISSING =       "Missing"              ; # Patch is missing
                        HERE =       "x"                    ; # Patch is installed

                    # Default columns size
                    COL_NODE =        8                     ;
                   COL_PATCH =        6                     ;
                   COL_DESCR =       10                     ;
                        cols =       cols -5                ; # Screen size, dont want to be too short

                  nb_missing = 0                            ; # Number of missing patches
                   split(hosts, tab_hosts, ",")             ; # An array with the hosts; n is number of hosts
                   n = asort(tab_hosts)                     ; # Sort by hostname
                   for (x in tab_hosts){
                       if (length(tab_hosts[x]) > COL_NODE) {COL_NODE = length(tab_hosts[x]) + 2}
                   }
        }
        #
        # A function to center the outputs with colors
        #
        function center( str, col_size, color, sep) {       right = int((col_size - length(str)) / 2)                                                                      ;
            left  = col_size - length(str) - right                                                                         ;
            return sprintf(COLOR_BEGIN color "%" left "s%s%" right "s" COLOR_END sep, "", str, "" )                 ;
        }
        #
        # A function that just print a "---" white line
        #
        function print_a_line(size){
            printf("%s", COLOR_BEGIN WHITE)                          ;
            for (k=1; k<=size; k++) {printf("%s", "-");}             ;       # n = number of nodes
            printf("%s", COLOR_END"\n")                              ;
        }
        {   # Save all the patches list
            if ($2 in all_patches){ cpt++ ;
            } else {
                all_patches[$2] = $3 ;
                if (length($2) > COL_PATCH) {COL_PATCH = length($2) + 2}
                if (length($3) > COL_DESCR) {COL_DESCR = length($3) + 1}
            }
            # Save all the patches per node
            tab_patches[$1][$2] = $2  ;
        }
        END {
            # To make it fit and nice depending on the screen size
            out_size=(COL_PATCH+n*COL_NODE+COL_DESCR+n+2)                   ;
            if (out_size > cols) {
                COL_DESCR = COL_DESCR - (out_size - cols)                   ;
                out_size = cols                                             ;
            }
            # Header
            print_a_line(out_size)                                          ;
            printf("%-"COL_PATCH"s|", " Patch id")                          ;
            for (i=1; i<=n; i++){                                             # Each node
                printf("%s", center(tab_hosts[i], COL_NODE, WHITE, "|"))    ;
            }
            printf(" %-"COL_DESCR"s", "Patch description")                  ;
            printf("\n")                                                    ;
            print_a_line(out_size)                                          ;
            y = asorti(all_patches, all_patches_sorted)
            for (j=1; j<=y; j++){
                patch_id = all_patches_sorted[j] ;
                printf("%s", center(patch_id, COL_PATCH, WHITE, "|"))       ;
                for (i=1; i<=n; i++){                                         # Each node
                    if (length(tab_patches[tab_hosts[i]][patch_id]) > 0){
                        printf("%s", center(HERE   , COL_NODE, GREEN, "|")) ;
                    } else {
                        printf("%s", center(MISSING, COL_NODE, RED  , "|")) ;
                        nb_missing++                                        ;
                    }
                }
                printf(" %-"COL_DESCR"s", substr(all_patches[patch_id], 1, COL_DESCR)) ;
                printf("\n")                                                ;
            }
            # Footer
            print_a_line(out_size)                                          ;
            print nb_missing >> tempfile                                    ;
        }' "${TEMP}"
        rm -f "${TEMP}"
    else
        printf "\033[1;31m%s\033[m\n" "$($TS) [ERROR] Cannot find ${OH}/OPatch/opatch; will skip ${OH}" >&2
    fi
done





./list-ohpatches.sh
2023-09-29_125128 [INFO] Starting collecting GI/OH patch information
2023-09-29_125128 [WARNING] It may be a bit slow if you have many nodes and patches as opatch lspatches is slow
2023-09-29_125128 [INFO] Proceeding with /u01/app/19.0.0/grid . . .

Oracle DBA

anuj blog Archive